From be52aa318191c057e6bd917c888aa25c24857463 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 6 Mar 2019 11:33:02 +0100 Subject: [PATCH] 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 {}; },