Skip to content

Commit 435dbba

Browse files
authored
[CIR] Fix a problem with global array dtor lowering (#169416)
In the LoweringPrepare pass, the handling for global array destructor lowering was mishandling the insertion point, so that if this code needed to create a declaration for the __cxa_atexit function, that declaration was being created in the dtor region, rather than at module scope. This change fixes that.
1 parent 9cff3f5 commit 435dbba

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,7 @@ cir::FuncOp LoweringPreparePass::getOrCreateDtorFunc(CIRBaseBuilderTy &builder,
702702
cir::GlobalOp op,
703703
mlir::Region &dtorRegion,
704704
cir::CallOp &dtorCall) {
705+
mlir::OpBuilder::InsertionGuard guard(builder);
705706
assert(!cir::MissingFeatures::astVarDeclInterface());
706707
assert(!cir::MissingFeatures::opGlobalThreadLocal());
707708

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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

Comments
 (0)