// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll // RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG // Test the record layout for a class with a primary virtual base. class Base { public: virtual void f(); }; class Derived : public virtual Base {}; void f() { Derived d; d.f(); } class DerivedFinal final : public virtual Base {}; void g() { DerivedFinal df; df.f(); } // CIR: !rec_Base = !cir.record // CIR: !rec_Derived = !cir.record // CIR: !rec_DerivedFinal = !cir.record // LLVM: %class.Derived = type { %class.Base } // LLVM: %class.Base = type { ptr } // LLVM: %class.DerivedFinal = type { %class.Base } // OGCG: %class.Derived = type { %class.Base } // OGCG: %class.Base = type { ptr } // OGCG: %class.DerivedFinal = type { %class.Base } // Test the constructor handling for a class with a virtual base. struct A { int a; }; struct B: virtual A { int b; }; void ppp() { B b; } // Note: OGCG speculatively emits the VTT and VTables. This is not yet implemented in CIR. // Vtable definition for B // CIR: cir.global "private" {{.*}}@_ZTV1B = #cir.vtable<{#cir.const_array<[#cir.ptr<12 : i64> : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1B> : !cir.ptr]> : !cir.array x 3>}> : !rec_anon_struct3 {alignment = 8 : i64} // LLVM: @_ZTV1B = linkonce_odr global { [3 x ptr] } { [3 x ptr] [ptr inttoptr (i64 12 to ptr), ptr null, ptr @_ZTI1B] }, comdat, align 8 // OGCG: @_ZTV1B = linkonce_odr unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr inttoptr (i64 12 to ptr), ptr null, ptr @_ZTI1B] }, comdat, align 8 // CIR: cir.func {{.*}}@_Z1fv() // CIR: %[[D:.+]] = cir.alloca !rec_Derived, !cir.ptr, ["d", init] // CIR: cir.call @_ZN7DerivedC1Ev(%[[D]]) nothrow : (!cir.ptr {{.*}}) -> () // CIR: %[[VPTR_PTR:.+]] = cir.vtable.get_vptr %[[D]] : !cir.ptr -> !cir.ptr // CIR: %[[VPTR:.+]] = cir.load {{.*}} %[[VPTR_PTR]] : !cir.ptr, !cir.vptr // CIR: %[[VPTR_I8:.+]] = cir.cast bitcast %[[VPTR]] : !cir.vptr -> !cir.ptr // CIR: %[[NEG32:.+]] = cir.const #cir.int<-32> : !s64i // CIR: %[[ADJ_VPTR_I8:.+]] = cir.ptr_stride %[[VPTR_I8]], %[[NEG32]] : (!cir.ptr, !s64i) -> !cir.ptr // CIR: %[[OFFSET_PTR:.+]] = cir.cast bitcast %[[ADJ_VPTR_I8]] : !cir.ptr -> !cir.ptr // CIR: %[[OFFSET:.+]] = cir.load {{.*}} %[[OFFSET_PTR]] : !cir.ptr, !s64i // CIR: %[[D_I8:.+]] = cir.cast bitcast %[[D]] : !cir.ptr -> !cir.ptr // CIR: %[[ADJ_THIS_I8:.+]] = cir.ptr_stride %[[D_I8]], %[[OFFSET]] : (!cir.ptr, !s64i) -> !cir.ptr // CIR: %[[ADJ_THIS_D:.+]] = cir.cast bitcast %[[ADJ_THIS_I8]] : !cir.ptr -> !cir.ptr // CIR: %[[BASE_THIS:.+]] = cir.cast bitcast %[[ADJ_THIS_D]] : !cir.ptr -> !cir.ptr // CIR: %[[BASE_VPTR_PTR:.+]] = cir.vtable.get_vptr %[[BASE_THIS]] : !cir.ptr -> !cir.ptr // CIR: %[[BASE_VPTR:.+]] = cir.load {{.*}} %[[BASE_VPTR_PTR]] : !cir.ptr, !cir.vptr // CIR: %[[SLOT_PTR:.+]] = cir.vtable.get_virtual_fn_addr %[[BASE_VPTR]][0] : !cir.vptr -> !cir.ptr)>>> // CIR: %[[FN:.+]] = cir.load {{.*}} %[[SLOT_PTR]] : !cir.ptr)>>>, !cir.ptr)>> // CIR: cir.call %[[FN]](%[[BASE_THIS]]) : (!cir.ptr)>>, !cir.ptr {{.*}}) -> () // CIR: cir.return // CIR: cir.func {{.*}}@_Z1gv() // CIR: %[[DF:.+]] = cir.alloca !rec_DerivedFinal, !cir.ptr, ["df", init] // CIR: cir.call @_ZN12DerivedFinalC1Ev(%[[DF]]) nothrow : (!cir.ptr {{.*}}) -> () // CIR: %[[BASE_THIS_2:.+]] = cir.base_class_addr %[[DF]] : !cir.ptr nonnull [0] -> !cir.ptr // CIR: %[[BASE_VPTR_PTR_2:.+]] = cir.vtable.get_vptr %[[BASE_THIS_2]] : !cir.ptr -> !cir.ptr // CIR: %[[BASE_VPTR_2:.+]] = cir.load {{.*}} %[[BASE_VPTR_PTR_2]] : !cir.ptr, !cir.vptr // CIR: %[[SLOT_PTR_2:.+]] = cir.vtable.get_virtual_fn_addr %[[BASE_VPTR_2]][0] : !cir.vptr -> !cir.ptr)>>> // CIR: %[[FN_2:.+]] = cir.load {{.*}} %[[SLOT_PTR_2]] : !cir.ptr)>>>, !cir.ptr)>> // CIR: cir.call %[[FN_2]](%[[BASE_THIS_2]]) : (!cir.ptr)>>, !cir.ptr {{.*}}) -> () // CIR: cir.return // LLVM: define {{.*}}void @_Z1fv(){{.*}} // LLVM: %[[D:.+]] = alloca {{.*}} // LLVM: call void @_ZN7DerivedC1Ev(ptr {{.*}} %[[D]]) // LLVM: %[[VPTR_ADDR:.+]] = load ptr, ptr %[[D]] // LLVM: %[[NEG32_PTR:.+]] = getelementptr i8, ptr %[[VPTR_ADDR]], i64 -32 // LLVM: %[[OFF:.+]] = load i64, ptr %[[NEG32_PTR]] // LLVM: %[[ADJ_THIS:.+]] = getelementptr i8, ptr %[[D]], i64 %[[OFF]] // LLVM: %[[VFN_TAB:.+]] = load ptr, ptr %[[ADJ_THIS]] // LLVM: %[[SLOT0:.+]] = getelementptr inbounds ptr, ptr %[[VFN_TAB]], i32 0 // LLVM: %[[VFN:.+]] = load ptr, ptr %[[SLOT0]] // LLVM: call void %[[VFN]](ptr {{.*}}%[[ADJ_THIS]]) // LLVM: ret void // LLVM: define {{.*}}void @_Z1gv(){{.*}} // LLVM: %[[DF:.+]] = alloca {{.*}} // LLVM: call void @_ZN12DerivedFinalC1Ev(ptr {{.*}} %[[DF]]) // LLVM: %[[VPTR2:.+]] = load ptr, ptr %[[DF]] // LLVM: %[[SLOT0_2:.+]] = getelementptr inbounds ptr, ptr %[[VPTR2]], i32 0 // LLVM: %[[VFN2:.+]] = load ptr, ptr %[[SLOT0_2]] // LLVM: call void %[[VFN2]](ptr {{.*}}%[[DF]]) // LLVM: ret void // OGCG: define {{.*}}void @_Z1fv() // OGCG: %[[D:.+]] = alloca {{.*}} // OGCG: call void @_ZN7DerivedC1Ev(ptr {{.*}} %[[D]]) // OGCG: %[[VTABLE:.+]] = load ptr, ptr %[[D]] // OGCG: %[[NEG32_PTR:.+]] = getelementptr i8, ptr %[[VTABLE]], i64 -32 // OGCG: %[[OFF:.+]] = load i64, ptr %[[NEG32_PTR]] // OGCG: %[[ADJ_THIS:.+]] = getelementptr inbounds i8, ptr %[[D]], i64 %[[OFF]] // OGCG: call void @_ZN4Base1fEv(ptr {{.*}} %[[ADJ_THIS]]) // OGCG: ret void // OGCG: define {{.*}}void @_Z1gv() // OGCG: %[[DF:.+]] = alloca {{.*}} // OGCG: call void @_ZN12DerivedFinalC1Ev(ptr {{.*}} %[[DF]]) // OGCG: call void @_ZN4Base1fEv(ptr {{.*}} %[[DF]]) // OGCG: ret void // Constructor for B // CIR: cir.func {{.*}} @_ZN1BC1Ev(%arg0: !cir.ptr // CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] // CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr, !cir.ptr> // CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr>, !cir.ptr // CIR: %[[BASE_A_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr nonnull [12] -> !cir.ptr // CIR: %[[VTABLE:.*]] = cir.vtable.address_point(@_ZTV1B, address_point = ) : !cir.vptr // CIR: %[[B_VPTR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr -> !cir.ptr // CIR: cir.store align(8) %[[VTABLE]], %[[B_VPTR]] : !cir.vptr, !cir.ptr // CIR: cir.return // LLVM: define{{.*}} void @_ZN1BC1Ev(ptr {{.*}} %[[THIS_ARG:.*]]){{.*}} { // LLVM: %[[THIS_ADDR:.*]] = alloca ptr // LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] // LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] // LLVM: %[[BASE_A_ADDR:.*]] = getelementptr i8, ptr %[[THIS]], i32 12 // LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 24), ptr %[[THIS]] // LLVM: ret void // OGCG: define{{.*}} void @_ZN1BC1Ev(ptr {{.*}} %[[THIS_ARG:.*]]) // OGCG: %[[THIS_ADDR:.*]] = alloca ptr // OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] // OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] // OGCG: %[[BASE_A_ADDR:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i64 12 // OGCG: store ptr getelementptr inbounds inrange(-24, 0) ({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3), ptr %[[THIS]] // OGCG: ret void