Optimize in case this is SSA.

This commit is contained in:
chriseth 2022-11-10 14:22:40 +01:00
parent 96e2a6d3fe
commit 6bbef64034
3 changed files with 38 additions and 7 deletions

View File

@ -33,6 +33,11 @@ using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::yul; using namespace solidity::yul;
KnowledgeBase::KnowledgeBase(map<YulString, AssignedValue> const& _ssaValues):
m_valuesAreSSA(true),
m_variableValues([_ssaValues](YulString _var) { return util::valueOrNullptr(_ssaValues, _var); })
{}
bool KnowledgeBase::knownToBeDifferent(YulString _a, YulString _b) bool KnowledgeBase::knownToBeDifferent(YulString _a, YulString _b)
{ {
if (optional<u256> difference = differenceIfKnownConstant(_a, _b)) if (optional<u256> difference = differenceIfKnownConstant(_a, _b))
@ -85,11 +90,23 @@ optional<u256> KnowledgeBase::valueIfKnownConstant(Expression const& _expression
KnowledgeBase::VariableOffset KnowledgeBase::explore(YulString _var) KnowledgeBase::VariableOffset KnowledgeBase::explore(YulString _var)
{ {
// We query the value first so that the variable is reset if it has changed Expression const* value = nullptr;
// since the last call. if (m_valuesAreSSA)
Expression const* value = valueOf(_var); {
if (VariableOffset const* varOff = util::valueOrNullptr(m_offsets, _var)) // In SSA, a once determined offset is always valid, so we first see
return *varOff; // 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 (value)
if (optional<VariableOffset> offset = explore(*value)) if (optional<VariableOffset> offset = explore(*value))
@ -129,9 +146,12 @@ optional<KnowledgeBase::VariableOffset> KnowledgeBase::explore(Expression const&
Expression const* KnowledgeBase::valueOf(YulString _var) Expression const* KnowledgeBase::valueOf(YulString _var)
{ {
Expression const* lastValue = m_lastKnownValue[_var];
AssignedValue const* assignedValue = m_variableValues(_var); AssignedValue const* assignedValue = m_variableValues(_var);
Expression const* currentValue = assignedValue ? assignedValue->value : nullptr; Expression const* currentValue = assignedValue ? assignedValue->value : nullptr;
if (m_valuesAreSSA)
return currentValue;
Expression const* lastValue = m_lastKnownValue[_var];
if (lastValue != currentValue) if (lastValue != currentValue)
reset(_var); reset(_var);
m_lastKnownValue[_var] = currentValue; m_lastKnownValue[_var] = currentValue;
@ -140,6 +160,8 @@ Expression const* KnowledgeBase::valueOf(YulString _var)
void KnowledgeBase::reset(YulString _var) void KnowledgeBase::reset(YulString _var)
{ {
yulAssert(!m_valuesAreSSA);
m_lastKnownValue.erase(_var); m_lastKnownValue.erase(_var);
if (VariableOffset const* offset = util::valueOrNullptr(m_offsets, _var)) if (VariableOffset const* offset = util::valueOrNullptr(m_offsets, _var))
{ {

View File

@ -47,6 +47,9 @@ struct AssignedValue;
* form. * form.
* The only requirement is that the assigned values are movable expressions. * 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 * Internally, tries to find groups of variables that have a mutual constant
* difference and stores these differences always relative to a specific * difference and stores these differences always relative to a specific
* representative variable of the group. * representative variable of the group.
@ -57,9 +60,13 @@ struct AssignedValue;
class KnowledgeBase class KnowledgeBase
{ {
public: public:
/// Constructor for arbitrary value callback that allows for variable values
/// to change in between calls to functions of this class.
KnowledgeBase(std::function<AssignedValue const*(YulString)> _variableValues): KnowledgeBase(std::function<AssignedValue const*(YulString)> _variableValues):
m_variableValues(std::move(_variableValues)) m_variableValues(std::move(_variableValues))
{} {}
/// Constructor to use if source code is in SSA form and values are constant.
KnowledgeBase(std::map<YulString, AssignedValue> const& _ssaValues);
bool knownToBeDifferent(YulString _a, YulString _b); bool knownToBeDifferent(YulString _a, YulString _b);
std::optional<u256> differenceIfKnownConstant(YulString _a, YulString _b); std::optional<u256> differenceIfKnownConstant(YulString _a, YulString _b);
@ -91,6 +98,8 @@ private:
VariableOffset setOffset(YulString _variable, VariableOffset _value); 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. /// Callback to retrieve the current value of a variable.
std::function<AssignedValue const*(YulString)> m_variableValues; std::function<AssignedValue const*(YulString)> m_variableValues;

View File

@ -104,7 +104,7 @@ UnusedStoreEliminator::UnusedStoreEliminator(
m_functionSideEffects(_functionSideEffects), m_functionSideEffects(_functionSideEffects),
m_controlFlowSideEffects(_controlFlowSideEffects), m_controlFlowSideEffects(_controlFlowSideEffects),
m_ssaValues(_ssaValues), m_ssaValues(_ssaValues),
m_knowledgeBase([this](YulString _var) { return util::valueOrNullptr(m_ssaValues, _var); }) m_knowledgeBase(_ssaValues)
{} {}
void UnusedStoreEliminator::operator()(FunctionCall const& _functionCall) void UnusedStoreEliminator::operator()(FunctionCall const& _functionCall)