Skip to content

Commit 6e1d30c

Browse files
holimanfirmianavan
authored andcommitted
core/vm: optimize MSTORE and SLOAD (ethereum#16939)
* vm/test: add tests+benchmarks for mstore * core/vm: less alloc and copying for mstore * core/vm: less allocs in sload * vm: check for errors more correctly
1 parent c196420 commit 6e1d30c

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

core/vm/instructions.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ func opMload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *St
556556
func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
557557
// pop value of the stack
558558
mStart, val := stack.pop(), stack.pop()
559-
memory.Set(mStart.Uint64(), 32, math.PaddedBigBytes(val, 32))
559+
memory.Set32(mStart.Uint64(), val)
560560

561561
evm.interpreter.intPool.put(mStart, val)
562562
return nil, nil
@@ -570,9 +570,9 @@ func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *
570570
}
571571

572572
func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
573-
loc := common.BigToHash(stack.pop())
574-
val := evm.StateDB.GetState(contract.Address(), loc).Big()
575-
stack.push(val)
573+
loc := stack.peek()
574+
val := evm.StateDB.GetState(contract.Address(), common.BigToHash(loc))
575+
loc.SetBytes(val.Bytes())
576576
return nil, nil
577577
}
578578

core/vm/instructions_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -425,3 +425,42 @@ func BenchmarkOpIsZero(b *testing.B) {
425425
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
426426
opBenchmark(b, opIszero, x)
427427
}
428+
429+
func TestOpMstore(t *testing.T) {
430+
var (
431+
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
432+
stack = newstack()
433+
mem = NewMemory()
434+
)
435+
mem.Resize(64)
436+
pc := uint64(0)
437+
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
438+
stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
439+
opMstore(&pc, env, nil, mem, stack)
440+
if got := common.Bytes2Hex(mem.Get(0, 32)); got != v {
441+
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
442+
}
443+
stack.pushN(big.NewInt(0x1), big.NewInt(0))
444+
opMstore(&pc, env, nil, mem, stack)
445+
if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
446+
t.Fatalf("Mstore failed to overwrite previous value")
447+
}
448+
}
449+
450+
func BenchmarkOpMstore(bench *testing.B) {
451+
var (
452+
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
453+
stack = newstack()
454+
mem = NewMemory()
455+
)
456+
mem.Resize(64)
457+
pc := uint64(0)
458+
memStart := big.NewInt(0)
459+
value := big.NewInt(0x1337)
460+
461+
bench.ResetTimer()
462+
for i := 0; i < bench.N; i++ {
463+
stack.pushN(value, memStart)
464+
opMstore(&pc, env, nil, mem, stack)
465+
}
466+
}

core/vm/memory.go

+25-7
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616

1717
package vm
1818

19-
import "fmt"
19+
import (
20+
"fmt"
21+
"math/big"
22+
23+
"github.com/ethereum/go-ethereum/common/math"
24+
)
2025

2126
// Memory implements a simple memory model for the ethereum virtual machine.
2227
type Memory struct {
@@ -30,19 +35,32 @@ func NewMemory() *Memory {
3035

3136
// Set sets offset + size to value
3237
func (m *Memory) Set(offset, size uint64, value []byte) {
33-
// length of store may never be less than offset + size.
34-
// The store should be resized PRIOR to setting the memory
35-
if size > uint64(len(m.store)) {
36-
panic("INVALID memory: store empty")
37-
}
38-
3938
// It's possible the offset is greater than 0 and size equals 0. This is because
4039
// the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP)
4140
if size > 0 {
41+
// length of store may never be less than offset + size.
42+
// The store should be resized PRIOR to setting the memory
43+
if offset+size > uint64(len(m.store)) {
44+
panic("invalid memory: store empty")
45+
}
4246
copy(m.store[offset:offset+size], value)
4347
}
4448
}
4549

50+
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
51+
// 32 bytes.
52+
func (m *Memory) Set32(offset uint64, val *big.Int) {
53+
// length of store may never be less than offset + size.
54+
// The store should be resized PRIOR to setting the memory
55+
if offset+32 > uint64(len(m.store)) {
56+
panic("invalid memory: store empty")
57+
}
58+
// Zero the memory area
59+
copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
60+
// Fill in relevant bits
61+
math.ReadBits(val, m.store[offset:offset+32])
62+
}
63+
4664
// Resize resizes the memory to size
4765
func (m *Memory) Resize(size uint64) {
4866
if uint64(m.Len()) < size {

0 commit comments

Comments
 (0)