Refactor data flow analyzer state access.

This commit is contained in:
chriseth 2022-03-10 18:25:00 +01:00
parent e154d43176
commit eab4ca906c
14 changed files with 81 additions and 48 deletions

View File

@ -95,10 +95,10 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
if (Identifier const* identifier = get_if<Identifier>(&_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<Identifier>(m_value.at(identifierName).value))
assertThrow(assignedValue->value, OptimizerException, "");
if (Identifier const* value = get_if<Identifier>(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

View File

@ -24,6 +24,7 @@
#include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/KnowledgeBase.h>
#include <libyul/AST.h>
#include <libyul/Dialect.h>
#include <libyul/Exceptions.h>
@ -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<unsigned>(StoreLoadLocation::Memory)] = builtin->name;
@ -218,6 +219,22 @@ void DataFlowAnalyzer::operator()(Block& _block)
assertThrow(numScopes == m_variableScopes.size(), OptimizerException, "");
}
optional<YulString> DataFlowAnalyzer::storageValue(YulString _key) const
{
if (YulString const* value = util::valueOrNullptr(m_storage, _key))
return *value;
else
return nullopt;
}
optional<YulString> DataFlowAnalyzer::memoryValue(YulString _key) const
{
if (YulString const* value = util::valueOrNullptr(m_memory, _key))
return *value;
else
return nullopt;
}
void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expression* _value, bool _isDeclaration)
{
if (!_isDeclaration)
@ -386,8 +403,8 @@ bool DataFlowAnalyzer::inScope(YulString _variableName) const
optional<u256> DataFlowAnalyzer::valueOfIdentifier(YulString const& _name)
{
if (m_value.count(_name))
if (Literal const* literal = get_if<Literal>(m_value.at(_name).value))
if (AssignedValue const* value = variableValue(_name))
if (Literal const* literal = get_if<Literal>(value->value))
return valueOfLiteral(*literal);
return nullopt;
}
@ -416,3 +433,4 @@ std::optional<YulString> DataFlowAnalyzer::isSimpleLoad(
return key->name;
return {};
}

View File

@ -29,6 +29,7 @@
#include <libyul/AST.h> // Needed for m_zero below.
#include <libyul/SideEffects.h>
#include <libsolutil/Numeric.h>
#include <libsolutil/Common.h>
#include <map>
@ -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<YulString> const* references(YulString _variable) const { return util::valueOrNullptr(m_references, _variable); }
std::map<YulString, AssignedValue> const& allValues() const { return m_value; }
std::optional<YulString> storageValue(YulString _key) const;
std::optional<YulString> memoryValue(YulString _key) const;
protected:
/// Registers the assignment.
void handleAssignment(std::set<YulString> const& _names, Expression* _value, bool _isDeclaration);

View File

@ -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<YulString> 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<YulString> currentValue = memoryValue(vars->first))
if (*currentValue == vars->second)
m_pendingRemovals.insert(&_statement);
}

View File

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

View File

@ -80,8 +80,8 @@ bool KnowledgeBase::knownToBeZero(YulString _a)
optional<u256> KnowledgeBase::valueIfKnownConstant(YulString _a)
{
if (m_variableValues.count(_a))
if (Literal const* literal = get_if<Literal>(m_variableValues.at(_a).value))
if (AssignedValue const* value = m_variableValues(_a))
if (Literal const* literal = get_if<Literal>(value->value))
return valueOfLiteral(*literal);
return {};
}

View File

@ -28,6 +28,7 @@
#include <libsolutil/Numeric.h>
#include <map>
#include <functional>
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<YulString, AssignedValue> const& _variableValues):
KnowledgeBase(
Dialect const& _dialect,
std::function<AssignedValue const*(YulString)> _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<YulString, AssignedValue> const& m_variableValues;
std::function<AssignedValue const*(YulString)> m_variableValues;
size_t m_counter = 0;
};

View File

@ -82,12 +82,12 @@ void LoadResolver::tryResolve(
YulString key = std::get<Identifier>(_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<YulString> value = memoryValue(memoryKey->name);
if (value && inScope(*value))
{
optional<u256> memoryContent = valueOfIdentifier(*memoryValue);
optional<u256> memoryContent = valueOfIdentifier(*value);
optional<u256> byteLength = valueOfIdentifier(length->name);
if (memoryContent && byteLength && *byteLength <= 32)
{

View File

@ -79,16 +79,15 @@ void Rematerialiser::visit(Expression& _e)
{
Identifier& identifier = std::get<Identifier>(_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<Identifier>(_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<Literal>(*value))
_e = *value;
assertThrow(value->value, OptimizerException, "");
if (holds_alternative<Literal>(*value->value))
_e = *value->value;
}
}
DataFlowAnalyzer::visit(_e);

View File

@ -41,7 +41,7 @@ using namespace solidity::yul;
SimplificationRules::Rule const* SimplificationRules::findFirstMatch(
Expression const& _expr,
Dialect const& _dialect,
map<YulString, AssignedValue> const& _ssaValues
function<AssignedValue const*(YulString)> const& _ssaValues
)
{
auto instruction = instructionAndArguments(_dialect, _expr);
@ -138,7 +138,7 @@ void Pattern::setMatchGroup(unsigned _group, map<unsigned, Expression const*>& _
bool Pattern::matches(
Expression const& _expr,
Dialect const& _dialect,
map<YulString, AssignedValue> const& _ssaValues
function<AssignedValue const*(YulString)> const& _ssaValues
) const
{
Expression const* expr = &_expr;
@ -148,8 +148,8 @@ bool Pattern::matches(
if (m_kind != PatternKind::Any && holds_alternative<Identifier>(_expr))
{
YulString varName = std::get<Identifier>(_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, "");

View File

@ -62,7 +62,7 @@ public:
static Rule const* findFirstMatch(
Expression const& _expr,
Dialect const& _dialect,
std::map<YulString, AssignedValue> const& _ssaValues
std::function<AssignedValue const*(YulString)> 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<YulString, AssignedValue> const& _ssaValues
std::function<AssignedValue const*(YulString)> const& _ssaValues
) const;
std::vector<Pattern> arguments() const { return m_arguments; }

View File

@ -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<YulString> const* ref = references(candidate);
cand[*cost * numRef].emplace_back(candidate, ref ? move(*ref) : set<YulString>{});
}
}
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<Identifier>(_e).name;
if (m_expressionCodeCost.count(name))
{
if (!m_value.count(name))
if (!variableValue(name))
rematImpossible(name);
else
++m_numReferences[name];

View File

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

View File

@ -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};