mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Use hash of candidates for CSE.
This commit is contained in:
parent
5c139b60b8
commit
40342264c2
@ -20,7 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libyul/optimiser/BlockHasher.h>
|
#include <libyul/optimiser/BlockHasher.h>
|
||||||
#include <libyul/optimiser/SyntacticalEquality.h>
|
|
||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
#include <libyul/Utilities.h>
|
#include <libyul/Utilities.h>
|
||||||
|
|
||||||
@ -195,3 +194,35 @@ void BlockHasher::operator()(Block const& _block)
|
|||||||
for (auto& externalReference: subBlockHasher.m_externalReferences)
|
for (auto& externalReference: subBlockHasher.m_externalReferences)
|
||||||
(*this)(Identifier{{}, externalReference});
|
(*this)(Identifier{{}, externalReference});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t ExpressionHasher::run(Expression const& _e)
|
||||||
|
{
|
||||||
|
ExpressionHasher expressionHasher;
|
||||||
|
expressionHasher.visit(_e);
|
||||||
|
return expressionHasher.m_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpressionHasher::operator()(Literal const& _literal)
|
||||||
|
{
|
||||||
|
hash64(compileTimeLiteralHash("Literal"));
|
||||||
|
if (_literal.kind == LiteralKind::Number)
|
||||||
|
hash64(std::hash<u256>{}(valueOfNumberLiteral(_literal)));
|
||||||
|
else
|
||||||
|
hash64(_literal.value.hash());
|
||||||
|
hash64(_literal.type.hash());
|
||||||
|
hash8(static_cast<uint8_t>(_literal.kind));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpressionHasher::operator()(Identifier const& _identifier)
|
||||||
|
{
|
||||||
|
hash64(compileTimeLiteralHash("Identifier"));
|
||||||
|
hash64(_identifier.name.hash());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpressionHasher::operator()(FunctionCall const& _funCall)
|
||||||
|
{
|
||||||
|
hash64(compileTimeLiteralHash("FunctionCall"));
|
||||||
|
hash64(_funCall.functionName.name.hash());
|
||||||
|
hash64(_funCall.arguments.size());
|
||||||
|
ASTWalker::operator()(_funCall);
|
||||||
|
}
|
||||||
|
@ -27,6 +27,38 @@
|
|||||||
namespace solidity::yul
|
namespace solidity::yul
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class HasherBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr uint64_t fnvPrime = 1099511628211u;
|
||||||
|
static constexpr uint64_t fnvEmptyHash = 14695981039346656037u;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void hash8(uint8_t _value)
|
||||||
|
{
|
||||||
|
m_hash *= fnvPrime;
|
||||||
|
m_hash ^= _value;
|
||||||
|
}
|
||||||
|
void hash16(uint16_t _value)
|
||||||
|
{
|
||||||
|
hash8(static_cast<uint8_t>(_value & 0xFF));
|
||||||
|
hash8(static_cast<uint8_t>(_value >> 8));
|
||||||
|
}
|
||||||
|
void hash32(uint32_t _value)
|
||||||
|
{
|
||||||
|
hash16(static_cast<uint16_t>(_value & 0xFFFF));
|
||||||
|
hash16(static_cast<uint16_t>(_value >> 16));
|
||||||
|
}
|
||||||
|
void hash64(uint64_t _value)
|
||||||
|
{
|
||||||
|
hash32(static_cast<uint32_t>(_value & 0xFFFFFFFF));
|
||||||
|
hash32(static_cast<uint32_t>(_value >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t m_hash = fnvEmptyHash;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optimiser component that calculates hash values for blocks.
|
* Optimiser component that calculates hash values for blocks.
|
||||||
* Syntactically equal blocks will have identical hashes and
|
* Syntactically equal blocks will have identical hashes and
|
||||||
@ -41,7 +73,7 @@ namespace solidity::yul
|
|||||||
*
|
*
|
||||||
* Prerequisite: Disambiguator, ForLoopInitRewriter
|
* Prerequisite: Disambiguator, ForLoopInitRewriter
|
||||||
*/
|
*/
|
||||||
class BlockHasher: public ASTWalker
|
class BlockHasher: public ASTWalker, public HasherBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -64,36 +96,12 @@ public:
|
|||||||
|
|
||||||
static std::map<Block const*, uint64_t> run(Block const& _block);
|
static std::map<Block const*, uint64_t> run(Block const& _block);
|
||||||
|
|
||||||
static constexpr uint64_t fnvPrime = 1099511628211u;
|
|
||||||
static constexpr uint64_t fnvEmptyHash = 14695981039346656037u;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BlockHasher(std::map<Block const*, uint64_t>& _blockHashes): m_blockHashes(_blockHashes) {}
|
BlockHasher(std::map<Block const*, uint64_t>& _blockHashes): m_blockHashes(_blockHashes) {}
|
||||||
|
|
||||||
void hash8(uint8_t _value)
|
|
||||||
{
|
|
||||||
m_hash *= fnvPrime;
|
|
||||||
m_hash ^= _value;
|
|
||||||
}
|
|
||||||
void hash16(uint16_t _value)
|
|
||||||
{
|
|
||||||
hash8(static_cast<uint8_t>(_value & 0xFF));
|
|
||||||
hash8(static_cast<uint8_t>(_value >> 8));
|
|
||||||
}
|
|
||||||
void hash32(uint32_t _value)
|
|
||||||
{
|
|
||||||
hash16(static_cast<uint16_t>(_value & 0xFFFF));
|
|
||||||
hash16(static_cast<uint16_t>(_value >> 16));
|
|
||||||
}
|
|
||||||
void hash64(uint64_t _value)
|
|
||||||
{
|
|
||||||
hash32(static_cast<uint32_t>(_value & 0xFFFFFFFF));
|
|
||||||
hash32(static_cast<uint32_t>(_value >> 32));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<Block const*, uint64_t>& m_blockHashes;
|
std::map<Block const*, uint64_t>& m_blockHashes;
|
||||||
|
|
||||||
uint64_t m_hash = fnvEmptyHash;
|
|
||||||
struct VariableReference
|
struct VariableReference
|
||||||
{
|
{
|
||||||
size_t id = 0;
|
size_t id = 0;
|
||||||
@ -106,4 +114,32 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes hashes of expressions that are likely different for syntactically different expressions.
|
||||||
|
* In contrast to the BlockHasher, hashes of identifiers are likely different if the identifiers
|
||||||
|
* have a different name and the same if the name matches.
|
||||||
|
* This means this hasher should only be used on disambiguated sources.
|
||||||
|
*/
|
||||||
|
class ExpressionHasher: public ASTWalker, public HasherBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Computes a hash of an expression that (in contrast to the behaviour of the class)
|
||||||
|
/// distinguishes (up to hash collisions) variables with different names.
|
||||||
|
static uint64_t run(Expression const& _e);
|
||||||
|
|
||||||
|
using ASTWalker::operator();
|
||||||
|
|
||||||
|
void operator()(Literal const&) override;
|
||||||
|
void operator()(Identifier const&) override;
|
||||||
|
void operator()(FunctionCall const& _funCall) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExpressionHash
|
||||||
|
{
|
||||||
|
uint64_t operator()(Expression const& _expression) const
|
||||||
|
{
|
||||||
|
return ExpressionHasher::run(_expression);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
|
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
|
||||||
|
|
||||||
#include <libyul/optimiser/SyntacticalEquality.h>
|
#include <libyul/optimiser/SyntacticalEquality.h>
|
||||||
|
#include <libyul/optimiser/BlockHasher.h>
|
||||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||||
#include <libyul/optimiser/Semantics.h>
|
#include <libyul/optimiser/Semantics.h>
|
||||||
#include <libyul/SideEffects.h>
|
#include <libyul/SideEffects.h>
|
||||||
@ -55,6 +56,7 @@ CommonSubexpressionEliminator::CommonSubexpressionEliminator(
|
|||||||
void CommonSubexpressionEliminator::operator()(FunctionDefinition& _fun)
|
void CommonSubexpressionEliminator::operator()(FunctionDefinition& _fun)
|
||||||
{
|
{
|
||||||
ScopedSaveAndRestore returnVariables(m_returnVariables, {});
|
ScopedSaveAndRestore returnVariables(m_returnVariables, {});
|
||||||
|
ScopedSaveAndRestore replacementCandidates(m_replacementCandidates, {});
|
||||||
|
|
||||||
for (auto const& v: _fun.returnVariables)
|
for (auto const& v: _fun.returnVariables)
|
||||||
m_returnVariables.insert(v.name);
|
m_returnVariables.insert(v.name);
|
||||||
@ -103,25 +105,31 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
|
|||||||
_e = Identifier{debugDataOf(_e), value->name};
|
_e = Identifier{debugDataOf(_e), value->name};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (auto const* candidates = util::valueOrNullptr(m_replacementCandidates, _e))
|
||||||
{
|
for (auto const& variable: *candidates)
|
||||||
// TODO this search is rather inefficient.
|
if (AssignedValue const* value = variableValue(variable))
|
||||||
for (auto const& [variable, value]: allValues())
|
|
||||||
{
|
|
||||||
assertThrow(value.value, OptimizerException, "");
|
|
||||||
// Prevent using the default value of return variables
|
|
||||||
// instead of literal zeros.
|
|
||||||
if (
|
|
||||||
m_returnVariables.count(variable) &&
|
|
||||||
holds_alternative<Literal>(*value.value) &&
|
|
||||||
valueOfLiteral(get<Literal>(*value.value)) == 0
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
if (SyntacticallyEqual{}(_e, *value.value) && inScope(variable))
|
|
||||||
{
|
{
|
||||||
_e = Identifier{debugDataOf(_e), variable};
|
assertThrow(value->value, OptimizerException, "");
|
||||||
break;
|
// Prevent using the default value of return variables
|
||||||
|
// instead of literal zeros.
|
||||||
|
if (
|
||||||
|
m_returnVariables.count(variable) &&
|
||||||
|
holds_alternative<Literal>(*value->value) &&
|
||||||
|
valueOfLiteral(get<Literal>(*value->value)) == 0
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
// We check for syntactic equality again because the value might have changed.
|
||||||
|
if (inScope(variable) && SyntacticallyEqual{}(_e, *value->value))
|
||||||
|
{
|
||||||
|
_e = Identifier{debugDataOf(_e), variable};
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
void CommonSubexpressionEliminator::assignValue(YulString _variable, Expression const* _value)
|
||||||
|
{
|
||||||
|
if (_value)
|
||||||
|
m_replacementCandidates[*_value].insert(_variable);
|
||||||
|
DataFlowAnalyzer::assignValue(_variable, _value);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include <libyul/optimiser/DataFlowAnalyzer.h>
|
#include <libyul/optimiser/DataFlowAnalyzer.h>
|
||||||
#include <libyul/optimiser/OptimiserStep.h>
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
#include <libyul/optimiser/SyntacticalEquality.h>
|
||||||
|
#include <libyul/optimiser/BlockHasher.h>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
@ -58,8 +60,16 @@ protected:
|
|||||||
using ASTModifier::visit;
|
using ASTModifier::visit;
|
||||||
void visit(Expression& _e) override;
|
void visit(Expression& _e) override;
|
||||||
|
|
||||||
|
void assignValue(YulString _variable, Expression const* _value) override;
|
||||||
private:
|
private:
|
||||||
std::set<YulString> m_returnVariables;
|
std::set<YulString> m_returnVariables;
|
||||||
|
std::unordered_map<
|
||||||
|
std::reference_wrapper<Expression const>,
|
||||||
|
std::set<YulString>,
|
||||||
|
ExpressionHash,
|
||||||
|
SyntacticallyEqualExpression
|
||||||
|
> m_replacementCandidates;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ protected:
|
|||||||
/// for example at points where control flow is merged.
|
/// for example at points where control flow is merged.
|
||||||
void clearValues(std::set<YulString> _names);
|
void clearValues(std::set<YulString> _names);
|
||||||
|
|
||||||
void assignValue(YulString _variable, Expression const* _value);
|
virtual void assignValue(YulString _variable, Expression const* _value);
|
||||||
|
|
||||||
/// Clears knowledge about storage or memory if they may be modified inside the block.
|
/// Clears knowledge about storage or memory if they may be modified inside the block.
|
||||||
void clearKnowledgeIfInvalidated(Block const& _block);
|
void clearKnowledgeIfInvalidated(Block const& _block);
|
||||||
|
@ -165,3 +165,9 @@ bool SyntacticallyEqual::visitDeclaration(TypedName const& _lhs, TypedName const
|
|||||||
m_identifiersRHS[_rhs.name] = id;
|
m_identifiersRHS[_rhs.name] = id;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SyntacticallyEqualExpression::operator()(Expression const& _lhs, Expression const& _rhs) const
|
||||||
|
{
|
||||||
|
return SyntacticallyEqual{}(_lhs, _rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -85,4 +85,13 @@ private:
|
|||||||
std::map<YulString, std::size_t> m_identifiersRHS;
|
std::map<YulString, std::size_t> m_identifiersRHS;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the same as SyntacticallyEqual just that the operator() function is const.
|
||||||
|
*/
|
||||||
|
struct SyntacticallyEqualExpression
|
||||||
|
{
|
||||||
|
bool operator()(Expression const& _lhs, Expression const& _rhs) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user