MLIR 24.0.0git
ACCDeclareCtorDtorConversion.cpp
Go to the documentation of this file.
1//===- ACCDeclareCtorDtorConversion.cpp - Declare ctor/dtor to LLVM -------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Convert ACC declare global constructors and destructors to LLVM functions
10// registered in llvm.mlir.global_ctors / llvm.mlir.global_dtors.
11//
12//===----------------------------------------------------------------------===//
13
17#include "mlir/IR/Builders.h"
18#include "mlir/IR/IRMapping.h"
19#include "mlir/Pass/Pass.h"
20
21namespace mlir {
22namespace acc {
23#define GEN_PASS_DEF_ACCDECLARECTORDTORCONVERSION
24#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
25} // namespace acc
26} // namespace mlir
27
28using namespace mlir;
29
30namespace {
31
32static void collectExistingGlobalCtors(
33 ModuleOp mod, SmallVectorImpl<Attribute> &ctors,
36 for (auto globalCtors : mod.getOps<LLVM::GlobalCtorsOp>()) {
37 ctors.append(globalCtors.getCtors().begin(), globalCtors.getCtors().end());
38 for (Attribute attr : globalCtors.getPriorities())
39 priorities.push_back(cast<IntegerAttr>(attr).getInt());
40 data.append(globalCtors.getData().begin(), globalCtors.getData().end());
41 globalCtorsOps.push_back(globalCtors);
42 }
43}
44
45static void collectExistingGlobalDtors(
46 ModuleOp mod, SmallVectorImpl<Attribute> &dtors,
49 for (auto globalDtors : mod.getOps<LLVM::GlobalDtorsOp>()) {
50 dtors.append(globalDtors.getDtors().begin(), globalDtors.getDtors().end());
51 for (Attribute attr : globalDtors.getPriorities())
52 priorities.push_back(cast<IntegerAttr>(attr).getInt());
53 data.append(globalDtors.getData().begin(), globalDtors.getData().end());
54 globalDtorsOps.push_back(globalDtors);
55 }
56}
57
58static void replaceGlobalCtors(ModuleOp mod, OpBuilder &builder,
60 ArrayRef<int32_t> priorities,
63 for (auto globalCtors : oldOps)
64 globalCtors.erase();
65 if (ctors.empty())
66 return;
67
68 builder.setInsertionPointToEnd(mod.getBody());
69 LLVM::GlobalCtorsOp::create(
70 builder, mod.getLoc(), builder.getArrayAttr(ctors),
71 builder.getI32ArrayAttr(priorities), builder.getArrayAttr(data));
72}
73
74static void replaceGlobalDtors(ModuleOp mod, OpBuilder &builder,
76 ArrayRef<int32_t> priorities,
79 for (auto globalDtors : oldOps)
80 globalDtors.erase();
81 if (dtors.empty())
82 return;
83
84 builder.setInsertionPointToEnd(mod.getBody());
85 LLVM::GlobalDtorsOp::create(
86 builder, mod.getLoc(), builder.getArrayAttr(dtors),
87 builder.getI32ArrayAttr(priorities), builder.getArrayAttr(data));
88}
89
90/// Create an llvm.func from an acc.global_ctor / acc.global_dtor region.
91/// Nested operations are cloned unchanged for later lowering.
92static LLVM::LLVMFuncOp createLLVMFunctionFromRegion(StringRef symName,
93 Region &region,
94 ModuleOp mod,
95 OpBuilder &builder) {
96 auto llvmVoidTy = LLVM::LLVMVoidType::get(mod.getContext());
97 auto funcTy = LLVM::LLVMFunctionType::get(llvmVoidTy, {}, /*isVarArg=*/false);
98 builder.setInsertionPointToEnd(mod.getBody());
99 auto newFunc = LLVM::LLVMFuncOp::create(builder, mod.getLoc(), symName,
100 funcTy, LLVM::Linkage::Internal);
101
102 Block *entry = newFunc.addEntryBlock(builder);
103 builder.setInsertionPointToStart(entry);
104
105 IRMapping mapping;
106 mapping.map(region.front().getArguments(), entry->getArguments());
107
108 for (Operation &op : region.front()) {
109 Operation *clonedOp = builder.clone(op, mapping);
110 mapping.map(op.getResults(), clonedOp->getResults());
111 }
112
113 Operation *accTerm = entry->getTerminator();
114 LLVM::ReturnOp::create(builder, mod.getLoc(), ValueRange{});
115 accTerm->erase();
116
117 return newFunc;
118}
119
120/// Declare extra runtime functions and call them from a defined llvm.func
121/// registered in llvm.mlir.global_ctors. The extra functions themselves stay
122/// declarations: llvm.mlir.global_ctors requires a function with a body.
123static LLVM::LLVMFuncOp
124createExtraConstructorCaller(ModuleOp mod, OpBuilder &builder,
125 ArrayRef<std::string> extraNames,
126 StringRef extraCtorName) {
127 auto llvmVoidTy = LLVM::LLVMVoidType::get(mod.getContext());
128 auto funcTy = LLVM::LLVMFunctionType::get(llvmVoidTy, {}, /*isVarArg=*/false);
129
130 builder.setInsertionPointToEnd(mod.getBody());
131 for (const std::string &name : extraNames) {
132 if (mod.lookupSymbol<LLVM::LLVMFuncOp>(name))
133 continue;
134 LLVM::LLVMFuncOp::create(builder, mod.getLoc(), name, funcTy,
135 LLVM::Linkage::External);
136 }
137
138 auto wrapper = LLVM::LLVMFuncOp::create(builder, mod.getLoc(), extraCtorName,
139 funcTy, LLVM::Linkage::Internal);
140 Block *entry = wrapper.addEntryBlock(builder);
141 builder.setInsertionPointToStart(entry);
142 for (const std::string &name : extraNames)
143 LLVM::CallOp::create(builder, mod.getLoc(), funcTy,
144 FlatSymbolRefAttr::get(mod.getContext(), name));
145 LLVM::ReturnOp::create(builder, mod.getLoc(), ValueRange{});
146 return wrapper;
147}
148
149struct ACCDeclareCtorDtorConversion
150 : public acc::impl::ACCDeclareCtorDtorConversionBase<
151 ACCDeclareCtorDtorConversion> {
152 using Base::Base;
153
154 void runOnOperation() override {
155 ModuleOp mod = getOperation();
156 OpBuilder builder{mod.getBodyRegion()};
157 SmallVector<Operation *> worklist;
158
159 SmallVector<Attribute, 8> allCtors;
160 SmallVector<int32_t, 8> ctorPriorities;
161 SmallVector<Attribute, 8> ctorData;
162 SmallVector<LLVM::GlobalCtorsOp, 4> globalCtorsOps;
163 collectExistingGlobalCtors(mod, allCtors, ctorPriorities, ctorData,
164 globalCtorsOps);
165 size_t existingCtorCount = allCtors.size();
166
167 SmallVector<Attribute, 8> allDtors;
168 SmallVector<int32_t, 8> dtorPriorities;
169 SmallVector<Attribute, 8> dtorData;
170 SmallVector<LLVM::GlobalDtorsOp, 4> globalDtorsOps;
171 collectExistingGlobalDtors(mod, allDtors, dtorPriorities, dtorData,
172 globalDtorsOps);
173 size_t existingDtorCount = allDtors.size();
174
175 mod.walk([&](acc::GlobalConstructorOp op) {
176 LLVM::LLVMFuncOp newCtor = createLLVMFunctionFromRegion(
177 op.getSymName(), op.getRegion(), mod, builder);
178 allCtors.push_back(
179 FlatSymbolRefAttr::get(mod.getContext(), newCtor.getSymName()));
180 ctorPriorities.push_back(priority);
181 // Null associated data: constructor always runs at load time.
182 ctorData.push_back(LLVM::ZeroAttr::get(builder.getContext()));
183 worklist.push_back(op.getOperation());
184 });
185
186 mod.walk([&](acc::GlobalDestructorOp op) {
187 if (generateDtors) {
188 LLVM::LLVMFuncOp newDtor = createLLVMFunctionFromRegion(
189 op.getSymName(), op.getRegion(), mod, builder);
190 allDtors.push_back(
191 FlatSymbolRefAttr::get(mod.getContext(), newDtor.getSymName()));
192 dtorPriorities.push_back(priority);
193 // Null associated data: destructor always runs at unload time.
194 dtorData.push_back(LLVM::ZeroAttr::get(builder.getContext()));
195 }
196 worklist.push_back(op.getOperation());
197 });
198
199 bool hasEntryPoint = !entryPointName.empty() &&
200 static_cast<bool>(mod.lookupSymbol(entryPointName));
201 SmallVector<std::string, 4> extraNames;
202 for (const auto &funcName : extraConstructors)
203 extraNames.push_back(funcName);
204 for (const auto &funcName : entryOnlyConstructors) {
205 if (hasEntryPoint)
206 extraNames.push_back(funcName);
207 }
208 if (!extraNames.empty()) {
209 LLVM::LLVMFuncOp extraCtor =
210 createExtraConstructorCaller(mod, builder, extraNames, extraCtorName);
211 allCtors.push_back(
212 FlatSymbolRefAttr::get(mod.getContext(), extraCtor.getSymName()));
213 ctorPriorities.push_back(priority);
214 ctorData.push_back(LLVM::ZeroAttr::get(builder.getContext()));
215 }
216
217 if (allCtors.size() > existingCtorCount)
218 replaceGlobalCtors(mod, builder, allCtors, ctorPriorities, ctorData,
219 globalCtorsOps);
220 if (allDtors.size() > existingDtorCount)
221 replaceGlobalDtors(mod, builder, allDtors, dtorPriorities, dtorData,
222 globalDtorsOps);
223
224 for (Operation *op : worklist)
225 op->erase();
226 }
227};
228
229} // namespace
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:34
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:112
ArrayAttr getI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:285
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Definition Builders.cpp:275
MLIRContext * getContext() const
Definition Builders.h:56
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
This class helps build Operations.
Definition Builders.h:210
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:581
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
result_range getResults()
Definition Operation.h:440
void erase()
Remove this operation from its parent block and delete it.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
Include the generated interface declarations.