unused store

This commit is contained in:
chriseth 2022-11-28 13:28:21 +01:00
parent ddbcea047b
commit b09a8c62bb
2 changed files with 43 additions and 55 deletions

View File

@ -50,7 +50,7 @@ static string const one{"@ 1"};
static string const thirtyTwo{"@ 32"}; static string const thirtyTwo{"@ 32"};
void UnusedStoreEliminator::run(OptimiserStepContext& /*_context*/, Block& /*_ast*/) void UnusedStoreEliminator::run(OptimiserStepContext& _context, Block& _ast)
{ {
map<YulString, SideEffects> functionSideEffects = SideEffectsPropagator::sideEffects( map<YulString, SideEffects> functionSideEffects = SideEffectsPropagator::sideEffects(
_context.dialect, _context.dialect,
@ -87,9 +87,8 @@ void UnusedStoreEliminator::run(OptimiserStepContext& /*_context*/, Block& /*_as
else else
rse.markActiveAsUsed(Location::Memory); rse.markActiveAsUsed(Location::Memory);
rse.markActiveAsUsed(Location::Storage); rse.markActiveAsUsed(Location::Storage);
rse.scheduleUnusedForDeletion();
StatementRemover remover(rse.m_pendingRemovals); StatementRemover remover{rse.m_allStores - rse.m_usedStores};
remover(_ast); remover(_ast);
} }
@ -192,9 +191,9 @@ void UnusedStoreEliminator::visit(Statement const& _statement)
vector<Operation> operations = operationsFromFunctionCall(*funCall); vector<Operation> operations = operationsFromFunctionCall(*funCall);
yulAssert(operations.size() == 1, ""); yulAssert(operations.size() == 1, "");
if (operations.front().location == Location::Storage) if (operations.front().location == Location::Storage)
m_activeStores["s"_yulstring].insert(&_statement); activeStorageStores().insert(&_statement);
else else
m_activeStores["m"_yulstring].insert(&_statement); activeMemoryStores().insert(&_statement);
m_storeOperations[&_statement] = std::move(operations.front()); m_storeOperations[&_statement] = std::move(operations.front());
} }
} }
@ -202,7 +201,6 @@ void UnusedStoreEliminator::visit(Statement const& _statement)
void UnusedStoreEliminator::finalizeFunctionDefinition(FunctionDefinition const&) void UnusedStoreEliminator::finalizeFunctionDefinition(FunctionDefinition const&)
{ {
markActiveAsUsed(); markActiveAsUsed();
scheduleUnusedForDeletion();
} }
vector<UnusedStoreEliminator::Operation> UnusedStoreEliminator::operationsFromFunctionCall( vector<UnusedStoreEliminator::Operation> UnusedStoreEliminator::operationsFromFunctionCall(
@ -257,31 +255,27 @@ vector<UnusedStoreEliminator::Operation> UnusedStoreEliminator::operationsFromFu
void UnusedStoreEliminator::applyOperation(UnusedStoreEliminator::Operation const& _operation) void UnusedStoreEliminator::applyOperation(UnusedStoreEliminator::Operation const& _operation)
{ {
// TODO only one will be relevant, depending on _operation.location set<Statement const*> toRemove;
for (Statement const* statement: m_activeStores["s"_yulstring]) set<Statement const*>& active =
_operation.location == Location::Storage ?
activeStorageStores() :
activeMemoryStores();
// TODO this loop could be done more efficiently - removing while iterating.
for (Statement const* statement: active)
{ {
Operation const& storeOperation = m_storeOperations.at(statement); Operation const& storeOperation = m_storeOperations.at(statement);
if (_operation.effect == Effect::Read && !knownUnrelated(storeOperation, _operation)) if (_operation.effect == Effect::Read && !knownUnrelated(storeOperation, _operation))
// TODO remove from active! {
// This store is read from, mark it as used and remove it from the active set.
m_usedStores.insert(statement); m_usedStores.insert(statement);
toRemove.insert(statement);
}
else if (_operation.effect == Effect::Write && knownCovered(storeOperation, _operation)) else if (_operation.effect == Effect::Write && knownCovered(storeOperation, _operation))
{ // This store is overwritten before being read, remove it from the active set.
// TODO remove from active toRemove.insert(statement);
// state = State::Unused;
}
}
for (Statement const* statement: m_activeStores["m"_yulstring])
{
Operation const& storeOperation = m_storeOperations.at(statement);
if (_operation.effect == Effect::Read && !knownUnrelated(storeOperation, _operation))
// TODO remove from active!
m_usedStores.insert(statement);
else if (_operation.effect == Effect::Write && knownCovered(storeOperation, _operation))
{
// TODO remove from active
// state = State::Unused;
}
} }
active -= toRemove;
} }
bool UnusedStoreEliminator::knownUnrelated( bool UnusedStoreEliminator::knownUnrelated(
@ -403,26 +397,26 @@ bool UnusedStoreEliminator::knownCovered(
} }
void UnusedStoreEliminator::markActiveAsUsed( void UnusedStoreEliminator::markActiveAsUsed(
optional<UnusedStoreEliminator::Location> _onlyLocation) optional<UnusedStoreEliminator::Location> _onlyLocation
)
{ {
// TODO it might make sense to use YulString{"m"} and YulString{"s"} for memory and storage. if (_onlyLocation == nullopt || _onlyLocation == Location::Memory)
// BUT: Could be both! for (Statement const* statement: activeMemoryStores())
for (Statement const* statement: m_activeStores[YulString{}])
if (_onlyLocation == nullopt || *_onlyLocation == m_storeOperations.at(statement).location)
m_usedStores.insert(statement); m_usedStores.insert(statement);
// TODO and remove from active if (_onlyLocation == nullopt || _onlyLocation == Location::Storage)
for (Statement const* statement: activeStorageStores())
m_usedStores.insert(statement);
clearActive(_onlyLocation);
} }
void UnusedStoreEliminator::changeUndecidedTo( void UnusedStoreEliminator::clearActive(
State _newState, optional<UnusedStoreEliminator::Location> _onlyLocation
optional<UnusedStoreEliminator::Location> _onlyLocation)
{
for (auto& [statement, state]: m_activeStores[YulString{}])
if (
state == State::Undecided &&
(_onlyLocation == nullopt || *_onlyLocation == m_storeOperations.at(statement).location)
) )
state = _newState; {
if (_onlyLocation == nullopt || _onlyLocation == Location::Memory)
activeMemoryStores() = {};
if (_onlyLocation == nullopt || _onlyLocation == Location::Storage)
activeStorageStores() = {};
} }
optional<YulString> UnusedStoreEliminator::identifierNameIfSSA(Expression const& _expression) const optional<YulString> UnusedStoreEliminator::identifierNameIfSSA(Expression const& _expression) const
@ -432,10 +426,3 @@ optional<YulString> UnusedStoreEliminator::identifierNameIfSSA(Expression const&
return {identifier->name}; return {identifier->name};
return nullopt; return nullopt;
} }
void UnusedStoreEliminator::scheduleUnusedForDeletion()
{
for (auto const& [statement, state]: m_activeStores[YulString{}])
if (state == State::Unused)
m_pendingRemovals.insert(statement);
}

View File

@ -49,8 +49,7 @@ struct AssignedValue;
* to sstore, as we don't know whether the memory location will be read once we leave the function's scope, * to sstore, as we don't know whether the memory location will be read once we leave the function's scope,
* so the statement will be removed only if all code code paths lead to a memory overwrite. * so the statement will be removed only if all code code paths lead to a memory overwrite.
* *
* The m_store member of UnusedStoreBase is only used with the empty yul string * The m_store member of UnusedStoreBase uses the key "m" for memory ond "s" for storage stores.
* as key in the first dimension.
* *
* Best run in SSA form. * Best run in SSA form.
* *
@ -64,12 +63,12 @@ public:
explicit UnusedStoreEliminator( explicit UnusedStoreEliminator(
Dialect const& _dialect, Dialect const& _dialect,
std::map<YulString, SideEffects> const& ,//_functionSideEffects, std::map<YulString, SideEffects> const& _functionSideEffects,
std::map<YulString, ControlFlowSideEffects>,// _controlFlowSideEffects, std::map<YulString, ControlFlowSideEffects> _controlFlowSideEffects,
std::map<YulString, AssignedValue> const&,// _ssaValues, std::map<YulString, AssignedValue> const& _ssaValues,
bool// _ignoreMemory bool _ignoreMemory
): ):
UnusedStoreBase(_dialect)//, UnusedStoreBase(_dialect),
m_ignoreMemory(_ignoreMemory), m_ignoreMemory(_ignoreMemory),
m_functionSideEffects(_functionSideEffects), m_functionSideEffects(_functionSideEffects),
m_controlFlowSideEffects(_controlFlowSideEffects), m_controlFlowSideEffects(_controlFlowSideEffects),
@ -98,10 +97,13 @@ public:
}; };
private: private:
std::set<Statement const*>& activeMemoryStores() { return m_activeStores["m"_yulstring]; }
std::set<Statement const*>& activeStorageStores() { return m_activeStores["m"_yulstring]; }
void shortcutNestedLoop(ActiveStores const&) override void shortcutNestedLoop(ActiveStores const&) override
{ {
// We might only need to do this for newly introduced stores in the loop. // We might only need to do this for newly introduced stores in the loop.
changeUndecidedTo(State::Used); markActiveAsUsed();
} }
void finalizeFunctionDefinition(FunctionDefinition const&) override; void finalizeFunctionDefinition(FunctionDefinition const&) override;
@ -112,7 +114,6 @@ private:
void markActiveAsUsed(std::optional<Location> _onlyLocation = std::nullopt); void markActiveAsUsed(std::optional<Location> _onlyLocation = std::nullopt);
void clearActive(std::optional<Location> _onlyLocation = std::nullopt); void clearActive(std::optional<Location> _onlyLocation = std::nullopt);
void scheduleUnusedForDeletion();
std::optional<YulString> identifierNameIfSSA(Expression const& _expression) const; std::optional<YulString> identifierNameIfSSA(Expression const& _expression) const;