diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index c34384024..5a812151d 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -117,6 +117,9 @@ void DataFlowAnalyzer::operator()(ForLoop& _for) for (auto& statement: _for.pre.statements) visit(statement); + AssignmentsSinceContinue assignmentsSinceCont; + assignmentsSinceCont(_for.body); + Assignments assignments; assignments(_for.body); assignments(_for.post); @@ -124,22 +127,13 @@ void DataFlowAnalyzer::operator()(ForLoop& _for) visit(*_for.condition); (*this)(_for.body); + clearValues(assignmentsSinceCont.names()); (*this)(_for.post); - clearValues(assignments.names()); + popScope(); } -void DataFlowAnalyzer::operator()(Break&) -{ - yulAssert(false, "Not implemented yet."); -} - -void DataFlowAnalyzer::operator()(Continue&) -{ - yulAssert(false, "Not implemented yet."); -} - void DataFlowAnalyzer::operator()(Block& _block) { size_t numScopes = m_variableScopes.size(); diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index 7a569513e..5fb5db958 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -53,8 +53,6 @@ public: void operator()(Switch& _switch) override; void operator()(FunctionDefinition&) override; void operator()(ForLoop&) override; - void operator()(Break& _continue) override; - void operator()(Continue& _continue) override; void operator()(Block& _block) override; protected: diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index 5195b8d8f..04631a86a 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -79,3 +79,29 @@ void Assignments::operator()(Assignment const& _assignment) for (auto const& var: _assignment.variableNames) m_names.emplace(var.name); } + + +void AssignmentsSinceContinue::operator()(ForLoop const& _forLoop) +{ + m_forLoopDepth++; + ASTWalker::operator()(_forLoop); + m_forLoopDepth--; +} + +void AssignmentsSinceContinue::operator()(Continue const&) +{ + if (m_forLoopDepth == 0) + m_continueFound = true; +} + +void AssignmentsSinceContinue::operator()(Assignment const& _assignment) +{ + if (m_continueFound) + for (auto const& var: _assignment.variableNames) + m_names.emplace(var.name); +} + +void AssignmentsSinceContinue::operator()(FunctionDefinition const&) +{ + yulAssert(false, ""); +} diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index 7e21c03f0..b6b4e1e6c 100644 --- a/libyul/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -81,4 +81,29 @@ private: std::set m_names; }; +/** + * Collects all names from a given continue statement on onwards. + * + * It makes only sense to be invoked from within a body of an outer for loop, that is, + * it will only collect all names from the beginning of the first continue statement + * of the outer-most ForLoop. + */ +class AssignmentsSinceContinue: public ASTWalker +{ +public: + using ASTWalker::operator(); + void operator()(ForLoop const& _forLoop) override; + void operator()(Continue const&) override; + void operator()(Assignment const& _assignment) override; + void operator()(FunctionDefinition const& _funDef) override; + + std::set const& names() const { return m_names; } + bool empty() const noexcept { return m_names.empty(); } + +private: + size_t m_forLoopDepth = 0; + bool m_continueFound = false; + std::set m_names; +}; + } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul new file mode 100644 index 000000000..f835e84ca --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul @@ -0,0 +1,38 @@ +{ + let a + let b + for {let i := 0} lt(i, 10) {i := add(a, b)} { + a := origin() + b := origin() + b := caller() + // a=origin, b=caller + if callvalue() { break } + // a=origin, b=caller + a := caller() + } + mstore(a, b) +} +// ---- +// rematerialiser +// { +// let a +// let b +// for { +// let i := 0 +// } +// lt(i, 10) +// { +// i := add(caller(), caller()) +// } +// { +// a := origin() +// b := origin() +// b := caller() +// if callvalue() +// { +// break +// } +// a := caller() +// } +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul new file mode 100644 index 000000000..96f65ddd5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul @@ -0,0 +1,40 @@ +{ + let a + let b + for { let i := 0 } + lt(i, 10) + { i := add(a, b) } // `b` is always known to be caller() but `a` may be origin() or caller(). + { + a := origin() + b := origin() + + b := caller() + if callvalue() { continue } + a := caller() + } + mstore(a, b) +} +// ---- +// rematerialiser +// { +// let a +// let b +// for { +// let i := 0 +// } +// lt(i, 10) +// { +// i := add(a, caller()) +// } +// { +// a := origin() +// b := origin() +// b := caller() +// if callvalue() +// { +// continue +// } +// a := caller() +// } +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul new file mode 100644 index 000000000..7620791c6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul @@ -0,0 +1,54 @@ +{ + let a + let b + let c + for { + let i := 0 + b := origin() + c := origin() + } + lt(i, 10) + { + i := add(a, b) + b := callvalue() + c := caller() + } + { + a := origin() + + b := caller() + if callvalue() { continue } + a := caller() + } + let x := b // does not rematerialize as b may be either origin() or callvalue() (btw: not caller()) + let y := c // does not rematerialize as c may be either origin() or caller() +} +// ---- +// rematerialiser +// { +// let a +// let b +// let c +// for { +// let i := 0 +// b := origin() +// c := origin() +// } +// lt(i, 10) +// { +// i := add(a, caller()) +// b := callvalue() +// c := caller() +// } +// { +// a := origin() +// b := caller() +// if callvalue() +// { +// continue +// } +// a := caller() +// } +// let x := b +// let y := c +// }