Skip to content

Commit 028e260

Browse files
Rollup merge of #122320 - erikdesjardins:vtable, r=nikic
Use ptradd for vtable indexing Extension of #121665. After this, the only remaining usages of GEP are [this](https://github.com/rust-lang/rust/blob/cd81f5b27ee00b49d413db50b5e6af871cebcf23/compiler/rustc_codegen_llvm/src/intrinsic.rs#L909-L920) kinda janky Emscription EH code, which I'll change in a future PR, and array indexing / pointer offsets, where there isn't yet a canonical `ptradd` form. (Out of curiosity I tried converting the latter to `ptradd(ptr, mul(size, index))`, but that causes codegen regressions right now.) r? `@nikic`
2 parents a11e6c3 + a7cd803 commit 028e260

File tree

4 files changed

+104
-17
lines changed

4 files changed

+104
-17
lines changed

compiler/rustc_codegen_ssa/src/base.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,11 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
165165
cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target));
166166

167167
if let Some(entry_idx) = vptr_entry_idx {
168-
let ptr_ty = cx.type_ptr();
169-
let ptr_align = cx.tcx().data_layout.pointer_align.abi;
170-
let gep = bx.inbounds_gep(
171-
ptr_ty,
172-
old_info,
173-
&[bx.const_usize(u64::try_from(entry_idx).unwrap())],
174-
);
175-
let new_vptr = bx.load(ptr_ty, gep, ptr_align);
168+
let ptr_size = bx.data_layout().pointer_size;
169+
let ptr_align = bx.data_layout().pointer_align.abi;
170+
let vtable_byte_offset = u64::try_from(entry_idx).unwrap() * ptr_size.bytes();
171+
let gep = bx.inbounds_ptradd(old_info, bx.const_usize(vtable_byte_offset));
172+
let new_vptr = bx.load(bx.type_ptr(), gep, ptr_align);
176173
bx.nonnull_metadata(new_vptr);
177174
// VTable loads are invariant.
178175
bx.set_invariant_load(new_vptr);

compiler/rustc_codegen_ssa/src/meth.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,24 @@ impl<'a, 'tcx> VirtualIndex {
2020
ty: Ty<'tcx>,
2121
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
2222
) -> Bx::Value {
23-
// Load the data pointer from the object.
23+
// Load the function pointer from the object.
2424
debug!("get_fn({llvtable:?}, {ty:?}, {self:?})");
25+
2526
let llty = bx.fn_ptr_backend_type(fn_abi);
27+
let ptr_size = bx.data_layout().pointer_size;
28+
let ptr_align = bx.data_layout().pointer_align.abi;
29+
let vtable_byte_offset = self.0 * ptr_size.bytes();
2630

2731
if bx.cx().sess().opts.unstable_opts.virtual_function_elimination
2832
&& bx.cx().sess().lto() == Lto::Fat
2933
{
3034
let typeid = bx
3135
.typeid_metadata(typeid_for_trait_ref(bx.tcx(), expect_dyn_trait_in_self(ty)))
3236
.unwrap();
33-
let vtable_byte_offset = self.0 * bx.data_layout().pointer_size.bytes();
3437
let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid);
3538
func
3639
} else {
37-
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
38-
let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
40+
let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset));
3941
let ptr = bx.load(llty, gep, ptr_align);
4042
bx.nonnull_metadata(ptr);
4143
// VTable loads are invariant.
@@ -53,9 +55,12 @@ impl<'a, 'tcx> VirtualIndex {
5355
debug!("get_int({:?}, {:?})", llvtable, self);
5456

5557
let llty = bx.type_isize();
56-
let usize_align = bx.tcx().data_layout.pointer_align.abi;
57-
let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
58-
let ptr = bx.load(llty, gep, usize_align);
58+
let ptr_size = bx.data_layout().pointer_size;
59+
let ptr_align = bx.data_layout().pointer_align.abi;
60+
let vtable_byte_offset = self.0 * ptr_size.bytes();
61+
62+
let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset));
63+
let ptr = bx.load(llty, gep, ptr_align);
5964
// VTable loads are invariant.
6065
bx.set_invariant_load(ptr);
6166
ptr

tests/codegen/dst-offset.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ struct Dst<T: ?Sized> {
2525
pub fn dst_dyn_trait_offset(s: &Dst<dyn Drop>) -> &dyn Drop {
2626
// The alignment of dyn trait is unknown, so we compute the offset based on align from the vtable.
2727

28-
// CHECK: [[SIZE_PTR:%[0-9]+]] = getelementptr inbounds {{.+}} [[VTABLE_PTR]]
28+
// CHECK: [[SIZE_PTR:%[0-9]+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]]
2929
// CHECK: load [[USIZE]], ptr [[SIZE_PTR]]
30-
// CHECK: [[ALIGN_PTR:%[0-9]+]] = getelementptr inbounds {{.+}} [[VTABLE_PTR]]
30+
// CHECK: [[ALIGN_PTR:%[0-9]+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]]
3131
// CHECK: load [[USIZE]], ptr [[ALIGN_PTR]]
3232

3333
// CHECK: getelementptr inbounds i8, ptr [[DATA_PTR]]

tests/codegen/vtable-upcast.rs

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//! This file tests that we correctly generate GEP instructions for vtable upcasting.
2+
//@ compile-flags: -C no-prepopulate-passes -Copt-level=0
3+
4+
#![crate_type = "lib"]
5+
#![feature(trait_upcasting)]
6+
7+
pub trait Base {
8+
fn base(&self);
9+
}
10+
11+
pub trait A : Base {
12+
fn a(&self);
13+
}
14+
15+
pub trait B : Base {
16+
fn b(&self);
17+
}
18+
19+
pub trait Diamond : A + B {
20+
fn diamond(&self);
21+
}
22+
23+
// CHECK-LABEL: upcast_a_to_base
24+
#[no_mangle]
25+
pub fn upcast_a_to_base(x: &dyn A) -> &dyn Base {
26+
// Requires no adjustment, since its vtable is extended from `Base`.
27+
28+
// CHECK: start:
29+
// CHECK-NEXT: insertvalue
30+
// CHECK-NEXT: insertvalue
31+
// CHECK-NEXT: ret
32+
x as &dyn Base
33+
}
34+
35+
// CHECK-LABEL: upcast_b_to_base
36+
#[no_mangle]
37+
pub fn upcast_b_to_base(x: &dyn B) -> &dyn Base {
38+
// Requires no adjustment, since its vtable is extended from `Base`.
39+
40+
// CHECK: start:
41+
// CHECK-NEXT: insertvalue
42+
// CHECK-NEXT: insertvalue
43+
// CHECK-NEXT: ret
44+
x as &dyn Base
45+
}
46+
47+
// CHECK-LABEL: upcast_diamond_to_a
48+
#[no_mangle]
49+
pub fn upcast_diamond_to_a(x: &dyn Diamond) -> &dyn A {
50+
// Requires no adjustment, since its vtable is extended from `A` (as the first supertrait).
51+
52+
// CHECK: start:
53+
// CHECK-NEXT: insertvalue
54+
// CHECK-NEXT: insertvalue
55+
// CHECK-NEXT: ret
56+
x as &dyn A
57+
}
58+
59+
// CHECK-LABEL: upcast_diamond_to_b
60+
// CHECK-SAME: (ptr align {{[0-9]+}} [[DATA_PTR:%.+]], ptr align {{[0-9]+}} [[VTABLE_PTR:%.+]])
61+
#[no_mangle]
62+
pub fn upcast_diamond_to_b(x: &dyn Diamond) -> &dyn B {
63+
// Requires adjustment, since it's a non-first supertrait.
64+
65+
// CHECK: start:
66+
// CHECK-NEXT: [[UPCAST_SLOT_PTR:%.+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]]
67+
// CHECK-NEXT: [[UPCAST_VTABLE_PTR:%.+]] = load ptr, ptr [[UPCAST_SLOT_PTR]]
68+
// CHECK-NEXT: [[FAT_PTR_1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[DATA_PTR]], 0
69+
// CHECK-NEXT: [[FAT_PTR_2:%.+]] = insertvalue { ptr, ptr } [[FAT_PTR_1]], ptr [[UPCAST_VTABLE_PTR]], 1
70+
// CHECK-NEXT: ret { ptr, ptr } [[FAT_PTR_2]]
71+
x as &dyn B
72+
}
73+
74+
// CHECK-LABEL: upcast_diamond_to_b
75+
#[no_mangle]
76+
pub fn upcast_diamond_to_base(x: &dyn Diamond) -> &dyn Base {
77+
// Requires no adjustment, since `Base` is the first supertrait of `A`,
78+
// which is the first supertrait of `Diamond`.
79+
80+
// CHECK: start:
81+
// CHECK-NEXT: insertvalue
82+
// CHECK-NEXT: insertvalue
83+
// CHECK-NEXT: ret
84+
x as &dyn Base
85+
}

0 commit comments

Comments
 (0)