/*( 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 . */ /** * Optimisation stage that aggressively rematerializes certain variables ina a function to free * space on the stack until it is compilable. */ #include #include #include #include #include #include #include #include #include using namespace std; using namespace dev; using namespace yul; namespace { template void eliminateVariables(shared_ptr const& _dialect, ASTNode& _node, size_t _numVariables) { SSAValueTracker ssaValues; ssaValues(_node); map references = ReferencesCounter::countReferences(_node); set> rematCosts; for (auto const& ssa: ssaValues.values()) { if (!MovableChecker{*_dialect, *ssa.second}.movable()) continue; size_t numRef = references[ssa.first]; size_t cost = 0; if (numRef > 1) cost = CodeCost::codeCost(*ssa.second) * (numRef - 1); rematCosts.insert(make_pair(cost, ssa.first)); } // Select at most _numVariables set varsToEliminate; for (auto const& costs: rematCosts) { if (varsToEliminate.size() >= _numVariables) break; varsToEliminate.insert(costs.second); } Rematerialiser::run(*_dialect, _node, std::move(varsToEliminate)); UnusedPruner::runUntilStabilised(*_dialect, _node); } } bool StackCompressor::run(shared_ptr const& _dialect, Block& _ast) { yulAssert( _ast.statements.size() > 0 && _ast.statements.at(0).type() == typeid(Block), "Need to run the function grouper before the stack compressor." ); for (size_t iterations = 0; iterations < 4; iterations++) { map stackSurplus = CompilabilityChecker::run(_dialect, _ast); if (stackSurplus.empty()) return true; if (stackSurplus.count(YulString{})) { yulAssert(stackSurplus.at({}) > 0, "Invalid surplus value."); eliminateVariables(_dialect, boost::get(_ast.statements.at(0)), stackSurplus.at({})); } for (size_t i = 1; i < _ast.statements.size(); ++i) { FunctionDefinition& fun = boost::get(_ast.statements[i]); if (!stackSurplus.count(fun.name)) continue; yulAssert(stackSurplus.at(fun.name) > 0, "Invalid surplus value."); eliminateVariables(_dialect, fun, stackSurplus.at(fun.name)); } } return false; }