From 30ebf724c683c3a523598b3f6ac4a47422e5b6d2 Mon Sep 17 00:00:00 2001 From: Giulio Rebuffo Date: Sat, 16 May 2026 03:38:22 +0200 Subject: [PATCH] Optimize MLOAD memory fast path --- vm/bytecode.go | 11 +++-------- vm/gen/main.go | 2 ++ vm/inst_memory.go | 19 ++++++++++++++----- vm/table_gen.go | 36 ++++++++++++++++++++++++++---------- 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/vm/bytecode.go b/vm/bytecode.go index bd57ff10..80a2e0c3 100644 --- a/vm/bytecode.go +++ b/vm/bytecode.go @@ -66,16 +66,11 @@ func (b *Bytecode) ResetWithHash(code []byte, hash types.B256) { // If same hash as previous call on this pooled bytecode, skip analysis if b.hash != nil && *b.hash == hash && b.originalLen == originalLen { needed := originalLen + bytecodeEndPadding - if cap(b.code) >= needed { - b.code = b.code[:needed] - } else { - b.code = make([]byte, needed) - } - copy(b.code, code) - clear(b.code[originalLen:needed]) + b.code = b.code[:needed] b.pc = 0 b.running = true - // jumpTable + jumpTableReady still valid from previous Reset + // code bytes, padding, jumpTable, and jumpTableReady are still valid + // from the previous Reset for this hash. return } diff --git a/vm/gen/main.go b/vm/gen/main.go index 5f80a129..203ddb03 100644 --- a/vm/gen/main.go +++ b/vm/gen/main.go @@ -747,6 +747,7 @@ func (e *emitter) emitHeader() { e.p("// Code generated by gen/main.go; DO NOT EDIT.\n\n") e.p("package vm\n\n") e.p("import (\n") + e.p("\t\"encoding/binary\"\n") e.p("\t\"github.com/Giulio2002/gevm/opcode\"\n") e.p("\t\"github.com/Giulio2002/gevm/spec\"\n") e.p("\t\"github.com/holiman/uint256\"\n") @@ -757,6 +758,7 @@ func (e *emitter) emitHeader() { e.p("\t_ = uint256.Int{}\n") e.p("\t_ = spec.GasVerylow\n") e.p("\t_ = opcode.STOP\n") + e.p("\t_ = binary.BigEndian\n") e.p(")\n\n") } diff --git a/vm/inst_memory.go b/vm/inst_memory.go index bf8f3212..5d05d5bf 100644 --- a/vm/inst_memory.go +++ b/vm/inst_memory.go @@ -2,6 +2,8 @@ package vm import ( + "encoding/binary" + "github.com/Giulio2002/gevm/spec" ) @@ -20,13 +22,20 @@ func opMload(interp *Interpreter) { top := &s.data[s.top-1] if top[1]|top[2]|top[3] == 0 { offset := int(top[0]) - if offset >= 0 && offset+32 <= interp.Memory.Len() { - *top = interp.Memory.GetU256(offset) - } else { - if interp.ResizeMemory(offset, 32) { - *top = interp.Memory.GetU256(offset) + mem := interp.Memory + if offset < 0 || offset+32 > len(*mem.buffer)-mem.checkpoint { + if !interp.ResizeMemory(offset, 32) { + return } } + p := mem.checkpoint + offset + b := (*mem.buffer)[p : p+32 : p+32] + *top = [4]uint64{ + binary.BigEndian.Uint64(b[24:]), + binary.BigEndian.Uint64(b[16:]), + binary.BigEndian.Uint64(b[8:]), + binary.BigEndian.Uint64(b[0:]), + } } else { interp.Halt(InstructionResultInvalidOperandOOG) } diff --git a/vm/table_gen.go b/vm/table_gen.go index 5df697d4..3cab80bc 100644 --- a/vm/table_gen.go +++ b/vm/table_gen.go @@ -3,6 +3,7 @@ package vm import ( + "encoding/binary" "github.com/Giulio2002/gevm/opcode" "github.com/Giulio2002/gevm/spec" "github.com/holiman/uint256" @@ -13,6 +14,7 @@ var ( _ = uint256.Int{} _ = spec.GasVerylow _ = opcode.STOP + _ = binary.BigEndian ) // Run executes bytecode until halted using direct switch dispatch. @@ -1127,13 +1129,20 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { top := &s.data[s.top-1] if top[1]|top[2]|top[3] == 0 { offset := int(top[0]) - if offset >= 0 && offset+32 <= interp.Memory.Len() { - *top = interp.Memory.GetU256(offset) - } else { - if interp.ResizeMemory(offset, 32) { - *top = interp.Memory.GetU256(offset) + mem := interp.Memory + if offset < 0 || offset+32 > len(*mem.buffer)-mem.checkpoint { + if !interp.ResizeMemory(offset, 32) { + return } } + p := mem.checkpoint + offset + b := (*mem.buffer)[p : p+32 : p+32] + *top = [4]uint64{ + binary.BigEndian.Uint64(b[24:]), + binary.BigEndian.Uint64(b[16:]), + binary.BigEndian.Uint64(b[8:]), + binary.BigEndian.Uint64(b[0:]), + } } else { interp.Halt(InstructionResultInvalidOperandOOG) } @@ -3671,13 +3680,20 @@ func (r *TracingRunner) Run(interp *Interpreter, host Host) { top := &s.data[s.top-1] if top[1]|top[2]|top[3] == 0 { offset := int(top[0]) - if offset >= 0 && offset+32 <= interp.Memory.Len() { - *top = interp.Memory.GetU256(offset) - } else { - if interp.ResizeMemory(offset, 32) { - *top = interp.Memory.GetU256(offset) + mem := interp.Memory + if offset < 0 || offset+32 > len(*mem.buffer)-mem.checkpoint { + if !interp.ResizeMemory(offset, 32) { + return } } + p := mem.checkpoint + offset + b := (*mem.buffer)[p : p+32 : p+32] + *top = [4]uint64{ + binary.BigEndian.Uint64(b[24:]), + binary.BigEndian.Uint64(b[16:]), + binary.BigEndian.Uint64(b[8:]), + binary.BigEndian.Uint64(b[0:]), + } } else { interp.Halt(InstructionResultInvalidOperandOOG) }