Skip to content
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

Merged
merged 2 commits into from
Sep 20, 2016

Conversation

pcwalton
Copy link
Contributor

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

@pcwalton pcwalton force-pushed the copy-propagation branch 2 times, most recently from 156deff to 4cd0bc9 Compare September 10, 2016 19:49
@@ -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,
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is that one?

Copy link
Member

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

@eddyb
Copy link
Member

eddyb commented Sep 10, 2016

LGTM except for the potentially contentious Nop introduction.

debug!(" Can't copy-propagate local: no uses");
continue
}
if src_use_info.use_count() != 1 {
Copy link
Contributor

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

Copy link
Member

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?

Copy link
Contributor

@arielb1 arielb1 Sep 11, 2016

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.

@eddyb
Copy link
Member

eddyb commented Sep 10, 2016

cc @rust-lang/compiler

}

/// Returns true if this lvalue context represents a mutation (def).
pub fn is_def(&self) -> bool {
Copy link
Contributor

@arielb1 arielb1 Sep 11, 2016

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.

Copy link
Contributor

@nikomatsakis nikomatsakis Sep 15, 2016

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.

Copy link
Contributor Author

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?

Copy link
Contributor

@arielb1 arielb1 Sep 16, 2016

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.

Copy link
Contributor

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

@pcwalton pcwalton changed the title Implement def-use chains and trivial copy propagation on MIR. librustc_mir: Implement def-use chains and trivial copy propagation on MIR. Sep 15, 2016
@pcwalton pcwalton force-pushed the copy-propagation branch 2 times, most recently from b489231 to 9173e9e Compare September 16, 2016 01:20
Copy link
Contributor

@nikomatsakis nikomatsakis left a 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),
Copy link
Contributor

@nikomatsakis nikomatsakis Sep 15, 2016

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>> {
Copy link
Contributor

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)
Copy link
Contributor

@nikomatsakis nikomatsakis Sep 15, 2016

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> {
Copy link
Contributor

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
Copy link
Contributor

@nikomatsakis nikomatsakis Sep 15, 2016

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 be Y. So we look for a variable Y 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.

@bors
Copy link
Contributor

bors commented Sep 16, 2016

☔ The latest upstream changes (presumably #36353) made this pull request unmergeable. Please resolve the merge conflicts.

@nikomatsakis
Copy link
Contributor

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
Copy link
Contributor

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 <=?

Copy link
Contributor

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.
@pcwalton pcwalton force-pushed the copy-propagation branch 2 times, most recently from 2eec6e1 to fb2fb68 Compare September 20, 2016 02:16
This only supports trivial cases in which there is exactly one def and
one use.
@pcwalton
Copy link
Contributor Author

Updated. All tests pass locally. This should be ready to go.

Please see the fix at 480287e#diff-033ea10cb9e9d0a0b2eb95445ea25502R125.

r? @nikomatsakis

@rust-highfive rust-highfive assigned nikomatsakis and unassigned eddyb Sep 20, 2016
@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Sep 20, 2016

📌 Commit 480287e has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Sep 20, 2016

⌛ Testing commit 480287e with merge c772948...

bors added a commit that referenced this pull request Sep 20, 2016
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
@bors bors merged commit 480287e into rust-lang:master Sep 20, 2016
@pcwalton pcwalton deleted the copy-propagation branch September 20, 2016 22:08
@nagisa nagisa mentioned this pull request Mar 15, 2017
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.

8 participants