mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #13682 from ethereum/improve_cse
Use hash of candidates for CSE.
This commit is contained in:
commit
1bd35b1be1
@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
#include <libyul/optimiser/BlockHasher.h>
|
||||
#include <libyul/optimiser/SyntacticalEquality.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/Utilities.h>
|
||||
|
||||
@ -195,3 +194,35 @@ void BlockHasher::operator()(Block const& _block)
|
||||
for (auto& externalReference: subBlockHasher.m_externalReferences)
|
||||
(*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
|
||||
{
|
||||
|
||||
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.
|
||||
* Syntactically equal blocks will have identical hashes and
|
||||
@ -41,7 +73,7 @@ namespace solidity::yul
|
||||
*
|
||||
* Prerequisite: Disambiguator, ForLoopInitRewriter
|
||||
*/
|
||||
class BlockHasher: public ASTWalker
|
||||
class BlockHasher: public ASTWalker, public HasherBase
|
||||
{
|
||||
public:
|
||||
|
||||
@ -64,36 +96,12 @@ public:
|
||||
|
||||
static std::map<Block const*, uint64_t> run(Block const& _block);
|
||||
|
||||
static constexpr uint64_t fnvPrime = 1099511628211u;
|
||||
static constexpr uint64_t fnvEmptyHash = 14695981039346656037u;
|
||||
|
||||
private:
|
||||
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;
|
||||
|
||||
uint64_t m_hash = fnvEmptyHash;
|
||||
struct VariableReference
|
||||
{
|
||||
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/SyntacticalEquality.h>
|
||||
#include <libyul/optimiser/BlockHasher.h>
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/SideEffects.h>
|
||||
@ -55,6 +56,7 @@ CommonSubexpressionEliminator::CommonSubexpressionEliminator(
|
||||
void CommonSubexpressionEliminator::operator()(FunctionDefinition& _fun)
|
||||
{
|
||||
ScopedSaveAndRestore returnVariables(m_returnVariables, {});
|
||||
ScopedSaveAndRestore replacementCandidates(m_replacementCandidates, {});
|
||||
|
||||
for (auto const& v: _fun.returnVariables)
|
||||
m_returnVariables.insert(v.name);
|
||||
@ -103,25 +105,31 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
|
||||
_e = Identifier{debugDataOf(_e), value->name};
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (auto const* candidates = util::valueOrNullptr(m_replacementCandidates, _e))
|
||||
for (auto const& variable: *candidates)
|
||||
if (AssignedValue const* value = variableValue(variable))
|
||||
{
|
||||
// TODO this search is rather inefficient.
|
||||
for (auto const& [variable, value]: allValues())
|
||||
{
|
||||
assertThrow(value.value, OptimizerException, "");
|
||||
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
|
||||
holds_alternative<Literal>(*value->value) &&
|
||||
valueOfLiteral(get<Literal>(*value->value)) == 0
|
||||
)
|
||||
continue;
|
||||
if (SyntacticallyEqual{}(_e, *value.value) && inScope(variable))
|
||||
// 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/OptimiserStep.h>
|
||||
#include <libyul/optimiser/SyntacticalEquality.h>
|
||||
#include <libyul/optimiser/BlockHasher.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
@ -58,8 +60,16 @@ protected:
|
||||
using ASTModifier::visit;
|
||||
void visit(Expression& _e) override;
|
||||
|
||||
void assignValue(YulString _variable, Expression const* _value) override;
|
||||
private:
|
||||
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.
|
||||
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.
|
||||
void clearKnowledgeIfInvalidated(Block const& _block);
|
||||
|
@ -165,3 +165,9 @@ bool SyntacticallyEqual::visitDeclaration(TypedName const& _lhs, TypedName const
|
||||
m_identifiersRHS[_rhs.name] = id;
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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