Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions compiler/rustc_codegen_ssa/src/size_of_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,37 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
}
ty::Slice(_) | ty::Str => {
let unit = layout.field(bx, 0);
// The info in this case is the length of the str, so the size is that
// times the unit size.
(
// All slice sizes must fit into `isize`, so this multiplication cannot
// wrap -- neither signed nor unsigned.
bx.unchecked_sumul(info.unwrap(), bx.const_usize(unit.size.bytes())),
bx.const_usize(unit.align.bytes()),
)
let unit_size = bx.const_usize(unit.size.bytes());
// The info is the length metadata, the total size is that times the unit size.
let length_meta = info.unwrap();

let size = match bx.const_to_opt_u128(unit_size, false) {
// When the unit size is 1 (like *const [u8]), the `mul nsw nuw` emitted doesn't
// help LLVM prove anything about the size, and the mul gets removed.
// We help it with an `assume` and not emitting the mul, since it's an identity.
Some(1) => {
let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
bx.assume_integer_range(
length_meta,
bx.type_isize(),
WrappingRange { start: 0, end: size_bound },
);
length_meta
}
// might as well optimize for 0 while we're here, any length times 0 unit size is 0
Some(0) => bx.const_usize(0),
// For a unit size of 2 or greater, the `mul nsw nuw` is sufficient for LLVM to
// understand the size bounds.
Some(2..) | None => {
// All object sizes must fit into `isize`, so this multiplication cannot
// wrap -- neither signed nor unsigned.
bx.unchecked_sumul(length_meta, unit_size)
}
};

let align = bx.const_usize(unit.align.bytes());

(size, align)
}
ty::Foreign(_) => {
// `extern` type. We cannot compute the size, so panic.
Expand Down
38 changes: 38 additions & 0 deletions tests/codegen-llvm/intrinsics/size_of_val_bounds_hint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled
//@ min-llvm-version: 23
// in older LLVM, the `assume` call stays around, newer versions are easier to check
// (but the old versions do still optimize to `ret i1 true`, just with an extra assume)

// Tests that `size_of_val_raw` on a slice with element size of one (*const [u8]) can be proven
// to have a size of <= isize::MAX by LLVM.
// The `mul nsw nuw` in size calculations was not sufficient when the element size was 1, so
// an `assume` is used in that case instead. Also an element size of 0 can always return 0.

#![feature(layout_for_ptr)]
#![crate_type = "lib"]

use std::mem::size_of_val_raw;

// CHECK-LABEL: elem_size_one
// CHECK: start
// CHECK-NEXT: ret i1 true
#[unsafe(no_mangle)]
pub fn elem_size_one(x: *const [u8]) -> bool {
unsafe { size_of_val_raw(x) <= isize::MAX.cast_unsigned() }
}

// CHECK-LABEL: elem_size_two
// CHECK: start
// CHECK-NEXT: ret i1 true
#[unsafe(no_mangle)]
pub fn elem_size_two(x: *const [u16]) -> bool {
unsafe { size_of_val_raw(x) <= isize::MAX.cast_unsigned() }
}

// CHECK-LABEL: elem_size_zero
// CHECK: start
// CHECK-NEXT: ret i1 true
#[unsafe(no_mangle)]
pub fn elem_size_zero(x: *const [()]) -> bool {
unsafe { size_of_val_raw(x) == 0 }
}
Loading