-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
librustc_mir: Implement def-use chains and trivial copy propagation on MIR. #36388
Conversation
156deff
to
4cd0bc9
Compare
@@ -699,6 +733,9 @@ pub enum StatementKind<'tcx> { | |||
|
|||
/// End the current live range for the storage of the local. | |||
StorageDead(Lvalue<'tcx>), | |||
|
|||
/// No-op. Useful for deleting instructions without affecting statement indices. | |||
Nop, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential point of disagreement (we already have at least one lazy patching mechanism which preserves indices).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is that one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's in src/librustc_borrowck/borrowck/mir/patch.rs
(because drop elaboration in there. IMO rustc_borrowck
should be replaced with a MIR version that doesn't live in a rustc_borrowck
crate but that's besides the point).
It doesn't have the ability to remove statements because it wasn't needed.
I'm not entirely clear on whether that functionality could be easily added. cc @arielb1
LGTM except for the potentially contentious |
debug!(" Can't copy-propagate local: no uses"); | ||
continue | ||
} | ||
if src_use_info.use_count() != 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Putting the result of use_count()
into a variable instead of calling it 3 times might be a good idea - it's O(n) to compute it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the candidate could be found with a single traversal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the source local having exactly 1 mutation (i.e. your assignment) is more interesting than it having 1 use, because that implies the assignment dominates all uses (you might want to at least debug-assert that, because mem::uninitialized
might violate it in the future).
BTW, you want to run this after drop elaboration, because that removes quite a few dominated drops (or at least add a dominated drop removal pass).
Also, I think that having the pivot/destination as the core of the iteration is more natural.
cc @rust-lang/compiler |
} | ||
|
||
/// Returns true if this lvalue context represents a mutation (def). | ||
pub fn is_def(&self) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer is_mutation
rather than is_def
for mutations. BTW, why isn't drop
considered? Sounds like it could create some trouble.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, the name def
really confused me (though I get why you chose it now). My preference would be is_mut()
. And yes Drop
seems like a mutation to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See e.g. http://www.cs.cmu.edu/afs/cs/academic/class/15745-s05/www/lectures/lect2.pdf — "def" seems to be the right term. @nikomatsakis, want to weigh in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. In SSA, mutations are defs ("FRU" updates, if needed). Tho drop
is a mutation too.
I think the term "def" for a mutation is confusing in a non-SSA context, but whatever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pcwalton my feeling is that def
is the right term, it's just overloaded. Without context, I interpreted it first as "definition" as in def_id
, versus definition as in "assignment to a variable".
b489231
to
9173e9e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, this looks great to me! It's simple, but we gotta start somewhere. =) I left a few nits with suggestions on comments and naming.
// Used as base for another lvalue, e.g. `x` in `x.y` | ||
Projection, | ||
Projection(Mutability), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment might be nice here -- what is this Mutability
referring to exactly? Ideally a snippet of Rust code to make the distinction clear.
} | ||
} | ||
|
||
fn lvalue_mut_info<'a>(&'a mut self, lvalue: &Lvalue<'tcx>) -> Option<&'a mut Info<'tcx>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: In these cases, I'd normally let elision do its thing instead of writing out 'a
. But whatever.
&mut self.info[local] | ||
} | ||
|
||
fn mutate_uses<F>(&self, local: Local, mir: &mut Mir<'tcx>, mut callback: F) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function could use a comment explaining what it does. Also, the term use
seems to be somewhat overloaded, no? This doesn't just iterate over "uses" in the "def-use" sense but really any "mention" (to use @arielb1's term).
} | ||
} | ||
|
||
impl<'tcx> Visitor<'tcx> for DefUseAnalysis<'tcx> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Personally, I would probably not make DefUseAnalysis
implement Visitor
directly, but rather have some wrapper that does it, and make the a method like record_uses_from_mir()
that invokes the visitor. This just makes other code a bit more explicit vs seeing def_use.visit_mir(mir)
, which isn't so clear.
let src_location; | ||
let dest_local; | ||
{ | ||
// There must be exactly one use of the source used in a statement (not in a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: It'd be great to have a little example of what pattern you are matching here, in terms of MIR. I am thinking of some comments like:
Look for patterns like
X = Y ... USE(X)
and replace with:
... USE(Y)
Here, the
src_local
would beY
. So we look for a variableY
that is used at most once, and that use is an assignment. (etc)
I find it particularly useful to link the names in the MIR examples to the variables in the Rust source doing the transform.
☔ The latest upstream changes (presumably #36353) made this pull request unmergeable. Please resolve the merge conflicts. |
9173e9e
to
bfb6a04
Compare
BTW, r=me modulo nits. (In case that wasn't clear.) (It doesn't seem like there have been any major objections raised, right?) |
impl Location { | ||
pub fn dominates(&self, other: &Location, dominators: &Dominators<BasicBlock>) -> bool { | ||
if self.block == other.block { | ||
self.statement_index < other.statement_index |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the method name is dominates
, should this be <=
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems correct, yeah.
This is useful when passes want to remove statements without affecting `Location`s.
2eec6e1
to
fb2fb68
Compare
This only supports trivial cases in which there is exactly one def and one use.
fb2fb68
to
480287e
Compare
Updated. All tests pass locally. This should be ready to go. Please see the fix at 480287e#diff-033ea10cb9e9d0a0b2eb95445ea25502R125. |
@bors r+ |
📌 Commit 480287e has been approved by |
librustc_mir: Implement def-use chains and trivial copy propagation on MIR. This only supports trivial cases in which there is exactly one def and one use. Currently, some random unrelated MIR tests are failing, probably just because they haven't been updated. r? @eddyb
This only supports trivial cases in which there is exactly one def and
one use.
Currently, some random unrelated MIR tests are failing, probably just because they haven't been updated.
r? @eddyb