diff --git a/Changelog.md b/Changelog.md index c5625d9db..75d1cf773 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,7 @@ Bugfixes: * Commandline Interface: It is no longer possible to specify both ``--optimize-yul`` and ``--no-optimize-yul`` at the same time. * SMTChecker: Fix encoding of side-effects inside ``if`` and ``ternary conditional``statements in the BMC engine. * SMTChecker: Fix false negative when a verification target can be violated only by trusted external call from another public function. + * Yul Optimizer: Ensure that the assignment of memory slots for variables moved to memory does not depend on AST IDs that may depend on whether additional files are included during compilation. * Yul Optimizer: Fix optimized IR being unnecessarily passed through the Yul optimizer again before bytecode generation. AST Changes: diff --git a/libyul/CompilabilityChecker.cpp b/libyul/CompilabilityChecker.cpp index acb48cfa5..b9b51731b 100644 --- a/libyul/CompilabilityChecker.cpp +++ b/libyul/CompilabilityChecker.cpp @@ -63,7 +63,9 @@ CompilabilityChecker::CompilabilityChecker( for (StackTooDeepError const& error: transform.stackErrors()) { - unreachableVariables[error.functionName].emplace(error.variable); + auto& unreachables = unreachableVariables[error.functionName]; + if (!util::contains(unreachables, error.variable)) + unreachables.emplace_back(error.variable); int& deficit = stackDeficit[error.functionName]; deficit = std::max(error.depth, deficit); } diff --git a/libyul/CompilabilityChecker.h b/libyul/CompilabilityChecker.h index 307d719e4..c046df94f 100644 --- a/libyul/CompilabilityChecker.h +++ b/libyul/CompilabilityChecker.h @@ -45,7 +45,7 @@ namespace solidity::yul struct CompilabilityChecker { CompilabilityChecker(Dialect const& _dialect, Object const& _object, bool _optimizeStackAllocation); - std::map> unreachableVariables; + std::map> unreachableVariables; std::map stackDeficit; }; diff --git a/libyul/optimiser/CallGraphGenerator.cpp b/libyul/optimiser/CallGraphGenerator.cpp index 5aeff50be..66f740391 100644 --- a/libyul/optimiser/CallGraphGenerator.cpp +++ b/libyul/optimiser/CallGraphGenerator.cpp @@ -22,6 +22,7 @@ #include #include +#include #include using namespace std; @@ -79,7 +80,9 @@ CallGraph CallGraphGenerator::callGraph(Block const& _ast) void CallGraphGenerator::operator()(FunctionCall const& _functionCall) { - m_callGraph.functionCalls[m_currentFunction].insert(_functionCall.functionName.name); + auto& functionCalls = m_callGraph.functionCalls[m_currentFunction]; + if (!util::contains(functionCalls, _functionCall.functionName.name)) + functionCalls.emplace_back(_functionCall.functionName.name); ASTWalker::operator()(_functionCall); } diff --git a/libyul/optimiser/CallGraphGenerator.h b/libyul/optimiser/CallGraphGenerator.h index a581df018..ad19878b6 100644 --- a/libyul/optimiser/CallGraphGenerator.h +++ b/libyul/optimiser/CallGraphGenerator.h @@ -32,7 +32,7 @@ namespace solidity::yul struct CallGraph { - std::map> functionCalls; + std::map> functionCalls; std::set functionsWithLoops; /// @returns the set of functions contained in cycles in the call graph, i.e. /// functions that are part of a (mutual) recursion. diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 9ffe871fa..0482e20d6 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -37,6 +37,8 @@ #include #include +#include + using namespace std; using namespace solidity; using namespace solidity::yul; @@ -156,7 +158,8 @@ map FullInliner::callDepths() const } for (auto& call: cg.functionCalls) - call.second -= removed; + for (YulString toBeRemoved: removed) + ranges::actions::remove(call.second, toBeRemoved); currentDepth++; diff --git a/libyul/optimiser/StackLimitEvader.cpp b/libyul/optimiser/StackLimitEvader.cpp index b4b9d5469..4c84f5155 100644 --- a/libyul/optimiser/StackLimitEvader.cpp +++ b/libyul/optimiser/StackLimitEvader.cpp @@ -97,9 +97,9 @@ struct MemoryOffsetAllocator /// Maps function names to the set of unreachable variables in that function. /// An empty variable name means that the function has too many arguments or return variables. - map> const& unreachableVariables; + map> const& unreachableVariables; /// The graph of immediate function calls of all functions. - map> const& callGraph; + map> const& callGraph; /// Maps the name of each user-defined function to its definition. map const& functionDefinitions; @@ -149,18 +149,23 @@ void StackLimitEvader::run( map> const& _stackTooDeepErrors ) { - map> unreachableVariables; + map> unreachableVariables; for (auto&& [function, stackTooDeepErrors]: _stackTooDeepErrors) + { + auto& unreachables = unreachableVariables[function]; // TODO: choose wisely. for (auto const& stackTooDeepError: stackTooDeepErrors) - unreachableVariables[function] += stackTooDeepError.variableChoices | ranges::views::take(stackTooDeepError.deficit) | ranges::to>; + for (auto variable: stackTooDeepError.variableChoices | ranges::views::take(stackTooDeepError.deficit)) + if (!util::contains(unreachables, variable)) + unreachables.emplace_back(variable); + } run(_context, _object, unreachableVariables); } void StackLimitEvader::run( OptimiserStepContext& _context, Object& _object, - map> const& _unreachableVariables + map> const& _unreachableVariables ) { yulAssert(_object.code, ""); diff --git a/libyul/optimiser/StackLimitEvader.h b/libyul/optimiser/StackLimitEvader.h index ae2d31c37..025553200 100644 --- a/libyul/optimiser/StackLimitEvader.h +++ b/libyul/optimiser/StackLimitEvader.h @@ -60,7 +60,7 @@ public: static void run( OptimiserStepContext& _context, Object& _object, - std::map> const& _unreachableVariables + std::map> const& _unreachableVariables ); /// @a _stackTooDeepErrors can be determined by the StackLayoutGenerator. /// Can only be run on the EVM dialect with objects. diff --git a/test/libyul/YulOptimizerTestCommon.cpp b/test/libyul/YulOptimizerTestCommon.cpp index 7e5f32430..48c7ea403 100644 --- a/test/libyul/YulOptimizerTestCommon.cpp +++ b/test/libyul/YulOptimizerTestCommon.cpp @@ -349,7 +349,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon( // Mark all variables with a name starting with "$" for escalation to memory. struct FakeUnreachableGenerator: ASTWalker { - map> fakeUnreachables; + map> fakeUnreachables; using ASTWalker::operator(); void operator()(FunctionDefinition const& _function) override { @@ -365,7 +365,8 @@ YulOptimizerTestCommon::YulOptimizerTestCommon( void visitVariableName(YulString _var) { if (!_var.empty() && _var.str().front() == '$') - fakeUnreachables[m_currentFunction].insert(_var); + if (!util::contains(fakeUnreachables[m_currentFunction], _var)) + fakeUnreachables[m_currentFunction].emplace_back(_var); } void operator()(VariableDeclaration const& _varDecl) override { diff --git a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/connected.yul b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/connected.yul index 82ab48a51..99e92c183 100644 --- a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/connected.yul +++ b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/connected.yul @@ -36,13 +36,13 @@ // } // function f() -> x // { -// mstore(0x20, 0) -// mstore(0x20, 42) +// mstore(0x40, 0) +// mstore(0x40, 42) // let $x3_4, $x4_5 := g() // mstore(0x00, $x4_5) -// mstore(0x40, $x3_4) -// x := mul(add(mload(0x20), mload(0x40)), h(mload(0x00))) -// sstore(mload(0x40), mload(0x00)) +// mstore(0x20, $x3_4) +// x := mul(add(mload(0x40), mload(0x20)), h(mload(0x00))) +// sstore(mload(0x20), mload(0x00)) // } // function h(v) -> a_1 // { diff --git a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/stub.yul b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/stub.yul index d2fcb116e..e86619e46 100644 --- a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/stub.yul +++ b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/stub.yul @@ -30,10 +30,10 @@ // mstore(0x40, memoryguard(0x80)) // function f() // { -// mstore(0x40, 0) -// mstore(0x60, 42) -// sstore(mload(0x40), mload(0x60)) -// mstore(0x40, 21) +// mstore(0x60, 0) +// mstore(0x40, 42) +// sstore(mload(0x60), mload(0x40)) +// mstore(0x60, 21) // } // function g(gx) // { @@ -60,9 +60,9 @@ // { // let $hx_9, $hy_10, $hz_11, $hw_12 := tuple4() // mstore(0x00, $hw_12) -// mstore(0x60, $hz_11) +// mstore(0x20, $hz_11) // mstore(0x40, $hy_10) -// mstore(0x20, $hx_9) +// mstore(0x60, $hx_9) // { // let hx_13, $hy_14, hz_15, $hw_16 := tuple4() // mstore(0x00, $hw_16) @@ -73,7 +73,7 @@ // { // let $hx_17, $hy_18, hz_19, hw_20 := tuple4() // mstore(0x40, $hy_18) -// mstore(0x20, $hx_17) +// mstore(0x60, $hx_17) // hw := hw_20 // hz := hz_19 // } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_14.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_14.yul index 74c74e36f..02614c492 100644 --- a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_14.yul +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_14.yul @@ -39,17 +39,17 @@ // mstore(0x0140, b12) // mstore(0x0120, b13) // mstore(0x0100, b14) -// mstore(0xc0, b17) -// mstore(0xa0, b18) -// mstore(0x80, b19) -// mstore(0xe0, b29) +// mstore(0xe0, b17) +// mstore(0xc0, b18) +// mstore(0xa0, b19) +// mstore(0x80, 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)) +// sstore(3, mload(0xe0)) +// sstore(4, mload(0xc0)) +// sstore(5, mload(0xa0)) +// sstore(6, mload(0x80)) +// v := add(mload(0x02a0), mload(0x80)) // } // } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_15.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_15.yul index 8df48f376..9ce9be70a 100644 --- a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_15.yul +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_15.yul @@ -42,20 +42,20 @@ // mstore(0x0160, b13) // mstore(0x0140, b14) // mstore(0x0120, b15) -// mstore(0xc0, b17) -// mstore(0xa0, b18) -// mstore(0x80, b19) -// mstore(0xe0, b29) -// mstore(0x0100, b30) +// mstore(0x0100, b17) +// mstore(0xe0, b18) +// mstore(0xc0, b19) +// mstore(0xa0, b29) +// mstore(0x80, 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)) +// sstore(3, mload(0x0100)) +// sstore(4, mload(0xe0)) +// sstore(5, mload(0xc0)) +// sstore(6, mload(0xa0)) +// sstore(7, mload(0x80)) +// v := mload(0x80) +// sstore(mload(0x02e0), mload(0x80)) // } // } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_16.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_16.yul index 42e1aabc2..5a3b3454c 100644 --- a/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_16.yul +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/too_many_args_16.yul @@ -43,21 +43,21 @@ // 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) +// mstore(0x0120, b17) +// mstore(0x0100, b18) +// mstore(0xe0, b19) +// mstore(0xc0, b29) +// mstore(0xa0, b30) +// mstore(0x80, 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)) +// sstore(3, mload(0x0120)) +// sstore(4, mload(0x0100)) +// sstore(5, mload(0xe0)) +// sstore(6, mload(0xc0)) +// sstore(7, mload(0xa0)) +// sstore(8, mload(0x80)) +// v := add(mload(0x0320), mload(0x80)) // } // } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/verbatim_many_arguments.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/verbatim_many_arguments.yul index cb4d69269..bde030f68 100644 --- a/test/libyul/yulOptimizerTests/stackLimitEvader/verbatim_many_arguments.yul +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/verbatim_many_arguments.yul @@ -39,8 +39,8 @@ // mstore(0x0140, 7) // mstore(0x0160, 8) // mstore(0x0180, 9) -// mstore(0x01c0, 10) -// mstore(0x01a0, 11) +// mstore(0x01a0, 10) +// mstore(0x01c0, 11) // mstore(0x01e0, 12) // let a_13 := 13 // let a_14 := 14 @@ -50,6 +50,6 @@ // let a_18 := 18 // let a_19 := 19 // let a_20 := 20 -// verbatim_20i_0o("test", mload(0x80), mload(0xa0), mload(0xc0), mload(0xe0), mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x01c0), mload(0x01a0), mload(0x01e0), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20) +// verbatim_20i_0o("test", mload(0x80), mload(0xa0), mload(0xc0), mload(0xe0), mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x01a0), mload(0x01c0), mload(0x01e0), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20) // } // } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/verbatim_many_arguments_and_returns.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/verbatim_many_arguments_and_returns.yul index de023e919..46435dd21 100644 --- a/test/libyul/yulOptimizerTests/stackLimitEvader/verbatim_many_arguments_and_returns.yul +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/verbatim_many_arguments_and_returns.yul @@ -51,17 +51,17 @@ // { // { // mstore(0x40, memoryguard(0x0280)) -// mstore(0x80, 1) -// mstore(0xa0, 2) -// mstore(0xc0, 3) -// mstore(0xe0, 4) -// mstore(0x0100, 5) -// mstore(0x0120, 6) -// mstore(0x0140, 7) -// mstore(0x0160, 8) -// mstore(0x0180, 9) -// mstore(0x0240, 10) -// mstore(0x0220, 11) +// mstore(0x0100, 1) +// mstore(0x0120, 2) +// mstore(0x0140, 3) +// mstore(0x0160, 4) +// mstore(0x0180, 5) +// mstore(0x01a0, 6) +// mstore(0x01c0, 7) +// mstore(0x01e0, 8) +// mstore(0x0200, 9) +// mstore(0x0220, 10) +// mstore(0x0240, 11) // mstore(0x0260, 12) // let a_13 := 13 // let a_14 := 14 @@ -71,11 +71,11 @@ // let a_18 := 18 // let a_19 := 19 // let a_20 := 20 -// let b_1_1, b_2_2, b_3_3, b_4_4, b_5_5, b_6_6, b_7_7, b_8_8, b_9_9, b_10_10, b_11_11, b_12_12, b_13_13, b_14_14, b_15_15, b_16_16, b_17_17, b_18_18, b_19_19, b_20_20 := verbatim_20i_20o("test", mload(0x80), mload(0xa0), mload(0xc0), mload(0xe0), mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x0240), mload(0x0220), mload(0x0260), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20) -// mstore(0x01a0, b_4_4) -// mstore(0x01c0, b_3_3) -// mstore(0x01e0, b_2_2) -// mstore(0x0200, b_1_1) +// let b_1_1, b_2_2, b_3_3, b_4_4, b_5_5, b_6_6, b_7_7, b_8_8, b_9_9, b_10_10, b_11_11, b_12_12, b_13_13, b_14_14, b_15_15, b_16_16, b_17_17, b_18_18, b_19_19, b_20_20 := verbatim_20i_20o("test", mload(0x0100), mload(0x0120), mload(0x0140), mload(0x0160), mload(0x0180), mload(0x01a0), mload(0x01c0), mload(0x01e0), mload(0x0200), mload(0x0220), mload(0x0240), mload(0x0260), a_13, a_14, a_15, a_16, a_17, a_18, a_19, a_20) +// mstore(0x80, b_4_4) +// mstore(0xa0, b_3_3) +// mstore(0xc0, b_2_2) +// mstore(0xe0, b_1_1) // let b_20 := b_20_20 // let b_19 := b_19_19 // let b_18 := b_18_18 @@ -92,10 +92,10 @@ // let b_7 := b_7_7 // let b_6 := b_6_6 // let b_5 := b_5_5 -// sstore(1, mload(0x0200)) -// sstore(2, mload(0x01e0)) -// sstore(3, mload(0x01c0)) -// sstore(4, mload(0x01a0)) +// sstore(1, mload(0xe0)) +// sstore(2, mload(0xc0)) +// sstore(3, mload(0xa0)) +// sstore(4, mload(0x80)) // sstore(5, b_5) // sstore(6, b_6) // sstore(7, b_7)