mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Various optimizations for the DataFlowAnalyzer.
This commit is contained in:
parent
e9dcd4f813
commit
625d402dbb
@ -18,8 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
/**
|
||||
* Data structure that keeps track of values and keys of a mapping.
|
||||
@ -27,68 +26,41 @@
|
||||
template <class K, class V>
|
||||
struct InvertibleMap
|
||||
{
|
||||
std::map<K, V> values;
|
||||
// references[x] == {y | values[y] == x}
|
||||
std::map<V, std::set<K>> references;
|
||||
std::unordered_map<K, V> values;
|
||||
|
||||
void set(K _key, V _value)
|
||||
{
|
||||
if (values.count(_key))
|
||||
references[values[_key]].erase(_key);
|
||||
values[_key] = _value;
|
||||
references[_value].insert(_key);
|
||||
}
|
||||
|
||||
std::optional<V> fetch(K _key)
|
||||
{
|
||||
auto it = values.find(_key);
|
||||
if (it == values.end())
|
||||
return std::nullopt;
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void eraseKey(K _key)
|
||||
{
|
||||
if (values.count(_key))
|
||||
references[values[_key]].erase(_key);
|
||||
values.erase(_key);
|
||||
}
|
||||
|
||||
void eraseValue(V _value)
|
||||
{
|
||||
if (references.count(_value))
|
||||
auto it = values.begin();
|
||||
while (it != values.end())
|
||||
{
|
||||
for (V v: references[_value])
|
||||
values.erase(v);
|
||||
references.erase(_value);
|
||||
if (it->second == _value)
|
||||
it = values.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
values.clear();
|
||||
references.clear();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct InvertibleRelation
|
||||
{
|
||||
/// forward[x] contains y <=> backward[y] contains x
|
||||
std::map<T, std::set<T>> forward;
|
||||
std::map<T, std::set<T>> backward;
|
||||
|
||||
void insert(T _key, T _value)
|
||||
{
|
||||
forward[_key].insert(_value);
|
||||
backward[_value].insert(_key);
|
||||
}
|
||||
|
||||
void set(T _key, std::set<T> _values)
|
||||
{
|
||||
for (T v: forward[_key])
|
||||
backward[v].erase(_key);
|
||||
for (T v: _values)
|
||||
backward[v].insert(_key);
|
||||
forward[_key] = std::move(_values);
|
||||
}
|
||||
|
||||
void eraseKey(T _key)
|
||||
{
|
||||
for (auto const& v: forward[_key])
|
||||
backward[v].erase(_key);
|
||||
forward.erase(_key);
|
||||
}
|
||||
};
|
||||
|
@ -168,3 +168,13 @@ inline YulString operator "" _yulstring(char const* _string, std::size_t _size)
|
||||
}
|
||||
|
||||
}
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash<solidity::yul::YulString>
|
||||
{
|
||||
size_t operator()(solidity::yul::YulString const& x) const
|
||||
{
|
||||
return static_cast<size_t>(x.hash());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -89,12 +89,9 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
|
||||
if (m_value.count(name))
|
||||
{
|
||||
assertThrow(m_value.at(name).value, OptimizerException, "");
|
||||
if (holds_alternative<Identifier>(*m_value.at(name).value))
|
||||
{
|
||||
YulString value = std::get<Identifier>(*m_value.at(name).value).name;
|
||||
assertThrow(inScope(value), OptimizerException, "");
|
||||
_e = Identifier{locationOf(_e), value};
|
||||
}
|
||||
if (Identifier const* value = get_if<Identifier>(m_value.at(name).value))
|
||||
if (inScope(value->name))
|
||||
_e = Identifier{locationOf(_e), value->name};
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -103,9 +100,8 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
|
||||
for (auto const& [variable, value]: m_value)
|
||||
{
|
||||
assertThrow(value.value, OptimizerException, "");
|
||||
if (SyntacticallyEqual{}(_e, *value.value))
|
||||
if (SyntacticallyEqual{}(_e, *value.value) && inScope(variable))
|
||||
{
|
||||
assertThrow(inScope(variable), OptimizerException, "");
|
||||
_e = Identifier{locationOf(_e), variable};
|
||||
break;
|
||||
}
|
||||
|
@ -62,26 +62,26 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement)
|
||||
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement))
|
||||
{
|
||||
ASTModifier::operator()(_statement);
|
||||
set<YulString> keysToErase;
|
||||
for (auto const& item: m_storage.values)
|
||||
auto it = m_storage.values.begin();
|
||||
while (it != m_storage.values.end())
|
||||
if (!(
|
||||
m_knowledgeBase.knownToBeDifferent(vars->first, item.first) ||
|
||||
m_knowledgeBase.knownToBeEqual(vars->second, item.second)
|
||||
m_knowledgeBase.knownToBeDifferent(vars->first, it->first) ||
|
||||
m_knowledgeBase.knownToBeEqual(vars->second, it->second)
|
||||
))
|
||||
keysToErase.insert(item.first);
|
||||
for (YulString const& key: keysToErase)
|
||||
m_storage.eraseKey(key);
|
||||
it = m_storage.values.erase(it);
|
||||
else
|
||||
++it;
|
||||
m_storage.set(vars->first, vars->second);
|
||||
}
|
||||
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement))
|
||||
{
|
||||
ASTModifier::operator()(_statement);
|
||||
set<YulString> keysToErase;
|
||||
for (auto const& item: m_memory.values)
|
||||
if (!m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, item.first))
|
||||
keysToErase.insert(item.first);
|
||||
for (YulString const& key: keysToErase)
|
||||
m_memory.eraseKey(key);
|
||||
auto it = m_memory.values.begin();
|
||||
while (it != m_memory.values.end())
|
||||
if (!m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, it->first))
|
||||
it = m_memory.values.erase(it);
|
||||
else
|
||||
++it;
|
||||
m_memory.set(vars->first, vars->second);
|
||||
}
|
||||
else
|
||||
@ -163,7 +163,7 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
|
||||
// but this could be difficult if it is subclassed.
|
||||
map<YulString, AssignedValue> value;
|
||||
size_t loopDepth{0};
|
||||
InvertibleRelation<YulString> references;
|
||||
unordered_map<YulString, set<YulString>> references;
|
||||
InvertibleMap<YulString, YulString> storage;
|
||||
InvertibleMap<YulString, YulString> memory;
|
||||
swap(m_value, value);
|
||||
@ -261,7 +261,7 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
|
||||
auto const& referencedVariables = movableChecker.referencedVariables();
|
||||
for (auto const& name: _variables)
|
||||
{
|
||||
m_references.set(name, referencedVariables);
|
||||
m_references[name] = referencedVariables;
|
||||
if (!_isDeclaration)
|
||||
{
|
||||
// assignment to slot denoted by "name"
|
||||
@ -298,7 +298,6 @@ void DataFlowAnalyzer::pushScope(bool _functionScope)
|
||||
|
||||
void DataFlowAnalyzer::popScope()
|
||||
{
|
||||
clearValues(std::move(m_variableScopes.back().variables));
|
||||
m_variableScopes.pop_back();
|
||||
}
|
||||
|
||||
@ -320,28 +319,28 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
|
||||
// First clear storage knowledge, because we do not have to clear
|
||||
// storage knowledge of variables whose expression has changed,
|
||||
// since the value is still unchanged.
|
||||
for (auto const& name: _variables)
|
||||
{
|
||||
// clear slot denoted by "name"
|
||||
m_storage.eraseKey(name);
|
||||
// clear slot contents denoted by "name"
|
||||
m_storage.eraseValue(name);
|
||||
// assignment to slot denoted by "name"
|
||||
m_memory.eraseKey(name);
|
||||
// assignment to slot contents denoted by "name"
|
||||
m_memory.eraseValue(name);
|
||||
}
|
||||
auto clear = [&](auto&& values) {
|
||||
auto it = values.begin();
|
||||
while (it != values.end())
|
||||
if (_variables.count(it->first) || _variables.count(it->second))
|
||||
it = values.erase(it);
|
||||
else
|
||||
++it;
|
||||
};
|
||||
clear(m_storage.values);
|
||||
clear(m_memory.values);
|
||||
|
||||
// Also clear variables that reference variables to be cleared.
|
||||
for (auto const& name: _variables)
|
||||
for (auto const& ref: m_references.backward[name])
|
||||
_variables.emplace(ref);
|
||||
for (auto const& [ref, names]: m_references)
|
||||
if (names.count(name))
|
||||
_variables.emplace(ref);
|
||||
|
||||
// Clear the value and update the reference relation.
|
||||
for (auto const& name: _variables)
|
||||
m_value.erase(name);
|
||||
for (auto const& name: _variables)
|
||||
m_references.eraseKey(name);
|
||||
m_references.erase(name);
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::assignValue(YulString _variable, Expression const* _value)
|
||||
@ -385,15 +384,15 @@ void DataFlowAnalyzer::joinKnowledgeHelper(
|
||||
// This also works for memory because _older is an "older version"
|
||||
// of m_memory and thus any overlapping write would have cleared the keys
|
||||
// that are not known to be different inside m_memory already.
|
||||
set<YulString> keysToErase;
|
||||
for (auto const& item: _this.values)
|
||||
auto it = _this.values.begin();
|
||||
while (it != _this.values.end())
|
||||
{
|
||||
auto it = _older.values.find(item.first);
|
||||
if (it == _older.values.end() || it->second != item.second)
|
||||
keysToErase.insert(item.first);
|
||||
auto oldit = _older.values.find(it->first);
|
||||
if (oldit != _older.values.end() && it->second == oldit->second)
|
||||
++it;
|
||||
else
|
||||
it = _this.values.erase(it);
|
||||
}
|
||||
for (auto const& key: keysToErase)
|
||||
_this.eraseKey(key);
|
||||
}
|
||||
|
||||
bool DataFlowAnalyzer::inScope(YulString _variableName) const
|
||||
|
@ -163,9 +163,8 @@ protected:
|
||||
|
||||
/// Current values of variables, always movable.
|
||||
std::map<YulString, AssignedValue> m_value;
|
||||
/// 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;
|
||||
/// m_references[a].contains(b) <=> the current expression assigned to a references b
|
||||
std::unordered_map<YulString, std::set<YulString>> m_references;
|
||||
|
||||
InvertibleMap<YulString, YulString> m_storage;
|
||||
InvertibleMap<YulString, YulString> m_memory;
|
||||
|
@ -65,15 +65,12 @@ void LoadResolver::tryResolve(
|
||||
return;
|
||||
|
||||
YulString key = std::get<Identifier>(_arguments.at(0)).name;
|
||||
if (
|
||||
_location == StoreLoadLocation::Storage &&
|
||||
m_storage.values.count(key)
|
||||
)
|
||||
_e = Identifier{locationOf(_e), m_storage.values[key]};
|
||||
else if (
|
||||
m_optimizeMLoad &&
|
||||
_location == StoreLoadLocation::Memory &&
|
||||
m_memory.values.count(key)
|
||||
)
|
||||
_e = Identifier{locationOf(_e), m_memory.values[key]};
|
||||
if (_location == StoreLoadLocation::Storage)
|
||||
{
|
||||
if (auto value = m_storage.fetch(key))
|
||||
_e = Identifier{locationOf(_e), *value};
|
||||
}
|
||||
else if (m_optimizeMLoad && _location == StoreLoadLocation::Memory)
|
||||
if (auto value = m_memory.fetch(key))
|
||||
_e = Identifier{locationOf(_e), *value};
|
||||
}
|
||||
|
@ -86,13 +86,17 @@ void Rematerialiser::visit(Expression& _e)
|
||||
)
|
||||
{
|
||||
assertThrow(m_referenceCounts[name] > 0, OptimizerException, "");
|
||||
for (auto const& ref: m_references.forward[name])
|
||||
assertThrow(inScope(ref), OptimizerException, "");
|
||||
// update reference counts
|
||||
m_referenceCounts[name]--;
|
||||
for (auto const& ref: ReferencesCounter::countReferences(*value.value))
|
||||
m_referenceCounts[ref.first] += ref.second;
|
||||
_e = (ASTCopier{}).translate(*value.value);
|
||||
bool allInScope = true;
|
||||
for (auto const& ref: m_references[name])
|
||||
allInScope = allInScope && inScope(ref);
|
||||
if (allInScope)
|
||||
{
|
||||
// update reference counts
|
||||
m_referenceCounts[name]--;
|
||||
for (auto const& ref: ReferencesCounter::countReferences(*value.value))
|
||||
m_referenceCounts[ref.first] += ref.second;
|
||||
_e = (ASTCopier{}).translate(*value.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public:
|
||||
for (auto const& codeCost: m_expressionCodeCost)
|
||||
{
|
||||
size_t numRef = m_numReferences[codeCost.first];
|
||||
cand.emplace(make_tuple(codeCost.second * numRef, codeCost.first, m_references.forward[codeCost.first]));
|
||||
cand.emplace(make_tuple(codeCost.second * numRef, codeCost.first, m_references[codeCost.first]));
|
||||
}
|
||||
return cand;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user