/* 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 . */ /** * Optimiser component that turns subsequent assignments to variable declarations * and assignments. */ #include #include #include #include #include using namespace std; using namespace dev; using namespace dev::yul; using namespace dev::solidity; void SSATransform::operator()(Identifier& _identifier) { if (m_currentVariableValues.count(_identifier.name)) _identifier.name = m_currentVariableValues[_identifier.name]; } void SSATransform::operator()(ForLoop& _for) { // This will clear the current value in case of a reassignment inside the // init part, although the new variable would still be in scope inside the whole loop. // This small inefficiency is fine if we move the pre part of all for loops out // of the for loop. (*this)(_for.pre); Assignments assignments; assignments(_for.body); assignments(_for.post); for (auto const& var: assignments.names()) m_currentVariableValues.erase(var); visit(*_for.condition); (*this)(_for.body); (*this)(_for.post); } void SSATransform::operator()(Block& _block) { set variablesToClearAtEnd; // Creates a new variable (and returns its declaration) with value _value // and replaces _value by a reference to that new variable. auto replaceByNew = [&](SourceLocation _loc, YulString _varName, YulString _type, shared_ptr& _value) -> VariableDeclaration { YulString newName = m_nameDispenser.newName(_varName); m_currentVariableValues[_varName] = newName; variablesToClearAtEnd.emplace(_varName); shared_ptr v = make_shared(Identifier{_loc, newName}); _value.swap(v); return VariableDeclaration{_loc, {TypedName{_loc, std::move(newName), std::move(_type)}}, std::move(v)}; }; iterateReplacing( _block.statements, [&](Statement& _s) -> boost::optional> { if (_s.type() == typeid(VariableDeclaration)) { VariableDeclaration& varDecl = boost::get(_s); if (varDecl.value) visit(*varDecl.value); if (varDecl.variables.size() != 1 || !m_variablesToReplace.count(varDecl.variables.front().name)) return {}; // Replace "let a := v" by "let a_1 := v let a := v" VariableDeclaration newVarDecl = replaceByNew( varDecl.location, varDecl.variables.front().name, varDecl.variables.front().type, varDecl.value ); return vector{std::move(newVarDecl), std::move(varDecl)}; } else if (_s.type() == typeid(Assignment)) { Assignment& assignment = boost::get(_s); visit(*assignment.value); if (assignment.variableNames.size() != 1) return {}; assertThrow(m_variablesToReplace.count(assignment.variableNames.front().name), OptimizerException, ""); // Replace "a := v" by "let a_1 := v a := v" VariableDeclaration newVarDecl = replaceByNew( assignment.location, assignment.variableNames.front().name, {}, // TODO determine type assignment.value ); return vector{std::move(newVarDecl), std::move(assignment)}; } else visit(_s); return {}; } ); for (auto const& var: variablesToClearAtEnd) m_currentVariableValues.erase(var); } void SSATransform::run(Block& _ast, NameDispenser& _nameDispenser) { Assignments assignments; assignments(_ast); SSATransform{_nameDispenser, assignments.names()}(_ast); }