From ccc8ac8f6f741b234e67680547286ef05d745b4c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Oct 2020 03:18:21 +0200 Subject: [PATCH] Move function arguments and return values. --- libyul/optimiser/StackLimitEvader.cpp | 41 +++- libyul/optimiser/StackToMemoryMover.cpp | 214 +++++++++++++----- libyul/optimiser/StackToMemoryMover.h | 75 +++++- test/libyul/YulOptimizerTestCommon.cpp | 4 + .../fakeStackLimitEvader/function_arg.yul | 5 +- .../fakeStackLimitEvader/return_leave.yul | 48 ++++ .../fakeStackLimitEvader/return_one.yul | 38 ++++ .../stackLimitEvader/function_arg.yul | 5 +- .../stackLimitEvader/stub.yul | 15 +- .../stackLimitEvader/too_many_args_14.yul | 55 +++++ .../stackLimitEvader/too_many_args_15.yul | 61 +++++ .../stackLimitEvader/too_many_args_16.yul | 63 ++++++ .../stackLimitEvader/too_many_returns_15.yul | 48 ++++ .../stackLimitEvader/too_many_returns_16.yul | 49 ++++ 14 files changed, 647 insertions(+), 74 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/fakeStackLimitEvader/return_leave.yul create mode 100644 test/libyul/yulOptimizerTests/fakeStackLimitEvader/return_one.yul create mode 100644 test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_14.yul create mode 100644 test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_15.yul create mode 100644 test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_16.yul create mode 100644 test/libyul/yulOptimizerTests/stackLimitEvader/too_many_returns_15.yul create mode 100644 test/libyul/yulOptimizerTests/stackLimitEvader/too_many_returns_16.yul diff --git a/libyul/optimiser/StackLimitEvader.cpp b/libyul/optimiser/StackLimitEvader.cpp index a7169f08a..d0ee6fe93 100644 --- a/libyul/optimiser/StackLimitEvader.cpp +++ b/libyul/optimiser/StackLimitEvader.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -66,12 +67,37 @@ struct MemoryOffsetAllocator if (unreachableVariables.count(_function)) { yulAssert(!slotAllocations.count(_function), ""); + + if (functionDefinitions.count(_function)) + { + FunctionDefinition const* functionDefinition = functionDefinitions.at(_function); + yulAssert(functionDefinition, ""); + size_t totalArgCount = functionDefinition->returnVariables.size() + functionDefinition->parameters.size(); + size_t slotsNeeded = (totalArgCount > 16) ? totalArgCount - 16 : 0; + + if (slotsNeeded) + for (TypedName const& param: functionDefinition->parameters) + { + slotAllocations[param.name] = requiredSlots++; + if (!--slotsNeeded) + break; + } + if (slotsNeeded) + for (TypedName const& returnVar: functionDefinition->returnVariables) + { + slotAllocations[returnVar.name] = requiredSlots++; + if (!--slotsNeeded) + break; + } + yulAssert(!slotsNeeded, ""); + } + + // Assign slots for all variables that become unreachable in the function body, if the above did not + // assign a slot for them already. for (YulString variable: unreachableVariables.at(_function)) - if (variable.empty()) - { - // TODO: Too many function arguments or return parameters. - } - else + // The empty case is a function with too many arguments or return values, + // which was already handled above. + if (!variable.empty() && !slotAllocations.count(variable)) slotAllocations[variable] = requiredSlots++; } @@ -80,6 +106,7 @@ struct MemoryOffsetAllocator map> const& unreachableVariables; map> const& callGraph; + map const& functionDefinitions; map slotAllocations{}; map slotsRequiredForFunction{}; @@ -128,7 +155,9 @@ void StackLimitEvader::run( if (_unreachableVariables.count(function)) return; - MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls}; + map functionDefinitions = FunctionDefinitionCollector::run(*_object.code); + + MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions}; uint64_t requiredSlots = memoryOffsetAllocator.run(); StackToMemoryMover::run(_context, reservedMemory, memoryOffsetAllocator.slotAllocations, requiredSlots, *_object.code); diff --git a/libyul/optimiser/StackToMemoryMover.cpp b/libyul/optimiser/StackToMemoryMover.cpp index 25eb25cd5..587aa8ef3 100644 --- a/libyul/optimiser/StackToMemoryMover.cpp +++ b/libyul/optimiser/StackToMemoryMover.cpp @@ -15,13 +15,19 @@ along with solidity. If not, see . */ #include +#include #include #include #include #include -#include + +#include +#include +#include +#include +#include using namespace std; using namespace solidity; @@ -77,17 +83,30 @@ void StackToMemoryMover::run( ) { VariableMemoryOffsetTracker memoryOffsetTracker(_reservedMemory, _memorySlots, _numRequiredSlots); - StackToMemoryMover stackToMemoryMover(_context, memoryOffsetTracker); + StackToMemoryMover stackToMemoryMover( + _context, + memoryOffsetTracker, + util::applyMap( + FunctionDefinitionCollector::run(_block), + util::mapTuple([](YulString _name, FunctionDefinition const* _funDef) { + return std::make_pair(_name, _funDef->returnVariables); + }), + map{} + ) + ); stackToMemoryMover(_block); + _block.statements += move(stackToMemoryMover.m_newFunctionDefinitions); } StackToMemoryMover::StackToMemoryMover( OptimiserStepContext& _context, - VariableMemoryOffsetTracker const& _memoryOffsetTracker + VariableMemoryOffsetTracker const& _memoryOffsetTracker, + map _functionReturnVariables ): m_context(_context), m_memoryOffsetTracker(_memoryOffsetTracker), -m_nameDispenser(_context.dispenser) +m_nameDispenser(_context.dispenser), +m_functionReturnVariables(move(_functionReturnVariables)) { auto const* evmDialect = dynamic_cast(&_context.dialect); yulAssert( @@ -98,68 +117,160 @@ m_nameDispenser(_context.dispenser) void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition) { - for (TypedName const& param: _functionDefinition.parameters + _functionDefinition.returnVariables) - if (m_memoryOffsetTracker(param.name)) - { - // TODO: we cannot handle function parameters yet. - return; - } + // It is important to first visit the function body, so that it doesn't replace the memory inits for + // variable arguments we might generate below. ASTModifier::operator()(_functionDefinition); + + vector memoryVariableInits; + + // All function arguments with a memory slot are moved at the beginning of the function body. + for (TypedName const& param: _functionDefinition.parameters) + if (auto slot = m_memoryOffsetTracker(param.name)) + memoryVariableInits += generateMemoryStore( + m_context.dialect, + param.location, + *slot, + Identifier{param.location, param.name} + ); + + // All memory return variables have to be initialized to zero in memory. + for (TypedName const& returnVariable: _functionDefinition.returnVariables) + if (auto slot = m_memoryOffsetTracker(returnVariable.name)) + memoryVariableInits += generateMemoryStore( + m_context.dialect, + returnVariable.location, + *slot, + Literal{returnVariable.location, LiteralKind::Number, "0"_yulstring, {}} + ); + + // Special case of a function with a single return argument that needs to move to memory. + if (_functionDefinition.returnVariables.size() == 1 && m_memoryOffsetTracker(_functionDefinition.returnVariables.front().name)) + { + TypedNameList stackArguments = _functionDefinition.parameters | ranges::views::filter([&](TypedName const& _arg){ + return !m_memoryOffsetTracker(_arg.name); + }) | ranges::to; + // Generate new function without return argument and with only the non-moved arguments. + YulString newFunctionName = m_context.dispenser.newName(_functionDefinition.name); + m_newFunctionDefinitions.emplace_back(FunctionDefinition{ + _functionDefinition.location, + newFunctionName, + stackArguments, + {}, + move(_functionDefinition.body) + }); + // Replace original function by a call to the new function and an assignment to the return variable from memory. + _functionDefinition.body = Block{_functionDefinition.location, move(memoryVariableInits)}; + _functionDefinition.body.statements.emplace_back(ExpressionStatement{ + _functionDefinition.location, + FunctionCall{ + _functionDefinition.location, + Identifier{_functionDefinition.location, newFunctionName}, + stackArguments | ranges::views::transform([&](TypedName const& _arg) { + return Expression{Identifier{ + _functionDefinition.location, + _arg.name + }}; + }) | ranges::to> + } + }); + _functionDefinition.body.statements.emplace_back(Assignment{ + _functionDefinition.location, + {Identifier{_functionDefinition.location, _functionDefinition.returnVariables.front().name}}, + make_unique(generateMemoryLoad( + m_context.dialect, + _functionDefinition.location, + *m_memoryOffsetTracker(_functionDefinition.returnVariables.front().name) + )) + }); + return; + } + + if (!memoryVariableInits.empty()) + { + memoryVariableInits += move(_functionDefinition.body.statements); + _functionDefinition.body.statements = move(memoryVariableInits); + } + + _functionDefinition.returnVariables = _functionDefinition.returnVariables | ranges::views::filter([&](TypedName const& _name){ + return !m_memoryOffsetTracker(_name.name); + }) | ranges::to; } void StackToMemoryMover::operator()(Block& _block) { using OptionalStatements = std::optional>; - auto rewriteAssignmentOrVariableDeclaration = [&]( + + auto rewriteAssignmentOrVariableDeclarationLeftHandSide = [this]( auto& _stmt, - auto const& _variables + auto& _lhsVars ) -> OptionalStatements { using StatementType = decay_t; - if (_stmt.value) - visit(*_stmt.value); - bool leftHandSideNeedsMoving = util::contains_if(_variables, [&](auto const& var) { - return m_memoryOffsetTracker(var.name); - }); - if (!leftHandSideNeedsMoving) + + langutil::SourceLocation loc = _stmt.location; + if (_lhsVars.size() == 1) + { + optional offset = m_memoryOffsetTracker(_lhsVars.front().name); + if (offset) + return generateMemoryStore( + m_context.dialect, + loc, + *offset, + _stmt.value ? *std::move(_stmt.value) : Literal{loc, LiteralKind::Number, "0"_yulstring, {}} + ); + else + return {}; + } + FunctionCall const* functionCall = get_if(_stmt.value.get()); + yulAssert(functionCall, ""); + auto rhsSlots = m_functionReturnVariables.at(functionCall->functionName.name) | + ranges::views::transform([&](auto const& _var) { + return m_memoryOffsetTracker(_var.name); + }) | ranges::to>>; + + // Nothing to do, if the right-hand-side remains entirely on the stack and + // none of the variables in the left-hand-side are moved. + if ( + ranges::none_of(rhsSlots, [](optional const& _slot) { return _slot.has_value(); }) && + !util::contains_if(_lhsVars, [&](auto const& var) { return m_memoryOffsetTracker(var.name); }) + ) return {}; - langutil::SourceLocation loc = _stmt.location; - - if (_variables.size() == 1) - { - optional offset = m_memoryOffsetTracker(_variables.front().name); - yulAssert(offset, ""); - return generateMemoryStore( - m_context.dialect, - loc, - *offset, - _stmt.value ? *std::move(_stmt.value) : Literal{loc, LiteralKind::Number, "0"_yulstring, {}} - ); - } - - VariableDeclaration tempDecl{loc, {}, std::move(_stmt.value)}; vector memoryAssignments; vector variableAssignments; - for (auto& var: _variables) - { - YulString tempVarName = m_nameDispenser.newName(var.name); - tempDecl.variables.emplace_back(TypedName{var.location, tempVarName, {}}); + VariableDeclaration tempDecl{loc, {}, std::move(_stmt.value)}; - if (optional offset = m_memoryOffsetTracker(var.name)) + yulAssert(rhsSlots.size() == _lhsVars.size(), ""); + for (auto&& [lhsVar, rhsSlot]: ranges::views::zip(_lhsVars, rhsSlots)) + { + unique_ptr rhs; + if (rhsSlot) + rhs = make_unique(generateMemoryLoad(m_context.dialect, loc, *rhsSlot)); + else + { + YulString tempVarName = m_nameDispenser.newName(lhsVar.name); + tempDecl.variables.emplace_back(TypedName{lhsVar.location, tempVarName, {}}); + rhs = make_unique(Identifier{loc, tempVarName}); + } + + if (optional offset = m_memoryOffsetTracker(lhsVar.name)) memoryAssignments += generateMemoryStore( m_context.dialect, loc, *offset, - Identifier{loc, tempVarName} + move(*rhs) ); else variableAssignments.emplace_back(StatementType{ - loc, {move(var)}, - make_unique(Identifier{loc, tempVarName}) + loc, { std::move(lhsVar) }, + move(rhs) }); } + std::vector result; - result.emplace_back(std::move(tempDecl)); + if (tempDecl.variables.empty()) + result.emplace_back(ExpressionStatement{loc, *move(tempDecl.value)}); + else + result.emplace_back(std::move(tempDecl)); std::reverse(memoryAssignments.begin(), memoryAssignments.end()); result += std::move(memoryAssignments); std::reverse(variableAssignments.begin(), variableAssignments.end()); @@ -169,19 +280,14 @@ void StackToMemoryMover::operator()(Block& _block) util::iterateReplacing( _block.statements, - [&](Statement& _statement) + [&](Statement& _statement) -> OptionalStatements { - return std::visit(util::GenericVisitor{ - [&](Assignment& _assignment) -> OptionalStatements - { - return rewriteAssignmentOrVariableDeclaration(_assignment, _assignment.variableNames); - }, - [&](VariableDeclaration& _varDecl) -> OptionalStatements - { - return rewriteAssignmentOrVariableDeclaration(_varDecl, _varDecl.variables); - }, - [&](auto& _stmt) -> OptionalStatements { (*this)(_stmt); return {}; } - }, _statement); + visit(_statement); + if (auto* assignment = std::get_if(&_statement)) + return rewriteAssignmentOrVariableDeclarationLeftHandSide(*assignment, assignment->variableNames); + else if (auto* varDecl = std::get_if(&_statement)) + return rewriteAssignmentOrVariableDeclarationLeftHandSide(*varDecl, varDecl->variables); + return {}; } ); } diff --git a/libyul/optimiser/StackToMemoryMover.h b/libyul/optimiser/StackToMemoryMover.h index 2ec831946..53fedb34d 100644 --- a/libyul/optimiser/StackToMemoryMover.h +++ b/libyul/optimiser/StackToMemoryMover.h @@ -22,6 +22,9 @@ #include #include +#include + +#include #include namespace solidity::yul @@ -52,6 +55,16 @@ namespace solidity::yul * let c := _3 * let a := _1 * + * In case f has return parameters that are moved to memory, fewer variables are returned and the return values read + * from memory instead. Assume the third return parameter of f (i.e. c) has to be moved to memory: + * let a, b, c, d := f() + * then it is replaced by + * let _1, _2, _4 := f() + * mstore(, _4) + * mstore(, _2) + * let c := mload() + * let a := _1 + * * Assignments to single variables are replaced by mstore's: * If a is in the map, replace * a := expr @@ -70,8 +83,44 @@ namespace solidity::yul * * Replace all references to a variable ``a`` in the map by ``mload()``. * - * If a visited function has arguments or return parameters that are contained in the map, - * the entire function is skipped (no local variables in the function will be moved at all). + * Function arguments are moved at the beginning of a function body: + * If a1 is in the map, replace + * function f(a1, a2, ..., a17) + * { + * ... + * sstore(a1, a17) + * } + * by + * function f(a1, a2, ..., a17) + * { + * mstore(, a1) + * ... + * sstore(mload(, a17) + * } + * This relies on the code transform popping arguments that are no longer used, if they are on the stack top. + * + * Functions with only one return argument that has to be moved are encapsulated in a wrapper function as follows: + * Suppose b and r need to be moved in: + * function f(a, b) -> r + * { + * ...body of f... + * r := b + * ...body of f continued... + * } + * then replace by: + * function f(a, b) -> r + * { + * mstore(, b) + * mstore(, 0) + * f_1(a) + * r := mload() + * } + * function f_1(a) + * { + * ...body of f... + * mstore(, mload()) + * ...body of f continued... + * } * * Prerequisite: Disambiguator, ForLoopInitRewriter, FunctionHoister. */ @@ -98,6 +147,7 @@ public: void operator()(FunctionDefinition& _functionDefinition) override; void operator()(Block& _block) override; + using ASTModifier::visit; void visit(Expression& _expression) override; private: class VariableMemoryOffsetTracker @@ -118,15 +168,34 @@ private: std::map const& m_memorySlots; uint64_t m_numRequiredSlots = 0; }; + struct FunctionMoveInfo + { + std::vector> returnVariableSlots; + }; StackToMemoryMover( OptimiserStepContext& _context, - VariableMemoryOffsetTracker const& _memoryOffsetTracker + VariableMemoryOffsetTracker const& _memoryOffsetTracker, + std::map> _functionReturnVariables + ); + + template + std::optional> rewriteAssignmentOrVariableDeclarationToFunctionCalls( + StatementType& _stmt, + std::vector& _variables + ); + + template + std::optional> rewriteAssignmentOrVariableDeclarationLeftHandSide( + StatementType& _stmt, + std::vector& _variables ); OptimiserStepContext& m_context; VariableMemoryOffsetTracker const& m_memoryOffsetTracker; NameDispenser& m_nameDispenser; + std::map> m_functionReturnVariables; + std::list m_newFunctionDefinitions; }; } diff --git a/test/libyul/YulOptimizerTestCommon.cpp b/test/libyul/YulOptimizerTestCommon.cpp index 2f8334eef..e98beb55b 100644 --- a/test/libyul/YulOptimizerTestCommon.cpp +++ b/test/libyul/YulOptimizerTestCommon.cpp @@ -338,6 +338,10 @@ YulOptimizerTestCommon::YulOptimizerTestCommon( { YulString originalFunctionName = m_currentFunction; m_currentFunction = _function.name; + for (TypedName const& _argument: _function.parameters) + visitVariableName(_argument.name); + for (TypedName const& _argument: _function.returnVariables) + visitVariableName(_argument.name); ASTWalker::operator()(_function); m_currentFunction = originalFunctionName; } diff --git a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/function_arg.yul b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/function_arg.yul index 6ce2e34b4..0e5aa9e13 100644 --- a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/function_arg.yul +++ b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/function_arg.yul @@ -15,6 +15,9 @@ // mstore(0x00, 0) // sstore(0, mload(0x00)) // function h($hx) -> y -// { y := $hx } +// { +// mstore(0x20, $hx) +// y := mload(0x20) +// } // sstore(1, h(32)) // } diff --git a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/return_leave.yul b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/return_leave.yul new file mode 100644 index 000000000..3b43bd534 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/return_leave.yul @@ -0,0 +1,48 @@ +{ + { + mstore(0x40, memoryguard(0x80)) + let a1, a2 := f() + sstore(a1, a2) + } + function g(x) -> a, b { a := x b := 2 } + function f() -> $b1, $b2 { + if calldataload(0) { + $b1, $b2 := g(1) + leave + } + $b1, $b2 := g(2) + } + +} +// ---- +// step: fakeStackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0xc0)) +// f() +// let a2 := mload(0x80) +// let a1 := mload(0xa0) +// sstore(a1, a2) +// } +// function g(x) -> a, b +// { +// a := x +// b := 2 +// } +// function f() +// { +// mstore(0xa0, 0) +// mstore(0x80, 0) +// if calldataload(0) +// { +// let $b1_1, $b2_2 := g(1) +// mstore(0x80, $b2_2) +// mstore(0xa0, $b1_1) +// leave +// } +// let $b1_3, $b2_4 := g(2) +// mstore(0x80, $b2_4) +// mstore(0xa0, $b1_3) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/return_one.yul b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/return_one.yul new file mode 100644 index 000000000..50a9d6486 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/return_one.yul @@ -0,0 +1,38 @@ +{ + { + mstore(0x40, memoryguard(0x80)) + sstore(0, f()) + } + function f() -> $b1 { + if calldataload(0) { + $b1 := 0 + leave + } + $b1 := 1 + } + +} +// ---- +// step: fakeStackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0xa0)) +// sstore(0, f()) +// } +// function f() -> $b1 +// { +// mstore(0x80, 0) +// f_1() +// $b1 := mload(0x80) +// } +// function f_1() +// { +// if calldataload(0) +// { +// mstore(0x80, 0) +// leave +// } +// mstore(0x80, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/function_arg.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/function_arg.yul index 7bc4d2e80..27f070515 100644 --- a/test/libyul/yulOptimizerTests/stackLimitEvader/function_arg.yul +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/function_arg.yul @@ -50,6 +50,7 @@ // } // function f(a1) -> v // { +// mstore(0x80, a1) // let a2 := calldataload(mul(2, 4)) // let a3 := calldataload(mul(3, 4)) // let a4 := calldataload(mul(4, 4)) @@ -66,7 +67,7 @@ // let a15 := calldataload(mul(15, 4)) // let a16 := calldataload(mul(16, 4)) // let a17 := calldataload(mul(17, 4)) -// sstore(0, a1) +// sstore(0, mload(0x80)) // sstore(mul(17, 4), a17) // sstore(mul(16, 4), a16) // sstore(mul(15, 4), a15) @@ -83,6 +84,6 @@ // sstore(mul(4, 4), a4) // sstore(mul(3, 4), a3) // sstore(mul(2, 4), a2) -// sstore(mul(1, 4), a1) +// sstore(mul(1, 4), mload(0x80)) // } // } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/stub.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/stub.yul index ce12229b6..9f5001c87 100644 --- a/test/libyul/yulOptimizerTests/stackLimitEvader/stub.yul +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/stub.yul @@ -1,13 +1,12 @@ { { mstore(0x40, memoryguard(128)) - sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16), f()) + sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17), f()) } - function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16) -> v { - // Should be, but cannot yet be escalated. - v := b16 + function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17) -> v { + v := add(b16, b17) } - function f() -> v{ + function f() -> v { let a1 := calldataload(mul(1,4)) let a2 := calldataload(mul(2,4)) let a3 := calldataload(mul(3,4)) @@ -52,10 +51,10 @@ // { // { // mstore(0x40, memoryguard(0xa0)) -// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), f()) +// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), f()) // } -// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16) -> v -// { v := b16 } +// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17) -> v +// { v := add(b16, b17) } // function f() -> v_1 // { // mstore(0x80, calldataload(mul(1, 4))) diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_14.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_14.yul new file mode 100644 index 000000000..74c74e36f --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_14.yul @@ -0,0 +1,55 @@ +{ + { + mstore(0x40, memoryguard(128)) + sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29), 0) + } + function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29) -> v { + sstore(0, b14) + sstore(1, b15) + sstore(2, b16) + sstore(3, b17) + sstore(4, b18) + sstore(5, b19) + sstore(6, b29) + v := add(b1,b29) + } + +} +// ---- +// step: stackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0x02c0)) +// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29), 0) +// } +// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29) -> v +// { +// mstore(0x02a0, b1) +// mstore(0x0280, b2) +// mstore(0x0260, b3) +// mstore(0x0240, b4) +// mstore(0x0220, b5) +// mstore(0x0200, b6) +// mstore(0x01e0, b7) +// mstore(0x01c0, b8) +// mstore(0x01a0, b9) +// mstore(0x0180, b10) +// mstore(0x0160, b11) +// mstore(0x0140, b12) +// mstore(0x0120, b13) +// mstore(0x0100, b14) +// mstore(0xc0, b17) +// mstore(0xa0, b18) +// mstore(0x80, b19) +// mstore(0xe0, b29) +// sstore(0, mload(0x0100)) +// sstore(1, b15) +// sstore(2, b16) +// sstore(3, mload(0xc0)) +// sstore(4, mload(0xa0)) +// sstore(5, mload(0x80)) +// sstore(6, mload(0xe0)) +// v := add(mload(0x02a0), mload(0xe0)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_15.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_15.yul new file mode 100644 index 000000000..8df48f376 --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_15.yul @@ -0,0 +1,61 @@ +{ + { + mstore(0x40, memoryguard(128)) + sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29, 30), 0) + } + function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30) -> v { + sstore(0, b14) + sstore(1, b15) + sstore(2, b16) + sstore(3, b17) + sstore(4, b18) + sstore(5, b19) + sstore(6, b29) + sstore(7, b30) + v := b30 + sstore(b1, b30) + } + +} +// ---- +// step: stackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0x0300)) +// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30), 0) +// } +// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30) -> v +// { +// mstore(0x02e0, b1) +// mstore(0x02c0, b2) +// mstore(0x02a0, b3) +// mstore(0x0280, b4) +// mstore(0x0260, b5) +// mstore(0x0240, b6) +// mstore(0x0220, b7) +// mstore(0x0200, b8) +// mstore(0x01e0, b9) +// mstore(0x01c0, b10) +// mstore(0x01a0, b11) +// mstore(0x0180, b12) +// mstore(0x0160, b13) +// mstore(0x0140, b14) +// mstore(0x0120, b15) +// mstore(0xc0, b17) +// mstore(0xa0, b18) +// mstore(0x80, b19) +// mstore(0xe0, b29) +// mstore(0x0100, b30) +// sstore(0, mload(0x0140)) +// sstore(1, mload(0x0120)) +// sstore(2, b16) +// sstore(3, mload(0xc0)) +// sstore(4, mload(0xa0)) +// sstore(5, mload(0x80)) +// sstore(6, mload(0xe0)) +// sstore(7, mload(0x0100)) +// v := mload(0x0100) +// sstore(mload(0x02e0), mload(0x0100)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_16.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_16.yul new file mode 100644 index 000000000..42e1aabc2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_16.yul @@ -0,0 +1,63 @@ +{ + { + mstore(0x40, memoryguard(128)) + sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31), 0) + } + function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31) -> v { + sstore(0, b14) + sstore(1, b15) + sstore(2, b16) + sstore(3, b17) + sstore(4, b18) + sstore(5, b19) + sstore(6, b29) + sstore(7, b30) + sstore(8, b31) + v := add(b1,b31) + } + +} +// ---- +// step: stackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0x0340)) +// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), 0) +// } +// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31) -> v +// { +// mstore(0x0320, b1) +// mstore(0x0300, b2) +// mstore(0x02e0, b3) +// mstore(0x02c0, b4) +// mstore(0x02a0, b5) +// mstore(0x0280, b6) +// mstore(0x0260, b7) +// mstore(0x0240, b8) +// mstore(0x0220, b9) +// mstore(0x0200, b10) +// mstore(0x01e0, b11) +// mstore(0x01c0, b12) +// mstore(0x01a0, b13) +// mstore(0x0180, b14) +// mstore(0x0160, b15) +// mstore(0x0140, b16) +// mstore(0xc0, b17) +// mstore(0xa0, b18) +// mstore(0x80, b19) +// mstore(0xe0, b29) +// mstore(0x0120, b30) +// mstore(0x0100, b31) +// sstore(0, mload(0x0180)) +// sstore(1, mload(0x0160)) +// sstore(2, mload(0x0140)) +// sstore(3, mload(0xc0)) +// sstore(4, mload(0xa0)) +// sstore(5, mload(0x80)) +// sstore(6, mload(0xe0)) +// sstore(7, mload(0x0120)) +// sstore(8, mload(0x0100)) +// v := add(mload(0x0320), mload(0x0100)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_returns_15.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_returns_15.yul new file mode 100644 index 000000000..b9d1a5571 --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_returns_15.yul @@ -0,0 +1,48 @@ +{ + { + mstore(0x40, memoryguard(128)) + let a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30) + sstore(0, 1) + a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30) + } + function g(b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30) -> b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 { + b1 := 1 + b2 := 2 + b15 := 15 + sstore(b16, b30) + } + +} +// ---- +// step: stackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0x0260)) +// let a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30) +// sstore(0, 1) +// a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30) +// } +// function g(b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30) -> b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 +// { +// mstore(0x0240, b16) +// mstore(0x0220, b17) +// mstore(0x0200, b18) +// mstore(0x01e0, b19) +// mstore(0x01c0, b20) +// mstore(0x01a0, b21) +// mstore(0x0180, b22) +// mstore(0x0160, b23) +// mstore(0x0140, b24) +// mstore(0x0120, b25) +// mstore(0x0100, b26) +// mstore(0xe0, b27) +// mstore(0xc0, b28) +// mstore(0xa0, b29) +// mstore(0x80, b30) +// b1 := 1 +// b2 := 2 +// b15 := 15 +// sstore(mload(0x0240), mload(0x80)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_returns_16.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_returns_16.yul new file mode 100644 index 000000000..40553083f --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_returns_16.yul @@ -0,0 +1,49 @@ +{ + { + mstore(0x40, memoryguard(128)) + let a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31) + sstore(0, 1) + a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31) + } + function g(b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31) -> b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 { + b1 := 1 + b2 := 2 + b15 := 15 + sstore(b16, b31) + } + +} +// ---- +// step: stackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0x0280)) +// let a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31) +// sstore(0, 1) +// a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 := g(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31) +// } +// function g(b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31) -> b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 +// { +// mstore(0x0260, b16) +// mstore(0x0240, b17) +// mstore(0x0220, b18) +// mstore(0x0200, b19) +// mstore(0x01e0, b20) +// mstore(0x01c0, b21) +// mstore(0x01a0, b22) +// mstore(0x0180, b23) +// mstore(0x0160, b24) +// mstore(0x0140, b25) +// mstore(0x0120, b26) +// mstore(0x0100, b27) +// mstore(0xe0, b28) +// mstore(0xc0, b29) +// mstore(0xa0, b30) +// mstore(0x80, b31) +// b1 := 1 +// b2 := 2 +// b15 := 15 +// sstore(mload(0x0260), mload(0x80)) +// } +// }