diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 52ffc321cbb6f..00e6c2512231f 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -159,7 +159,10 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Furthermore, `align >= unsized_align`, and therefore we only need to do: // let full_size = (unsized_offset_unadjusted + unsized_size).align_to(full_align); - let full_size = bx.add(unsized_offset_unadjusted, unsized_size); + // This is the unrounded size before alignment padding. It cannot exceed the + // rounded size, which itself cannot exceed `isize::MAX`. Thus the addition + // cannot overflow `isize::MAX`, let alone `usize::MAX`. + let unrounded_size = bx.unchecked_suadd(unsized_offset_unadjusted, unsized_size); // Issue #27023: must add any necessary padding to `size` // (to make it a multiple of `align`) before returning it. @@ -173,10 +176,18 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // `(size + (align-1)) & -align` let one = bx.const_usize(1); let addend = bx.sub(full_align, one); - let add = bx.add(full_size, addend); + let add = bx.add(unrounded_size, addend); let neg = bx.neg(full_align); let full_size = bx.and(add, neg); + // Alignment rounding can only increase the size, never decrease it: + // `round_up(x, a) >= x` for power-of-two `a`. With the `nuw` on the + // addition above, LLVM can therefore deduce + // `full_size >= unrounded_size >= offset`, which proves `full_size > 0` + // for types with a non-zero-sized prefix (#152788). + let size_ge = bx.icmp(IntPredicate::IntUGE, full_size, unrounded_size); + bx.assume(size_ge); + (full_size, full_align) } _ => bug!("size_and_align_of_dst: {t} not supported"), diff --git a/tests/codegen-llvm/dst-size-of-val-not-zst.rs b/tests/codegen-llvm/dst-size-of-val-not-zst.rs new file mode 100644 index 0000000000000..9db959cb64d8f --- /dev/null +++ b/tests/codegen-llvm/dst-size-of-val-not-zst.rs @@ -0,0 +1,36 @@ +//@ compile-flags: -Copt-level=3 -Z merge-functions=disabled +//@ needs-deterministic-layouts + +#![crate_type = "lib"] + +// Regression test for #152788: `size_of_val(p) == 0` should optimize to `false` +// for types whose statically-known prefix makes them clearly not ZSTs. +// +// This works because: +// 1. The `offset + unsized_size` addition has NUW+NSW, so LLVM knows +// `unrounded_size >= offset` +// 2. An `llvm.assume` tells LLVM `aligned_size >= unrounded_size` +// 3. Together: `aligned_size >= unrounded_size >= offset > 0` + +pub struct Foo(pub [u32; 3], pub T); + +// CHECK-LABEL: @size_of_val_dyn_not_zero +#[no_mangle] +pub fn size_of_val_dyn_not_zero(p: &Foo) -> bool { + // CHECK: ret i1 false + std::mem::size_of_val(p) == 0 +} + +// CHECK-LABEL: @size_of_val_slice_u8_not_zero +#[no_mangle] +pub fn size_of_val_slice_u8_not_zero(p: &Foo<[u8]>) -> bool { + // CHECK: ret i1 false + std::mem::size_of_val(p) == 0 +} + +// CHECK-LABEL: @size_of_val_slice_i32_not_zero +#[no_mangle] +pub fn size_of_val_slice_i32_not_zero(p: &Foo<[i32]>) -> bool { + // CHECK: ret i1 false + std::mem::size_of_val(p) == 0 +} diff --git a/tests/codegen-llvm/dst-vtable-align-nonzero.rs b/tests/codegen-llvm/dst-vtable-align-nonzero.rs index 2eee91876683c..e9bb1b5616138 100644 --- a/tests/codegen-llvm/dst-vtable-align-nonzero.rs +++ b/tests/codegen-llvm/dst-vtable-align-nonzero.rs @@ -30,9 +30,8 @@ pub struct Struct { pub fn eliminates_runtime_check_when_align_1( x: &Struct>, ) -> &WrapperWithAlign1 { - // CHECK: load [[USIZE:i[0-9]+]], {{.+}} !range [[RANGE_META:![0-9]+]] + // CHECK: load [[USIZE:i[0-9]+]] // CHECK-NOT: llvm.umax - // CHECK-NOT: icmp // CHECK-NOT: select // CHECK: ret &x.dst @@ -43,7 +42,7 @@ pub fn eliminates_runtime_check_when_align_1( pub fn does_not_eliminate_runtime_check_when_align_2( x: &Struct>, ) -> &WrapperWithAlign2 { - // CHECK: [[X0:%[0-9]+]] = load [[USIZE]], {{.+}} !range [[RANGE_META]] + // CHECK: load [[USIZE]] // CHECK: {{icmp|llvm.umax}} // CHECK: ret &x.dst @@ -52,7 +51,7 @@ pub fn does_not_eliminate_runtime_check_when_align_2( // CHECK-LABEL: @align_load_from_align_of_val #[no_mangle] pub fn align_load_from_align_of_val(x: &dyn Trait) -> usize { - // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] + // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META:![0-9]+]] core::mem::align_of_val(x) }