Add should_skip_bounds_check to MIR Builder#153602
Add should_skip_bounds_check to MIR Builder#153602Bryntet wants to merge 1 commit intorust-lang:mainfrom
should_skip_bounds_check to MIR Builder#153602Conversation
315e492 to
1dc7eae
Compare
|
r? @nnethercote rustbot has assigned @nnethercote. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
One question I have is: is it possible to get a circular recursion of |
|
@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 `should_skip_bounds_check` to MIR `Builder`
|
I'm not a MIR expert, I'll try someone I think knows more: r? @saethlin |
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? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
LLVM is a codegen backend just like cranelift, gcc, etc. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
I assume this is fine though, because we don't read from these two pointers here, so it just being poisoned should be ok
There was a problem hiding this comment.
and this changing would be due to it being in debug, where LLVM doesn't elide bounds checks
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@rustbot author |
|
Reminder, once the PR becomes ready for a review, use |
|
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
|
| @@ -0,0 +1,81 @@ | |||
| //@ test-mir-pass: GVN | |||
| // EMIT_MIR_FOR_EACH_BIT_WIDTH | |||
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
...which answers my other comment too, and you should have things that care about that distinction in the tests.
|
Should you implement this in https://github.com/rust-lang/rust/blob/main/compiler/rustc_mir_transform/src/ssa_range_prop.rs? |
| } | ||
|
|
||
| fn max_value_of_cast(&self, value: VnIndex) -> Option<u128> { | ||
| let Value::Cast { kind: CastKind::IntToInt, value } = self.get(value) else { |
There was a problem hiding this comment.
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.
|
Some changes occurred in compiler/rustc_ast_lowering/src/format.rs cc @m-ou-se |
|
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
|
running that perf run that was requested earlier: |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Add `should_skip_bounds_check` to MIR `Builder`
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (2568452): comparison URL. Overall result: ❌ regressions - no action neededBenchmarking 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 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 2.3%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesThis benchmark run did not return any relevant results for this metric. Binary sizeResults (primary -0.2%, secondary -0.1%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 488.876s -> 485.799s (-0.63%) |
| 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()) |
There was a problem hiding this comment.
Here is the actual Size::unsigned_int_max() call
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. |
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 < 256anyu8value can have its bounds elidedin 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