Skip to content

Commit

Permalink
use unchecked mul to compute slice sizes
Browse files Browse the repository at this point in the history
...since slice sizes can't signed wrap

see https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html

> The total size len * mem::size_of::<T>() of the slice must be no larger than isize::MAX.
  • Loading branch information
erikdesjardins committed Jun 14, 2022
1 parent ca122c7 commit 50f6a9e
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 50f6a9e

Please sign in to comment.