diff --git a/core/vm/analysis.go b/core/vm/analysis.go index 449cded2a8..2a8aafd4e0 100644 --- a/core/vm/analysis.go +++ b/core/vm/analysis.go @@ -16,6 +16,11 @@ package vm +import ( + "github.com/ethereum/go-ethereum/common" + lru "github.com/hashicorp/golang-lru" +) + const ( set2BitsMask = uint16(0b1100_0000_0000_0000) set3BitsMask = uint16(0b1110_0000_0000_0000) @@ -25,6 +30,10 @@ const ( set7BitsMask = uint16(0b1111_1110_0000_0000) ) +const codeBitmapCacheSize = 10000 + +var codeBitmapCache, _ = lru.NewARC(codeBitmapCacheSize) + // 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). @@ -122,3 +131,17 @@ func codeBitmapInternal(code, bits bitvec) bitvec { } return bits } + +func codeBitmapWithCache(code []byte, codeHash common.Hash) bitvec { + if (codeHash == emptyCodeHash || codeHash == common.Hash{}) { + return nil + } + + if val, ok := codeBitmapCache.Get(codeHash); ok { + return val.(bitvec) + } else { + val := codeBitmap(code) + codeBitmapCache.Add(codeHash, val) + return val + } +} diff --git a/core/vm/contract.go b/core/vm/contract.go index 61dbd5007a..6ed46e525f 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -106,13 +106,13 @@ func (c *Contract) isCode(udest uint64) bool { // Do we have a contract hash already? // If we do have a hash, that means it's a 'regular' contract. For regular // contracts ( not temporary initcode), we store the analysis in a map - if c.CodeHash != (common.Hash{}) { + if c.CodeHash != (common.Hash{}) && c.CodeHash != emptyCodeHash { // Does parent context have the analysis? analysis, exist := c.jumpdests[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) + analysis = codeBitmapWithCache(c.Code, c.CodeHash) c.jumpdests[c.CodeHash] = analysis } // Also stash it in current contract for faster access