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/ExpressionSimplifier.h
|
||||||
optimiser/ExpressionSplitter.cpp
|
optimiser/ExpressionSplitter.cpp
|
||||||
optimiser/ExpressionSplitter.h
|
optimiser/ExpressionSplitter.h
|
||||||
|
optimiser/ForLoopConditionIntoBody.cpp
|
||||||
|
optimiser/ForLoopConditionIntoBody.h
|
||||||
optimiser/ForLoopInitRewriter.cpp
|
optimiser/ForLoopInitRewriter.cpp
|
||||||
optimiser/ForLoopInitRewriter.h
|
optimiser/ForLoopInitRewriter.h
|
||||||
optimiser/FullInliner.cpp
|
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.
|
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
|
### 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.
|
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
|
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)
|
The final program should be in a form such that (with the exception of loop conditions)
|
||||||
function calls cannot appear nested inside expressions
|
function calls cannot appear nested inside expressions
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <libyul/optimiser/FunctionHoister.h>
|
#include <libyul/optimiser/FunctionHoister.h>
|
||||||
#include <libyul/optimiser/ExpressionInliner.h>
|
#include <libyul/optimiser/ExpressionInliner.h>
|
||||||
#include <libyul/optimiser/FullInliner.h>
|
#include <libyul/optimiser/FullInliner.h>
|
||||||
|
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
|
||||||
#include <libyul/optimiser/ForLoopInitRewriter.h>
|
#include <libyul/optimiser/ForLoopInitRewriter.h>
|
||||||
#include <libyul/optimiser/MainFunction.h>
|
#include <libyul/optimiser/MainFunction.h>
|
||||||
#include <libyul/optimiser/Rematerialiser.h>
|
#include <libyul/optimiser/Rematerialiser.h>
|
||||||
@ -121,6 +122,11 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
|||||||
VarDeclInitializer{}(*m_ast);
|
VarDeclInitializer{}(*m_ast);
|
||||||
else if (m_optimizerStep == "varNameCleaner")
|
else if (m_optimizerStep == "varNameCleaner")
|
||||||
VarNameCleaner{*m_ast, *m_dialect}(*m_ast);
|
VarNameCleaner{*m_ast, *m_dialect}(*m_ast);
|
||||||
|
else if (m_optimizerStep == "forLoopConditionIntoBody")
|
||||||
|
{
|
||||||
|
disambiguate();
|
||||||
|
ForLoopConditionIntoBody{}(*m_ast);
|
||||||
|
}
|
||||||
else if (m_optimizerStep == "forLoopInitRewriter")
|
else if (m_optimizerStep == "forLoopInitRewriter")
|
||||||
{
|
{
|
||||||
disambiguate();
|
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/FunctionHoister.h>
|
||||||
#include <libyul/optimiser/ExpressionInliner.h>
|
#include <libyul/optimiser/ExpressionInliner.h>
|
||||||
#include <libyul/optimiser/FullInliner.h>
|
#include <libyul/optimiser/FullInliner.h>
|
||||||
|
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
|
||||||
#include <libyul/optimiser/ForLoopInitRewriter.h>
|
#include <libyul/optimiser/ForLoopInitRewriter.h>
|
||||||
#include <libyul/optimiser/MainFunction.h>
|
#include <libyul/optimiser/MainFunction.h>
|
||||||
#include <libyul/optimiser/Rematerialiser.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 << "(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 << " (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 << " 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 << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/? " << endl;
|
||||||
cout.flush();
|
cout.flush();
|
||||||
@ -145,6 +146,9 @@ public:
|
|||||||
case 'o':
|
case 'o':
|
||||||
ForLoopInitRewriter{}(*m_ast);
|
ForLoopInitRewriter{}(*m_ast);
|
||||||
break;
|
break;
|
||||||
|
case 'O':
|
||||||
|
ForLoopConditionIntoBody{}(*m_ast);
|
||||||
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
(CommonSubexpressionEliminator{m_dialect})(*m_ast);
|
(CommonSubexpressionEliminator{m_dialect})(*m_ast);
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user