|
| 1 | +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir |
| 2 | +// RUN: FileCheck --input-file=%t-before.cir %s --check-prefix=CIR-BEFORE-LPP |
| 3 | +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR |
| 4 | +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll |
| 5 | +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM |
| 6 | +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll |
| 7 | +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG |
| 8 | + |
| 9 | +// This duplicates a test case in global-init.cpp, but having it by itself |
| 10 | +// forces the __cxa_atexit function to be emitted for this case, which was |
| 11 | +// broken in the original implementation. |
| 12 | + |
| 13 | +struct ArrayDtor { |
| 14 | + ~ArrayDtor(); |
| 15 | +}; |
| 16 | + |
| 17 | +ArrayDtor arrDtor[16]; |
| 18 | + |
| 19 | +// CIR-BEFORE-LPP: cir.global external @arrDtor = #cir.zero : !cir.array<!rec_ArrayDtor x 16> |
| 20 | +// CIR-BEFORE-LPP-SAME: dtor { |
| 21 | +// CIR-BEFORE-LPP: %[[THIS:.*]] = cir.get_global @arrDtor : !cir.ptr<!cir.array<!rec_ArrayDtor x 16>> |
| 22 | +// CIR-BEFORE-LPP: cir.array.dtor %[[THIS]] : !cir.ptr<!cir.array<!rec_ArrayDtor x 16>> { |
| 23 | +// CIR-BEFORE-LPP: ^bb0(%[[ELEM:.*]]: !cir.ptr<!rec_ArrayDtor>): |
| 24 | +// CIR-BEFORE-LPP: cir.call @_ZN9ArrayDtorD1Ev(%[[ELEM]]) nothrow : (!cir.ptr<!rec_ArrayDtor>) -> () |
| 25 | +// CIR-BEFORE-LPP: cir.yield |
| 26 | +// CIR-BEFORE-LPP: } |
| 27 | +// CIR-BEFORE-LPP: } |
| 28 | + |
| 29 | +// CIR: cir.global external @arrDtor = #cir.zero : !cir.array<!rec_ArrayDtor x 16> {alignment = 16 : i64} |
| 30 | +// CIR: cir.func internal private @__cxx_global_array_dtor(%[[ARR_ARG:.*]]: !cir.ptr<!void> {{.*}}) { |
| 31 | +// CIR: %[[CONST15:.*]] = cir.const #cir.int<15> : !u64i |
| 32 | +// CIR: %[[BEGIN:.*]] = cir.cast array_to_ptrdecay %[[ARR_ARG]] : !cir.ptr<!void> -> !cir.ptr<!rec_ArrayDtor> |
| 33 | +// CIR: %[[END:.*]] = cir.ptr_stride %[[BEGIN]], %[[CONST15]] : (!cir.ptr<!rec_ArrayDtor>, !u64i) -> !cir.ptr<!rec_ArrayDtor> |
| 34 | +// CIR: %[[CUR_ADDR:.*]] = cir.alloca !cir.ptr<!rec_ArrayDtor>, !cir.ptr<!cir.ptr<!rec_ArrayDtor>>, ["__array_idx"] |
| 35 | +// CIR: cir.store %[[END]], %[[CUR_ADDR]] : !cir.ptr<!rec_ArrayDtor>, !cir.ptr<!cir.ptr<!rec_ArrayDtor>> |
| 36 | +// CIR: cir.do { |
| 37 | +// CIR: %[[CUR:.*]] = cir.load %[[CUR_ADDR]] : !cir.ptr<!cir.ptr<!rec_ArrayDtor>>, !cir.ptr<!rec_ArrayDtor> |
| 38 | +// CIR: cir.call @_ZN9ArrayDtorD1Ev(%[[CUR]]) nothrow : (!cir.ptr<!rec_ArrayDtor>) -> () |
| 39 | +// CIR: %[[NEG_ONE:.*]] = cir.const #cir.int<-1> : !s64i |
| 40 | +// CIR: %[[NEXT:.*]] = cir.ptr_stride %[[CUR]], %[[NEG_ONE]] : (!cir.ptr<!rec_ArrayDtor>, !s64i) -> !cir.ptr<!rec_ArrayDtor> |
| 41 | +// CIR: cir.store %[[NEXT]], %[[CUR_ADDR]] : !cir.ptr<!rec_ArrayDtor>, !cir.ptr<!cir.ptr<!rec_ArrayDtor>> |
| 42 | +// CIR: cir.yield |
| 43 | +// CIR: } while { |
| 44 | +// CIR: %[[CUR:.*]] = cir.load %[[CUR_ADDR]] : !cir.ptr<!cir.ptr<!rec_ArrayDtor>>, !cir.ptr<!rec_ArrayDtor> |
| 45 | +// CIR: %[[CMP:.*]] = cir.cmp(ne, %[[CUR]], %[[BEGIN]]) : !cir.ptr<!rec_ArrayDtor>, !cir.bool |
| 46 | +// CIR: cir.condition(%[[CMP]]) |
| 47 | +// CIR: } |
| 48 | +// CIR: cir.return |
| 49 | +// CIR: } |
| 50 | +// |
| 51 | +// CIR: cir.func internal private @__cxx_global_var_init() { |
| 52 | +// CIR: %[[ARR:.*]] = cir.get_global @arrDtor : !cir.ptr<!cir.array<!rec_ArrayDtor x 16>> |
| 53 | +// CIR: %[[DTOR:.*]] = cir.get_global @__cxx_global_array_dtor : !cir.ptr<!cir.func<(!cir.ptr<!void>)>> |
| 54 | +// CIR: %[[DTOR_CAST:.*]] = cir.cast bitcast %[[DTOR]] : !cir.ptr<!cir.func<(!cir.ptr<!void>)>> -> !cir.ptr<!cir.func<(!cir.ptr<!void>)>> |
| 55 | +// CIR: %[[ARR_CAST:.*]] = cir.cast bitcast %[[ARR]] : !cir.ptr<!cir.array<!rec_ArrayDtor x 16>> -> !cir.ptr<!void> |
| 56 | +// CIR: %[[HANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8> |
| 57 | +// CIR: cir.call @__cxa_atexit(%[[DTOR_CAST]], %[[ARR_CAST]], %[[HANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, !cir.ptr<i8>) -> () |
| 58 | + |
| 59 | +// LLVM: define internal void @__cxx_global_array_dtor(ptr %[[ARR_ARG:.*]]) { |
| 60 | +// LLVM: %[[BEGIN:.*]] = getelementptr %struct.ArrayDtor, ptr %[[ARR_ARG]], i32 0 |
| 61 | +// LLVM: %[[END:.*]] = getelementptr %struct.ArrayDtor, ptr %[[BEGIN]], i64 15 |
| 62 | +// LLVM: %[[CUR_ADDR:.*]] = alloca ptr |
| 63 | +// LLVM: store ptr %[[END]], ptr %[[CUR_ADDR]] |
| 64 | +// LLVM: br label %[[LOOP_BODY:.*]] |
| 65 | +// LLVM: [[LOOP_COND:.*]]: |
| 66 | +// LLVM: %[[CUR:.*]] = load ptr, ptr %[[CUR_ADDR]] |
| 67 | +// LLVM: %[[CMP:.*]] = icmp ne ptr %[[CUR]], %[[BEGIN]] |
| 68 | +// LLVM: br i1 %[[CMP]], label %[[LOOP_BODY]], label %[[LOOP_END:.*]] |
| 69 | +// LLVM: [[LOOP_BODY]]: |
| 70 | +// LLVM: %[[CUR:.*]] = load ptr, ptr %[[CUR_ADDR]] |
| 71 | +// LLVM: call void @_ZN9ArrayDtorD1Ev(ptr %[[CUR]]) #0 |
| 72 | +// LLVM: %[[PREV:.*]] = getelementptr %struct.ArrayDtor, ptr %[[CUR]], i64 -1 |
| 73 | +// LLVM: store ptr %[[PREV]], ptr %[[CUR_ADDR]] |
| 74 | +// LLVM: br label %[[LOOP_COND]] |
| 75 | +// LLVM: [[LOOP_END]]: |
| 76 | +// LLVM: ret void |
| 77 | +// LLVM: } |
| 78 | +// |
| 79 | +// LLVM: define internal void @__cxx_global_var_init() { |
| 80 | +// LLVM: call void @__cxa_atexit(ptr @__cxx_global_array_dtor, ptr @arrDtor, ptr @__dso_handle) |
| 81 | + |
| 82 | +// Note: OGCG defines these functions in reverse order of CIR->LLVM. |
| 83 | +// Note also: OGCG doesn't pass the address of the array to the destructor function. |
| 84 | +// Instead, it uses the global directly in the helper function. |
| 85 | + |
| 86 | +// OGCG: define internal void @__cxx_global_var_init() {{.*}} section ".text.startup" { |
| 87 | +// OGCG: call i32 @__cxa_atexit(ptr @__cxx_global_array_dtor, ptr null, ptr @__dso_handle) |
| 88 | + |
| 89 | +// OGCG: define internal void @__cxx_global_array_dtor(ptr noundef %[[ARG:.*]]) {{.*}} section ".text.startup" { |
| 90 | +// OGCG: entry: |
| 91 | +// OGCG: %[[UNUSED_ADDR:.*]] = alloca ptr |
| 92 | +// OGCG: store ptr %[[ARG]], ptr %[[UNUSED_ADDR]] |
| 93 | +// OGCG: br label %[[LOOP_BODY:.*]] |
| 94 | +// OGCG: [[LOOP_BODY]]: |
| 95 | +// OGCG: %[[PREV:.*]] = phi ptr [ getelementptr inbounds (%struct.ArrayDtor, ptr @arrDtor, i64 16), %entry ], [ %[[CUR:.*]], %[[LOOP_BODY]] ] |
| 96 | +// OGCG: %[[CUR]] = getelementptr inbounds %struct.ArrayDtor, ptr %[[PREV]], i64 -1 |
| 97 | +// OGCG: call void @_ZN9ArrayDtorD1Ev(ptr noundef nonnull align 1 dereferenceable(1) %[[CUR]]) |
| 98 | +// OGCG: %[[DONE:.*]] = icmp eq ptr %[[CUR]], @arrDtor |
| 99 | +// OGCG: br i1 %[[DONE]], label %[[LOOP_END:.*]], label %[[LOOP_BODY]] |
| 100 | +// OGCG: [[LOOP_END]]: |
| 101 | +// OGCG: ret void |
| 102 | +// OGCG: } |
| 103 | + |
| 104 | +// Common init function for all globals with default priority |
| 105 | + |
| 106 | +// CIR: cir.func private @_GLOBAL__sub_I_[[FILENAME:.*]]() { |
| 107 | +// CIR: cir.call @__cxx_global_var_init() : () -> () |
| 108 | + |
| 109 | +// LLVM: define void @_GLOBAL__sub_I_[[FILENAME:.*]]() |
| 110 | +// LLVM: call void @__cxx_global_var_init() |
| 111 | + |
| 112 | +// OGCG: define internal void @_GLOBAL__sub_I_[[FILENAME:.*]]() {{.*}} section ".text.startup" { |
| 113 | +// OGCG: call void @__cxx_global_var_init() |
0 commit comments