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::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)
{
if (optional<u256> difference = differenceIfKnownConstant(_a, _b))
@ -85,11 +90,23 @@ optional<u256> 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<VariableOffset> offset = explore(*value))
@ -129,9 +146,12 @@ optional<KnowledgeBase::VariableOffset> 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))
{

View File

@ -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<AssignedValue const*(YulString)> _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);
std::optional<u256> 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<AssignedValue const*(YulString)> m_variableValues;

View File

@ -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)