/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 /** * Optimiser component that removes stores to memory and storage slots that are not used * or overwritten later on. */ #pragma once #include #include #include #include #include #include #include #include #include namespace solidity::yul { struct Dialect; struct AssignedValue; /** * Optimizer component that removes sstore and memory store statements if conditions are met for their removal. * In case of an sstore, if all outgoing code paths revert (due to an explicit revert(), invalid(), * or infinite recursion) or lead to another ``sstore`` for which the optimizer can tell that it will overwrite the first store, * the statement will be removed. * * For memory store operations, things are generally simpler, at least in the outermost yul block as all such statements * will be removed if they are never read from in any code path. At function analysis level however, the approach is similar * 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. * * The m_store member of UnusedStoreBase uses the key "m" for memory and "s" for storage stores. * * Best run in SSA form. * * Prerequisite: Disambiguator, ForLoopInitRewriter. */ class UnusedStoreEliminator: public UnusedStoreBase { public: static constexpr char const* name{"UnusedStoreEliminator"}; static void run(OptimiserStepContext& _context, Block& _ast); explicit UnusedStoreEliminator( Dialect const& _dialect, std::map const& _functionSideEffects, std::map _controlFlowSideEffects, std::map const& _ssaValues, bool _ignoreMemory ); using UnusedStoreBase::operator(); void operator()(FunctionCall const& _functionCall) override; void operator()(FunctionDefinition const&) override; void operator()(Leave const&) override; using UnusedStoreBase::visit; void visit(Statement const& _statement) override; using Location = evmasm::SemanticInformation::Location; using Effect = evmasm::SemanticInformation::Effect; struct Operation { Location location; Effect effect; /// Start of affected area. Unknown if not provided. std::optional start; /// Length of affected area, unknown if not provided. /// Unused for storage. std::optional length; }; private: std::set& activeMemoryStores() { return m_activeStores["m"_yulstring]; } std::set& activeStorageStores() { return m_activeStores["s"_yulstring]; } void shortcutNestedLoop(ActiveStores const&) override { // We might only need to do this for newly introduced stores in the loop. markActiveAsUsed(); } void finalizeFunctionDefinition(FunctionDefinition const&) override { markActiveAsUsed(); } std::vector operationsFromFunctionCall(FunctionCall const& _functionCall) const; void applyOperation(Operation const& _operation); bool knownUnrelated(Operation const& _op1, Operation const& _op2) const; bool knownCovered(Operation const& _covered, Operation const& _covering) const; void markActiveAsUsed(std::optional _onlyLocation = std::nullopt); void clearActive(std::optional _onlyLocation = std::nullopt); std::optional identifierNameIfSSA(Expression const& _expression) const; bool const m_ignoreMemory; std::map const& m_functionSideEffects; std::map m_controlFlowSideEffects; std::map const& m_ssaValues; std::map m_storeOperations; KnowledgeBase mutable m_knowledgeBase; }; }