Refactor redundant assign#11774
Conversation
|
@leonardoalt Is that an "approve"? :) |
|
not yet |
| ForLoopInfo outerForLoopInfo; | ||
| swap(outerForLoopInfo, m_forLoopInfo); | ||
| ScopedSaveAndRestore outerForLoopInfo(m_forLoopInfo, {}); | ||
| ++m_forLoopNestingDepth; |
There was a problem hiding this comment.
| ++m_forLoopNestingDepth; | |
| ScopedSaveAndRestore loopNestingDepthIncrement(m_forLoopNestingDepth, m_forLoopNestingDepth + 1); |
not sure that's really nicer, but it's at least more local than having the decrement at the very end. But also fine keeping as is.
|
|
||
| /// Working data for traversing for-loops. | ||
| template <class TrackedAssignments> | ||
| struct ForLoopInfo |
There was a problem hiding this comment.
That's quite a generic name for a top level construct... and also not very descriptive...
There was a problem hiding this comment.
I know... Let me see what I can do.
427ed2c to
66a4d8f
Compare
|
Did a complete makeover and create a common base class. |
|
Sorry, this is probably difficult to review, something like a three-way diff is probably the best way... |
956e763 to
e30ab69
Compare
| Value m_value = Undecided; | ||
| }; | ||
|
|
||
| using TrackedStores = std::map<YulString, std::map<Statement const*, State>>; |
There was a problem hiding this comment.
Might make sense to use a compare function on the inner map based on Statement id or something similar
There was a problem hiding this comment.
You mean to ensure determinism? I don't think this is needed, to be honest. Furthermore, there are no IDs in yul.
There was a problem hiding this comment.
Yea, because there was a TODO somewhere about determinism
There was a problem hiding this comment.
YulString comparison is "weird", but deterministic. (Ah, sorry you meant the inner one... but still probably fine, I'll keep an eye on it)
| void finalize(YulString _variable, State _finalState); | ||
|
|
||
| Dialect const& m_dialect; | ||
| std::set<Statement const*> m_pendingRemovals; |
e30ab69 to
f2d89b0
Compare
| } | ||
|
|
||
| for (auto const& assignment: assignments) | ||
| for (auto const& store: stores) |
There was a problem hiding this comment.
| for (auto const& store: stores) | |
| for (auto&& [statement, state]: stores) |
?
There was a problem hiding this comment.
considered this, but there is another local variable called state...
There was a problem hiding this comment.
How can I enforce statement and state to be non-references? Is auto [statement, state] enough?
There was a problem hiding this comment.
The condition below can just be
if ((state == State::Unused || (state == State::Undecided && _finalState == State::Unused)) && ...
or the inner one could just be renamed... but also fine keeping it.
Forcing auto [statement, state] to be of value type is something I wouldn't try myself :-). (The problem there not so much being auto, but the structured binding working via weird intermediate tuple types by spec ;-))
| Value m_value = Undecided; | ||
| }; | ||
|
|
||
| using TrackedStores = std::map<YulString, std::map<Statement const*, State>>; |
There was a problem hiding this comment.
YulString comparison is "weird", but deterministic. (Ah, sorry you meant the inner one... but still probably fine, I'll keep an eye on it)
ekpyron
left a comment
There was a problem hiding this comment.
Some more comments, but I'm rather confident that it does what it did before.
Why don't we remove multi-assignments? As in both: what prevents them to be removed in the code and why is removing them prevented at all...
| struct Dialect; | ||
|
|
||
| /** | ||
| * Base class for both RedundantAsssignEliminator and RedundantStoreEliminator. |
There was a problem hiding this comment.
May state the prerequisites induced by the base here already as well?
There was a problem hiding this comment.
Also says Ass_sign by the way ;-).
| */ | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
| /** | ||
| * Base class for both RedundantAsssignEliminator and RedundantStoreEliminator. |
There was a problem hiding this comment.
| * Base class for both RedundantAsssignEliminator and RedundantStoreEliminator. | |
| * Base class for both RedundantAssignEliminator and RedundantStoreEliminator. |
Ass_sign is a nice typo :-).
| std::map<Statement const*, State> stores; | ||
| util::joinMap(stores, std::move(m_stores[_variable]), State::join); |
There was a problem hiding this comment.
Isn't this just
std::map<Statement const*, State> stores = std::move(m_stores[_variable]);
?
There was a problem hiding this comment.
Yes, not sure why I did it that way, maybe because of conformity? I can change it to use move it is easier to understand.
| if (assignment->variableNames.size() == 1) | ||
| // Default-construct it in "Undecided" state if it does not yet exist. | ||
| m_stores[assignment->variableNames.front().name][&_statement]; |
There was a problem hiding this comment.
It has always worked like that apparently, but I'm not entirely clear on why only single assignments are relevant here...
There was a problem hiding this comment.
On the other hand: why exactly did this move, since it still only applies to the Assignment case?
There was a problem hiding this comment.
Explanation about moving is here: https://github.com/ethereum/solidity/pull/11774/files#r688454105
single-assignments are fine because of SSA: If this is a multi-assignment and turns out to be redundant, the actual SSA assignment will be redundant and removed, while this assignment has to stay unless the right-hand-side is side-effect-free. We have to assign the value of the righ-hand-side somewhere, so this will turn into a variable declaration and the variable will not be used at all.
There was a problem hiding this comment.
And the reason for multi-assignments to generally not be removed by any of this here, is that they are never actually entered in the map in the first place? Might be worth an assert in finalize... but also fine.
| } | ||
|
|
||
| for (auto const& assignment: assignments) | ||
| for (auto const& store: stores) |
There was a problem hiding this comment.
The condition below can just be
if ((state == State::Unused || (state == State::Undecided && _finalState == State::Unused)) && ...
or the inner one could just be renamed... but also fine keeping it.
Forcing auto [statement, state] to be of value type is something I wouldn't try myself :-). (The problem there not so much being auto, but the structured binding working via weird intermediate tuple types by spec ;-))
|
Addressed all comments. |
ekpyron
left a comment
There was a problem hiding this comment.
Can be squashed and merged.
e098005 to
3622b30
Compare
Needed for #11352