diff --git a/src/action/evm_bytecode_visitor.h b/src/action/evm_bytecode_visitor.h index 20d88056..1f51679f 100644 --- a/src/action/evm_bytecode_visitor.h +++ b/src/action/evm_bytecode_visitor.h @@ -1618,14 +1618,23 @@ template class EVMByteCodeVisitor { RequiredSize <= Plan.MaxRequiredSize; } + void maybePrepareLinearBlockMemoryPrecheck(evmc_opcode Opcode, + Operand Stride) { + if (!CurBlockLinearPrecheckPlan.Eligible || + CurBlockLinearPrecheckPlan.CoveredOpcode != Opcode) { + return; + } + Builder.prepareLinearBlockMemoryPrecheck(Stride); + } + void maybePrepareLinearBlockMemoryPrecheck(evmc_opcode Opcode) { if (!CurBlockLinearPrecheckPlan.Eligible || CurBlockLinearPrecheckPlan.CoveredOpcode != Opcode || Stack.getSize() <= CurBlockLinearPrecheckPlan.StrideStackIndex) { return; } - Builder.prepareLinearBlockMemoryPrecheck( - Stack.peek(CurBlockLinearPrecheckPlan.StrideStackIndex)); + maybePrepareLinearBlockMemoryPrecheck( + Opcode, Stack.peek(CurBlockLinearPrecheckPlan.StrideStackIndex)); } LargeStaticWorkspaceVerifierResult @@ -3085,7 +3094,7 @@ template class EVMByteCodeVisitor { Operand Current = Stack.peek(0); Operand Stride = Stack.peek(1); Builder.noteMemoryOpcodeInBlock(OP_MSTORE, MStorePC); - maybePrepareLinearBlockMemoryPrecheck(OP_MSTORE); + maybePrepareLinearBlockMemoryPrecheck(OP_MSTORE, Stride); Builder.handleMStore(Current, Current); Operand Next = Builder.template handleBinaryArithmetic(Current, diff --git a/src/tests/evm_jit_frontend_tests.cpp b/src/tests/evm_jit_frontend_tests.cpp index f046c7ad..3f80d982 100644 --- a/src/tests/evm_jit_frontend_tests.cpp +++ b/src/tests/evm_jit_frontend_tests.cpp @@ -1554,7 +1554,7 @@ class MockEVMBuilder { MOCK_OPERAND_STUB(handleBlobHash); MOCK_OPERAND_STUB(handleBlockHash); MOCK_OPERAND_STUB(handleByte); - MOCK_OPERAND_STUB(handleCallDataLoad); + Operand handleCallDataLoad(Operand) { return Operand(CallDataLoadResult); } MOCK_OPERAND_STUB(handleCallDataSize); MOCK_OPERAND_STUB(handleCallValue); MOCK_OPERAND_STUB(handleCaller); @@ -1690,8 +1690,9 @@ class MockEVMBuilder { LargeStaticWorkspaceRejectHelperByteExactRisk += RejectHelperByteExactRisk; LargeStaticWorkspaceRejectTooFewOps += RejectTooFewOps; } - void prepareLinearBlockMemoryPrecheck(Operand) { + void prepareLinearBlockMemoryPrecheck(Operand Stride) { LinearPrecheckPrepareCount++; + LastLinearPrecheckStride = Stride.resolvedValue()[0]; } void noteMemoryOpcodeInBlock(evmc_opcode, uint64_t) {} void noteHelperOpcodeInBlock(evmc_opcode, uint64_t) {} @@ -1796,6 +1797,10 @@ class MockEVMBuilder { return LinearPrecheckPrepareCount; } + void setCallDataLoadResult(uint64_t Value) { CallDataLoadResult = Value; } + + uint64_t lastLinearPrecheckStride() const { return LastLinearPrecheckStride; } + bool Trapped = false; bool Undefined = false; uint64_t LargeStaticWorkspaceCandidates = 0; @@ -1850,6 +1855,8 @@ class MockEVMBuilder { uint64_t LastLinearPrecheckCoveredDirectOps = 0; bool LastLinearPrecheckValueEqualsFirstAddr = false; uint32_t LinearPrecheckPrepareCount = 0; + uint64_t LastLinearPrecheckStride = 0; + uint64_t CallDataLoadResult = 0; #undef MOCK_OPERAND_STUB #undef MOCK_VOID_STUB @@ -3053,6 +3060,49 @@ TEST(EVMJITFrontendVisitorTest, FusesLinearMStoreNextMotifIntoMeteredRange) { EXPECT_EQ(Builder.topStackValue()[0], 0x60U); } +TEST(EVMJITFrontendVisitorTest, + PlansFusedLinearMStoreMemoryPrecheckWithDynamicStride) { + const std::vector Bytecode = { + 0x5f, // PUSH0 calldata offset for stride + 0x35, // CALLDATALOAD + 0x5f, // PUSH0 current + 0x80, // DUP1 + 0x80, // DUP1 + 0x52, // MSTORE + 0x81, // DUP2 + 0x01, // ADD + 0x80, // DUP1 + 0x80, // DUP1 + 0x52, // MSTORE + 0x81, // DUP2 + 0x01, // ADD + 0x00 // STOP + }; + + COMPILER::EVMFrontendContext Ctx; + Ctx.setRevision(EVMC_CANCUN); + Ctx.setBytecode(reinterpret_cast(Bytecode.data()), + Bytecode.size()); + + MockEVMBuilder Builder; + Builder.setCallDataLoadResult(7); + COMPILER::EVMByteCodeVisitor Visitor(Builder, &Ctx); + EXPECT_TRUE(Visitor.compile()); + EXPECT_FALSE(Builder.Trapped); + EXPECT_FALSE(Builder.Undefined); + + EXPECT_EQ(Builder.linearPrecheckPlanCount(), 1U); + EXPECT_EQ(Builder.lastLinearPrecheckAccessWidth(), 32U); + EXPECT_EQ(Builder.lastLinearPrecheckCoveredDirectOps(), 2U); + EXPECT_TRUE(Builder.lastLinearPrecheckValueEqualsFirstAddr()); + EXPECT_EQ(Builder.linearPrecheckPrepareCount(), 2U); + EXPECT_EQ(Builder.lastLinearPrecheckStride(), 7U); + EXPECT_EQ(Builder.meteredOpcodeCount(OP_MSTORE), 2U); + EXPECT_EQ(Builder.mstoreCount(), 2U); + EXPECT_EQ(Builder.runtimeStackDepth(), 2U); + EXPECT_EQ(Builder.topStackValue()[0], 14U); +} + TEST(EVMJITFrontendVisitorTest, PlansLinearMStore8NextMotifMemoryPrecheck) { const std::vector Bytecode = { 0x5f, // PUSH0 calldata offset for stride @@ -3077,6 +3127,7 @@ TEST(EVMJITFrontendVisitorTest, PlansLinearMStore8NextMotifMemoryPrecheck) { Bytecode.size()); MockEVMBuilder Builder; + Builder.setCallDataLoadResult(11); COMPILER::EVMByteCodeVisitor Visitor(Builder, &Ctx); EXPECT_TRUE(Visitor.compile()); EXPECT_FALSE(Builder.Trapped); @@ -3087,6 +3138,7 @@ TEST(EVMJITFrontendVisitorTest, PlansLinearMStore8NextMotifMemoryPrecheck) { EXPECT_EQ(Builder.lastLinearPrecheckCoveredDirectOps(), 2U); EXPECT_FALSE(Builder.lastLinearPrecheckValueEqualsFirstAddr()); EXPECT_EQ(Builder.linearPrecheckPrepareCount(), 2U); + EXPECT_EQ(Builder.lastLinearPrecheckStride(), 11U); EXPECT_EQ(Builder.meteredOpcodeCount(OP_MSTORE8), 2U); EXPECT_EQ(Builder.runtimeStackDepth(), 2U); }