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

MIR cleanups and predecessor cache #34149

Merged
merged 9 commits into from
Jun 9, 2016

Conversation

arielb1
Copy link
Contributor

@arielb1 arielb1 commented Jun 7, 2016

This PR cleans up a few things in MIR and adds a predecessor cache to allow graph algorithms to be run easily.

r? @nikomatsakis

@arielb1 arielb1 force-pushed the remove-remove-dead-blocks branch 3 times, most recently from b74fdd6 to 72f2715 Compare June 7, 2016 19:59
//!
//! Here the block (`{ return; }`) has the return type `char`,
//! rather than `()`, but the MIR we naively generate still contains
//! the `_a = ()` write in the unreachable block "after" the return.
Copy link
Contributor

Choose a reason for hiding this comment

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

great comment =)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just moved it.

@nikomatsakis
Copy link
Contributor

I'm not keen on making the basic_blocks field private, for the reasons I listed, but the rest seems good.

@nikomatsakis
Copy link
Contributor

(I don't have a strong opinion on whether to keep removal of dead blocks separate or not.)

// fn should_run(Session) to check if pass should run?
fn dep_node(&self, def_id: DefId) -> DepNode<DefId> {
DepNode::MirPass(def_id)
}
fn name(&self) -> &str;
Copy link
Member

Choose a reason for hiding this comment

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

A default implementation of this could be ::std::intrinsics::type_name::<Self>(). With that as a default, I doubt many passes would like to manually implement this method.

@nikomatsakis
Copy link
Contributor

Regarding the privacy of basic_blocks:

First, there is an accessor that gives &mut access to the fields (here), so making the field private doesn't prevent anyone from invalidating the predecessors.

I think if we wanted to guarantee things, we would need to have the Predecessors struct store a reference to the Mir, so that it is frozen -- but that is perhaps overkill, and might be overly strict, since everything is frozen.

I'm not sure how much it is worth using the type system to prevent these sorts of bugs. It might be. Dynamic asserts or other things might also be effective.

What I had originally envisioned was maybe something where the Predecessors stores an &mut Mir, so that nobody gets any kind of access except via the Predecessors struct. The Predecessors can then expose accessors and mutators that know to invalidate the cache where necessary. For example, you might have fn statements(&mut self, bb: BasicBlock) -> &mut Vec<Statement> that does not have to invalidate the cache, and fn set_terminator(&mut self, bb: BasicBlock, term: Terminator) that can do a more targeted update rather than a complete invalidation. At worst you can have get_mir(&mut self) -> &mut Mir that just invalidates the cache before giving it to you, and maybe a fn into_results(self) -> (&mut Mir, IndexVec<BasicBlock, Vec<BasicBlock>>) that just gives you back the results and the mir, and lets you worry about it.

cc @scottcarr, who was also talking to me about this independently over IRC

@arielb1
Copy link
Contributor Author

arielb1 commented Jun 8, 2016

@nikomatsakis

See a later commit. The accessor invalidates the cache to prevent you from using it incorrectly.

I might also want to add a more complicated set of methods that does not invalidate the cache, but ATM all passes work in an analyze-commit pattern, except for simplify-cfg which handles everything on its own, which takes O(n) between calls to predecessors anyway.

@nikomatsakis
Copy link
Contributor

Yes, I stand corrected. I am trying to decide my revised opinion. =)

I like that we would cache between passes.

I dislike losing the exhaustive visitor code, but then I think that the danger of visitor falling out of date is probably lowest at this very high-level.

I imagine there will be other bits of analysis data that might be at a similar risk of going out of date, and which we might want to use a different pattern, but probably in those cases we can be careful instead.

Conclusion: I think I'm happy with this version for time being.

@nikomatsakis
Copy link
Contributor

@arielb1 r=me after rebase I think; @nagisa had a few nice suggestions you may want to incorporate as well

@arielb1
Copy link
Contributor Author

arielb1 commented Jun 8, 2016

added commits for the suggestions

@arielb1
Copy link
Contributor Author

arielb1 commented Jun 8, 2016

r? @nikomatsakis for the changes

@nikomatsakis
Copy link
Contributor

btw, something that @scottcarr was asking me last night -- do we expect the predecessor cache to really persist between passes? That is, do we have passes that don't make changes?

I figure the answer is that yes, we do expect it to persist, at least long term, for two reasons:

  1. There are a number of passes that do not make changes:
    • analysis passes early on
    • optimization passes that fail to trigger (like simplify-cfg after we've run it enough times)
  2. I presume we will add finer-grained mutation methods to do things like get mutable access to the list of statements for a block, in which case optimization passes can make changes without invalidating the overall graph structure.

On a related note, what other kinds of information may make sense to put in this cache. My rule of thumb was that it should contain analysis results that are properties of the graph structure and which more than one pass is interested in. Examples beyond predecessors might include dominators, post-dominators, loop interval trees, that sort of thing.

I guess there is no fundamental reason the results must be tied to the graph structure. I suggested that however because it ensures that the number of "setter" methods we might want are relatively limited:

  • basic_blocks_mut -- the most coarse-grained, lets you do anything
  • statements_mut(block) -- lets you alter statements for a block in any way, never affects graph structure
  • set_terminator(block, terminator) -- adjusts one terminator, possibly just "patches" cached info, if we ever deem that worthwhile

(In particular there is no reason to track whatever random changes are made to statements, since it cannot affect cached data that is tied to the graph structure.)

if seen.insert(succ.index()) {
worklist.push(*succ);
// we can't use mir.predecessors() here because that counts
// dead blocks, which we don't want to.
Copy link
Contributor

Choose a reason for hiding this comment

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

to be clear, we will be adjusting this count to keep it up to date, right?

I guess this code is simple enough it's fine to cut-and-paste. In theory, it might be nice if we could "re-use" some underlying predecessor abstraction independent from the cache.

@nikomatsakis
Copy link
Contributor

r=me for new commits

@nikomatsakis
Copy link
Contributor

(still needs rebase)

@arielb1
Copy link
Contributor Author

arielb1 commented Jun 9, 2016

@nikomatsakis

Sure there are - NoLandingPads does not do anything if there are no landing pads, and TypeckMir never changes anything. Actually, I was more worried about a single pass calling dominators multiple times at different abstraction levels.

Use it instead of a `panic` for inexhaustive matches and correct the
comment. I think we trust our match-generation algorithm enough to
generate these blocks, and not generating an `unreachable` means that
LLVM won't optimize `match void() {}` to an `unreachable`.
@arielb1 arielb1 force-pushed the remove-remove-dead-blocks branch from f0d40eb to f5b1ba6 Compare June 9, 2016 12:28
@arielb1
Copy link
Contributor Author

arielb1 commented Jun 9, 2016

@bors r=nikomatsakis

@bors
Copy link
Contributor

bors commented Jun 9, 2016

📌 Commit f5b1ba6 has been approved by nikomatsakis

@arielb1
Copy link
Contributor Author

arielb1 commented Jun 9, 2016

@bors r-

@arielb1
Copy link
Contributor Author

arielb1 commented Jun 9, 2016

@bors r=nikomatsakis

@arielb1 arielb1 force-pushed the remove-remove-dead-blocks branch from 944b183 to 4cfb145 Compare June 9, 2016 14:29
@arielb1
Copy link
Contributor Author

arielb1 commented Jun 9, 2016

@bors r=nikomatsakis

@bors
Copy link
Contributor

bors commented Jun 9, 2016

💡 This pull request was already approved, no need to approve it again.

@bors
Copy link
Contributor

bors commented Jun 9, 2016

📌 Commit 4cfb145 has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Jun 9, 2016

⌛ Testing commit 4cfb145 with merge 6b812b0...

bors added a commit that referenced this pull request Jun 9, 2016
MIR cleanups and predecessor cache

This PR cleans up a few things in MIR and adds a predecessor cache to allow graph algorithms to be run easily.

r? @nikomatsakis
@bors
Copy link
Contributor

bors commented Jun 9, 2016

💔 Test failed - auto-linux-64-opt-rustbuild

@arielb1 arielb1 force-pushed the remove-remove-dead-blocks branch from 4cfb145 to ce4fdef Compare June 9, 2016 18:48
@arielb1
Copy link
Contributor Author

arielb1 commented Jun 9, 2016

@bors r=nikomatsakis

silly Cargo.lock.

@bors
Copy link
Contributor

bors commented Jun 9, 2016

📌 Commit ce4fdef has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Jun 9, 2016

⌛ Testing commit ce4fdef with merge ee00760...

bors added a commit that referenced this pull request Jun 9, 2016
MIR cleanups and predecessor cache

This PR cleans up a few things in MIR and adds a predecessor cache to allow graph algorithms to be run easily.

r? @nikomatsakis
@bors bors merged commit ce4fdef into rust-lang:master Jun 9, 2016
bors added a commit that referenced this pull request Jun 27, 2016
[MIR] Add Dominators to MIR and Add Graph Algorithms

~~This PR assumes PR #34149 lands.~~

Add generic graph algorithms to rustc_data_structures.

Add dominators and successors to the ~~cache (that currently only holds predecessors).~~ `Mir`.
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.

5 participants