diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 9a5886db6..d604167b3 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -528,7 +528,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) ); stackError(std::move(error), static_cast(_function.returnVariables.size()) + 1); } - else + else if (m_allowStackOpt) { // This vector stores true for stack slots containing return labels or return values, // false otherwise. @@ -573,6 +573,55 @@ void CodeTransform::operator()(FunctionDefinition const& _function) // So we need an additional swap to completely order the return values and get the return label to the top. m_assembly.appendInstruction(evmasm::swapInstruction(static_cast(_function.returnVariables.size()))); } + else + { + // The stack layout here is: + // + // But we would like it to be: + // + // So we have to append some SWAP and POP instructions. + + // This vector holds the desired target positions of all stack slots and is + // modified parallel to the actual stack. + vector stackLayout(static_cast(m_assembly.stackHeight()), -1); + stackLayout[0] = static_cast(_function.returnVariables.size()); // Move return label to the top + for (auto&& [n, returnVariable]: ranges::views::enumerate(_function.returnVariables)) + stackLayout.at(m_context->variableStackHeights.at( + &std::get(virtualFunctionScope->identifiers.at(returnVariable.name)) + )) = static_cast(n); + + if (stackLayout.size() > 17) + { + StackTooDeepError error( + _function.name, + YulString{}, + static_cast(stackLayout.size()) - 17, + "The function " + + _function.name.str() + + " has " + + to_string(stackLayout.size() - 17) + + " parameters or return variables too many to fit the stack size." + ); + stackError(std::move(error), m_assembly.stackHeight() - static_cast(_function.parameters.size())); + } + else + { + while (!stackLayout.empty() && stackLayout.back() != static_cast(stackLayout.size() - 1)) + if (stackLayout.back() < 0) + { + m_assembly.appendInstruction(evmasm::Instruction::POP); + stackLayout.pop_back(); + } + else + { + m_assembly.appendInstruction(evmasm::swapInstruction(static_cast(stackLayout.size()) - static_cast(stackLayout.back()) - 1u)); + swap(stackLayout[static_cast(stackLayout.back())], stackLayout.back()); + } + for (size_t i = 0; i < stackLayout.size(); ++i) + yulAssert(i == static_cast(stackLayout[i]), "Error reshuffling stack."); + } + } + m_assembly.appendJump( stackHeightBefore - static_cast(_function.returnVariables.size()), AbstractAssembly::JumpType::OutOfFunction @@ -708,6 +757,15 @@ void CodeTransform::setupReturnVariablesAndFunctionExit() return; } + if (!m_allowStackOpt) + { + for (TypedName const& var: m_delayedReturnVariables) + (*this)(VariableDeclaration{var.location, {var}, {}}); + m_functionExitStackHeight = m_assembly.stackHeight(); + m_delayedReturnVariables.clear(); + return; + } + int height = m_assembly.stackHeight(); // Determine the set of stack slots available for putting in return variables. diff --git a/test/libsolidity/gasTests/abiv2.sol b/test/libsolidity/gasTests/abiv2.sol index e09f06884..17e42cf1b 100644 --- a/test/libsolidity/gasTests/abiv2.sol +++ b/test/libsolidity/gasTests/abiv2.sol @@ -14,9 +14,9 @@ contract C { } // ---- // creation: -// codeDepositCost: 1167800 +// codeDepositCost: 1170600 // executionCost: 1214 -// totalCost: 1169014 +// totalCost: 1171814 // external: // a(): 1130 // b(uint256): infinite