Skip to content

Commit 4100a59

Browse files
authored
Merge pull request #13708 from ethereum/refactor_join_knowledge
Refactor join knowledge.
2 parents 310a58d + 62ab78b commit 4100a59

File tree

2 files changed

+63
-65
lines changed

2 files changed

+63
-65
lines changed

libyul/optimiser/DataFlowAnalyzer.cpp

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,21 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement)
7272
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement))
7373
{
7474
ASTModifier::operator()(_statement);
75-
cxx20::erase_if(m_state.storage, mapTuple([&](auto&& key, auto&& value) {
75+
cxx20::erase_if(m_state.environment.storage, mapTuple([&](auto&& key, auto&& value) {
7676
return
7777
!m_knowledgeBase.knownToBeDifferent(vars->first, key) &&
7878
!m_knowledgeBase.knownToBeEqual(vars->second, value);
7979
}));
80-
m_state.storage[vars->first] = vars->second;
80+
m_state.environment.storage[vars->first] = vars->second;
8181
return;
8282
}
8383
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement))
8484
{
8585
ASTModifier::operator()(_statement);
86-
cxx20::erase_if(m_state.memory, mapTuple([&](auto&& key, auto&& /* value */) {
86+
cxx20::erase_if(m_state.environment.memory, mapTuple([&](auto&& key, auto&& /* value */) {
8787
return !m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, key);
8888
}));
89-
m_state.memory[vars->first] = vars->second;
89+
m_state.environment.memory[vars->first] = vars->second;
9090
return;
9191
}
9292
}
@@ -124,12 +124,11 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
124124
void DataFlowAnalyzer::operator()(If& _if)
125125
{
126126
clearKnowledgeIfInvalidated(*_if.condition);
127-
unordered_map<YulString, YulString> storage = m_state.storage;
128-
unordered_map<YulString, YulString> memory = m_state.memory;
127+
Environment preEnvironment = m_state.environment;
129128

130129
ASTModifier::operator()(_if);
131130

132-
joinKnowledge(storage, memory);
131+
joinKnowledge(preEnvironment);
133132

134133
clearValues(assignedVariableNames(_if.body));
135134
}
@@ -141,10 +140,9 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
141140
set<YulString> assignedVariables;
142141
for (auto& _case: _switch.cases)
143142
{
144-
unordered_map<YulString, YulString> storage = m_state.storage;
145-
unordered_map<YulString, YulString> memory = m_state.memory;
143+
Environment preEnvironment = m_state.environment;
146144
(*this)(_case.body);
147-
joinKnowledge(storage, memory);
145+
joinKnowledge(preEnvironment);
148146

149147
set<YulString> variables = assignedVariableNames(_case.body);
150148
assignedVariables += variables;
@@ -225,15 +223,15 @@ void DataFlowAnalyzer::operator()(Block& _block)
225223

226224
optional<YulString> DataFlowAnalyzer::storageValue(YulString _key) const
227225
{
228-
if (YulString const* value = util::valueOrNullptr(m_state.storage, _key))
226+
if (YulString const* value = util::valueOrNullptr(m_state.environment.storage, _key))
229227
return *value;
230228
else
231229
return nullopt;
232230
}
233231

234232
optional<YulString> DataFlowAnalyzer::memoryValue(YulString _key) const
235233
{
236-
if (YulString const* value = util::valueOrNullptr(m_state.memory, _key))
234+
if (YulString const* value = util::valueOrNullptr(m_state.environment.memory, _key))
237235
return *value;
238236
else
239237
return nullopt;
@@ -267,13 +265,13 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
267265
if (!_isDeclaration)
268266
{
269267
// assignment to slot denoted by "name"
270-
m_state.storage.erase(name);
268+
m_state.environment.storage.erase(name);
271269
// assignment to slot contents denoted by "name"
272-
cxx20::erase_if(m_state.storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
270+
cxx20::erase_if(m_state.environment.storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
273271
// assignment to slot denoted by "name"
274-
m_state.memory.erase(name);
272+
m_state.environment.memory.erase(name);
275273
// assignment to slot contents denoted by "name"
276-
cxx20::erase_if(m_state.memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
274+
cxx20::erase_if(m_state.environment.memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
277275
}
278276
}
279277

@@ -286,9 +284,9 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
286284
// On the other hand, if we knew the value in the slot
287285
// already, then the sload() / mload() would have been replaced by a variable anyway.
288286
if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value))
289-
m_state.memory[*key] = variable;
287+
m_state.environment.memory[*key] = variable;
290288
else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value))
291-
m_state.storage[*key] = variable;
289+
m_state.environment.storage[*key] = variable;
292290
}
293291
}
294292
}
@@ -329,8 +327,8 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
329327
auto eraseCondition = mapTuple([&_variables](auto&& key, auto&& value) {
330328
return _variables.count(key) || _variables.count(value);
331329
});
332-
cxx20::erase_if(m_state.storage, eraseCondition);
333-
cxx20::erase_if(m_state.memory, eraseCondition);
330+
cxx20::erase_if(m_state.environment.storage, eraseCondition);
331+
cxx20::erase_if(m_state.environment.memory, eraseCondition);
334332

335333
// Also clear variables that reference variables to be cleared.
336334
for (auto const& variableToClear: _variables)
@@ -357,9 +355,9 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Block const& _block)
357355
return;
358356
SideEffectsCollector sideEffects(m_dialect, _block, &m_functionSideEffects);
359357
if (sideEffects.invalidatesStorage())
360-
m_state.storage.clear();
358+
m_state.environment.storage.clear();
361359
if (sideEffects.invalidatesMemory())
362-
m_state.memory.clear();
360+
m_state.environment.memory.clear();
363361
}
364362

365363
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
@@ -368,35 +366,9 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
368366
return;
369367
SideEffectsCollector sideEffects(m_dialect, _expr, &m_functionSideEffects);
370368
if (sideEffects.invalidatesStorage())
371-
m_state.storage.clear();
369+
m_state.environment.storage.clear();
372370
if (sideEffects.invalidatesMemory())
373-
m_state.memory.clear();
374-
}
375-
376-
void DataFlowAnalyzer::joinKnowledge(
377-
unordered_map<YulString, YulString> const& _olderStorage,
378-
unordered_map<YulString, YulString> const& _olderMemory
379-
)
380-
{
381-
if (!m_analyzeStores)
382-
return;
383-
joinKnowledgeHelper(m_state.storage, _olderStorage);
384-
joinKnowledgeHelper(m_state.memory, _olderMemory);
385-
}
386-
387-
void DataFlowAnalyzer::joinKnowledgeHelper(
388-
std::unordered_map<YulString, YulString>& _this,
389-
std::unordered_map<YulString, YulString> const& _older
390-
)
391-
{
392-
// We clear if the key does not exist in the older map or if the value is different.
393-
// This also works for memory because _older is an "older version"
394-
// of m_state.memory and thus any overlapping write would have cleared the keys
395-
// that are not known to be different inside m_state.memory already.
396-
cxx20::erase_if(_this, mapTuple([&_older](auto&& key, auto&& currentValue){
397-
YulString const* oldValue = util::valueOrNullptr(_older, key);
398-
return !oldValue || *oldValue != currentValue;
399-
}));
371+
m_state.environment.memory.clear();
400372
}
401373

402374
bool DataFlowAnalyzer::inScope(YulString _variableName) const
@@ -443,3 +415,26 @@ std::optional<YulString> DataFlowAnalyzer::isSimpleLoad(
443415
return key->name;
444416
return {};
445417
}
418+
419+
void DataFlowAnalyzer::joinKnowledge(Environment const& _olderEnvironment)
420+
{
421+
if (!m_analyzeStores)
422+
return;
423+
joinKnowledgeHelper(m_state.environment.storage, _olderEnvironment.storage);
424+
joinKnowledgeHelper(m_state.environment.memory, _olderEnvironment.memory);
425+
}
426+
427+
void DataFlowAnalyzer::joinKnowledgeHelper(
428+
std::unordered_map<YulString, YulString>& _this,
429+
std::unordered_map<YulString, YulString> const& _older
430+
)
431+
{
432+
// We clear if the key does not exist in the older map or if the value is different.
433+
// This also works for memory because _older is an "older version"
434+
// of m_state.environment.memory and thus any overlapping write would have cleared the keys
435+
// that are not known to be different inside m_state.environment.memory already.
436+
cxx20::erase_if(_this, mapTuple([&_older](auto&& key, auto&& currentValue){
437+
YulString const* oldValue = util::valueOrNullptr(_older, key);
438+
return !oldValue || *oldValue != currentValue;
439+
}));
440+
}

libyul/optimiser/DataFlowAnalyzer.h

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,6 @@ class DataFlowAnalyzer: public ASTModifier
131131
/// Clears knowledge about storage or memory if they may be modified inside the expression.
132132
void clearKnowledgeIfInvalidated(Expression const& _expression);
133133

134-
/// Joins knowledge about storage and memory with an older point in the control-flow.
135-
/// This only works if the current state is a direct successor of the older point,
136-
/// i.e. `_otherStorage` and `_otherMemory` cannot have additional changes.
137-
void joinKnowledge(
138-
std::unordered_map<YulString, YulString> const& _olderStorage,
139-
std::unordered_map<YulString, YulString> const& _olderMemory
140-
);
141-
142-
static void joinKnowledgeHelper(
143-
std::unordered_map<YulString, YulString>& _thisData,
144-
std::unordered_map<YulString, YulString> const& _olderData
145-
);
146-
147134
/// Returns true iff the variable is in scope.
148135
bool inScope(YulString _variableName) const;
149136

@@ -176,16 +163,32 @@ class DataFlowAnalyzer: public ASTModifier
176163
std::map<YulString, SideEffects> m_functionSideEffects;
177164

178165
private:
166+
struct Environment
167+
{
168+
std::unordered_map<YulString, YulString> storage;
169+
std::unordered_map<YulString, YulString> memory;
170+
};
179171
struct State
180172
{
181173
/// Current values of variables, always movable.
182174
std::map<YulString, AssignedValue> value;
183175
/// m_references[a].contains(b) <=> the current expression assigned to a references b
184176
std::unordered_map<YulString, std::set<YulString>> references;
185177

186-
std::unordered_map<YulString, YulString> storage;
187-
std::unordered_map<YulString, YulString> memory;
178+
Environment environment;
188179
};
180+
181+
/// Joins knowledge about storage and memory with an older point in the control-flow.
182+
/// This only works if the current state is a direct successor of the older point,
183+
/// i.e. `_olderState.storage` and `_olderState.memory` cannot have additional changes.
184+
/// Does nothing if memory and storage analysis is disabled / ignored.
185+
void joinKnowledge(Environment const& _olderEnvironment);
186+
187+
static void joinKnowledgeHelper(
188+
std::unordered_map<YulString, YulString>& _thisData,
189+
std::unordered_map<YulString, YulString> const& _olderData
190+
);
191+
189192
State m_state;
190193

191194
protected:

0 commit comments

Comments
 (0)