/* 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 /** * UnusedFunctionParameterPruner: Optimiser step that removes unused parameters from function * definition. */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace solidity::util; using namespace solidity::yul; using namespace solidity::yul::unusedFunctionsCommon; void UnusedFunctionParameterPruner::run(OptimiserStepContext& _context, Block& _ast) { map references = ReferencesCounter::countReferences(_ast); auto used = [&](auto v) -> bool { return references.count(v.name); }; // Function name and a pair of boolean masks, the first corresponds to parameters and the second // corresponds to returnVariables. // // For the first vector in the pair, a value `false` at index `i` indicates that the function // argument at index `i` in `FunctionDefinition::parameters` is unused inside the function body. // // Similarly for the second vector in the pair, a value `false` at index `i` indicates that the // return parameter at index `i` in `FunctionDefinition::returnVariables` is unused inside // function body. map, vector>> usedParametersAndReturnVariables; // Step 1 of UnusedFunctionParameterPruner: Find functions whose parameters (both arguments and // return-parameters) are not used in its body. for (auto const& statement: _ast.statements) if (holds_alternative(statement)) { FunctionDefinition const& f = std::get(statement); if (tooSimpleToBePruned(f) || boost::algorithm::all_of(f.parameters + f.returnVariables, used)) continue; usedParametersAndReturnVariables[f.name] = { applyMap(f.parameters, used), applyMap(f.returnVariables, used) }; } set functionNamesToFree = util::keys(usedParametersAndReturnVariables); // Step 2 of UnusedFunctionParameterPruner: Renames the function and replaces all references to // the function, say `f`, by its new name, say `f_1`. NameDisplacer replace{_context.dispenser, functionNamesToFree}; replace(_ast); // Inverse-Map of the above translations. In the above example, this will store an element with // key `f_1` and value `f`. std::map newToOriginalNames = invertMap(replace.translations()); // Step 3 of UnusedFunctionParameterPruner: introduce a new function in the block with body of // the old one. Replace the body of the old one with a function call to the new one with reduced // parameters. // // For example: introduce a new 'linking' function `f` with the same the body as `f_1`, but with // reduced parameters, i.e., `function f() -> y { y := 1 }`. Now replace the body of `f_1` with // a call to `f`, i.e., `f_1(x) -> y { y := f() }`. iterateReplacing(_ast.statements, [&](Statement& _s) -> optional> { if (holds_alternative(_s)) { // The original function except that it has a new name (e.g., `f_1`) FunctionDefinition& originalFunction = get(_s); if (newToOriginalNames.count(originalFunction.name)) { YulString linkingFunctionName = originalFunction.name; YulString originalFunctionName = newToOriginalNames.at(linkingFunctionName); pair, vector> used = usedParametersAndReturnVariables.at(originalFunctionName); FunctionDefinition linkingFunction = createLinkingFunction( originalFunction, used, originalFunctionName, linkingFunctionName, _context.dispenser ); originalFunction.name = originalFunctionName; originalFunction.parameters = filter(originalFunction.parameters, used.first); originalFunction.returnVariables = filter(originalFunction.returnVariables, used.second); return make_vector(move(originalFunction), move(linkingFunction)); } } return nullopt; }); }