Refactor join knowledge.

This commit is contained in:
chriseth 2022-03-17 19:04:00 +01:00
parent 310a58dd31
commit 62ab78bf70
2 changed files with 63 additions and 65 deletions

View File

@ -72,21 +72,21 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement)
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement)) if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement))
{ {
ASTModifier::operator()(_statement); ASTModifier::operator()(_statement);
cxx20::erase_if(m_state.storage, mapTuple([&](auto&& key, auto&& value) { cxx20::erase_if(m_state.environment.storage, mapTuple([&](auto&& key, auto&& value) {
return return
!m_knowledgeBase.knownToBeDifferent(vars->first, key) && !m_knowledgeBase.knownToBeDifferent(vars->first, key) &&
!m_knowledgeBase.knownToBeEqual(vars->second, value); !m_knowledgeBase.knownToBeEqual(vars->second, value);
})); }));
m_state.storage[vars->first] = vars->second; m_state.environment.storage[vars->first] = vars->second;
return; return;
} }
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement)) else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement))
{ {
ASTModifier::operator()(_statement); ASTModifier::operator()(_statement);
cxx20::erase_if(m_state.memory, mapTuple([&](auto&& key, auto&& /* value */) { cxx20::erase_if(m_state.environment.memory, mapTuple([&](auto&& key, auto&& /* value */) {
return !m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, key); return !m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, key);
})); }));
m_state.memory[vars->first] = vars->second; m_state.environment.memory[vars->first] = vars->second;
return; return;
} }
} }
@ -124,12 +124,11 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
void DataFlowAnalyzer::operator()(If& _if) void DataFlowAnalyzer::operator()(If& _if)
{ {
clearKnowledgeIfInvalidated(*_if.condition); clearKnowledgeIfInvalidated(*_if.condition);
unordered_map<YulString, YulString> storage = m_state.storage; Environment preEnvironment = m_state.environment;
unordered_map<YulString, YulString> memory = m_state.memory;
ASTModifier::operator()(_if); ASTModifier::operator()(_if);
joinKnowledge(storage, memory); joinKnowledge(preEnvironment);
clearValues(assignedVariableNames(_if.body)); clearValues(assignedVariableNames(_if.body));
} }
@ -141,10 +140,9 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
set<YulString> assignedVariables; set<YulString> assignedVariables;
for (auto& _case: _switch.cases) for (auto& _case: _switch.cases)
{ {
unordered_map<YulString, YulString> storage = m_state.storage; Environment preEnvironment = m_state.environment;
unordered_map<YulString, YulString> memory = m_state.memory;
(*this)(_case.body); (*this)(_case.body);
joinKnowledge(storage, memory); joinKnowledge(preEnvironment);
set<YulString> variables = assignedVariableNames(_case.body); set<YulString> variables = assignedVariableNames(_case.body);
assignedVariables += variables; assignedVariables += variables;
@ -225,7 +223,7 @@ void DataFlowAnalyzer::operator()(Block& _block)
optional<YulString> DataFlowAnalyzer::storageValue(YulString _key) const optional<YulString> DataFlowAnalyzer::storageValue(YulString _key) const
{ {
if (YulString const* value = util::valueOrNullptr(m_state.storage, _key)) if (YulString const* value = util::valueOrNullptr(m_state.environment.storage, _key))
return *value; return *value;
else else
return nullopt; return nullopt;
@ -233,7 +231,7 @@ optional<YulString> DataFlowAnalyzer::storageValue(YulString _key) const
optional<YulString> DataFlowAnalyzer::memoryValue(YulString _key) const optional<YulString> DataFlowAnalyzer::memoryValue(YulString _key) const
{ {
if (YulString const* value = util::valueOrNullptr(m_state.memory, _key)) if (YulString const* value = util::valueOrNullptr(m_state.environment.memory, _key))
return *value; return *value;
else else
return nullopt; return nullopt;
@ -267,13 +265,13 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
if (!_isDeclaration) if (!_isDeclaration)
{ {
// assignment to slot denoted by "name" // assignment to slot denoted by "name"
m_state.storage.erase(name); m_state.environment.storage.erase(name);
// assignment to slot contents denoted by "name" // assignment to slot contents denoted by "name"
cxx20::erase_if(m_state.storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; })); cxx20::erase_if(m_state.environment.storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
// assignment to slot denoted by "name" // assignment to slot denoted by "name"
m_state.memory.erase(name); m_state.environment.memory.erase(name);
// assignment to slot contents denoted by "name" // assignment to slot contents denoted by "name"
cxx20::erase_if(m_state.memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; })); cxx20::erase_if(m_state.environment.memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
} }
} }
@ -286,9 +284,9 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
// On the other hand, if we knew the value in the slot // On the other hand, if we knew the value in the slot
// already, then the sload() / mload() would have been replaced by a variable anyway. // already, then the sload() / mload() would have been replaced by a variable anyway.
if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value)) if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value))
m_state.memory[*key] = variable; m_state.environment.memory[*key] = variable;
else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value)) else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value))
m_state.storage[*key] = variable; m_state.environment.storage[*key] = variable;
} }
} }
} }
@ -329,8 +327,8 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
auto eraseCondition = mapTuple([&_variables](auto&& key, auto&& value) { auto eraseCondition = mapTuple([&_variables](auto&& key, auto&& value) {
return _variables.count(key) || _variables.count(value); return _variables.count(key) || _variables.count(value);
}); });
cxx20::erase_if(m_state.storage, eraseCondition); cxx20::erase_if(m_state.environment.storage, eraseCondition);
cxx20::erase_if(m_state.memory, eraseCondition); cxx20::erase_if(m_state.environment.memory, eraseCondition);
// Also clear variables that reference variables to be cleared. // Also clear variables that reference variables to be cleared.
for (auto const& variableToClear: _variables) for (auto const& variableToClear: _variables)
@ -357,9 +355,9 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Block const& _block)
return; return;
SideEffectsCollector sideEffects(m_dialect, _block, &m_functionSideEffects); SideEffectsCollector sideEffects(m_dialect, _block, &m_functionSideEffects);
if (sideEffects.invalidatesStorage()) if (sideEffects.invalidatesStorage())
m_state.storage.clear(); m_state.environment.storage.clear();
if (sideEffects.invalidatesMemory()) if (sideEffects.invalidatesMemory())
m_state.memory.clear(); m_state.environment.memory.clear();
} }
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr) void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
@ -368,35 +366,9 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
return; return;
SideEffectsCollector sideEffects(m_dialect, _expr, &m_functionSideEffects); SideEffectsCollector sideEffects(m_dialect, _expr, &m_functionSideEffects);
if (sideEffects.invalidatesStorage()) if (sideEffects.invalidatesStorage())
m_state.storage.clear(); m_state.environment.storage.clear();
if (sideEffects.invalidatesMemory()) if (sideEffects.invalidatesMemory())
m_state.memory.clear(); m_state.environment.memory.clear();
}
void DataFlowAnalyzer::joinKnowledge(
unordered_map<YulString, YulString> const& _olderStorage,
unordered_map<YulString, YulString> const& _olderMemory
)
{
if (!m_analyzeStores)
return;
joinKnowledgeHelper(m_state.storage, _olderStorage);
joinKnowledgeHelper(m_state.memory, _olderMemory);
}
void DataFlowAnalyzer::joinKnowledgeHelper(
std::unordered_map<YulString, YulString>& _this,
std::unordered_map<YulString, YulString> const& _older
)
{
// We clear if the key does not exist in the older map or if the value is different.
// This also works for memory because _older is an "older version"
// of m_state.memory and thus any overlapping write would have cleared the keys
// that are not known to be different inside m_state.memory already.
cxx20::erase_if(_this, mapTuple([&_older](auto&& key, auto&& currentValue){
YulString const* oldValue = util::valueOrNullptr(_older, key);
return !oldValue || *oldValue != currentValue;
}));
} }
bool DataFlowAnalyzer::inScope(YulString _variableName) const bool DataFlowAnalyzer::inScope(YulString _variableName) const
@ -443,3 +415,26 @@ std::optional<YulString> DataFlowAnalyzer::isSimpleLoad(
return key->name; return key->name;
return {}; return {};
} }
void DataFlowAnalyzer::joinKnowledge(Environment const& _olderEnvironment)
{
if (!m_analyzeStores)
return;
joinKnowledgeHelper(m_state.environment.storage, _olderEnvironment.storage);
joinKnowledgeHelper(m_state.environment.memory, _olderEnvironment.memory);
}
void DataFlowAnalyzer::joinKnowledgeHelper(
std::unordered_map<YulString, YulString>& _this,
std::unordered_map<YulString, YulString> const& _older
)
{
// We clear if the key does not exist in the older map or if the value is different.
// This also works for memory because _older is an "older version"
// of m_state.environment.memory and thus any overlapping write would have cleared the keys
// that are not known to be different inside m_state.environment.memory already.
cxx20::erase_if(_this, mapTuple([&_older](auto&& key, auto&& currentValue){
YulString const* oldValue = util::valueOrNullptr(_older, key);
return !oldValue || *oldValue != currentValue;
}));
}

View File

@ -131,19 +131,6 @@ protected:
/// Clears knowledge about storage or memory if they may be modified inside the expression. /// Clears knowledge about storage or memory if they may be modified inside the expression.
void clearKnowledgeIfInvalidated(Expression const& _expression); void clearKnowledgeIfInvalidated(Expression const& _expression);
/// Joins knowledge about storage and memory with an older point in the control-flow.
/// This only works if the current state is a direct successor of the older point,
/// i.e. `_otherStorage` and `_otherMemory` cannot have additional changes.
void joinKnowledge(
std::unordered_map<YulString, YulString> const& _olderStorage,
std::unordered_map<YulString, YulString> const& _olderMemory
);
static void joinKnowledgeHelper(
std::unordered_map<YulString, YulString>& _thisData,
std::unordered_map<YulString, YulString> const& _olderData
);
/// Returns true iff the variable is in scope. /// Returns true iff the variable is in scope.
bool inScope(YulString _variableName) const; bool inScope(YulString _variableName) const;
@ -176,6 +163,11 @@ protected:
std::map<YulString, SideEffects> m_functionSideEffects; std::map<YulString, SideEffects> m_functionSideEffects;
private: private:
struct Environment
{
std::unordered_map<YulString, YulString> storage;
std::unordered_map<YulString, YulString> memory;
};
struct State struct State
{ {
/// Current values of variables, always movable. /// Current values of variables, always movable.
@ -183,9 +175,20 @@ private:
/// m_references[a].contains(b) <=> the current expression assigned to a references b /// m_references[a].contains(b) <=> the current expression assigned to a references b
std::unordered_map<YulString, std::set<YulString>> references; std::unordered_map<YulString, std::set<YulString>> references;
std::unordered_map<YulString, YulString> storage; Environment environment;
std::unordered_map<YulString, YulString> memory;
}; };
/// Joins knowledge about storage and memory with an older point in the control-flow.
/// This only works if the current state is a direct successor of the older point,
/// i.e. `_olderState.storage` and `_olderState.memory` cannot have additional changes.
/// Does nothing if memory and storage analysis is disabled / ignored.
void joinKnowledge(Environment const& _olderEnvironment);
static void joinKnowledgeHelper(
std::unordered_map<YulString, YulString>& _thisData,
std::unordered_map<YulString, YulString> const& _olderData
);
State m_state; State m_state;
protected: protected: