// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -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 -fclangir -emit-llvm %s -o %t-cir.ll // RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG struct A { A(); virtual void f(char); }; // This should initialize the vtable pointer. A::A() {} // CIR: !rec_A = !cir.record // CIR: !rec_anon_struct = !cir.record x 3>}> // CIR: cir.global "private" external @_ZTV1A : !rec_anon_struct // LLVM: @_ZTV1A = external global { [3 x ptr] } // OGCG: @_ZTV1A = external unnamed_addr constant { [3 x ptr] } // CIR: cir.func{{.*}} @_ZN1AC2Ev(%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: %[[VPTR:.*]] = cir.vtable.address_point(@_ZTV1A, address_point = ) : !cir.vptr // CIR: %[[THIS_VPTR_PTR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr -> !cir.ptr // CIR: cir.store{{.*}} align(8) %[[VPTR]], %[[THIS_VPTR_PTR]] : !cir.vptr, !cir.ptr // CIR: cir.return // LLVM: define{{.*}} void @_ZN1AC2Ev(ptr {{.*}} %[[ARG0:.*]]) // LLVM: %[[THIS_ADDR:.*]] = alloca ptr // LLVM: store ptr %[[ARG0]], ptr %[[THIS_ADDR]] // LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] // LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1A, i64 16), ptr %[[THIS]] // OGCG: define{{.*}} void @_ZN1AC2Ev(ptr {{.*}} %[[ARG0:.*]]) // OGCG: %[[THIS_ADDR:.*]] = alloca ptr // OGCG: store ptr %[[ARG0]], ptr %[[THIS_ADDR]] // OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] // OGCG: store ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2), ptr %[[THIS]] // NOTE: The GEP in OGCG looks very different from the one generated with CIR, // but it is equivalent. The OGCG GEP indexes by base pointer, then // structure, then array, whereas the CIR GEP indexes by byte offset. void f1(A *a) { a->f('c'); } // CIR: cir.func{{.*}} @_Z2f1P1A(%arg0: !cir.ptr {{.*}}) // CIR: %[[A_ADDR:.*]] = cir.alloca !cir.ptr // CIR: cir.store %arg0, %[[A_ADDR]] // CIR: %[[A:.*]] = cir.load{{.*}} %[[A_ADDR]] // CIR: %[[C_LITERAL:.*]] = cir.const #cir.int<99> : !s8i // CIR: %[[VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[A]] : !cir.ptr -> !cir.ptr // CIR: %[[VPTR:.*]] = cir.load{{.*}} %[[VPTR_ADDR]] : !cir.ptr, !cir.vptr // CIR: %[[FN_PTR_PTR:.*]] = cir.vtable.get_virtual_fn_addr %[[VPTR]][0] : !cir.vptr -> !cir.ptr, !s8i)>>> // CIR: %[[FN_PTR:.*]] = cir.load{{.*}} %[[FN_PTR_PTR:.*]] : !cir.ptr, !s8i)>>>, !cir.ptr, !s8i)>> // CIR: cir.call %[[FN_PTR]](%[[A]], %[[C_LITERAL]]) : (!cir.ptr, !s8i)>>, !cir.ptr {{.*}}, !s8i {{.*}}) -> () // LLVM: define{{.*}} void @_Z2f1P1A(ptr {{.*}} %[[ARG0:.*]]) // LLVM: %[[A_ADDR:.*]] = alloca ptr // LLVM: store ptr %[[ARG0]], ptr %[[A_ADDR]] // LLVM: %[[A:.*]] = load ptr, ptr %[[A_ADDR]] // LLVM: %[[VPTR:.*]] = load ptr, ptr %[[A]] // LLVM: %[[FN_PTR_PTR:.*]] = getelementptr inbounds ptr, ptr %[[VPTR]], i32 0 // LLVM: %[[FN_PTR:.*]] = load ptr, ptr %[[FN_PTR_PTR]] // LLVM: call void %[[FN_PTR]](ptr {{.*}} %[[A]], i8 {{.*}} 99) // OGCG: define{{.*}} void @_Z2f1P1A(ptr {{.*}} %[[ARG0:.*]]) // OGCG: %[[A_ADDR:.*]] = alloca ptr // OGCG: store ptr %[[ARG0]], ptr %[[A_ADDR]] // OGCG: %[[A:.*]] = load ptr, ptr %[[A_ADDR]] // OGCG: %[[VPTR:.*]] = load ptr, ptr %[[A]] // OGCG: %[[FN_PTR_PTR:.*]] = getelementptr inbounds ptr, ptr %[[VPTR]], i64 0 // OGCG: %[[FN_PTR:.*]] = load ptr, ptr %[[FN_PTR_PTR]] // OGCG: call void %[[FN_PTR]](ptr {{.*}} %[[A]], i8 {{.*}} 99) struct B { ~B(); virtual void f(char); }; void call_virtual_fn_in_cleanup_scope() { B b; b.f('c'); } // CIR: cir.func {{.*}} @_Z32call_virtual_fn_in_cleanup_scopev() // CIR: %[[B:.*]] = cir.alloca !rec_B, !cir.ptr, ["b", init] // CIR: cir.call @_ZN1BC2Ev(%[[B]]) // CIR: cir.cleanup.scope { // CIR: %[[C_LITERAL:.*]] = cir.const #cir.int<99> : !s8i // CIR: %[[VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[B]] : !cir.ptr -> !cir.ptr // CIR: %[[VPTR:.*]] = cir.load{{.*}} %[[VPTR_ADDR]] // CIR: %[[FN_PTR_ADDR:.*]] = cir.vtable.get_virtual_fn_addr %[[VPTR]][0] : !cir.vptr -> !cir.ptr, !s8i)>>> // CIR: %[[FN_PTR:.*]] = cir.load{{.*}} %[[FN_PTR_ADDR:.*]] : !cir.ptr, !s8i)>>>, !cir.ptr, !s8i)>> // CIR: cir.call %[[FN_PTR]](%[[B]], %[[C_LITERAL]]) : (!cir.ptr, !s8i)>>, !cir.ptr {{.*}}, !s8i {{.*}}) -> () // CIR: cir.yield // CIR: } cleanup normal { // CIR: cir.call @_ZN1BD1Ev(%[[B]]) nothrow : (!cir.ptr {{.*}}) -> () // CIR: cir.yield // CIR: } // LLVM: define {{.*}} void @_Z32call_virtual_fn_in_cleanup_scopev() // LLVM: %[[B:.*]] = alloca %struct.B // LLVM: call void @_ZN1BC2Ev(ptr {{.*}} %[[B]]) // LLVM: br label %[[CLEANUP_SCOPE:.*]] // LLVM: [[CLEANUP_SCOPE]]: // LLVM: %[[B_VPTR:.*]] = load ptr, ptr %[[B]] // LLVM: %[[FN_PTR_ADDR:.*]] = getelementptr inbounds ptr, ptr %[[B_VPTR]], i32 0 // LLVM: %[[FN_PTR:.*]] = load ptr, ptr %[[FN_PTR_ADDR]] // LLVM: call void %[[FN_PTR]](ptr {{.*}} %[[B]], i8 noundef 99) // LLVM: br label %[[NORMAL_CLEANUP:.*]] // LLVM: [[NORMAL_CLEANUP]]: // LLVM: call void @_ZN1BD1Ev(ptr {{.*}} %[[B]]) // Note: OGCG devirtualizes the call. We don't do that yet in CIR. // OGCG: define {{.*}} void @_Z32call_virtual_fn_in_cleanup_scopev() // OGCG: %[[B:.*]] = alloca %struct.B, align 8 // OGCG: call void @_ZN1BC2Ev(ptr {{.*}} %[[B]]) // OGCG: call void @_ZN1B1fEc(ptr {{.*}} %[[B]], i8 noundef signext 99) // OGCG: call void @_ZN1BD1Ev(ptr {{.*}} %[[B]])