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-OPT: Less conservative EarlyOtherwiseBranch #77163

Conversation

simonvandel
Copy link
Contributor

It is not important that the types match in this optimization.
Instead, we can apply the optimization more liberally if the layouts match.

Locally when compiling stage 1 std, stage2 and stage 2 std this increases the times the optimization fires from 543 to 573.
A ~5% increase.

Also reorders checks so we check the cheapest first.

@rust-highfive
Copy link
Collaborator

r? @lcnr

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 24, 2020
@lcnr
Copy link
Contributor

lcnr commented Sep 24, 2020

r? @oli-obk

@rust-highfive rust-highfive assigned oli-obk and unassigned lcnr Sep 24, 2020
discr_info.type_adt_matched_on,
this_bb_discr_info.type_adt_matched_on
);
// The layouts of the two ADTs have to be equal for this optimization to apply
Copy link
Contributor

Choose a reason for hiding this comment

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

Please explain this a bit more. It will be very surprising for future readers. It's not actually the layouts we care about, but the discriminant ids corresponding to the variants that we're looking for

Copy link
Contributor

Choose a reason for hiding this comment

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

We do have all the information at compile-time, maybe we should use it to figure out whether the two variants of different types that we're looking at have the same discriminant as returned by the Discriminant rvalue. That would be even less conservative

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'm a bit unsure what part of Layout (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/abi/struct.Layout.html) actually needs to match between the two types for us to say that this optimization is valid.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you need to check anything in the layout, you can check each type for the discriminant values directly:

let index = match *op.layout.ty.kind() {
ty::Adt(adt, _) => {
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
}
ty::Generator(def_id, substs, _) => {
let substs = substs.as_generator();
substs
.discriminants(def_id, *self.tcx)
.find(|(_, var)| var.val == discr_bits)
}

If you do this, please deduplicate your logic with the logic in const eval

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, in what cases is it actually even necessary to check the type of the discriminants? We already check in find_discriminant_switch_pairing that the discriminant value of both switches match. See (of which the first check is redundant with the second)

// check that the value being matched on is the same. The
if this_bb_discr_info.targets_with_values.iter().find(|x| x.1 == value).is_none() {
trace!("NO: values being matched on are not the same");
return None;
}
// only allow optimization if the left and right of the tuple being matched are the same variants.
// so the following should not optimize
// ```rust
// let x: Option<()>;
// let y: Option<()>;
// match (x,y) {
// (Some(_), None) => {},
// _ => {}
// }
// ```
// We check this by seeing that the value of the first discriminant is the only other discriminant value being used as a target in the second switch
if !(this_bb_discr_info.targets_with_values.len() == 1
&& this_bb_discr_info.targets_with_values[0].1 == value)
{
trace!(
"NO: The second switch did not have only 1 target (besides otherwise) that had the same value as the value from the first switch that got us here"
);
return None;
}

So this code https://godbolt.org/z/vW3j4h can still be optimized as both the lhs and lhs of the match are of the same discriminant value (1).

Do you see any problems with simply removing the layout check?

Copy link
Contributor

Choose a reason for hiding this comment

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

so... to recap. If the discriminants of the two variants being checked are the same and a very specific value, then we apply the optimization. This sounds to me like we can generalize the optimization even further, since almost the same thing happens with

if x == 42 && y == 42 {
    99
} else {
    69
}

I wasn't able to produce the same MIR as we get with the match, but if we can come up with user-code that produces the same code without using a match, then we should think about generalizing it.

Anyway, back to this PR. Yea, we need neither the redundant value check nor the type/layout equality check, as these don't actually prevent miscompilations as far as I can tell

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For this PR: The newest commit removes the layout check and the redundant value check. So this should be ready for a review again.

For a future PR: Yeah true, your snippet does seem like something awfully lot like something this optimization could handle.

@simonvandel
Copy link
Contributor Author

Seems like this miscompiles when compiling stage2. I'll see if I can find a repro

@simonvandel simonvandel force-pushed the less-conservative-early-otherwise branch from b6b1492 to 2920f33 Compare September 30, 2020 20:58
@simonvandel
Copy link
Contributor Author

I (think) I fixed one miscompilation in the previous commit. However CI is not very happy. It fails quite early now compiling psm. It's probably another miscompilation which needs to be investigated. However, i'm not sure how to approach this. One idea was to binary search through optimization fuel until I find which function gets miscompiled and then dump the MIR for that function. But running RUSTFLAGS="-Z fuel=rustc=0" ./x.py build complains that i'm using a nightly Z flag on a beta compiler.

@bjorn3
Copy link
Member

bjorn3 commented Sep 30, 2020

Use RUSTFLAGS_NOT_BOOTSTRAP.

@bors
Copy link
Contributor

bors commented Oct 1, 2020

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

Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:

@rustbot modify labels: +S-waiting-on-review -S-waiting-on-author

It is not important that the types match in this optimization.
Instead, we can apply the optimization more liberally if the layouts match.

Locally when compiling stage 1 std, stage2 and stage 2 std this increases the times the optimization fires from 543 to 573.
A ~5% increase.
We only need to check that the discriminant value is the same
Fixes a miscompilation and generally simplifies the MIR generated
@simonvandel simonvandel force-pushed the less-conservative-early-otherwise branch from 2920f33 to 67a7013 Compare October 1, 2020 21:29
@simonvandel
Copy link
Contributor Author

I added back the block since that is definitely needed to disambiguate that we don't jump to the wrong case (and added some comments so I won't remove it again).

CI is still not pleased, so I'll stare some more at tests this week to figure out what is miscompiling.

@simonvandel
Copy link
Contributor Author

It's still not clear to me what is miscompiling. I couldn't find any examples that are not handled correct, so at this point I don't know how to debug this. The stage2 compiler (or stage1 std) probably gets miscompiled which causes rustc to do funny stuff when running tests.

@tmiasko
Copy link
Contributor

tmiasko commented Oct 3, 2020

#[derive(Default)]
pub struct Matrix {
   rows: Vec<Option<u32>>,
}

impl Matrix {
   fn get(&self, row: usize) -> Option<&u32> {
       if let Some(Some(row)) = self.rows.get(row) { Some(row) } else { None }
   }
}

fn main() {
   let m  = Matrix::default();
   println!("{:?}", m.get(0));
}

@simonvandel
Copy link
Contributor Author

Hi @tmiasko
Thanks! I'm curious, what was your process arriving at this repro?

@tmiasko
Copy link
Contributor

tmiasko commented Oct 4, 2020

@simonvandel I started by reviewing the code. There were two things that caught my attention:

  1. Code commented with "assume this is the last statement of the block".
  2. Hoisting the load of discriminant from one place to another.

I briefly tried to construct an example for the latter, but without possibility of constructing an arbitrary MIR failed to do so. Then I run rustc under gdb to find out where it crashed. The code looked like something that would be misoptimized, so by this point I was pretty sure I had a reproducer.

pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> {
if let Some(Some(row)) = self.rows.get(row) { Some(row) } else { None }
}

@bors
Copy link
Contributor

bors commented Oct 13, 2020

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

Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:

@rustbot modify labels: +S-waiting-on-review -S-waiting-on-author

@simonvandel
Copy link
Contributor Author

I'm closing this, since I won't be driving it further soon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants