From e99da7e01514dd53b3e8371a585a555371f4b423 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 9 Mar 2021 14:53:10 +0100 Subject: [PATCH] Extract StackReuseCodegen tests. --- libevmasm/Instruction.cpp | 9 +- libevmasm/Instruction.h | 2 +- test/CMakeLists.txt | 3 +- test/InteractiveTests.h | 2 + test/libyul/EVMCodeTransformTest.cpp | 63 +++ test/libyul/EVMCodeTransformTest.h | 39 ++ test/libyul/StackReuseCodegen.cpp | 414 ------------------ .../evmCodeTransform/stackReuse/for_1.yul | 19 + .../evmCodeTransform/stackReuse/for_2.yul | 22 + .../stackReuse/function_call.yul | 34 ++ .../stackReuse/function_params.yul | 14 + .../function_params_and_retparams.yul | 25 ++ ...ction_params_and_retparams_partly_used.yul | 32 ++ .../stackReuse/function_retparam.yul | 16 + .../stackReuse/function_trivial.yul | 12 + .../function_with_body_embedded.yul | 35 ++ .../stackReuse/functions_multi_return.yul | 61 +++ .../libyul/evmCodeTransform/stackReuse/if.yul | 17 + .../stackReuse/last_use_in_nested_block.yul | 10 + .../multi_reuse_same_variable_name.yul | 23 + .../stackReuse/multi_reuse_single_slot.yul | 14 + .../multi_reuse_single_slot_nested.yul | 14 + .../reuse_on_decl_assign_not_same_scope.yul | 17 + .../reuse_on_decl_assign_to_last_used.yul | 16 + ...reuse_on_decl_assign_to_last_used_expr.yul | 18 + .../reuse_on_decl_assign_to_not_last_used.yul | 15 + .../stackReuse/reuse_slots.yul | 23 + .../stackReuse/reuse_slots_function.yul | 40 ++ .../reuse_slots_function_with_gaps.yul | 50 +++ .../stackReuse/single_var.yul | 6 + .../stackReuse/single_var_assigned.yul | 6 + .../single_var_assigned_plus_code.yul | 9 + ...ngle_var_assigned_plus_code_and_reused.yul | 12 + .../evmCodeTransform/stackReuse/smoke.yul | 4 + .../evmCodeTransform/stackReuse/switch.yul | 26 ++ test/tools/CMakeLists.txt | 1 + 36 files changed, 703 insertions(+), 420 deletions(-) create mode 100644 test/libyul/EVMCodeTransformTest.cpp create mode 100644 test/libyul/EVMCodeTransformTest.h delete mode 100644 test/libyul/StackReuseCodegen.cpp create mode 100644 test/libyul/evmCodeTransform/stackReuse/for_1.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/for_2.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/function_call.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/function_params.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/function_params_and_retparams.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/function_params_and_retparams_partly_used.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/function_retparam.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/function_trivial.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/function_with_body_embedded.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/functions_multi_return.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/if.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/last_use_in_nested_block.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/multi_reuse_same_variable_name.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/multi_reuse_single_slot.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/multi_reuse_single_slot_nested.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_not_same_scope.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_last_used.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_last_used_expr.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_not_last_used.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/reuse_slots.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/reuse_slots_function.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/reuse_slots_function_with_gaps.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/single_var.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/single_var_assigned.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/single_var_assigned_plus_code.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/single_var_assigned_plus_code_and_reused.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/smoke.yul create mode 100644 test/libyul/evmCodeTransform/stackReuse/switch.yul diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 55af2340a..5ccc4c542 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -353,18 +353,19 @@ void solidity::evmasm::eachInstruction( } } -string solidity::evmasm::disassemble(bytes const& _mem) +string solidity::evmasm::disassemble(bytes const& _mem, string const& _delimiter) { stringstream ret; eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) { if (!isValidInstruction(_instr)) - ret << "0x" << std::uppercase << std::hex << static_cast(_instr) << " "; + ret << "0x" << std::uppercase << std::hex << static_cast(_instr) << _delimiter; else { InstructionInfo info = instructionInfo(_instr); - ret << info.name << " "; + ret << info.name; if (info.additional) - ret << "0x" << std::uppercase << std::hex << _data << " "; + ret << " 0x" << std::uppercase << std::hex << _data; + ret << _delimiter; } }); return ret.str(); diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 5c8eeb3ee..a06c567ba 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -314,6 +314,6 @@ extern const std::map c_instructions; void eachInstruction(bytes const& _mem, std::function const& _onInstruction); /// Convert from EVM code to simple EVM assembly language. -std::string disassemble(bytes const& _mem); +std::string disassemble(bytes const& _mem, std::string const& _delimiter = " "); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2e9fbbc4e..931e35492 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -121,6 +121,8 @@ set(libyul_sources libyul/Common.cpp libyul/Common.h libyul/CompilabilityChecker.cpp + libyul/EVMCodeTransformTest.cpp + libyul/EVMCodeTransformTest.h libyul/EwasmTranslationTest.cpp libyul/EwasmTranslationTest.h libyul/FunctionSideEffects.cpp @@ -131,7 +133,6 @@ set(libyul_sources libyul/ObjectCompilerTest.h libyul/ObjectParser.cpp libyul/Parser.cpp - libyul/StackReuseCodegen.cpp libyul/SyntaxTest.h libyul/SyntaxTest.cpp libyul/YulInterpreterTest.cpp diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 2daf716aa..92869bd24 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ Testsuite const g_interactiveTestsuites[] = { {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, {"Function Side Effects","libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, + {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, {"Error Recovery", "libsolidity", "errorRecoveryTests", false, false, &SyntaxTest::createErrorRecovery}, {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, diff --git a/test/libyul/EVMCodeTransformTest.cpp b/test/libyul/EVMCodeTransformTest.cpp new file mode 100644 index 000000000..58a0c2795 --- /dev/null +++ b/test/libyul/EVMCodeTransformTest.cpp @@ -0,0 +1,63 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include + +#include + +#include + +#include + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::yul; +using namespace solidity::yul::test; +using namespace solidity::frontend; +using namespace solidity::frontend::test; +using namespace std; + +EVMCodeTransformTest::EVMCodeTransformTest(string const& _filename): + TestCase(_filename) +{ + m_source = m_reader.source(); + m_stackOpt = m_reader.boolSetting("stackOptimization", false); + m_expectation = m_reader.simpleExpectations(); +} + +TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + solidity::frontend::OptimiserSettings settings = solidity::frontend::OptimiserSettings::full(); + settings.runYulOptimiser = false; + settings.optimizeStackAllocation = m_stackOpt; + AssemblyStack stack(EVMVersion{}, AssemblyStack::Language::StrictAssembly, settings); + if (!stack.parseAndAnalyze("", m_source)) + { + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + SourceReferenceFormatter formatter(_stream, true, false); + for (auto const& error: stack.errors()) + formatter.printErrorInformation(*error); + return TestResult::FatalError; + } + + m_obtainedResult = evmasm::disassemble(stack.assemble(AssemblyStack::Machine::EVM).bytecode->bytecode, "\n"); + + return checkResult(_stream, _linePrefix, _formatted); +} diff --git a/test/libyul/EVMCodeTransformTest.h b/test/libyul/EVMCodeTransformTest.h new file mode 100644 index 000000000..f1ae73f74 --- /dev/null +++ b/test/libyul/EVMCodeTransformTest.h @@ -0,0 +1,39 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +namespace solidity::yul::test +{ + +class EVMCodeTransformTest: public solidity::frontend::test::TestCase +{ +public: + static std::unique_ptr create(Config const& _config) + { + return std::make_unique(_config.filename); + } + explicit EVMCodeTransformTest(std::string const& _filename); + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; +private: + bool m_stackOpt = false; +}; + +} diff --git a/test/libyul/StackReuseCodegen.cpp b/test/libyul/StackReuseCodegen.cpp deleted file mode 100644 index 1b6b91657..000000000 --- a/test/libyul/StackReuseCodegen.cpp +++ /dev/null @@ -1,414 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see . -*/ -/** - * Unit tests for stack-reusing code generator. - */ - -#include - -#include -#include - -#include - -using namespace std; - -namespace solidity::yul::test -{ - -namespace -{ -string assemble(string const& _input) -{ - solidity::frontend::OptimiserSettings settings = solidity::frontend::OptimiserSettings::full(); - settings.runYulOptimiser = false; - settings.optimizeStackAllocation = true; - AssemblyStack asmStack(langutil::EVMVersion{}, AssemblyStack::Language::StrictAssembly, settings); - BOOST_REQUIRE_MESSAGE(asmStack.parseAndAnalyze("", _input), "Source did not parse: " + _input); - return evmasm::disassemble(asmStack.assemble(AssemblyStack::Machine::EVM).bytecode->bytecode); -} -} - -BOOST_AUTO_TEST_SUITE(StackReuseCodegen, *boost::unit_test::label("nooptions")) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - string out = assemble("{}"); - BOOST_CHECK_EQUAL(out, ""); -} - -BOOST_AUTO_TEST_CASE(single_var) -{ - string out = assemble("{ let x }"); - BOOST_CHECK_EQUAL(out, "PUSH1 0x0 POP "); -} - -BOOST_AUTO_TEST_CASE(single_var_assigned) -{ - string out = assemble("{ let x := 1 }"); - BOOST_CHECK_EQUAL(out, "PUSH1 0x1 POP "); -} - -BOOST_AUTO_TEST_CASE(single_var_assigned_plus_code) -{ - string out = assemble("{ let x := 1 mstore(3, 4) }"); - BOOST_CHECK_EQUAL(out, "PUSH1 0x1 POP PUSH1 0x4 PUSH1 0x3 MSTORE "); -} - -BOOST_AUTO_TEST_CASE(single_var_assigned_plus_code_and_reused) -{ - string out = assemble("{ let x := 1 mstore(3, 4) pop(mload(x)) }"); - BOOST_CHECK_EQUAL(out, "PUSH1 0x1 PUSH1 0x4 PUSH1 0x3 MSTORE DUP1 MLOAD POP POP "); -} - -BOOST_AUTO_TEST_CASE(multi_reuse_single_slot) -{ - string out = assemble("{ let x := 1 x := 6 let y := 2 y := 4 }"); - BOOST_CHECK_EQUAL(out, "PUSH1 0x1 PUSH1 0x6 SWAP1 POP POP PUSH1 0x2 PUSH1 0x4 SWAP1 POP POP "); -} - -BOOST_AUTO_TEST_CASE(multi_reuse_single_slot_nested) -{ - string out = assemble("{ let x := 1 x := 6 { let y := 2 y := 4 } }"); - BOOST_CHECK_EQUAL(out, "PUSH1 0x1 PUSH1 0x6 SWAP1 POP POP PUSH1 0x2 PUSH1 0x4 SWAP1 POP POP "); -} - -BOOST_AUTO_TEST_CASE(multi_reuse_same_variable_name) -{ - string out = assemble("{ let z := mload(0) { let x := 1 x := 6 z := x } { let x := 2 z := x x := 4 } }"); - BOOST_CHECK_EQUAL(out, - "PUSH1 0x0 MLOAD " - "PUSH1 0x1 PUSH1 0x6 SWAP1 POP DUP1 SWAP2 POP POP " - "PUSH1 0x2 DUP1 SWAP2 POP PUSH1 0x4 SWAP1 POP POP " - "POP " - ); -} - -BOOST_AUTO_TEST_CASE(last_use_in_nested_block) -{ - string out = assemble("{ let z := 0 { pop(z) } let x := 1 }"); - BOOST_CHECK_EQUAL(out, "PUSH1 0x0 DUP1 POP POP PUSH1 0x1 POP "); -} - -BOOST_AUTO_TEST_CASE(if_) -{ - // z is only removed after the if (after the jumpdest) - string out = assemble("{ let z := mload(0) if z { let x := z } let t := 3 }"); - BOOST_CHECK_EQUAL(out, "PUSH1 0x0 MLOAD DUP1 ISZERO PUSH1 0xA JUMPI DUP1 POP JUMPDEST POP PUSH1 0x3 POP "); -} - -BOOST_AUTO_TEST_CASE(switch_) -{ - string out = assemble("{ let z := 0 switch z case 0 { let x := 2 let y := 3 } default { z := 3 } let t := 9 }"); - BOOST_CHECK_EQUAL(out, - "PUSH1 0x0 DUP1 " - "PUSH1 0x0 DUP2 EQ PUSH1 0x11 JUMPI " - "PUSH1 0x3 SWAP2 POP PUSH1 0x18 JUMP " - "JUMPDEST PUSH1 0x2 POP PUSH1 0x3 POP " - "JUMPDEST POP POP " // This is where z and its copy (switch condition) can be removed. - "PUSH1 0x9 POP " - ); -} - -BOOST_AUTO_TEST_CASE(reuse_slots) -{ - // x and y should reuse the slots of b and d - string out = assemble("{ let a, b, c, d let x := 2 let y := 3 mstore(x, a) mstore(y, c) }"); - BOOST_CHECK_EQUAL(out, - "PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 " - "POP " // d is removed right away - "PUSH1 0x2 SWAP2 POP " // x is stored at b's slot - "PUSH1 0x3 DUP4 DUP4 MSTORE " - "DUP2 DUP2 MSTORE " - "POP POP POP POP " - ); -} - -BOOST_AUTO_TEST_CASE(for_1) -{ - // Special scoping rules, but can remove z early - string out = assemble("{ for { let z := 0 } 1 { } { let x := 3 } let t := 2 }"); - BOOST_CHECK_EQUAL(out, - "PUSH1 0x0 POP " - "JUMPDEST PUSH1 0x1 ISZERO PUSH1 0x11 JUMPI " - "PUSH1 0x3 POP JUMPDEST PUSH1 0x3 JUMP " - "JUMPDEST PUSH1 0x2 POP " - ); -} - -BOOST_AUTO_TEST_CASE(for_2) -{ - // Special scoping rules, cannot remove z until after the loop! - string out = assemble("{ for { let z := 0 } 1 { } { z := 8 let x := 3 } let t := 2 }"); - BOOST_CHECK_EQUAL(out, - "PUSH1 0x0 " - "JUMPDEST PUSH1 0x1 ISZERO PUSH1 0x14 JUMPI " - "PUSH1 0x8 SWAP1 POP " - "PUSH1 0x3 POP " - "JUMPDEST PUSH1 0x2 JUMP " - "JUMPDEST POP " // z is removed - "PUSH1 0x2 POP " - ); -} - -BOOST_AUTO_TEST_CASE(function_trivial) -{ - string in = R"({ - function f() { } - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x6 JUMP JUMPDEST JUMPDEST JUMP JUMPDEST " - ); -} - -BOOST_AUTO_TEST_CASE(function_retparam) -{ - string in = R"({ - function f() -> x, y { } - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0xC JUMP " - "JUMPDEST PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP1 SWAP2 JUMP " - "JUMPDEST " - ); -} - -BOOST_AUTO_TEST_CASE(function_params) -{ - string in = R"({ - function f(a, b) { } - })"; - BOOST_CHECK_EQUAL(assemble(in), "PUSH1 0x8 JUMP JUMPDEST JUMPDEST POP POP JUMP JUMPDEST "); -} - -BOOST_AUTO_TEST_CASE(function_params_and_retparams) -{ - string in = R"({ - function f(a, b, c, d) -> x, y { } - })"; - // This does not re-use the parameters for the return parameters - // We do not expect parameters to be fully unused, so the stack - // layout for a function is still fixed, even though parameters - // can be re-used. - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x11 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP5 POP SWAP5 SWAP3 POP POP POP JUMP JUMPDEST " - ); -} - -BOOST_AUTO_TEST_CASE(function_params_and_retparams_partly_unused) -{ - string in = R"({ - function f(a, b, c, d) -> x, y { b := 3 let s := 9 y := 2 mstore(s, y) } - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x1F JUMP " - "JUMPDEST PUSH1 0x0 PUSH1 0x0 " - "PUSH1 0x3 SWAP4 POP " - "PUSH1 0x9 PUSH1 0x2 SWAP2 POP " - "DUP2 DUP2 MSTORE " - "POP JUMPDEST SWAP5 POP SWAP5 SWAP3 POP POP POP JUMP " - "JUMPDEST " - ); -} - -BOOST_AUTO_TEST_CASE(function_with_body_embedded) -{ - string in = R"({ - let b := 3 - function f(a, r) -> t { - // r could be removed right away, but a cannot - this is not implemented, though - let x := a a := 3 t := a - } - b := 7 - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x3 PUSH1 " - "0x17 JUMP " - "JUMPDEST PUSH1 0x0 " // start of f, initialize t - "DUP2 POP " // let x := a - "PUSH1 0x3 SWAP2 POP " - "DUP2 SWAP1 POP " - "JUMPDEST SWAP3 SWAP2 POP POP JUMP " - "JUMPDEST PUSH1 0x7 SWAP1 " - "POP POP " - ); -} - -BOOST_AUTO_TEST_CASE(function_call) -{ - string in = R"({ - let b := f(1, 2) - function f(a, r) -> t { } - b := f(3, 4) - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x9 PUSH1 0x2 PUSH1 0x1 PUSH1 0xD JUMP " - "JUMPDEST PUSH1 0x16 JUMP " // jump over f - "JUMPDEST PUSH1 0x0 JUMPDEST SWAP3 SWAP2 POP POP JUMP " // f - "JUMPDEST PUSH1 0x20 PUSH1 0x4 PUSH1 0x3 PUSH1 0xD JUMP " - "JUMPDEST SWAP1 POP POP " - ); -} - - -BOOST_AUTO_TEST_CASE(functions_multi_return) -{ - string in = R"({ - function f(a, b) -> t { } - function g() -> r, s { } - let x := f(1, 2) - x := f(3, 4) - let y, z := g() - y, z := g() - let unused := 7 - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x15 JUMP " - "JUMPDEST PUSH1 0x0 JUMPDEST SWAP3 SWAP2 POP POP JUMP " // f - "JUMPDEST PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP1 SWAP2 JUMP " // g - "JUMPDEST PUSH1 0x1F PUSH1 0x2 PUSH1 0x1 PUSH1 0x3 JUMP " // f(1, 2) - "JUMPDEST PUSH1 0x29 PUSH1 0x4 PUSH1 0x3 PUSH1 0x3 JUMP " // f(3, 4) - "JUMPDEST SWAP1 POP " // assignment to x - "POP " // remove x - "PUSH1 0x32 PUSH1 0xC JUMP " // g() - "JUMPDEST PUSH1 0x38 PUSH1 0xC JUMP " // g() - "JUMPDEST SWAP2 POP SWAP2 POP " // assignments - "POP POP " // removal of y and z - "PUSH1 0x7 POP " - ); -} - -BOOST_AUTO_TEST_CASE(reuse_slots_function) -{ - string in = R"({ - function f() -> x, y, z, t {} - let a, b, c, d := f() let x1 := 2 let y1 := 3 mstore(x1, a) mstore(y1, c) - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x12 JUMP " - "JUMPDEST PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP1 SWAP2 SWAP3 SWAP4 JUMP " - "JUMPDEST PUSH1 0x18 PUSH1 0x3 JUMP " - // Stack: a b c d - "JUMPDEST POP " // d is unused - // Stack: a b c - "PUSH1 0x2 SWAP2 POP " // x1 reuses b's slot - "PUSH1 0x3 " - // Stack: a x1 c y1 - "DUP4 DUP4 MSTORE " - "DUP2 DUP2 MSTORE " - "POP POP POP POP " - ); -} - -BOOST_AUTO_TEST_CASE(reuse_slots_function_with_gaps) -{ - string in = R"({ - // Only x3 is actually used, the slots of - // x1 and x2 will be reused right away. - let x1 := 5 let x2 := 6 let x3 := 7 - mstore(x1, x2) - function f() -> x, y, z, t {} - let a, b, c, d := f() mstore(x3, a) mstore(c, d) - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x5 PUSH1 0x6 PUSH1 0x7 " - "DUP2 DUP4 MSTORE " - "PUSH1 0x1B JUMP " // jump across function - "JUMPDEST PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP1 SWAP2 SWAP3 SWAP4 JUMP " - "JUMPDEST PUSH1 0x21 PUSH1 0xC JUMP " - // stack: x1 x2 x3 a b c d - "JUMPDEST SWAP6 POP " // move d into x1 - // stack: d x2 x3 a b c - "SWAP4 POP " - // stack: d c x3 a b - "POP " - // stack: d c x3 a - "DUP1 DUP3 MSTORE " - "POP POP " - // stack: d c - "DUP2 DUP2 MSTORE " - "POP POP " - ); -} - -BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_to_last_used) -{ - string in = R"({ - let x := 5 - let y := x // y should reuse the stack slot of x - sstore(y, y) - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x5 " - "DUP1 SWAP1 POP " - "DUP1 DUP2 SSTORE " - "POP " - ); -} - -BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_to_last_used_expr) -{ - string in = R"({ - let x := 5 - let y := add(x, 2) // y should reuse the stack slot of x - sstore(y, y) - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x5 " - "PUSH1 0x2 DUP2 ADD " - "SWAP1 POP " - "DUP1 DUP2 SSTORE " - "POP " - ); -} - -BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_to_not_last_used) -{ - string in = R"({ - let x := 5 - let y := x // y should not reuse the stack slot of x, since x is still used below - sstore(y, x) - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x5 " - "DUP1 " - "DUP2 DUP2 SSTORE " - "POP POP " - ); -} - -BOOST_AUTO_TEST_CASE(reuse_on_decl_assign_not_same_scope) -{ - string in = R"({ - let x := 5 - { - let y := x // y should not reuse the stack slot of x, since x is not in the same scope - sstore(y, y) - } - })"; - BOOST_CHECK_EQUAL(assemble(in), - "PUSH1 0x5 " - "DUP1 " - "DUP1 DUP2 SSTORE " - "POP POP " - ); -} - - -BOOST_AUTO_TEST_SUITE_END() - -} diff --git a/test/libyul/evmCodeTransform/stackReuse/for_1.yul b/test/libyul/evmCodeTransform/stackReuse/for_1.yul new file mode 100644 index 000000000..e2f4ba128 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/for_1.yul @@ -0,0 +1,19 @@ +{ for { let z := 0 } 1 { } { let x := 3 } let t := 2 } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x0 +// POP +// JUMPDEST +// PUSH1 0x1 +// ISZERO +// PUSH1 0x11 +// JUMPI +// PUSH1 0x3 +// POP +// JUMPDEST +// PUSH1 0x3 +// JUMP +// JUMPDEST +// PUSH1 0x2 +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/for_2.yul b/test/libyul/evmCodeTransform/stackReuse/for_2.yul new file mode 100644 index 000000000..f1f7460d4 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/for_2.yul @@ -0,0 +1,22 @@ +{ for { let z := 0 } 1 { } { z := 8 let x := 3 } let t := 2 } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x0 +// JUMPDEST +// PUSH1 0x1 +// ISZERO +// PUSH1 0x14 +// JUMPI +// PUSH1 0x8 +// SWAP1 +// POP +// PUSH1 0x3 +// POP +// JUMPDEST +// PUSH1 0x2 +// JUMP +// JUMPDEST +// POP +// PUSH1 0x2 +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/function_call.yul b/test/libyul/evmCodeTransform/stackReuse/function_call.yul new file mode 100644 index 000000000..fcf2ef5af --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/function_call.yul @@ -0,0 +1,34 @@ +{ + let b := f(1, 2) + function f(a, r) -> t { } + b := f(3, 4) +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x9 +// PUSH1 0x2 +// PUSH1 0x1 +// PUSH1 0xD +// JUMP +// JUMPDEST +// PUSH1 0x16 +// JUMP +// JUMPDEST +// PUSH1 0x0 +// JUMPDEST +// SWAP3 +// SWAP2 +// POP +// POP +// JUMP +// JUMPDEST +// PUSH1 0x20 +// PUSH1 0x4 +// PUSH1 0x3 +// PUSH1 0xD +// JUMP +// JUMPDEST +// SWAP1 +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/function_params.yul b/test/libyul/evmCodeTransform/stackReuse/function_params.yul new file mode 100644 index 000000000..39f961a5b --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/function_params.yul @@ -0,0 +1,14 @@ +{ + function f(a, b) { } +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x8 +// JUMP +// JUMPDEST +// JUMPDEST +// POP +// POP +// JUMP +// JUMPDEST diff --git a/test/libyul/evmCodeTransform/stackReuse/function_params_and_retparams.yul b/test/libyul/evmCodeTransform/stackReuse/function_params_and_retparams.yul new file mode 100644 index 000000000..513893fb8 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/function_params_and_retparams.yul @@ -0,0 +1,25 @@ +// This does not re-use the parameters for the return parameters +// We do not expect parameters to be fully unused, so the stack +// layout for a function is still fixed, even though parameters +// can be re-used. +{ + function f(a, b, c, d) -> x, y { } +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x11 +// JUMP +// JUMPDEST +// PUSH1 0x0 +// PUSH1 0x0 +// JUMPDEST +// SWAP5 +// POP +// SWAP5 +// SWAP3 +// POP +// POP +// POP +// JUMP +// JUMPDEST diff --git a/test/libyul/evmCodeTransform/stackReuse/function_params_and_retparams_partly_used.yul b/test/libyul/evmCodeTransform/stackReuse/function_params_and_retparams_partly_used.yul new file mode 100644 index 000000000..bd5b05fd0 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/function_params_and_retparams_partly_used.yul @@ -0,0 +1,32 @@ +{ + function f(a, b, c, d) -> x, y { b := 3 let s := 9 y := 2 mstore(s, y) } +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x1F +// JUMP +// JUMPDEST +// PUSH1 0x0 +// PUSH1 0x0 +// PUSH1 0x3 +// SWAP4 +// POP +// PUSH1 0x9 +// PUSH1 0x2 +// SWAP2 +// POP +// DUP2 +// DUP2 +// MSTORE +// POP +// JUMPDEST +// SWAP5 +// POP +// SWAP5 +// SWAP3 +// POP +// POP +// POP +// JUMP +// JUMPDEST diff --git a/test/libyul/evmCodeTransform/stackReuse/function_retparam.yul b/test/libyul/evmCodeTransform/stackReuse/function_retparam.yul new file mode 100644 index 000000000..4dde5208b --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/function_retparam.yul @@ -0,0 +1,16 @@ +{ + function f() -> x, y { } +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0xC +// JUMP +// JUMPDEST +// PUSH1 0x0 +// PUSH1 0x0 +// JUMPDEST +// SWAP1 +// SWAP2 +// JUMP +// JUMPDEST diff --git a/test/libyul/evmCodeTransform/stackReuse/function_trivial.yul b/test/libyul/evmCodeTransform/stackReuse/function_trivial.yul new file mode 100644 index 000000000..2c803e85f --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/function_trivial.yul @@ -0,0 +1,12 @@ +{ + function f() { } +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x6 +// JUMP +// JUMPDEST +// JUMPDEST +// JUMP +// JUMPDEST diff --git a/test/libyul/evmCodeTransform/stackReuse/function_with_body_embedded.yul b/test/libyul/evmCodeTransform/stackReuse/function_with_body_embedded.yul new file mode 100644 index 000000000..60f287734 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/function_with_body_embedded.yul @@ -0,0 +1,35 @@ +{ + let b := 3 + function f(a, r) -> t { + // r could be removed right away, but a cannot - this is not implemented, though + let x := a a := 3 t := a + } + b := 7 +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x3 +// PUSH1 0x17 +// JUMP +// JUMPDEST +// PUSH1 0x0 +// DUP2 +// POP +// PUSH1 0x3 +// SWAP2 +// POP +// DUP2 +// SWAP1 +// POP +// JUMPDEST +// SWAP3 +// SWAP2 +// POP +// POP +// JUMP +// JUMPDEST +// PUSH1 0x7 +// SWAP1 +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/functions_multi_return.yul b/test/libyul/evmCodeTransform/stackReuse/functions_multi_return.yul new file mode 100644 index 000000000..d9621ff35 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/functions_multi_return.yul @@ -0,0 +1,61 @@ +{ + function f(a, b) -> t { } + function g() -> r, s { } + let x := f(1, 2) + x := f(3, 4) + let y, z := g() + y, z := g() + let unused := 7 +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x15 +// JUMP +// JUMPDEST +// PUSH1 0x0 +// JUMPDEST +// SWAP3 +// SWAP2 +// POP +// POP +// JUMP +// JUMPDEST +// PUSH1 0x0 +// PUSH1 0x0 +// JUMPDEST +// SWAP1 +// SWAP2 +// JUMP +// JUMPDEST +// PUSH1 0x1F +// PUSH1 0x2 +// PUSH1 0x1 +// PUSH1 0x3 +// JUMP +// JUMPDEST +// PUSH1 0x29 +// PUSH1 0x4 +// PUSH1 0x3 +// PUSH1 0x3 +// JUMP +// JUMPDEST +// SWAP1 +// POP +// POP +// PUSH1 0x32 +// PUSH1 0xC +// JUMP +// JUMPDEST +// PUSH1 0x38 +// PUSH1 0xC +// JUMP +// JUMPDEST +// SWAP2 +// POP +// SWAP2 +// POP +// POP +// POP +// PUSH1 0x7 +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/if.yul b/test/libyul/evmCodeTransform/stackReuse/if.yul new file mode 100644 index 000000000..a407bb54c --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/if.yul @@ -0,0 +1,17 @@ +// z is only removed after the if (after the jumpdest) +{ let z := mload(0) if z { let x := z } let t := 3 } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x0 +// MLOAD +// DUP1 +// ISZERO +// PUSH1 0xA +// JUMPI +// DUP1 +// POP +// JUMPDEST +// POP +// PUSH1 0x3 +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/last_use_in_nested_block.yul b/test/libyul/evmCodeTransform/stackReuse/last_use_in_nested_block.yul new file mode 100644 index 000000000..1fc780d21 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/last_use_in_nested_block.yul @@ -0,0 +1,10 @@ +{ let z := 0 { pop(z) } let x := 1 } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x0 +// DUP1 +// POP +// POP +// PUSH1 0x1 +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/multi_reuse_same_variable_name.yul b/test/libyul/evmCodeTransform/stackReuse/multi_reuse_same_variable_name.yul new file mode 100644 index 000000000..88d42510c --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/multi_reuse_same_variable_name.yul @@ -0,0 +1,23 @@ +{ let z := mload(0) { let x := 1 x := 6 z := x } { let x := 2 z := x x := 4 } } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x0 +// MLOAD +// PUSH1 0x1 +// PUSH1 0x6 +// SWAP1 +// POP +// DUP1 +// SWAP2 +// POP +// POP +// PUSH1 0x2 +// DUP1 +// SWAP2 +// POP +// PUSH1 0x4 +// SWAP1 +// POP +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/multi_reuse_single_slot.yul b/test/libyul/evmCodeTransform/stackReuse/multi_reuse_single_slot.yul new file mode 100644 index 000000000..ba0ad96e7 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/multi_reuse_single_slot.yul @@ -0,0 +1,14 @@ +{ let x := 1 x := 6 let y := 2 y := 4 } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x1 +// PUSH1 0x6 +// SWAP1 +// POP +// POP +// PUSH1 0x2 +// PUSH1 0x4 +// SWAP1 +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/multi_reuse_single_slot_nested.yul b/test/libyul/evmCodeTransform/stackReuse/multi_reuse_single_slot_nested.yul new file mode 100644 index 000000000..b064fcc20 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/multi_reuse_single_slot_nested.yul @@ -0,0 +1,14 @@ +{ let x := 1 x := 6 { let y := 2 y := 4 } } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x1 +// PUSH1 0x6 +// SWAP1 +// POP +// POP +// PUSH1 0x2 +// PUSH1 0x4 +// SWAP1 +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_not_same_scope.yul b/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_not_same_scope.yul new file mode 100644 index 000000000..fe50cb997 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_not_same_scope.yul @@ -0,0 +1,17 @@ +{ + let x := 5 + { + let y := x // y should not reuse the stack slot of x, since x is not in the same scope + sstore(y, y) + } +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x5 +// DUP1 +// DUP1 +// DUP2 +// SSTORE +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_last_used.yul b/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_last_used.yul new file mode 100644 index 000000000..5e4cfd093 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_last_used.yul @@ -0,0 +1,16 @@ +{ + let x := 5 + let y := x // y should reuse the stack slot of x + sstore(y, y) +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x5 +// DUP1 +// SWAP1 +// POP +// DUP1 +// DUP2 +// SSTORE +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_last_used_expr.yul b/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_last_used_expr.yul new file mode 100644 index 000000000..89a82f65c --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_last_used_expr.yul @@ -0,0 +1,18 @@ +{ + let x := 5 + let y := add(x, 2) // y should reuse the stack slot of x + sstore(y, y) +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x5 +// PUSH1 0x2 +// DUP2 +// ADD +// SWAP1 +// POP +// DUP1 +// DUP2 +// SSTORE +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_not_last_used.yul b/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_not_last_used.yul new file mode 100644 index 000000000..7c7cdfd55 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/reuse_on_decl_assign_to_not_last_used.yul @@ -0,0 +1,15 @@ +{ + let x := 5 + let y := x // y should not reuse the stack slot of x, since x is still used below + sstore(y, x) +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x5 +// DUP1 +// DUP2 +// DUP2 +// SSTORE +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/reuse_slots.yul b/test/libyul/evmCodeTransform/stackReuse/reuse_slots.yul new file mode 100644 index 000000000..44b916670 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/reuse_slots.yul @@ -0,0 +1,23 @@ +{ let a, b, c, d let x := 2 let y := 3 mstore(x, a) mstore(y, c) } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x0 +// PUSH1 0x0 +// PUSH1 0x0 +// PUSH1 0x0 +// POP +// PUSH1 0x2 +// SWAP2 +// POP +// PUSH1 0x3 +// DUP4 +// DUP4 +// MSTORE +// DUP2 +// DUP2 +// MSTORE +// POP +// POP +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/reuse_slots_function.yul b/test/libyul/evmCodeTransform/stackReuse/reuse_slots_function.yul new file mode 100644 index 000000000..f5e855a46 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/reuse_slots_function.yul @@ -0,0 +1,40 @@ +{ + function f() -> x, y, z, t {} + let a, b, c, d := f() let x1 := 2 let y1 := 3 mstore(x1, a) mstore(y1, c) +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x12 +// JUMP +// JUMPDEST +// PUSH1 0x0 +// PUSH1 0x0 +// PUSH1 0x0 +// PUSH1 0x0 +// JUMPDEST +// SWAP1 +// SWAP2 +// SWAP3 +// SWAP4 +// JUMP +// JUMPDEST +// PUSH1 0x18 +// PUSH1 0x3 +// JUMP +// JUMPDEST +// POP +// PUSH1 0x2 +// SWAP2 +// POP +// PUSH1 0x3 +// DUP4 +// DUP4 +// MSTORE +// DUP2 +// DUP2 +// MSTORE +// POP +// POP +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/reuse_slots_function_with_gaps.yul b/test/libyul/evmCodeTransform/stackReuse/reuse_slots_function_with_gaps.yul new file mode 100644 index 000000000..36800b204 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/reuse_slots_function_with_gaps.yul @@ -0,0 +1,50 @@ +{ + // Only x3 is actually used, the slots of + // x1 and x2 will be reused right away. + let x1 := 5 let x2 := 6 let x3 := 7 + mstore(x1, x2) + function f() -> x, y, z, t {} + let a, b, c, d := f() mstore(x3, a) mstore(c, d) +} +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x5 +// PUSH1 0x6 +// PUSH1 0x7 +// DUP2 +// DUP4 +// MSTORE +// PUSH1 0x1B +// JUMP +// JUMPDEST +// PUSH1 0x0 +// PUSH1 0x0 +// PUSH1 0x0 +// PUSH1 0x0 +// JUMPDEST +// SWAP1 +// SWAP2 +// SWAP3 +// SWAP4 +// JUMP +// JUMPDEST +// PUSH1 0x21 +// PUSH1 0xC +// JUMP +// JUMPDEST +// SWAP6 +// POP +// SWAP4 +// POP +// POP +// DUP1 +// DUP3 +// MSTORE +// POP +// POP +// DUP2 +// DUP2 +// MSTORE +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/single_var.yul b/test/libyul/evmCodeTransform/stackReuse/single_var.yul new file mode 100644 index 000000000..06957305e --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/single_var.yul @@ -0,0 +1,6 @@ +{ let x } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x0 +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/single_var_assigned.yul b/test/libyul/evmCodeTransform/stackReuse/single_var_assigned.yul new file mode 100644 index 000000000..6bc5f504f --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/single_var_assigned.yul @@ -0,0 +1,6 @@ +{ let x := 1 } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x1 +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/single_var_assigned_plus_code.yul b/test/libyul/evmCodeTransform/stackReuse/single_var_assigned_plus_code.yul new file mode 100644 index 000000000..9fdccea85 --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/single_var_assigned_plus_code.yul @@ -0,0 +1,9 @@ +{ let x := 1 mstore(3, 4) } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x1 +// POP +// PUSH1 0x4 +// PUSH1 0x3 +// MSTORE diff --git a/test/libyul/evmCodeTransform/stackReuse/single_var_assigned_plus_code_and_reused.yul b/test/libyul/evmCodeTransform/stackReuse/single_var_assigned_plus_code_and_reused.yul new file mode 100644 index 000000000..2d66c74db --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/single_var_assigned_plus_code_and_reused.yul @@ -0,0 +1,12 @@ +{ let x := 1 mstore(3, 4) pop(mload(x)) } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x1 +// PUSH1 0x4 +// PUSH1 0x3 +// MSTORE +// DUP1 +// MLOAD +// POP +// POP diff --git a/test/libyul/evmCodeTransform/stackReuse/smoke.yul b/test/libyul/evmCodeTransform/stackReuse/smoke.yul new file mode 100644 index 000000000..df3d9b90e --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/smoke.yul @@ -0,0 +1,4 @@ +{} +// ==== +// stackOptimization: true +// ---- \ No newline at end of file diff --git a/test/libyul/evmCodeTransform/stackReuse/switch.yul b/test/libyul/evmCodeTransform/stackReuse/switch.yul new file mode 100644 index 000000000..480343dcc --- /dev/null +++ b/test/libyul/evmCodeTransform/stackReuse/switch.yul @@ -0,0 +1,26 @@ +{ let z := 0 switch z case 0 { let x := 2 let y := 3 } default { z := 3 } let t := 9 } +// ==== +// stackOptimization: true +// ---- +// PUSH1 0x0 +// DUP1 +// PUSH1 0x0 +// DUP2 +// EQ +// PUSH1 0x11 +// JUMPI +// PUSH1 0x3 +// SWAP2 +// POP +// PUSH1 0x18 +// JUMP +// JUMPDEST +// PUSH1 0x2 +// POP +// PUSH1 0x3 +// POP +// JUMPDEST +// POP +// POP +// PUSH1 0x9 +// POP diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index edd4eb686..95eb3a636 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable(isoltest ../libsolidity/ASTJSONTest.cpp ../libsolidity/SMTCheckerTest.cpp ../libyul/Common.cpp + ../libyul/EVMCodeTransformTest.cpp ../libyul/EwasmTranslationTest.cpp ../libyul/FunctionSideEffects.cpp ../libyul/ObjectCompilerTest.cpp