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] Refine representation and translation of calls #30481

Merged
merged 13 commits into from
Jan 6, 2016

Conversation

nagisa
Copy link
Member

@nagisa nagisa commented Dec 19, 2015

r? @nikomatsakis

This is a pretty big PR conflating changes to a few different block terminators (Call, DivergingCall, Panic, Resume, Diverge), because they are somewhat closely related.

Each commit has a pretty good description on what is being changed in each commit. The end result is greatly simplified CFG and translation for calls (no success branch if the function is diverging, no cleanup branch if there’s nothing to cleanup etc).

Fixes #30480
Fixes #29767
Partialy solves #29575
Fixes #29573

match self.unreachable_block {
Some(b) => b,
None => {
let bl = self.bcx(mir::START_BLOCK).fcx.new_block(false, "unreachable", None);
Copy link
Member Author

Choose a reason for hiding this comment

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

Note to self: maybe self should contain a fcx too?

@nagisa nagisa changed the title Refine representation and translation of MIR calls [MIR] Refine representation and translation of calls Dec 19, 2015
@nagisa nagisa force-pushed the mir-calls-2 branch 3 times, most recently from 0f066dc to fdaa56a Compare December 21, 2015 19:58
@nagisa
Copy link
Member Author

nagisa commented Dec 21, 2015

All in all, I think it is in a good enough spot now to begin reviewing it @nikomatsakis.

Some(attrs),
debugloc);
if must_copy_dest {
// FIXME: What do we do here?
Copy link
Member

Choose a reason for hiding this comment

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

AFAIK should just do the same as below: store_ty the result from Invoke to the destination.

Copy link
Member Author

Choose a reason for hiding this comment

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

The problem here (is much harder than it looks at the first look!) is that invoke is a basic block terminator and thus we cannot add anything else to the current block. We would have to add, or rather, prepend a store instruction to a success branch, but… that’s tricky, because:

  1. That’s hacky (duh);
  2. I can’t seem to see anyway to do it;
  3. It would make some already-generated blocks invalid (e.g. we would have to assert that success branch has higher basic-block number and has not been translated yet).

Basically, to me it seems like we should change the MIR some more and figure out if destination must be copied (or rather, avoid copying destination altogether as well as encoding the copy, if necessary) during the MIR build phase.


EDIT: we could also generate a temporary block with a store and branch to success block in it (still doesn’t avoid 3rd issue). Sounds like a most correct hack to do, but I’ll see if anybody has better ideas.

@nikomatsakis
Copy link
Contributor

As I mentioned on IRC, I'm not sure if I think that splitting out "diverging call" into its own terminator is a good idea. My concerns are:

  1. Duplication: it's harder to know that you're getting all calls.
  2. If you have foo = diverging_fn(), we need to still consider that a "write" to foo in a logical sense (i.e., for the purposes of borrowck). To do that under the approach of this PR, we'll need some marker that foo has been initialized, since DivergingCall no longer has a destination.

Basically, I'm not sure if the code for handling calls vs diverging calls will wind up being so very different in other passes. But it's a bit hard to know just now. A compromise might just be to introduce an enum into the call that is either NormalCall(BasicBlock, BasicBlock) or DivergingCall(BasicBlock).

In any case, let me read further before reaching final judgement.

@nagisa
Copy link
Member Author

nagisa commented Dec 22, 2015

As I mentioned on IRC, I'm not sure if I think that splitting out "diverging call" into its own terminator is a good idea. My concerns are:

The last commit contains conversion to inner-enum, like you proposed on IRC. Feel free to compare.

@nagisa
Copy link
Member Author

nagisa commented Dec 22, 2015

Comments on nagisa@0c7d57d are yet to be fixed.


EDIT: changed/fixed in nagisa@1c2f0d8. @nikomatsakis the code still looks equally ugly to me, but not sure if it could be made any better.

@bors
Copy link
Contributor

bors commented Jan 4, 2016

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

@nikomatsakis
Copy link
Contributor

In all those cases where I gave nits about exhaustive matches, we were just trying to handle converging cases -- might be that a helper function for extracting the converging cases would be good. OTOH, I guess this is why you split those cases into two to start with.

@nikomatsakis
Copy link
Contributor

OK, so, as you say, this is a big PR, and I feel like I could go on pointing out nits forever. But I'd rather we land it and just hack and try to clean things up. Some of these commits get pretty involved, but that may just be unavoidable if we want to generate less naive IR. Calls are just complicated. :) Anyway, so I'm leaning now towards landing this PR and then trying to keep improving things. One thing which would be nice are some more test cases for calls that generate invoke, and in particular to exercise some of those edge cases paths (like loop { c = call(); }).

@bors
Copy link
Contributor

bors commented Jan 4, 2016

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

@nagisa nagisa force-pushed the mir-calls-2 branch 3 times, most recently from e482e81 to d468ec9 Compare January 5, 2016 00:42
@nagisa
Copy link
Member Author

nagisa commented Jan 5, 2016

@nikomatsakis rebased and comments addressed (I hope I didn’t miss any). I added a few more tests as well.

@nagisa nagisa force-pushed the mir-calls-2 branch 2 times, most recently from 2e9e015 to 9e7ffb6 Compare January 5, 2016 15:18
@nikomatsakis
Copy link
Contributor

r=me modulo final nits (in particular, the test with graphviz attribute should really be fixed)

@bors
Copy link
Contributor

bors commented Jan 6, 2016

💔 Test failed - auto-win-msvc-64-opt

@bors
Copy link
Contributor

bors commented Jan 6, 2016

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

DivergingCall is different enough from the regular converging Call to warrant the split. This also
inlines CallData struct and creates a new CallTargets enum in order to have a way to differentiate
between calls that do not have an associated cleanup block.

Note, that this patch still does not produce DivergingCall terminator anywhere. Look for that in
the next patches.
This simplifies CFG greatly for some cases :)
Diverge should eventually go away
Unreachable terminator can be contained all within the trans.
It is useful for various cases where direct unreachable cannot be translated and a separate block
is necessary.
* Implement landing pads; and
* Implement DivergingCall translation; and
* Modernise previous implementation of Call somewhat.
This merges two separate Call terminators and uses a separate CallKind sub-enum instead.

A little bit unrelatedly, copying into destination value for a certain kind of invoke, is also
implemented here. See the associated comment in code for various details that arise with this
implementation.
This considerably simplifies code around calling functions and translation of Resume itself. This
removes requirement that a block containing Resume terminator is always translated after something
which creates a landing pad, thus allowing us to actually translate some valid MIRs we could not
translate before.

However, an assumption is added that translator is correct (in regards to landing pad generation)
and code will never reach the Resume terminator without going through a landing pad first. Breaking
these assumptions would pass an `undef` value into the personality functions.
@Manishearth
Copy link
Member

@bors r=nikomatsakis

@bors
Copy link
Contributor

bors commented Jan 6, 2016

📌 Commit 36b3951 has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Jan 6, 2016

⌛ Testing commit 36b3951 with merge d5ac1a1...

bors added a commit that referenced this pull request Jan 6, 2016
r? @nikomatsakis

This is a pretty big PR conflating changes to a few different block terminators (Call, DivergingCall, Panic, Resume, Diverge), because they are somewhat closely related.

Each commit has a pretty good description on what is being changed in each commit. The end result is greatly simplified CFG and translation for calls (no success branch if the function is diverging, no cleanup branch if there’s nothing to cleanup etc).

Fixes #30480
Fixes #29767
Partialy solves #29575
Fixes #29573
pub fn destination(&self) -> Option<Lvalue<'tcx>> {
match *self {
CallKind::Converging { ref destination, .. } |
CallKind::ConvergingCleanup { ref destination, .. } => Some(destination.clone()),
Copy link
Member

Choose a reason for hiding this comment

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

Why clone here instead of returning Option<&Lvalue<'tcx>> and letting the caller decide if it needs to clone?

Copy link
Member Author

Choose a reason for hiding this comment

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

There’s two reasons, mostly, I had in mind:

  1. Cloning Lvalue is very cheap most of the time (i.e. when Lvalue is not a Projection);
  2. There’s users who want &mut lvalue and there’s users who want &lvalue. Returning a value allows to make either one easier when pattern matching (ref vs ref mut).

On the hindsight, I think this might actually be a bug to return destination by value in some cases which I didn’t think about before, thus I’ll submit a patch in a moment making these return a mutable reference instead.

bors added a commit that referenced this pull request Jan 8, 2016
This just removes the `Some()` that appeared around terminators in MIR text output after #30481 (cc @nagisa). The graphviz is already fixed.

r? @eddyb
nagisa added a commit to nagisa/rust that referenced this pull request Jan 9, 2016
This just removes the `Some()` that appeared around terminators in MIR text output after rust-lang#30481 (cc @nagisa). The graphviz is already fixed.

r? @eddyb
steveklabnik added a commit to steveklabnik/rust that referenced this pull request Jan 9, 2016
This just removes the `Some()` that appeared around terminators in MIR text output after rust-lang#30481 (cc @nagisa). The graphviz is already fixed.

r? @eddyb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment