More logic about control flow with continue and about finalize. Remove BlockScope.

This commit is contained in:
chriseth 2019-03-26 17:35:26 +01:00
parent a1ec49409d
commit 91f96c299e
10 changed files with 356 additions and 103 deletions

View File

@ -97,31 +97,30 @@ void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDe
{
std::set<YulString> outerDeclaredVariables;
TrackedAssignments outerAssignments;
ForLoopInfo forLoopInfo;
swap(m_declaredVariables, outerDeclaredVariables);
swap(m_assignments, outerAssignments);
swap(m_forLoopInfo, forLoopInfo);
(*this)(_functionDefinition.body);
for (auto const& param: _functionDefinition.parameters)
{
changeUndecidedTo(param.name, State::Unused);
finalize(param.name);
}
finalize(param.name, State::Unused);
for (auto const& retParam: _functionDefinition.returnVariables)
{
changeUndecidedTo(retParam.name, State::Used);
finalize(retParam.name);
}
finalize(retParam.name, State::Used);
swap(m_declaredVariables, outerDeclaredVariables);
swap(m_assignments, outerAssignments);
swap(m_forLoopInfo, forLoopInfo);
}
void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
{
// This will set all variables that are declared in this
// block to "unused" when it is destroyed.
BlockScope scope(*this);
ForLoopInfo outerForLoopInfo;
swap(outerForLoopInfo, m_forLoopInfo);
set<YulString> outerDeclaredVariables;
swap(m_declaredVariables, outerDeclaredVariables);
// We need to visit the statements directly because of the
// scoping rules.
@ -133,9 +132,6 @@ void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
visit(*_forLoop.condition);
ForLoopInfo outerForLoopInfo;
swap(outerForLoopInfo, m_forLoopInfo);
TrackedAssignments zeroRuns{m_assignments};
(*this)(_forLoop.body);
@ -150,6 +146,7 @@ void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
(*this)(_forLoop.body);
merge(m_assignments, move(m_forLoopInfo.pendingContinueStmts));
m_forLoopInfo.pendingContinueStmts.clear();
(*this)(_forLoop.post);
visit(*_forLoop.condition);
@ -158,28 +155,39 @@ void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
merge(m_assignments, move(oneRun));
merge(m_assignments, move(zeroRuns));
merge(m_assignments, move(m_forLoopInfo.pendingBreakStmts));
m_forLoopInfo.pendingBreakStmts.clear();
// Oestore potential outer for-loop states.
for (auto const& var: m_declaredVariables)
finalize(var, State::Unused);
swap(m_declaredVariables, outerDeclaredVariables);
// Restore potential outer for-loop states.
swap(m_forLoopInfo, outerForLoopInfo);
}
void RedundantAssignEliminator::operator()(Break const&)
{
m_forLoopInfo.pendingBreakStmts.push_back(m_assignments);
m_forLoopInfo.pendingBreakStmts.emplace_back(move(m_assignments));
m_assignments.clear();
}
void RedundantAssignEliminator::operator()(Continue const&)
{
m_forLoopInfo.pendingContinueStmts.push_back(m_assignments);
m_forLoopInfo.pendingContinueStmts.emplace_back(move(m_assignments));
m_assignments.clear();
}
void RedundantAssignEliminator::operator()(Block const& _block)
{
// This will set all variables that are declared in this
// block to "unused" when it is destroyed.
BlockScope scope(*this);
set<YulString> outerDeclaredVariables;
swap(m_declaredVariables, outerDeclaredVariables);
ASTWalker::operator()(_block);
for (auto const& var: m_declaredVariables)
finalize(var, State::Unused);
swap(m_declaredVariables, outerDeclaredVariables);
}
void RedundantAssignEliminator::run(Dialect const& _dialect, Block& _ast)
@ -239,21 +247,35 @@ void RedundantAssignEliminator::merge(TrackedAssignments& _target, vector<Tracke
void RedundantAssignEliminator::changeUndecidedTo(YulString _variable, RedundantAssignEliminator::State _newState)
{
for (auto& assignment: m_assignments[_variable])
if (assignment.second == State{State::Undecided})
if (assignment.second == State::Undecided)
assignment.second = _newState;
}
void RedundantAssignEliminator::finalize(YulString _variable)
void RedundantAssignEliminator::finalize(YulString _variable, RedundantAssignEliminator::State _finalState)
{
for (auto& assignment: m_assignments[_variable])
finalize(m_assignments, _variable, _finalState);
for (auto& assignments: m_forLoopInfo.pendingBreakStmts)
finalize(assignments, _variable, _finalState);
for (auto& assignments: m_forLoopInfo.pendingContinueStmts)
finalize(assignments, _variable, _finalState);
}
void RedundantAssignEliminator::finalize(
TrackedAssignments& _assignments,
YulString _variable,
RedundantAssignEliminator::State _finalState
)
{
for (auto const& assignment: _assignments[_variable])
{
assertThrow(assignment.second != State::Undecided, OptimizerException, "");
if (assignment.second == State{State::Unused} && MovableChecker{*m_dialect, *assignment.first->value}.movable())
State const state = assignment.second == State::Undecided ? _finalState : assignment.second;
if (state == State::Unused && MovableChecker{*m_dialect, *assignment.first->value}.movable())
// TODO the only point where we actually need this
// to be a set is for the for loop
m_pendingRemovals.insert(assignment.first);
}
m_assignments.erase(_variable);
_assignments.erase(_variable);
}
void AssignmentRemover::operator()(Block& _block)

View File

@ -137,33 +137,6 @@ private:
Value m_value = Undecided;
};
/**
* Takes care about storing the list of declared variables and
* sets them to "unused" when it is destroyed.
*/
class BlockScope
{
public:
explicit BlockScope(RedundantAssignEliminator& _rae): m_rae(_rae)
{
swap(m_rae.m_declaredVariables, m_outerDeclaredVariables);
}
~BlockScope()
{
// This should actually store all declared variables
// into a different mapping
for (auto const& var: m_rae.m_declaredVariables)
m_rae.changeUndecidedTo(var, State::Unused);
for (auto const& var: m_rae.m_declaredVariables)
m_rae.finalize(var);
swap(m_rae.m_declaredVariables, m_outerDeclaredVariables);
}
private:
RedundantAssignEliminator& m_rae;
std::set<YulString> m_outerDeclaredVariables;
};
// TODO check that this does not cause nondeterminism!
// This could also be a pseudo-map from state to assignment.
using TrackedAssignments = std::map<YulString, std::map<Assignment const*, State>>;
@ -174,7 +147,12 @@ private:
static void merge(TrackedAssignments& _target, TrackedAssignments&& _source);
static void merge(TrackedAssignments& _target, std::vector<TrackedAssignments>&& _source);
void changeUndecidedTo(YulString _variable, State _newState);
void finalize(YulString _variable);
/// Called when a variable goes out of scope. Sets the state of all still undecided
/// assignments to the final state. In this case, this also applies to pending
/// break and continue TrackedAssignments.
void finalize(YulString _variable, State _finalState);
/// Helper function for the above.
void finalize(TrackedAssignments& _assignments, YulString _variable, State _finalState);
Dialect const* m_dialect;
std::set<YulString> m_declaredVariables;

View File

@ -1,20 +1,22 @@
{
// Cannot be removed, because we might run the loop only once
let x := 1
for { } calldataload(0) { }
{
if callvalue() {
x := 2 // is preserved because of break stmt below.
break
}
x := 3
}
mstore(x, 0x42)
let x
// Cannot be removed, because we might skip the loop
x := 1
for { } calldataload(0) { }
{
if callvalue() {
x := 2 // is preserved because of break stmt below.
break
}
x := 3
}
mstore(x, 0x42)
}
// ----
// redundantAssignEliminator
// {
// let x := 1
// let x
// x := 1
// for {
// }
// calldataload(0)

View File

@ -1,21 +1,24 @@
{
// Cannot be removed, because we might run the loop only once
let x := 1
for { } calldataload(0) { }
{
x := 2 // Will not be removed as if-condition can be false.
if callvalue() {
x := 3
continue
}
mstore(x, 2)
}
x := 3
let x
// Can be removed, because x is reassigned after the loop
x := 1
for { } calldataload(0) { }
{
x := 2 // Will not be removed as if-condition can be false.
if callvalue() {
// This can be removed because x is overwritten both after the
// loop at at the start of the next iteration.
x := 3
continue
}
mstore(x, 2)
}
x := 3
}
// ----
// redundantAssignEliminator
// {
// let x := 1
// let x
// for {
// }
// calldataload(0)
@ -25,7 +28,6 @@
// x := 2
// if callvalue()
// {
// x := 3
// continue
// }
// mstore(x, 2)

View File

@ -1,20 +1,22 @@
{
// Cannot be removed, because we might run the loop only once
let x := 1
for { } calldataload(0) { }
{
if callvalue() {
x := 2 // is preserved because of continue stmt below.
continue
}
x := 3
}
mstore(x, 0x42)
let x
// Cannot be removed, because we might skip the loop
x := 1
for { } calldataload(0) { }
{
if callvalue() {
x := 2 // is preserved because of continue stmt below.
continue
}
x := 3
}
mstore(x, 0x42)
}
// ----
// redundantAssignEliminator
// {
// let x := 1
// let x
// x := 1
// for {
// }
// calldataload(0)

View File

@ -1,19 +1,20 @@
{
// Cannot be removed, because we might run the loop only once
let x := 1
for { } calldataload(0) { mstore(x, 0x42) }
{
if callvalue() {
x := 2 // is preserved because of continue stmt below.
continue
}
x := 3
}
let x
// Can be removed, because x is not used after the loop.
x := 1
for { } calldataload(0) { mstore(x, 0x42) }
{
if callvalue() {
x := 2 // is preserved because of continue stmt below.
continue
}
x := 3
}
}
// ----
// redundantAssignEliminator
// {
// let x := 1
// let x
// for {
// }
// calldataload(0)

View File

@ -0,0 +1,44 @@
{
let x := 1
for { } calldataload(0) { }
{
// This will go out of scope at the end of the block,
// but the continue/break statements still refer to it.
{
let y := 9
if callvalue() {
y := 2 // will be removed
break
}
if eq(callvalue(), 3) {
y := 12 // will be removed
continue
}
}
}
mstore(x, 0x42)
}
// ----
// redundantAssignEliminator
// {
// let x := 1
// for {
// }
// calldataload(0)
// {
// }
// {
// {
// let y := 9
// if callvalue()
// {
// break
// }
// if eq(callvalue(), 3)
// {
// continue
// }
// }
// }
// mstore(x, 0x42)
// }

View File

@ -0,0 +1,74 @@
{
let x := 1
let y := 1
let z := 1
for { } calldataload(0) { mstore(x, 2) mstore(z, 2) }
{
y := 3
switch callvalue()
case 0 {
x := 2
y := 2
z := 2
break
}
case 1 {
x := 3
y := 3
z := 3
continue
}
case 2 {
x := 4
y := 4
z := 4
break
}
case 3 {
x := 5
y := 5
z := 5
continue
}
mstore(y, 9)
}
mstore(x, 0x42)
}
// ----
// redundantAssignEliminator
// {
// let x := 1
// let y := 1
// let z := 1
// for {
// }
// calldataload(0)
// {
// mstore(x, 2)
// mstore(z, 2)
// }
// {
// y := 3
// switch callvalue()
// case 0 {
// x := 2
// break
// }
// case 1 {
// x := 3
// z := 3
// continue
// }
// case 2 {
// x := 4
// break
// }
// case 3 {
// x := 5
// z := 5
// continue
// }
// mstore(y, 9)
// }
// mstore(x, 0x42)
// }

View File

@ -0,0 +1,76 @@
{
let x := 1
let y := 1
let a := 7
let b := 9
for { } calldataload(0) { }
{
y := 9
mstore(a, 7)
if callvalue() {
x := 2
for {} calldataload(1) {}
{
a := 2 // can be removed
if eq(x, 3) {
b := 3 // cannot be removed
y := 2 // will be removed
continue
}
}
mstore(b, 2)
break
}
if eq(callvalue(), 3) {
x := 12
y := 12
continue
}
x := 3
mstore(y, 3)
}
mstore(x, 0x42)
}
// ----
// redundantAssignEliminator
// {
// let x := 1
// let y := 1
// let a := 7
// let b := 9
// for {
// }
// calldataload(0)
// {
// }
// {
// y := 9
// mstore(a, 7)
// if callvalue()
// {
// x := 2
// for {
// }
// calldataload(1)
// {
// }
// {
// if eq(x, 3)
// {
// b := 3
// continue
// }
// }
// mstore(b, 2)
// break
// }
// if eq(callvalue(), 3)
// {
// x := 12
// continue
// }
// x := 3
// mstore(y, 3)
// }
// mstore(x, 0x42)
// }

View File

@ -0,0 +1,52 @@
{
let x := 1
let y := 1
for { } calldataload(0) { }
{
y := 9
if callvalue() {
x := 2
y := 2 // will be removed
break
x := 7 // after break, we start with fresh state.
}
if eq(callvalue(), 3) {
x := 12
y := 12 // will be removed
continue
x := 17 // after continue, we start with fresh state.
y := 9
}
x := 3
mstore(y, 3)
}
mstore(x, 0x42)
}
// ----
// redundantAssignEliminator
// {
// let x := 1
// let y := 1
// for {
// }
// calldataload(0)
// {
// }
// {
// y := 9
// if callvalue()
// {
// x := 2
// break
// }
// if eq(callvalue(), 3)
// {
// x := 12
// continue
// y := 9
// }
// x := 3
// mstore(y, 3)
// }
// mstore(x, 0x42)
// }