mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[yul] Ensures DataFlowAnalyzer works fine with break/continue statements just like without.
This commit is contained in:
parent
94cd81de8c
commit
57bcb8ba83
@ -117,6 +117,9 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
|
|||||||
for (auto& statement: _for.pre.statements)
|
for (auto& statement: _for.pre.statements)
|
||||||
visit(statement);
|
visit(statement);
|
||||||
|
|
||||||
|
AssignmentsSinceContinue assignmentsSinceCont;
|
||||||
|
assignmentsSinceCont(_for.body);
|
||||||
|
|
||||||
Assignments assignments;
|
Assignments assignments;
|
||||||
assignments(_for.body);
|
assignments(_for.body);
|
||||||
assignments(_for.post);
|
assignments(_for.post);
|
||||||
@ -124,22 +127,13 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
|
|||||||
|
|
||||||
visit(*_for.condition);
|
visit(*_for.condition);
|
||||||
(*this)(_for.body);
|
(*this)(_for.body);
|
||||||
|
clearValues(assignmentsSinceCont.names());
|
||||||
(*this)(_for.post);
|
(*this)(_for.post);
|
||||||
|
|
||||||
clearValues(assignments.names());
|
clearValues(assignments.names());
|
||||||
|
|
||||||
popScope();
|
popScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowAnalyzer::operator()(Break&)
|
|
||||||
{
|
|
||||||
yulAssert(false, "Not implemented yet.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowAnalyzer::operator()(Continue&)
|
|
||||||
{
|
|
||||||
yulAssert(false, "Not implemented yet.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowAnalyzer::operator()(Block& _block)
|
void DataFlowAnalyzer::operator()(Block& _block)
|
||||||
{
|
{
|
||||||
size_t numScopes = m_variableScopes.size();
|
size_t numScopes = m_variableScopes.size();
|
||||||
|
@ -53,8 +53,6 @@ public:
|
|||||||
void operator()(Switch& _switch) override;
|
void operator()(Switch& _switch) override;
|
||||||
void operator()(FunctionDefinition&) override;
|
void operator()(FunctionDefinition&) override;
|
||||||
void operator()(ForLoop&) override;
|
void operator()(ForLoop&) override;
|
||||||
void operator()(Break& _continue) override;
|
|
||||||
void operator()(Continue& _continue) override;
|
|
||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -79,3 +79,29 @@ void Assignments::operator()(Assignment const& _assignment)
|
|||||||
for (auto const& var: _assignment.variableNames)
|
for (auto const& var: _assignment.variableNames)
|
||||||
m_names.emplace(var.name);
|
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, "");
|
||||||
|
}
|
||||||
|
@ -81,4 +81,29 @@ private:
|
|||||||
std::set<YulString> m_names;
|
std::set<YulString> 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<YulString> 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<YulString> m_names;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
38
test/libyul/yulOptimizerTests/rematerialiser/for_break.yul
Normal file
38
test/libyul/yulOptimizerTests/rematerialiser/for_break.yul
Normal file
@ -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)
|
||||||
|
// }
|
@ -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)
|
||||||
|
// }
|
@ -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
|
||||||
|
// }
|
Loading…
Reference in New Issue
Block a user