mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6777 from sifmelcara/loop-cond-rewriter
[YulOpt] Implement ForLoopConditionIntoBody
This commit is contained in:
commit
e5902c58a4
@ -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
|
||||
|
56
libyul/optimiser/ForLoopConditionIntoBody.cpp
Normal file
56
libyul/optimiser/ForLoopConditionIntoBody.cpp
Normal 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);
|
||||
}
|
||||
|
45
libyul/optimiser/ForLoopConditionIntoBody.h
Normal file
45
libyul/optimiser/ForLoopConditionIntoBody.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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 }
|
||||
// }
|
||||
// }
|
@ -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 }
|
||||
// }
|
||||
// }
|
@ -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)
|
||||
// }
|
||||
// }
|
@ -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)
|
||||
// }
|
||||
// }
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user