[yul] Ensures DataFlowAnalyzer works fine with break/continue statements just like without.

This commit is contained in:
Christian Parpart 2019-03-11 16:06:43 +01:00 committed by Christian Parpart
parent 94cd81de8c
commit 57bcb8ba83
No known key found for this signature in database
GPG Key ID: 19BC8DD20312C929
7 changed files with 188 additions and 13 deletions

View File

@ -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();

View File

@ -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:

View File

@ -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, "");
}

View File

@ -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;
};
} }

View 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)
// }

View File

@ -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)
// }

View File

@ -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
// }