Skip to content

Commit 79d2461

Browse files
committed
Auto merge of rust-lang#122048 - erikdesjardins:inbounds, r=oli-obk
Use GEP inbounds for ZST and DST field offsets ZST field offsets have been non-`inbounds` since I made [this old layout change](https://github.com/rust-lang/rust/pull/73453/files#diff-160634de1c336f2cf325ff95b312777326f1ab29fec9b9b21d5ee9aae215ecf5). Before that, they would have been `inbounds` due to using `struct_gep`. Using `inbounds` for ZSTs likely doesn't matter for performance, but I'd like to remove the special case. DST field offsets have been non-`inbounds` since the alignment-aware DST field offset computation was first [implemented](erikdesjardins@a2557d4#diff-04fd352da30ca186fe0bb71cc81a503d1eb8a02ca17a3769e1b95981cd20964aR1188) in 1.6 (back then `GEPi()` would be used for `inbounds`), but I don't think there was any reason for it. Split out from rust-lang#121577 / rust-lang#121665. r? `@oli-obk` cc `@RalfJung` -- is there some weird situation where field offsets can't be `inbounds`? Note that it's fine for `inbounds` offsets to be one-past-the-end, so it's okay even if there's a ZST as the last field in the layout: > The base pointer has an in bounds address of an allocated object, which means that it points into an allocated object, or to its end. [(link)](https://llvm.org/docs/LangRef.html#getelementptr-instruction) For rust-lang/unsafe-code-guidelines#93, zero-offset GEP is (now) always `inbounds`: > Note that getelementptr with all-zero indices is always considered to be inbounds, even if the base pointer does not point to an allocated object. [(link)](https://llvm.org/docs/LangRef.html#getelementptr-instruction)
2 parents 9823f17 + e349900 commit 79d2461

File tree

3 files changed

+88
-9
lines changed

3 files changed

+88
-9
lines changed

compiler/rustc_codegen_ssa/src/mir/place.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
104104
let mut simple = || {
105105
let llval = if offset.bytes() == 0 {
106106
self.llval
107-
} else if field.is_zst() {
108-
// FIXME(erikdesjardins): it should be fine to use inbounds for ZSTs too;
109-
// keeping this logic for now to preserve previous behavior.
110-
bx.ptradd(self.llval, bx.const_usize(offset.bytes()))
111107
} else {
112108
bx.inbounds_ptradd(self.llval, bx.const_usize(offset.bytes()))
113109
};
@@ -168,8 +164,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
168164
debug!("struct_field_ptr: DST field offset: {:?}", offset);
169165

170166
// Adjust pointer.
171-
// FIXME(erikdesjardins): should be able to use inbounds here too.
172-
let ptr = bx.ptradd(self.llval, offset);
167+
let ptr = bx.inbounds_ptradd(self.llval, offset);
173168

174169
PlaceRef { llval: ptr, llextra: self.llextra, layout: field, align: effective_field_align }
175170
}

tests/codegen/dst-offset.rs

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//! This file tests that we correctly generate GEP instructions for DST
2+
//! field offsets.
3+
//@ compile-flags: -C no-prepopulate-passes -Copt-level=0
4+
5+
#![crate_type = "lib"]
6+
7+
#![feature(extern_types)]
8+
9+
use std::ptr::addr_of;
10+
11+
// Hack to get the correct type for usize
12+
// CHECK: @helper([[USIZE:i[0-9]+]] %_1)
13+
#[no_mangle]
14+
pub fn helper(_: usize) {
15+
}
16+
17+
struct Dst<T: ?Sized> {
18+
x: u32,
19+
y: u8,
20+
z: T,
21+
}
22+
23+
// CHECK: @dst_dyn_trait_offset(ptr align {{[0-9]+}} [[DATA_PTR:%.+]], ptr align {{[0-9]+}} [[VTABLE_PTR:%.+]])
24+
#[no_mangle]
25+
pub fn dst_dyn_trait_offset(s: &Dst<dyn Drop>) -> &dyn Drop {
26+
// The alignment of dyn trait is unknown, so we compute the offset based on align from the vtable.
27+
28+
// CHECK: [[SIZE_PTR:%[0-9]+]] = getelementptr inbounds {{.+}} [[VTABLE_PTR]]
29+
// CHECK: load [[USIZE]], ptr [[SIZE_PTR]]
30+
// CHECK: [[ALIGN_PTR:%[0-9]+]] = getelementptr inbounds {{.+}} [[VTABLE_PTR]]
31+
// CHECK: load [[USIZE]], ptr [[ALIGN_PTR]]
32+
33+
// CHECK: getelementptr inbounds i8, ptr [[DATA_PTR]]
34+
// CHECK-NEXT: insertvalue
35+
// CHECK-NEXT: insertvalue
36+
// CHECK-NEXT: ret
37+
&s.z
38+
}
39+
40+
// CHECK-LABEL: @dst_slice_offset
41+
#[no_mangle]
42+
pub fn dst_slice_offset(s: &Dst<[u16]>) -> &[u16] {
43+
// The alignment of [u16] is known, so we generate a GEP directly.
44+
45+
// CHECK: start:
46+
// CHECK-NEXT: getelementptr inbounds i8, {{.+}}, [[USIZE]] 6
47+
// CHECK-NEXT: insertvalue
48+
// CHECK-NEXT: insertvalue
49+
// CHECK-NEXT: ret
50+
&s.z
51+
}
52+
53+
#[repr(packed)]
54+
struct PackedDstSlice {
55+
x: u32,
56+
y: u8,
57+
z: [u16],
58+
}
59+
60+
// CHECK-LABEL: @packed_dst_slice_offset
61+
#[no_mangle]
62+
pub fn packed_dst_slice_offset(s: &PackedDstSlice) -> *const [u16] {
63+
// The alignment of [u16] is known, so we generate a GEP directly.
64+
65+
// CHECK: start:
66+
// CHECK-NEXT: getelementptr inbounds i8, {{.+}}, [[USIZE]] 5
67+
// CHECK-NEXT: insertvalue
68+
// CHECK-NEXT: insertvalue
69+
// CHECK-NEXT: ret
70+
addr_of!(s.z)
71+
}
72+
73+
extern {
74+
pub type Extern;
75+
}
76+
77+
// CHECK-LABEL: @dst_extern
78+
#[no_mangle]
79+
pub fn dst_extern(s: &Dst<Extern>) -> &Extern {
80+
// Computing the alignment of an extern type is currently unsupported and just panics.
81+
82+
// CHECK: call void @{{.+}}panic
83+
&s.z
84+
}

tests/codegen/zst-offset.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub fn helper(_: usize) {
1313
// CHECK-LABEL: @scalar_layout
1414
#[no_mangle]
1515
pub fn scalar_layout(s: &(u64, ())) {
16-
// CHECK: getelementptr i8, {{.+}}, [[USIZE]] 8
16+
// CHECK: getelementptr inbounds i8, {{.+}}, [[USIZE]] 8
1717
let x = &s.1;
1818
witness(&x); // keep variable in an alloca
1919
}
@@ -22,7 +22,7 @@ pub fn scalar_layout(s: &(u64, ())) {
2222
// CHECK-LABEL: @scalarpair_layout
2323
#[no_mangle]
2424
pub fn scalarpair_layout(s: &(u64, u32, ())) {
25-
// CHECK: getelementptr i8, {{.+}}, [[USIZE]] 12
25+
// CHECK: getelementptr inbounds i8, {{.+}}, [[USIZE]] 12
2626
let x = &s.2;
2727
witness(&x); // keep variable in an alloca
2828
}
@@ -34,7 +34,7 @@ pub struct U64x4(u64, u64, u64, u64);
3434
// CHECK-LABEL: @vector_layout
3535
#[no_mangle]
3636
pub fn vector_layout(s: &(U64x4, ())) {
37-
// CHECK: getelementptr i8, {{.+}}, [[USIZE]] 32
37+
// CHECK: getelementptr inbounds i8, {{.+}}, [[USIZE]] 32
3838
let x = &s.1;
3939
witness(&x); // keep variable in an alloca
4040
}

0 commit comments

Comments
 (0)