mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12775 from ethereum/refactorDataFlowAnalyzer
Refactor data flow analyzer state access.
This commit is contained in:
commit
5afa2adec2
@ -95,10 +95,10 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
|
|||||||
if (Identifier const* identifier = get_if<Identifier>(&_e))
|
if (Identifier const* identifier = get_if<Identifier>(&_e))
|
||||||
{
|
{
|
||||||
YulString identifierName = identifier->name;
|
YulString identifierName = identifier->name;
|
||||||
if (m_value.count(identifierName))
|
if (AssignedValue const* assignedValue = variableValue(identifierName))
|
||||||
{
|
{
|
||||||
assertThrow(m_value.at(identifierName).value, OptimizerException, "");
|
assertThrow(assignedValue->value, OptimizerException, "");
|
||||||
if (Identifier const* value = get_if<Identifier>(m_value.at(identifierName).value))
|
if (Identifier const* value = get_if<Identifier>(assignedValue->value))
|
||||||
if (inScope(value->name))
|
if (inScope(value->name))
|
||||||
_e = Identifier{debugDataOf(_e), value->name};
|
_e = Identifier{debugDataOf(_e), value->name};
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO this search is rather inefficient.
|
// TODO this search is rather inefficient.
|
||||||
for (auto const& [variable, value]: m_value)
|
for (auto const& [variable, value]: allValues())
|
||||||
{
|
{
|
||||||
assertThrow(value.value, OptimizerException, "");
|
assertThrow(value.value, OptimizerException, "");
|
||||||
// Prevent using the default value of return variables
|
// Prevent using the default value of return variables
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <libyul/optimiser/NameCollector.h>
|
#include <libyul/optimiser/NameCollector.h>
|
||||||
#include <libyul/optimiser/Semantics.h>
|
#include <libyul/optimiser/Semantics.h>
|
||||||
|
#include <libyul/optimiser/KnowledgeBase.h>
|
||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
#include <libyul/Dialect.h>
|
#include <libyul/Dialect.h>
|
||||||
#include <libyul/Exceptions.h>
|
#include <libyul/Exceptions.h>
|
||||||
@ -47,7 +48,7 @@ DataFlowAnalyzer::DataFlowAnalyzer(
|
|||||||
):
|
):
|
||||||
m_dialect(_dialect),
|
m_dialect(_dialect),
|
||||||
m_functionSideEffects(std::move(_functionSideEffects)),
|
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{}))
|
if (auto const* builtin = _dialect.memoryStoreFunction(YulString{}))
|
||||||
m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Memory)] = builtin->name;
|
m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Memory)] = builtin->name;
|
||||||
@ -64,20 +65,20 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement)
|
|||||||
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement))
|
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement))
|
||||||
{
|
{
|
||||||
ASTModifier::operator()(_statement);
|
ASTModifier::operator()(_statement);
|
||||||
cxx20::erase_if(m_storage, mapTuple([&](auto&& key, auto&& value) {
|
cxx20::erase_if(m_state.storage, mapTuple([&](auto&& key, auto&& value) {
|
||||||
return
|
return
|
||||||
!m_knowledgeBase.knownToBeDifferent(vars->first, key) &&
|
!m_knowledgeBase.knownToBeDifferent(vars->first, key) &&
|
||||||
!m_knowledgeBase.knownToBeEqual(vars->second, value);
|
!m_knowledgeBase.knownToBeEqual(vars->second, value);
|
||||||
}));
|
}));
|
||||||
m_storage[vars->first] = vars->second;
|
m_state.storage[vars->first] = vars->second;
|
||||||
}
|
}
|
||||||
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement))
|
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement))
|
||||||
{
|
{
|
||||||
ASTModifier::operator()(_statement);
|
ASTModifier::operator()(_statement);
|
||||||
cxx20::erase_if(m_memory, mapTuple([&](auto&& key, auto&& /* value */) {
|
cxx20::erase_if(m_state.memory, mapTuple([&](auto&& key, auto&& /* value */) {
|
||||||
return !m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, key);
|
return !m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, key);
|
||||||
}));
|
}));
|
||||||
m_memory[vars->first] = vars->second;
|
m_state.memory[vars->first] = vars->second;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -116,8 +117,8 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
|
|||||||
void DataFlowAnalyzer::operator()(If& _if)
|
void DataFlowAnalyzer::operator()(If& _if)
|
||||||
{
|
{
|
||||||
clearKnowledgeIfInvalidated(*_if.condition);
|
clearKnowledgeIfInvalidated(*_if.condition);
|
||||||
unordered_map<YulString, YulString> storage = m_storage;
|
unordered_map<YulString, YulString> storage = m_state.storage;
|
||||||
unordered_map<YulString, YulString> memory = m_memory;
|
unordered_map<YulString, YulString> memory = m_state.memory;
|
||||||
|
|
||||||
ASTModifier::operator()(_if);
|
ASTModifier::operator()(_if);
|
||||||
|
|
||||||
@ -133,8 +134,8 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
|
|||||||
set<YulString> assignedVariables;
|
set<YulString> assignedVariables;
|
||||||
for (auto& _case: _switch.cases)
|
for (auto& _case: _switch.cases)
|
||||||
{
|
{
|
||||||
unordered_map<YulString, YulString> storage = m_storage;
|
unordered_map<YulString, YulString> storage = m_state.storage;
|
||||||
unordered_map<YulString, YulString> memory = m_memory;
|
unordered_map<YulString, YulString> memory = m_state.memory;
|
||||||
(*this)(_case.body);
|
(*this)(_case.body);
|
||||||
joinKnowledge(storage, memory);
|
joinKnowledge(storage, memory);
|
||||||
|
|
||||||
@ -153,11 +154,8 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
|
|||||||
{
|
{
|
||||||
// Save all information. We might rather reinstantiate this class,
|
// Save all information. We might rather reinstantiate this class,
|
||||||
// but this could be difficult if it is subclassed.
|
// but this could be difficult if it is subclassed.
|
||||||
ScopedSaveAndRestore valueResetter(m_value, {});
|
ScopedSaveAndRestore stateResetter(m_state, {});
|
||||||
ScopedSaveAndRestore loopDepthResetter(m_loopDepth, 0u);
|
ScopedSaveAndRestore loopDepthResetter(m_loopDepth, 0u);
|
||||||
ScopedSaveAndRestore referencesResetter(m_references, {});
|
|
||||||
ScopedSaveAndRestore storageResetter(m_storage, {});
|
|
||||||
ScopedSaveAndRestore memoryResetter(m_memory, {});
|
|
||||||
pushScope(true);
|
pushScope(true);
|
||||||
|
|
||||||
for (auto const& parameter: _fun.parameters)
|
for (auto const& parameter: _fun.parameters)
|
||||||
@ -218,6 +216,22 @@ void DataFlowAnalyzer::operator()(Block& _block)
|
|||||||
assertThrow(numScopes == m_variableScopes.size(), OptimizerException, "");
|
assertThrow(numScopes == m_variableScopes.size(), OptimizerException, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optional<YulString> DataFlowAnalyzer::storageValue(YulString _key) const
|
||||||
|
{
|
||||||
|
if (YulString const* value = util::valueOrNullptr(m_state.storage, _key))
|
||||||
|
return *value;
|
||||||
|
else
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<YulString> DataFlowAnalyzer::memoryValue(YulString _key) const
|
||||||
|
{
|
||||||
|
if (YulString const* value = util::valueOrNullptr(m_state.memory, _key))
|
||||||
|
return *value;
|
||||||
|
else
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expression* _value, bool _isDeclaration)
|
void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expression* _value, bool _isDeclaration)
|
||||||
{
|
{
|
||||||
if (!_isDeclaration)
|
if (!_isDeclaration)
|
||||||
@ -242,17 +256,17 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
|
|||||||
auto const& referencedVariables = movableChecker.referencedVariables();
|
auto const& referencedVariables = movableChecker.referencedVariables();
|
||||||
for (auto const& name: _variables)
|
for (auto const& name: _variables)
|
||||||
{
|
{
|
||||||
m_references[name] = referencedVariables;
|
m_state.references[name] = referencedVariables;
|
||||||
if (!_isDeclaration)
|
if (!_isDeclaration)
|
||||||
{
|
{
|
||||||
// assignment to slot denoted by "name"
|
// assignment to slot denoted by "name"
|
||||||
m_storage.erase(name);
|
m_state.storage.erase(name);
|
||||||
// assignment to slot contents denoted by "name"
|
// assignment to slot contents denoted by "name"
|
||||||
cxx20::erase_if(m_storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
|
cxx20::erase_if(m_state.storage, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
|
||||||
// assignment to slot denoted by "name"
|
// assignment to slot denoted by "name"
|
||||||
m_memory.erase(name);
|
m_state.memory.erase(name);
|
||||||
// assignment to slot contents denoted by "name"
|
// assignment to slot contents denoted by "name"
|
||||||
cxx20::erase_if(m_memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
|
cxx20::erase_if(m_state.memory, mapTuple([&name](auto&& /* key */, auto&& value) { return value == name; }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,9 +279,9 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
|
|||||||
// On the other hand, if we knew the value in the slot
|
// On the other hand, if we knew the value in the slot
|
||||||
// already, then the sload() / mload() would have been replaced by a variable anyway.
|
// already, then the sload() / mload() would have been replaced by a variable anyway.
|
||||||
if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value))
|
if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value))
|
||||||
m_memory[*key] = variable;
|
m_state.memory[*key] = variable;
|
||||||
else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value))
|
else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value))
|
||||||
m_storage[*key] = variable;
|
m_state.storage[*key] = variable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,8 +295,8 @@ void DataFlowAnalyzer::popScope()
|
|||||||
{
|
{
|
||||||
for (auto const& name: m_variableScopes.back().variables)
|
for (auto const& name: m_variableScopes.back().variables)
|
||||||
{
|
{
|
||||||
m_value.erase(name);
|
m_state.value.erase(name);
|
||||||
m_references.erase(name);
|
m_state.references.erase(name);
|
||||||
}
|
}
|
||||||
m_variableScopes.pop_back();
|
m_variableScopes.pop_back();
|
||||||
}
|
}
|
||||||
@ -308,44 +322,44 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
|
|||||||
auto eraseCondition = mapTuple([&_variables](auto&& key, auto&& value) {
|
auto eraseCondition = mapTuple([&_variables](auto&& key, auto&& value) {
|
||||||
return _variables.count(key) || _variables.count(value);
|
return _variables.count(key) || _variables.count(value);
|
||||||
});
|
});
|
||||||
cxx20::erase_if(m_storage, eraseCondition);
|
cxx20::erase_if(m_state.storage, eraseCondition);
|
||||||
cxx20::erase_if(m_memory, eraseCondition);
|
cxx20::erase_if(m_state.memory, eraseCondition);
|
||||||
|
|
||||||
// Also clear variables that reference variables to be cleared.
|
// Also clear variables that reference variables to be cleared.
|
||||||
for (auto const& variableToClear: _variables)
|
for (auto const& variableToClear: _variables)
|
||||||
for (auto const& [ref, names]: m_references)
|
for (auto const& [ref, names]: m_state.references)
|
||||||
if (names.count(variableToClear))
|
if (names.count(variableToClear))
|
||||||
_variables.emplace(ref);
|
_variables.emplace(ref);
|
||||||
|
|
||||||
// Clear the value and update the reference relation.
|
// Clear the value and update the reference relation.
|
||||||
for (auto const& name: _variables)
|
for (auto const& name: _variables)
|
||||||
{
|
{
|
||||||
m_value.erase(name);
|
m_state.value.erase(name);
|
||||||
m_references.erase(name);
|
m_state.references.erase(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowAnalyzer::assignValue(YulString _variable, Expression const* _value)
|
void DataFlowAnalyzer::assignValue(YulString _variable, Expression const* _value)
|
||||||
{
|
{
|
||||||
m_value[_variable] = {_value, m_loopDepth};
|
m_state.value[_variable] = {_value, m_loopDepth};
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Block const& _block)
|
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Block const& _block)
|
||||||
{
|
{
|
||||||
SideEffectsCollector sideEffects(m_dialect, _block, &m_functionSideEffects);
|
SideEffectsCollector sideEffects(m_dialect, _block, &m_functionSideEffects);
|
||||||
if (sideEffects.invalidatesStorage())
|
if (sideEffects.invalidatesStorage())
|
||||||
m_storage.clear();
|
m_state.storage.clear();
|
||||||
if (sideEffects.invalidatesMemory())
|
if (sideEffects.invalidatesMemory())
|
||||||
m_memory.clear();
|
m_state.memory.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
|
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
|
||||||
{
|
{
|
||||||
SideEffectsCollector sideEffects(m_dialect, _expr, &m_functionSideEffects);
|
SideEffectsCollector sideEffects(m_dialect, _expr, &m_functionSideEffects);
|
||||||
if (sideEffects.invalidatesStorage())
|
if (sideEffects.invalidatesStorage())
|
||||||
m_storage.clear();
|
m_state.storage.clear();
|
||||||
if (sideEffects.invalidatesMemory())
|
if (sideEffects.invalidatesMemory())
|
||||||
m_memory.clear();
|
m_state.memory.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowAnalyzer::joinKnowledge(
|
void DataFlowAnalyzer::joinKnowledge(
|
||||||
@ -353,8 +367,8 @@ void DataFlowAnalyzer::joinKnowledge(
|
|||||||
unordered_map<YulString, YulString> const& _olderMemory
|
unordered_map<YulString, YulString> const& _olderMemory
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
joinKnowledgeHelper(m_storage, _olderStorage);
|
joinKnowledgeHelper(m_state.storage, _olderStorage);
|
||||||
joinKnowledgeHelper(m_memory, _olderMemory);
|
joinKnowledgeHelper(m_state.memory, _olderMemory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowAnalyzer::joinKnowledgeHelper(
|
void DataFlowAnalyzer::joinKnowledgeHelper(
|
||||||
@ -364,8 +378,8 @@ void DataFlowAnalyzer::joinKnowledgeHelper(
|
|||||||
{
|
{
|
||||||
// We clear if the key does not exist in the older map or if the value is different.
|
// We clear if the key does not exist in the older map or if the value is different.
|
||||||
// This also works for memory because _older is an "older version"
|
// This also works for memory because _older is an "older version"
|
||||||
// of m_memory and thus any overlapping write would have cleared the keys
|
// of m_state.memory and thus any overlapping write would have cleared the keys
|
||||||
// that are not known to be different inside m_memory already.
|
// that are not known to be different inside m_state.memory already.
|
||||||
cxx20::erase_if(_this, mapTuple([&_older](auto&& key, auto&& currentValue){
|
cxx20::erase_if(_this, mapTuple([&_older](auto&& key, auto&& currentValue){
|
||||||
YulString const* oldValue = util::valueOrNullptr(_older, key);
|
YulString const* oldValue = util::valueOrNullptr(_older, key);
|
||||||
return !oldValue || *oldValue != currentValue;
|
return !oldValue || *oldValue != currentValue;
|
||||||
@ -386,8 +400,8 @@ bool DataFlowAnalyzer::inScope(YulString _variableName) const
|
|||||||
|
|
||||||
optional<u256> DataFlowAnalyzer::valueOfIdentifier(YulString const& _name)
|
optional<u256> DataFlowAnalyzer::valueOfIdentifier(YulString const& _name)
|
||||||
{
|
{
|
||||||
if (m_value.count(_name))
|
if (AssignedValue const* value = variableValue(_name))
|
||||||
if (Literal const* literal = get_if<Literal>(m_value.at(_name).value))
|
if (Literal const* literal = get_if<Literal>(value->value))
|
||||||
return valueOfLiteral(*literal);
|
return valueOfLiteral(*literal);
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <libyul/AST.h> // Needed for m_zero below.
|
#include <libyul/AST.h> // Needed for m_zero below.
|
||||||
#include <libyul/SideEffects.h>
|
#include <libyul/SideEffects.h>
|
||||||
|
|
||||||
|
#include <libsolutil/Numeric.h>
|
||||||
#include <libsolutil/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
@ -38,6 +39,7 @@ namespace solidity::yul
|
|||||||
{
|
{
|
||||||
struct Dialect;
|
struct Dialect;
|
||||||
struct SideEffects;
|
struct SideEffects;
|
||||||
|
class KnowledgeBase;
|
||||||
|
|
||||||
/// Value assigned to a variable.
|
/// Value assigned to a variable.
|
||||||
struct AssignedValue
|
struct AssignedValue
|
||||||
@ -98,6 +100,13 @@ public:
|
|||||||
void operator()(ForLoop&) override;
|
void operator()(ForLoop&) override;
|
||||||
void operator()(Block& _block) 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_state.value, _variable); }
|
||||||
|
std::set<YulString> const* references(YulString _variable) const { return util::valueOrNullptr(m_state.references, _variable); }
|
||||||
|
std::map<YulString, AssignedValue> const& allValues() const { return m_state.value; }
|
||||||
|
std::optional<YulString> storageValue(YulString _key) const;
|
||||||
|
std::optional<YulString> memoryValue(YulString _key) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Registers the assignment.
|
/// Registers the assignment.
|
||||||
void handleAssignment(std::set<YulString> const& _names, Expression* _value, bool _isDeclaration);
|
void handleAssignment(std::set<YulString> const& _names, Expression* _value, bool _isDeclaration);
|
||||||
@ -164,14 +173,20 @@ protected:
|
|||||||
/// if this is not provided or the function is not found.
|
/// if this is not provided or the function is not found.
|
||||||
std::map<YulString, SideEffects> m_functionSideEffects;
|
std::map<YulString, SideEffects> m_functionSideEffects;
|
||||||
|
|
||||||
/// Current values of variables, always movable.
|
private:
|
||||||
std::map<YulString, AssignedValue> m_value;
|
struct State
|
||||||
/// m_references[a].contains(b) <=> the current expression assigned to a references b
|
{
|
||||||
std::unordered_map<YulString, std::set<YulString>> m_references;
|
/// Current values of variables, always movable.
|
||||||
|
std::map<YulString, AssignedValue> value;
|
||||||
|
/// m_references[a].contains(b) <=> the current expression assigned to a references b
|
||||||
|
std::unordered_map<YulString, std::set<YulString>> references;
|
||||||
|
|
||||||
std::unordered_map<YulString, YulString> m_storage;
|
std::unordered_map<YulString, YulString> storage;
|
||||||
std::unordered_map<YulString, YulString> m_memory;
|
std::unordered_map<YulString, YulString> memory;
|
||||||
|
};
|
||||||
|
State m_state;
|
||||||
|
|
||||||
|
protected:
|
||||||
KnowledgeBase m_knowledgeBase;
|
KnowledgeBase m_knowledgeBase;
|
||||||
|
|
||||||
YulString m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Last) + 1];
|
YulString m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Last) + 1];
|
||||||
|
@ -54,13 +54,13 @@ void EqualStoreEliminator::visit(Statement& _statement)
|
|||||||
{
|
{
|
||||||
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, *expression))
|
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)
|
if (*currentValue == vars->second)
|
||||||
m_pendingRemovals.insert(&_statement);
|
m_pendingRemovals.insert(&_statement);
|
||||||
}
|
}
|
||||||
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, *expression))
|
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)
|
if (*currentValue == vars->second)
|
||||||
m_pendingRemovals.insert(&_statement);
|
m_pendingRemovals.insert(&_statement);
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,10 @@ void ExpressionSimplifier::visit(Expression& _expression)
|
|||||||
{
|
{
|
||||||
ASTModifier::visit(_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));
|
_expression = match->action().toExpression(debugDataOf(_expression));
|
||||||
}
|
}
|
||||||
|
@ -80,8 +80,8 @@ bool KnowledgeBase::knownToBeZero(YulString _a)
|
|||||||
|
|
||||||
optional<u256> KnowledgeBase::valueIfKnownConstant(YulString _a)
|
optional<u256> KnowledgeBase::valueIfKnownConstant(YulString _a)
|
||||||
{
|
{
|
||||||
if (m_variableValues.count(_a))
|
if (AssignedValue const* value = m_variableValues(_a))
|
||||||
if (Literal const* literal = get_if<Literal>(m_variableValues.at(_a).value))
|
if (Literal const* literal = get_if<Literal>(value->value))
|
||||||
return valueOfLiteral(*literal);
|
return valueOfLiteral(*literal);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <libsolutil/Numeric.h>
|
#include <libsolutil/Numeric.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace solidity::yul
|
namespace solidity::yul
|
||||||
{
|
{
|
||||||
@ -37,15 +38,16 @@ struct AssignedValue;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that can answer questions about values of variables and their relations.
|
* 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
|
class KnowledgeBase
|
||||||
{
|
{
|
||||||
public:
|
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_dialect(_dialect),
|
||||||
m_variableValues(_variableValues)
|
m_variableValues(std::move(_variableValues))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool knownToBeDifferent(YulString _a, YulString _b);
|
bool knownToBeDifferent(YulString _a, YulString _b);
|
||||||
@ -60,7 +62,7 @@ private:
|
|||||||
Expression simplifyRecursively(Expression _expression);
|
Expression simplifyRecursively(Expression _expression);
|
||||||
|
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
std::map<YulString, AssignedValue> const& m_variableValues;
|
std::function<AssignedValue const*(YulString)> m_variableValues;
|
||||||
size_t m_counter = 0;
|
size_t m_counter = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,12 +82,12 @@ void LoadResolver::tryResolve(
|
|||||||
YulString key = std::get<Identifier>(_arguments.at(0)).name;
|
YulString key = std::get<Identifier>(_arguments.at(0)).name;
|
||||||
if (_location == StoreLoadLocation::Storage)
|
if (_location == StoreLoadLocation::Storage)
|
||||||
{
|
{
|
||||||
if (auto value = util::valueOrNullptr(m_storage, key))
|
if (auto value = storageValue(key))
|
||||||
if (inScope(*value))
|
if (inScope(*value))
|
||||||
_e = Identifier{debugDataOf(_e), *value};
|
_e = Identifier{debugDataOf(_e), *value};
|
||||||
}
|
}
|
||||||
else if (!m_containsMSize && _location == StoreLoadLocation::Memory)
|
else if (!m_containsMSize && _location == StoreLoadLocation::Memory)
|
||||||
if (auto value = util::valueOrNullptr(m_memory, key))
|
if (auto value = memoryValue(key))
|
||||||
if (inScope(*value))
|
if (inScope(*value))
|
||||||
_e = Identifier{debugDataOf(_e), *value};
|
_e = Identifier{debugDataOf(_e), *value};
|
||||||
}
|
}
|
||||||
@ -129,10 +129,10 @@ void LoadResolver::tryEvaluateKeccak(
|
|||||||
if (costOfLiteral > costOfKeccak)
|
if (costOfLiteral > costOfKeccak)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto memoryValue = util::valueOrNullptr(m_memory, memoryKey->name);
|
optional<YulString> value = memoryValue(memoryKey->name);
|
||||||
if (memoryValue && inScope(*memoryValue))
|
if (value && inScope(*value))
|
||||||
{
|
{
|
||||||
optional<u256> memoryContent = valueOfIdentifier(*memoryValue);
|
optional<u256> memoryContent = valueOfIdentifier(*value);
|
||||||
optional<u256> byteLength = valueOfIdentifier(length->name);
|
optional<u256> byteLength = valueOfIdentifier(length->name);
|
||||||
if (memoryContent && byteLength && *byteLength <= 32)
|
if (memoryContent && byteLength && *byteLength <= 32)
|
||||||
{
|
{
|
||||||
|
@ -79,16 +79,15 @@ void Rematerialiser::visit(Expression& _e)
|
|||||||
{
|
{
|
||||||
Identifier& identifier = std::get<Identifier>(_e);
|
Identifier& identifier = std::get<Identifier>(_e);
|
||||||
YulString name = identifier.name;
|
YulString name = identifier.name;
|
||||||
if (m_value.count(name))
|
if (AssignedValue const* value = variableValue(name))
|
||||||
{
|
{
|
||||||
assertThrow(m_value.at(name).value, OptimizerException, "");
|
assertThrow(value->value, OptimizerException, "");
|
||||||
AssignedValue const& value = m_value.at(name);
|
|
||||||
size_t refs = m_referenceCounts[name];
|
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 (
|
if (
|
||||||
(
|
(
|
||||||
!m_onlySelectedVariables && (
|
!m_onlySelectedVariables && (
|
||||||
(refs <= 1 && value.loopDepth == m_loopDepth) ||
|
(refs <= 1 && value->loopDepth == m_loopDepth) ||
|
||||||
cost == 0 ||
|
cost == 0 ||
|
||||||
(refs <= 5 && cost <= 1 && m_loopDepth == 0)
|
(refs <= 5 && cost <= 1 && m_loopDepth == 0)
|
||||||
)
|
)
|
||||||
@ -96,13 +95,14 @@ void Rematerialiser::visit(Expression& _e)
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
assertThrow(m_referenceCounts[name] > 0, OptimizerException, "");
|
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
|
// update reference counts
|
||||||
m_referenceCounts[name]--;
|
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;
|
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);
|
Identifier& identifier = std::get<Identifier>(_e);
|
||||||
YulString name = identifier.name;
|
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->value, OptimizerException, "");
|
||||||
assertThrow(value, OptimizerException, "");
|
if (holds_alternative<Literal>(*value->value))
|
||||||
if (holds_alternative<Literal>(*value))
|
_e = *value->value;
|
||||||
_e = *value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataFlowAnalyzer::visit(_e);
|
DataFlowAnalyzer::visit(_e);
|
||||||
|
@ -41,7 +41,7 @@ using namespace solidity::yul;
|
|||||||
SimplificationRules::Rule const* SimplificationRules::findFirstMatch(
|
SimplificationRules::Rule const* SimplificationRules::findFirstMatch(
|
||||||
Expression const& _expr,
|
Expression const& _expr,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
map<YulString, AssignedValue> const& _ssaValues
|
function<AssignedValue const*(YulString)> const& _ssaValues
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto instruction = instructionAndArguments(_dialect, _expr);
|
auto instruction = instructionAndArguments(_dialect, _expr);
|
||||||
@ -138,7 +138,7 @@ void Pattern::setMatchGroup(unsigned _group, map<unsigned, Expression const*>& _
|
|||||||
bool Pattern::matches(
|
bool Pattern::matches(
|
||||||
Expression const& _expr,
|
Expression const& _expr,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
map<YulString, AssignedValue> const& _ssaValues
|
function<AssignedValue const*(YulString)> const& _ssaValues
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
Expression const* expr = &_expr;
|
Expression const* expr = &_expr;
|
||||||
@ -148,8 +148,8 @@ bool Pattern::matches(
|
|||||||
if (m_kind != PatternKind::Any && holds_alternative<Identifier>(_expr))
|
if (m_kind != PatternKind::Any && holds_alternative<Identifier>(_expr))
|
||||||
{
|
{
|
||||||
YulString varName = std::get<Identifier>(_expr).name;
|
YulString varName = std::get<Identifier>(_expr).name;
|
||||||
if (_ssaValues.count(varName))
|
if (AssignedValue const* value = _ssaValues(varName))
|
||||||
if (Expression const* new_expr = _ssaValues.at(varName).value)
|
if (Expression const* new_expr = value->value)
|
||||||
expr = new_expr;
|
expr = new_expr;
|
||||||
}
|
}
|
||||||
assertThrow(expr, OptimizerException, "");
|
assertThrow(expr, OptimizerException, "");
|
||||||
|
@ -62,7 +62,7 @@ public:
|
|||||||
static Rule const* findFirstMatch(
|
static Rule const* findFirstMatch(
|
||||||
Expression const& _expr,
|
Expression const& _expr,
|
||||||
Dialect const& _dialect,
|
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
|
/// Checks whether the rulelist is non-empty. This is usually enforced
|
||||||
@ -119,7 +119,7 @@ public:
|
|||||||
bool matches(
|
bool matches(
|
||||||
Expression const& _expr,
|
Expression const& _expr,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
std::map<YulString, AssignedValue> const& _ssaValues
|
std::function<AssignedValue const*(YulString)> const& _ssaValues
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
std::vector<Pattern> arguments() const { return m_arguments; }
|
std::vector<Pattern> arguments() const { return m_arguments; }
|
||||||
|
@ -67,7 +67,8 @@ public:
|
|||||||
if (size_t const* cost = util::valueOrNullptr(m_expressionCodeCost, candidate))
|
if (size_t const* cost = util::valueOrNullptr(m_expressionCodeCost, candidate))
|
||||||
{
|
{
|
||||||
size_t numRef = m_numReferences[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;
|
return cand;
|
||||||
@ -80,11 +81,11 @@ public:
|
|||||||
if (_varDecl.variables.size() == 1)
|
if (_varDecl.variables.size() == 1)
|
||||||
{
|
{
|
||||||
YulString varName = _varDecl.variables.front().name;
|
YulString varName = _varDecl.variables.front().name;
|
||||||
if (m_value.count(varName))
|
if (AssignedValue const* value = variableValue(varName))
|
||||||
{
|
{
|
||||||
yulAssert(!m_expressionCodeCost.count(varName), "");
|
yulAssert(!m_expressionCodeCost.count(varName), "");
|
||||||
m_candidates.emplace_back(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;
|
YulString name = std::get<Identifier>(_e).name;
|
||||||
if (m_expressionCodeCost.count(name))
|
if (m_expressionCodeCost.count(name))
|
||||||
{
|
{
|
||||||
if (!m_value.count(name))
|
if (!variableValue(name))
|
||||||
rematImpossible(name);
|
rematImpossible(name);
|
||||||
else
|
else
|
||||||
++m_numReferences[name];
|
++m_numReferences[name];
|
||||||
|
@ -238,7 +238,7 @@ bool UnusedStoreEliminator::knownUnrelated(
|
|||||||
UnusedStoreEliminator::Operation const& _op2
|
UnusedStoreEliminator::Operation const& _op2
|
||||||
) const
|
) 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)
|
if (_op1.location != _op2.location)
|
||||||
return true;
|
return true;
|
||||||
@ -319,7 +319,7 @@ bool UnusedStoreEliminator::knownCovered(
|
|||||||
return true;
|
return true;
|
||||||
if (_covered.location == Location::Memory)
|
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))
|
if (_covered.length && knowledge.knownToBeZero(*_covered.length))
|
||||||
return true;
|
return true;
|
||||||
|
@ -58,7 +58,7 @@ protected:
|
|||||||
for (auto const& [name, expression]: m_ssaValues.values())
|
for (auto const& [name, expression]: m_ssaValues.values())
|
||||||
m_values[name].value = expression;
|
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};
|
EVMDialect m_dialect{EVMVersion{}, true};
|
||||||
|
Loading…
Reference in New Issue
Block a user