From e2bfb1a6634254c169ac0c31d7d4824abd7f3b28 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 25 Jun 2019 11:27:58 +0200 Subject: [PATCH] Translation of switch statement for wasm backend. --- libyul/backends/wasm/WordSizeTransform.cpp | 111 +++++++++++++++++- libyul/backends/wasm/WordSizeTransform.h | 11 ++ .../wordSizeTransform/switch_1.yul | 71 +++++++++++ .../wordSizeTransform/switch_2.yul | 77 ++++++++++++ .../wordSizeTransform/switch_3.yul | 89 ++++++++++++++ .../wordSizeTransform/switch_4.yul | 97 +++++++++++++++ .../wordSizeTransform/switch_5.yul | 29 +++++ 7 files changed, 483 insertions(+), 2 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul create mode 100644 test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul create mode 100644 test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul create mode 100644 test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul create mode 100644 test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index 08d55e296..01f0c3784 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -24,6 +24,7 @@ #include #include +#include using namespace std; using namespace dev; @@ -62,7 +63,7 @@ void WordSizeTransform::operator()(If& _if) void WordSizeTransform::operator()(Switch&) { - yulAssert(false, "Switch statement not implemented."); + yulAssert(false, "Switch statement has to be handled inside the containing block."); } void WordSizeTransform::operator()(ForLoop& _for) @@ -153,6 +154,8 @@ void WordSizeTransform::operator()(Block& _block) else yulAssert(false, ""); } + else if (_s.type() == typeid(Switch)) + return handleSwitch(boost::get(_s)); else visit(_s); return boost::none; @@ -206,6 +209,110 @@ void WordSizeTransform::rewriteFunctionCallArguments(vector& _args) ); } +vector WordSizeTransform::handleSwitchInternal( + langutil::SourceLocation const& _location, + vector const& _splitExpressions, + vector _cases, + YulString _runDefaultFlag, + size_t _depth +) +{ + if (_depth == 4) + { + yulAssert(_cases.size() == 1, ""); + return std::move(_cases.front().body.statements); + } + + // Extract current 64 bit segment and group by it. + map> cases; + for (Case& c: _cases) + { + yulAssert(c.value, "Default case still present."); + cases[ + (valueOfLiteral(*c.value) >> (256 - 64 * (_depth + 1))) & + std::numeric_limits::max() + ].emplace_back(std::move(c)); + } + + Switch ret{ + _location, + make_unique(Identifier{_location, _splitExpressions.at(_depth)}), + {} + }; + + for (auto& c: cases) + { + Literal label{_location, LiteralKind::Number, YulString(c.first.str()), "u64"_yulstring}; + ret.cases.emplace_back(Case{ + c.second.front().location, + make_unique(std::move(label)), + Block{_location, handleSwitchInternal( + _location, + _splitExpressions, + std::move(c.second), + _runDefaultFlag, + _depth + 1 + )} + }); + } + if (!_runDefaultFlag.empty()) + ret.cases.emplace_back(Case{ + _location, + nullptr, + Block{_location, make_vector( + Assignment{ + _location, + {{_location, _runDefaultFlag}}, + make_unique(Literal{_location, LiteralKind::Number, "1"_yulstring, "u64"_yulstring}) + } + )} + }); + return make_vector(std::move(ret)); +} + +std::vector WordSizeTransform::handleSwitch(Switch& _switch) +{ + for (auto& c: _switch.cases) + (*this)(c.body); + + // Turns the switch into a quadruply-nested switch plus + // a flag that tells to execute the default case after all the switches. + vector ret; + + YulString runDefaultFlag; + Case defaultCase; + if (!_switch.cases.back().value) + { + runDefaultFlag = m_nameDispenser.newName("run_default"_yulstring); + defaultCase = std::move(_switch.cases.back()); + _switch.cases.pop_back(); + ret.emplace_back(VariableDeclaration{ + _switch.location, + {TypedName{_switch.location, runDefaultFlag, "u64"_yulstring}}, + {} + }); + } + vector splitExpressions; + for (auto const& expr: expandValue(*_switch.expression)) + splitExpressions.emplace_back(boost::get(*expr).name); + + ret += handleSwitchInternal( + _switch.location, + splitExpressions, + std::move(_switch.cases), + runDefaultFlag, + 0 + ); + if (!runDefaultFlag.empty()) + ret.emplace_back(If{ + _switch.location, + make_unique(Identifier{_switch.location, runDefaultFlag}), + std::move(defaultCase.body) + }); + return ret; +} + + array WordSizeTransform::generateU64IdentifierNames(YulString const& _s) { yulAssert(m_variableMapping.find(_s) == m_variableMapping.end(), ""); @@ -242,7 +349,7 @@ array, 4> WordSizeTransform::expandValue(Expression const } } else - yulAssert(false, ""); + yulAssert(false, "Invalid expression to split."); return ret; } diff --git a/libyul/backends/wasm/WordSizeTransform.h b/libyul/backends/wasm/WordSizeTransform.h index 42bf58940..21d586439 100644 --- a/libyul/backends/wasm/WordSizeTransform.h +++ b/libyul/backends/wasm/WordSizeTransform.h @@ -23,6 +23,8 @@ #include #include +#include + #include #include @@ -78,6 +80,15 @@ private: void rewriteIdentifierList(std::vector&); void rewriteFunctionCallArguments(std::vector&); + std::vector handleSwitch(Switch& _switch); + std::vector handleSwitchInternal( + langutil::SourceLocation const& _location, + std::vector const& _splitExpressions, + std::vector _cases, + YulString _runDefaultFlag, + size_t _depth + ); + std::array generateU64IdentifierNames(YulString const& _s); std::array, 4> expandValue(Expression const& _e); std::vector expandValueToVector(Expression const& _e); diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul new file mode 100644 index 000000000..22cb7631e --- /dev/null +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul @@ -0,0 +1,71 @@ +{ + switch calldataload(0) + case 0 { sstore(0, 1) } + case 1 { sstore(1, 1) } + case 2 { sstore(2, 1) } + case 3 { sstore(3, 1) } +} +// ==== +// step: wordSizeTransform +// ---- +// { +// let _1_0 := 0 +// let _1_1 := 0 +// let _1_2 := 0 +// let _1_3 := 0 +// let _2_0, _2_1, _2_2, _2_3 := calldataload(_1_0, _1_1, _1_2, _1_3) +// switch _2_0 +// case 0 { +// switch _2_1 +// case 0 { +// switch _2_2 +// case 0 { +// switch _2_3 +// case 0 { +// let _3_0 := 0 +// let _3_1 := 0 +// let _3_2 := 0 +// let _3_3 := 1 +// let _4_0 := 0 +// let _4_1 := 0 +// let _4_2 := 0 +// let _4_3 := 0 +// sstore(_4_0, _4_1, _4_2, _4_3, _3_0, _3_1, _3_2, _3_3) +// } +// case 1 { +// let _5_0 := 0 +// let _5_1 := 0 +// let _5_2 := 0 +// let _5_3 := 1 +// let _6_0 := 0 +// let _6_1 := 0 +// let _6_2 := 0 +// let _6_3 := 1 +// sstore(_6_0, _6_1, _6_2, _6_3, _5_0, _5_1, _5_2, _5_3) +// } +// case 2 { +// let _7_0 := 0 +// let _7_1 := 0 +// let _7_2 := 0 +// let _7_3 := 1 +// let _8_0 := 0 +// let _8_1 := 0 +// let _8_2 := 0 +// let _8_3 := 2 +// sstore(_8_0, _8_1, _8_2, _8_3, _7_0, _7_1, _7_2, _7_3) +// } +// case 3 { +// let _9_0 := 0 +// let _9_1 := 0 +// let _9_2 := 0 +// let _9_3 := 1 +// let _10_0 := 0 +// let _10_1 := 0 +// let _10_2 := 0 +// let _10_3 := 3 +// sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3) +// } +// } +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul new file mode 100644 index 000000000..12759e230 --- /dev/null +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul @@ -0,0 +1,77 @@ +{ + switch calldataload(0) + case 0x01000000000000000000000000000000000000010 { sstore(0, 1) } + case 0x02000000000000000000000000000000000000010 { sstore(1, 1) } + case 0x01000000000000000000000000000000000000020 { sstore(2, 1) } + case 0x02000000000000000000000000000000000000020 { sstore(3, 1) } +} +// ==== +// step: wordSizeTransform +// ---- +// { +// let _1_0 := 0 +// let _1_1 := 0 +// let _1_2 := 0 +// let _1_3 := 0 +// let _2_0, _2_1, _2_2, _2_3 := calldataload(_1_0, _1_1, _1_2, _1_3) +// switch _2_0 +// case 0 { +// switch _2_1 +// case 268435456 { +// switch _2_2 +// case 0 { +// switch _2_3 +// case 16 { +// let _3_0 := 0 +// let _3_1 := 0 +// let _3_2 := 0 +// let _3_3 := 1 +// let _4_0 := 0 +// let _4_1 := 0 +// let _4_2 := 0 +// let _4_3 := 0 +// sstore(_4_0, _4_1, _4_2, _4_3, _3_0, _3_1, _3_2, _3_3) +// } +// case 32 { +// let _7_0 := 0 +// let _7_1 := 0 +// let _7_2 := 0 +// let _7_3 := 1 +// let _8_0 := 0 +// let _8_1 := 0 +// let _8_2 := 0 +// let _8_3 := 2 +// sstore(_8_0, _8_1, _8_2, _8_3, _7_0, _7_1, _7_2, _7_3) +// } +// } +// } +// case 536870912 { +// switch _2_2 +// case 0 { +// switch _2_3 +// case 16 { +// let _5_0 := 0 +// let _5_1 := 0 +// let _5_2 := 0 +// let _5_3 := 1 +// let _6_0 := 0 +// let _6_1 := 0 +// let _6_2 := 0 +// let _6_3 := 1 +// sstore(_6_0, _6_1, _6_2, _6_3, _5_0, _5_1, _5_2, _5_3) +// } +// case 32 { +// let _9_0 := 0 +// let _9_1 := 0 +// let _9_2 := 0 +// let _9_3 := 1 +// let _10_0 := 0 +// let _10_1 := 0 +// let _10_2 := 0 +// let _10_3 := 3 +// sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3) +// } +// } +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul new file mode 100644 index 000000000..50a36f61f --- /dev/null +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul @@ -0,0 +1,89 @@ +{ + switch calldataload(0) + case 0 { sstore(0, 1) } + case 1 { sstore(1, 1) } + case 2 { sstore(2, 1) } + case 3 { sstore(3, 1) } + default { sstore(8, 9) } +} +// ==== +// step: wordSizeTransform +// ---- +// { +// let _1_0 := 0 +// let _1_1 := 0 +// let _1_2 := 0 +// let _1_3 := 0 +// let _2_0, _2_1, _2_2, _2_3 := calldataload(_1_0, _1_1, _1_2, _1_3) +// let run_default +// switch _2_0 +// case 0 { +// switch _2_1 +// case 0 { +// switch _2_2 +// case 0 { +// switch _2_3 +// case 0 { +// let _3_0 := 0 +// let _3_1 := 0 +// let _3_2 := 0 +// let _3_3 := 1 +// let _4_0 := 0 +// let _4_1 := 0 +// let _4_2 := 0 +// let _4_3 := 0 +// sstore(_4_0, _4_1, _4_2, _4_3, _3_0, _3_1, _3_2, _3_3) +// } +// case 1 { +// let _5_0 := 0 +// let _5_1 := 0 +// let _5_2 := 0 +// let _5_3 := 1 +// let _6_0 := 0 +// let _6_1 := 0 +// let _6_2 := 0 +// let _6_3 := 1 +// sstore(_6_0, _6_1, _6_2, _6_3, _5_0, _5_1, _5_2, _5_3) +// } +// case 2 { +// let _7_0 := 0 +// let _7_1 := 0 +// let _7_2 := 0 +// let _7_3 := 1 +// let _8_0 := 0 +// let _8_1 := 0 +// let _8_2 := 0 +// let _8_3 := 2 +// sstore(_8_0, _8_1, _8_2, _8_3, _7_0, _7_1, _7_2, _7_3) +// } +// case 3 { +// let _9_0 := 0 +// let _9_1 := 0 +// let _9_2 := 0 +// let _9_3 := 1 +// let _10_0 := 0 +// let _10_1 := 0 +// let _10_2 := 0 +// let _10_3 := 3 +// sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3) +// } +// default { run_default := 1 } +// } +// default { run_default := 1 } +// } +// default { run_default := 1 } +// } +// default { run_default := 1 } +// if run_default +// { +// let _11_0 := 0 +// let _11_1 := 0 +// let _11_2 := 0 +// let _11_3 := 9 +// let _12_0 := 0 +// let _12_1 := 0 +// let _12_2 := 0 +// let _12_3 := 8 +// sstore(_12_0, _12_1, _12_2, _12_3, _11_0, _11_1, _11_2, _11_3) +// } +// } diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul new file mode 100644 index 000000000..106941302 --- /dev/null +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul @@ -0,0 +1,97 @@ +{ + switch calldataload(0) + case 0x01000000000000000000000000000000000000010 { sstore(0, 1) } + case 0x02000000000000000000000000000000000000010 { sstore(1, 1) } + case 0x01000000000000000000000000000000000000020 { sstore(2, 1) } + case 0x02000000000000000000000000000000000000020 { sstore(3, 1) } + default { sstore(8, 9) } +} +// ==== +// step: wordSizeTransform +// ---- +// { +// let _1_0 := 0 +// let _1_1 := 0 +// let _1_2 := 0 +// let _1_3 := 0 +// let _2_0, _2_1, _2_2, _2_3 := calldataload(_1_0, _1_1, _1_2, _1_3) +// let run_default +// switch _2_0 +// case 0 { +// switch _2_1 +// case 268435456 { +// switch _2_2 +// case 0 { +// switch _2_3 +// case 16 { +// let _3_0 := 0 +// let _3_1 := 0 +// let _3_2 := 0 +// let _3_3 := 1 +// let _4_0 := 0 +// let _4_1 := 0 +// let _4_2 := 0 +// let _4_3 := 0 +// sstore(_4_0, _4_1, _4_2, _4_3, _3_0, _3_1, _3_2, _3_3) +// } +// case 32 { +// let _7_0 := 0 +// let _7_1 := 0 +// let _7_2 := 0 +// let _7_3 := 1 +// let _8_0 := 0 +// let _8_1 := 0 +// let _8_2 := 0 +// let _8_3 := 2 +// sstore(_8_0, _8_1, _8_2, _8_3, _7_0, _7_1, _7_2, _7_3) +// } +// default { run_default := 1 } +// } +// default { run_default := 1 } +// } +// case 536870912 { +// switch _2_2 +// case 0 { +// switch _2_3 +// case 16 { +// let _5_0 := 0 +// let _5_1 := 0 +// let _5_2 := 0 +// let _5_3 := 1 +// let _6_0 := 0 +// let _6_1 := 0 +// let _6_2 := 0 +// let _6_3 := 1 +// sstore(_6_0, _6_1, _6_2, _6_3, _5_0, _5_1, _5_2, _5_3) +// } +// case 32 { +// let _9_0 := 0 +// let _9_1 := 0 +// let _9_2 := 0 +// let _9_3 := 1 +// let _10_0 := 0 +// let _10_1 := 0 +// let _10_2 := 0 +// let _10_3 := 3 +// sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3) +// } +// default { run_default := 1 } +// } +// default { run_default := 1 } +// } +// default { run_default := 1 } +// } +// default { run_default := 1 } +// if run_default +// { +// let _11_0 := 0 +// let _11_1 := 0 +// let _11_2 := 0 +// let _11_3 := 9 +// let _12_0 := 0 +// let _12_1 := 0 +// let _12_2 := 0 +// let _12_3 := 8 +// sstore(_12_0, _12_1, _12_2, _12_3, _11_0, _11_1, _11_2, _11_3) +// } +// } diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul new file mode 100644 index 000000000..eb05e0a6c --- /dev/null +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul @@ -0,0 +1,29 @@ +{ + switch calldataload(0) + default { sstore(8, 9) } +} +// ==== +// step: wordSizeTransform +// ---- +// { +// let _1_0 := 0 +// let _1_1 := 0 +// let _1_2 := 0 +// let _1_3 := 0 +// let _2_0, _2_1, _2_2, _2_3 := calldataload(_1_0, _1_1, _1_2, _1_3) +// let run_default +// switch _2_0 +// default { run_default := 1 } +// if run_default +// { +// let _3_0 := 0 +// let _3_1 := 0 +// let _3_2 := 0 +// let _3_3 := 9 +// let _4_0 := 0 +// let _4_1 := 0 +// let _4_2 := 0 +// let _4_3 := 8 +// sstore(_4_0, _4_1, _4_2, _4_3, _3_0, _3_1, _3_2, _3_3) +// } +// }