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.
|
Finished benchmarking commit (78a8d36): comparison URL. Overall result: ✅ improvements - 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 -1.5%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary 1.1%, secondary -2.0%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary -0.1%, secondary -0.4%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 479.865s -> 478.813s (-0.22%) |
|
|
||
| fn should_skip_bounds_check(&self, slice: Place<'tcx>, index: ExprId) -> bool { | ||
| let slice_ty = slice.ty(&self.local_decls, self.tcx); | ||
| fn check_kind<'tcx>( |
There was a problem hiding this comment.
I think it would be good to give this function a name that documents what it does, such as expr_always_less_than_len(). “kind” doesn’t say much because, the kind of what?
| fn check_kind<'tcx>( | ||
| thir: &Thir<'tcx>, | ||
| tcx: TyCtxt<'tcx>, | ||
| source: ExprId, |
There was a problem hiding this comment.
How about calling this something like index_expr_id?
| ) -> bool { | ||
| match thir[source].kind { | ||
| ExprKind::Cast { source } => match thir[source].ty.kind() { | ||
| ty::Int(int_ty) => match int_ty { |
There was a problem hiding this comment.
I don’t think this is correct. Assuming I am correct that this corresponds to a Rust expression like array[index as usize], such a cast can produce large usize, because the cast is sign-extending:
fn main() {
let arr = [0; 256];
arr[(-1i16) as usize];
}error: this operation will panic at runtime
--> src/main.rs:3:5
|
3 | arr[(-1i16) as usize];
| ^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the length is 256 but the index is usize::MAX
| array_ty: Ty<'tcx>, | ||
| ) -> bool { | ||
| match thir[source].kind { | ||
| ExprKind::Cast { source } => match thir[source].ty.kind() { |
There was a problem hiding this comment.
It would be nice if the same optimization was available for infallible conversions through array[usize::from(index)], which are otherwise superior when starting with u8 or u16. I imagine that is much harder to implement, though.
| _ => false, | ||
| }, | ||
| ExprKind::Scope { value, .. } => check_kind(thir, tcx, value, array_len, array_ty), | ||
| ExprKind::Literal { lit, .. } if let LitKind::Int(num, _) = lit.node => { |
There was a problem hiding this comment.
This could be combined into a single pattern instead of using a guard.
| ExprKind::Literal { lit, .. } if let LitKind::Int(num, _) = lit.node => { | |
| ExprKind::Literal { lit: Lit { node: LitKind::Int(num, _), .. }, .. } => { |
| array_len: u128, | ||
| array_ty: Ty<'tcx>, | ||
| ) -> bool { | ||
| match thir[source].kind { |
There was a problem hiding this comment.
You have a case for literal values, and a case for casts, but the case for casts doesn’t take advantage of the fact that the cast might be of a literal, like character_table[b'x' as usize], which seems like an easy opportunity to me. (I also notice that this code won’t look at named constants at all, but I assume resolving them to their values is nontrivial here.)
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