Skip to content

Commit 4034226

Browse files
committed
Use hash of candidates for CSE.
1 parent 5c139b6 commit 4034226

File tree

7 files changed

+146
-46
lines changed

7 files changed

+146
-46
lines changed

libyul/optimiser/BlockHasher.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
*/
2121

2222
#include <libyul/optimiser/BlockHasher.h>
23-
#include <libyul/optimiser/SyntacticalEquality.h>
2423
#include <libyul/AST.h>
2524
#include <libyul/Utilities.h>
2625

@@ -195,3 +194,35 @@ void BlockHasher::operator()(Block const& _block)
195194
for (auto& externalReference: subBlockHasher.m_externalReferences)
196195
(*this)(Identifier{{}, externalReference});
197196
}
197+
198+
uint64_t ExpressionHasher::run(Expression const& _e)
199+
{
200+
ExpressionHasher expressionHasher;
201+
expressionHasher.visit(_e);
202+
return expressionHasher.m_hash;
203+
}
204+
205+
void ExpressionHasher::operator()(Literal const& _literal)
206+
{
207+
hash64(compileTimeLiteralHash("Literal"));
208+
if (_literal.kind == LiteralKind::Number)
209+
hash64(std::hash<u256>{}(valueOfNumberLiteral(_literal)));
210+
else
211+
hash64(_literal.value.hash());
212+
hash64(_literal.type.hash());
213+
hash8(static_cast<uint8_t>(_literal.kind));
214+
}
215+
216+
void ExpressionHasher::operator()(Identifier const& _identifier)
217+
{
218+
hash64(compileTimeLiteralHash("Identifier"));
219+
hash64(_identifier.name.hash());
220+
}
221+
222+
void ExpressionHasher::operator()(FunctionCall const& _funCall)
223+
{
224+
hash64(compileTimeLiteralHash("FunctionCall"));
225+
hash64(_funCall.functionName.name.hash());
226+
hash64(_funCall.arguments.size());
227+
ASTWalker::operator()(_funCall);
228+
}

libyul/optimiser/BlockHasher.h

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,38 @@
2727
namespace solidity::yul
2828
{
2929

30+
class HasherBase
31+
{
32+
public:
33+
static constexpr uint64_t fnvPrime = 1099511628211u;
34+
static constexpr uint64_t fnvEmptyHash = 14695981039346656037u;
35+
36+
protected:
37+
void hash8(uint8_t _value)
38+
{
39+
m_hash *= fnvPrime;
40+
m_hash ^= _value;
41+
}
42+
void hash16(uint16_t _value)
43+
{
44+
hash8(static_cast<uint8_t>(_value & 0xFF));
45+
hash8(static_cast<uint8_t>(_value >> 8));
46+
}
47+
void hash32(uint32_t _value)
48+
{
49+
hash16(static_cast<uint16_t>(_value & 0xFFFF));
50+
hash16(static_cast<uint16_t>(_value >> 16));
51+
}
52+
void hash64(uint64_t _value)
53+
{
54+
hash32(static_cast<uint32_t>(_value & 0xFFFFFFFF));
55+
hash32(static_cast<uint32_t>(_value >> 32));
56+
}
57+
58+
59+
uint64_t m_hash = fnvEmptyHash;
60+
};
61+
3062
/**
3163
* Optimiser component that calculates hash values for blocks.
3264
* Syntactically equal blocks will have identical hashes and
@@ -41,7 +73,7 @@ namespace solidity::yul
4173
*
4274
* Prerequisite: Disambiguator, ForLoopInitRewriter
4375
*/
44-
class BlockHasher: public ASTWalker
76+
class BlockHasher: public ASTWalker, public HasherBase
4577
{
4678
public:
4779

@@ -64,36 +96,12 @@ class BlockHasher: public ASTWalker
6496

6597
static std::map<Block const*, uint64_t> run(Block const& _block);
6698

67-
static constexpr uint64_t fnvPrime = 1099511628211u;
68-
static constexpr uint64_t fnvEmptyHash = 14695981039346656037u;
6999

70100
private:
71101
BlockHasher(std::map<Block const*, uint64_t>& _blockHashes): m_blockHashes(_blockHashes) {}
72102

73-
void hash8(uint8_t _value)
74-
{
75-
m_hash *= fnvPrime;
76-
m_hash ^= _value;
77-
}
78-
void hash16(uint16_t _value)
79-
{
80-
hash8(static_cast<uint8_t>(_value & 0xFF));
81-
hash8(static_cast<uint8_t>(_value >> 8));
82-
}
83-
void hash32(uint32_t _value)
84-
{
85-
hash16(static_cast<uint16_t>(_value & 0xFFFF));
86-
hash16(static_cast<uint16_t>(_value >> 16));
87-
}
88-
void hash64(uint64_t _value)
89-
{
90-
hash32(static_cast<uint32_t>(_value & 0xFFFFFFFF));
91-
hash32(static_cast<uint32_t>(_value >> 32));
92-
}
93-
94103
std::map<Block const*, uint64_t>& m_blockHashes;
95104

96-
uint64_t m_hash = fnvEmptyHash;
97105
struct VariableReference
98106
{
99107
size_t id = 0;
@@ -106,4 +114,32 @@ class BlockHasher: public ASTWalker
106114
};
107115

108116

117+
/**
118+
* Computes hashes of expressions that are likely different for syntactically different expressions.
119+
* In contrast to the BlockHasher, hashes of identifiers are likely different if the identifiers
120+
* have a different name and the same if the name matches.
121+
* This means this hasher should only be used on disambiguated sources.
122+
*/
123+
class ExpressionHasher: public ASTWalker, public HasherBase
124+
{
125+
public:
126+
/// Computes a hash of an expression that (in contrast to the behaviour of the class)
127+
/// distinguishes (up to hash collisions) variables with different names.
128+
static uint64_t run(Expression const& _e);
129+
130+
using ASTWalker::operator();
131+
132+
void operator()(Literal const&) override;
133+
void operator()(Identifier const&) override;
134+
void operator()(FunctionCall const& _funCall) override;
135+
};
136+
137+
struct ExpressionHash
138+
{
139+
uint64_t operator()(Expression const& _expression) const
140+
{
141+
return ExpressionHasher::run(_expression);
142+
}
143+
};
144+
109145
}

libyul/optimiser/CommonSubexpressionEliminator.cpp

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
2323

2424
#include <libyul/optimiser/SyntacticalEquality.h>
25+
#include <libyul/optimiser/BlockHasher.h>
2526
#include <libyul/optimiser/CallGraphGenerator.h>
2627
#include <libyul/optimiser/Semantics.h>
2728
#include <libyul/SideEffects.h>
@@ -55,6 +56,7 @@ CommonSubexpressionEliminator::CommonSubexpressionEliminator(
5556
void CommonSubexpressionEliminator::operator()(FunctionDefinition& _fun)
5657
{
5758
ScopedSaveAndRestore returnVariables(m_returnVariables, {});
59+
ScopedSaveAndRestore replacementCandidates(m_replacementCandidates, {});
5860

5961
for (auto const& v: _fun.returnVariables)
6062
m_returnVariables.insert(v.name);
@@ -103,25 +105,31 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
103105
_e = Identifier{debugDataOf(_e), value->name};
104106
}
105107
}
106-
else
107-
{
108-
// TODO this search is rather inefficient.
109-
for (auto const& [variable, value]: allValues())
110-
{
111-
assertThrow(value.value, OptimizerException, "");
112-
// Prevent using the default value of return variables
113-
// instead of literal zeros.
114-
if (
115-
m_returnVariables.count(variable) &&
116-
holds_alternative<Literal>(*value.value) &&
117-
valueOfLiteral(get<Literal>(*value.value)) == 0
118-
)
119-
continue;
120-
if (SyntacticallyEqual{}(_e, *value.value) && inScope(variable))
108+
else if (auto const* candidates = util::valueOrNullptr(m_replacementCandidates, _e))
109+
for (auto const& variable: *candidates)
110+
if (AssignedValue const* value = variableValue(variable))
121111
{
122-
_e = Identifier{debugDataOf(_e), variable};
123-
break;
112+
assertThrow(value->value, OptimizerException, "");
113+
// Prevent using the default value of return variables
114+
// instead of literal zeros.
115+
if (
116+
m_returnVariables.count(variable) &&
117+
holds_alternative<Literal>(*value->value) &&
118+
valueOfLiteral(get<Literal>(*value->value)) == 0
119+
)
120+
continue;
121+
// We check for syntactic equality again because the value might have changed.
122+
if (inScope(variable) && SyntacticallyEqual{}(_e, *value->value))
123+
{
124+
_e = Identifier{debugDataOf(_e), variable};
125+
break;
126+
}
124127
}
125-
}
126-
}
128+
}
129+
130+
void CommonSubexpressionEliminator::assignValue(YulString _variable, Expression const* _value)
131+
{
132+
if (_value)
133+
m_replacementCandidates[*_value].insert(_variable);
134+
DataFlowAnalyzer::assignValue(_variable, _value);
127135
}

libyul/optimiser/CommonSubexpressionEliminator.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
#include <libyul/optimiser/DataFlowAnalyzer.h>
2626
#include <libyul/optimiser/OptimiserStep.h>
27+
#include <libyul/optimiser/SyntacticalEquality.h>
28+
#include <libyul/optimiser/BlockHasher.h>
2729

2830
#include <set>
2931

@@ -58,8 +60,16 @@ class CommonSubexpressionEliminator: public DataFlowAnalyzer
5860
using ASTModifier::visit;
5961
void visit(Expression& _e) override;
6062

63+
void assignValue(YulString _variable, Expression const* _value) override;
6164
private:
6265
std::set<YulString> m_returnVariables;
66+
std::unordered_map<
67+
std::reference_wrapper<Expression const>,
68+
std::set<YulString>,
69+
ExpressionHash,
70+
SyntacticallyEqualExpression
71+
> m_replacementCandidates;
6372
};
6473

74+
6575
}

libyul/optimiser/DataFlowAnalyzer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class DataFlowAnalyzer: public ASTModifier
123123
/// for example at points where control flow is merged.
124124
void clearValues(std::set<YulString> _names);
125125

126-
void assignValue(YulString _variable, Expression const* _value);
126+
virtual void assignValue(YulString _variable, Expression const* _value);
127127

128128
/// Clears knowledge about storage or memory if they may be modified inside the block.
129129
void clearKnowledgeIfInvalidated(Block const& _block);

libyul/optimiser/SyntacticalEquality.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,9 @@ bool SyntacticallyEqual::visitDeclaration(TypedName const& _lhs, TypedName const
165165
m_identifiersRHS[_rhs.name] = id;
166166
return true;
167167
}
168+
169+
bool SyntacticallyEqualExpression::operator()(Expression const& _lhs, Expression const& _rhs) const
170+
{
171+
return SyntacticallyEqual{}(_lhs, _rhs);
172+
}
173+

libyul/optimiser/SyntacticalEquality.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,13 @@ class SyntacticallyEqual
8585
std::map<YulString, std::size_t> m_identifiersRHS;
8686
};
8787

88+
/**
89+
* Does the same as SyntacticallyEqual just that the operator() function is const.
90+
*/
91+
struct SyntacticallyEqualExpression
92+
{
93+
bool operator()(Expression const& _lhs, Expression const& _rhs) const;
94+
};
95+
96+
8897
}

0 commit comments

Comments
 (0)