From eab4ca906c2bcf9b44687f0eb5d2987bf75ec4dd Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Mar 2022 18:25:00 +0100 Subject: [PATCH] Refactor data flow analyzer state access. --- .../CommonSubexpressionEliminator.cpp | 8 +++--- libyul/optimiser/DataFlowAnalyzer.cpp | 24 +++++++++++++++--- libyul/optimiser/DataFlowAnalyzer.h | 9 +++++++ libyul/optimiser/EqualStoreEliminator.cpp | 4 +-- libyul/optimiser/ExpressionSimplifier.cpp | 6 ++++- libyul/optimiser/KnowledgeBase.cpp | 4 +-- libyul/optimiser/KnowledgeBase.h | 12 +++++---- libyul/optimiser/LoadResolver.cpp | 10 ++++---- libyul/optimiser/Rematerialiser.cpp | 25 +++++++++---------- libyul/optimiser/SimplificationRules.cpp | 8 +++--- libyul/optimiser/SimplificationRules.h | 4 +-- libyul/optimiser/StackCompressor.cpp | 9 ++++--- libyul/optimiser/UnusedStoreEliminator.cpp | 4 +-- test/libyul/KnowledgeBaseTest.cpp | 2 +- 14 files changed, 81 insertions(+), 48 deletions(-) diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 16cdfd893..d25beb6bd 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -95,10 +95,10 @@ void CommonSubexpressionEliminator::visit(Expression& _e) if (Identifier const* identifier = get_if(&_e)) { YulString identifierName = identifier->name; - if (m_value.count(identifierName)) + if (AssignedValue const* assignedValue = variableValue(identifierName)) { - assertThrow(m_value.at(identifierName).value, OptimizerException, ""); - if (Identifier const* value = get_if(m_value.at(identifierName).value)) + assertThrow(assignedValue->value, OptimizerException, ""); + if (Identifier const* value = get_if(assignedValue->value)) if (inScope(value->name)) _e = Identifier{debugDataOf(_e), value->name}; } @@ -106,7 +106,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e) else { // TODO this search is rather inefficient. - for (auto const& [variable, value]: m_value) + for (auto const& [variable, value]: allValues()) { assertThrow(value.value, OptimizerException, ""); // Prevent using the default value of return variables diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index c1ad2116a..34622e738 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -47,7 +48,7 @@ DataFlowAnalyzer::DataFlowAnalyzer( ): m_dialect(_dialect), m_functionSideEffects(std::move(_functionSideEffects)), - m_knowledgeBase(_dialect, m_value) + m_knowledgeBase(_dialect, [this](YulString _var) { return variableValue(_var); }) { if (auto const* builtin = _dialect.memoryStoreFunction(YulString{})) m_storeFunctionName[static_cast(StoreLoadLocation::Memory)] = builtin->name; @@ -218,6 +219,22 @@ void DataFlowAnalyzer::operator()(Block& _block) assertThrow(numScopes == m_variableScopes.size(), OptimizerException, ""); } +optional DataFlowAnalyzer::storageValue(YulString _key) const +{ + if (YulString const* value = util::valueOrNullptr(m_storage, _key)) + return *value; + else + return nullopt; +} + +optional DataFlowAnalyzer::memoryValue(YulString _key) const +{ + if (YulString const* value = util::valueOrNullptr(m_memory, _key)) + return *value; + else + return nullopt; +} + void DataFlowAnalyzer::handleAssignment(set const& _variables, Expression* _value, bool _isDeclaration) { if (!_isDeclaration) @@ -386,8 +403,8 @@ bool DataFlowAnalyzer::inScope(YulString _variableName) const optional DataFlowAnalyzer::valueOfIdentifier(YulString const& _name) { - if (m_value.count(_name)) - if (Literal const* literal = get_if(m_value.at(_name).value)) + if (AssignedValue const* value = variableValue(_name)) + if (Literal const* literal = get_if(value->value)) return valueOfLiteral(*literal); return nullopt; } @@ -416,3 +433,4 @@ std::optional DataFlowAnalyzer::isSimpleLoad( return key->name; return {}; } + diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index deaa71f89..56e48fed5 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -29,6 +29,7 @@ #include // Needed for m_zero below. #include +#include #include #include @@ -38,6 +39,7 @@ namespace solidity::yul { struct Dialect; struct SideEffects; +class KnowledgeBase; /// Value assigned to a variable. struct AssignedValue @@ -98,6 +100,13 @@ public: void operator()(ForLoop&) override; 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 const* references(YulString _variable) const { return util::valueOrNullptr(m_references, _variable); } + std::map const& allValues() const { return m_value; } + std::optional storageValue(YulString _key) const; + std::optional memoryValue(YulString _key) const; + protected: /// Registers the assignment. void handleAssignment(std::set const& _names, Expression* _value, bool _isDeclaration); diff --git a/libyul/optimiser/EqualStoreEliminator.cpp b/libyul/optimiser/EqualStoreEliminator.cpp index 56f9902f8..542d8d50b 100644 --- a/libyul/optimiser/EqualStoreEliminator.cpp +++ b/libyul/optimiser/EqualStoreEliminator.cpp @@ -54,13 +54,13 @@ void EqualStoreEliminator::visit(Statement& _statement) { if (auto vars = isSimpleStore(StoreLoadLocation::Storage, *expression)) { - if (auto const* currentValue = util::valueOrNullptr(m_storage, vars->first)) + if (optional currentValue = storageValue(vars->first)) if (*currentValue == vars->second) m_pendingRemovals.insert(&_statement); } else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, *expression)) { - if (auto const* currentValue = util::valueOrNullptr(m_memory, vars->first)) + if (optional currentValue = memoryValue(vars->first)) if (*currentValue == vars->second) m_pendingRemovals.insert(&_statement); } diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index 736ebe4b9..8c3a038f1 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -38,6 +38,10 @@ void ExpressionSimplifier::visit(Expression& _expression) { ASTModifier::visit(_expression); - while (auto const* match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value)) + while (auto const* match = SimplificationRules::findFirstMatch( + _expression, + m_dialect, + [this](YulString _var) { return variableValue(_var); } + )) _expression = match->action().toExpression(debugDataOf(_expression)); } diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 5b5c07dda..460f6707f 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -80,8 +80,8 @@ bool KnowledgeBase::knownToBeZero(YulString _a) optional KnowledgeBase::valueIfKnownConstant(YulString _a) { - if (m_variableValues.count(_a)) - if (Literal const* literal = get_if(m_variableValues.at(_a).value)) + if (AssignedValue const* value = m_variableValues(_a)) + if (Literal const* literal = get_if(value->value)) return valueOfLiteral(*literal); return {}; } diff --git a/libyul/optimiser/KnowledgeBase.h b/libyul/optimiser/KnowledgeBase.h index 75e060eb1..999d0e312 100644 --- a/libyul/optimiser/KnowledgeBase.h +++ b/libyul/optimiser/KnowledgeBase.h @@ -28,6 +28,7 @@ #include #include +#include namespace solidity::yul { @@ -37,15 +38,16 @@ struct AssignedValue; /** * Class that can answer questions about values of variables and their relations. - * - * The reference to the map of values provided at construction is assumed to be updating. */ class KnowledgeBase { public: - KnowledgeBase(Dialect const& _dialect, std::map const& _variableValues): + KnowledgeBase( + Dialect const& _dialect, + std::function _variableValues + ): m_dialect(_dialect), - m_variableValues(_variableValues) + m_variableValues(std::move(_variableValues)) {} bool knownToBeDifferent(YulString _a, YulString _b); @@ -60,7 +62,7 @@ private: Expression simplifyRecursively(Expression _expression); Dialect const& m_dialect; - std::map const& m_variableValues; + std::function m_variableValues; size_t m_counter = 0; }; diff --git a/libyul/optimiser/LoadResolver.cpp b/libyul/optimiser/LoadResolver.cpp index 550ca6fb3..abcfbaabb 100644 --- a/libyul/optimiser/LoadResolver.cpp +++ b/libyul/optimiser/LoadResolver.cpp @@ -82,12 +82,12 @@ void LoadResolver::tryResolve( YulString key = std::get(_arguments.at(0)).name; if (_location == StoreLoadLocation::Storage) { - if (auto value = util::valueOrNullptr(m_storage, key)) + if (auto value = storageValue(key)) if (inScope(*value)) _e = Identifier{debugDataOf(_e), *value}; } else if (!m_containsMSize && _location == StoreLoadLocation::Memory) - if (auto value = util::valueOrNullptr(m_memory, key)) + if (auto value = memoryValue(key)) if (inScope(*value)) _e = Identifier{debugDataOf(_e), *value}; } @@ -129,10 +129,10 @@ void LoadResolver::tryEvaluateKeccak( if (costOfLiteral > costOfKeccak) return; - auto memoryValue = util::valueOrNullptr(m_memory, memoryKey->name); - if (memoryValue && inScope(*memoryValue)) + optional value = memoryValue(memoryKey->name); + if (value && inScope(*value)) { - optional memoryContent = valueOfIdentifier(*memoryValue); + optional memoryContent = valueOfIdentifier(*value); optional byteLength = valueOfIdentifier(length->name); if (memoryContent && byteLength && *byteLength <= 32) { diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index f1597a7f2..6e00fcd8a 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -79,16 +79,15 @@ void Rematerialiser::visit(Expression& _e) { Identifier& identifier = std::get(_e); YulString name = identifier.name; - if (m_value.count(name)) + if (AssignedValue const* value = variableValue(name)) { - assertThrow(m_value.at(name).value, OptimizerException, ""); - AssignedValue const& value = m_value.at(name); + assertThrow(value->value, OptimizerException, ""); size_t refs = m_referenceCounts[name]; - size_t cost = CodeCost::codeCost(m_dialect, *value.value); + size_t cost = CodeCost::codeCost(m_dialect, *value->value); if ( ( !m_onlySelectedVariables && ( - (refs <= 1 && value.loopDepth == m_loopDepth) || + (refs <= 1 && value->loopDepth == m_loopDepth) || cost == 0 || (refs <= 5 && cost <= 1 && m_loopDepth == 0) ) @@ -96,13 +95,14 @@ void Rematerialiser::visit(Expression& _e) ) { assertThrow(m_referenceCounts[name] > 0, OptimizerException, ""); - if (ranges::all_of(m_references[name], [&](auto const& ref) { return inScope(ref); })) + auto variableReferences = references(name); + if (!variableReferences || ranges::all_of(*variableReferences, [&](auto const& ref) { return inScope(ref); })) { // update reference counts m_referenceCounts[name]--; - for (auto const& ref: ReferencesCounter::countReferences(*value.value)) + for (auto const& ref: ReferencesCounter::countReferences(*value->value)) m_referenceCounts[ref.first] += ref.second; - _e = (ASTCopier{}).translate(*value.value); + _e = (ASTCopier{}).translate(*value->value); } } } @@ -116,12 +116,11 @@ void LiteralRematerialiser::visit(Expression& _e) { Identifier& identifier = std::get(_e); YulString name = identifier.name; - if (m_value.count(name)) + if (AssignedValue const* value = variableValue(name)) { - Expression const* value = m_value.at(name).value; - assertThrow(value, OptimizerException, ""); - if (holds_alternative(*value)) - _e = *value; + assertThrow(value->value, OptimizerException, ""); + if (holds_alternative(*value->value)) + _e = *value->value; } } DataFlowAnalyzer::visit(_e); diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 3e906fcdf..9d0144750 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -41,7 +41,7 @@ using namespace solidity::yul; SimplificationRules::Rule const* SimplificationRules::findFirstMatch( Expression const& _expr, Dialect const& _dialect, - map const& _ssaValues + function const& _ssaValues ) { auto instruction = instructionAndArguments(_dialect, _expr); @@ -138,7 +138,7 @@ void Pattern::setMatchGroup(unsigned _group, map& _ bool Pattern::matches( Expression const& _expr, Dialect const& _dialect, - map const& _ssaValues + function const& _ssaValues ) const { Expression const* expr = &_expr; @@ -148,8 +148,8 @@ bool Pattern::matches( if (m_kind != PatternKind::Any && holds_alternative(_expr)) { YulString varName = std::get(_expr).name; - if (_ssaValues.count(varName)) - if (Expression const* new_expr = _ssaValues.at(varName).value) + if (AssignedValue const* value = _ssaValues(varName)) + if (Expression const* new_expr = value->value) expr = new_expr; } assertThrow(expr, OptimizerException, ""); diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index 77018bb38..7444c47fb 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -62,7 +62,7 @@ public: static Rule const* findFirstMatch( Expression const& _expr, Dialect const& _dialect, - std::map const& _ssaValues + std::function const& _ssaValues ); /// Checks whether the rulelist is non-empty. This is usually enforced @@ -119,7 +119,7 @@ public: bool matches( Expression const& _expr, Dialect const& _dialect, - std::map const& _ssaValues + std::function const& _ssaValues ) const; std::vector arguments() const { return m_arguments; } diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp index 2deef5234..8b06d6625 100644 --- a/libyul/optimiser/StackCompressor.cpp +++ b/libyul/optimiser/StackCompressor.cpp @@ -67,7 +67,8 @@ public: if (size_t const* cost = util::valueOrNullptr(m_expressionCodeCost, candidate)) { size_t numRef = m_numReferences[candidate]; - cand[*cost * numRef].emplace_back(candidate, m_references[candidate]); + set const* ref = references(candidate); + cand[*cost * numRef].emplace_back(candidate, ref ? move(*ref) : set{}); } } return cand; @@ -80,11 +81,11 @@ public: if (_varDecl.variables.size() == 1) { YulString varName = _varDecl.variables.front().name; - if (m_value.count(varName)) + if (AssignedValue const* value = variableValue(varName)) { yulAssert(!m_expressionCodeCost.count(varName), ""); m_candidates.emplace_back(varName); - m_expressionCodeCost[varName] = CodeCost::codeCost(m_dialect, *m_value[varName].value); + m_expressionCodeCost[varName] = CodeCost::codeCost(m_dialect, *value->value); } } } @@ -105,7 +106,7 @@ public: YulString name = std::get(_e).name; if (m_expressionCodeCost.count(name)) { - if (!m_value.count(name)) + if (!variableValue(name)) rematImpossible(name); else ++m_numReferences[name]; diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index e3ff2d77a..343bd720a 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -238,7 +238,7 @@ bool UnusedStoreEliminator::knownUnrelated( UnusedStoreEliminator::Operation const& _op2 ) const { - KnowledgeBase knowledge(m_dialect, m_ssaValues); + KnowledgeBase knowledge(m_dialect, [this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); if (_op1.location != _op2.location) return true; @@ -319,7 +319,7 @@ bool UnusedStoreEliminator::knownCovered( return true; if (_covered.location == Location::Memory) { - KnowledgeBase knowledge(m_dialect, m_ssaValues); + KnowledgeBase knowledge(m_dialect, [this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }); if (_covered.length && knowledge.knownToBeZero(*_covered.length)) return true; diff --git a/test/libyul/KnowledgeBaseTest.cpp b/test/libyul/KnowledgeBaseTest.cpp index ac48f962f..ec2f0313d 100644 --- a/test/libyul/KnowledgeBaseTest.cpp +++ b/test/libyul/KnowledgeBaseTest.cpp @@ -58,7 +58,7 @@ protected: for (auto const& [name, expression]: m_ssaValues.values()) m_values[name].value = expression; - return KnowledgeBase(m_dialect, m_values); + return KnowledgeBase(m_dialect, [this](YulString _var) { return util::valueOrNullptr(m_values, _var); }); } EVMDialect m_dialect{EVMVersion{}, true};