From c686a65876725fe38688ff132a2ed5c389b80e99 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 27 Feb 2019 16:55:42 +0100 Subject: [PATCH] Yul Optimizer: reduce switches with const arguments --- Changelog.md | 1 + libyul/optimiser/StructuralSimplifier.cpp | 48 ++++++++++++++++++- libyul/optimiser/StructuralSimplifier.h | 3 ++ .../fullSuite/switch_inline.yul | 13 +++++ .../fullSuite/switch_inline_match_default.yul | 14 ++++++ .../structuralSimplifier/switch_inline.yul | 14 ++++++ .../switch_inline_match_default.yul | 15 ++++++ .../switch_inline_no_match.yul | 11 +++++ 8 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul create mode 100644 test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul create mode 100644 test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul create mode 100644 test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul create mode 100644 test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul diff --git a/Changelog.md b/Changelog.md index cf05a948d..b0af39acd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Compiler Features: * Optimizer: Add rule for shifts with constants for Constantinople. * Optimizer: Combine multiple shifts with constant shift-by values into one. * Optimizer: Support shifts in the constant optimiser for Constantinople. + * Yul Optimizer: Add rule to replace switch statements with const expr. with matching case body Bugfixes: diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 727775cb3..34ee26a16 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -45,6 +45,26 @@ void StructuralSimplifier::operator()(Block& _block) 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 valueOfNumberLiteral(literal); + } + + return boost::optional(); +} + void StructuralSimplifier::simplify(std::vector& _statements) { using OptionalStatements = boost::optional>; @@ -62,7 +82,7 @@ void StructuralSimplifier::simplify(std::vector& _statements) return {vector{}}; return {}; }, - [](Switch& _switchStmt) -> OptionalStatements { + [&](Switch& _switchStmt) -> OptionalStatements { if (_switchStmt.cases.size() == 1) { auto& switchCase = _switchStmt.cases.front(); @@ -87,6 +107,32 @@ void StructuralSimplifier::simplify(std::vector& _statements) return s; } } + else if (boost::optional const constExprVal = hasLiteralValue(*_switchStmt.expression)) + { + Block* matchingCaseBlock = nullptr; + Case* defaultCase = nullptr; + + for (auto& _case: _switchStmt.cases) + { + if (_case.value && valueOfNumberLiteral(*_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; + } else return {}; }, diff --git a/libyul/optimiser/StructuralSimplifier.h b/libyul/optimiser/StructuralSimplifier.h index 111c7fdc7..7bf8b7ec4 100644 --- a/libyul/optimiser/StructuralSimplifier.h +++ b/libyul/optimiser/StructuralSimplifier.h @@ -18,6 +18,7 @@ #include #include +#include namespace yul { @@ -29,6 +30,7 @@ namespace yul * - remove if with false condition * - 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 * - remove for with false condition * * Prerequisites: Disambiguator @@ -46,6 +48,7 @@ private: void simplify(std::vector& _statements); bool expressionAlwaysTrue(Expression const& _expression); bool expressionAlwaysFalse(Expression const& _expression); + boost::optional hasLiteralValue(Expression const& _expression) const; }; } diff --git a/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul b/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul new file mode 100644 index 000000000..876400ce9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul @@ -0,0 +1,13 @@ +{ + mstore(f(1), 0) + function f(x) -> y { + switch x + case 0 { y := 8 } + case 1 { y := 9 } + } +} +// ---- +// fullSuite +// { +// mstore(9, 0) +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul b/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul new file mode 100644 index 000000000..260bcdc68 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul @@ -0,0 +1,14 @@ +{ + mstore(f(3), 0) + function f(x) -> y { + switch x + case 0 { y := 8 } + case 1 { y := 9 } + default { y := 10 } + } +} +// ---- +// fullSuite +// { +// mstore(10, 0) +// } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul new file mode 100644 index 000000000..bca6d481c --- /dev/null +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul @@ -0,0 +1,14 @@ +{ + let y := 200 + switch 1 + case 0 { y := 8 } + case 1 { y := 9 } +} +// ---- +// structuralSimplifier +// { +// let y := 200 +// { +// y := 9 +// } +// } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul new file mode 100644 index 000000000..5ffcf16a5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul @@ -0,0 +1,15 @@ +{ + let y := 200 + switch 3 + case 0 { y := 8 } + case 1 { y := 9 } + default { y := 10 } +} +// ---- +// structuralSimplifier +// { +// let y := 200 +// { +// y := 10 +// } +// } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul new file mode 100644 index 000000000..2006b49c7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul @@ -0,0 +1,11 @@ +{ + let y := 200 + switch 3 + case 0 { y := 8 } + case 1 { y := 9 } +} +// ---- +// structuralSimplifier +// { +// let y := 200 +// }