|
| 1 | +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-cir %s -o %t.cir |
| 2 | +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s |
| 3 | +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-llvm %s -o %t-cir.ll |
| 4 | +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s |
| 5 | +// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -emit-llvm %s -o %t.ll |
| 6 | +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s |
| 7 | + |
| 8 | +// TODO(cir): Try to emit base destructor as an alias at O1 or higher. |
| 9 | + |
| 10 | +// FIXME: LLVM IR dialect does not yet support function ptr globals, which precludes |
| 11 | +// a lot of the proper semantics for properly representing alias functions in LLVM |
| 12 | +// (see the note on LLVM_O1 below). |
| 13 | + |
| 14 | +struct Member { |
| 15 | + ~Member(); |
| 16 | +}; |
| 17 | + |
| 18 | +struct A { |
| 19 | + virtual ~A(); |
| 20 | +}; |
| 21 | + |
| 22 | +struct B : A { |
| 23 | + Member m; |
| 24 | + virtual ~B(); |
| 25 | +}; |
| 26 | + |
| 27 | +B::~B() { } |
| 28 | + |
| 29 | +// Aliases are inserted before the function definitions in LLVM IR |
| 30 | +// FIXME: These should have unnamed_addr set. |
| 31 | +// LLVM: @_ZN1BD1Ev = alias void (ptr), ptr @_ZN1BD2Ev |
| 32 | +// LLVM: @_ZN1CD1Ev = alias void (ptr), ptr @_ZN1CD2Ev |
| 33 | + |
| 34 | +// OGCG: @_ZN1BD1Ev = unnamed_addr alias void (ptr), ptr @_ZN1BD2Ev |
| 35 | +// OGCG: @_ZN1CD1Ev = unnamed_addr alias void (ptr), ptr @_ZN1CD2Ev |
| 36 | + |
| 37 | + |
| 38 | +// Base (D2) dtor for B: calls A's base dtor. |
| 39 | + |
| 40 | +// CIR: cir.func{{.*}} @_ZN1BD2Ev |
| 41 | +// CIR: cir.call @_ZN6MemberD1Ev |
| 42 | +// CIR: cir.call @_ZN1AD2Ev |
| 43 | + |
| 44 | +// LLVM: define{{.*}} void @_ZN1BD2Ev |
| 45 | +// LLVM: call void @_ZN6MemberD1Ev |
| 46 | +// LLVM: call void @_ZN1AD2Ev |
| 47 | + |
| 48 | +// OGCG: define{{.*}} @_ZN1BD2Ev |
| 49 | +// OGCG: call void @_ZN6MemberD1Ev |
| 50 | +// OGCG: call void @_ZN1AD2Ev |
| 51 | + |
| 52 | +// Complete (D1) dtor for B: just an alias because there are no virtual bases. |
| 53 | + |
| 54 | +// CIR: cir.func{{.*}} @_ZN1BD1Ev(!cir.ptr<!rec_B>) alias(@_ZN1BD2Ev) |
| 55 | +// This is defined above for LLVM and OGCG. |
| 56 | + |
| 57 | +// Deleting (D0) dtor for B: defers to the complete dtor but also calls operator delete. |
| 58 | + |
| 59 | +// CIR: cir.func{{.*}} @_ZN1BD0Ev |
| 60 | +// CIR: cir.call @_ZN1BD1Ev(%[[THIS:.*]]) nothrow : (!cir.ptr<!rec_B>) -> () |
| 61 | +// CIR: %[[THIS_VOID:.*]] = cir.cast bitcast %[[THIS]] : !cir.ptr<!rec_B> -> !cir.ptr<!void> |
| 62 | +// CIR: %[[SIZE:.*]] = cir.const #cir.int<16> |
| 63 | +// CIR: cir.call @_ZdlPvm(%[[THIS_VOID]], %[[SIZE]]) |
| 64 | + |
| 65 | +// LLVM: define{{.*}} void @_ZN1BD0Ev |
| 66 | +// LLVM: call void @_ZN1BD1Ev(ptr %[[THIS:.*]]) |
| 67 | +// LLVM: call void @_ZdlPvm(ptr %[[THIS]], i64 16) |
| 68 | + |
| 69 | +// OGCG: define{{.*}} @_ZN1BD0Ev |
| 70 | +// OGCG: call void @_ZN1BD1Ev(ptr{{.*}} %[[THIS:.*]]) |
| 71 | +// OGCG: call void @_ZdlPvm(ptr{{.*}} %[[THIS]], i64{{.*}} 16) |
| 72 | + |
| 73 | +struct C : B { |
| 74 | + ~C(); |
| 75 | +}; |
| 76 | + |
| 77 | +C::~C() { } |
| 78 | + |
| 79 | +// Base (D2) dtor for C: calls B's base dtor. |
| 80 | + |
| 81 | +// CIR: cir.func{{.*}} @_ZN1CD2Ev |
| 82 | +// CIR: %[[B:.*]] = cir.base_class_addr %[[THIS:.*]] : !cir.ptr<!rec_C> nonnull [0] -> !cir.ptr<!rec_B> |
| 83 | +// CIR: cir.call @_ZN1BD2Ev(%[[B]]) |
| 84 | + |
| 85 | +// LLVM: define{{.*}} void @_ZN1CD2Ev |
| 86 | +// LLVM: call void @_ZN1BD2Ev |
| 87 | + |
| 88 | +// OGCG: define{{.*}} @_ZN1CD2Ev |
| 89 | +// OGCG: call void @_ZN1BD2Ev |
| 90 | + |
| 91 | +// Complete (D1) dtor for C: just an alias because there are no virtual bases. |
| 92 | + |
| 93 | +// CIR: cir.func{{.*}} @_ZN1CD1Ev(!cir.ptr<!rec_C>) alias(@_ZN1CD2Ev) |
| 94 | +// This is defined above for LLVM and OGCG. |
| 95 | + |
| 96 | + |
| 97 | +// Deleting (D0) dtor for C: defers to the complete dtor but also calls operator delete. |
| 98 | + |
| 99 | +// CIR: cir.func{{.*}} @_ZN1CD0Ev |
| 100 | +// CIR: cir.call @_ZN1CD1Ev(%[[THIS:.*]]) nothrow : (!cir.ptr<!rec_C>) -> () |
| 101 | +// CIR: %[[THIS_VOID:.*]] = cir.cast bitcast %[[THIS]] : !cir.ptr<!rec_C> -> !cir.ptr<!void> |
| 102 | +// CIR: %[[SIZE:.*]] = cir.const #cir.int<16> |
| 103 | +// CIR: cir.call @_ZdlPvm(%[[THIS_VOID]], %[[SIZE]]) |
| 104 | + |
| 105 | +// LLVM: define{{.*}} void @_ZN1CD0Ev |
| 106 | +// LLVM: call void @_ZN1CD1Ev(ptr %[[THIS:.*]]) |
| 107 | +// LLVM: call void @_ZdlPvm(ptr %[[THIS]], i64 16) |
| 108 | + |
| 109 | +// OGCG: define{{.*}} @_ZN1CD0Ev |
| 110 | +// OGCG: call void @_ZN1CD1Ev(ptr{{.*}} %[[THIS:.*]]) |
| 111 | +// OGCG: call void @_ZdlPvm(ptr{{.*}} %[[THIS]], i64{{.*}} 16) |
| 112 | + |
| 113 | +namespace PR12798 { |
| 114 | + // A qualified call to a base class destructor should not undergo virtual |
| 115 | + // dispatch. Template instantiation used to lose the qualifier. |
| 116 | + struct A { virtual ~A(); }; |
| 117 | + template<typename T> void f(T *p) { p->A::~A(); } |
| 118 | + |
| 119 | + // CIR: cir.func{{.*}} @_ZN7PR127981fINS_1AEEEvPT_ |
| 120 | + // CIR: cir.call @_ZN7PR127981AD1Ev |
| 121 | + |
| 122 | + // LLVM: define{{.*}} @_ZN7PR127981fINS_1AEEEvPT_ |
| 123 | + // LLVM: call void @_ZN7PR127981AD1Ev |
| 124 | + |
| 125 | + // OGCG: define{{.*}} @_ZN7PR127981fINS_1AEEEvPT_ |
| 126 | + // OGCG: call void @_ZN7PR127981AD1Ev |
| 127 | + |
| 128 | + template void f(A*); |
| 129 | +} |
0 commit comments