Tune Rematerializer

This commit is contained in:
chriseth
2020-01-08 09:56:08 +01:00
parent d1a7ff0fbc
commit 25d3f27c11
12 changed files with 159 additions and 12 deletions
+23 -4
View File
@@ -145,10 +145,14 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
// Save all information. We might rather reinstantiate this class,
// but this could be difficult if it is subclassed.
map<YulString, Expression const*> value;
map<YulString, size_t> variableLoopDepth;
size_t loopDepth{0};
InvertibleRelation<YulString> references;
InvertibleMap<YulString, YulString> storage;
InvertibleMap<YulString, YulString> memory;
m_value.swap(value);
swap(m_value, value);
swap(m_variableLoopDepth, variableLoopDepth);
swap(m_loopDepth, loopDepth);
swap(m_references, references);
swap(m_storage, storage);
swap(m_memory, memory);
@@ -168,7 +172,9 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
// statement.
popScope();
m_value.swap(value);
swap(m_value, value);
swap(m_variableLoopDepth, variableLoopDepth);
swap(m_loopDepth, loopDepth);
swap(m_references, references);
swap(m_storage, storage);
swap(m_memory, memory);
@@ -180,6 +186,8 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
// we would have to deal with more complicated scoping rules.
assertThrow(_for.pre.statements.empty(), OptimizerException, "");
++m_loopDepth;
AssignmentsSinceContinue assignmentsSinceCont;
assignmentsSinceCont(_for.body);
@@ -202,6 +210,8 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
clearKnowledgeIfInvalidated(*_for.condition);
clearKnowledgeIfInvalidated(_for.post);
clearKnowledgeIfInvalidated(_for.body);
--m_loopDepth;
}
void DataFlowAnalyzer::operator()(Block& _block)
@@ -222,7 +232,7 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
movableChecker.visit(*_value);
else
for (auto const& var: _variables)
m_value[var] = &m_zero;
assignValue(var, &m_zero);
if (_value && _variables.size() == 1)
{
@@ -230,7 +240,7 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
// Expression has to be movable and cannot contain a reference
// to the variable that will be assigned to.
if (movableChecker.movable() && !movableChecker.referencedVariables().count(name))
m_value[name] = _value;
assignValue(name, _value);
}
auto const& referencedVariables = movableChecker.referencedVariables();
@@ -296,11 +306,20 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
// Clear the value and update the reference relation.
for (auto const& name: _variables)
{
m_value.erase(name);
m_variableLoopDepth.erase(name);
}
for (auto const& name: _variables)
m_references.eraseKey(name);
}
void DataFlowAnalyzer::assignValue(YulString _variable, Expression const* _value)
{
m_value[_variable] = _value;
m_variableLoopDepth[_variable] = m_loopDepth;
}
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Block const& _block)
{
SideEffectsCollector sideEffects(m_dialect, _block, &m_functionSideEffects);
+7
View File
@@ -110,6 +110,8 @@ protected:
/// for example at points where control flow is merged.
void clearValues(std::set<YulString> _names);
void assignValue(YulString _variable, Expression const* _value);
/// Clears knowledge about storage or memory if they may be modified inside the block.
void clearKnowledgeIfInvalidated(Block const& _block);
@@ -144,6 +146,8 @@ protected:
/// Current values of variables, always movable.
std::map<YulString, Expression const*> m_value;
/// The loop nesting depth of the definition of variables (those used in m_value).
std::map<YulString, size_t> m_variableLoopDepth;
/// m_references.forward[a].contains(b) <=> the current expression assigned to a references b
/// m_references.backward[b].contains(a) <=> the current expression assigned to a references b
InvertibleRelation<YulString> m_references;
@@ -153,6 +157,9 @@ protected:
KnowledgeBase m_knowledgeBase;
/// Current nesting depth of loops.
size_t m_loopDepth{0};
struct Scope
{
explicit Scope(bool _isFunction): isFunction(_isFunction) {}
+6 -1
View File
@@ -78,7 +78,12 @@ void Rematerialiser::visit(Expression& _e)
auto const& value = *m_value.at(name);
size_t refs = m_referenceCounts[name];
size_t cost = CodeCost::codeCost(m_dialect, value);
if (refs <= 1 || cost == 0 || (refs <= 5 && cost <= 1) || m_varsToAlwaysRematerialize.count(name))
if (
(refs <= 1 && m_variableLoopDepth.at(name) == m_loopDepth) ||
cost == 0 ||
(refs <= 5 && cost <= 1) ||
m_varsToAlwaysRematerialize.count(name)
)
{
assertThrow(m_referenceCounts[name] > 0, OptimizerException, "");
for (auto const& ref: m_references.forward[name])
+5 -2
View File
@@ -27,9 +27,10 @@ namespace solidity::yul
{
/**
* Optimisation stage that replaces variables by their most recently assigned expressions,
* Optimisation stage that replaces variable references by those expressions
* that are most recently assigned to the referenced variables,
* but only if the expression is movable and one of the following holds:
* - the variable is referenced exactly once
* - the variable is referenced exactly once (and definition-to-reference does not cross a loop boundary)
* - the value is extremely cheap ("cost" of zero like ``caller()``)
* - the variable is referenced at most 5 times and the value is rather cheap
* ("cost" of at most 1 like a constant up to 0xff)
@@ -68,6 +69,8 @@ protected:
std::set<YulString> _varsToAlwaysRematerialize = {}
);
using DataFlowAnalyzer::operator();
using ASTModifier::visit;
void visit(Expression& _e) override;