core/vm: hoist jumptable nil-check out of the interpreter loop

This commit is contained in:
Ömer Faruk IRMAK 2025-06-20 14:41:54 +03:00
parent 09289fd154
commit 301d5a8b27
2 changed files with 25 additions and 5 deletions

View file

@ -181,10 +181,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} }
var ( var (
op OpCode // current opcode op OpCode // current opcode
mem = NewMemory() // bound memory jumpTable *JumpTable = in.table
stack = newstack() // local stack mem = NewMemory() // bound memory
callContext = &ScopeContext{ stack = newstack() // local stack
callContext = &ScopeContext{
Memory: mem, Memory: mem,
Stack: stack, Stack: stack,
Contract: contract, Contract: contract,
@ -227,6 +228,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the // the execution of one of the operations or until the done flag is set by the
// parent context. // parent context.
_ = jumpTable[0] // nil-check the jumpTable out of the loop
for { for {
if debug { if debug {
// Capture pre-execution values for tracing. // Capture pre-execution values for tracing.
@ -247,7 +249,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// Get the operation from the jump table and validate the stack to ensure there are // Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation. // enough stack items available to perform the operation.
op = contract.GetOp(pc) op = contract.GetOp(pc)
operation := in.table[op] operation := jumpTable[op]
cost = operation.constantGas // For tracing cost = operation.constantGas // For tracing
// Validate stack // Validate stack
if sLen := stack.len(); sLen < operation.minStack { if sLen := stack.len(); sLen < operation.minStack {

View file

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"github.com/stretchr/testify/require"
) )
var loopInterruptTests = []string{ var loopInterruptTests = []string{
@ -74,3 +75,20 @@ func TestLoopInterrupt(t *testing.T) {
} }
} }
} }
func BenchmarkLoop(b *testing.B) {
address := common.BytesToAddress([]byte("contract"))
vmctx := BlockContext{
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
statedb.CreateAccount(address)
statedb.SetCode(address, common.Hex2Bytes(loopInterruptTests[0]))
statedb.Finalise(true)
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{})
b.ResetTimer()
_, _, err := evm.Call(common.Address{}, address, nil, uint64(b.N*100), new(uint256.Int))
require.EqualError(b, err, ErrOutOfGas.Error())
}