Add intrinsics for integer minimum and maximum - #161081
Conversation
| #[miri::fallback_is_spec] | ||
| pub const fn integer_min<T: Copy + [const] PartialOrd>(a: T, b: T) -> T { | ||
| if a < b { a } else { b } | ||
| } |
There was a problem hiding this comment.
cc @RalfJung because I said miri::fallback_is_spec
There was a problem hiding this comment.
Odd, why did the bot not ping me...
... ah, because it's a draft. :)
There was a problem hiding this comment.
Yeah, because I know my odds of getting a working PR first push are about 2% on a good day 🙃
EDIT: oh, and also I managed to spell it wrong 🤦
There was a problem hiding this comment.
LGTM apart from the comment nit. :)
This comment has been minimized.
This comment has been minimized.
0392eb2 to
2560466
Compare
This comment has been minimized.
This comment has been minimized.
2a35cda to
56bf46d
Compare
|
Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr
cc @bjorn3 Any special-casing of Miri in the standard library requires review. cc @rust-lang/miri
cc @rust-lang/miri |
|
r? @clarfonthey rustbot has assigned @clarfonthey. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
Given that LLVM can collapse all the mess this probably won't show much of a difference, but might as well make sure it's at least not worse somehow |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Add intrinsics for integer minimum and maximum
| #[rustc_nounwind] | ||
| #[rustc_intrinsic] | ||
| #[miri::intrinsic_fallback_is_spec] | ||
| pub const fn integer_min<T: Copy + [const] PartialOrd>(a: T, b: T) -> T { |
There was a problem hiding this comment.
The name says integer but the type signature does not. If there are constraints on the type that go beyond the signature, please spell them out in the doc comment.
There was a problem hiding this comment.
Do you have thoughts about doing it via a trait?
Like we have FloatPrimitive I could add IntegerPrimitive and bound it that way.
That exists for fallbacks more than for just type checking, though, so I don't know if it's worth doing here vs just documenting it.
Edit: Oh, actually, I think I might as well do that because then the Ord requirement can come from that trait instead of listing it out.
There was a problem hiding this comment.
Done, with both a bound and a doc-comment update.
|
I'm happy with this and would be fine merging once you make whatever fallback changes you were mentioning. I'm not on the compiler team, but just from the perspective of optimising the giant pile of operations we have in libstd, adding more intrinsics for "obvious" primitives like this is fine, even if they're technically redundant. For example, #161069 which also fell under my review recommended adding some potential additional reasoning for |
|
|
||
| other => { | ||
| tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other }); | ||
| tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other, file: file!() }); |
There was a problem hiding this comment.
I can definitely tell that this was motivated by you forgetting which file this was when you added this intrinsic. :p
There was a problem hiding this comment.
Yeah, this is where span_bug! might be better than emit_err. ;)
I don't know why we bother with "pretty" errors for intrinsic misuse anyway...
|
Curious, the build didn't get queued |
This comment has been minimized.
This comment has been minimized.
|
@rust-timer build 8764a9b |
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (8764a9b): comparison URL. Overall result: ❌✅ regressions and improvements - no action neededBenchmarking means the PR may be perf-sensitive. Consider adding rollup=never if this change is not fit for rolling up. @rustbot label: -S-waiting-on-perf -perf-regression Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary -0.9%, secondary -0.7%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (secondary 1.7%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary -0.0%, secondary -0.0%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 461.184s -> 456.796s (-0.95%) |
7e7bd40 to
31b0b44
Compare
31b0b44 to
564405d
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Add intrinsics for integer minimum and maximum
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (c92286c): comparison URL. Overall result: ❌✅ regressions and improvements - please read:Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf. Next, please: If you can, justify the regressions found in this try perf run in writing along with @bors rollup=never rustc-perf Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary -0.9%, secondary -1.4%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary -2.2%, secondary 4.0%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary -0.0%, secondary -0.0%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 457.537s -> 456s (-0.34%) |
|
Perf changes look mostly like the kind of thing that makes sense from better inlining: reduced Overall perf seems perhaps slightly better, and the bootstrap being green in both runs is also promising. |
View all comments
I got inspired to do this when looking at
SliceOrd::comparewhere I was reminded thatif a < b { a } else { b }isn't great in MIR since it takes 4 BBs. Looking at the codegen side, it turns out we currently emit 42 lines of LLVM-IR including 4allocas foru16::max(pre-optimization), which is also unnecessarily bad†.But both LLVM and Cranelift have dedicated things for min & max:
so let's just use those directly!
This actually wouldn't have been worth doing originally, but a couple of things have happened to change that:
cmp::min&cmp::max, so there was no place to actually do this at all, but in 2017 they were added toOrdas overridable things Tracking issue for Ord::{min, max} #25663 (comment)allocas in codegen anyway, but now we can keep the result in SSA without needing to make it a primitive.† Admittedly we could clean up the gratuitous badness there without needing an intrinsic, but I like doing the intrinsic anyway because that's the only way to avoid it always being stuck in the non-SSA path from the multi-BB assignments. Even if we made it inlineable, GVN and such will still just give up on seeing the
x = if a < b { a } else { b }because it's multiple assignments to the same Local, which is non-ideal for something primitive-like.Done without LLMs.