Skip to content

Commit 1931533

Browse files
TKanXscottmcm
andcommitted
perf(codegen): Eliminate size_of_val == 0 for non-ZST DSTs via nuw+assume
Co-authored-by: Scott McMurray <scottmcm@users.noreply.github.com>
1 parent 5fb2ff8 commit 1931533

4 files changed

Lines changed: 70 additions & 23 deletions

File tree

‎compiler/rustc_codegen_ssa/src/size_of_val.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
159159
// Furthermore, `align >= unsized_align`, and therefore we only need to do:
160160
// let full_size = (unsized_offset_unadjusted + unsized_size).align_to(full_align);
161161

162-
let full_size = bx.add(unsized_offset_unadjusted, unsized_size);
162+
let unrounded_size = bx.unchecked_suadd(unsized_offset_unadjusted, unsized_size);
163163

164164
// Issue #27023: must add any necessary padding to `size`
165165
// (to make it a multiple of `align`) before returning it.
@@ -173,10 +173,14 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
173173
// `(size + (align-1)) & -align`
174174
let one = bx.const_usize(1);
175175
let addend = bx.sub(full_align, one);
176-
let add = bx.add(full_size, addend);
176+
let add = bx.add(unrounded_size, addend);
177177
let neg = bx.neg(full_align);
178178
let full_size = bx.and(add, neg);
179179

180+
// `round_up(x, a) >= x` for pow2 `a` (#152788).
181+
let size_ge = bx.icmp(IntPredicate::IntUGE, full_size, unrounded_size);
182+
bx.assume(size_ge);
183+
180184
(full_size, full_align)
181185
}
182186
_ => bug!("size_and_align_of_dst: {t} not supported"),
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//@ compile-flags: -Copt-level=3 -Z merge-functions=disabled
2+
//@ min-llvm-version: 21
3+
//@ needs-deterministic-layouts
4+
5+
// Regression test for #152788.
6+
7+
#![crate_type = "lib"]
8+
9+
pub struct Foo<T: ?Sized>(pub [u32; 3], pub T);
10+
11+
// CHECK-LABEL: @size_of_val_dyn_not_zero
12+
#[no_mangle]
13+
pub fn size_of_val_dyn_not_zero(p: &Foo<dyn std::fmt::Debug>) -> bool {
14+
// CHECK: ret i1 false
15+
std::mem::size_of_val(p) == 0
16+
}
17+
18+
// CHECK-LABEL: @size_of_val_slice_u8_not_zero
19+
#[no_mangle]
20+
pub fn size_of_val_slice_u8_not_zero(p: &Foo<[u8]>) -> bool {
21+
// CHECK: ret i1 false
22+
std::mem::size_of_val(p) == 0
23+
}
24+
25+
// CHECK-LABEL: @size_of_val_slice_i32_not_zero
26+
#[no_mangle]
27+
pub fn size_of_val_slice_i32_not_zero(p: &Foo<[i32]>) -> bool {
28+
// CHECK: ret i1 false
29+
std::mem::size_of_val(p) == 0
30+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ compile-flags: -Copt-level=3 -Z merge-functions=disabled
2+
//@ min-llvm-version: 21
3+
4+
#![crate_type = "lib"]
5+
6+
pub trait Trait {
7+
fn f(&self);
8+
}
9+
10+
pub struct WrapperWithAlign1<T: ?Sized> {
11+
x: u8,
12+
y: T,
13+
}
14+
15+
pub struct Struct<W: ?Sized> {
16+
_field: i8,
17+
dst: W,
18+
}
19+
20+
// CHECK-LABEL: @eliminates_runtime_check_when_align_1
21+
#[no_mangle]
22+
pub fn eliminates_runtime_check_when_align_1(
23+
x: &Struct<WrapperWithAlign1<dyn Trait>>,
24+
) -> &WrapperWithAlign1<dyn Trait> {
25+
// CHECK: load [[USIZE:i[0-9]+]], {{.+}} !range {{![0-9]+}}
26+
// CHECK-NOT: llvm.umax
27+
// CHECK-NOT: icmp
28+
// CHECK-NOT: select
29+
// CHECK: ret
30+
&x.dst
31+
}

‎tests/codegen-llvm/dst-vtable-align-nonzero.rs‎

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,13 @@
33
#![crate_type = "lib"]
44
#![feature(core_intrinsics)]
55

6-
// This test checks that we annotate alignment loads from vtables with nonzero range metadata,
7-
// and that this allows LLVM to eliminate redundant `align >= 1` checks.
6+
// This test checks that we annotate alignment loads from vtables with nonzero range metadata.
7+
// The companion LLVM-21-only fold check lives in `dst-vtable-align-1-elide-check.rs`.
88

99
pub trait Trait {
1010
fn f(&self);
1111
}
1212

13-
pub struct WrapperWithAlign1<T: ?Sized> {
14-
x: u8,
15-
y: T,
16-
}
17-
1813
pub struct WrapperWithAlign2<T: ?Sized> {
1914
x: u16,
2015
y: T,
@@ -25,25 +20,12 @@ pub struct Struct<W: ?Sized> {
2520
dst: W,
2621
}
2722

28-
// CHECK-LABEL: @eliminates_runtime_check_when_align_1
29-
#[no_mangle]
30-
pub fn eliminates_runtime_check_when_align_1(
31-
x: &Struct<WrapperWithAlign1<dyn Trait>>,
32-
) -> &WrapperWithAlign1<dyn Trait> {
33-
// CHECK: load [[USIZE:i[0-9]+]], {{.+}} !range [[RANGE_META:![0-9]+]]
34-
// CHECK-NOT: llvm.umax
35-
// CHECK-NOT: icmp
36-
// CHECK-NOT: select
37-
// CHECK: ret
38-
&x.dst
39-
}
40-
4123
// CHECK-LABEL: @does_not_eliminate_runtime_check_when_align_2
4224
#[no_mangle]
4325
pub fn does_not_eliminate_runtime_check_when_align_2(
4426
x: &Struct<WrapperWithAlign2<dyn Trait>>,
4527
) -> &WrapperWithAlign2<dyn Trait> {
46-
// CHECK: [[X0:%[0-9]+]] = load [[USIZE]], {{.+}} !range [[RANGE_META]]
28+
// CHECK: [[X0:%[0-9]+]] = load [[USIZE:i[0-9]+]], {{.+}} !range [[RANGE_META:![0-9]+]]
4729
// CHECK: {{icmp|llvm.umax}}
4830
// CHECK: ret
4931
&x.dst

0 commit comments

Comments
 (0)