-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Likely unlikely fix #120370
base: master
Are you sure you want to change the base?
Likely unlikely fix #120370
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @cjgillot (or someone else) soon. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
|
This PR changes MIR cc @oli-obk, @RalfJung, @JakobDegen, @davidtwco, @celinval, @vakaras Some changes occurred in compiler/rustc_codegen_gcc Some changes occurred in compiler/rustc_codegen_cranelift cc @bjorn3 Some changes occurred to the CTFE / Miri engine cc @rust-lang/miri This PR changes Stable MIR cc @oli-obk, @celinval, @spastorino, @ouz-a Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt |
True, // condition is probably true | ||
False, // condition is probably false | ||
Unpredictable, |
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.
Please make these doc comments, and also explain the last variant.
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.
done
// 'then' branch weight | ||
self.cx.const_u32(if expect == ExpectKind::True { 2000 } else { 1 }), | ||
// 'else' branch weight | ||
self.cx.const_u32(if expect == ExpectKind::True { 1 } else { 2000 }), |
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.
Why 2000?
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 is what Clang emits. I will add a comment
} | ||
sym::unlikely => { | ||
self.expect(args[0].immediate(), false) | ||
} |
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.
There are similar match arms here that can be cleaned up now.
EDIT: Ah, you already did. :)
@@ -70,6 +70,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | |||
let op_val = self.codegen_operand(bx, op); | |||
bx.assume(op_val.immediate()); | |||
} | |||
mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Expect(..)) => {} |
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.
Could you please also add this at
NonDivergingIntrinsic::Assume(_) => {} |
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. Normally, only the LLVM backend is build, right? Because I didn't get any compile time errors in GCC or Cranelift
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.
./x.py check
should check all backends. ./x.py build
only builds backends other than LLVM if you tell the build system to with the rust.codegen-backends
option in config.toml
.
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.
Thanks, I updated this and one more match
in Cranelift
Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
} | ||
} else { | ||
ExpectKind::None | ||
}; |
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: could this be a Option<mir::ExpectKind>
?
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.
Good point. Updated
@@ -328,10 +329,26 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | |||
let lltrue = helper.llbb_with_cleanup(self, target); | |||
let llfalse = helper.llbb_with_cleanup(self, targets.otherwise()); | |||
if switch_ty == bx.tcx().types.bool { | |||
let expect = if let Some(x) = self.mir[bb].statements.last() |
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 if the Expect
is not the last statement? This could happen because of MIR opts.
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.
Could it also be moved to a different BB? Or is that something worth considering?
I was also thinking it could be useful to emit a warning if there is Expect which cannot be paired with a branch.
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.
Updated to handle expect anywhere in a BB
@@ -429,6 +451,9 @@ pub enum NonDivergingIntrinsic<'tcx> { | |||
/// If the argument is `false`, this operation is equivalent to `TerminatorKind::Unreachable`. | |||
Assume(Operand<'tcx>), | |||
|
|||
// Denotes a call to the intrinsic functions `likely`, `unlikely` and `unpredictable`. | |||
Expect(Operand<'tcx>, ExpectKind), |
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.
For MIR opts:
- SimplifyLocals: should this statement be removed when
Operand
is an unused local? - DeadStoreElimination: when the operand is a local that is never read again?
- SimplifyConstCondition: when the operand if a constant?
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.
Opt passes updated
HashStable, | ||
TypeFoldable, | ||
TypeVisitable | ||
)] |
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: please split the derives in 2 lines.
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.
Do you mean like this?
#[derive(
Clone, TyEncodable, TyDecodable, Debug, PartialEq,
Hash, HashStable, TypeFoldable, TypeVisitable
)]
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 mean:
#[derive(Clone, TyEncodable, TyDecodable, Debug, PartialEq)]
#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
To avoid rustfmt using a dozen lines.
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.
Done
@rustbot ready |
There are merge commits (commits with multiple parents) in your changes. We have a no merge policy so these commits will need to be removed for this pull request to be merged. You can start a rebase with the following commands:
The following commits are merge commits: |
12a1bb3
to
2d49c41
Compare
This comment has been minimized.
This comment has been minimized.
@cjgillot I found I introduced a bug in simplify_branches.rs 🤦 It should be fixed now. Anyway, the number of affected files is getting quite big. If there is anything I can do to make the review easier, please let me know |
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.
small style nit (in case you're touching this code again, if you aren't it's fine to not implement it)
/// condition is probably true | ||
True, | ||
|
||
/// condition is probably false | ||
False, | ||
|
||
/// condition is unpredictable by hardware | ||
Unpredictable, |
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.
/// condition is probably true | |
True, | |
/// condition is probably false | |
False, | |
/// condition is unpredictable by hardware | |
Unpredictable, | |
/// Condition is probably true | |
True, | |
/// Condition is probably false | |
False, | |
/// Condition is unpredictable by hardware | |
Unpredictable, |
☔ The latest upstream changes (presumably #120500) made this pull request unmergeable. Please resolve the merge conflicts. |
e4cc72b
to
d31b045
Compare
This comment has been minimized.
This comment has been minimized.
8a5b474
to
436c4fd
Compare
library/core/src/intrinsics.rs
Outdated
pub const fn likely(b: bool) -> bool { | ||
b | ||
#[cfg(any(bootstrap, miri))] |
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.
There shouldn't be any need to special-case miri here, just bootstrap
is enough.
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.
Without the special-case, I'm getting error in tests/pass/shims/time-with-isolation.rs
It reports "The loop took around 13s" instead of 12. I'm not sure why that is, because likely/unlikely are not used anywhere in the test
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.
The next line in that test is:
println!("(It's fine for this number to change when you `--bless` this test.)")
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.
So it's ok to bless the new number? I didn't want to slow things down.
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.
Yes, it's fine.
#[cfg(not(bootstrap))] | ||
#[rustc_nounwind] | ||
#[miri::intrinsic_fallback_is_spec] | ||
#[cold] |
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 isn't marked as inline, so can you add a test to check that the call to cold_path
is properly eliminated from the output IR?
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 added CHECK-NOT
to the tests and verified it fails when the call is not eliminated
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 you have noticed the implementation requires that Rust's own inline pass does not inline an empty cold function, but the LLVM's inline pass does.
At the moment this works. If the behavior changed, we would have to make cold_path()
a real intrinsic. It would be a no-op that's ignored by all mir passes and backends, but is detected by the find_cold_blocks()
function.
I didn't implement this change yet in order to keep the pull request small and simple.
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.
You can just an an empty implementation of the intrinsic in compiler/rustc_codegen_ssa/src/mir/intrinsic.rs to avoid emitting a call to the cold_path
function during codegen.
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 added the empty implementation of cold_path()
to compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
I also removed the #[rustc_intrinsic]
from likely
and unlikely
. They are ordinary functions now. I found that Rust's inliner has several checks to avoid inlining intrinsics. But these functions should really be inlined.
Overall I like the cleanness of the design based on the |
This comment has been minimized.
This comment has been minimized.
The Miri subtree was changed cc @rust-lang/miri |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
☔ The latest upstream changes (presumably #122551) made this pull request unmergeable. Please resolve the merge conflicts. |
e1cb8ca
to
8096ed5
Compare
Rebased |
@cjgillot Are you still reviewing this or does this need to be reviewed by the compiler team? |
☔ The latest upstream changes (presumably #121614) made this pull request unmergeable. Please resolve the merge conflicts. |
8096ed5
to
d6396ee
Compare
☔ The latest upstream changes (presumably #130483) made this pull request unmergeable. Please resolve the merge conflicts. |
let true_cold = self.cold_blocks[target]; | ||
let false_cold = self.cold_blocks[otherwise]; |
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.
target_cold
and otherwise_cold
?
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.
Renamed
let lltrue = helper.llbb_with_cleanup(self, target); | ||
let llfalse = helper.llbb_with_cleanup(self, targets.otherwise()); | ||
let llfalse = helper.llbb_with_cleanup(self, otherwise); |
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.
Could we take the opportunity to rename those to lltarget
and llotherwise
?
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.
Renamed
let otherwise = targets.otherwise(); | ||
let true_cold = self.cold_blocks[target]; | ||
let false_cold = self.cold_blocks[otherwise]; | ||
let expect = if true_cold != false_cold { Some(false_cold) } else { None }; |
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 had to scratch my head for quite some time to understand why you wrote it this way. IIUC:
- if
true_cold == false_cold
, both branches have the same weight, so we don't emit anything; - if
false_cold
and1 => target
we expect to go tolltrue
, so weexpect
isSome(true)
; - ...
This definitely needs some comment.
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.
That is correct. Commend added
8c4f8fc
to
54da6f7
Compare
54da6f7
to
383de54
Compare
Rebased and squashed commits |
I edited the PR description to reflect the redesign |
☔ The latest upstream changes (presumably #132756) made this pull request unmergeable. Please resolve the merge conflicts. |
RFC 1131 ( #26179 ) added likely/unlikely intrinsics, but they have been broken for a while: #96276 , #96275 , #88767 . This PR tries to fix them.
Changes:
cold_path()
intrinsiclikely()
andunlikely()
changed to regular functions implemented usingcold_path()