diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 19ba2822d..2b9bc016b 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -43,10 +43,12 @@ using namespace solidity::yul; DataFlowAnalyzer::DataFlowAnalyzer( Dialect const& _dialect, - map _functionSideEffects + map _functionSideEffects, + map _controlFlowSideEffects ): m_dialect(_dialect), m_functionSideEffects(std::move(_functionSideEffects)), + m_controlFlowSideEffects(std::move(_controlFlowSideEffects)), m_knowledgeBase(_dialect, m_value) { if (auto const* builtin = _dialect.memoryStoreFunction(YulString{})) @@ -120,9 +122,10 @@ void DataFlowAnalyzer::operator()(If& _if) unordered_map memory = m_memory; ASTModifier::operator()(_if); + joinKnowledge(storage, memory, _if.body); - joinKnowledge(storage, memory); - + // TODO we can do the same optimization here, but maybe this is already done in the ssa + // transform or some other step? clearValues(assignedVariableNames(_if.body)); } @@ -136,8 +139,8 @@ void DataFlowAnalyzer::operator()(Switch& _switch) unordered_map storage = m_storage; unordered_map memory = m_memory; (*this)(_case.body); - //TODO throw away knowledge if it is terminating - joinKnowledge(storage, memory); + + joinKnowledge(storage, memory, _case.body); set variables = assignedVariableNames(_case.body); assignedVariables += variables; @@ -343,7 +346,8 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Block const& _block) void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr) { - // TODO with the extended side-effects, this could also be more precise now. + // TODO with the extended side-effects, this could also be more precise now, + // I.e. we should check collisions like we do for sstore and mstore SideEffectsCollector sideEffects(m_dialect, _expr, &m_functionSideEffects); if (sideEffects.invalidatesStorage()) m_storage.clear(); @@ -353,11 +357,25 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr) void DataFlowAnalyzer::joinKnowledge( unordered_map const& _olderStorage, - unordered_map const& _olderMemory + unordered_map const& _olderMemory, + Block const& _branch ) { - joinKnowledgeHelper(m_storage, _olderStorage); - joinKnowledgeHelper(m_memory, _olderMemory); + if ( + _branch.statements.empty() || + TerminationFinder(m_dialect, &m_controlFlowSideEffects).controlFlowKind(_branch.statements.back()) == + TerminationFinder::ControlFlow::FlowOut + ) + { + joinKnowledgeHelper(m_storage, _olderStorage); + joinKnowledgeHelper(m_memory, _olderMemory); + } + else + { + // TOOD test that this works with 'break' and 'continue' + m_storage = _olderStorage; + m_memory = _olderMemory; + } } void DataFlowAnalyzer::joinKnowledgeHelper( diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index deaa71f89..6edccae5e 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -28,6 +28,7 @@ #include #include // Needed for m_zero below. #include +#include #include @@ -85,7 +86,8 @@ public: /// The parameter is mostly used to determine movability of expressions. explicit DataFlowAnalyzer( Dialect const& _dialect, - std::map _functionSideEffects = {} + std::map _functionSideEffects = {}, + std::map _controlFlowSideEffects = {} ); using ASTModifier::operator(); @@ -123,9 +125,12 @@ protected: /// Joins knowledge about storage and memory with an older point in the control-flow. /// This only works if the current state is a direct successor of the older point, /// i.e. `_otherStorage` and `_otherMemory` cannot have additional changes. + /// If the last statement in the vector is not continuing, replace the current knowledge + /// with the old instead. void joinKnowledge( std::unordered_map const& _olderStorage, - std::unordered_map const& _olderMemory + std::unordered_map const& _olderMemory, + Block const& _branch ); static void joinKnowledgeHelper( @@ -163,6 +168,7 @@ protected: /// Side-effects of user-defined functions. Worst-case side-effects are assumed /// if this is not provided or the function is not found. std::map m_functionSideEffects; + std::map m_controlFlowSideEffects; /// Current values of variables, always movable. std::map m_value; diff --git a/libyul/optimiser/LoadResolver.cpp b/libyul/optimiser/LoadResolver.cpp index 550ca6fb3..35dc394f3 100644 --- a/libyul/optimiser/LoadResolver.cpp +++ b/libyul/optimiser/LoadResolver.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ void LoadResolver::run(OptimiserStepContext& _context, Block& _ast) LoadResolver{ _context.dialect, SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)), + ControlFlowSideEffectsCollector{_context.dialect, _ast}.functionSideEffectsNamed(), containsMSize, _context.expectedExecutionsPerDeployment }(_ast); diff --git a/libyul/optimiser/LoadResolver.h b/libyul/optimiser/LoadResolver.h index 93d37d779..16a26cebc 100644 --- a/libyul/optimiser/LoadResolver.h +++ b/libyul/optimiser/LoadResolver.h @@ -24,6 +24,7 @@ #include #include +#include namespace solidity::yul { @@ -50,10 +51,11 @@ private: LoadResolver( Dialect const& _dialect, std::map _functionSideEffects, + std::map _controlFlowSideEffects, bool _containsMSize, std::optional _expectedExecutionsPerDeployment ): - DataFlowAnalyzer(_dialect, std::move(_functionSideEffects)), + DataFlowAnalyzer(_dialect, std::move(_functionSideEffects), std::move(_controlFlowSideEffects)), m_containsMSize(_containsMSize), m_expectedExecutionsPerDeployment(std::move(_expectedExecutionsPerDeployment)) {} diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol index d2d7f4e48..384269e4c 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol @@ -14,6 +14,6 @@ contract c { // compileViaYul: also // ---- // test() -> 9, 4 -// gas irOptimized: 123162 +// gas irOptimized: 123195 // gas legacy: 123579 // gas legacyOptimized: 123208 diff --git a/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol b/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol index 7550a9225..dbeb30f3a 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol @@ -18,6 +18,6 @@ contract C { // compileViaYul: also // ---- // test() -> 7 -// gas irOptimized: 124041 +// gas irOptimized: 124024 // gas legacy: 205196 // gas legacyOptimized: 204987 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol index 07a054514..4ed2ef2f0 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol @@ -11,7 +11,7 @@ contract Creator { // compileViaYul: also // ---- // constructor(): 1, 2, 3, 4 -> -// gas irOptimized: 129908 +// gas irOptimized: 127748 // gas legacy: 176789 // gas legacyOptimized: 129585 // r() -> 4 diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol index 73f9799d4..74987fed3 100644 --- a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -45,6 +45,6 @@ contract C { // compileViaYul: also // ---- // test() -> 5, 6, 7 -// gas irOptimized: 298983 +// gas irOptimized: 298177 // gas legacy: 452172 // gas legacyOptimized: 285017 diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol index cc303899a..c27d197da 100644 --- a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -26,6 +26,6 @@ contract Creator { // compileViaYul: also // ---- // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 -// gas irOptimized: 443960 +// gas irOptimized: 446570 // gas legacy: 590683 // gas legacyOptimized: 448326 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index ffe0fb8c2..8dd0509e5 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -26,6 +26,6 @@ contract Creator { // compileViaYul: also // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" -// gas irOptimized: 300804 +// gas irOptimized: 299178 // gas legacy: 428917 // gas legacyOptimized: 298128 diff --git a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol index 2206fd351..1ba5cc153 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol @@ -19,7 +19,7 @@ contract Main { // compileViaYul: also // ---- // constructor(): "abc", true -// gas irOptimized: 107175 +// gas irOptimized: 105225 // gas legacy: 145838 // gas legacyOptimized: 104017 // getFlag() -> true diff --git a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol index f6fab1092..b69b19626 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol @@ -12,7 +12,7 @@ contract C { // compileViaYul: also // ---- // constructor(): 1, 2, 3, 4 -> -// gas irOptimized: 174905 +// gas irOptimized: 175559 // gas legacy: 221377 // gas legacyOptimized: 177671 // a() -> 1 diff --git a/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol b/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol index 2518deb92..d52bca2c7 100644 --- a/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol +++ b/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol @@ -15,5 +15,5 @@ contract B is A { // compileViaYul: true // ---- // constructor() -> -// gas irOptimized: 122017 +// gas irOptimized: 120289 // y() -> 42 diff --git a/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol b/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol index 68506528b..6c5ef9475 100644 --- a/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol +++ b/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol @@ -12,7 +12,7 @@ contract B is A { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 122017 +// gas irOptimized: 120289 // gas legacy: 135046 // gas legacyOptimized: 116176 // y() -> 42 diff --git a/test/libsolidity/semanticTests/constructor_with_params.sol b/test/libsolidity/semanticTests/constructor_with_params.sol index cb6192774..d2687d4d9 100644 --- a/test/libsolidity/semanticTests/constructor_with_params.sol +++ b/test/libsolidity/semanticTests/constructor_with_params.sol @@ -11,7 +11,7 @@ contract C { // compileViaYul: also // ---- // constructor(): 2, 0 -> -// gas irOptimized: 104227 +// gas irOptimized: 102283 // gas legacy: 117158 // i() -> 2 // k() -> 0 diff --git a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol index 8f891116e..666fc3791 100644 --- a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol +++ b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol @@ -23,7 +23,7 @@ contract D is B, C { // compileViaYul: also // ---- // constructor(): 2, 0 -> -// gas irOptimized: 160166 +// gas irOptimized: 159086 // gas legacy: 170665 // gas legacyOptimized: 145396 // i() -> 2 diff --git a/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol b/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol index 3d90783f8..5c7687019 100644 --- a/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol +++ b/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol @@ -14,7 +14,7 @@ contract D is C { // compileViaYul: also // ---- // constructor(): 2, 0 -> -// gas irOptimized: 124844 +// gas irOptimized: 122900 // gas legacy: 139250 // gas legacyOptimized: 119367 // i() -> 2 diff --git a/test/libsolidity/semanticTests/externalContracts/strings.sol b/test/libsolidity/semanticTests/externalContracts/strings.sol index a16f884e8..b1c1ac37d 100644 --- a/test/libsolidity/semanticTests/externalContracts/strings.sol +++ b/test/libsolidity/semanticTests/externalContracts/strings.sol @@ -51,7 +51,7 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 707330 +// gas irOptimized: 708410 // gas legacy: 1130761 // gas legacyOptimized: 750416 // toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 @@ -71,6 +71,6 @@ contract test { // gas legacy: 31621 // gas legacyOptimized: 27914 // benchmark(string,bytes32): 0x40, 0x0842021, 8, "solidity" -> 0x2020 -// gas irOptimized: 2040045 +// gas irOptimized: 2040061 // gas legacy: 4381235 // gas legacyOptimized: 2317529 diff --git a/test/libsolidity/semanticTests/functionCall/failed_create.sol b/test/libsolidity/semanticTests/functionCall/failed_create.sol index 2ebf8740d..f02beec75 100644 --- a/test/libsolidity/semanticTests/functionCall/failed_create.sol +++ b/test/libsolidity/semanticTests/functionCall/failed_create.sol @@ -18,7 +18,7 @@ contract C { // compileViaYul: also // ---- // constructor(), 20 wei -// gas irOptimized: 219285 +// gas irOptimized: 218415 // gas legacy: 294569 // gas legacyOptimized: 174699 // f(uint256): 20 -> 1370859564726510389319704988634906228201275401179 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol index 4321753d6..c10ec1d61 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol @@ -41,7 +41,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 283040 +// gas irOptimized: 281960 // gas legacy: 402654 // gas legacyOptimized: 274470 // sendAmount(uint256): 5 -> 5 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol index 2f89517b7..684d2610e 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol @@ -40,7 +40,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 283040 +// gas irOptimized: 281960 // gas legacy: 402654 // gas legacyOptimized: 274470 // sendAmount(uint256): 5 -> 5 diff --git a/test/libsolidity/semanticTests/immutable/multi_creation.sol b/test/libsolidity/semanticTests/immutable/multi_creation.sol index e834663d0..9d90b3e96 100644 --- a/test/libsolidity/semanticTests/immutable/multi_creation.sol +++ b/test/libsolidity/semanticTests/immutable/multi_creation.sol @@ -29,7 +29,7 @@ contract C { // compileViaYul: also // ---- // f() -> 3, 7, 5 -// gas irOptimized: 127347 +// gas irOptimized: 127321 // gas legacy: 151334 // gas legacyOptimized: 125166 // x() -> 7 diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol index a15650992..c16a89502 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol @@ -25,6 +25,6 @@ contract B { // compileViaYul: also // ---- // g() -> 42 -// gas irOptimized: 111794 +// gas irOptimized: 112591 // gas legacy: 185053 // gas legacyOptimized: 114598 diff --git a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol index e6f94bdcf..f67072a21 100644 --- a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol +++ b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol @@ -42,7 +42,7 @@ contract Main { // compileViaYul: also // ---- // constructor(), 22 wei -> -// gas irOptimized: 284287 +// gas irOptimized: 281653 // gas legacy: 402045 // gas legacyOptimized: 266772 // getFlag() -> true diff --git a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol index 4ce8b5149..2a098b836 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol @@ -22,6 +22,6 @@ contract A { // compileViaYul: also // ---- // f(), 10 ether -> 3007, 3008, 3009 -// gas irOptimized: 272413 +// gas irOptimized: 273010 // gas legacy: 422501 // gas legacyOptimized: 287472 diff --git a/test/libsolidity/semanticTests/state/blockhash_basic.sol b/test/libsolidity/semanticTests/state/blockhash_basic.sol index 9abeb90aa..2b954188f 100644 --- a/test/libsolidity/semanticTests/state/blockhash_basic.sol +++ b/test/libsolidity/semanticTests/state/blockhash_basic.sol @@ -14,7 +14,7 @@ contract C { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 115297 +// gas irOptimized: 114001 // gas legacy: 155081 // gas legacyOptimized: 107997 // genesisHash() -> 0x3737373737373737373737373737373737373737373737373737373737373737 diff --git a/test/libsolidity/semanticTests/various/address_code.sol b/test/libsolidity/semanticTests/various/address_code.sol index 38e0fb13f..7377d48ee 100644 --- a/test/libsolidity/semanticTests/various/address_code.sol +++ b/test/libsolidity/semanticTests/various/address_code.sol @@ -17,7 +17,7 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 199723 +// gas irOptimized: 199084 // gas legacy: 241124 // gas legacyOptimized: 155549 // initCode() -> 0x20, 0 diff --git a/test/libsolidity/semanticTests/various/destructuring_assignment.sol b/test/libsolidity/semanticTests/various/destructuring_assignment.sol index cc93152ea..37662e16a 100644 --- a/test/libsolidity/semanticTests/various/destructuring_assignment.sol +++ b/test/libsolidity/semanticTests/various/destructuring_assignment.sol @@ -36,6 +36,6 @@ contract C { // compileViaYul: also // ---- // f(bytes): 0x20, 0x5, "abcde" -> 0 -// gas irOptimized: 240691 +// gas irOptimized: 240688 // gas legacy: 240358 // gas legacyOptimized: 239682 diff --git a/test/libyul/yulOptimizerTests/fullSuite/loadrestest.yul b/test/libyul/yulOptimizerTests/fullSuite/loadrestest.yul index 8cd05ece3..f9c1402fb 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/loadrestest.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/loadrestest.yul @@ -23,6 +23,6 @@ // mstore(_1, sload(0)) // return(_1, 32) // } -// case 0xc2985578 { return(mload(64), 0) } +// case 0xc2985578 { return(_1, 0) } // } // }