Skip to content

Refactor redundant assign#11774

Merged
chriseth merged 1 commit into
developfrom
refactorRedundantAssign
Aug 13, 2021
Merged

Refactor redundant assign#11774
chriseth merged 1 commit into
developfrom
refactorRedundantAssign

Conversation

@chriseth

Copy link
Copy Markdown
Contributor

Needed for #11352

Comment thread libsolutil/CommonData.h
@chriseth

Copy link
Copy Markdown
Contributor Author

@leonardoalt Is that an "approve"? :)

@leonardoalt

Copy link
Copy Markdown

not yet

leonardoalt
leonardoalt previously approved these changes Aug 11, 2021
ekpyron
ekpyron previously approved these changes Aug 11, 2021
ForLoopInfo outerForLoopInfo;
swap(outerForLoopInfo, m_forLoopInfo);
ScopedSaveAndRestore outerForLoopInfo(m_forLoopInfo, {});
++m_forLoopNestingDepth;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
++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.

Comment thread libyul/optimiser/RedundantAssignUtils.h Outdated

/// Working data for traversing for-loops.
template <class TrackedAssignments>
struct ForLoopInfo

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's quite a generic name for a top level construct... and also not very descriptive...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know... Let me see what I can do.

@chriseth chriseth dismissed stale reviews from ekpyron and leonardoalt via 66a4d8f August 11, 2021 15:42
@chriseth chriseth force-pushed the refactorRedundantAssign branch from 427ed2c to 66a4d8f Compare August 11, 2021 15:42
@chriseth

Copy link
Copy Markdown
Contributor Author

Did a complete makeover and create a common base class.

@chriseth

Copy link
Copy Markdown
Contributor Author

Sorry, this is probably difficult to review, something like a three-way diff is probably the best way...

@chriseth chriseth force-pushed the refactorRedundantAssign branch 2 times, most recently from 956e763 to e30ab69 Compare August 11, 2021 16:18
@chriseth chriseth requested a review from ekpyron August 12, 2021 09:15
Value m_value = Undecided;
};

using TrackedStores = std::map<YulString, std::map<Statement const*, State>>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might make sense to use a compare function on the inner map based on Statement id or something similar

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean to ensure determinism? I don't think this is needed, to be honest. Furthermore, there are no IDs in yul.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, because there was a TODO somewhere about determinism

@ekpyron ekpyron Aug 13, 2021

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here (re compare function)

@chriseth chriseth force-pushed the refactorRedundantAssign branch from e30ab69 to f2d89b0 Compare August 13, 2021 08:03
}

for (auto const& assignment: assignments)
for (auto const& store: stores)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (auto const& store: stores)
for (auto&& [statement, state]: stores)

?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

considered this, but there is another local variable called state...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can I enforce statement and state to be non-references? Is auto [statement, state] enough?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@ekpyron ekpyron Aug 13, 2021

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ekpyron left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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...

Comment thread libyul/optimiser/RedundantStoreBase.h Outdated
struct Dialect;

/**
* Base class for both RedundantAsssignEliminator and RedundantStoreEliminator.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May state the prerequisites induced by the base here already as well?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also says Ass_sign by the way ;-).

Comment thread libyul/optimiser/RedundantAssignEliminator.cpp
Comment thread libyul/optimiser/RedundantStoreBase.cpp Outdated
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Base class for both RedundantAsssignEliminator and RedundantStoreEliminator.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Base class for both RedundantAsssignEliminator and RedundantStoreEliminator.
* Base class for both RedundantAssignEliminator and RedundantStoreEliminator.

Ass_sign is a nice typo :-).

Comment on lines +136 to +137
std::map<Statement const*, State> stores;
util::joinMap(stores, std::move(m_stores[_variable]), State::join);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this just

std::map<Statement const*, State> stores = std::move(m_stores[_variable]);

?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +97 to +99
if (assignment->variableNames.size() == 1)
// Default-construct it in "Undecided" state if it does not yet exist.
m_stores[assignment->variableNames.front().name][&_statement];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has always worked like that apparently, but I'm not entirely clear on why only single assignments are relevant here...

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand: why exactly did this move, since it still only applies to the Assignment case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yul needs a pop<n> :-).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@chriseth

Copy link
Copy Markdown
Contributor Author

Addressed all comments.

ekpyron
ekpyron previously approved these changes Aug 13, 2021

@ekpyron ekpyron left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be squashed and merged.

@chriseth chriseth merged commit 07def75 into develop Aug 13, 2021
@chriseth chriseth deleted the refactorRedundantAssign branch August 13, 2021 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants