-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Eliminate UbCheck
for non-standard libraries
#122282
Conversation
Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt |
This comment has been minimized.
This comment has been minimized.
1fdfb9b
to
1357144
Compare
1357144
to
e6474b8
Compare
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
Eliminate `UbCheck` for non-standard libraries Since only core and std are prebuilt crates, I think it's only necessary to keep UB checks for them. `mir-opt` may not optimize some code due to `UbCheck`. In more complex cases, I'm also not sure that LLVM utilizes `assume` correctly. r? saethlin
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
I'd really like to have an example of this, if you think it's happening. I think this other PR of mine should ensure that LLVM never sees the checks when they are not enabled: #121421. But if the problem is that LLVM using I also tried a different kind of approach to eliminate the impact of the checks on MIR optimizations: #121767 I expected to see perf improvements as a result of getting the optimized MIR back to where it was before, but that didn't happen. Hopefully this approach works better 🤞 |
Finished benchmarking commit (acc30d0): comparison URL. Overall result: ❌✅ regressions and improvements - ACTION NEEDEDBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. Next Steps: If you can justify the regressions found in this try perf run, please indicate this with @bors rollup=never Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 648.121s -> 643.906s (-0.65%) |
The purpose of this PR come from rust-lang/hashbrown#509. Current LLVM does not handle assume well. (Of course there is a PR that is improving it.) Based on my discussion with @dtcxzyw offline, it would be easier for LLVM to handle instructions than intrinsic functions. We may miss out some optimization. If I can remove x.checked_add(y).unwrap_unchecked() |
Are you saying you're trying to write a MIR optimization or something like one so that rustc emits the LLVM IR that's easier for LLVM to optimize well, and So long as I understand that correctly I'm in favor of this, I just have the comments I left above (and much farther above). Usually attributes are more of a verb or a whole sentence, and |
e6474b8
to
e70f5e6
Compare
Yes. See https://rust.godbolt.org/z/YG6YrosEb. We can know
Thanks! I get it. |
Very nice. @bors r+ |
This is a MIR optimization, and we already deliberately use |
This will add checks in Miri when mir-opts are enabled, not remove them. I think it makes the situation wrt when the checks are actually run way too messy (there's like 3 or 4 places that together define the behavior of this and one has to know all of them to understand what happens, and even you seem to have gotten wrong how they will interact). That is why I suggested above how this could be done with less cross-compiler-entanglement. Or put differently, this is a mir-opt that fundamentally alters the semantics of these intrinsics, and I don't think their new semantics are a good choice. |
I would expect that A does not get a |
The entire point of these intrinsics is to still make A get UB checks in that case! Here, B is the standard library. If B doesn't want UbCheck in such cases it should just use |
IIUC, this is the current behavior of the master branch. This PR still retains this behavior. This PR will only work on BB inlined from std.
Maybe I got your point. :3 We should consider replacing |
No, the PR does not retain this behavior. MIR-opts will now use the flags of the crate containing the MIR to resolve the intrinsic (except in the standard library), rather than the crate doing the codegen. I'm confused why you would say this PR does not change behavior when the entire point of this PR is to change behavior. Currently, the behavior is as intended for all crates B. After this PR, it only works when B is the standard library. That's changing the documented contract of the intrinsic. The intrinsic is unstable so we can change the contract, but I'm saying the new contract is too fragile and non-local and I proposed an alternative that is IMO better.
That's just a rename, so it changes nothing about our discussion. The first step is to agree on what the intended behavior of the intrinsics should be. I like what they currently are. I understand they should be changed to make mir-opt more effective. I'm okay with changing them but not with the change proposed here, primarily because it can lead to Miri running checks that we document and intend for Miri to never run. The current behavior for
The new behavior is:
That's just not a sensible intrinsic IMO. It can be improved by changing the way Miri enters the picture: making Miri not part of how the intrinsic behaves but part of how it is used. As a side-benefit we also don't need two of these intrinsics any more. |
Indeed, I get it. Perhaps we can wait until |
I've opened a PR that implements the plan I laid out above: #122629. After that lands, this PR can be resurrected, it then just needs some proper docs changes in the |
… r=<try> refactor check_{lang,library}_ub: use a single intrinsics This enacts the plan I laid out [here](rust-lang#122282 (comment)): use a single intrinsic, called `ub_checks` (in aniticpation of rust-lang/compiler-team#725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like rust-lang#122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin`
… r=<try> refactor check_{lang,library}_ub: use a single intrinsics This enacts the plan I laid out [here](rust-lang#122282 (comment)): use a single intrinsic, called `ub_checks` (in aniticpation of rust-lang/compiler-team#725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like rust-lang#122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin`
… r=<try> refactor check_{lang,library}_ub: use a single intrinsics This enacts the plan I laid out [here](rust-lang#122282 (comment)): use a single intrinsic, called `ub_checks` (in aniticpation of rust-lang/compiler-team#725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like rust-lang#122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin`
… r=<try> refactor check_{lang,library}_ub: use a single intrinsic This enacts the plan I laid out [here](rust-lang#122282 (comment)): use a single intrinsic, called `ub_checks` (in aniticpation of rust-lang/compiler-team#725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like rust-lang#122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin`
… r=saethlin refactor check_{lang,library}_ub: use a single intrinsic This enacts the plan I laid out [here](rust-lang#122282 (comment)): use a single intrinsic, called `ub_checks` (in aniticpation of rust-lang/compiler-team#725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like rust-lang#122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin`
… r=saethlin refactor check_{lang,library}_ub: use a single intrinsic This enacts the plan I laid out [here](rust-lang#122282 (comment)): use a single intrinsic, called `ub_checks` (in aniticpation of rust-lang/compiler-team#725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like rust-lang#122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin`
I've tried to update the document based on the new changes. |
@bors try @rust-timer queue |
Awaiting bors try build completion. @rustbot label: +S-waiting-on-perf |
I can't reopen this PR because GitHub doesn't allow it. It looks like this The new PR is #122975. |
refactor check_{lang,library}_ub: use a single intrinsic This enacts the plan I laid out [here](rust-lang/rust#122282 (comment)): use a single intrinsic, called `ub_checks` (in aniticpation of rust-lang/compiler-team#725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like rust-lang/rust#122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin`
(If you close a PR and then push to a branch, you can't reopen. You can only reopen when the branch is at the exact same commit as when it was closed.) |
I see. Yes, this is the proper procedure. |
… r=saethlin refactor check_{lang,library}_ub: use a single intrinsic This enacts the plan I laid out [here](rust-lang#122282 (comment)): use a single intrinsic, called `ub_checks` (in aniticpation of rust-lang/compiler-team#725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like rust-lang#122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin`
refactor check_{lang,library}_ub: use a single intrinsic This enacts the plan I laid out [here](rust-lang/rust#122282 (comment)): use a single intrinsic, called `ub_checks` (in aniticpation of rust-lang/compiler-team#725), that just exposes the value of `debug_assertions` (consistently implemented in both codegen and the interpreter). Put the language vs library UB logic into the library. This makes it easier to do something like rust-lang/rust#122282 in the future: that just slightly alters the semantics of `ub_checks` (making it more approximating when crates built with different flags are mixed), but it no longer affects whether these checks can happen in Miri or compile-time. The first commit just moves things around; I don't think these macros and functions belong into `intrinsics.rs` as they are not intrinsics. r? `@saethlin`
I think it's only necessary to keep UB checks for core and std because only they are prebuilt crates,
mir-opt
may not optimize some code due toUbCheck
.The purpose of this PR is to allow other passes to treat
UbCheck
as constants in MIR for optimization.In more complex cases, I'm also not sure that LLVM utilizes
assume
correctly.r? saethlin