@ -23,6 +23,7 @@
# include <functional>
# include <boost/range/adaptor/reversed.hpp>
# include <libdevcrypto/SHA3.h>
# include <libevmcore/CommonSubexpressionEliminator.h>
# include <libevmcore/AssemblyItem.h>
@ -34,8 +35,8 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{
optimizeBreakingItem ( ) ;
map < int , ExpressionClasses : : Id > initialStackContents ;
map < int , ExpressionClasses : : Id > targetStackContents ;
map < int , Id > initialStackContents ;
map < int , Id > targetStackContents ;
int minHeight = m_stackHeight + 1 ;
if ( ! m_stackElements . empty ( ) )
minHeight = min ( minHeight , m_stackElements . begin ( ) - > first ) ;
@ -58,18 +59,18 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
ostream & CommonSubexpressionEliminator : : stream (
ostream & _out ,
map < int , ExpressionClasses : : Id > _initialStack ,
map < int , ExpressionClasses : : Id > _targetStack
map < int , Id > _initialStack ,
map < int , Id > _targetStack
) const
{
auto streamExpressionClass = [ this ] ( ostream & _out , ExpressionClasses : : Id _id )
auto streamExpressionClass = [ this ] ( ostream & _out , Id _id )
{
auto const & expr = m_expressionClasses . representative ( _id ) ;
_out < < " " < < dec < < _id < < " : " < < * expr . item ;
if ( expr . sequenceNumber )
_out < < " @ " < < dec < < expr . sequenceNumber ;
_out < < " ( " ;
for ( ExpressionClasses : : Id arg : expr . arguments )
for ( Id arg : expr . arguments )
_out < < dec < < arg < < " , " ;
_out < < " ) " < < endl ;
} ;
@ -77,7 +78,7 @@ ostream& CommonSubexpressionEliminator::stream(
_out < < " Optimizer analysis: " < < endl ;
_out < < " Final stack height: " < < dec < < m_stackHeight < < endl ;
_out < < " Equivalence classes: " < < endl ;
for ( ExpressionClasses : : Id eqClass = 0 ; eqClass < m_expressionClasses . size ( ) ; + + eqClass )
for ( Id eqClass = 0 ; eqClass < m_expressionClasses . size ( ) ; + + eqClass )
streamExpressionClass ( _out , eqClass ) ;
_out < < " Initial stack: " < < endl ;
@ -119,7 +120,7 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
) ;
else if ( instruction ! = Instruction : : POP )
{
vector < ExpressionClasses : : Id > arguments ( info . args ) ;
vector < Id > arguments ( info . args ) ;
for ( int i = 0 ; i < info . args ; + + i )
arguments [ i ] = stackElement ( m_stackHeight - i ) ;
if ( _item . instruction ( ) = = Instruction : : SSTORE )
@ -130,6 +131,8 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _co
storeInMemory ( arguments [ 0 ] , arguments [ 1 ] ) ;
else if ( _item . instruction ( ) = = Instruction : : MLOAD )
setStackElement ( m_stackHeight + _item . deposit ( ) , loadFromMemory ( arguments [ 0 ] ) ) ;
else if ( _item . instruction ( ) = = Instruction : : SHA3 )
setStackElement ( m_stackHeight + _item . deposit ( ) , applySha3 ( arguments . at ( 0 ) , arguments . at ( 1 ) ) ) ;
else
setStackElement ( m_stackHeight + _item . deposit ( ) , m_expressionClasses . find ( _item , arguments , _copyItem ) ) ;
}
@ -142,7 +145,6 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
if ( ! m_breakingItem | | * m_breakingItem ! = AssemblyItem ( Instruction : : JUMPI ) )
return ;
using Id = ExpressionClasses : : Id ;
static AssemblyItem s_jump = Instruction : : JUMP ;
Id condition = stackElement ( m_stackHeight - 1 ) ;
@ -163,7 +165,7 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
}
}
void CommonSubexpressionEliminator : : setStackElement ( int _stackHeight , ExpressionClasses : : Id _class )
void CommonSubexpressionEliminator : : setStackElement ( int _stackHeight , Id _class )
{
m_stackElements [ _stackHeight ] = _class ;
}
@ -194,26 +196,28 @@ ExpressionClasses::Id CommonSubexpressionEliminator::initialStackElement(int _st
return m_expressionClasses . find ( AssemblyItem ( dupInstruction ( 1 - _stackHeight ) ) ) ;
}
void CommonSubexpressionEliminator : : storeInStorage ( ExpressionClasses : : Id _slot , ExpressionClasses : : Id _value )
void CommonSubexpressionEliminator : : storeInStorage ( Id _slot , Id _value )
{
if ( m_storageContent . count ( _slot ) & & m_storageContent [ _slot ] = = _value )
// do not execute the storage if we know that the value is already there
return ;
m_sequenceNumber + + ;
decltype ( m_storageContent ) storageContents ;
// copy over values at points where we know that they are different from _slot
// Copy over all values (i.e. retain knowledge about them) where we know that this store
// operation will not destroy the knowledge. Specifically, we copy storage locations we know
// are different from _slot or locations where we know that the stored value is equal to _value.
for ( auto const & storageItem : m_storageContent )
if ( m_expressionClasses . knownToBeDifferent ( storageItem . first , _slot ) )
if ( m_expressionClasses . knownToBeDifferent ( storageItem . first , _slot ) | | storageItem . second = = _value )
storageContents . insert ( storageItem ) ;
m_storageContent = move ( storageContents ) ;
ExpressionClasses : : Id id = m_expressionClasses . find ( Instruction : : SSTORE , { _slot , _value } , true , m_sequenceNumber ) ;
Id id = m_expressionClasses . find ( Instruction : : SSTORE , { _slot , _value } , true , m_sequenceNumber ) ;
m_storeOperations . push_back ( StoreOperation ( StoreOperation : : Storage , _slot , m_sequenceNumber , id ) ) ;
m_storageContent [ _slot ] = _value ;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber + + ;
}
ExpressionClasses : : Id CommonSubexpressionEliminator : : loadFromStorage ( ExpressionClasses : : Id _slot )
ExpressionClasses : : Id CommonSubexpressionEliminator : : loadFromStorage ( Id _slot )
{
if ( m_storageContent . count ( _slot ) )
return m_storageContent . at ( _slot ) ;
@ -221,7 +225,7 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromStorage(ExpressionC
return m_storageContent [ _slot ] = m_expressionClasses . find ( Instruction : : SLOAD , { _slot } , true , m_sequenceNumber ) ;
}
void CommonSubexpressionEliminator : : storeInMemory ( ExpressionClasses : : Id _slot , ExpressionClasses : : Id _value )
void CommonSubexpressionEliminator : : storeInMemory ( Id _slot , Id _value )
{
if ( m_memoryContent . count ( _slot ) & & m_memoryContent [ _slot ] = = _value )
// do not execute the store if we know that the value is already there
@ -233,14 +237,14 @@ void CommonSubexpressionEliminator::storeInMemory(ExpressionClasses::Id _slot, E
if ( m_expressionClasses . knownToBeDifferentBy32 ( memoryItem . first , _slot ) )
memoryContents . insert ( memoryItem ) ;
m_memoryContent = move ( memoryContents ) ;
ExpressionClasses : : Id id = m_expressionClasses . find ( Instruction : : MSTORE , { _slot , _value } , true , m_sequenceNumber ) ;
Id id = m_expressionClasses . find ( Instruction : : MSTORE , { _slot , _value } , true , m_sequenceNumber ) ;
m_storeOperations . push_back ( StoreOperation ( StoreOperation : : Memory , _slot , m_sequenceNumber , id ) ) ;
m_memoryContent [ _slot ] = _value ;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber + + ;
}
ExpressionClasses : : Id CommonSubexpressionEliminator : : loadFromMemory ( ExpressionClasses : : Id _slot )
ExpressionClasses : : Id CommonSubexpressionEliminator : : loadFromMemory ( Id _slot )
{
if ( m_memoryContent . count ( _slot ) )
return m_memoryContent . at ( _slot ) ;
@ -248,6 +252,37 @@ ExpressionClasses::Id CommonSubexpressionEliminator::loadFromMemory(ExpressionCl
return m_memoryContent [ _slot ] = m_expressionClasses . find ( Instruction : : MLOAD , { _slot } , true , m_sequenceNumber ) ;
}
CommonSubexpressionEliminator : : Id CommonSubexpressionEliminator : : applySha3 ( Id _start , Id _length )
{
// Special logic if length is a short constant, otherwise we cannot tell.
u256 const * l = m_expressionClasses . knownConstant ( _length ) ;
// unknown or too large length
if ( ! l | | * l > 128 )
return m_expressionClasses . find ( Instruction : : SHA3 , { _start , _length } , true , m_sequenceNumber ) ;
vector < Id > arguments ;
for ( u256 i = 0 ; i < * l ; i + = 32 )
{
Id slot = m_expressionClasses . find ( Instruction : : ADD , { _start , m_expressionClasses . find ( i ) } ) ;
arguments . push_back ( loadFromMemory ( slot ) ) ;
}
if ( m_knownSha3Hashes . count ( arguments ) )
return m_knownSha3Hashes . at ( arguments ) ;
Id v ;
// If all arguments are known constants, compute the sha3 here
if ( all_of ( arguments . begin ( ) , arguments . end ( ) , [ this ] ( Id _a ) { return ! ! m_expressionClasses . knownConstant ( _a ) ; } ) )
{
bytes data ;
for ( Id a : arguments )
data + = toBigEndian ( * m_expressionClasses . knownConstant ( a ) ) ;
data . resize ( size_t ( * l ) ) ;
v = m_expressionClasses . find ( u256 ( sha3 ( data ) ) ) ;
}
else
v = m_expressionClasses . find ( Instruction : : SHA3 , { _start , _length } , true , m_sequenceNumber ) ;
return m_knownSha3Hashes [ arguments ] = v ;
}
CSECodeGenerator : : CSECodeGenerator (
ExpressionClasses & _expressionClasses ,
vector < CSECodeGenerator : : StoreOperation > const & _storeOperations
@ -259,8 +294,8 @@ CSECodeGenerator::CSECodeGenerator(
}
AssemblyItems CSECodeGenerator : : generateCode (
map < int , ExpressionClasses : : Id > const & _initialStack ,
map < int , ExpressionClasses : : Id > const & _targetStackContents
map < int , Id > const & _initialStack ,
map < int , Id > const & _targetStackContents
)
{
m_stack = _initialStack ;
@ -280,7 +315,7 @@ AssemblyItems CSECodeGenerator::generateCode(
}
// store all needed sequenced expressions
set < pair < unsigned , ExpressionClasses : : Id > > sequencedExpressions ;
set < pair < unsigned , Id > > sequencedExpressions ;
for ( auto const & p : m_neededBy )
for ( auto id : { p . first , p . second } )
if ( unsigned seqNr = m_expressionClasses . representative ( id ) . sequenceNumber )
@ -327,19 +362,20 @@ AssemblyItems CSECodeGenerator::generateCode(
return m_generatedItems ;
}
void CSECodeGenerator : : addDependencies ( ExpressionClasses : : Id _c )
void CSECodeGenerator : : addDependencies ( Id _c )
{
if ( m_neededBy . count ( _c ) )
return ; // we already computed the dependencies for _c
ExpressionClasses : : Expression expr = m_expressionClasses . representative ( _c ) ;
for ( ExpressionClasses : : Id argument : expr . arguments )
for ( Id argument : expr . arguments )
{
addDependencies ( argument ) ;
m_neededBy . insert ( make_pair ( argument , _c ) ) ;
}
if ( expr . item - > type ( ) = = Operation & & (
expr . item - > instruction ( ) = = Instruction : : SLOAD | |
expr . item - > instruction ( ) = = Instruction : : MLOAD
expr . item - > instruction ( ) = = Instruction : : MLOAD | |
expr . item - > instruction ( ) = = Instruction : : SHA3
) )
{
// this loads an unknown value from storage or memory and thus, in addition to its
@ -347,22 +383,52 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
// they are different that occur before this load
StoreOperation : : Target target = expr . item - > instruction ( ) = = Instruction : : SLOAD ?
StoreOperation : : Storage : StoreOperation : : Memory ;
ExpressionClasses : : Id slotToLoadFrom = expr . arguments . at ( 0 ) ;
Id slotToLoadFrom = expr . arguments . at ( 0 ) ;
for ( auto const & p : m_storeOperations )
{
if ( p . first . first ! = target )
continue ;
ExpressionClasses : : Id slot = p . first . second ;
Id slot = p . first . second ;
StoreOperations const & storeOps = p . second ;
if ( storeOps . front ( ) . sequenceNumber > expr . sequenceNumber )
continue ;
if (
( target = = StoreOperation : : Memory & & m_expressionClasses . knownToBeDifferentBy32 ( slot , slotToLoadFrom ) ) | |
( target = = StoreOperation : : Storage & & m_expressionClasses . knownToBeDifferent ( slot , slotToLoadFrom ) )
)
bool knownToBeIndependent = false ;
switch ( expr . item - > instruction ( ) )
{
case Instruction : : SLOAD :
knownToBeIndependent = m_expressionClasses . knownToBeDifferent ( slot , slotToLoadFrom ) ;
break ;
case Instruction : : MLOAD :
knownToBeIndependent = m_expressionClasses . knownToBeDifferentBy32 ( slot , slotToLoadFrom ) ;
break ;
case Instruction : : SHA3 :
{
Id length = expr . arguments . at ( 1 ) ;
Id offsetToStart = m_expressionClasses . find ( Instruction : : SUB , { slot , slotToLoadFrom } ) ;
u256 const * o = m_expressionClasses . knownConstant ( offsetToStart ) ;
u256 const * l = m_expressionClasses . knownConstant ( length ) ;
if ( l & & * l = = 0 )
knownToBeIndependent = true ;
else if ( o )
{
// We could get problems here if both *o and *l are larger than 2**254
// but it is probably ok for the optimizer to produce wrong code for such cases
// which cannot be executed anyway because of the non-payable price.
if ( u2s ( * o ) < = - 32 )
knownToBeIndependent = true ;
else if ( l & & u2s ( * o ) > = 0 & & * o > = * l )
knownToBeIndependent = true ;
}
break ;
}
default :
break ;
}
if ( knownToBeIndependent )
continue ;
// note that store and load never have the same sequence number
ExpressionClasses : : Id latestStore = storeOps . front ( ) . expression ;
Id latestStore = storeOps . front ( ) . expression ;
for ( auto it = + + storeOps . begin ( ) ; it ! = storeOps . end ( ) ; + + it )
if ( it - > sequenceNumber < expr . sequenceNumber )
latestStore = it - > expression ;
@ -372,7 +438,7 @@ void CSECodeGenerator::addDependencies(ExpressionClasses::Id _c)
}
}
int CSECodeGenerator : : generateClassElement ( ExpressionClasses : : Id _c , bool _allowSequenced )
int CSECodeGenerator : : generateClassElement ( Id _c , bool _allowSequenced )
{
// do some cleanup
removeStackTopIfPossible ( ) ;
@ -392,8 +458,8 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allow
OptimizerException ,
" Sequence constrained operation requested out of sequence. "
) ;
ExpressionClasses : : Ids const & arguments = expr . arguments ;
for ( ExpressionClasses : : Id arg : boost : : adaptors : : reverse ( arguments ) )
vector < Id > const & arguments = expr . arguments ;
for ( Id arg : boost : : adaptors : : reverse ( arguments ) )
generateClassElement ( arg ) ;
// The arguments are somewhere on the stack now, so it remains to move them at the correct place.
@ -478,7 +544,7 @@ int CSECodeGenerator::generateClassElement(ExpressionClasses::Id _c, bool _allow
}
}
int CSECodeGenerator : : classElementPosition ( ExpressionClasses : : Id _id ) const
int CSECodeGenerator : : classElementPosition ( Id _id ) const
{
assertThrow (
m_classPositions . count ( _id ) & & m_classPositions . at ( _id ) ! = c_invalidPosition ,
@ -488,7 +554,7 @@ int CSECodeGenerator::classElementPosition(ExpressionClasses::Id _id) const
return m_classPositions . at ( _id ) ;
}
bool CSECodeGenerator : : canBeRemoved ( ExpressionClasses : : Id _element , ExpressionClasses : : Id _result )
bool CSECodeGenerator : : canBeRemoved ( Id _element , Id _result )
{
// Returns false if _element is finally needed or is needed by a class that has not been
// computed yet. Note that m_classPositions also includes classes that were deleted in the meantime.
@ -506,8 +572,8 @@ bool CSECodeGenerator::removeStackTopIfPossible()
{
if ( m_stack . empty ( ) )
return false ;
assertThrow ( m_stack . count ( m_stackHeight ) , OptimizerException , " " ) ;
ExpressionClasses : : Id top = m_stack [ m_stackHeight ] ;
assertThrow ( m_stack . count ( m_stackHeight ) > 0 , OptimizerException , " " ) ;
Id top = m_stack [ m_stackHeight ] ;
if ( ! canBeRemoved ( top ) )
return false ;
m_generatedItems . push_back ( AssemblyItem ( Instruction : : POP ) ) ;