Merge pull request #6777 from sifmelcara/loop-cond-rewriter

[YulOpt] Implement ForLoopConditionIntoBody
This commit is contained in:
chriseth 2019-05-23 12:51:51 +02:00 committed by GitHub
commit e5902c58a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 228 additions and 2 deletions

View File

@ -78,6 +78,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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/AsmData.h>
#include <libdevcore/CommonData.h>
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<Expression>(
FunctionalInstruction {
loc,
eth::Instruction::ISZERO,
make_vector<Expression>(std::move(*_forLoop.condition))
}
),
Block {loc, make_vector<Statement>(Break{{}})}
}
);
_forLoop.condition = make_unique<Expression>(
Literal {
loc,
LiteralKind::Number,
"1"_yulstring,
{}
}
);
}
ASTModifier::operator()(_forLoop);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <libyul/optimiser/ASTWalker.h>
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;
};
}

View File

@ -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

View File

@ -33,6 +33,7 @@
#include <libyul/optimiser/FunctionHoister.h>
#include <libyul/optimiser/ExpressionInliner.h>
#include <libyul/optimiser/FullInliner.h>
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/optimiser/ForLoopInitRewriter.h>
#include <libyul/optimiser/MainFunction.h>
#include <libyul/optimiser/Rematerialiser.h>
@ -121,6 +122,11 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
VarDeclInitializer{}(*m_ast);
else if (m_optimizerStep == "varNameCleaner")
VarNameCleaner{*m_ast, *m_dialect}(*m_ast);
else if (m_optimizerStep == "forLoopConditionIntoBody")
{
disambiguate();
ForLoopConditionIntoBody{}(*m_ast);
}
else if (m_optimizerStep == "forLoopInitRewriter")
{
disambiguate();

View File

@ -0,0 +1,26 @@
{
let a := 1
for { } 42 { } { }
for { } "hello" { } { }
for { } 0 { } { }
for { } a { } { }
for { } add(a, a) { } { }
}
// ====
// step: forLoopConditionIntoBody
// ----
// {
// let a := 1
// for { } 42 { }
// { }
// for { } "hello" { }
// { }
// for { } 0 { }
// { }
// for { } 1 { }
// { if iszero(a) { break } }
// for { } 1 { }
// {
// if iszero(add(a, a)) { break }
// }
// }

View File

@ -0,0 +1,12 @@
{
for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { }
}
// ====
// step: forLoopConditionIntoBody
// ----
// {
// for { let a := 1 } 1 { a := add(a, 1) }
// {
// if iszero(iszero(eq(a, 10))) { break }
// }
// }

View File

@ -0,0 +1,41 @@
{
let random := 42
for {
for { let a := 1} iszero(eq(a,10)) {} {
a := add(a, 1)
}
let b := 1
} iszero(eq(b, 10)) {
for { let c := 1 } iszero(eq(c,2)) { c := add(c, 1) } {
b := add(b, 1)
}
} {
mstore(b,b)
}
}
// ====
// step: forLoopConditionIntoBody
// ----
// {
// let random := 42
// for {
// for { let a := 1 } 1 { }
// {
// if iszero(iszero(eq(a, 10))) { break }
// a := add(a, 1)
// }
// let b := 1
// }
// 1
// {
// for { let c := 1 } 1 { c := add(c, 1) }
// {
// if iszero(iszero(eq(c, 2))) { break }
// b := add(b, 1)
// }
// }
// {
// if iszero(iszero(eq(b, 10))) { break }
// mstore(b, b)
// }
// }

View File

@ -0,0 +1,17 @@
{
let random := 42
for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } {
a := add(a, 1)
}
}
// ====
// step: forLoopConditionIntoBody
// ----
// {
// let random := 42
// for { let a := 1 } 1 { a := add(a, 1) }
// {
// if iszero(iszero(eq(a, 10))) { break }
// a := add(a, 1)
// }
// }

View File

@ -40,6 +40,7 @@
#include <libyul/optimiser/FunctionHoister.h>
#include <libyul/optimiser/ExpressionInliner.h>
#include <libyul/optimiser/FullInliner.h>
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/optimiser/ForLoopInitRewriter.h>
#include <libyul/optimiser/MainFunction.h>
#include <libyul/optimiser/Rematerialiser.h>
@ -129,7 +130,7 @@ public:
}
cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl;
cout << " (e)xpr inline/(i)nline/(s)implify/varname c(l)eaner/(u)nusedprune/ss(a) transform/" << endl;
cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter/" << endl;
cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-init-rewriter/f(O)r-loop-condition-into-body/" << endl;
cout << " s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser/? " << endl;
cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/? " << endl;
cout.flush();
@ -145,6 +146,9 @@ public:
case 'o':
ForLoopInitRewriter{}(*m_ast);
break;
case 'O':
ForLoopConditionIntoBody{}(*m_ast);
break;
case 'c':
(CommonSubexpressionEliminator{m_dialect})(*m_ast);
break;