forked from cerc-io/plugeth
core/vm: optimize MSTORE and SLOAD (#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
This commit is contained in:
parent
1836366ac1
commit
947e0afeb3
@ -556,7 +556,7 @@ func opMload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *St
|
|||||||
func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
// pop value of the stack
|
// pop value of the stack
|
||||||
mStart, val := stack.pop(), stack.pop()
|
mStart, val := stack.pop(), stack.pop()
|
||||||
memory.Set(mStart.Uint64(), 32, math.PaddedBigBytes(val, 32))
|
memory.Set32(mStart.Uint64(), val)
|
||||||
|
|
||||||
evm.interpreter.intPool.put(mStart, val)
|
evm.interpreter.intPool.put(mStart, val)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -570,9 +570,9 @@ func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||||
loc := common.BigToHash(stack.pop())
|
loc := stack.peek()
|
||||||
val := evm.StateDB.GetState(contract.Address(), loc).Big()
|
val := evm.StateDB.GetState(contract.Address(), common.BigToHash(loc))
|
||||||
stack.push(val)
|
loc.SetBytes(val.Bytes())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,3 +425,42 @@ func BenchmarkOpIsZero(b *testing.B) {
|
|||||||
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
|
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
|
||||||
opBenchmark(b, opIszero, x)
|
opBenchmark(b, opIszero, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpMstore(t *testing.T) {
|
||||||
|
var (
|
||||||
|
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
|
||||||
|
stack = newstack()
|
||||||
|
mem = NewMemory()
|
||||||
|
)
|
||||||
|
mem.Resize(64)
|
||||||
|
pc := uint64(0)
|
||||||
|
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||||
|
stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
|
||||||
|
opMstore(&pc, env, nil, mem, stack)
|
||||||
|
if got := common.Bytes2Hex(mem.Get(0, 32)); got != v {
|
||||||
|
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
|
||||||
|
}
|
||||||
|
stack.pushN(big.NewInt(0x1), big.NewInt(0))
|
||||||
|
opMstore(&pc, env, nil, mem, stack)
|
||||||
|
if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
||||||
|
t.Fatalf("Mstore failed to overwrite previous value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkOpMstore(bench *testing.B) {
|
||||||
|
var (
|
||||||
|
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
|
||||||
|
stack = newstack()
|
||||||
|
mem = NewMemory()
|
||||||
|
)
|
||||||
|
mem.Resize(64)
|
||||||
|
pc := uint64(0)
|
||||||
|
memStart := big.NewInt(0)
|
||||||
|
value := big.NewInt(0x1337)
|
||||||
|
|
||||||
|
bench.ResetTimer()
|
||||||
|
for i := 0; i < bench.N; i++ {
|
||||||
|
stack.pushN(value, memStart)
|
||||||
|
opMstore(&pc, env, nil, mem, stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,7 +16,12 @@
|
|||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
)
|
||||||
|
|
||||||
// Memory implements a simple memory model for the ethereum virtual machine.
|
// Memory implements a simple memory model for the ethereum virtual machine.
|
||||||
type Memory struct {
|
type Memory struct {
|
||||||
@ -30,19 +35,32 @@ func NewMemory() *Memory {
|
|||||||
|
|
||||||
// Set sets offset + size to value
|
// Set sets offset + size to value
|
||||||
func (m *Memory) Set(offset, size uint64, value []byte) {
|
func (m *Memory) Set(offset, size uint64, value []byte) {
|
||||||
// length of store may never be less than offset + size.
|
|
||||||
// The store should be resized PRIOR to setting the memory
|
|
||||||
if size > uint64(len(m.store)) {
|
|
||||||
panic("INVALID memory: store empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's possible the offset is greater than 0 and size equals 0. This is because
|
// It's possible the offset is greater than 0 and size equals 0. This is because
|
||||||
// the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP)
|
// the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP)
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
|
// length of store may never be less than offset + size.
|
||||||
|
// The store should be resized PRIOR to setting the memory
|
||||||
|
if offset+size > uint64(len(m.store)) {
|
||||||
|
panic("invalid memory: store empty")
|
||||||
|
}
|
||||||
copy(m.store[offset:offset+size], value)
|
copy(m.store[offset:offset+size], value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
|
||||||
|
// 32 bytes.
|
||||||
|
func (m *Memory) Set32(offset uint64, val *big.Int) {
|
||||||
|
// length of store may never be less than offset + size.
|
||||||
|
// The store should be resized PRIOR to setting the memory
|
||||||
|
if offset+32 > uint64(len(m.store)) {
|
||||||
|
panic("invalid memory: store empty")
|
||||||
|
}
|
||||||
|
// Zero the memory area
|
||||||
|
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})
|
||||||
|
// Fill in relevant bits
|
||||||
|
math.ReadBits(val, m.store[offset:offset+32])
|
||||||
|
}
|
||||||
|
|
||||||
// Resize resizes the memory to size
|
// Resize resizes the memory to size
|
||||||
func (m *Memory) Resize(size uint64) {
|
func (m *Memory) Resize(size uint64) {
|
||||||
if uint64(m.Len()) < size {
|
if uint64(m.Len()) < size {
|
||||||
|
Loading…
Reference in New Issue
Block a user