Files
llvm-project/clang/test/CIR/CodeGen/multi-vtable.cpp
Erich Keane 94d4cb5f7b [CIR] Implement basic attributes for this/arguments (#182910)
Similar to what I did for return types in #181052, this patch adds
support for 4 of the function attributes on arguments (noundef, nonnull,
    dereferenceable, align).  The logic for these is all pretty similar
(though SLIGHTLY different enough from eachother unfortunately), so they
are being submitted together. This handles 'this' and normal arguments.
2026-02-24 06:23:14 -08:00

184 lines
8.3 KiB
C++

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fno-rtti -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fno-rtti -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fno-rtti -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
// Note: This test is using -fno-rtti so that we can delay implemntation of that handling.
// When rtti handling for vtables is implemented, that option should be removed.
class Mother {
public:
virtual void MotherKey();
void simple() { }
virtual void MotherNonKey() {}
};
class Father {
public:
virtual void FatherKey();
};
class Child : public Mother, public Father {
public:
Child();
void MotherKey() override;
};
void Mother::MotherKey() {}
void Father::FatherKey() {}
void Child::MotherKey() {}
// CIR-DAG: [[MOTHER_VTABLE_TYPE:.*]] = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
// CIR-DAG: [[FATHER_VTABLE_TYPE:.*]] = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 3>}>
// CIR-DAG: [[CHILD_VTABLE_TYPE:.*]] = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>, !cir.array<!cir.ptr<!u8i> x 3>}>
// CIR-DAG: !rec_Father = !cir.record<class "Father" {!cir.vptr}
// CIR-DAG: !rec_Mother = !cir.record<class "Mother" {!cir.vptr}
// CIR-DAG: !rec_Child = !cir.record<class "Child" {!rec_Mother, !rec_Father}
// Child vtable
// CIR: cir.global "private" external @_ZTV5Child = #cir.vtable<{
// CIR-SAME: #cir.const_array<[
// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.global_view<@_ZN5Child9MotherKeyEv> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.global_view<@_ZN6Mother12MotherNonKeyEv> : !cir.ptr<!u8i>
// CIR-SAME: ]> : !cir.array<!cir.ptr<!u8i> x 4>,
// CIR-SAME: #cir.const_array<[
// CIR-SAME: #cir.ptr<-8 : i64> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.global_view<@_ZN6Father9FatherKeyEv> : !cir.ptr<!u8i>
// CIR-SAME: ]> : !cir.array<!cir.ptr<!u8i> x 3>
// CIR-SAME: }> : [[CHILD_VTABLE_TYPE]]
// LLVM: @_ZTV5Child = global { [4 x ptr], [3 x ptr] } {
// LLVM-SAME: [4 x ptr] [
// LLVM-SAME: ptr null,
// LLVM-SAME: ptr null,
// LLVM-SAME: ptr @_ZN5Child9MotherKeyEv,
// LLVM-SAME: ptr @_ZN6Mother12MotherNonKeyEv
// LLVM-SAME: ],
// LLVM-SAME: [3 x ptr] [
// LLVM-SAME: ptr inttoptr (i64 -8 to ptr),
// LLVM-SAME: ptr null,
// LLVM-SAME: ptr @_ZN6Father9FatherKeyEv
// LLVM-SAME: ]
// LLVM-SAME: }
// OGCG: @_ZTV5Child = unnamed_addr constant { [4 x ptr], [3 x ptr] } {
// OGCG-SAME: [4 x ptr] [
// OGCG-SAME: ptr null,
// OGCG-SAME: ptr null,
// OGCG-SAME: ptr @_ZN5Child9MotherKeyEv,
// OGCG-SAME: ptr @_ZN6Mother12MotherNonKeyEv
// OGCG-SAME: ],
// OGCG-SAME: [3 x ptr] [
// OGCG-SAME: ptr inttoptr (i64 -8 to ptr),
// OGCG-SAME: ptr null,
// OGCG-SAME: ptr @_ZN6Father9FatherKeyEv
// OGCG-SAME: ]
// OGCG-SAME: }
// Mother vtable
// CIR: cir.global "private" external @_ZTV6Mother = #cir.vtable<{
// CIR-SAME: #cir.const_array<[
// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.global_view<@_ZN6Mother9MotherKeyEv> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.global_view<@_ZN6Mother12MotherNonKeyEv> : !cir.ptr<!u8i>
// CIR-SAME: ]> : !cir.array<!cir.ptr<!u8i> x 4>
// CIR-SAME: }> : [[MOTHER_VTABLE_TYPE]]
// LLVM: @_ZTV6Mother = global { [4 x ptr] } {
// LLVM-SAME: [4 x ptr] [
// LLVM-SAME: ptr null,
// LLVM-SAME: ptr null,
// LLVM-SAME: ptr @_ZN6Mother9MotherKeyEv,
// LLVM-SAME: ptr @_ZN6Mother12MotherNonKeyEv
// LLVM-SAME: ]
// LLVM-SAME: }
// OGCG: @_ZTV6Mother = unnamed_addr constant { [4 x ptr] } {
// OGCG-SAME: [4 x ptr] [
// OGCG-SAME: ptr null,
// OGCG-SAME: ptr null,
// OGCG-SAME: ptr @_ZN6Mother9MotherKeyEv,
// OGCG-SAME: ptr @_ZN6Mother12MotherNonKeyEv
// OGCG-SAME: ]
// OGCG-SAME: }
// Father vtable
// CIR: cir.global "private" external @_ZTV6Father = #cir.vtable<{
// CIR-SAME: #cir.const_array<[
// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
// CIR-SAME: #cir.global_view<@_ZN6Father9FatherKeyEv> : !cir.ptr<!u8i>
// CIR-SAME: ]> : !cir.array<!cir.ptr<!u8i> x 3>
// CIR-SAME: }> : [[FATHER_VTABLE_TYPE]]
// LLVM: @_ZTV6Father = global { [3 x ptr] } {
// LLVM-SAME: [3 x ptr] [
// LLVM-SAME: ptr null,
// LLVM-SAME: ptr null,
// LLVM-SAME: ptr @_ZN6Father9FatherKeyEv
// LLVM-SAME: ]
// LLVM-SAME: }
// OGCG: @_ZTV6Father = unnamed_addr constant { [3 x ptr] } {
// OGCG-SAME: [3 x ptr] [
// OGCG-SAME: ptr null,
// OGCG-SAME: ptr null,
// OGCG-SAME: ptr @_ZN6Father9FatherKeyEv
// OGCG-SAME: ]
// OGCG-SAME: }
Child::Child() {}
// CIR: cir.func {{.*}} @_ZN5ChildC2Ev(%[[THIS_ARG:.*]]: !cir.ptr<!rec_Child>
// CIR: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init]
// CIR: cir.store %[[THIS_ARG]], %[[THIS_ADDR]]
// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
// CIR: %[[MOTHER_BASE:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_Child> nonnull [0] -> !cir.ptr<!rec_Mother>
// CIR: cir.call @_ZN6MotherC2Ev(%[[MOTHER_BASE]]) nothrow : (!cir.ptr<!rec_Mother> {{.*}}) -> ()
// CIR: %[[FATHER_BASE:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_Child> nonnull [8] -> !cir.ptr<!rec_Father>
// CIR: cir.call @_ZN6FatherC2Ev(%[[FATHER_BASE]]) nothrow : (!cir.ptr<!rec_Father> {{.*}}) -> ()
// CIR: %[[CHILD_VPTR:.*]] = cir.vtable.address_point(@_ZTV5Child, address_point = <index = 0, offset = 2>) : !cir.vptr
// CIR: %[[CHILD_VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr<!rec_Child> -> !cir.ptr<!cir.vptr>
// CIR: cir.store{{.*}} %[[CHILD_VPTR]], %[[CHILD_VPTR_ADDR]] : !cir.vptr, !cir.ptr<!cir.vptr>
// CIR: %[[FATHER_IN_CHILD_VPTR:.*]] = cir.vtable.address_point(@_ZTV5Child, address_point = <index = 1, offset = 2>) : !cir.vptr
// CIR: %[[FATHER_BASE:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_Child> nonnull [8] -> !cir.ptr<!rec_Father>
// CIR: %[[FATHER_IN_CHILD_VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[FATHER_BASE]] : !cir.ptr<!rec_Father> -> !cir.ptr<!cir.vptr>
// CIR: cir.store{{.*}} %[[FATHER_IN_CHILD_VPTR]], %[[FATHER_IN_CHILD_VPTR_ADDR]] : !cir.vptr, !cir.ptr<!cir.vptr>
// CIR: cir.return
// The GEP instructions are different between LLVM and OGCG, but they calculate the same addresses.
// LLVM: define{{.*}} void @_ZN5ChildC2Ev(ptr{{.*}} %[[THIS_ARG:.*]])
// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
// LLVM: call void @_ZN6MotherC2Ev(ptr{{.*}} %[[THIS]])
// LLVM: %[[FATHER_BASE:.*]] = getelementptr{{.*}} i8, ptr %[[THIS]], i32 8
// LLVM: call void @_ZN6FatherC2Ev(ptr{{.*}} %[[FATHER_BASE]])
// LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV5Child, i64 16), ptr %[[THIS]]
// LLVM: %[[FATHER_BASE:.*]] = getelementptr{{.*}} i8, ptr %[[THIS]], i32 8
// LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV5Child, i64 48), ptr %[[FATHER_BASE]]
// LLVM: ret void
// OGCG: define{{.*}} void @_ZN5ChildC2Ev(ptr{{.*}} %[[THIS_ARG:.*]])
// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
// OGCG: call void @_ZN6MotherC2Ev(ptr {{.*}} %[[THIS]])
// OGCG: %[[FATHER_BASE:.*]] = getelementptr{{.*}} i8, ptr %[[THIS]], i64 8
// OGCG: call void @_ZN6FatherC2Ev(ptr{{.*}} %[[FATHER_BASE]])
// OGCG: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr], [3 x ptr] }, ptr @_ZTV5Child, i32 0, i32 0, i32 2), ptr %[[THIS]]
// OGCG: %[[FATHER_BASE:.*]] = getelementptr{{.*}} i8, ptr %[[THIS]], i64 8
// OGCG: store ptr getelementptr inbounds inrange(-16, 8) ({ [4 x ptr], [3 x ptr] }, ptr @_ZTV5Child, i32 0, i32 1, i32 2), ptr %[[FATHER_BASE]]
// OGCG: ret void