Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions core/vm/analysis_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,54 +25,54 @@ const (
set7BitsMask = uint16(0b111_1111)
)

// bitvec is a bit vector which maps bytes in a program.
// BitVec is a bit vector which maps bytes in a program.
// An unset bit means the byte is an opcode, a set bit means
// it's data (i.e. argument of PUSHxx).
type bitvec []byte
type BitVec []byte

func (bits bitvec) set1(pos uint64) {
func (bits BitVec) set1(pos uint64) {
bits[pos/8] |= 1 << (pos % 8)
}

func (bits bitvec) setN(flag uint16, pos uint64) {
func (bits BitVec) setN(flag uint16, pos uint64) {
a := flag << (pos % 8)
bits[pos/8] |= byte(a)
if b := byte(a >> 8); b != 0 {
bits[pos/8+1] = b
}
}

func (bits bitvec) set8(pos uint64) {
func (bits BitVec) set8(pos uint64) {
a := byte(0xFF << (pos % 8))
bits[pos/8] |= a
bits[pos/8+1] = ^a
}

func (bits bitvec) set16(pos uint64) {
func (bits BitVec) set16(pos uint64) {
a := byte(0xFF << (pos % 8))
bits[pos/8] |= a
bits[pos/8+1] = 0xFF
bits[pos/8+2] = ^a
}

// codeSegment checks if the position is in a code segment.
func (bits *bitvec) codeSegment(pos uint64) bool {
func (bits *BitVec) codeSegment(pos uint64) bool {
return (((*bits)[pos/8] >> (pos % 8)) & 1) == 0
}

// codeBitmap collects data locations in code.
func codeBitmap(code []byte) bitvec {
func codeBitmap(code []byte) BitVec {
// The bitmap is 4 bytes longer than necessary, in case the code
// ends with a PUSH32, the algorithm will set bits on the
// bitvector outside the bounds of the actual code.
bits := make(bitvec, len(code)/8+1+4)
bits := make(BitVec, len(code)/8+1+4)
return codeBitmapInternal(code, bits)
}

// codeBitmapInternal is the internal implementation of codeBitmap.
// It exists for the purpose of being able to run benchmark tests
// without dynamic allocations affecting the results.
func codeBitmapInternal(code, bits bitvec) bitvec {
func codeBitmapInternal(code, bits BitVec) BitVec {
for pc := uint64(0); pc < uint64(len(code)); {
op := OpCode(code[pc])
pc++
Expand Down
2 changes: 1 addition & 1 deletion core/vm/analysis_legacy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
for i := range code {
code[i] = byte(op)
}
bits := make(bitvec, len(code)/8+1+4)
bits := make(BitVec, len(code)/8+1+4)
b.ResetTimer()
for i := 0; i < b.N; i++ {
clear(bits)
Expand Down
16 changes: 8 additions & 8 deletions core/vm/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ type Contract struct {
caller common.Address
address common.Address

jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
analysis bitvec // Locally cached result of JUMPDEST analysis
jumpDests JumpDestCache // Aggregated result of JUMPDEST analysis.
analysis BitVec // Locally cached result of JUMPDEST analysis

Code []byte
CodeHash common.Hash
Expand All @@ -47,15 +47,15 @@ type Contract struct {
}

// NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests map[common.Hash]bitvec) *Contract {
// Initialize the jump analysis map if it's nil, mostly for tests
func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests JumpDestCache) *Contract {
// Initialize the jump analysis cache if it's nil, mostly for tests
if jumpDests == nil {
jumpDests = make(map[common.Hash]bitvec)
jumpDests = newMapJumpDests()
}
return &Contract{
caller: caller,
address: address,
jumpdests: jumpDests,
jumpDests: jumpDests,
Gas: gas,
value: value,
}
Expand Down Expand Up @@ -87,12 +87,12 @@ func (c *Contract) isCode(udest uint64) bool {
// contracts ( not temporary initcode), we store the analysis in a map
if c.CodeHash != (common.Hash{}) {
// Does parent context have the analysis?
analysis, exist := c.jumpdests[c.CodeHash]
analysis, exist := c.jumpDests.Load(c.CodeHash)
if !exist {
// Do the analysis and save in parent context
// We do not need to store it in c.analysis
analysis = codeBitmap(c.Code)
c.jumpdests[c.CodeHash] = analysis
c.jumpDests.Store(c.CodeHash, analysis)
}
// Also stash it in current contract for faster access
c.analysis = analysis
Expand Down
12 changes: 8 additions & 4 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,8 @@ type EVM struct {
// precompiles holds the precompiled contracts for the current epoch
precompiles map[common.Address]PrecompiledContract

// jumpDests is the aggregated result of JUMPDEST analysis made through
// the life cycle of EVM.
jumpDests map[common.Hash]bitvec
// jumpDests stores results of JUMPDEST analysis.
jumpDests JumpDestCache
}

// NewEVM constructs an EVM instance with the supplied block context, state
Expand All @@ -138,7 +137,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
jumpDests: make(map[common.Hash]bitvec),
jumpDests: newMapJumpDests(),
}
evm.precompiles = activePrecompiledContracts(evm.chainRules)
evm.interpreter = NewEVMInterpreter(evm)
Expand All @@ -152,6 +151,11 @@ func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) {
evm.precompiles = precompiles
}

// SetJumpDestCache configures the analysis cache.
func (evm *EVM) SetJumpDestCache(jumpDests JumpDestCache) {
evm.jumpDests = jumpDests
}

// SetTxContext resets the EVM with a new transaction context.
// This is not threadsafe and should only be done very cautiously.
func (evm *EVM) SetTxContext(txCtx TxContext) {
Expand Down
47 changes: 47 additions & 0 deletions core/vm/jumpdests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2024 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 "github.com/ethereum/go-ethereum/common"

// JumpDestCache represents the cache of jumpdest analysis results.
type JumpDestCache interface {
// Load retrieves the cached jumpdest analysis for the given code hash.
// Returns the BitVec and true if found, or nil and false if not cached.
Load(codeHash common.Hash) (BitVec, bool)

// Store saves the jumpdest analysis for the given code hash.
Store(codeHash common.Hash, vec BitVec)
}

// mapJumpDests is the default implementation of JumpDests using a map.
// This implementation is not thread-safe and is meant to be used per EVM instance.
type mapJumpDests map[common.Hash]BitVec

// newMapJumpDests creates a new map-based JumpDests implementation.
func newMapJumpDests() JumpDestCache {
return make(mapJumpDests)
}

func (j mapJumpDests) Load(codeHash common.Hash) (BitVec, bool) {
vec, ok := j[codeHash]
return vec, ok
}

func (j mapJumpDests) Store(codeHash common.Hash, vec BitVec) {
j[codeHash] = vec
}