diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 2fd5fb0a9..23627653a 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -33,6 +33,11 @@ using namespace std; using namespace solidity; using namespace solidity::yul; +KnowledgeBase::KnowledgeBase(map const& _ssaValues): + m_valuesAreSSA(true), + m_variableValues([_ssaValues](YulString _var) { return util::valueOrNullptr(_ssaValues, _var); }) +{} + bool KnowledgeBase::knownToBeDifferent(YulString _a, YulString _b) { if (optional difference = differenceIfKnownConstant(_a, _b)) @@ -85,11 +90,23 @@ optional KnowledgeBase::valueIfKnownConstant(Expression const& _expression KnowledgeBase::VariableOffset KnowledgeBase::explore(YulString _var) { - // We query the value first so that the variable is reset if it has changed - // since the last call. - Expression const* value = valueOf(_var); - if (VariableOffset const* varOff = util::valueOrNullptr(m_offsets, _var)) - return *varOff; + Expression const* value = nullptr; + if (m_valuesAreSSA) + { + // In SSA, a once determined offset is always valid, so we first see + // if we already computed it. + if (VariableOffset const* varOff = util::valueOrNullptr(m_offsets, _var)) + return *varOff; + value = valueOf(_var); + } + else + { + // For non-SSA, we query the value first so that the variable is reset if it has changed + // since the last call. + value = valueOf(_var); + if (VariableOffset const* varOff = util::valueOrNullptr(m_offsets, _var)) + return *varOff; + } if (value) if (optional offset = explore(*value)) @@ -129,9 +146,12 @@ optional KnowledgeBase::explore(Expression const& Expression const* KnowledgeBase::valueOf(YulString _var) { - Expression const* lastValue = m_lastKnownValue[_var]; AssignedValue const* assignedValue = m_variableValues(_var); Expression const* currentValue = assignedValue ? assignedValue->value : nullptr; + if (m_valuesAreSSA) + return currentValue; + + Expression const* lastValue = m_lastKnownValue[_var]; if (lastValue != currentValue) reset(_var); m_lastKnownValue[_var] = currentValue; @@ -140,6 +160,8 @@ Expression const* KnowledgeBase::valueOf(YulString _var) void KnowledgeBase::reset(YulString _var) { + yulAssert(!m_valuesAreSSA); + m_lastKnownValue.erase(_var); if (VariableOffset const* offset = util::valueOrNullptr(m_offsets, _var)) { diff --git a/libyul/optimiser/KnowledgeBase.h b/libyul/optimiser/KnowledgeBase.h index 82c82a7e9..934b2e21a 100644 --- a/libyul/optimiser/KnowledgeBase.h +++ b/libyul/optimiser/KnowledgeBase.h @@ -47,6 +47,9 @@ struct AssignedValue; * form. * The only requirement is that the assigned values are movable expressions. * + * There is a constructor to provide all SSA values right at the beginning. + * If you use this, the KnowledgeBase will be slightly more efficient. + * * Internally, tries to find groups of variables that have a mutual constant * difference and stores these differences always relative to a specific * representative variable of the group. @@ -57,9 +60,13 @@ struct AssignedValue; class KnowledgeBase { public: + /// Constructor for arbitrary value callback that allows for variable values + /// to change in between calls to functions of this class. KnowledgeBase(std::function _variableValues): m_variableValues(std::move(_variableValues)) {} + /// Constructor to use if source code is in SSA form and values are constant. + KnowledgeBase(std::map const& _ssaValues); bool knownToBeDifferent(YulString _a, YulString _b); std::optional differenceIfKnownConstant(YulString _a, YulString _b); @@ -91,6 +98,8 @@ private: VariableOffset setOffset(YulString _variable, VariableOffset _value); + /// If true, we can assume that variable values never change and skip some steps. + bool m_valuesAreSSA = false; /// Callback to retrieve the current value of a variable. std::function m_variableValues; diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index 245533395..3df9c6319 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -104,7 +104,7 @@ UnusedStoreEliminator::UnusedStoreEliminator( m_functionSideEffects(_functionSideEffects), m_controlFlowSideEffects(_controlFlowSideEffects), m_ssaValues(_ssaValues), - m_knowledgeBase([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }) + m_knowledgeBase(_ssaValues) {} void UnusedStoreEliminator::operator()(FunctionCall const& _functionCall)