Skip to content

Commit 5afa2ad

Browse files
authored
Merge pull request #12775 from ethereum/refactorDataFlowAnalyzer
Refactor data flow analyzer state access.
2 parents d946b6b + afdf22f commit 5afa2ad

14 files changed

+124
-89
lines changed

libyul/optimiser/CommonSubexpressionEliminator.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,18 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
9595
if (Identifier const* identifier = get_if<Identifier>(&_e))
9696
{
9797
YulString identifierName = identifier->name;
98-
if (m_value.count(identifierName))
98+
if (AssignedValue const* assignedValue = variableValue(identifierName))
9999
{
100-
assertThrow(m_value.at(identifierName).value, OptimizerException, "");
101-
if (Identifier const* value = get_if<Identifier>(m_value.at(identifierName).value))
100+
assertThrow(assignedValue->value, OptimizerException, "");
101+
if (Identifier const* value = get_if<Identifier>(assignedValue->value))
102102
if (inScope(value->name))
103103
_e = Identifier{debugDataOf(_e), value->name};
104104
}
105105
}
106106
else
107107
{
108108
// TODO this search is rather inefficient.
109-
for (auto const& [variable, value]: m_value)
109+
for (auto const& [variable, value]: allValues())
110110
{
111111
assertThrow(value.value, OptimizerException, "");
112112
// Prevent using the default value of return variables

libyul/optimiser/DataFlowAnalyzer.cpp

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include <libyul/optimiser/NameCollector.h>
2626
#include <libyul/optimiser/Semantics.h>
27+
#include <libyul/optimiser/KnowledgeBase.h>
2728
#include <libyul/AST.h>
2829
#include <libyul/Dialect.h>
2930
#include <libyul/Exceptions.h>
@@ -47,7 +48,7 @@ DataFlowAnalyzer::DataFlowAnalyzer(
4748
):
4849
m_dialect(_dialect),
4950
m_functionSideEffects(std::move(_functionSideEffects)),
50-
m_knowledgeBase(_dialect, m_value)
51+
m_knowledgeBase(_dialect, [this](YulString _var) { return variableValue(_var); })
5152
{
5253
if (auto const* builtin = _dialect.memoryStoreFunction(YulString{}))
5354
m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Memory)] = builtin->name;
@@ -64,20 +65,20 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement)
6465
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement))
6566
{
6667
ASTModifier::operator()(_statement);
67-
cxx20::erase_if(m_storage, mapTuple([&](auto&& key, auto&& value) {
68+
cxx20::erase_if(m_state.storage, mapTuple([&](auto&& key, auto&& value) {
6869
return
6970
!m_knowledgeBase.knownToBeDifferent(vars->first, key) &&
7071
!m_knowledgeBase.knownToBeEqual(vars->second, value);
7172
}));
72-
m_storage[vars->first] = vars->second;
73+
m_state.storage[vars->first] = vars->second;
7374
}
7475
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement))
7576
{
7677
ASTModifier::operator()(_statement);
77-
cxx20::erase_if(m_memory, mapTuple([&](auto&& key, auto&& /* value */) {
78+
cxx20::erase_if(m_state.memory, mapTuple([&](auto&& key, auto&& /* value */) {
7879
return !m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, key);
7980
}));
80-
m_memory[vars->first] = vars->second;
81+
m_state.memory[vars->first] = vars->second;
8182
}
8283
else
8384
{
@@ -116,8 +117,8 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
116117
void DataFlowAnalyzer::operator()(If& _if)
117118
{
118119
clearKnowledgeIfInvalidated(*_if.condition);
119-
unordered_map<YulString, YulString> storage = m_storage;
120-
unordered_map<YulString, YulString> memory = m_memory;
120+
unordered_map<YulString, YulString> storage = m_state.storage;
121+
unordered_map<YulString, YulString> memory = m_state.memory;
121122

122123
ASTModifier::operator()(_if);
123124

@@ -133,8 +134,8 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
133134
set<YulString> assignedVariables;
134135
for (auto& _case: _switch.cases)
135136
{
136-
unordered_map<YulString, YulString> storage = m_storage;
137-
unordered_map<YulString, YulString> memory = m_memory;
137+
unordered_map<YulString, YulString> storage = m_state.storage;
138+
unordered_map<YulString, YulString> memory = m_state.memory;
138139
(*this)(_case.body);
139140
joinKnowledge(storage, memory);
140141

@@ -153,11 +154,8 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
153154
{
154155
// Save all information. We might rather reinstantiate this class,
155156
// but this could be difficult if it is subclassed.
156-
ScopedSaveAndRestore valueResetter(m_value, {});
157+
ScopedSaveAndRestore stateResetter(m_state, {});
157158
ScopedSaveAndRestore loopDepthResetter(m_loopDepth, 0u);
158-
ScopedSaveAndRestore referencesResetter(m_references, {});
159-
ScopedSaveAndRestore storageResetter(m_storage, {});
160-
ScopedSaveAndRestore memoryResetter(m_memory, {});
161159
pushScope(true);
162160

163161
for (auto const& parameter: _fun.parameters)
@@ -218,6 +216,22 @@ void DataFlowAnalyzer::operator()(Block& _block)
218216
assertThrow(numScopes == m_variableScopes.size(), OptimizerException, "");
219217
}
220218

219+
optional<YulString> DataFlowAnalyzer::storageValue(YulString _key) const
220+
{
221+
if (YulString const* value = util::valueOrNullptr(m_state.storage, _key))
222+
return *value;
223+
else
224+
return nullopt;
225+
}
226+
227+
optional<YulString> DataFlowAnalyzer::memoryValue(YulString _key) const
228+
{
229+
if (YulString const* value = util::valueOrNullptr(m_state.memory, _key))
230+
return *value;
231+
else
232+
return nullopt;
233+
}
234+
221235
void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expression* _value, bool _isDeclaration)
222236
{
223237
if (!_isDeclaration)
@@ -242,17 +256,17 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
242256
auto const& referencedVariables = movableChecker.referencedVariables();
243257
for (auto const& name: _variables)
244258
{
245-
m_references[name] = referencedVariables;
259+
m_state.references[name] = referencedVariables;
246260
if (!_isDeclaration)
247261
{
248262
// assignment to slot denoted by "name"
249-
m_storage.erase(name);
263+
m_state.storage.erase(name);
250264
// assignment to slot contents denoted by "name"
251-
cxx20::erase_if(m_storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
265+
cxx20::erase_if(m_state.storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
252266
// assignment to slot denoted by "name"
253-
m_memory.erase(name);
267+
m_state.memory.erase(name);
254268
// assignment to slot contents denoted by "name"
255-
cxx20::erase_if(m_memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
269+
cxx20::erase_if(m_state.memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
256270
}
257271
}
258272

@@ -265,9 +279,9 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
265279
// On the other hand, if we knew the value in the slot
266280
// already, then the sload() / mload() would have been replaced by a variable anyway.
267281
if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value))
268-
m_memory[*key] = variable;
282+
m_state.memory[*key] = variable;
269283
else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value))
270-
m_storage[*key] = variable;
284+
m_state.storage[*key] = variable;
271285
}
272286
}
273287
}
@@ -281,8 +295,8 @@ void DataFlowAnalyzer::popScope()
281295
{
282296
for (auto const& name: m_variableScopes.back().variables)
283297
{
284-
m_value.erase(name);
285-
m_references.erase(name);
298+
m_state.value.erase(name);
299+
m_state.references.erase(name);
286300
}
287301
m_variableScopes.pop_back();
288302
}
@@ -308,53 +322,53 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
308322
auto eraseCondition = mapTuple([&_variables](auto&& key, auto&& value) {
309323
return _variables.count(key) || _variables.count(value);
310324
});
311-
cxx20::erase_if(m_storage, eraseCondition);
312-
cxx20::erase_if(m_memory, eraseCondition);
325+
cxx20::erase_if(m_state.storage, eraseCondition);
326+
cxx20::erase_if(m_state.memory, eraseCondition);
313327

314328
// Also clear variables that reference variables to be cleared.
315329
for (auto const& variableToClear: _variables)
316-
for (auto const& [ref, names]: m_references)
330+
for (auto const& [ref, names]: m_state.references)
317331
if (names.count(variableToClear))
318332
_variables.emplace(ref);
319333

320334
// Clear the value and update the reference relation.
321335
for (auto const& name: _variables)
322336
{
323-
m_value.erase(name);
324-
m_references.erase(name);
337+
m_state.value.erase(name);
338+
m_state.references.erase(name);
325339
}
326340
}
327341

328342
void DataFlowAnalyzer::assignValue(YulString _variable, Expression const* _value)
329343
{
330-
m_value[_variable] = {_value, m_loopDepth};
344+
m_state.value[_variable] = {_value, m_loopDepth};
331345
}
332346

333347
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Block const& _block)
334348
{
335349
SideEffectsCollector sideEffects(m_dialect, _block, &m_functionSideEffects);
336350
if (sideEffects.invalidatesStorage())
337-
m_storage.clear();
351+
m_state.storage.clear();
338352
if (sideEffects.invalidatesMemory())
339-
m_memory.clear();
353+
m_state.memory.clear();
340354
}
341355

342356
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
343357
{
344358
SideEffectsCollector sideEffects(m_dialect, _expr, &m_functionSideEffects);
345359
if (sideEffects.invalidatesStorage())
346-
m_storage.clear();
360+
m_state.storage.clear();
347361
if (sideEffects.invalidatesMemory())
348-
m_memory.clear();
362+
m_state.memory.clear();
349363
}
350364

351365
void DataFlowAnalyzer::joinKnowledge(
352366
unordered_map<YulString, YulString> const& _olderStorage,
353367
unordered_map<YulString, YulString> const& _olderMemory
354368
)
355369
{
356-
joinKnowledgeHelper(m_storage, _olderStorage);
357-
joinKnowledgeHelper(m_memory, _olderMemory);
370+
joinKnowledgeHelper(m_state.storage, _olderStorage);
371+
joinKnowledgeHelper(m_state.memory, _olderMemory);
358372
}
359373

360374
void DataFlowAnalyzer::joinKnowledgeHelper(
@@ -364,8 +378,8 @@ void DataFlowAnalyzer::joinKnowledgeHelper(
364378
{
365379
// We clear if the key does not exist in the older map or if the value is different.
366380
// This also works for memory because _older is an "older version"
367-
// of m_memory and thus any overlapping write would have cleared the keys
368-
// that are not known to be different inside m_memory already.
381+
// of m_state.memory and thus any overlapping write would have cleared the keys
382+
// that are not known to be different inside m_state.memory already.
369383
cxx20::erase_if(_this, mapTuple([&_older](auto&& key, auto&& currentValue){
370384
YulString const* oldValue = util::valueOrNullptr(_older, key);
371385
return !oldValue || *oldValue != currentValue;
@@ -386,8 +400,8 @@ bool DataFlowAnalyzer::inScope(YulString _variableName) const
386400

387401
optional<u256> DataFlowAnalyzer::valueOfIdentifier(YulString const& _name)
388402
{
389-
if (m_value.count(_name))
390-
if (Literal const* literal = get_if<Literal>(m_value.at(_name).value))
403+
if (AssignedValue const* value = variableValue(_name))
404+
if (Literal const* literal = get_if<Literal>(value->value))
391405
return valueOfLiteral(*literal);
392406
return nullopt;
393407
}

libyul/optimiser/DataFlowAnalyzer.h

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <libyul/AST.h> // Needed for m_zero below.
3030
#include <libyul/SideEffects.h>
3131

32+
#include <libsolutil/Numeric.h>
3233
#include <libsolutil/Common.h>
3334

3435
#include <map>
@@ -38,6 +39,7 @@ namespace solidity::yul
3839
{
3940
struct Dialect;
4041
struct SideEffects;
42+
class KnowledgeBase;
4143

4244
/// Value assigned to a variable.
4345
struct AssignedValue
@@ -98,6 +100,13 @@ class DataFlowAnalyzer: public ASTModifier
98100
void operator()(ForLoop&) override;
99101
void operator()(Block& _block) override;
100102

103+
/// @returns the current value of the given variable, if known - always movable.
104+
AssignedValue const* variableValue(YulString _variable) const { return util::valueOrNullptr(m_state.value, _variable); }
105+
std::set<YulString> const* references(YulString _variable) const { return util::valueOrNullptr(m_state.references, _variable); }
106+
std::map<YulString, AssignedValue> const& allValues() const { return m_state.value; }
107+
std::optional<YulString> storageValue(YulString _key) const;
108+
std::optional<YulString> memoryValue(YulString _key) const;
109+
101110
protected:
102111
/// Registers the assignment.
103112
void handleAssignment(std::set<YulString> const& _names, Expression* _value, bool _isDeclaration);
@@ -164,14 +173,20 @@ class DataFlowAnalyzer: public ASTModifier
164173
/// if this is not provided or the function is not found.
165174
std::map<YulString, SideEffects> m_functionSideEffects;
166175

167-
/// Current values of variables, always movable.
168-
std::map<YulString, AssignedValue> m_value;
169-
/// m_references[a].contains(b) <=> the current expression assigned to a references b
170-
std::unordered_map<YulString, std::set<YulString>> m_references;
176+
private:
177+
struct State
178+
{
179+
/// Current values of variables, always movable.
180+
std::map<YulString, AssignedValue> value;
181+
/// m_references[a].contains(b) <=> the current expression assigned to a references b
182+
std::unordered_map<YulString, std::set<YulString>> references;
171183

172-
std::unordered_map<YulString, YulString> m_storage;
173-
std::unordered_map<YulString, YulString> m_memory;
184+
std::unordered_map<YulString, YulString> storage;
185+
std::unordered_map<YulString, YulString> memory;
186+
};
187+
State m_state;
174188

189+
protected:
175190
KnowledgeBase m_knowledgeBase;
176191

177192
YulString m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Last) + 1];

libyul/optimiser/EqualStoreEliminator.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ void EqualStoreEliminator::visit(Statement& _statement)
5454
{
5555
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, *expression))
5656
{
57-
if (auto const* currentValue = util::valueOrNullptr(m_storage, vars->first))
57+
if (optional<YulString> currentValue = storageValue(vars->first))
5858
if (*currentValue == vars->second)
5959
m_pendingRemovals.insert(&_statement);
6060
}
6161
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, *expression))
6262
{
63-
if (auto const* currentValue = util::valueOrNullptr(m_memory, vars->first))
63+
if (optional<YulString> currentValue = memoryValue(vars->first))
6464
if (*currentValue == vars->second)
6565
m_pendingRemovals.insert(&_statement);
6666
}

libyul/optimiser/ExpressionSimplifier.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ void ExpressionSimplifier::visit(Expression& _expression)
3838
{
3939
ASTModifier::visit(_expression);
4040

41-
while (auto const* match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value))
41+
while (auto const* match = SimplificationRules::findFirstMatch(
42+
_expression,
43+
m_dialect,
44+
[this](YulString _var) { return variableValue(_var); }
45+
))
4246
_expression = match->action().toExpression(debugDataOf(_expression));
4347
}

libyul/optimiser/KnowledgeBase.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ bool KnowledgeBase::knownToBeZero(YulString _a)
8080

8181
optional<u256> KnowledgeBase::valueIfKnownConstant(YulString _a)
8282
{
83-
if (m_variableValues.count(_a))
84-
if (Literal const* literal = get_if<Literal>(m_variableValues.at(_a).value))
83+
if (AssignedValue const* value = m_variableValues(_a))
84+
if (Literal const* literal = get_if<Literal>(value->value))
8585
return valueOfLiteral(*literal);
8686
return {};
8787
}

libyul/optimiser/KnowledgeBase.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <libsolutil/Numeric.h>
2929

3030
#include <map>
31+
#include <functional>
3132

3233
namespace solidity::yul
3334
{
@@ -37,15 +38,16 @@ struct AssignedValue;
3738

3839
/**
3940
* Class that can answer questions about values of variables and their relations.
40-
*
41-
* The reference to the map of values provided at construction is assumed to be updating.
4241
*/
4342
class KnowledgeBase
4443
{
4544
public:
46-
KnowledgeBase(Dialect const& _dialect, std::map<YulString, AssignedValue> const& _variableValues):
45+
KnowledgeBase(
46+
Dialect const& _dialect,
47+
std::function<AssignedValue const*(YulString)> _variableValues
48+
):
4749
m_dialect(_dialect),
48-
m_variableValues(_variableValues)
50+
m_variableValues(std::move(_variableValues))
4951
{}
5052

5153
bool knownToBeDifferent(YulString _a, YulString _b);
@@ -60,7 +62,7 @@ class KnowledgeBase
6062
Expression simplifyRecursively(Expression _expression);
6163

6264
Dialect const& m_dialect;
63-
std::map<YulString, AssignedValue> const& m_variableValues;
65+
std::function<AssignedValue const*(YulString)> m_variableValues;
6466
size_t m_counter = 0;
6567
};
6668

0 commit comments

Comments
 (0)