Merge pull request #6131 from ethereum/fix-yul-opt

Yul Optimizer: reduce switches with const arguments
This commit is contained in:
chriseth 2019-02-28 16:28:53 +01:00 committed by GitHub
commit 3dee9c50cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 118 additions and 1 deletions

View File

@ -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:

View File

@ -45,6 +45,26 @@ void StructuralSimplifier::operator()(Block& _block)
popScope();
}
boost::optional<dev::u256> StructuralSimplifier::hasLiteralValue(Expression const& _expression) const
{
Expression const* expr = &_expression;
if (expr->type() == typeid(Identifier))
{
Identifier const& ident = boost::get<Identifier>(*expr);
if (m_value.count(ident.name))
expr = m_value.at(ident.name);
}
if (expr && expr->type() == typeid(Literal))
{
Literal const& literal = boost::get<Literal>(*expr);
return valueOfNumberLiteral(literal);
}
return boost::optional<u256>();
}
void StructuralSimplifier::simplify(std::vector<yul::Statement>& _statements)
{
using OptionalStatements = boost::optional<vector<Statement>>;
@ -62,7 +82,7 @@ void StructuralSimplifier::simplify(std::vector<yul::Statement>& _statements)
return {vector<Statement>{}};
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<yul::Statement>& _statements)
return s;
}
}
else if (boost::optional<u256> 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<Statement>{};
if (matchingCaseBlock)
s->emplace_back(std::move(*matchingCaseBlock));
return s;
}
else
return {};
},

View File

@ -18,6 +18,7 @@
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/DataFlowAnalyzer.h>
#include <libdevcore/Common.h>
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<Statement>& _statements);
bool expressionAlwaysTrue(Expression const& _expression);
bool expressionAlwaysFalse(Expression const& _expression);
boost::optional<dev::u256> hasLiteralValue(Expression const& _expression) const;
};
}

View File

@ -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)
// }

View File

@ -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)
// }

View File

@ -0,0 +1,14 @@
{
let y := 200
switch 1
case 0 { y := 8 }
case 1 { y := 9 }
}
// ----
// structuralSimplifier
// {
// let y := 200
// {
// y := 9
// }
// }

View File

@ -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
// }
// }

View File

@ -0,0 +1,11 @@
{
let y := 200
switch 3
case 0 { y := 8 }
case 1 { y := 9 }
}
// ----
// structuralSimplifier
// {
// let y := 200
// }