Skip to content

Commit 166ef3d

Browse files
authored
feat(core/vm): add configurable jumpdest analysis cache ethereum#32143 (#2321)
1 parent 7cafe43 commit 166ef3d

5 files changed

Lines changed: 75 additions & 22 deletions

File tree

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,54 +25,54 @@ const (
2525
set7BitsMask = uint16(0b111_1111)
2626
)
2727

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

33-
func (bits bitvec) set1(pos uint64) {
33+
func (bits BitVec) set1(pos uint64) {
3434
bits[pos/8] |= 1 << (pos % 8)
3535
}
3636

37-
func (bits bitvec) setN(flag uint16, pos uint64) {
37+
func (bits BitVec) setN(flag uint16, pos uint64) {
3838
a := flag << (pos % 8)
3939
bits[pos/8] |= byte(a)
4040
if b := byte(a >> 8); b != 0 {
4141
bits[pos/8+1] = b
4242
}
4343
}
4444

45-
func (bits bitvec) set8(pos uint64) {
45+
func (bits BitVec) set8(pos uint64) {
4646
a := byte(0xFF << (pos % 8))
4747
bits[pos/8] |= a
4848
bits[pos/8+1] = ^a
4949
}
5050

51-
func (bits bitvec) set16(pos uint64) {
51+
func (bits BitVec) set16(pos uint64) {
5252
a := byte(0xFF << (pos % 8))
5353
bits[pos/8] |= a
5454
bits[pos/8+1] = 0xFF
5555
bits[pos/8+2] = ^a
5656
}
5757

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

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

7272
// codeBitmapInternal is the internal implementation of codeBitmap.
7373
// It exists for the purpose of being able to run benchmark tests
7474
// without dynamic allocations affecting the results.
75-
func codeBitmapInternal(code, bits bitvec) bitvec {
75+
func codeBitmapInternal(code, bits BitVec) BitVec {
7676
for pc := uint64(0); pc < uint64(len(code)); {
7777
op := OpCode(code[pc])
7878
pc++
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
9090
for i := range code {
9191
code[i] = byte(op)
9292
}
93-
bits := make(bitvec, len(code)/8+1+4)
93+
bits := make(BitVec, len(code)/8+1+4)
9494
b.ResetTimer()
9595
for i := 0; i < b.N; i++ {
9696
clear(bits)

core/vm/contract.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ type Contract struct {
3232
caller common.Address
3333
address common.Address
3434

35-
jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
36-
analysis bitvec // Locally cached result of JUMPDEST analysis
35+
jumpDests JumpDestCache // Aggregated result of JUMPDEST analysis.
36+
analysis BitVec // Locally cached result of JUMPDEST analysis
3737

3838
Code []byte
3939
CodeHash common.Hash
@@ -44,15 +44,15 @@ type Contract struct {
4444
}
4545

4646
// NewContract returns a new contract environment for the execution of EVM.
47-
func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests map[common.Hash]bitvec) *Contract {
47+
func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests JumpDestCache) *Contract {
4848
// Initialize the jump analysis map if it's nil, mostly for tests
4949
if jumpDests == nil {
50-
jumpDests = make(map[common.Hash]bitvec)
50+
jumpDests = newMapJumpDests()
5151
}
5252
return &Contract{
5353
caller: caller,
5454
address: address,
55-
jumpdests: jumpDests,
55+
jumpDests: jumpDests,
5656
Gas: gas,
5757
value: value,
5858
}
@@ -84,12 +84,12 @@ func (c *Contract) isCode(udest uint64) bool {
8484
// contracts ( not temporary initcode), we store the analysis in a map
8585
if c.CodeHash != (common.Hash{}) {
8686
// Does parent context have the analysis?
87-
analysis, exist := c.jumpdests[c.CodeHash]
87+
analysis, exist := c.jumpDests.Load(c.CodeHash)
8888
if !exist {
8989
// Do the analysis and save in parent context
9090
// We do not need to store it in c.analysis
9191
analysis = codeBitmap(c.Code)
92-
c.jumpdests[c.CodeHash] = analysis
92+
c.jumpDests.Store(c.CodeHash, analysis)
9393
}
9494
// Also stash it in current contract for faster access
9595
c.analysis = analysis

core/vm/evm.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,8 @@ type EVM struct {
121121
// precompiles holds the precompiled contracts for the current epoch
122122
precompiles map[common.Address]PrecompiledContract
123123

124-
// jumpDests is the aggregated result of JUMPDEST analysis made through
125-
// the life cycle of EVM.
126-
jumpDests map[common.Hash]bitvec
124+
// jumpDests stores results of JUMPDEST analysis.
125+
jumpDests JumpDestCache
127126

128127
hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes
129128
hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes
@@ -144,7 +143,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, tradingStateDB *tradingstate
144143
Config: config,
145144
chainConfig: chainConfig,
146145
chainRules: chainConfig.Rules(blockCtx.BlockNumber),
147-
jumpDests: make(map[common.Hash]bitvec),
146+
jumpDests: newMapJumpDests(),
148147
hasher: crypto.NewKeccakState(),
149148
}
150149
evm.precompiles = activePrecompiledContracts(evm.chainRules)
@@ -206,6 +205,11 @@ func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) {
206205
evm.precompiles = precompiles
207206
}
208207

208+
// SetJumpDestCache configures the analysis cache.
209+
func (evm *EVM) SetJumpDestCache(jumpDests JumpDestCache) {
210+
evm.jumpDests = jumpDests
211+
}
212+
209213
// SetTxContext updates the EVM with a new transaction context.
210214
// This is not threadsafe and should only be done very cautiously.
211215
func (evm *EVM) SetTxContext(txCtx TxContext) {

core/vm/jumpdests.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2024 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package vm
18+
19+
import (
20+
"github.com/XinFinOrg/XDPoSChain/common"
21+
)
22+
23+
// JumpDestCache represents the cache of jumpdest analysis results.
24+
type JumpDestCache interface {
25+
// Load retrieves the cached jumpdest analysis for the given code hash.
26+
// Returns the BitVec and true if found, or nil and false if not cached.
27+
Load(codeHash common.Hash) (BitVec, bool)
28+
29+
// Store saves the jumpdest analysis for the given code hash.
30+
Store(codeHash common.Hash, vec BitVec)
31+
}
32+
33+
// mapJumpDests is the default implementation of JumpDests using a map.
34+
// This implementation is not thread-safe and is meant to be used per EVM instance.
35+
type mapJumpDests map[common.Hash]BitVec
36+
37+
// newMapJumpDests creates a new map-based JumpDests implementation.
38+
func newMapJumpDests() JumpDestCache {
39+
return make(mapJumpDests)
40+
}
41+
42+
func (j mapJumpDests) Load(codeHash common.Hash) (BitVec, bool) {
43+
vec, ok := j[codeHash]
44+
return vec, ok
45+
}
46+
47+
func (j mapJumpDests) Store(codeHash common.Hash, vec BitVec) {
48+
j[codeHash] = vec
49+
}

0 commit comments

Comments
 (0)