Create a state struct.

This commit is contained in:
chriseth 2022-03-10 19:35:58 +01:00
parent eab4ca906c
commit afdf22f5c7
2 changed files with 49 additions and 47 deletions

View File

@ -65,20 +65,20 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement)
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement))
{
ASTModifier::operator()(_statement);
cxx20::erase_if(m_storage, mapTuple([&](auto&& key, auto&& value) {
cxx20::erase_if(m_state.storage, mapTuple([&](auto&& key, auto&& value) {
return
!m_knowledgeBase.knownToBeDifferent(vars->first, key) &&
!m_knowledgeBase.knownToBeEqual(vars->second, value);
}));
m_storage[vars->first] = vars->second;
m_state.storage[vars->first] = vars->second;
}
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement))
{
ASTModifier::operator()(_statement);
cxx20::erase_if(m_memory, mapTuple([&](auto&& key, auto&& /* value */) {
cxx20::erase_if(m_state.memory, mapTuple([&](auto&& key, auto&& /* value */) {
return !m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, key);
}));
m_memory[vars->first] = vars->second;
m_state.memory[vars->first] = vars->second;
}
else
{
@ -117,8 +117,8 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
void DataFlowAnalyzer::operator()(If& _if)
{
clearKnowledgeIfInvalidated(*_if.condition);
unordered_map<YulString, YulString> storage = m_storage;
unordered_map<YulString, YulString> memory = m_memory;
unordered_map<YulString, YulString> storage = m_state.storage;
unordered_map<YulString, YulString> memory = m_state.memory;
ASTModifier::operator()(_if);
@ -134,8 +134,8 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
set<YulString> assignedVariables;
for (auto& _case: _switch.cases)
{
unordered_map<YulString, YulString> storage = m_storage;
unordered_map<YulString, YulString> memory = m_memory;
unordered_map<YulString, YulString> storage = m_state.storage;
unordered_map<YulString, YulString> memory = m_state.memory;
(*this)(_case.body);
joinKnowledge(storage, memory);
@ -154,11 +154,8 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
{
// Save all information. We might rather reinstantiate this class,
// but this could be difficult if it is subclassed.
ScopedSaveAndRestore valueResetter(m_value, {});
ScopedSaveAndRestore stateResetter(m_state, {});
ScopedSaveAndRestore loopDepthResetter(m_loopDepth, 0u);
ScopedSaveAndRestore referencesResetter(m_references, {});
ScopedSaveAndRestore storageResetter(m_storage, {});
ScopedSaveAndRestore memoryResetter(m_memory, {});
pushScope(true);
for (auto const& parameter: _fun.parameters)
@ -221,7 +218,7 @@ void DataFlowAnalyzer::operator()(Block& _block)
optional<YulString> DataFlowAnalyzer::storageValue(YulString _key) const
{
if (YulString const* value = util::valueOrNullptr(m_storage, _key))
if (YulString const* value = util::valueOrNullptr(m_state.storage, _key))
return *value;
else
return nullopt;
@ -229,7 +226,7 @@ optional<YulString> DataFlowAnalyzer::storageValue(YulString _key) const
optional<YulString> DataFlowAnalyzer::memoryValue(YulString _key) const
{
if (YulString const* value = util::valueOrNullptr(m_memory, _key))
if (YulString const* value = util::valueOrNullptr(m_state.memory, _key))
return *value;
else
return nullopt;
@ -259,17 +256,17 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
auto const& referencedVariables = movableChecker.referencedVariables();
for (auto const& name: _variables)
{
m_references[name] = referencedVariables;
m_state.references[name] = referencedVariables;
if (!_isDeclaration)
{
// assignment to slot denoted by "name"
m_storage.erase(name);
m_state.storage.erase(name);
// assignment to slot contents denoted by "name"
cxx20::erase_if(m_storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
cxx20::erase_if(m_state.storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
// assignment to slot denoted by "name"
m_memory.erase(name);
m_state.memory.erase(name);
// assignment to slot contents denoted by "name"
cxx20::erase_if(m_memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
cxx20::erase_if(m_state.memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
}
}
@ -282,9 +279,9 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
// 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.
if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value))
m_memory[*key] = variable;
m_state.memory[*key] = variable;
else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value))
m_storage[*key] = variable;
m_state.storage[*key] = variable;
}
}
}
@ -298,8 +295,8 @@ void DataFlowAnalyzer::popScope()
{
for (auto const& name: m_variableScopes.back().variables)
{
m_value.erase(name);
m_references.erase(name);
m_state.value.erase(name);
m_state.references.erase(name);
}
m_variableScopes.pop_back();
}
@ -325,44 +322,44 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
auto eraseCondition = mapTuple([&_variables](auto&& key, auto&& value) {
return _variables.count(key) || _variables.count(value);
});
cxx20::erase_if(m_storage, eraseCondition);
cxx20::erase_if(m_memory, eraseCondition);
cxx20::erase_if(m_state.storage, eraseCondition);
cxx20::erase_if(m_state.memory, eraseCondition);
// Also clear variables that reference variables to be cleared.
for (auto const& variableToClear: _variables)
for (auto const& [ref, names]: m_references)
for (auto const& [ref, names]: m_state.references)
if (names.count(variableToClear))
_variables.emplace(ref);
// Clear the value and update the reference relation.
for (auto const& name: _variables)
{
m_value.erase(name);
m_references.erase(name);
m_state.value.erase(name);
m_state.references.erase(name);
}
}
void DataFlowAnalyzer::assignValue(YulString _variable, Expression const* _value)
{
m_value[_variable] = {_value, m_loopDepth};
m_state.value[_variable] = {_value, m_loopDepth};
}
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Block const& _block)
{
SideEffectsCollector sideEffects(m_dialect, _block, &m_functionSideEffects);
if (sideEffects.invalidatesStorage())
m_storage.clear();
m_state.storage.clear();
if (sideEffects.invalidatesMemory())
m_memory.clear();
m_state.memory.clear();
}
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
{
SideEffectsCollector sideEffects(m_dialect, _expr, &m_functionSideEffects);
if (sideEffects.invalidatesStorage())
m_storage.clear();
m_state.storage.clear();
if (sideEffects.invalidatesMemory())
m_memory.clear();
m_state.memory.clear();
}
void DataFlowAnalyzer::joinKnowledge(
@ -370,8 +367,8 @@ void DataFlowAnalyzer::joinKnowledge(
unordered_map<YulString, YulString> const& _olderMemory
)
{
joinKnowledgeHelper(m_storage, _olderStorage);
joinKnowledgeHelper(m_memory, _olderMemory);
joinKnowledgeHelper(m_state.storage, _olderStorage);
joinKnowledgeHelper(m_state.memory, _olderMemory);
}
void DataFlowAnalyzer::joinKnowledgeHelper(
@ -381,8 +378,8 @@ void DataFlowAnalyzer::joinKnowledgeHelper(
{
// 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_memory and thus any overlapping write would have cleared the keys
// that are not known to be different inside m_memory already.
// 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;
@ -433,4 +430,3 @@ std::optional<YulString> DataFlowAnalyzer::isSimpleLoad(
return key->name;
return {};
}

View File

@ -101,9 +101,9 @@ public:
void operator()(Block& _block) override;
/// @returns the current value of the given variable, if known - always movable.
AssignedValue const* variableValue(YulString _variable) const { return util::valueOrNullptr(m_value, _variable); }
std::set<YulString> const* references(YulString _variable) const { return util::valueOrNullptr(m_references, _variable); }
std::map<YulString, AssignedValue> const& allValues() const { return m_value; }
AssignedValue const* variableValue(YulString _variable) const { return util::valueOrNullptr(m_state.value, _variable); }
std::set<YulString> const* references(YulString _variable) const { return util::valueOrNullptr(m_state.references, _variable); }
std::map<YulString, AssignedValue> const& allValues() const { return m_state.value; }
std::optional<YulString> storageValue(YulString _key) const;
std::optional<YulString> memoryValue(YulString _key) const;
@ -173,14 +173,20 @@ protected:
/// if this is not provided or the function is not found.
std::map<YulString, SideEffects> m_functionSideEffects;
/// Current values of variables, always movable.
std::map<YulString, AssignedValue> m_value;
/// m_references[a].contains(b) <=> the current expression assigned to a references b
std::unordered_map<YulString, std::set<YulString>> m_references;
private:
struct State
{
/// Current values of variables, always movable.
std::map<YulString, AssignedValue> value;
/// 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, YulString> m_storage;
std::unordered_map<YulString, YulString> m_memory;
std::unordered_map<YulString, YulString> storage;
std::unordered_map<YulString, YulString> memory;
};
State m_state;
protected:
KnowledgeBase m_knowledgeBase;
YulString m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Last) + 1];