Skip to content

Commit

Permalink
Rollup merge of rust-lang#98078 - erikdesjardins:uncheckedsize, r=pet…
Browse files Browse the repository at this point in the history
…rochenkov

Use unchecked mul to compute slice sizes

This allows LLVM to realize that `slice.len() > 0` iff `slice.len() * size_of::<T>() > 0`, allowing a branch on the latter to be folded into the former when dropping vecs and boxed slices, in some cases.

Fixes (partially) rust-lang#96497
  • Loading branch information
JohnTitor authored Jun 14, 2022
2 parents bedb059 + 50f6a9e commit d18d756
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 1 deletion.
7 changes: 6 additions & 1 deletion compiler/rustc_codegen_ssa/src/glue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// The info in this case is the length of the str, so the size is that
// times the unit size.
(
bx.mul(info.unwrap(), bx.const_usize(unit.size.bytes())),
// All slice sizes must fit into `isize`, so this multiplication cannot (signed) wrap.
// NOTE: ideally, we want the effects of both `unchecked_smul` and `unchecked_umul`
// (resulting in `mul nsw nuw` in LLVM IR), since we know that the multiplication
// cannot signed wrap, and that both operands are non-negative. But at the time of writing,
// `BuilderMethods` can't do this, and it doesn't seem to enable any further optimizations.
bx.unchecked_smul(info.unwrap(), bx.const_usize(unit.size.bytes())),
bx.const_usize(unit.align.abi.bytes()),
)
}
Expand Down
29 changes: 29 additions & 0 deletions src/test/codegen/issue-96497-slice-size-nowrap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// This test case checks that LLVM is aware that computing the size of a slice cannot wrap.
// The possibility of wrapping results in an additional branch when dropping boxed slices
// in some situations, see https://github.com/rust-lang/rust/issues/96497#issuecomment-1112865218

// compile-flags: -O
// min-llvm-version: 14.0

#![crate_type="lib"]

// CHECK-LABEL: @simple_size_of_nowrap
#[no_mangle]
pub fn simple_size_of_nowrap(x: &[u32]) -> usize {
// Make sure the shift used to compute the size has a nowrap flag.

// CHECK: [[A:%.*]] = shl nsw {{.*}}, 2
// CHECK-NEXT: ret {{.*}} [[A]]
core::mem::size_of_val(x)
}

// CHECK-LABEL: @drop_write
#[no_mangle]
pub fn drop_write(mut x: Box<[u32]>) {
// Check that this write is optimized out.
// This depends on the size calculation not wrapping,
// since otherwise LLVM can't tell that the memory is always deallocated if the slice len > 0.

// CHECK-NOT: store i32 42
x[1] = 42;
}

0 comments on commit d18d756

Please sign in to comment.