diff --git a/libjulia/optimiser/ASTWalker.cpp b/libjulia/optimiser/ASTWalker.cpp index 6386b29dc..034449849 100644 --- a/libjulia/optimiser/ASTWalker.cpp +++ b/libjulia/optimiser/ASTWalker.cpp @@ -86,8 +86,8 @@ void ASTWalker::operator()(ForLoop const& _for) { (*this)(_for.pre); visit(*_for.condition); - (*this)(_for.post); (*this)(_for.body); + (*this)(_for.post); } void ASTWalker::operator()(Block const& _block) diff --git a/libjulia/optimiser/NameCollector.cpp b/libjulia/optimiser/NameCollector.cpp index f94104b75..510ee2897 100644 --- a/libjulia/optimiser/NameCollector.cpp +++ b/libjulia/optimiser/NameCollector.cpp @@ -67,3 +67,9 @@ map ReferencesCounter::countReferences(Expression const& _expres counter.visit(_expression); return counter.references(); } + +void Assignments::operator()(Assignment const& _assignment) +{ + for (auto const& var: _assignment.variableNames) + m_names.insert(var.name); +} diff --git a/libjulia/optimiser/NameCollector.h b/libjulia/optimiser/NameCollector.h index 7fe386f72..2d4a1d4bf 100644 --- a/libjulia/optimiser/NameCollector.h +++ b/libjulia/optimiser/NameCollector.h @@ -66,5 +66,19 @@ private: std::map m_references; }; +/** + * Specific AST walker that finds all variables that are assigned to. + */ +class Assignments: public ASTWalker +{ +public: + using ASTWalker::operator (); + virtual void operator()(Assignment const& _assignment) override; + + std::set const& names() const { return m_names; } +private: + std::set m_names; +}; + } } diff --git a/libjulia/optimiser/Rematerialiser.cpp b/libjulia/optimiser/Rematerialiser.cpp new file mode 100644 index 000000000..ca5f94b06 --- /dev/null +++ b/libjulia/optimiser/Rematerialiser.cpp @@ -0,0 +1,143 @@ +/*( + 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 replaces variables by their most recently assigned expressions. + */ + +#include + +#include +#include + +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::julia; + +void Rematerialiser::operator()(Assignment& _assignment) +{ + set names; + for (auto const& var: _assignment.variableNames) + names.insert(var.name); + handleAssignment(names, _assignment.value.get()); +} + +void Rematerialiser::operator()(VariableDeclaration& _varDecl) +{ + set names; + for (auto const& var: _varDecl.variables) + names.insert(var.name); + handleAssignment(names, _varDecl.value.get()); +} + +void Rematerialiser::operator()(If& _if) +{ + ASTModifier::operator()(_if); + + Assignments ass; + ass(_if.body); + handleAssignment(ass.names(), nullptr); +} + +void Rematerialiser::operator()(Switch& _switch) +{ + boost::apply_visitor(*this, *_switch.expression); + set assignedVariables; + for (auto& _case: _switch.cases) + { + (*this)(_case.body); + Assignments ass; + ass(_case.body); + assignedVariables += ass.names(); + // This is a little too destructive, we could retain the old replacements. + handleAssignment(ass.names(), nullptr); + } + handleAssignment(assignedVariables, nullptr); +} + +void Rematerialiser::operator()(ForLoop& _for) +{ + (*this)(_for.pre); + + Assignments ass; + ass(_for.body); + ass(_for.post); + handleAssignment(ass.names(), nullptr); + + visit(*_for.condition); + (*this)(_for.body); + (*this)(_for.post); + + handleAssignment(ass.names(), nullptr); +} + +void Rematerialiser::handleAssignment(set const& _variables, Expression* _value) +{ + MovableChecker movableChecker; + if (_value) + { + visit(*_value); + movableChecker.visit(*_value); + } + if (_variables.size() == 1) + { + string const& name = *_variables.begin(); + if (movableChecker.movable() && _value) + // TODO Plus heuristic about size of value + // TODO If _value is null, we could use zero. + m_substitutions[name] = _value; + else + m_substitutions.erase(name); + } + // Disallow substitutions that use a variable that will be reassigned by this assignment. + for (auto const& name: _variables) + for (auto const& ref: m_referencedBy[name]) + m_substitutions.erase(ref); + // Update the fact which variables are referenced by the newly assigned variables + for (auto const& name: _variables) + { + for (auto const& ref: m_references[name]) + m_referencedBy[ref].erase(name); + m_references[name].clear(); + } + auto const& referencedVariables = movableChecker.referencedVariables(); + for (auto const& name: _variables) + { + m_references[name] = referencedVariables; + for (auto const& ref: referencedVariables) + m_referencedBy[ref].insert(name); + } +} + +void Rematerialiser::visit(Expression& _e) +{ + if (_e.type() == typeid(Identifier)) + { + Identifier& identifier = boost::get(_e); + if (m_substitutions.count(identifier.name)) + { + string name = identifier.name; + _e = (ASTCopier{}).translate(*m_substitutions.at(name)); + } + } + ASTModifier::visit(_e); +} diff --git a/libjulia/optimiser/Rematerialiser.h b/libjulia/optimiser/Rematerialiser.h new file mode 100644 index 000000000..f2a3102ff --- /dev/null +++ b/libjulia/optimiser/Rematerialiser.h @@ -0,0 +1,64 @@ +/* + 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 replaces variables by their most recently assigned expressions. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace dev +{ +namespace julia +{ + +/** + * Optimisation stage that replaces variables by their most recently assigned expressions. + * + * Prerequisite: Disambiguator + */ +class Rematerialiser: public ASTModifier +{ +public: + using ASTModifier::operator(); + virtual void operator()(Assignment& _assignment) override; + virtual void operator()(VariableDeclaration& _varDecl) override; + virtual void operator()(If& _if) override; + virtual void operator()(Switch& _switch) override; + virtual void operator()(ForLoop&) override; + +protected: + virtual void visit(Expression& _e) override; + +private: + void handleAssignment(std::set const& _names, Expression* _value); + + /// Substitutions to be performed, if possible. + std::map m_substitutions; + /// m_references[a].contains(b) <=> the current expression assigned to a references b + std::map> m_references; + /// m_referencedBy[b].contains(a) <=> the current expression assigned to a references b + std::map> m_referencedBy; +}; + +} +}