From d7b366ff46e88df9c615b9c00e2f5ae1a74a6c32 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 19 Jun 2019 11:37:22 +0200 Subject: [PATCH] Name displacer. --- libdevcore/CommonData.h | 16 ++++ libyul/CMakeLists.txt | 2 + libyul/optimiser/ASTWalker.cpp | 2 + libyul/optimiser/NameDispenser.cpp | 4 +- libyul/optimiser/NameDispenser.h | 6 +- libyul/optimiser/NameDisplacer.cpp | 86 +++++++++++++++++++ libyul/optimiser/NameDisplacer.h | 70 +++++++++++++++ libyul/optimiser/Suite.cpp | 2 +- test/libyul/YulOptimizerTest.cpp | 10 +++ .../nameDisplacer/funtion_call.yul | 25 ++++++ .../nameDisplacer/variables.yul | 11 +++ .../variables_inside_functions.yul | 16 ++++ 12 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 libyul/optimiser/NameDisplacer.cpp create mode 100644 libyul/optimiser/NameDisplacer.h create mode 100644 test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul create mode 100644 test/libyul/yulOptimizerTests/nameDisplacer/variables.yul create mode 100644 test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 0a13e7940..2fca07171 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -81,6 +81,22 @@ inline std::vector operator+(std::vector&& _a, std::vector&& _b) ret += std::move(_b); return ret; } +/// Concatenate something to a sets of elements. +template +inline std::set operator+(std::set const& _a, U&& _b) +{ + std::set ret(_a); + ret += std::forward(_b); + return ret; +} +/// Concatenate something to a sets of elements, move variant. +template +inline std::set operator+(std::set&& _a, U&& _b) +{ + std::set ret(std::move(_a)); + ret += std::forward(_b); + return ret; +} namespace dev { diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index bf09207b6..f85f4e7c4 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -102,6 +102,8 @@ add_library(yul optimiser/NameCollector.h optimiser/NameDispenser.cpp optimiser/NameDispenser.h + optimiser/NameDisplacer.cpp + optimiser/NameDisplacer.h optimiser/OptimizerUtilities.cpp optimiser/OptimizerUtilities.h optimiser/RedundantAssignEliminator.cpp diff --git a/libyul/optimiser/ASTWalker.cpp b/libyul/optimiser/ASTWalker.cpp index 85234c4c7..523cc8a92 100644 --- a/libyul/optimiser/ASTWalker.cpp +++ b/libyul/optimiser/ASTWalker.cpp @@ -35,6 +35,7 @@ void ASTWalker::operator()(FunctionalInstruction const& _instr) void ASTWalker::operator()(FunctionCall const& _funCall) { + // Does not visit _funCall.functionName on purpose walkVector(_funCall.arguments | boost::adaptors::reversed); } @@ -108,6 +109,7 @@ void ASTModifier::operator()(FunctionalInstruction& _instr) void ASTModifier::operator()(FunctionCall& _funCall) { + // Does not visit _funCall.functionName on purpose walkVector(_funCall.arguments | boost::adaptors::reversed); } diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index 2a0c1e44f..f5bc1314a 100644 --- a/libyul/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -32,8 +32,8 @@ using namespace std; using namespace dev; using namespace yul; -NameDispenser::NameDispenser(Dialect const& _dialect, Block const& _ast): - NameDispenser(_dialect, NameCollector(_ast).names()) +NameDispenser::NameDispenser(Dialect const& _dialect, Block const& _ast, set _reservedNames): + NameDispenser(_dialect, NameCollector(_ast).names() + std::move(_reservedNames)) { } diff --git a/libyul/optimiser/NameDispenser.h b/libyul/optimiser/NameDispenser.h index 9591dd135..db871f905 100644 --- a/libyul/optimiser/NameDispenser.h +++ b/libyul/optimiser/NameDispenser.h @@ -39,13 +39,17 @@ class NameDispenser { public: /// Initialize the name dispenser with all the names used in the given AST. - explicit NameDispenser(Dialect const& _dialect, Block const& _ast); + explicit NameDispenser(Dialect const& _dialect, Block const& _ast, std::set _reservedNames = {}); /// Initialize the name dispenser with the given used names. explicit NameDispenser(Dialect const& _dialect, std::set _usedNames); /// @returns a currently unused name that should be similar to _nameHint. YulString newName(YulString _nameHint); + /// Mark @a _name as used, i.e. the dispenser's newName function will not + /// return it. + void markUsed(YulString _name) { m_usedNames.insert(_name); } + private: bool illegalName(YulString _name); diff --git a/libyul/optimiser/NameDisplacer.cpp b/libyul/optimiser/NameDisplacer.cpp new file mode 100644 index 000000000..b143e3d98 --- /dev/null +++ b/libyul/optimiser/NameDisplacer.cpp @@ -0,0 +1,86 @@ +/* + 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 renames identifiers to free up certain names. + */ + +#include + +#include + + +using namespace std; +using namespace dev; +using namespace yul; + +void NameDisplacer::operator()(Identifier& _identifier) +{ + checkAndReplace(_identifier.name); +} + +void NameDisplacer::operator()(VariableDeclaration& _varDecl) +{ + for (TypedName& var: _varDecl.variables) + checkAndReplaceNew(var.name); + + ASTModifier::operator()(_varDecl); +} + +void NameDisplacer::operator()(FunctionDefinition& _function) +{ + // Should have been done in the block already. + yulAssert(!m_namesToFree.count(_function.name), ""); + + for (auto& param: _function.parameters) + checkAndReplaceNew(param.name); + for (auto& retVar: _function.returnVariables) + checkAndReplaceNew(retVar.name); + + ASTModifier::operator()(_function); +} + +void NameDisplacer::operator()(FunctionCall& _funCall) +{ + checkAndReplace(_funCall.functionName.name); + ASTModifier::operator()(_funCall); +} + +void NameDisplacer::operator()(Block& _block) +{ + // First replace all the names of function definitions + // because of scoping. + for (auto& st: _block.statements) + if (st.type() == typeid(FunctionDefinition)) + checkAndReplaceNew(boost::get(st).name); + + ASTModifier::operator()(_block); +} + +void NameDisplacer::checkAndReplaceNew(YulString& _name) +{ + yulAssert(!m_translations.count(_name), ""); + if (m_namesToFree.count(_name)) + _name = (m_translations[_name] = m_nameDispenser.newName(_name)); +} + +void NameDisplacer::checkAndReplace(YulString& _name) const +{ + if (m_translations.count(_name)) + _name = m_translations.at(_name); + yulAssert(!m_namesToFree.count(_name), ""); +} + diff --git a/libyul/optimiser/NameDisplacer.h b/libyul/optimiser/NameDisplacer.h new file mode 100644 index 000000000..ed025d9f6 --- /dev/null +++ b/libyul/optimiser/NameDisplacer.h @@ -0,0 +1,70 @@ +/* + 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 renames identifiers to free up certain names. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace yul +{ +struct Dialect; + +/** + * Optimiser component that renames identifiers to free up certain names. + * + * Prerequisites: Disambiguator + */ +class NameDisplacer: public ASTModifier +{ +public: + explicit NameDisplacer( + NameDispenser& _dispenser, + std::set const& _namesToFree + ): + m_nameDispenser(_dispenser), + m_namesToFree(_namesToFree) + { + for (YulString n: _namesToFree) + m_nameDispenser.markUsed(n); + } + + using ASTModifier::operator(); + void operator()(Identifier& _identifier) override; + void operator()(VariableDeclaration& _varDecl) override; + void operator()(FunctionDefinition& _function) override; + void operator()(FunctionCall& _funCall) override; + void operator()(Block& _block) override; + +protected: + /// Check if the newly introduced identifier @a _name has to be replaced. + void checkAndReplaceNew(YulString& _name); + /// Replace the identifier @a _name if it is in the translation map. + void checkAndReplace(YulString& _name) const; + + NameDispenser& m_nameDispenser; + std::set const& m_namesToFree; + std::map m_translations; +}; + +} diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 05b4b1b40..fd5c1499a 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -87,7 +87,7 @@ void OptimiserSuite::run( // None of the above can make stack problems worse. - NameDispenser dispenser{_dialect, ast}; + NameDispenser dispenser{_dialect, ast, reservedIdentifiers}; size_t codeSize = 0; for (size_t rounds = 0; rounds < 12; ++rounds) diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 496f0837c..66979cb66 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,15 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line soltestAssert(m_dialect, "Dialect not set."); if (m_optimizerStep == "disambiguator") disambiguate(); + else if (m_optimizerStep == "nameDisplacer") + { + disambiguate(); + NameDispenser nameDispenser{*m_dialect, *m_ast}; + NameDisplacer{ + nameDispenser, + {"illegal1"_yulstring, "illegal2"_yulstring, "illegal3"_yulstring, "illegal4"_yulstring, "illegal5"_yulstring} + }(*m_ast); + } else if (m_optimizerStep == "blockFlattener") { disambiguate(); diff --git a/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul b/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul new file mode 100644 index 000000000..9c547d17a --- /dev/null +++ b/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul @@ -0,0 +1,25 @@ +{ + let x := illegal4(1, 2) + function illegal4(illegal1, illegal2) -> illegal3 { illegal3 := add(illegal1, illegal2) } + { + let y := illegal5(3, 4) + function illegal5(illegal1, illegal2) -> illegal3 { illegal3 := add(illegal1, illegal2) } + } +} +// ==== +// step: nameDisplacer +// ---- +// { +// let x := illegal4_1(1, 2) +// function illegal4_1(illegal1_2, illegal2_3) -> illegal3_4 +// { +// illegal3_4 := add(illegal1_2, illegal2_3) +// } +// { +// let y := illegal5_5(3, 4) +// function illegal5_5(illegal1_1, illegal2_2) -> illegal3_3 +// { +// illegal3_3 := add(illegal1_1, illegal2_2) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul b/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul new file mode 100644 index 000000000..dae343217 --- /dev/null +++ b/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul @@ -0,0 +1,11 @@ +{ { let illegal1 := 1 } { let illegal2 := 2 let illegal3, illegal4 } } +// ==== +// step: nameDisplacer +// ---- +// { +// { let illegal1_1 := 1 } +// { +// let illegal2_2 := 2 +// let illegal3_3, illegal4_4 +// } +// } diff --git a/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul b/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul new file mode 100644 index 000000000..73228739a --- /dev/null +++ b/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul @@ -0,0 +1,16 @@ +{ + function f(illegal1, illegal2) -> illegal3 { + let illegal4 := illegal1 + illegal3 := add(illegal1, illegal2) + } +} +// ==== +// step: nameDisplacer +// ---- +// { +// function f(illegal1_1, illegal2_2) -> illegal3_3 +// { +// let illegal4_4 := illegal1_1 +// illegal3_3 := add(illegal1_1, illegal2_2) +// } +// }