Use builtin iszero for for loop condition rewriting.

This commit is contained in:
chriseth 2019-09-11 16:30:47 +02:00 committed by Daniel Kirchner
parent 324cc71b13
commit 4f80117eef
12 changed files with 111 additions and 16 deletions

View File

@ -61,6 +61,7 @@ struct Dialect: boost::noncopyable
virtual BuiltinFunction const* discardFunction() const { return nullptr; }
virtual BuiltinFunction const* equalityFunction() const { return nullptr; }
virtual BuiltinFunction const* booleanNegationFunction() const { return nullptr; }
virtual std::set<YulString> fixedFunctionNames() const { return {}; }

View File

@ -70,6 +70,7 @@ struct EVMDialect: public Dialect
BuiltinFunctionForEVM const* discardFunction() const override { return builtin("pop"_yulstring); }
BuiltinFunctionForEVM const* equalityFunction() const override { return builtin("eq"_yulstring); }
BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("iszero"_yulstring); }
static EVMDialect const& looseAssemblyForEVM(langutil::EVMVersion _version);
static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version);

View File

@ -47,6 +47,7 @@ struct WasmDialect: public Dialect
BuiltinFunction const* builtin(YulString _name) const override;
BuiltinFunction const* discardFunction() const override { return builtin("drop"_yulstring); }
BuiltinFunction const* equalityFunction() const override { return builtin("i64.eq"_yulstring); }
BuiltinFunction const* booleanNegationFunction() const override { return builtin("i64.eqz"_yulstring); }
std::set<YulString> fixedFunctionNames() const override { return {"main"_yulstring}; }

View File

@ -25,7 +25,7 @@ using namespace yul;
void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop)
{
if (_forLoop.condition->type() != typeid(Literal))
if (m_dialect.booleanNegationFunction() && _forLoop.condition->type() != typeid(Literal))
{
langutil::SourceLocation loc = locationOf(*_forLoop.condition);
_forLoop.body.statements.insert(
@ -33,9 +33,9 @@ void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop)
If {
loc,
make_unique<Expression>(
FunctionalInstruction {
FunctionCall {
loc,
eth::Instruction::ISZERO,
{loc, m_dialect.booleanNegationFunction()->name},
make_vector<Expression>(std::move(*_forLoop.condition))
}
),

View File

@ -17,6 +17,7 @@
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/Dialect.h>
namespace yul
{
@ -29,17 +30,22 @@ namespace yul
* 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.
* This rewriter 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.
* - Only works for dialects with a builtin boolean negation function.
*/
class ForLoopConditionIntoBody: public ASTModifier
{
public:
ForLoopConditionIntoBody(Dialect const& _dialect): m_dialect(_dialect) {}
using ASTModifier::operator();
void operator()(ForLoop& _forLoop) override;
private:
Dialect const& m_dialect;
};
}

View File

@ -33,6 +33,7 @@
#include <libyul/optimiser/ExpressionJoiner.h>
#include <libyul/optimiser/ExpressionInliner.h>
#include <libyul/optimiser/FullInliner.h>
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/optimiser/ForLoopInitRewriter.h>
#include <libyul/optimiser/Rematerialiser.h>
#include <libyul/optimiser/UnusedPruner.h>
@ -94,6 +95,7 @@ void OptimiserSuite::run(
LiteralRematerialiser{_dialect}(ast);
StructuralSimplifier{}(ast);
ControlFlowSimplifier{_dialect}(ast);
ForLoopConditionIntoBody{_dialect}(ast);
BlockFlattener{}(ast);
// None of the above can make stack problems worse.

View File

@ -17,9 +17,9 @@ contract C {
// optimize-yul: true
// ----
// creation:
// codeDepositCost: 610600
// executionCost: 645
// totalCost: 611245
// codeDepositCost: 624200
// executionCost: 657
// totalCost: 624857
// external:
// a(): 429
// b(uint256): 884

View File

@ -141,7 +141,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
else if (m_optimizerStep == "forLoopConditionIntoBody")
{
disambiguate();
ForLoopConditionIntoBody{}(*m_ast);
ForLoopConditionIntoBody{*m_dialect}(*m_ast);
}
else if (m_optimizerStep == "forLoopInitRewriter")
{

View File

@ -0,0 +1,77 @@
{
let _1 := 0
let _33 := calldataload(_1)
let sum_50_141 := _1
let sum_50_146 := _1
let sum_50 := _1
let length_51 := calldataload(_33)
let i_53_142 := _1
let i_53_147 := _1
let i_53 := _1
for { }
1
{
let _108 := 1
let i_53_121 := add(i_53, _108)
let i_53_144 := i_53_121
let i_53_149 := i_53_121
i_53 := i_53_121
}
{
let _109 := lt(i_53, length_51)
let _110 := iszero(_109)
if _110 { break }
let _114_128 := iszero(_109)
if _114_128 { revert(_1, _1) }
let _13_129 := 0x20
let _115_130 := mul(i_53, _13_129)
let _116_131 := add(_33, _115_130)
let _117_132 := add(_116_131, _13_129)
let v_122_133 := calldataload(_117_132)
let sum_50_120 := add(sum_50, v_122_133)
let sum_50_143 := sum_50_120
let sum_50_148 := sum_50_120
sum_50 := sum_50_120
}
sstore(_1, sum_50)
}
// ====
// step: commonSubexpressionEliminator
// ----
// {
// let _1 := 0
// let _33 := calldataload(_1)
// let sum_50_141 := _1
// let sum_50_146 := _1
// let sum_50 := _1
// let length_51 := calldataload(_33)
// let i_53_142 := _1
// let i_53_147 := _1
// let i_53 := _1
// for { }
// 1
// {
// let _108 := 1
// let i_53_121 := add(i_53, _108)
// let i_53_144 := i_53_121
// let i_53_149 := i_53_121
// i_53 := i_53_121
// }
// {
// let _109 := lt(i_53, length_51)
// let _110 := iszero(_109)
// if _110 { break }
// let _114_128 := _110
// if _110 { revert(_1, _1) }
// let _13_129 := 0x20
// let _115_130 := mul(i_53, _13_129)
// let _116_131 := add(_33, _115_130)
// let _117_132 := add(_116_131, _13_129)
// let v_122_133 := calldataload(_117_132)
// let sum_50_120 := add(sum_50, v_122_133)
// let sum_50_143 := sum_50_120
// let sum_50_148 := sum_50_120
// sum_50 := sum_50_120
// }
// sstore(_1, sum_50)
// }

View File

@ -471,8 +471,9 @@
// pos := 64
// let srcPtr := add(_2, pos_1)
// let i := _1
// for { } lt(i, length) { i := add(i, 1) }
// for { } 1 { i := add(i, 1) }
// {
// if iszero(lt(i, length)) { break }
// abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(mload(srcPtr), pos)
// srcPtr := add(srcPtr, pos_1)
// pos := add(pos, 0x60)
@ -503,8 +504,9 @@
// let src := add(offset, _1)
// if gt(add(add(offset, mul(length, 0x40)), _1), end) { revert(0, 0) }
// let i := 0
// for { } lt(i, length) { i := add(i, 1) }
// for { } 1 { i := add(i, 1) }
// {
// if iszero(lt(i, length)) { break }
// if iszero(slt(add(src, 0x1f), end)) { revert(0, 0) }
// let dst_1 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2))
// let dst_2 := dst_1
@ -512,8 +514,9 @@
// let _2 := add(src, 0x40)
// if gt(_2, end) { revert(0, 0) }
// let i_1 := 0
// for { } lt(i_1, 0x2) { i_1 := add(i_1, 1) }
// for { } 1 { i_1 := add(i_1, 1) }
// {
// if iszero(lt(i_1, 0x2)) { break }
// mstore(dst_1, calldataload(src_1))
// dst_1 := add(dst_1, _1)
// src_1 := add(src_1, _1)
@ -535,8 +538,9 @@
// let src := add(offset, _1)
// if gt(add(add(offset, mul(length, _1)), _1), end) { revert(0, 0) }
// let i := 0
// for { } lt(i, length) { i := add(i, 1) }
// for { } 1 { i := add(i, 1) }
// {
// if iszero(lt(i, length)) { break }
// mstore(dst, calldataload(src))
// dst := add(dst, _1)
// src := add(src, _1)
@ -546,8 +550,9 @@
// {
// let srcPtr := value
// let i := 0
// for { } lt(i, 0x3) { i := add(i, 1) }
// for { } 1 { i := add(i, 1) }
// {
// if iszero(lt(i, 0x3)) { break }
// mstore(pos, and(mload(srcPtr), sub(shl(160, 1), 1)))
// srcPtr := add(srcPtr, 0x20)
// pos := add(pos, 0x20)

View File

@ -254,8 +254,9 @@
// let b := add(0x300, mul(n, 0x80))
// let i := 0
// let i_1 := i
// for { } lt(i, n) { i := add(i, 0x01) }
// for { } 1 { i := add(i, 0x01) }
// {
// if iszero(lt(i, n)) { break }
// let _2 := add(calldataload(0x04), mul(i, 0xc0))
// let noteIndex := add(_2, 0x24)
// let k := i_1
@ -373,8 +374,9 @@
// function hashCommitments(notes, n)
// {
// let i := 0
// for { } lt(i, n) { i := add(i, 0x01) }
// for { } 1 { i := add(i, 0x01) }
// {
// if iszero(lt(i, n)) { break }
// calldatacopy(add(0x300, mul(i, 0x80)), add(add(notes, mul(i, 0xc0)), 0x60), 0x80)
// }
// mstore(0, keccak256(0x300, mul(n, 0x80)))

View File

@ -151,7 +151,7 @@ public:
ForLoopInitRewriter{}(*m_ast);
break;
case 'O':
ForLoopConditionIntoBody{}(*m_ast);
ForLoopConditionIntoBody{m_dialect}(*m_ast);
break;
case 'c':
CommonSubexpressionEliminator::run(m_dialect, *m_ast);