mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 13:46:43 +00:00
switched from map[common.Hash]bitvec to JumpDests
- renamed `bitvec` to `BitVec` - `BitVec` needs to be exported to support thirdparty `JumpDests` implementations
This commit is contained in:
parent
afbcd4e957
commit
b358b593fa
4 changed files with 44 additions and 23 deletions
|
|
@ -25,16 +25,16 @@ 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 {
|
||||
|
|
@ -42,13 +42,13 @@ func (bits bitvec) setN(flag uint16, pos uint64) {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -56,23 +56,23 @@ func (bits bitvec) set16(pos uint64) {
|
|||
}
|
||||
|
||||
// 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++
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 JumpDests // Aggregated result of JUMPDEST analysis.
|
||||
analysis BitVec // Locally cached result of JUMPDEST analysis
|
||||
|
||||
Code []byte
|
||||
CodeHash common.Hash
|
||||
|
|
@ -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 JumpDests) *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,
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 JumpDests
|
||||
}
|
||||
|
||||
// NewEVM constructs an EVM instance with the supplied block context, state
|
||||
|
|
@ -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: nil,
|
||||
}
|
||||
evm.precompiles = activePrecompiledContracts(evm.chainRules)
|
||||
evm.interpreter = NewEVMInterpreter(evm)
|
||||
|
|
@ -152,6 +151,13 @@ func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) {
|
|||
evm.precompiles = precompiles
|
||||
}
|
||||
|
||||
// SetJumpDests sets a custom JumpDests implementation for the EVM.
|
||||
// This allows for flexible caching strategies, including global caches
|
||||
// that can be shared across multiple EVM instances.
|
||||
func (evm *EVM) SetJumpDests(jumpDests JumpDests) {
|
||||
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) {
|
||||
|
|
@ -238,6 +244,9 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
ret, err = nil, nil // gas is unchanged
|
||||
} else {
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
if evm.jumpDests == nil {
|
||||
evm.jumpDests = newMapJumpDests()
|
||||
}
|
||||
contract := NewContract(caller, addr, value, gas, evm.jumpDests)
|
||||
contract.IsSystemCall = isSystemCall(caller)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), code)
|
||||
|
|
@ -298,6 +307,9 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
} else {
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
if evm.jumpDests == nil {
|
||||
evm.jumpDests = newMapJumpDests()
|
||||
}
|
||||
contract := NewContract(caller, caller, value, gas, evm.jumpDests)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
ret, err = evm.interpreter.Run(contract, input, false)
|
||||
|
|
@ -342,6 +354,9 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
// Initialise a new contract and make initialise the delegate values
|
||||
//
|
||||
// Note: The value refers to the original value from the parent call.
|
||||
if evm.jumpDests == nil {
|
||||
evm.jumpDests = newMapJumpDests()
|
||||
}
|
||||
contract := NewContract(originCaller, caller, value, gas, evm.jumpDests)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
ret, err = evm.interpreter.Run(contract, input, false)
|
||||
|
|
@ -393,6 +408,9 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
|||
} else {
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
if evm.jumpDests == nil {
|
||||
evm.jumpDests = newMapJumpDests()
|
||||
}
|
||||
contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests)
|
||||
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
|
||||
|
||||
|
|
@ -500,6 +518,9 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
if evm.jumpDests == nil {
|
||||
evm.jumpDests = newMapJumpDests()
|
||||
}
|
||||
contract := NewContract(caller, address, value, gas, evm.jumpDests)
|
||||
|
||||
// Explicitly set the code to a null hash to prevent caching of jump analysis
|
||||
|
|
|
|||
Loading…
Reference in a new issue