diff --git a/bvm/Shaders/dummy/contract.cpp b/bvm/Shaders/dummy/contract.cpp index 6e881ddae..5135d9ecd 100644 --- a/bvm/Shaders/dummy/contract.cpp +++ b/bvm/Shaders/dummy/contract.cpp @@ -372,3 +372,25 @@ BEAM_EXPORT void Method_15(Dummy::TestFarCallFlags& r) Env::SaveVar_T((uint8_t) 1, (uint8_t) 2); } } + +template +void TestFloatOp(TMethod& r) +{ + switch (r.m_Op) + { + case 0: r.m_Arg1 += r.m_Arg2; break; + case 1: r.m_Arg1 -= r.m_Arg2; break; + case 2: r.m_Arg1 *= r.m_Arg2; break; + case 3: r.m_Arg1 /= r.m_Arg2; break; + } +} + +BEAM_EXPORT void Method_16(Dummy::TestFloat1& r) +{ + TestFloatOp(r); +} + +BEAM_EXPORT void Method_17(Dummy::TestFloat2& r) +{ + TestFloatOp(r); +} diff --git a/bvm/Shaders/dummy/contract.h b/bvm/Shaders/dummy/contract.h index 333e8c044..e3e38a765 100644 --- a/bvm/Shaders/dummy/contract.h +++ b/bvm/Shaders/dummy/contract.h @@ -129,6 +129,22 @@ namespace Dummy uint8_t m_TryWrite; }; + struct TestFloat1 + { + static const uint32_t s_iMethod = 16; + MultiPrecision::Float m_Arg1; + MultiPrecision::Float m_Arg2; + uint8_t m_Op; + }; + + struct TestFloat2 + { + static const uint32_t s_iMethod = 17; + MultiPrecision::FloatEx m_Arg1; + MultiPrecision::FloatEx m_Arg2; + uint8_t m_Op; + }; + #pragma pack (pop) } diff --git a/bvm/Shaders/dummy/contract.wasm b/bvm/Shaders/dummy/contract.wasm index 1eb9a01d3..350d67801 100644 Binary files a/bvm/Shaders/dummy/contract.wasm and b/bvm/Shaders/dummy/contract.wasm differ diff --git a/bvm/unittest/shaders_test.cpp b/bvm/unittest/shaders_test.cpp index 29afc1b52..eec336fc1 100644 --- a/bvm/unittest/shaders_test.cpp +++ b/bvm/unittest/shaders_test.cpp @@ -119,6 +119,10 @@ namespace Shaders { } template void Convert(Dummy::FindVarTest& x) { } + template void Convert(Dummy::TestFloat1& x) { + } + template void Convert(Dummy::TestFloat2& x) { + } template void Convert(Roulette::Params& x) { } @@ -2717,6 +2721,75 @@ namespace bvm2 { verify_test(RunGuarded_T(cid, args.s_iMethod, args)); } + { + Shaders::Dummy::TestFloat1 args1; + ZeroObject(args1); + Shaders::Dummy::TestFloat2 args2; + ZeroObject(args2); + + for (uint32_t i = 0; i < 100; i++) + { + uintBigFor::Type val; + uint64_t a, b, c; + ECC::GenRandom(val); + val.Export(a); + ECC::GenRandom(val); + val.Export(b); + + if (a < b) + std::swap(a, b); + + args1.m_Arg1 = a; + args1.m_Arg2 = b; + args1.m_Op = 1; // subtract + + verify_test(args1.m_Arg1 >= args1.m_Arg2); + + verify_test(RunGuarded_T(cid, args1.s_iMethod, args1)); + + verify_test(args1.m_Arg1.RoundDown(c)); + verify_test(c == a - b); + + args1.m_Arg2 = args1.m_Arg1; + verify_test(args1.m_Arg1 == args1.m_Arg2); + verify_test(RunGuarded_T(cid, args1.s_iMethod, args1)); // must be 0 + + verify_test(args1.m_Arg1.IsZero()); + verify_test(args1.m_Arg1.RoundDown(c)); + verify_test(!c); + + args2.m_Arg1 = a; + args2.m_Arg2 = b; + args2.m_Op = 1; // subtract + + verify_test(args2.m_Arg1 >= args2.m_Arg2); + + verify_test(RunGuarded_T(cid, args2.s_iMethod, args2)); + + verify_test(!args2.m_Arg1.IsNaN() && !args2.m_Arg1.IsNegative()); + verify_test(args2.m_Arg1.RoundDown(c)); + verify_test(c == a - b); + + // check negative subtraction. Make sure result fits in int64_t (for comparison) + b >>= 1; + a >>= 1; + args2.m_Arg1 = b; + args2.m_Arg2 = a; + + verify_test(args2.m_Arg1 <= args2.m_Arg2); + + verify_test(RunGuarded_T(cid, args2.s_iMethod, args2)); + + verify_test(!args2.m_Arg1.IsNaN() && !args2.m_Arg1.IsPositive()); + args2.m_Arg1.RoundDown(c); + verify_test(!c); // underflow, truncated to 0 + + int64_t d; + verify_test(args2.m_Arg1.RoundDown(d)); + verify_test(d == static_cast(b - a)); + } + } + verify_test(ContractDestroy_T(cid, zero)); }