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](cd81f5b27e/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`
This commit is contained in:
commit
028e2600c9
@ -165,14 +165,11 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target));
|
||||
|
||||
if let Some(entry_idx) = vptr_entry_idx {
|
||||
let ptr_ty = cx.type_ptr();
|
||||
let ptr_align = cx.tcx().data_layout.pointer_align.abi;
|
||||
let gep = bx.inbounds_gep(
|
||||
ptr_ty,
|
||||
old_info,
|
||||
&[bx.const_usize(u64::try_from(entry_idx).unwrap())],
|
||||
);
|
||||
let new_vptr = bx.load(ptr_ty, gep, ptr_align);
|
||||
let ptr_size = bx.data_layout().pointer_size;
|
||||
let ptr_align = bx.data_layout().pointer_align.abi;
|
||||
let vtable_byte_offset = u64::try_from(entry_idx).unwrap() * ptr_size.bytes();
|
||||
let gep = bx.inbounds_ptradd(old_info, bx.const_usize(vtable_byte_offset));
|
||||
let new_vptr = bx.load(bx.type_ptr(), gep, ptr_align);
|
||||
bx.nonnull_metadata(new_vptr);
|
||||
// VTable loads are invariant.
|
||||
bx.set_invariant_load(new_vptr);
|
||||
|
@ -20,9 +20,13 @@ pub fn get_fn<Bx: BuilderMethods<'a, 'tcx>>(
|
||||
ty: Ty<'tcx>,
|
||||
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
) -> Bx::Value {
|
||||
// Load the data pointer from the object.
|
||||
// Load the function pointer from the object.
|
||||
debug!("get_fn({llvtable:?}, {ty:?}, {self:?})");
|
||||
|
||||
let llty = bx.fn_ptr_backend_type(fn_abi);
|
||||
let ptr_size = bx.data_layout().pointer_size;
|
||||
let ptr_align = bx.data_layout().pointer_align.abi;
|
||||
let vtable_byte_offset = self.0 * ptr_size.bytes();
|
||||
|
||||
if bx.cx().sess().opts.unstable_opts.virtual_function_elimination
|
||||
&& bx.cx().sess().lto() == Lto::Fat
|
||||
@ -30,12 +34,10 @@ pub fn get_fn<Bx: BuilderMethods<'a, 'tcx>>(
|
||||
let typeid = bx
|
||||
.typeid_metadata(typeid_for_trait_ref(bx.tcx(), expect_dyn_trait_in_self(ty)))
|
||||
.unwrap();
|
||||
let vtable_byte_offset = self.0 * bx.data_layout().pointer_size.bytes();
|
||||
let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid);
|
||||
func
|
||||
} else {
|
||||
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
|
||||
let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
|
||||
let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset));
|
||||
let ptr = bx.load(llty, gep, ptr_align);
|
||||
bx.nonnull_metadata(ptr);
|
||||
// VTable loads are invariant.
|
||||
@ -53,9 +55,12 @@ pub fn get_usize<Bx: BuilderMethods<'a, 'tcx>>(
|
||||
debug!("get_int({:?}, {:?})", llvtable, self);
|
||||
|
||||
let llty = bx.type_isize();
|
||||
let usize_align = bx.tcx().data_layout.pointer_align.abi;
|
||||
let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
|
||||
let ptr = bx.load(llty, gep, usize_align);
|
||||
let ptr_size = bx.data_layout().pointer_size;
|
||||
let ptr_align = bx.data_layout().pointer_align.abi;
|
||||
let vtable_byte_offset = self.0 * ptr_size.bytes();
|
||||
|
||||
let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset));
|
||||
let ptr = bx.load(llty, gep, ptr_align);
|
||||
// VTable loads are invariant.
|
||||
bx.set_invariant_load(ptr);
|
||||
ptr
|
||||
|
@ -25,9 +25,9 @@ struct Dst<T: ?Sized> {
|
||||
pub fn dst_dyn_trait_offset(s: &Dst<dyn Drop>) -> &dyn Drop {
|
||||
// The alignment of dyn trait is unknown, so we compute the offset based on align from the vtable.
|
||||
|
||||
// CHECK: [[SIZE_PTR:%[0-9]+]] = getelementptr inbounds {{.+}} [[VTABLE_PTR]]
|
||||
// CHECK: [[SIZE_PTR:%[0-9]+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]]
|
||||
// CHECK: load [[USIZE]], ptr [[SIZE_PTR]]
|
||||
// CHECK: [[ALIGN_PTR:%[0-9]+]] = getelementptr inbounds {{.+}} [[VTABLE_PTR]]
|
||||
// CHECK: [[ALIGN_PTR:%[0-9]+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]]
|
||||
// CHECK: load [[USIZE]], ptr [[ALIGN_PTR]]
|
||||
|
||||
// CHECK: getelementptr inbounds i8, ptr [[DATA_PTR]]
|
||||
|
85
tests/codegen/vtable-upcast.rs
Normal file
85
tests/codegen/vtable-upcast.rs
Normal file
@ -0,0 +1,85 @@
|
||||
//! This file tests that we correctly generate GEP instructions for vtable upcasting.
|
||||
//@ compile-flags: -C no-prepopulate-passes -Copt-level=0
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(trait_upcasting)]
|
||||
|
||||
pub trait Base {
|
||||
fn base(&self);
|
||||
}
|
||||
|
||||
pub trait A : Base {
|
||||
fn a(&self);
|
||||
}
|
||||
|
||||
pub trait B : Base {
|
||||
fn b(&self);
|
||||
}
|
||||
|
||||
pub trait Diamond : A + B {
|
||||
fn diamond(&self);
|
||||
}
|
||||
|
||||
// CHECK-LABEL: upcast_a_to_base
|
||||
#[no_mangle]
|
||||
pub fn upcast_a_to_base(x: &dyn A) -> &dyn Base {
|
||||
// Requires no adjustment, since its vtable is extended from `Base`.
|
||||
|
||||
// CHECK: start:
|
||||
// CHECK-NEXT: insertvalue
|
||||
// CHECK-NEXT: insertvalue
|
||||
// CHECK-NEXT: ret
|
||||
x as &dyn Base
|
||||
}
|
||||
|
||||
// CHECK-LABEL: upcast_b_to_base
|
||||
#[no_mangle]
|
||||
pub fn upcast_b_to_base(x: &dyn B) -> &dyn Base {
|
||||
// Requires no adjustment, since its vtable is extended from `Base`.
|
||||
|
||||
// CHECK: start:
|
||||
// CHECK-NEXT: insertvalue
|
||||
// CHECK-NEXT: insertvalue
|
||||
// CHECK-NEXT: ret
|
||||
x as &dyn Base
|
||||
}
|
||||
|
||||
// CHECK-LABEL: upcast_diamond_to_a
|
||||
#[no_mangle]
|
||||
pub fn upcast_diamond_to_a(x: &dyn Diamond) -> &dyn A {
|
||||
// Requires no adjustment, since its vtable is extended from `A` (as the first supertrait).
|
||||
|
||||
// CHECK: start:
|
||||
// CHECK-NEXT: insertvalue
|
||||
// CHECK-NEXT: insertvalue
|
||||
// CHECK-NEXT: ret
|
||||
x as &dyn A
|
||||
}
|
||||
|
||||
// CHECK-LABEL: upcast_diamond_to_b
|
||||
// CHECK-SAME: (ptr align {{[0-9]+}} [[DATA_PTR:%.+]], ptr align {{[0-9]+}} [[VTABLE_PTR:%.+]])
|
||||
#[no_mangle]
|
||||
pub fn upcast_diamond_to_b(x: &dyn Diamond) -> &dyn B {
|
||||
// Requires adjustment, since it's a non-first supertrait.
|
||||
|
||||
// CHECK: start:
|
||||
// CHECK-NEXT: [[UPCAST_SLOT_PTR:%.+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]]
|
||||
// CHECK-NEXT: [[UPCAST_VTABLE_PTR:%.+]] = load ptr, ptr [[UPCAST_SLOT_PTR]]
|
||||
// CHECK-NEXT: [[FAT_PTR_1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[DATA_PTR]], 0
|
||||
// CHECK-NEXT: [[FAT_PTR_2:%.+]] = insertvalue { ptr, ptr } [[FAT_PTR_1]], ptr [[UPCAST_VTABLE_PTR]], 1
|
||||
// CHECK-NEXT: ret { ptr, ptr } [[FAT_PTR_2]]
|
||||
x as &dyn B
|
||||
}
|
||||
|
||||
// CHECK-LABEL: upcast_diamond_to_b
|
||||
#[no_mangle]
|
||||
pub fn upcast_diamond_to_base(x: &dyn Diamond) -> &dyn Base {
|
||||
// Requires no adjustment, since `Base` is the first supertrait of `A`,
|
||||
// which is the first supertrait of `Diamond`.
|
||||
|
||||
// CHECK: start:
|
||||
// CHECK-NEXT: insertvalue
|
||||
// CHECK-NEXT: insertvalue
|
||||
// CHECK-NEXT: ret
|
||||
x as &dyn Base
|
||||
}
|
Loading…
Reference in New Issue
Block a user