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

Likely unlikely fix #120370

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

x17jiri
Copy link

@x17jiri x17jiri commented Jan 26, 2024

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:

  • added a new cold_path() intrinsic
  • likely() and unlikely() changed to regular functions implemented using cold_path()

@rustbot
Copy link
Collaborator

rustbot commented Jan 26, 2024

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 (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jan 26, 2024
@rustbot
Copy link
Collaborator

rustbot commented Jan 26, 2024

This PR changes MIR

cc @oli-obk, @RalfJung, @JakobDegen, @davidtwco, @celinval, @vakaras

Some changes occurred in compiler/rustc_codegen_gcc

cc @antoyo, @GuillaumeGomez

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

Comment on lines 424 to 421
True, // condition is probably true
False, // condition is probably false
Unpredictable,
Copy link
Member

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.

Copy link
Author

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 }),
Copy link
Member

Choose a reason for hiding this comment

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

Why 2000?

Copy link
Author

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)
}
Copy link
Member

@RalfJung RalfJung Jan 26, 2024

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(..)) => {}
Copy link
Member

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(_) => {}
?

Copy link
Author

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

Copy link
Member

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.

Copy link
Author

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

@rustbot
Copy link
Collaborator

rustbot commented Jan 26, 2024

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

}
} else {
ExpectKind::None
};
Copy link
Contributor

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

Copy link
Author

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

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.

Copy link
Author

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.

Copy link
Author

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

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?

Copy link
Author

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

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.

Copy link
Author

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

Copy link
Contributor

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.

Copy link
Author

Choose a reason for hiding this comment

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

Done

@cjgillot cjgillot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 27, 2024
@x17jiri
Copy link
Author

x17jiri commented Jan 29, 2024

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jan 29, 2024
@rustbot rustbot added has-merge-commits PR has merge commits, merge with caution. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 7, 2024
@rustbot
Copy link
Collaborator

rustbot commented Feb 7, 2024

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:

$ # rebase
$ git rebase -i master
$ # delete any merge commits in the editor that appears
$ git push --force-with-lease

The following commits are merge commits:

@rustbot rustbot removed has-merge-commits PR has merge commits, merge with caution. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 7, 2024
@rust-log-analyzer

This comment has been minimized.

@x17jiri
Copy link
Author

x17jiri commented Feb 8, 2024

@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

Copy link
Member

@Noratrieb Noratrieb left a 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)

Comment on lines 416 to 421
/// condition is probably true
True,

/// condition is probably false
False,

/// condition is unpredictable by hardware
Unpredictable,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// 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,

@bors
Copy link
Contributor

bors commented Feb 16, 2024

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

@rust-log-analyzer

This comment has been minimized.

pub const fn likely(b: bool) -> bool {
b
#[cfg(any(bootstrap, miri))]
Copy link
Member

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.

Copy link
Author

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

Copy link
Member

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

Copy link
Author

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.

Copy link
Member

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]
Copy link
Member

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?

Copy link
Author

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

Copy link
Author

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.

Copy link
Member

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.

Copy link
Author

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.

@Amanieu
Copy link
Member

Amanieu commented Aug 1, 2024

Overall I like the cleanness of the design based on the cold_path intrinsic. Perhaps that is a way we could stabilize it through a hint in core::hint.

@rust-log-analyzer

This comment has been minimized.

@rustbot
Copy link
Collaborator

rustbot commented Aug 1, 2024

The Miri subtree was changed

cc @rust-lang/miri

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@Dylan-DPC Dylan-DPC added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 6, 2024
@bors
Copy link
Contributor

bors commented Aug 20, 2024

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

@x17jiri
Copy link
Author

x17jiri commented Aug 27, 2024

Rebased

@Amanieu
Copy link
Member

Amanieu commented Aug 28, 2024

@cjgillot Are you still reviewing this or does this need to be reviewed by the compiler team?

@bors
Copy link
Contributor

bors commented Sep 6, 2024

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

@bors
Copy link
Contributor

bors commented Sep 17, 2024

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

Comment on lines 380 to 381
let true_cold = self.cold_blocks[target];
let false_cold = self.cold_blocks[otherwise];
Copy link
Contributor

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?

Copy link
Author

Choose a reason for hiding this comment

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

Renamed

Comment on lines 383 to 384
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);
Copy link
Contributor

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?

Copy link
Author

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

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 and 1 => target we expect to go to lltrue, so we expect is Some(true);
  • ...

This definitely needs some comment.

Copy link
Author

@x17jiri x17jiri Sep 21, 2024

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

@cjgillot
Copy link
Contributor

@x17jiri sorry for the delay. Do you mind squashing the commits?

@Amanieu thanks. I could work this a second look by somebody more experienced with codegen.

@x17jiri
Copy link
Author

x17jiri commented Sep 21, 2024

Rebased and squashed commits

@x17jiri
Copy link
Author

x17jiri commented Sep 22, 2024

I edited the PR description to reflect the redesign

@bors
Copy link
Contributor

bors commented Nov 8, 2024

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

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. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.