Skip to content

Add should_skip_bounds_check to MIR Builder#153602

Open
Bryntet wants to merge 1 commit intorust-lang:mainfrom
Bryntet:opt
Open

Add should_skip_bounds_check to MIR Builder#153602
Bryntet wants to merge 1 commit intorust-lang:mainfrom
Bryntet:opt

Conversation

@Bryntet
Copy link
Copy Markdown
Contributor

@Bryntet Bryntet commented Mar 9, 2026

View all comments

This PR attempts to improve compiletimes by improving MIR generation, with the hope of making it so the chosen codegen backend has to do less work (this also comes with the benefit of eliding some bounds checks in debug builds!)

take the following two playground examples, and see how they always emit a bounds check in MIR

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=a10561edd6a4a37a7326e785723aa95d

eliding in this case is sound because the literal is less than the length of the array (it's equivalent to doing the runtime bounds check, but at comp-time )

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=a791356ddb6293f01138632d6a7671af

in this case it's a bit less obvious why it's sound to always elide the bounds check, but since u8::MAX < 256 any u8 value can have its bounds elided

in the future, it might be nice to add support for pattern types here?

I have tested with a subset of rustc-perf tests locally and instruction count wise it seems to be an improvement, but probably a good idea to run a perf test anyways

@rustbot rustbot added 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. labels Mar 9, 2026
@Bryntet Bryntet force-pushed the opt branch 3 times, most recently from 315e492 to 1dc7eae Compare March 9, 2026 10:32
@Bryntet Bryntet marked this pull request as ready for review March 9, 2026 10:32
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 9, 2026
@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 Mar 9, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 9, 2026

r? @nnethercote

rustbot has assigned @nnethercote.
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: compiler
  • compiler expanded to 69 candidates
  • Random selection from 16 candidates

@Bryntet
Copy link
Copy Markdown
Contributor Author

Bryntet commented Mar 9, 2026

One question I have is: is it possible to get a circular recursion of ExprKind::Scope? this would ICE due to hitting recursion limit in my PR.

@JonathanBrouwer
Copy link
Copy Markdown
Contributor

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

rust-bors bot pushed a commit that referenced this pull request Mar 9, 2026
Add `should_skip_bounds_check` to MIR `Builder`
@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Mar 9, 2026
@nnethercote
Copy link
Copy Markdown
Contributor

nnethercote commented Mar 9, 2026

I'm not a MIR expert, I'll try someone I think knows more:

r? @saethlin

@rustbot rustbot assigned saethlin and unassigned nnethercote Mar 9, 2026
@JonathanBrouwer
Copy link
Copy Markdown
Contributor

with the hope of making it so the linker has to do less work

It would be LLVM that does less work, not the linker right?

@Bryntet
Copy link
Copy Markdown
Contributor Author

Bryntet commented Mar 9, 2026

It would be LLVM that does less work, not the linker right?

LLVM is a linker right? I tried to keep the language vague since cranelift and alternatives also exist, and improvement to MIR should be an improvement for all these backends. Maybe backend is the word I was looking for?

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@JonathanBrouwer
Copy link
Copy Markdown
Contributor

JonathanBrouwer commented Mar 9, 2026

It would be LLVM that does less work, not the linker right?

LLVM is a linker right? I tried to keep the language vague since cranelift and alternatives also exist, and improvement to MIR should be an improvement for all these backends. Maybe backend is the word I was looking for?

LLVM is a codegen backend just like cranelift, gcc, etc.
Codegen backends take crates or parts of crates and compile them to an .o ELF file.
Linkers (like lld, mold, wild) take all the seperately produced ELF files and link them together (and do some other magic)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can't say I'm intimately familiar with how LLVM works, so it would be good if someone could say that this changing in this way is ok?

Copy link
Copy Markdown
Contributor Author

@Bryntet Bryntet Mar 9, 2026

Choose a reason for hiding this comment

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

I assume this is fine though, because we don't read from these two pointers here, so it just being poisoned should be ok

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

and this changing would be due to it being in debug, where LLVM doesn't elide bounds checks

@rust-log-analyzer

This comment has been minimized.

@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors bot commented Mar 9, 2026

☀️ Try build successful (CI)
Build commit: 78a8d36 (78a8d36332a4b4054f62c90406b543c67784ea80, parent: 98e7077b903559d7a4fafb775cd5292cc9427b67)

@rust-timer

This comment has been minimized.

@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 Mar 16, 2026
@rust-log-analyzer

This comment has been minimized.

@Bryntet
Copy link
Copy Markdown
Contributor Author

Bryntet commented Mar 16, 2026

@rustbot author

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 16, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 16, 2026

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Mar 16, 2026
@Bryntet
Copy link
Copy Markdown
Contributor Author

Bryntet commented Mar 16, 2026

seems like the tests that fail in std all are failing in unsafe blocks, which makes me think that my code has done either of the following

  • is unsound
  • has changed memory layout in some way

@@ -0,0 +1,81 @@
//@ test-mir-pass: GVN
// EMIT_MIR_FOR_EACH_BIT_WIDTH
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.

I don't think you're doing anything here that depends on the actual width of usize, just that it's more than u8 which is of course always true. So you could probably use the same MIR on all bitwidths and save some files.

That or maybe you should have some such tests, like (usize::MAX as u64) > 5_000_000_000, that would behave differently between 32- and 64-bit?

ty::Uint(ty::UintTy::U16) => u16::MAX as u128,
ty::Uint(ty::UintTy::U32) => u32::MAX as u128,
ty::Uint(ty::UintTy::U64) => u64::MAX as u128,
ty::Uint(ty::UintTy::Usize) => usize::MAX as u128,
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.

fix: this is wrong; it's the host's usize but you need to be using the target usize.

Also, you shouldn't need to write out this match. Find a helper that already does it. (There might not be a direct one, but if you can get the Size there's https://doc.rust-lang.org/nightly/nightly-rustc/rustc_abi/struct.Size.html#method.unsigned_int_max.)

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.

...which answers my other comment too, and you should have things that care about that distinction in the tests.

@dianqk
Copy link
Copy Markdown
Member

dianqk commented Mar 17, 2026

Should you implement this in https://github.com/rust-lang/rust/blob/main/compiler/rustc_mir_transform/src/ssa_range_prop.rs?
Then we can let GVN still do what it does.

}

fn max_value_of_cast(&self, value: VnIndex) -> Option<u128> {
let Value::Cast { kind: CastKind::IntToInt, value } = self.get(value) else {
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.

Does this support continuous casts? Perhaps this can be a recusive function, like this: https://github.com/rust-lang/rust/pull/148443/changes#diff-6202eda9e5f3c8b8a291cf165920ea8484c372002e485eafa920b319f7cb4dd3R1515.
I haven't port this PR to SsaRangePropagation that should be more easy to implement.

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 20, 2026

Some changes occurred in compiler/rustc_ast_lowering/src/format.rs

cc @m-ou-se

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 20, 2026

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.

IntTy::Isize => self.tcx.data_layout.pointer_size().signed_int_max() as u128,
IntTy::Isize => self.tcx.target_isize_max() as u128,
IntTy::I8 => i8::MAX as u128,
IntTy::I16 => i16::MAX as u128,
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.

I still think you shouldn't need this match, by finding some method that'll do the right thing. Like if you can get to Integer the match would be replaced with https://doc.rust-lang.org/nightly/nightly-rustc/rustc_abi/enum.Integer.html#method.signed_max.

Or Size::from_bytes(int_ty.num_bytes()).signed_int_max() or something.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Whoops, this is actually an unrelated change to the PR that snuck in, this isn't the actual match I added but one I noticed existed while searching for the good way to get Size, like you said, I'll ping you in a comment on the cofe where I actually changed it for this PR

@scottmcm
Copy link
Copy Markdown
Member

running that perf run that was requested earlier:
@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

rust-bors bot pushed a commit that referenced this pull request Mar 20, 2026
Add `should_skip_bounds_check` to MIR `Builder`
@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Mar 20, 2026
@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors bot commented Mar 20, 2026

☀️ Try build successful (CI)
Build commit: 2568452 (2568452023d9d7ac15386d11d279faaf8ab37129, parent: 461e9738a47e313e4457957fa95ff6a19a4b88d4)

@rust-timer

This comment has been minimized.

@rust-timer
Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (2568452): comparison URL.

Overall result: ❌ regressions - no action needed

Benchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf.

@bors rollup=never
@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.5% [0.5%, 0.5%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.5% [0.5%, 0.5%] 1

Max RSS (memory usage)

Results (primary 2.3%)

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

mean range count
Regressions ❌
(primary)
2.3% [2.3%, 2.3%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 2.3% [2.3%, 2.3%] 1

Cycles

This benchmark run did not return any relevant results for this metric.

Binary size

Results (primary -0.2%, secondary -0.1%)

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)
- - 0
Improvements ✅
(primary)
-0.2% [-0.7%, -0.0%] 5
Improvements ✅
(secondary)
-0.1% [-0.1%, -0.0%] 5
All ❌✅ (primary) -0.2% [-0.7%, -0.0%] 5

Bootstrap: 488.876s -> 485.799s (-0.63%)
Artifact size: 396.98 MiB -> 396.96 MiB (-0.01%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Mar 20, 2026
if let Value::Cast { kind: CastKind::IntToInt, value } = self.get(value) {
self.max_value_of_cast(value)
} else if let ty::Uint(uty) = self.ty(value).kind() {
Some(Integer::from_uint_ty(&self.tcx, *uty).size().unsigned_int_max())
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Here is the actual Size::unsigned_int_max() call

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@dianqk
Copy link
Copy Markdown
Member

dianqk commented Mar 25, 2026

Should you implement this in https://github.com/rust-lang/rust/blob/main/compiler/rustc_mir_transform/src/ssa_range_prop.rs? Then we can let GVN still do what it does.

Just in case you may miss this. I think maintaining a range rather than a MAX value is more extensible. Using a range can handle signed and unsigned integers well. It also can handle other cases like Add/Sub, etc. I will resubmit #148443, but it's fine with me to implement a simpler (Cast) version compared to #148443 if you would like to do this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-MIR Area: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants