Skip to content

Add intrinsics for integer minimum and maximum - #161081

Open
scottmcm wants to merge 1 commit into
rust-lang:mainfrom
scottmcm:min-max-intrinsics
Open

Add intrinsics for integer minimum and maximum#161081
scottmcm wants to merge 1 commit into
rust-lang:mainfrom
scottmcm:min-max-intrinsics

Conversation

@scottmcm

@scottmcm scottmcm commented Aug 14, 2026

Copy link
Copy Markdown
Member

View all comments

I got inspired to do this when looking at SliceOrd::compare where I was reminded that if 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 4 allocas for u16::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:

  • Back in 1.0 there was only cmp::min & cmp::max, so there was no place to actually do this at all, but in 2017 they were added to Ord as overridable things Tracking issue for Ord::{min, max} #25663 (comment)
  • LLVM originally used icmp+select for these, not a dedicated construct, but then added one and as of 2022 the intrinsic is fully usable https://www.npopov.com/2022/12/20/This-year-in-LLVM-2022.html#integer-minmax-intrinsics
  • Before we had intrinsic fallback this would have been more annoying to support everywhere -- GCC, 128-bit numbers on cg_clif, CTFE, anything out-of-tree -- but now that we can write the obvious fallback we don't need to worry about that.
  • The intrinsic would have helped less when it forced extra BBs and 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.

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. 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 Aug 14, 2026
Comment thread library/core/src/intrinsics/mod.rs Outdated
Comment on lines +1845 to +1848
#[miri::fallback_is_spec]
pub const fn integer_min<T: Copy + [const] PartialOrd>(a: T, b: T) -> T {
if a < b { a } else { b }
}

@scottmcm scottmcm Aug 14, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

cc @RalfJung because I said miri::fallback_is_spec

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Odd, why did the bot not ping me...

... ah, because it's a draft. :)

@scottmcm scottmcm Aug 14, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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 🤦

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM apart from the comment nit. :)

@rust-log-analyzer

This comment has been minimized.

@scottmcm
scottmcm force-pushed the min-max-intrinsics branch from 0392eb2 to 2560466 Compare August 14, 2026 08:07
@rust-log-analyzer

This comment has been minimized.

@scottmcm
scottmcm force-pushed the min-max-intrinsics branch 2 times, most recently from 2a35cda to 56bf46d Compare August 14, 2026 16:59
@scottmcm
scottmcm marked this pull request as ready for review August 14, 2026 18:25
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 14, 2026
@rustbot

rustbot commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter
gets adapted for the changes, if necessary.

cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr

rustc_codegen_cranelift is developed in its own repository. If possible, consider making this change to rust-lang/rustc_codegen_cranelift instead.

cc @bjorn3

Any special-casing of Miri in the standard library requires review.

cc @rust-lang/miri

⚠️ #[miri::intrinsic_fallback_is_spec] must only be used if the function actively checks for all UB cases,
and explores the possible non-determinism of the intrinsic.

cc @rust-lang/miri

@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Aug 14, 2026
@rustbot

rustbot commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

r? @clarfonthey

rustbot has assigned @clarfonthey.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: libs
  • libs expanded to 12 candidates
  • Random selection from JohnTitor, Mark-Simulacrum, clarfonthey, nia-e

@scottmcm

Copy link
Copy Markdown
Member Author

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
@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 14, 2026
rust-bors Bot pushed a commit that referenced this pull request Aug 14, 2026
Add intrinsics for integer minimum and maximum
Comment thread library/core/src/intrinsics/mod.rs Outdated
#[rustc_nounwind]
#[rustc_intrinsic]
#[miri::intrinsic_fallback_is_spec]
pub const fn integer_min<T: Copy + [const] PartialOrd>(a: T, b: T) -> T {

@RalfJung RalfJung Aug 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

View changes since the review

@scottmcm scottmcm Aug 14, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done, with both a bound and a doc-comment update.

@rust-bors

rust-bors Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 8764a9b (8764a9b9995264025b6234cf175d01db8c729849)
Base parent: d453bdd (d453bdd8f092d099bc336f0bda4163f809ad18e0)

Comment thread compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs Outdated
@clarfonthey

clarfonthey commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

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 checked_next_power_of_two and I was already a bit worried that the complexity of the operations involved would not optimise correctly, which I haven't yet verified. If the codegen backends support it, I think we should go ahead with these, especially if there's some extra empirical justification as there seems to be here.


other => {
tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });
tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other, file: file!() });

@clarfonthey clarfonthey Aug 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I can definitely tell that this was motivated by you forgetting which file this was when you added this intrinsic. :p

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

💯

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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

Comment thread compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@scottmcm

Copy link
Copy Markdown
Member Author

Curious, the build didn't get queued
@rust-timer queue

@rust-timer

This comment has been minimized.

@clarfonthey

Copy link
Copy Markdown
Contributor

@rust-timer build 8764a9b

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (8764a9b): comparison URL.

Overall result: ❌✅ regressions and improvements - no action needed

Benchmarking 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 count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.9% [0.9%, 0.9%] 1
Regressions ❌
(secondary)
1.5% [1.5%, 1.5%] 1
Improvements ✅
(primary)
-0.5% [-0.5%, -0.5%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.2% [-0.5%, 0.9%] 2

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.

mean range count
Regressions ❌
(primary)
2.6% [1.5%, 3.6%] 2
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-4.3% [-6.1%, -2.5%] 2
Improvements ✅
(secondary)
-0.7% [-0.7%, -0.7%] 1
All ❌✅ (primary) -0.9% [-6.1%, 3.6%] 4

Cycles

Results (secondary 1.7%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
6.7% [1.7%, 16.2%] 3
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-3.4% [-4.2%, -2.9%] 3
All ❌✅ (primary) - - 0

Binary size

Results (primary -0.0%, secondary -0.0%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
0.2% [0.1%, 0.3%] 7
Regressions ❌
(secondary)
0.5% [0.5%, 0.5%] 1
Improvements ✅
(primary)
-0.1% [-0.7%, -0.0%] 16
Improvements ✅
(secondary)
-0.1% [-0.2%, -0.0%] 14
All ❌✅ (primary) -0.0% [-0.7%, 0.3%] 23

Bootstrap: 461.184s -> 456.796s (-0.95%)
Artifact size: 396.50 MiB -> 396.41 MiB (-0.02%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 15, 2026
@scottmcm
scottmcm force-pushed the min-max-intrinsics branch 2 times, most recently from 7e7bd40 to 31b0b44 Compare August 15, 2026 03:25
@scottmcm scottmcm added I-lang-nominated Nominated for discussion during a lang team meeting. I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination labels Aug 15, 2026
@scottmcm
scottmcm force-pushed the min-max-intrinsics branch from 31b0b44 to 564405d Compare August 17, 2026 04:33
@rustbot

rustbot commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

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.

@scottmcm

Copy link
Copy Markdown
Member Author

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 17, 2026
rust-bors Bot pushed a commit that referenced this pull request Aug 17, 2026
Add intrinsics for integer minimum and maximum
@rust-bors

rust-bors Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: c92286c (c92286c8f9a943cfbc1adca426cf24e708a61c76)
Base parent: 2c39ff4 (2c39ff499469be916d4e45506d1afed69bbaddb7)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

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 @rustbot label: +perf-regression-triaged. If not, fix the regressions and do another perf run. Neutral or positive results will clear the label automatically.

@bors rollup=never rustc-perf
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.6% [0.3%, 0.9%] 2
Regressions ❌
(secondary)
1.5% [1.5%, 1.5%] 1
Improvements ✅
(primary)
-0.7% [-1.1%, -0.4%] 3
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -0.2% [-1.1%, 0.9%] 5

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.

mean range count
Regressions ❌
(primary)
1.3% [1.3%, 1.3%] 1
Regressions ❌
(secondary)
2.6% [2.4%, 2.9%] 2
Improvements ✅
(primary)
-1.2% [-2.9%, -0.5%] 7
Improvements ✅
(secondary)
-2.1% [-2.9%, -1.1%] 12
All ❌✅ (primary) -0.9% [-2.9%, 1.3%] 8

Cycles

Results (primary -2.2%, secondary 4.0%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
4.0% [1.8%, 6.1%] 2
Improvements ✅
(primary)
-2.2% [-2.2%, -2.2%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -2.2% [-2.2%, -2.2%] 1

Binary size

Results (primary -0.0%, secondary -0.0%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
0.2% [0.0%, 0.8%] 25
Regressions ❌
(secondary)
0.5% [0.5%, 0.5%] 1
Improvements ✅
(primary)
-0.4% [-3.4%, -0.0%] 12
Improvements ✅
(secondary)
-0.1% [-0.2%, -0.0%] 10
All ❌✅ (primary) -0.0% [-3.4%, 0.8%] 37

Bootstrap: 457.537s -> 456s (-0.34%)
Artifact size: 398.93 MiB -> 398.97 MiB (0.01%)

@rustbot rustbot added perf-regression Performance regression. and removed S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Aug 17, 2026
@scottmcm

Copy link
Copy Markdown
Member Author

Perf changes look mostly like the kind of thing that makes sense from better inlining: reduced is_mir_available calls, and permuted codegen schedules from different CGU partitioning that sometimes is better, sometimes worse.

Overall perf seems perhaps slightly better, and the bootstrap being green in both runs is also promising.
@rustbot label: +perf-regression-triaged

@rustbot rustbot added the perf-regression-triaged The performance regression has been triaged. label Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination I-lang-nominated Nominated for discussion during a lang team meeting. perf-regression Performance regression. perf-regression-triaged The performance regression has been triaged. 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.

6 participants