From be52aa318191c057e6bd917c888aa25c24857463 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 6 Mar 2019 11:33:02 +0100 Subject: [PATCH 1/2] YulOpt: Move if-branches into functions --- libyul/optimiser/StructuralSimplifier.cpp | 170 +++++++++++++--------- 1 file changed, 101 insertions(+), 69 deletions(-) diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index dfa1e7f92..77106e451 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -22,12 +22,14 @@ #include #include -#include +#include using namespace std; using namespace dev; using namespace yul; +using OptionalStatements = boost::optional>; + namespace { ExpressionStatement makePopExpressionStatement(langutil::SourceLocation const& _location, Expression&& _expression) @@ -39,6 +41,84 @@ ExpressionStatement makePopExpressionStatement(langutil::SourceLocation const& _ }}; } +void removeEmptyDefaultFromSwitch(Switch& _switchStmt) +{ + boost::remove_erase_if( + _switchStmt.cases, + [](Case const& _case) { return !_case.value && _case.body.statements.empty(); } + ); +} + +void removeEmptyCasesFromSwitch(Switch& _switchStmt) +{ + bool hasDefault = boost::algorithm::any_of( + _switchStmt.cases, + [](Case const& _case) { return !_case.value; } + ); + + if (hasDefault) + return; + + boost::remove_erase_if( + _switchStmt.cases, + [](Case const& _case) { return _case.body.statements.empty(); } + ); +} + +OptionalStatements replaceConstArgSwitch(Switch& _switchStmt, u256 const& _constExprVal) +{ + Block* matchingCaseBlock = nullptr; + Case* defaultCase = nullptr; + + for (auto& _case: _switchStmt.cases) + { + if (_case.value && valueOfLiteral(*_case.value) == _constExprVal) + { + matchingCaseBlock = &_case.body; + break; + } + else if (!_case.value) + defaultCase = &_case; + } + + if (!matchingCaseBlock && defaultCase) + matchingCaseBlock = &defaultCase->body; + + OptionalStatements s = vector{}; + + if (matchingCaseBlock) + s->emplace_back(std::move(*matchingCaseBlock)); + + return s; +} + +OptionalStatements reduceSingleCaseSwitch(Switch& _switchStmt) +{ + yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!"); + + auto& switchCase = _switchStmt.cases.front(); + auto loc = locationOf(*_switchStmt.expression); + if (switchCase.value) + { + OptionalStatements s = vector{}; + s->emplace_back(If{ + std::move(_switchStmt.location), + make_unique(FunctionalInstruction{ + std::move(loc), + solidity::Instruction::EQ, + {std::move(*switchCase.value), std::move(*_switchStmt.expression)} + }), std::move(switchCase.body)}); + return s; + } + else + { + OptionalStatements s = vector{}; + s->emplace_back(makePopExpressionStatement(loc, std::move(*_switchStmt.expression))); + s->emplace_back(std::move(switchCase.body)); + return s; + } +} + } void StructuralSimplifier::operator()(Block& _block) @@ -48,6 +128,19 @@ void StructuralSimplifier::operator()(Block& _block) popScope(); } +OptionalStatements StructuralSimplifier::reduceNoCaseSwitch(Switch& _switchStmt) const +{ + yulAssert(_switchStmt.cases.empty(), "Expected no case!"); + + auto loc = locationOf(*_switchStmt.expression); + + OptionalStatements s = vector{}; + + s->emplace_back(makePopExpressionStatement(loc, std::move(*_switchStmt.expression))); + + return s; +} + boost::optional StructuralSimplifier::hasLiteralValue(Expression const& _expression) const { Expression const* expr = &_expression; @@ -70,7 +163,6 @@ boost::optional StructuralSimplifier::hasLiteralValue(Expression cons void StructuralSimplifier::simplify(std::vector& _statements) { - using OptionalStatements = boost::optional>; GenericFallbackReturnsVisitor const visitor( [&](If& _ifStmt) -> OptionalStatements { if (_ifStmt.body.statements.empty()) @@ -86,75 +178,15 @@ void StructuralSimplifier::simplify(std::vector& _statements) return {}; }, [&](Switch& _switchStmt) -> OptionalStatements { - auto& cases = _switchStmt.cases; + removeEmptyDefaultFromSwitch(_switchStmt); + removeEmptyCasesFromSwitch(_switchStmt); - if (cases.size() == 1) - { - auto& switchCase = cases.front(); - auto loc = locationOf(*_switchStmt.expression); - if (switchCase.value) - { - OptionalStatements s = vector{}; - s->emplace_back(If{ - std::move(_switchStmt.location), - make_unique(FunctionalInstruction{ - std::move(loc), - solidity::Instruction::EQ, - {std::move(*switchCase.value), std::move(*_switchStmt.expression)} - }), std::move(switchCase.body)}); - return s; - } - else - { - OptionalStatements s = vector{}; - s->emplace_back(makePopExpressionStatement(loc, std::move(*_switchStmt.expression))); - s->emplace_back(std::move(switchCase.body)); - return s; - } - } - // Replace the whole switch with the resulting case body if arg. is - // a constant + if (_switchStmt.cases.empty()) + return reduceNoCaseSwitch(_switchStmt); + else if (_switchStmt.cases.size() == 1) + return reduceSingleCaseSwitch(_switchStmt); else if (boost::optional const constExprVal = hasLiteralValue(*_switchStmt.expression)) - { - Block* matchingCaseBlock = nullptr; - Case* defaultCase = nullptr; - - for (auto& _case: cases) - { - if (_case.value && valueOfLiteral(*_case.value) == constExprVal) - { - matchingCaseBlock = &_case.body; - break; - } - else if (!_case.value) - defaultCase = &_case; - } - - if (!matchingCaseBlock && defaultCase) - matchingCaseBlock = &defaultCase->body; - - OptionalStatements s = vector{}; - - if (matchingCaseBlock) - s->emplace_back(std::move(*matchingCaseBlock)); - - return s; - } - - // Remove cases with empty body if no default case exists - auto const defaultCase = boost::find_if( - cases, - [](Case const& _case) { return !_case.value; }); - - if ( - (defaultCase != cases.end() && - defaultCase->body.statements.empty()) || - defaultCase == cases.end() - ) - boost::remove_erase_if( - cases, - [](Case const& _case) { return _case.body.statements.empty(); } - ); + return replaceConstArgSwitch(_switchStmt, constExprVal.get()); return {}; }, From 21322dae29f122aa3c0dd990348a5c380681d07b Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 6 Mar 2019 11:33:27 +0100 Subject: [PATCH 2/2] YulOpt: Remove empty cases --- Changelog.md | 3 +++ libyul/optimiser/StructuralSimplifier.h | 4 ++++ .../switch_remove_empty_all.yul | 19 +++++++++++++++++++ .../switch_remove_empty_case.yul | 4 ++-- .../switch_remove_empty_cases.yul | 4 ++-- .../switch_remove_empty_default_case.yul | 4 ++-- 6 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_all.yul diff --git a/Changelog.md b/Changelog.md index 04603bcbf..9ac719cac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,9 @@ Language Features: Compiler Features: * SMTChecker: Support one-dimensional arrays. + * Yul Optimizer: Add rule to remove empty default switch cases + * Yul Optimizer: Add rule to remove empty cases if no default exists + * Yul Optimizer: Add rule to replace a switch with no cases with pop(expression) Bugfixes: diff --git a/libyul/optimiser/StructuralSimplifier.h b/libyul/optimiser/StructuralSimplifier.h index 7bf8b7ec4..4c403dc81 100644 --- a/libyul/optimiser/StructuralSimplifier.h +++ b/libyul/optimiser/StructuralSimplifier.h @@ -28,6 +28,9 @@ namespace yul * - replace if with empty body with pop(condition) * - replace if with true condition with its body * - remove if with false condition + * - remove empty default switch case + * - remove empty switch case if no default case exists + * - replace switch with no cases with pop(expression) * - turn switch with single case into if * - replace switch with only default case with pop(expression) and body * - replace switch with const expr with matching case body @@ -49,6 +52,7 @@ private: bool expressionAlwaysTrue(Expression const& _expression); bool expressionAlwaysFalse(Expression const& _expression); boost::optional hasLiteralValue(Expression const& _expression) const; + boost::optional> reduceNoCaseSwitch(Switch& _switchStmt) const; }; } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_all.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_all.yul new file mode 100644 index 000000000..c19faa5ca --- /dev/null +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_all.yul @@ -0,0 +1,19 @@ +{ + let y := 200 + switch add(y, 4) + case 0 { } + case 1 { } + default { } + + switch 4 + case 0 { } + case 1 { } + default { } +} +// ---- +// structuralSimplifier +// { +// let y := 200 +// pop(add(y, 4)) +// pop(4) +// } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_case.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_case.yul index 50eba24be..5203feeab 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_case.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_case.yul @@ -8,8 +8,8 @@ // structuralSimplifier // { // let y := 200 -// switch y -// case 1 { +// if eq(1, y) +// { // y := 9 // } // } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_cases.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_cases.yul index 2dc1c48c8..3841af4b1 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_cases.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_cases.yul @@ -9,8 +9,8 @@ // structuralSimplifier // { // let y := 200 -// switch y -// case 1 { +// if eq(1, y) +// { // y := 9 // } // } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_default_case.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_default_case.yul index 751d856b0..4b6c2ccda 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_default_case.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_remove_empty_default_case.yul @@ -8,8 +8,8 @@ // structuralSimplifier // { // let y := 200 -// switch y -// case 1 { +// if eq(1, y) +// { // y := 9 // } // }