mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-10 10:06:47 +00:00
Here, we change the EVM stack implementation to use an 'arena', i.e. a shared allocation pool for sub-call stacks. The stack is now more GC-friendly, since it is a slice of uint256 values instead of a slice of pointers. Code that pushes an item to the stack has been changed to get() the top item, then overwrite it. The PR is a rewrite/rebase of #30362. --------- Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
186 lines
6.1 KiB
Go
186 lines
6.1 KiB
Go
// Copyright 2014 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package vm
|
|
|
|
import (
|
|
"slices"
|
|
"sync"
|
|
|
|
"github.com/holiman/uint256"
|
|
)
|
|
|
|
// stackArena is an arena which actual evm stacks use for data storage
|
|
type stackArena struct {
|
|
data []uint256.Int
|
|
top int // first free slot
|
|
}
|
|
|
|
func newArena() *stackArena {
|
|
return stackPool.Get().(*stackArena)
|
|
}
|
|
|
|
// 1025, because in stack() there is a condition check
|
|
// for the stack size that would fail if it was set to
|
|
// 1024.
|
|
const initialStackSize = 1025
|
|
|
|
var stackPool = sync.Pool{
|
|
New: func() any {
|
|
return &stackArena{
|
|
data: make([]uint256.Int, initialStackSize),
|
|
}
|
|
},
|
|
}
|
|
|
|
func returnStack(arena *stackArena) {
|
|
arena.top = 0 // defensive, not strictly needed as s.inner.top = s.bottom in release()
|
|
stackPool.Put(arena)
|
|
}
|
|
|
|
// stack returns an instance of a stack which uses the underlying arena. The instance
|
|
// must be released by invoking the (*Stack).release() method
|
|
func (sa *stackArena) stack() *Stack {
|
|
// make sure every substack has at least 1024 elements
|
|
if len(sa.data) <= sa.top+1024 {
|
|
// we need to grow the arena
|
|
sa.data = slices.Grow(sa.data, 1024)
|
|
sa.data = sa.data[:cap(sa.data)]
|
|
}
|
|
return &Stack{
|
|
bottom: sa.top,
|
|
size: 0,
|
|
inner: sa,
|
|
}
|
|
}
|
|
|
|
// newStackForTesting is meant to be used solely for testing. It creates a stack
|
|
// backed by a newly allocated arena.
|
|
func newStackForTesting() *Stack {
|
|
arena := &stackArena{
|
|
data: make([]uint256.Int, 1025),
|
|
}
|
|
return arena.stack()
|
|
}
|
|
|
|
// Stack is an object for basic stack operations. Items popped to the stack are
|
|
// expected to be changed and modified. stack does not take care of adding newly
|
|
// initialized objects.
|
|
type Stack struct {
|
|
bottom int // bottom is the index of the first element of this stack
|
|
size int // size is the number of elements in this stack
|
|
inner *stackArena
|
|
}
|
|
|
|
// release un-claims the area of the arena which was claimed by the stack.
|
|
func (s *Stack) release() {
|
|
// When the stack is returned, need to notify the arena that the new 'top' is
|
|
// the returned stack's bottom.
|
|
s.inner.top = s.bottom
|
|
}
|
|
|
|
// Data returns the underlying uint256.Int array.
|
|
func (s *Stack) Data() []uint256.Int {
|
|
return s.inner.data[s.bottom : s.bottom+s.size]
|
|
}
|
|
|
|
func (s *Stack) push(d *uint256.Int) {
|
|
elem := s.get()
|
|
*elem = *d
|
|
}
|
|
|
|
// get returns a pointer to a newly created element
|
|
// on top of the stack
|
|
func (s *Stack) get() *uint256.Int {
|
|
elem := &s.inner.data[s.inner.top]
|
|
s.inner.top++
|
|
s.size++
|
|
return elem
|
|
}
|
|
|
|
func (s *Stack) pop() uint256.Int {
|
|
s.inner.top--
|
|
s.size--
|
|
return s.inner.data[s.inner.top]
|
|
}
|
|
|
|
func (s *Stack) len() int {
|
|
return s.size
|
|
}
|
|
|
|
func (s *Stack) swap1() {
|
|
s.inner.data[s.bottom+s.size-2], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-2]
|
|
}
|
|
func (s *Stack) swap2() {
|
|
s.inner.data[s.bottom+s.size-3], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-3]
|
|
}
|
|
func (s *Stack) swap3() {
|
|
s.inner.data[s.bottom+s.size-4], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-4]
|
|
}
|
|
func (s *Stack) swap4() {
|
|
s.inner.data[s.bottom+s.size-5], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-5]
|
|
}
|
|
func (s *Stack) swap5() {
|
|
s.inner.data[s.bottom+s.size-6], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-6]
|
|
}
|
|
func (s *Stack) swap6() {
|
|
s.inner.data[s.bottom+s.size-7], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-7]
|
|
}
|
|
func (s *Stack) swap7() {
|
|
s.inner.data[s.bottom+s.size-8], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-8]
|
|
}
|
|
func (s *Stack) swap8() {
|
|
s.inner.data[s.bottom+s.size-9], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-9]
|
|
}
|
|
func (s *Stack) swap9() {
|
|
s.inner.data[s.bottom+s.size-10], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-10]
|
|
}
|
|
func (s *Stack) swap10() {
|
|
s.inner.data[s.bottom+s.size-11], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-11]
|
|
}
|
|
func (s *Stack) swap11() {
|
|
s.inner.data[s.bottom+s.size-12], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-12]
|
|
}
|
|
func (s *Stack) swap12() {
|
|
s.inner.data[s.bottom+s.size-13], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-13]
|
|
}
|
|
func (s *Stack) swap13() {
|
|
s.inner.data[s.bottom+s.size-14], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-14]
|
|
}
|
|
func (s *Stack) swap14() {
|
|
s.inner.data[s.bottom+s.size-15], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-15]
|
|
}
|
|
func (s *Stack) swap15() {
|
|
s.inner.data[s.bottom+s.size-16], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-16]
|
|
}
|
|
func (s *Stack) swap16() {
|
|
s.inner.data[s.bottom+s.size-17], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-17]
|
|
}
|
|
|
|
func (s *Stack) dup(n int) {
|
|
s.inner.data[s.bottom+s.size] = s.inner.data[s.bottom+s.size-n]
|
|
s.size++
|
|
s.inner.top++
|
|
}
|
|
|
|
func (s *Stack) peek() *uint256.Int {
|
|
return &s.inner.data[s.bottom+s.size-1]
|
|
}
|
|
|
|
// back returns the n'th item in stack
|
|
func (s *Stack) back(n int) *uint256.Int {
|
|
return &s.inner.data[s.bottom+s.size-n-1]
|
|
}
|