From a86b00e8d0ddf784501e6f079928e72f75e57052 Mon Sep 17 00:00:00 2001 From: mingchuan Date: Sat, 18 May 2019 13:35:36 +0800 Subject: [PATCH] [YulOpt] Implement ForLoopConditionIntoBody --- libyul/CMakeLists.txt | 2 + libyul/optimiser/ForLoopConditionIntoBody.cpp | 56 +++++++++++++++++++ libyul/optimiser/ForLoopConditionIntoBody.h | 45 +++++++++++++++ libyul/optimiser/README.md | 19 ++++++- 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 libyul/optimiser/ForLoopConditionIntoBody.cpp create mode 100644 libyul/optimiser/ForLoopConditionIntoBody.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index ff4d2cdbb..fe8ab141e 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -76,6 +76,8 @@ add_library(yul optimiser/ExpressionSimplifier.h optimiser/ExpressionSplitter.cpp optimiser/ExpressionSplitter.h + optimiser/ForLoopConditionIntoBody.cpp + optimiser/ForLoopConditionIntoBody.h optimiser/ForLoopInitRewriter.cpp optimiser/ForLoopInitRewriter.h optimiser/FullInliner.cpp diff --git a/libyul/optimiser/ForLoopConditionIntoBody.cpp b/libyul/optimiser/ForLoopConditionIntoBody.cpp new file mode 100644 index 000000000..b169d0a51 --- /dev/null +++ b/libyul/optimiser/ForLoopConditionIntoBody.cpp @@ -0,0 +1,56 @@ +/* + 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 . +*/ + +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; + +void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop) +{ + if (_forLoop.condition->type() != typeid(Literal)) + { + langutil::SourceLocation loc = locationOf(*_forLoop.condition); + _forLoop.body.statements.insert( + _forLoop.body.statements.begin(), + If { + loc, + make_unique( + FunctionalInstruction { + loc, + eth::Instruction::ISZERO, + make_vector(std::move(*_forLoop.condition)) + } + ), + Block {loc, make_vector(Break{{}})} + } + ); + _forLoop.condition = make_unique( + Literal { + loc, + LiteralKind::Number, + "1"_yulstring, + {} + } + ); + } + ASTModifier::operator()(_forLoop); +} + diff --git a/libyul/optimiser/ForLoopConditionIntoBody.h b/libyul/optimiser/ForLoopConditionIntoBody.h new file mode 100644 index 000000000..bbb1b47c2 --- /dev/null +++ b/libyul/optimiser/ForLoopConditionIntoBody.h @@ -0,0 +1,45 @@ +/* + 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 . +*/ +#pragma once + +#include + +namespace yul +{ + +/** + * Rewrites ForLoop by moving iteration condition into the ForLoop body. + * For example, `for {} lt(a, b) {} { mstore(1, 2) }` will become + * `for {} 1 {} { if iszero(lt(a, b)) { break } mstore(1, 2) }` + * + * By moving the iteration check part into the ForLoop body, we can apply expression splitter + * to the condition expression. + * + * This rewritter will skip loops that already have literal constant as iteration condition. + * + * Requirements: + * - The Disambiguator must be run upfront. + * - To avoid unnecessary rewrite, it is recommended to run this rewriter after StructuralSimplifier. + */ +class ForLoopConditionIntoBody: public ASTModifier +{ +public: + using ASTModifier::operator(); + void operator()(ForLoop& _forLoop) override; +}; + +} diff --git a/libyul/optimiser/README.md b/libyul/optimiser/README.md index 0c631615e..dfd09184c 100644 --- a/libyul/optimiser/README.md +++ b/libyul/optimiser/README.md @@ -74,6 +74,22 @@ and F is a list of function definitions such that no function contains a functio The benefit of this stage is that we always know where the list of function begins. +### For Loop Condition Into Body + +This transformation moves the iteration condition of a for-loop into loop body. +We need this transformation because [expression splitter](#expression-splitter) won't +apply to iteration condition expressions (the `C` in the following example). + + for { Init... } C { Post... } { + Body... + } + +is transformed to + + for { Init... } 1 { Post... } { + if iszero(C) { break } + Body... + } ### For Loop Init Rewriter @@ -172,7 +188,8 @@ The above would be transformed into Note that this transformation does not change the order of opcodes or function calls. It is not applied to loop conditions, because the loop control flow does not allow -this "outlining" of the inner expressions in all cases. +this "outlining" of the inner expressions in all cases. We can sidestep this limitation by applying +[for loop condition into body](#for-loop-condition-into-body) to move the iteration condition into loop body. The final program should be in a form such that (with the exception of loop conditions) function calls cannot appear nested inside expressions