From fcfe8295349b00fbe5a4475caace8b984e91c300 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Sep 2019 19:16:33 +0200 Subject: [PATCH] Introduce LiteralRematerializer and thus simplify StructuralSimplifier. --- libyul/optimiser/Rematerialiser.cpp | 21 +++++- libyul/optimiser/Rematerialiser.h | 21 ++++++ libyul/optimiser/StructuralSimplifier.cpp | 64 +++++------------ libyul/optimiser/StructuralSimplifier.h | 10 +-- libyul/optimiser/Suite.cpp | 9 ++- test/cmdlineTests/yul_stack_opt/output | 54 +++++++------- test/libyul/YulOptimizerTest.cpp | 4 +- .../fullSuite/abi_example1.yul | 42 +++++------ .../yulOptimizerTests/fullSuite/aztec.yul | 70 +++++++++---------- test/tools/yulopti.cpp | 2 +- 10 files changed, 152 insertions(+), 145 deletions(-) diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index e11ed98f3..c9088a431 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -71,9 +71,9 @@ void Rematerialiser::visit(Expression& _e) if (_e.type() == typeid(Identifier)) { Identifier& identifier = boost::get(_e); - if (m_value.count(identifier.name)) + YulString name = identifier.name; + if (m_value.count(name)) { - YulString name = identifier.name; assertThrow(m_value.at(name), OptimizerException, ""); auto const& value = *m_value.at(name); size_t refs = m_referenceCounts[name]; @@ -93,3 +93,20 @@ void Rematerialiser::visit(Expression& _e) } DataFlowAnalyzer::visit(_e); } + +void LiteralRematerialiser::visit(Expression& _e) +{ + if (_e.type() == typeid(Identifier)) + { + Identifier& identifier = boost::get(_e); + YulString name = identifier.name; + if (m_value.count(name)) + { + Expression const* value = m_value.at(name); + assertThrow(value, OptimizerException, ""); + if (value->type() == typeid(Literal)) + _e = *value; + } + } + DataFlowAnalyzer::visit(_e); +} diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index 6871cdaa9..339a6a38f 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -68,4 +68,25 @@ protected: std::set m_varsToAlwaysRematerialize; }; +/** + * If a variable is referenced that is known to have a literal + * value at that point, replace it by a literal. + * + * This is mostly used so that other components do not have to rely + * on the data flow analyzer. + * + * Prerequisite: Disambiguator, ForLoopInitRewriter. + */ +class LiteralRematerialiser: public DataFlowAnalyzer +{ +public: + LiteralRematerialiser(Dialect const& _dialect): + DataFlowAnalyzer(_dialect) + {} + + using ASTModifier::visit; + void visit(Expression& _e) override; +}; + + } diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 98e70a3ee..b9d941e43 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -61,29 +61,7 @@ OptionalStatements replaceConstArgSwitch(Switch& _switchStmt, u256 const& _const void StructuralSimplifier::operator()(Block& _block) { - pushScope(false); simplify(_block.statements); - popScope(); -} - -boost::optional StructuralSimplifier::hasLiteralValue(Expression const& _expression) const -{ - Expression const* expr = &_expression; - - if (expr->type() == typeid(Identifier)) - { - Identifier const& ident = boost::get(*expr); - if (m_value.count(ident.name)) - expr = m_value.at(ident.name); - } - - if (expr && expr->type() == typeid(Literal)) - { - Literal const& literal = boost::get(*expr); - return valueOfLiteral(literal); - } - - return boost::optional(); } void StructuralSimplifier::simplify(std::vector& _statements) @@ -124,34 +102,24 @@ void StructuralSimplifier::simplify(std::vector& _statements) bool StructuralSimplifier::expressionAlwaysTrue(Expression const& _expression) { - return boost::apply_visitor(GenericFallbackReturnsVisitor( - [&](Identifier const& _identifier) -> bool { - if (auto expr = m_value[_identifier.name]) - return expressionAlwaysTrue(*expr); - return false; - }, - [](Literal const& _literal) -> bool { - return - (_literal.kind == LiteralKind::Boolean && _literal.value == "true"_yulstring) || - (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) != u256(0)) - ; - } - ), _expression); + if (boost::optional value = hasLiteralValue(_expression)) + return *value != 0; + else + return false; } bool StructuralSimplifier::expressionAlwaysFalse(Expression const& _expression) { - return boost::apply_visitor(GenericFallbackReturnsVisitor( - [&](Identifier const& _identifier) -> bool { - if (auto expr = m_value[_identifier.name]) - return expressionAlwaysFalse(*expr); - return false; - }, - [](Literal const& _literal) -> bool { - return - (_literal.kind == LiteralKind::Boolean && _literal.value == "false"_yulstring) || - (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) == u256(0)) - ; - } - ), _expression); + if (boost::optional value = hasLiteralValue(_expression)) + return *value == 0; + else + return false; +} + +boost::optional StructuralSimplifier::hasLiteralValue(Expression const& _expression) const +{ + if (_expression.type() == typeid(Literal)) + return valueOfLiteral(boost::get(_expression)); + else + return boost::optional(); } diff --git a/libyul/optimiser/StructuralSimplifier.h b/libyul/optimiser/StructuralSimplifier.h index a003abd6e..34a077a6f 100644 --- a/libyul/optimiser/StructuralSimplifier.h +++ b/libyul/optimiser/StructuralSimplifier.h @@ -30,16 +30,16 @@ namespace yul * - replace switch with const expr with matching case body * - replace for with false condition by its initialization part * - * Prerequisite: Disambiguator, ForLoopInitRewriter. + * The LiteralRematerialiser should be run before this. + * + * Prerequisite: Disambiguator. * * Important: Can only be used on EVM code. */ -class StructuralSimplifier: public DataFlowAnalyzer +class StructuralSimplifier: public ASTModifier { public: - explicit StructuralSimplifier(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} - - using DataFlowAnalyzer::operator(); + using ASTModifier::operator(); void operator()(Block& _block) override; private: void simplify(std::vector& _statements); diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index ac92761d7..bcd1a0b5e 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -91,7 +91,8 @@ void OptimiserSuite::run( UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); BlockFlattener{}(ast); ControlFlowSimplifier{_dialect}(ast); - StructuralSimplifier{_dialect}(ast); + LiteralRematerialiser{_dialect}(ast); + StructuralSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast); BlockFlattener{}(ast); @@ -125,7 +126,8 @@ void OptimiserSuite::run( { // still in SSA, perform structural simplification ControlFlowSimplifier{_dialect}(ast); - StructuralSimplifier{_dialect}(ast); + LiteralRematerialiser{_dialect}(ast); + StructuralSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast); BlockFlattener{}(ast); DeadCodeEliminator{_dialect}(ast); @@ -182,7 +184,8 @@ void OptimiserSuite::run( RedundantAssignEliminator::run(_dialect, ast); LoadResolver::run(_dialect, ast); ExpressionSimplifier::run(_dialect, ast); - StructuralSimplifier{_dialect}(ast); + LiteralRematerialiser{_dialect}(ast); + StructuralSimplifier{}(ast); BlockFlattener{}(ast); DeadCodeEliminator{_dialect}(ast); ControlFlowSimplifier{_dialect}(ast); diff --git a/test/cmdlineTests/yul_stack_opt/output b/test/cmdlineTests/yul_stack_opt/output index 0f987c7e6..7168d526b 100644 --- a/test/cmdlineTests/yul_stack_opt/output +++ b/test/cmdlineTests/yul_stack_opt/output @@ -11,20 +11,20 @@ object "object" { } function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3 { - let a := 1 - sstore(a, a) - sstore(2, a) - sstore(3, a) - sstore(4, a) - sstore(5, a) - sstore(6, a) - sstore(7, a) - sstore(8, a) - sstore(9, a) - sstore(10, a) - sstore(11, a) - sstore(12, a) - sstore(13, a) + let _1 := 1 + sstore(_1, _1) + sstore(2, _1) + sstore(3, _1) + sstore(4, _1) + sstore(5, _1) + sstore(6, _1) + sstore(7, _1) + sstore(8, _1) + sstore(9, _1) + sstore(10, _1) + sstore(11, _1) + sstore(12, _1) + sstore(13, _1) } } } @@ -103,79 +103,77 @@ tag_2: 0x00 /* "yul_stack_opt/input.sol":98:99 */ 0x01 - /* "yul_stack_opt/input.sol":139:140 */ dup1 - /* "yul_stack_opt/input.sol":136:137 */ dup2 /* "yul_stack_opt/input.sol":129:141 */ sstore - /* "yul_stack_opt/input.sol":162:163 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":151:160 */ 0x02 /* "yul_stack_opt/input.sol":144:164 */ sstore - /* "yul_stack_opt/input.sol":185:186 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":174:183 */ 0x03 /* "yul_stack_opt/input.sol":167:187 */ sstore - /* "yul_stack_opt/input.sol":208:209 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":197:206 */ 0x04 /* "yul_stack_opt/input.sol":190:210 */ sstore - /* "yul_stack_opt/input.sol":231:232 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":220:229 */ 0x05 /* "yul_stack_opt/input.sol":213:233 */ sstore - /* "yul_stack_opt/input.sol":254:255 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":243:252 */ 0x06 /* "yul_stack_opt/input.sol":236:256 */ sstore - /* "yul_stack_opt/input.sol":277:278 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":266:275 */ 0x07 /* "yul_stack_opt/input.sol":259:279 */ sstore - /* "yul_stack_opt/input.sol":300:301 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":289:298 */ 0x08 /* "yul_stack_opt/input.sol":282:302 */ sstore - /* "yul_stack_opt/input.sol":323:324 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":312:321 */ 0x09 /* "yul_stack_opt/input.sol":305:325 */ sstore - /* "yul_stack_opt/input.sol":346:347 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":335:344 */ 0x0a /* "yul_stack_opt/input.sol":328:348 */ sstore - /* "yul_stack_opt/input.sol":370:371 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":358:368 */ 0x0b /* "yul_stack_opt/input.sol":351:372 */ sstore - /* "yul_stack_opt/input.sol":394:395 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":382:392 */ 0x0c /* "yul_stack_opt/input.sol":375:396 */ sstore - /* "yul_stack_opt/input.sol":418:419 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":406:416 */ 0x0d diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 88e8c4e2b..431443685 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -279,7 +279,9 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line else if (m_optimizerStep == "structuralSimplifier") { disambiguate(); - StructuralSimplifier{*m_dialect}(*m_ast); + ForLoopInitRewriter{}(*m_ast); + LiteralRematerialiser{*m_dialect}(*m_ast); + StructuralSimplifier{}(*m_ast); } else if (m_optimizerStep == "equivalentFunctionCombiner") { diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index fdb6e724e..6ac882c70 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -462,34 +462,34 @@ // ---- // { // { -// let _1 := 0x20 -// let _2 := 0 -// let _3 := mload(_2) -// let pos := _1 -// let length := mload(_3) -// mstore(_1, length) +// let _1 := 0 +// let _2 := mload(_1) +// let pos := 0x20 +// let pos_1 := pos +// let length := mload(_2) +// mstore(pos, length) // pos := 64 -// let srcPtr := add(_3, _1) -// let i := _2 +// let srcPtr := add(_2, pos_1) +// let i := _1 // for { } lt(i, length) { i := add(i, 1) } // { // abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(mload(srcPtr), pos) -// srcPtr := add(srcPtr, _1) +// srcPtr := add(srcPtr, pos_1) // pos := add(pos, 0x60) // } -// let _4 := mload(64) -// let _5 := mload(_1) -// if slt(sub(_4, _5), 128) { revert(_2, _2) } -// let offset := calldataload(add(_5, 64)) -// let _6 := 0xffffffffffffffff -// if gt(offset, _6) { revert(_2, _2) } -// let value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_5, offset), _4) -// let offset_1 := calldataload(add(_5, 96)) -// if gt(offset_1, _6) { revert(_2, _2) } -// let value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_5, offset_1), _4) -// sstore(calldataload(_5), calldataload(add(_5, _1))) +// let _3 := mload(64) +// let _4 := mload(pos_1) +// if slt(sub(_3, _4), 128) { revert(_1, _1) } +// let offset := calldataload(add(_4, 64)) +// let _5 := 0xffffffffffffffff +// if gt(offset, _5) { revert(_1, _1) } +// let value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_4, offset), _3) +// let offset_1 := calldataload(add(_4, 96)) +// if gt(offset_1, _5) { revert(_1, _1) } +// let value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_4, offset_1), _3) +// sstore(calldataload(_4), calldataload(add(_4, pos_1))) // sstore(value2, value3) -// sstore(_2, pos) +// sstore(_1, pos) // } // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset, end) -> array // { diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index 82a6dcb6a..54193a4e6 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -238,8 +238,8 @@ // let notes := add(0x04, calldataload(0x04)) // let m := calldataload(0x24) // let n := calldataload(notes) -// let gen_order := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 -// let challenge := mod(calldataload(0x44), gen_order) +// let _1 := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +// let challenge := mod(calldataload(0x44), _1) // if gt(m, n) // { // mstore(0x00, 404) @@ -249,72 +249,70 @@ // mstore(0x2a0, caller()) // mstore(0x2c0, kn) // mstore(0x2e0, m) -// kn := mulmod(sub(gen_order, kn), challenge, gen_order) +// kn := mulmod(sub(_1, kn), challenge, _1) // hashCommitments(notes, n) // let b := add(0x300, mul(n, 0x80)) // let i := 0 // let i_1 := i // for { } lt(i, n) { i := add(i, 0x01) } // { -// let _1 := add(calldataload(0x04), mul(i, 0xc0)) -// let noteIndex := add(_1, 0x24) +// let _2 := add(calldataload(0x04), mul(i, 0xc0)) +// let noteIndex := add(_2, 0x24) // let k := i_1 -// let a := calldataload(add(_1, 0x44)) +// let a := calldataload(add(_2, 0x44)) // let c := challenge -// let _2 := add(i, 0x01) -// switch eq(_2, n) +// let _3 := add(i, 0x01) +// switch eq(_3, n) // case 1 { // k := kn -// if eq(m, n) { k := sub(gen_order, kn) } +// if eq(m, n) { k := sub(_1, kn) } // } // case 0 { k := calldataload(noteIndex) } // validateCommitment(noteIndex, k, a) -// switch gt(_2, m) +// switch gt(_3, m) // case 1 { -// kn := addmod(kn, sub(gen_order, k), gen_order) -// let x := mod(mload(i_1), gen_order) -// k := mulmod(k, x, gen_order) -// a := mulmod(a, x, gen_order) -// c := mulmod(challenge, x, gen_order) +// kn := addmod(kn, sub(_1, k), _1) +// let x := mod(mload(i_1), _1) +// k := mulmod(k, x, _1) +// a := mulmod(a, x, _1) +// c := mulmod(challenge, x, _1) // mstore(i_1, keccak256(i_1, 0x20)) // } -// case 0 { -// kn := addmod(kn, k, gen_order) -// } -// let _3 := 0x40 -// calldatacopy(0xe0, add(_1, 164), _3) -// calldatacopy(0x20, add(_1, 100), _3) -// mstore(0x120, sub(gen_order, c)) +// case 0 { kn := addmod(kn, k, _1) } +// let _4 := 0x40 +// calldatacopy(0xe0, add(_2, 164), _4) +// calldatacopy(0x20, add(_2, 100), _4) +// mstore(0x120, sub(_1, c)) // mstore(0x60, k) // mstore(0xc0, a) -// let result := call(gas(), 7, i_1, 0xe0, 0x60, 0x1a0, _3) -// let result_1 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x120, _3)) -// let result_2 := and(result_1, call(gas(), 7, i_1, 0x80, 0x60, 0x160, _3)) -// let result_3 := and(result_2, call(gas(), 6, i_1, 0x120, 0x80, 0x160, _3)) -// result := and(result_3, call(gas(), 6, i_1, 0x160, 0x80, b, _3)) +// let result := call(gas(), 7, i_1, 0xe0, 0x60, 0x1a0, _4) +// let result_1 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x120, _4)) +// let result_2 := and(result_1, call(gas(), 7, i_1, 0x80, 0x60, 0x160, _4)) +// let result_3 := and(result_2, call(gas(), 6, i_1, 0x120, 0x80, 0x160, _4)) +// result := and(result_3, call(gas(), 6, i_1, 0x160, 0x80, b, _4)) // if eq(i, m) // { // mstore(0x260, mload(0x20)) -// mstore(0x280, mload(_3)) +// mstore(0x280, mload(_4)) // mstore(0x1e0, mload(0xe0)) // mstore(0x200, sub(0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47, mload(0x100))) // } // if gt(i, m) // { // mstore(0x60, c) -// let result_4 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x220, _3)) -// let result_5 := and(result_4, call(gas(), 6, i_1, 0x220, 0x80, 0x260, _3)) -// result := and(result_5, call(gas(), 6, i_1, 0x1a0, 0x80, 0x1e0, _3)) +// let result_4 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x220, _4)) +// let result_5 := and(result_4, call(gas(), 6, i_1, 0x220, 0x80, 0x260, _4)) +// result := and(result_5, call(gas(), 6, i_1, 0x1a0, 0x80, 0x1e0, _4)) // } // if iszero(result) // { // mstore(i_1, 400) // revert(i_1, 0x20) // } -// b := add(b, _3) +// b := add(b, _4) // } // if lt(m, n) { validatePairing(0x64) } -// if iszero(eq(mod(keccak256(0x2a0, add(b, not(671))), gen_order), challenge)) +// if iszero(eq(mod(keccak256(0x2a0, add(b, not(671))), _1), challenge)) // { // mstore(i_1, 404) // revert(i_1, 0x20) @@ -360,13 +358,13 @@ // } // function validateCommitment(note, k, a) // { -// let gen_order := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 -// let field_order := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 // let gammaX := calldataload(add(note, 0x40)) // let gammaY := calldataload(add(note, 0x60)) // let sigmaX := calldataload(add(note, 0x80)) // let sigmaY := calldataload(add(note, 0xa0)) -// if iszero(and(and(and(eq(mod(a, gen_order), a), gt(a, 1)), and(eq(mod(k, gen_order), k), gt(k, 1))), and(eq(addmod(mulmod(mulmod(sigmaX, sigmaX, field_order), sigmaX, field_order), 3, field_order), mulmod(sigmaY, sigmaY, field_order)), eq(addmod(mulmod(mulmod(gammaX, gammaX, field_order), gammaX, field_order), 3, field_order), mulmod(gammaY, gammaY, field_order))))) +// let _1 := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 +// let _2 := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +// if iszero(and(and(and(eq(mod(a, _2), a), gt(a, 1)), and(eq(mod(k, _2), k), gt(k, 1))), and(eq(addmod(mulmod(mulmod(sigmaX, sigmaX, _1), sigmaX, _1), 3, _1), mulmod(sigmaY, sigmaY, _1)), eq(addmod(mulmod(mulmod(gammaX, gammaX, _1), gammaX, _1), 3, _1), mulmod(gammaY, gammaY, _1))))) // { // mstore(0x00, 400) // revert(0x00, 0x20) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index eff091714..875275328 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -184,7 +184,7 @@ public: ExpressionSimplifier::run(m_dialect, *m_ast); break; case 't': - (StructuralSimplifier{m_dialect})(*m_ast); + StructuralSimplifier{}(*m_ast); break; case 'n': (ControlFlowSimplifier{m_dialect})(*m_ast);