clang 24.0.0git
LoweringPrepare.cpp
Go to the documentation of this file.
1//===- LoweringPrepare.cpp - pareparation work for LLVM lowering ----------===//
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#include "PassDetail.h"
11#include "mlir/IR/Attributes.h"
12#include "mlir/IR/BuiltinAttributeInterfaces.h"
13#include "mlir/IR/IRMapping.h"
14#include "mlir/IR/Location.h"
15#include "mlir/IR/Value.h"
16#include "clang/Basic/Cuda.h"
31#include "llvm/ADT/StringRef.h"
32#include "llvm/ADT/TypeSwitch.h"
33#include "llvm/IR/Instructions.h"
34#include "llvm/Support/ErrorHandling.h"
35#include "llvm/Support/Path.h"
36#include "llvm/Support/VersionTuple.h"
37
38#include <map>
39#include <memory>
40#include <optional>
41
42using namespace mlir;
43using namespace cir;
44
45namespace mlir {
46#define GEN_PASS_DEF_LOWERINGPREPARE
47#include "clang/CIR/Dialect/Passes.h.inc"
48} // namespace mlir
49
50static SmallString<128> getTransformedFileName(mlir::ModuleOp mlirModule) {
51 SmallString<128> fileName;
52
53 if (mlirModule.getSymName())
54 fileName = llvm::sys::path::filename(mlirModule.getSymName()->str());
55
56 if (fileName.empty())
57 fileName = "<null>";
58
59 for (size_t i = 0; i < fileName.size(); ++i) {
60 // Replace everything that's not [a-zA-Z0-9._] with a _. This set happens
61 // to be the set of C preprocessing numbers.
62 if (!clang::isPreprocessingNumberBody(fileName[i]))
63 fileName[i] = '_';
64 }
65
66 return fileName;
67}
68
69namespace {
70struct LoweringPreparePass
71 : public impl::LoweringPrepareBase<LoweringPreparePass> {
72 LoweringPreparePass() = default;
73
74 // `mlir::SymbolTableCollection` is move-only (it owns lazily-created
75 // `unique_ptr<SymbolTable>` entries), which makes the implicit copy
76 // constructor ill-formed. MLIR's `clonePass()` requires copy
77 // construction, so define one explicitly. Per-run state members
78 // (dynamic initializers, guard maps, symbol-table cache, etc.) all
79 // start fresh in the cloned pass, which matches MLIR convention for
80 // pass clones and is more correct than the previous default-generated
81 // behavior that silently copied them.
82 LoweringPreparePass(const LoweringPreparePass &other)
83 : impl::LoweringPrepareBase<LoweringPreparePass>(other) {}
84
85 void runOnOperation() override;
86
87 void runOnOp(mlir::Operation *op);
88 void lowerCastOp(cir::CastOp op);
89 void lowerComplexConjOp(cir::ComplexConjOp op);
90 void lowerComplexDivOp(cir::ComplexDivOp op);
91 void lowerComplexMulOp(cir::ComplexMulOp op);
92 void lowerComplexFDivOp(cir::ComplexFDivOp op);
93 void lowerComplexFMulOp(cir::ComplexFMulOp op);
94 void lowerGetGlobalOp(cir::GetGlobalOp op);
95 void lowerGlobalOp(cir::GlobalOp op);
96 void lowerThreeWayCmpOp(cir::CmpThreeWayOp op);
97 void lowerArrayDtor(cir::ArrayDtor op);
98 void lowerArrayCtor(cir::ArrayCtor op);
99 void lowerTrivialCopyCall(cir::CallOp op);
100 void lowerStoreOfConstAggregate(cir::StoreOp op);
101 void lowerLocalInitOp(cir::LocalInitOp op);
102 void lowerRegisterExitDtorOp(cir::RegisterExitDtorOp op);
103 void lowerStdOp(cir::StdOpInterface op);
104
105 /// Return the FuncOp called by `callOp`. Uses the cached `symbolTables`
106 /// member to avoid the O(M) module-wide scan that the static
107 /// `mlir::SymbolTable::lookupNearestSymbolFrom` would do per call.
108 cir::FuncOp getCalledFunction(cir::CallOp callOp);
109
110 /// Return a private constant cir::GlobalOp with the given type and initial
111 /// value, suitable for backing a memcpy-initialized local aggregate.
112 ///
113 /// If a global with `baseName` (or one of its `.<n>` versioned siblings)
114 /// already has a matching type and initial value, that global is reused.
115 /// Otherwise a new global is created with the next available `.<n>` suffix
116 /// (matching CIRGenBuilder::createVersionedGlobal and OGCG behavior).
117 cir::GlobalOp
118 getOrCreateConstAggregateGlobal(CIRBaseBuilderTy &builder, mlir::Location loc,
119 llvm::StringRef baseName, mlir::Type ty,
120 mlir::TypedAttr constant, uint64_t alignment);
121
122 /// Build the function that initializes the specified global
123 cir::FuncOp buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op);
124
125 /// When looking at the 'global' op, create the wrapper function.
126 void defineGlobalThreadLocalWrapper(cir::GlobalOp op, cir::FuncOp initAlias,
127 bool isVarDefinition);
128 /// Create an initialization alias for a thread-local variable.
129 cir::FuncOp defineGlobalThreadLocalInitAlias(cir::GlobalOp op,
130 cir::FuncOp aliasee);
131 /// Get the declaration for the 'wrapper' function for a global-TLS variable.
132 cir::FuncOp getOrCreateThreadLocalWrapper(CIRBaseBuilderTy &builder,
133 cir::GlobalOp op);
134 // Function that generates the get-global and 'if' condition on a guard
135 // variable for an init function that must run once: the global TLS init
136 // function, or the initializer of a C++20 named-module interface unit. This
137 // inserts an 'if' with the store at the beginning of the 'then' region, so
138 // inserts into the body should happen after that.
139 cir::IfOp buildInitGuardCheck(CIRBaseBuilderTy &builder, mlir::Location loc,
140 cir::GlobalOp guard, bool threadLocal);
141 /// Handle the dtor region by registering destructor with __cxa_atexit
142 cir::FuncOp getOrCreateDtorFunc(CIRBaseBuilderTy &builder, cir::GlobalOp op,
143 mlir::Region &dtorRegion,
144 cir::CallOp &dtorCall);
145 /// Create a `void __cxx_global_array_dtor[.N](void *)` helper after `op`,
146 /// with an empty entry block.
147 cir::FuncOp buildGlobalArrayDtorFunc(CIRBaseBuilderTy &builder,
148 mlir::Operation *op);
149
150 /// Build a function named `fnName` with the given linkage that calls each
151 /// of `initializers` in order, then returns, and register it in
152 /// `globalCtorList` under `priority`. With a `guard` byte, the calls run
153 /// only when it is still zero and set it first. Shared by the default
154 /// `_GLOBAL__sub_I_*` initializer function, the C++20 named-module
155 /// initializer, and the per-priority `_GLOBAL__I_<priority>` initializer
156 /// functions.
157 cir::FuncOp
158 buildGlobalInitCallerFunc(llvm::StringRef fnName,
159 cir::GlobalLinkageKind linkage,
160 llvm::ArrayRef<cir::FuncOp> initializers,
161 uint32_t priority, cir::GlobalOp guard = {});
162
163 /// Build a module init function that calls all the dynamic initializers.
164 void buildCXXGlobalInitFunc();
165 /// Build one `_GLOBAL__I_<priority>` function per distinct priority found
166 /// in `prioritizedDynamicInitializers`, in ascending priority order, and
167 /// register each with `globalCtorList`. The first of them, the highest
168 /// priority, calls `importedInits` before its own initializers and takes
169 /// them out of the vector, as classic CodeGen does.
170 void buildCXXGlobalPriorityInitFuncs(
171 llvm::SmallVectorImpl<cir::FuncOp> &importedInits);
172 // Build an init function for all of the ordered global thread local storage
173 // variables.
174 void buildCXXGlobalTlsFunc();
175
176 /// Materialize global ctor/dtor list
177 void buildGlobalCtorDtorList();
178
179 cir::FuncOp buildRuntimeFunction(
180 mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
181 cir::FuncType type,
182 cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage);
183
184 cir::GlobalOp getOrCreateRuntimeVariable(
185 mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
186 mlir::Type type,
187 cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage,
188 cir::VisibilityKind visibility = cir::VisibilityKind::Default);
189
190 /// ------------
191 /// CUDA registration related
192 /// ------------
193
194 llvm::StringMap<FuncOp> cudaKernelMap;
195 llvm::SmallVector<std::pair<cir::GlobalOp, cir::CUDAVarRegistrationInfoAttr>>
196 cudaDeviceVars;
197
198 /// Build the CUDA module constructor that registers the fat binary
199 /// with the CUDA runtime.
200 void buildCUDAModuleCtor();
201 std::optional<FuncOp> buildCUDAModuleDtor();
202 std::optional<FuncOp> buildHIPModuleDtor();
203 std::optional<FuncOp> buildCUDARegisterGlobals();
204 void buildCUDARegisterVars(cir::CIRBaseBuilderTy &builder,
205 FuncOp regGlobalFunc);
206 void buildCUDARegisterGlobalFunctions(cir::CIRBaseBuilderTy &builder,
207 FuncOp regGlobalFunc);
208
209 /// Handle static local variable initialization with guard variables.
210 void handleStaticLocal(cir::GlobalOp globalOp, cir::LocalInitOp localInitOp);
211
212 /// Handle the guard-protected init for static local/weak/linkonce-odr
213 /// variables.
214 void emitGuardedInit(CIRBaseBuilderTy &builder, cir::GlobalOp globalOp,
215 mlir::Region &ctorRegion, mlir::Region &dtorRegion);
216
217 /// Get or create __cxa_guard_acquire function.
218 cir::FuncOp getGuardAcquireFn(cir::PointerType guardPtrTy);
219
220 /// Get or create __cxa_guard_release function.
221 cir::FuncOp getGuardReleaseFn(cir::PointerType guardPtrTy);
222
223 /// Get or create __cxa_guard_abort function.
224 cir::FuncOp getGuardAbortFn(cir::PointerType guardPtrTy);
225
226 /// Get or create the __init_tls function.
227 cir::FuncOp getTlsInitFn();
228
229 // Create the __tls_guard variable.
230 cir::GlobalOp createGlobalThreadLocalGuard(CIRBaseBuilderTy &builder,
231 mlir::Location loc);
232
233 /// Create a guard global variable for a static local.
234 cir::GlobalOp createGuardGlobalOp(CIRBaseBuilderTy &builder,
235 mlir::Location loc, llvm::StringRef name,
236 cir::IntType guardTy,
237 cir::GlobalLinkageKind linkage);
238
239 /// Append 'global' to the module's llvm.used list to make sure it doesn't get
240 /// garbage-collected by the linker.
241 void addUsedGlobal(cir::GlobalOp global);
242
243 /// Get the guard variable for a static local declaration.
244 cir::GlobalOp getStaticLocalDeclGuardAddress(llvm::StringRef globalSymName) {
245 auto it = staticLocalDeclGuardMap.find(globalSymName);
246 if (it != staticLocalDeclGuardMap.end())
247 return it->second;
248 return nullptr;
249 }
250
251 /// Set the guard variable for a static local declaration.
252 void setStaticLocalDeclGuardAddress(llvm::StringRef globalSymName,
253 cir::GlobalOp guard) {
254 staticLocalDeclGuardMap[globalSymName] = guard;
255 }
256
257 /// Get or create the guard variable for a static local declaration.
258 cir::GlobalOp getOrCreateStaticLocalDeclGuardAddress(
259 CIRBaseBuilderTy &builder, cir::GlobalOp globalOp, StringRef guardName,
260 bool isLocalVarDecl, bool useInt8GuardVariable) {
261
262 cir::CIRDataLayout dataLayout(mlirModule);
263 cir::IntType guardTy;
264 clang::CharUnits guardAlignment;
265 // Guard variables are 64 bits in the generic ABI and size width on ARM
266 // (i.e. 32-bit on AArch32, 64-bit on AArch64).
267 if (useInt8GuardVariable) {
268 guardTy = cir::IntType::get(&getContext(), 8, /*isSigned=*/true);
269 guardAlignment = clang::CharUnits::One();
270 } else if (useARMGuardVarABI()) {
271 // Guard variables are size width on ARM (32-bit AArch32, 64-bit AArch64).
272 const unsigned sizeTypeSize =
273 getTargetInfo().getTypeWidth(getTargetInfo().getSignedSizeType());
274 guardTy =
275 cir::IntType::get(&getContext(), sizeTypeSize, /*isSigned=*/true);
276 guardAlignment =
277 clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy));
278 } else {
279 guardTy = cir::IntType::get(&getContext(), 64, /*isSigned=*/true);
280 guardAlignment =
281 clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy));
282 }
283 assert(guardTy && guardAlignment.getQuantity() != 0);
284
285 llvm::StringRef globalSymName = globalOp.getSymName();
286 cir::GlobalOp guard = getStaticLocalDeclGuardAddress(globalSymName);
287 if (!guard) {
288 // Create the guard variable with a zero-initializer.
289 guard = createGuardGlobalOp(builder, globalOp->getLoc(), guardName,
290 guardTy, globalOp.getLinkage());
291 guard.setInitialValueAttr(cir::IntAttr::get(guardTy, 0));
292 guard.setDSOLocal(globalOp.getDsoLocal());
293 guard.setAlignment(guardAlignment.getAsAlign().value());
294 guard.setTlsModel(globalOp.getTlsModel());
295 guard.setGlobalVisibility(globalOp.getGlobalVisibility());
296
297 // The ABI says: "It is suggested that it be emitted in the same COMDAT
298 // group as the associated data object." In practice, this doesn't work
299 // for non-ELF and non-Wasm object formats, so only do it for ELF and
300 // Wasm.
301 std::optional<llvm::StringRef> comdat = globalOp.getComdat();
302 const llvm::Triple &triple = getTargetInfo().getTriple();
303 if (!isLocalVarDecl && comdat.has_value() &&
304 (triple.isOSBinFormatELF() || triple.isOSBinFormatWasm())) {
305 guard.setComdat(comdat->empty() ? globalOp.getSymName() : *comdat);
306 } else if (triple.supportsCOMDAT() && guard.isWeakForLinker()) {
307 // A weak guard that isn't joining the variable's COMDAT gets its own,
308 // even if the variable has none (such as a plain weak variable).
309 guard.setSelfComdat();
310 }
311
312 setStaticLocalDeclGuardAddress(globalSymName, guard);
313 }
314 return guard;
315 }
316
317 /// Target/ABI facts sourced from the module's own attributes.
318 std::unique_ptr<cir::LowerModule> lowerModule;
319
320 const clang::TargetInfo &getTargetInfo() const {
321 assert(lowerModule && "LoweringPrepare requires a module with a triple");
322 return lowerModule->getTarget();
323 }
324
325 const clang::LangOptions &getLangOpts() const {
326 assert(lowerModule && "LoweringPrepare requires a module with LangOptions");
327 return lowerModule->getLangOpts();
328 }
329
330 /// Platform SDK version recorded on the module by CIRGen. An absent attribute
331 /// yields an empty VersionTuple, which is what a compilation without
332 /// -target-sdk-version behaves like: no version-gated feature is enabled.
333 /// Returns std::nullopt if the attribute is present but unparseable; an error
334 /// has been emitted and the pass marked as failed, so the caller must bail
335 /// out.
336 std::optional<llvm::VersionTuple> getSDKVersion() {
337 auto sdkVersionAttr = mlirModule->getAttrOfType<mlir::StringAttr>(
338 CIRDialect::getSDKVersionAttrName());
339 if (!sdkVersionAttr)
340 return llvm::VersionTuple();
341
342 llvm::VersionTuple sdkVersion;
343 if (sdkVersion.tryParse(sdkVersionAttr.getValue())) {
344 mlirModule->emitError("cannot parse platform SDK version from ")
345 << CIRDialect::getSDKVersionAttrName() << " = '"
346 << sdkVersionAttr.getValue() << "'";
347 signalPassFailure();
348 return std::nullopt;
349 }
350 return sdkVersion;
351 }
352
353 /// Tracks current module.
354 mlir::ModuleOp mlirModule;
355
356 /// Cached symbol tables used to avoid repeated O(M) module-wide scans
357 /// during per-call/per-global symbol lookups. Lazily populated on first
358 /// use. Pass methods access this directly rather than threading it
359 /// through helper signatures (see PR feedback on #195919).
360 ///
361 /// Invariant: every site that mutates the module's symbol table either
362 /// (a) keeps `symbolTables` in sync via
363 /// `symbolTables.getSymbolTable(mlirModule).insert(...)` (as
364 /// `getOrCreateConstAggregateGlobal` does), or (b) creates a symbol
365 /// that is never resolved through the cache later. Today
366 /// `buildRuntimeFunction` and `getOrCreateRuntimeVariable` fall in the
367 /// (b) bucket: their callers either use a separate map
368 /// (`cudaKernelMap`, `staticLocalDeclGuardMap`, `dynamicInitializers`)
369 /// or the static `mlir::SymbolTable::lookupNearestSymbolFrom`, never
370 /// the cached path. If a future change adds a cached lookup of a
371 /// freshly created symbol, the corresponding create site MUST move
372 /// to bucket (a) (insert into the cache or call
373 /// `invalidateSymbolTable`).
374 mlir::SymbolTableCollection symbolTables;
375
376 /// Tracks existing dynamic initializers.
377 llvm::StringMap<uint32_t> dynamicInitializerNames;
378 llvm::SmallVector<cir::FuncOp> dynamicInitializers;
379 /// Dynamic initializers with an explicit `init_priority` attribute,
380 /// grouped by priority (keys kept in ascending order). Each group is
381 /// emitted into its own `_GLOBAL__I_<priority>` function instead of being
382 /// folded into the single default-priority `_GLOBAL__sub_I_*` function.
383 std::map<unsigned, llvm::SmallVector<cir::FuncOp, 4>>
384 prioritizedDynamicInitializers;
385 llvm::SmallVector<cir::FuncOp> globalThreadLocalInitializers;
386 llvm::StringMap<cir::FuncOp> threadLocalWrappers;
387 llvm::StringMap<cir::FuncOp> threadLocalInitAliases;
388
389 /// Tracks guard variables for static locals (keyed by global symbol name).
390 llvm::StringMap<cir::GlobalOp> staticLocalDeclGuardMap;
391
392 llvm::StringMap<llvm::SmallVector<cir::GlobalOp, 1>> constAggregateGlobals;
393
394 /// List of ctors and their priorities to be called before main()
395 llvm::SmallVector<cir::GlobalCtorDtorEntry, 4> globalCtorList;
396 /// List of dtors and their priorities to be called when unloading module.
397 llvm::SmallVector<cir::GlobalCtorDtorEntry, 4> globalDtorList;
398
399 /// Returns true if the target uses ARM-style guard variables for static
400 /// local initialization (32-bit guard, check bit 0 only).
401 bool useARMGuardVarABI() const {
402 switch (lowerModule->getCXXABIKind()) {
403 case clang::TargetCXXABI::GenericARM:
404 case clang::TargetCXXABI::iOS:
405 case clang::TargetCXXABI::WatchOS:
406 case clang::TargetCXXABI::GenericAArch64:
407 case clang::TargetCXXABI::WebAssembly:
408 return true;
409 default:
410 return false;
411 }
412 }
413
414 /// Register `dtorFunc` to be called with `arg` when the program exits, or
415 /// when the current thread exits if `tls` is set.
416 cir::CallOp emitAtExitCall(CIRBaseBuilderTy &builder, mlir::Location loc,
417 cir::FuncOp dtorFunc, mlir::Value arg, bool tls) {
418 cir::GlobalOp handle;
419 cir::FuncOp fnAtExit;
420 cir::PointerType voidPtrTy = builder.getVoidPtrTy();
421 cir::PointerType voidFnPtrTy = builder.getVoidFnPtrTy({voidPtrTy});
422 {
423 mlir::OpBuilder::InsertionGuard guard(builder);
424 // Create a variable that binds the atexit to this shared object.
425 builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
426 handle = getOrCreateRuntimeVariable(
427 builder, "__dso_handle", loc, builder.getUIntNTy(8),
428 cir::GlobalLinkageKind::ExternalLinkage, cir::VisibilityKind::Hidden);
429
430 // Create a runtime helper function:
431 // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
432 cir::PointerType handlePtrTy = builder.getPointerTo(handle.getSymType());
433 IntType intTy = builder.getSIntNTy(32);
434 auto fnAtExitType =
435 cir::FuncType::get({voidFnPtrTy, voidPtrTy, handlePtrTy}, intTy);
436
437 llvm::StringLiteral nameAtExit = "__cxa_atexit";
438 if (tls)
439 nameAtExit = getTargetInfo().getTriple().isOSDarwin()
440 ? llvm::StringLiteral("_tlv_atexit")
441 : llvm::StringLiteral("__cxa_thread_atexit");
442
443 fnAtExit = buildRuntimeFunction(builder, nameAtExit, loc, fnAtExitType);
444 }
445
446 mlir::Value args[3];
447 auto dtorPtrTy = cir::PointerType::get(dtorFunc.getFunctionType());
448 args[0] = cir::GetGlobalOp::create(builder, loc, dtorPtrTy,
449 dtorFunc.getSymName());
450 args[0] = cir::CastOp::create(builder, loc, voidFnPtrTy,
451 cir::CastKind::bitcast, args[0]);
452 args[1] = cir::CastOp::create(builder, loc, voidPtrTy,
453 cir::CastKind::bitcast, arg);
454 args[2] = cir::GetGlobalOp::create(
455 builder, loc, builder.getPointerTo(handle.getSymType()),
456 handle.getSymName());
457 // TODO(cir): set the runtime calling convention on the __cxa_atexit call.
459 return builder.createCallOp(loc, fnAtExit, args);
460 }
461
462 void emitGlobalGuardedDtorRegion(CIRBaseBuilderTy &builder,
463 cir::GlobalOp global,
464 mlir::Region &dtorRegion, bool tls,
465 mlir::Block &entryBB) {
466 // If this is a simple call to a destructor, get the called function.
467 // Otherwise, create a helper function for the entire dtor region,
468 // replacing the current dtor region body with a call to the helper
469 // function.
470 cir::CallOp dtorCall;
471 cir::FuncOp dtorFunc =
472 getOrCreateDtorFunc(builder, global, dtorRegion, dtorCall);
473
474 // Replace the dtor (or helper) call with a call to
475 // __cxa_atexit(&dtor, &var, &__dso_handle)
476 builder.setInsertionPointAfter(dtorCall);
477 emitAtExitCall(builder, dtorCall.getLoc(), dtorFunc,
478 dtorCall.getArgOperand(0), tls);
479 dtorCall->erase();
480 mlir::Block &dtorBlock = dtorRegion.front();
481 entryBB.getOperations().splice(entryBB.end(), dtorBlock.getOperations(),
482 dtorBlock.begin(),
483 std::prev(dtorBlock.end()));
484 // make sure we leave the insert location after the operations we just
485 // inserted.
486 builder.setInsertionPointToEnd(&entryBB);
487 }
488
489 /// Emit the guarded initialization for a static local variable.
490 /// This handles the if/else structure after the guard byte check,
491 /// following OG's ItaniumCXXABI::EmitGuardedInit skeleton.
492 void emitCXXGuardedInitIf(CIRBaseBuilderTy &builder, cir::GlobalOp globalOp,
493 mlir::Region &ctorRegion, mlir::Region &dtorRegion,
494 bool isLocalVarDecl, mlir::Value guardPtr,
495 cir::PointerType guardPtrTy, bool threadsafe) {
496 auto loc = globalOp->getLoc();
497
498 // TODO(cir): set the runtime calling convention on the __cxa_guard_* calls.
500
501 // The semantics of dynamic initialization of variables with static or
502 // thread storage duration depends on whether they are declared at
503 // block-scope. The initialization of such variables at block-scope can be
504 // aborted with an exception and later retried (per C++20 [stmt.dcl]p4),
505 // and recursive entry to their initialization has undefined behavior (also
506 // per C++20 [stmt.dcl]p4). For such variables declared at non-block scope,
507 // exceptions lead to termination (per C++20 [except.terminate]p1), and
508 // recursive references to the variables are governed only by the lifetime
509 // rules (per C++20 [class.cdtor]p2), which means such references are
510 // perfectly fine as long as they avoid touching memory. As a result,
511 // block-scope variables must not be marked as initialized until after
512 // initialization completes (unless the mark is reverted following an
513 // exception), but non-block-scope variables must be marked prior to
514 // initialization so that recursive accesses during initialization do not
515 // restart initialization.
516
517 auto emitBody = [&]() {
518 // Emit the initializer and add a global destructor if appropriate.
519 mlir::Block *insertBlock = builder.getInsertionBlock();
520 if (!ctorRegion.empty()) {
521 assert(ctorRegion.hasOneBlock() && "Enforced by MaxSizedRegion<1>");
522
523 mlir::Block &block = ctorRegion.front();
524 insertBlock->getOperations().splice(
525 insertBlock->end(), block.getOperations(), block.begin(),
526 std::prev(block.end()));
527 }
528
529 if (!dtorRegion.empty()) {
530 assert(dtorRegion.hasOneBlock() && "Enforced by MaxSizedRegion<1>");
531
532 // Whether the destructor is registered with __cxa_thread_atexit depends
533 // on the variable being thread_local, not on the guard being
534 // thread-safe, so make sure we use getTlsModel here instead of
535 // threadsafe.
536 emitGlobalGuardedDtorRegion(builder, globalOp, dtorRegion,
537 globalOp.getTlsModel().has_value(),
538 *insertBlock);
539 }
540 builder.setInsertionPointToEnd(insertBlock);
541 ctorRegion.getBlocks().clear();
542 };
543
544 // Variables used when coping with thread-safe statics and exceptions.
545 if (threadsafe) {
546 // Call __cxa_guard_acquire.
547 cir::CallOp acquireCall = builder.createCallOp(
548 loc, getGuardAcquireFn(guardPtrTy), mlir::ValueRange{guardPtr});
549 mlir::Value acquireResult = acquireCall.getResult();
550
551 auto acquireZero = builder.getConstantInt(
552 loc, mlir::cast<cir::IntType>(acquireResult.getType()), 0);
553 auto shouldInit = builder.createCompare(loc, cir::CmpOpKind::ne,
554 acquireResult, acquireZero);
555
556 // Create the IfOp for the shouldInit check.
557 // Pass an empty callback to avoid auto-creating a yield terminator.
558 auto ifOp =
559 cir::IfOp::create(builder, loc, shouldInit, /*withElseRegion=*/false,
560 [](mlir::OpBuilder &, mlir::Location) {});
561 mlir::OpBuilder::InsertionGuard insertGuard(builder);
562 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
563
564 // An exception out of the initializer leaves the variable uninitialized
565 // and the next entry has to run the initializer again ([stmt.dcl]p4), so
566 // the guard has to be given back along the exceptional edge. Without
567 // this the guard stays held and that retry terminates instead.
568 //
569 // OG pushes an EH cleanup here and pops it after the initializer, which
570 // costs nothing when there is no unwind path. A cir.cleanup.scope is
571 // structural, so it is only worth building when there can be one.
572 // OG: CGF.EHStack.pushCleanup<CallGuardAbort>(EHCleanup, guard);
573 // ... CGF.PopCleanupBlock();
574 if (getLangOpts().Exceptions) {
575 cir::CleanupScopeOp::create(
576 builder, loc, cir::CleanupKind::EH,
577 [&](mlir::OpBuilder &, mlir::Location bodyLoc) {
578 emitBody();
579 builder.createYield(bodyLoc);
580 },
581 [&](mlir::OpBuilder &, mlir::Location cleanupLoc) {
582 cir::CallOp abortCall =
583 builder.createCallOp(cleanupLoc, getGuardAbortFn(guardPtrTy),
584 mlir::ValueRange{guardPtr});
585 // __cxa_guard_abort is declared noexcept, and marking it so keeps
586 // this out of an invoke: a cleanup that can throw while unwinding
587 // needs a terminate edge, which is dead weight here.
588 abortCall.setNothrowAttr(builder.getUnitAttr());
589 builder.createYield(cleanupLoc);
590 });
591 builder.setInsertionPointToEnd(&ifOp.getThenRegion().front());
592 } else {
593 emitBody();
594 }
595
596 // Call __cxa_guard_release. This cannot throw.
597 builder.createCallOp(loc, getGuardReleaseFn(guardPtrTy),
598 mlir::ValueRange{guardPtr});
599
600 builder.createYield(loc);
601 } else if (!isLocalVarDecl) {
602 // For non-local variables, store 1 into the first byte of the guard
603 // variable before the object initialization begins so that references
604 // to the variable during initialization don't restart initialization.
605 mlir::Value byteGuardPtr = builder.createBitcast(
606 guardPtr, cir::PointerType::get(builder.getSIntNTy(8)));
607 builder.createStore(loc,
608 builder.getConstantInt(loc, builder.getSIntNTy(8), 1),
609 byteGuardPtr);
610 emitBody();
611 } else {
612 emitBody();
613 // For local variables, store 1 into the first byte of the guard variable
614 // after the object initialization completes so that initialization is
615 // retried if initialization is interrupted by an exception.
616 mlir::Value byteGuardPtr = builder.createBitcast(
617 guardPtr, cir::PointerType::get(builder.getSIntNTy(8)));
618 builder.createStore(loc,
619 builder.getConstantInt(loc, builder.getSIntNTy(8), 1),
620 byteGuardPtr);
621 }
622
623 builder.createYield(loc); // Outermost IfOp
624 }
625};
626
627} // namespace
628
629cir::GlobalOp LoweringPreparePass::getOrCreateRuntimeVariable(
630 mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
631 mlir::Type type, cir::GlobalLinkageKind linkage,
632 cir::VisibilityKind visibility) {
633 cir::GlobalOp g = dyn_cast_or_null<cir::GlobalOp>(
634 mlir::SymbolTable::lookupNearestSymbolFrom(
635 mlirModule, mlir::StringAttr::get(mlirModule->getContext(), name)));
636 if (!g) {
637 g = cir::GlobalOp::create(builder, loc, name, type);
638 g.setLinkageAttr(
639 cir::GlobalLinkageKindAttr::get(builder.getContext(), linkage));
640 mlir::SymbolTable::setSymbolVisibility(
641 g, mlir::SymbolTable::Visibility::Private);
642 g.setGlobalVisibility(visibility);
643 }
644 return g;
645}
646
647cir::FuncOp LoweringPreparePass::buildRuntimeFunction(
648 mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
649 cir::FuncType type, cir::GlobalLinkageKind linkage) {
650 cir::FuncOp f = dyn_cast_or_null<FuncOp>(SymbolTable::lookupNearestSymbolFrom(
651 mlirModule, StringAttr::get(mlirModule->getContext(), name)));
652 if (!f) {
653 f = cir::FuncOp::create(builder, loc, name, type);
654 f.setLinkageAttr(
655 cir::GlobalLinkageKindAttr::get(builder.getContext(), linkage));
656 mlir::SymbolTable::setSymbolVisibility(
657 f, mlir::SymbolTable::Visibility::Private);
658
660 }
661 return f;
662}
663
664static mlir::Value lowerScalarToComplexCast(mlir::MLIRContext &ctx,
665 cir::CastOp op) {
666 cir::CIRBaseBuilderTy builder(ctx);
667 builder.setInsertionPoint(op);
668
669 mlir::Value src = op.getSrc();
670 mlir::Value imag = builder.getNullValue(src.getType(), op.getLoc());
671 return builder.createComplexCreate(op.getLoc(), src, imag);
672}
673
674static mlir::Value lowerComplexToScalarCast(mlir::MLIRContext &ctx,
675 cir::CastOp op,
676 cir::CastKind elemToBoolKind) {
677 cir::CIRBaseBuilderTy builder(ctx);
678 builder.setInsertionPoint(op);
679
680 mlir::Value src = op.getSrc();
681 if (!mlir::isa<cir::BoolType>(op.getType()))
682 return builder.createComplexReal(op.getLoc(), src);
683
684 // Complex cast to bool: (bool)(a+bi) => (bool)a || (bool)b
685 mlir::Value srcReal = builder.createComplexReal(op.getLoc(), src);
686 mlir::Value srcImag = builder.createComplexImag(op.getLoc(), src);
687
688 cir::BoolType boolTy = builder.getBoolTy();
689 mlir::Value srcRealToBool =
690 builder.createCast(op.getLoc(), elemToBoolKind, srcReal, boolTy);
691 mlir::Value srcImagToBool =
692 builder.createCast(op.getLoc(), elemToBoolKind, srcImag, boolTy);
693 return builder.createLogicalOr(op.getLoc(), srcRealToBool, srcImagToBool);
694}
695
696static mlir::Value lowerComplexToComplexCast(mlir::MLIRContext &ctx,
697 cir::CastOp op,
698 cir::CastKind scalarCastKind) {
699 CIRBaseBuilderTy builder(ctx);
700 builder.setInsertionPoint(op);
701
702 mlir::Value src = op.getSrc();
703 auto dstComplexElemTy =
704 mlir::cast<cir::ComplexType>(op.getType()).getElementType();
705
706 mlir::Value srcReal = builder.createComplexReal(op.getLoc(), src);
707 mlir::Value srcImag = builder.createComplexImag(op.getLoc(), src);
708
709 mlir::Value dstReal = builder.createCast(op.getLoc(), scalarCastKind, srcReal,
710 dstComplexElemTy);
711 mlir::Value dstImag = builder.createCast(op.getLoc(), scalarCastKind, srcImag,
712 dstComplexElemTy);
713 return builder.createComplexCreate(op.getLoc(), dstReal, dstImag);
714}
715
716void LoweringPreparePass::lowerCastOp(cir::CastOp op) {
717 mlir::MLIRContext &ctx = getContext();
718 mlir::Value loweredValue = [&]() -> mlir::Value {
719 switch (op.getKind()) {
720 case cir::CastKind::float_to_complex:
721 case cir::CastKind::int_to_complex:
722 return lowerScalarToComplexCast(ctx, op);
723 case cir::CastKind::float_complex_to_real:
724 case cir::CastKind::int_complex_to_real:
725 return lowerComplexToScalarCast(ctx, op, op.getKind());
726 case cir::CastKind::float_complex_to_bool:
727 return lowerComplexToScalarCast(ctx, op, cir::CastKind::float_to_bool);
728 case cir::CastKind::int_complex_to_bool:
729 return lowerComplexToScalarCast(ctx, op, cir::CastKind::int_to_bool);
730 case cir::CastKind::float_complex:
731 return lowerComplexToComplexCast(ctx, op, cir::CastKind::floating);
732 case cir::CastKind::float_complex_to_int_complex:
733 return lowerComplexToComplexCast(ctx, op, cir::CastKind::float_to_int);
734 case cir::CastKind::int_complex:
735 return lowerComplexToComplexCast(ctx, op, cir::CastKind::integral);
736 case cir::CastKind::int_complex_to_float_complex:
737 return lowerComplexToComplexCast(ctx, op, cir::CastKind::int_to_float);
738 default:
739 return nullptr;
740 }
741 }();
742
743 if (loweredValue) {
744 op.replaceAllUsesWith(loweredValue);
745 op.erase();
746 }
747}
748
749static mlir::Value buildComplexBinOpLibCall(
750 LoweringPreparePass &pass, CIRBaseBuilderTy &builder,
751 llvm::StringRef (*libFuncNameGetter)(llvm::APFloat::Semantics),
752 mlir::Location loc, cir::ComplexType ty, mlir::Value lhsReal,
753 mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag) {
754 cir::FPTypeInterface elementTy =
755 mlir::cast<cir::FPTypeInterface>(ty.getElementType());
756
757 llvm::StringRef libFuncName = libFuncNameGetter(
758 llvm::APFloat::SemanticsToEnum(elementTy.getFloatSemantics()));
759 llvm::SmallVector<mlir::Type, 4> libFuncInputTypes(4, elementTy);
760
761 cir::FuncType libFuncTy = cir::FuncType::get(libFuncInputTypes, ty);
762
763 // Insert a declaration for the runtime function to be used in Complex
764 // multiplication and division when needed
765 cir::FuncOp libFunc;
766 {
767 mlir::OpBuilder::InsertionGuard ipGuard{builder};
768 builder.setInsertionPointToStart(pass.mlirModule.getBody());
769 libFunc = pass.buildRuntimeFunction(builder, libFuncName, loc, libFuncTy);
770 }
771
772 // TODO(cir): set the runtime calling convention to this call.
774 cir::CallOp call =
775 builder.createCallOp(loc, libFunc, {lhsReal, lhsImag, rhsReal, rhsImag});
776 return call.getResult();
777}
778
779static llvm::StringRef
780getComplexDivLibCallName(llvm::APFloat::Semantics semantics) {
781 switch (semantics) {
782 case llvm::APFloat::S_IEEEhalf:
783 return "__divhc3";
784 case llvm::APFloat::S_IEEEsingle:
785 return "__divsc3";
786 case llvm::APFloat::S_IEEEdouble:
787 return "__divdc3";
788 case llvm::APFloat::S_PPCDoubleDouble:
789 return "__divtc3";
790 case llvm::APFloat::S_x87DoubleExtended:
791 return "__divxc3";
792 case llvm::APFloat::S_IEEEquad:
793 return "__divtc3";
794 default:
795 llvm_unreachable("unsupported floating point type");
796 }
797}
798
799static mlir::Value
800buildAlgebraicComplexDiv(CIRBaseBuilderTy &builder, mlir::Location loc,
801 mlir::Value lhsReal, mlir::Value lhsImag,
802 mlir::Value rhsReal, mlir::Value rhsImag) {
803 // (a+bi) / (c+di) = ((ac+bd)/(cc+dd)) + ((bc-ad)/(cc+dd))i
804 mlir::Value &a = lhsReal;
805 mlir::Value &b = lhsImag;
806 mlir::Value &c = rhsReal;
807 mlir::Value &d = rhsImag;
808
809 // The element type of the complex (lhs/rhs) determines whether floating
810 // point or integer ops are needed.
811 bool isFP = cir::isFPOrVectorOfFPType(a.getType());
812 auto mul = [&](mlir::Location l, mlir::Value x, mlir::Value y) {
813 return isFP ? builder.createFMul(l, x, y) : builder.createMul(l, x, y);
814 };
815 auto add = [&](mlir::Location l, mlir::Value x, mlir::Value y) {
816 return isFP ? builder.createFAdd(l, x, y) : builder.createAdd(l, x, y);
817 };
818 auto sub = [&](mlir::Location l, mlir::Value x, mlir::Value y) {
819 return isFP ? builder.createFSub(l, x, y) : builder.createSub(l, x, y);
820 };
821 auto div = [&](mlir::Location l, mlir::Value x, mlir::Value y) {
822 return isFP ? builder.createFDiv(l, x, y) : builder.createDiv(l, x, y);
823 };
824
825 mlir::Value ac = mul(loc, a, c); // a*c
826 mlir::Value bd = mul(loc, b, d); // b*d
827 mlir::Value cc = mul(loc, c, c); // c*c
828 mlir::Value dd = mul(loc, d, d); // d*d
829 mlir::Value acbd = add(loc, ac, bd); // ac+bd
830 mlir::Value ccdd = add(loc, cc, dd); // cc+dd
831 mlir::Value resultReal = div(loc, acbd, ccdd);
832
833 mlir::Value bc = mul(loc, b, c); // b*c
834 mlir::Value ad = mul(loc, a, d); // a*d
835 mlir::Value bcad = sub(loc, bc, ad); // bc-ad
836 mlir::Value resultImag = div(loc, bcad, ccdd);
837 return builder.createComplexCreate(loc, resultReal, resultImag);
838}
839
840static mlir::Value
842 mlir::Value lhsReal, mlir::Value lhsImag,
843 mlir::Value rhsReal, mlir::Value rhsImag) {
844 // Implements Smith's algorithm for complex division.
845 // SMITH, R. L. Algorithm 116: Complex division. Commun. ACM 5, 8 (1962).
846
847 // Let:
848 // - lhs := a+bi
849 // - rhs := c+di
850 // - result := lhs / rhs = e+fi
851 //
852 // The algorithm pseudocode looks like follows:
853 // if fabs(c) >= fabs(d):
854 // r := d / c
855 // tmp := c + r*d
856 // e = (a + b*r) / tmp
857 // f = (b - a*r) / tmp
858 // else:
859 // r := c / d
860 // tmp := d + r*c
861 // e = (a*r + b) / tmp
862 // f = (b*r - a) / tmp
863
864 mlir::Value &a = lhsReal;
865 mlir::Value &b = lhsImag;
866 mlir::Value &c = rhsReal;
867 mlir::Value &d = rhsImag;
868
869 // Smith's algorithm is only used for floating-point complex division.
870 assert(cir::isFPOrVectorOfFPType(a.getType()) &&
871 "range-reduction complex divide expects floating-point operands");
872
873 auto trueBranchBuilder = [&](mlir::OpBuilder &, mlir::Location) {
874 mlir::Value r = builder.createFDiv(loc, d, c); // r := d / c
875 mlir::Value rd = builder.createFMul(loc, r, d); // r*d
876 mlir::Value tmp = builder.createFAdd(loc, c, rd); // tmp := c + r*d
877
878 mlir::Value br = builder.createFMul(loc, b, r); // b*r
879 mlir::Value abr = builder.createFAdd(loc, a, br); // a + b*r
880 mlir::Value e = builder.createFDiv(loc, abr, tmp);
881
882 mlir::Value ar = builder.createFMul(loc, a, r); // a*r
883 mlir::Value bar = builder.createFSub(loc, b, ar); // b - a*r
884 mlir::Value f = builder.createFDiv(loc, bar, tmp);
885
886 mlir::Value result = builder.createComplexCreate(loc, e, f);
887 builder.createYield(loc, result);
888 };
889
890 auto falseBranchBuilder = [&](mlir::OpBuilder &, mlir::Location) {
891 mlir::Value r = builder.createFDiv(loc, c, d); // r := c / d
892 mlir::Value rc = builder.createFMul(loc, r, c); // r*c
893 mlir::Value tmp = builder.createFAdd(loc, d, rc); // tmp := d + r*c
894
895 mlir::Value ar = builder.createFMul(loc, a, r); // a*r
896 mlir::Value arb = builder.createFAdd(loc, ar, b); // a*r + b
897 mlir::Value e = builder.createFDiv(loc, arb, tmp);
898
899 mlir::Value br = builder.createFMul(loc, b, r); // b*r
900 mlir::Value bra = builder.createFSub(loc, br, a); // b*r - a
901 mlir::Value f = builder.createFDiv(loc, bra, tmp);
902
903 mlir::Value result = builder.createComplexCreate(loc, e, f);
904 builder.createYield(loc, result);
905 };
906
907 auto cFabs = cir::FAbsOp::create(builder, loc, c);
908 auto dFabs = cir::FAbsOp::create(builder, loc, d);
909 cir::CmpOp cmpResult =
910 builder.createCompare(loc, cir::CmpOpKind::ge, cFabs, dFabs);
911 auto ternary = cir::TernaryOp::create(builder, loc, cmpResult,
912 trueBranchBuilder, falseBranchBuilder);
913
914 return ternary.getResult();
915}
916
918 mlir::MLIRContext &context, const clang::TargetInfo &targetInfo,
919 const clang::LangOptions &langOpts, CIRBaseBuilderTy &builder,
920 mlir::Type elementType) {
921
922 auto getHigherPrecisionFPType = [&context](mlir::Type type) -> mlir::Type {
923 if (mlir::isa<cir::FP16Type>(type))
924 return cir::SingleType::get(&context);
925
926 if (mlir::isa<cir::SingleType>(type) || mlir::isa<cir::BF16Type>(type))
927 return cir::DoubleType::get(&context);
928
929 if (mlir::isa<cir::DoubleType>(type))
930 return cir::LongDoubleType::get(&context, type);
931
932 return type;
933 };
934
935 auto getFloatTypeSemantics =
936 [&langOpts, &targetInfo](mlir::Type type) -> const llvm::fltSemantics & {
937 const clang::TargetInfo &info = targetInfo;
938 if (mlir::isa<cir::FP16Type>(type))
939 return info.getHalfFormat();
940
941 if (mlir::isa<cir::BF16Type>(type))
942 return info.getBFloat16Format();
943
944 if (mlir::isa<cir::SingleType>(type))
945 return info.getFloatFormat();
946
947 if (mlir::isa<cir::DoubleType>(type))
948 return info.getDoubleFormat();
949
950 if (mlir::isa<cir::LongDoubleType>(type)) {
951 if (langOpts.OpenMP && langOpts.OpenMPIsTargetDevice)
952 llvm_unreachable("NYI Float type semantics with OpenMP");
953 return info.getLongDoubleFormat();
954 }
955
956 if (mlir::isa<cir::FP128Type>(type)) {
957 if (langOpts.OpenMP && langOpts.OpenMPIsTargetDevice)
958 llvm_unreachable("NYI Float type semantics with OpenMP");
959 return info.getFloat128Format();
960 }
961
962 llvm_unreachable("Unsupported float type semantics");
963 };
964
965 const mlir::Type higherElementType = getHigherPrecisionFPType(elementType);
966 const llvm::fltSemantics &elementTypeSemantics =
967 getFloatTypeSemantics(elementType);
968 const llvm::fltSemantics &higherElementTypeSemantics =
969 getFloatTypeSemantics(higherElementType);
970
971 // Check that the promoted type can handle the intermediate values without
972 // overflowing. This can be interpreted as:
973 // (SmallerType.LargestFiniteVal * SmallerType.LargestFiniteVal) * 2 <=
974 // LargerType.LargestFiniteVal.
975 // In terms of exponent it gives this formula:
976 // (SmallerType.LargestFiniteVal * SmallerType.LargestFiniteVal
977 // doubles the exponent of SmallerType.LargestFiniteVal)
978 if (llvm::APFloat::semanticsMaxExponent(elementTypeSemantics) * 2 + 1 <=
979 llvm::APFloat::semanticsMaxExponent(higherElementTypeSemantics)) {
980 return higherElementType;
981 }
982
983 // The intermediate values can't be represented in the promoted type
984 // without overflowing.
985 return {};
986}
987
988static mlir::Value lowerComplexFDiv(LoweringPreparePass &pass,
989 CIRBaseBuilderTy &builder,
990 mlir::Location loc, cir::ComplexFDivOp op,
991 mlir::Value lhsReal, mlir::Value lhsImag,
992 mlir::Value rhsReal, mlir::Value rhsImag,
993 mlir::MLIRContext &mlirCx,
994 const clang::TargetInfo &targetInfo) {
995 cir::ComplexType complexTy = op.getType();
996 cir::ComplexRangeKind range = op.getRange();
997 switch (range) {
998 case ComplexRangeKind::Full: {
1000 loc, complexTy, lhsReal, lhsImag, rhsReal,
1001 rhsImag);
1002 }
1003 case ComplexRangeKind::Improved: {
1004 return buildRangeReductionComplexDiv(builder, loc, lhsReal, lhsImag,
1005 rhsReal, rhsImag);
1006 }
1007 case ComplexRangeKind::Promoted: {
1008 mlir::Type originalElementType = complexTy.getElementType();
1009 mlir::Type higherPrecisionElementType =
1011 mlirCx, targetInfo, pass.getLangOpts(), builder,
1012 originalElementType);
1013
1014 if (!higherPrecisionElementType)
1015 return buildRangeReductionComplexDiv(builder, loc, lhsReal, lhsImag,
1016 rhsReal, rhsImag);
1017
1018 cir::CastKind floatingCastKind = cir::CastKind::floating;
1019 lhsReal = builder.createCast(floatingCastKind, lhsReal,
1020 higherPrecisionElementType);
1021 lhsImag = builder.createCast(floatingCastKind, lhsImag,
1022 higherPrecisionElementType);
1023 rhsReal = builder.createCast(floatingCastKind, rhsReal,
1024 higherPrecisionElementType);
1025 rhsImag = builder.createCast(floatingCastKind, rhsImag,
1026 higherPrecisionElementType);
1027
1028 mlir::Value algebraicResult = buildAlgebraicComplexDiv(
1029 builder, loc, lhsReal, lhsImag, rhsReal, rhsImag);
1030
1031 mlir::Value resultReal = builder.createComplexReal(loc, algebraicResult);
1032 mlir::Value resultImag = builder.createComplexImag(loc, algebraicResult);
1033
1034 mlir::Value finalReal =
1035 builder.createCast(floatingCastKind, resultReal, originalElementType);
1036 mlir::Value finalImag =
1037 builder.createCast(floatingCastKind, resultImag, originalElementType);
1038 return builder.createComplexCreate(loc, finalReal, finalImag);
1039 }
1040 case ComplexRangeKind::Basic: {
1041 return buildAlgebraicComplexDiv(builder, loc, lhsReal, lhsImag, rhsReal,
1042 rhsImag);
1043 }
1044 }
1045}
1046
1047void LoweringPreparePass::lowerComplexFDivOp(cir::ComplexFDivOp op) {
1048 cir::CIRBaseBuilderTy builder(getContext());
1049 builder.setInsertionPointAfter(op);
1050 mlir::Location loc = op.getLoc();
1051 mlir::TypedValue<cir::ComplexType> lhs = op.getLhs();
1052 mlir::TypedValue<cir::ComplexType> rhs = op.getRhs();
1053 mlir::Value lhsReal = builder.createComplexReal(loc, lhs);
1054 mlir::Value lhsImag = builder.createComplexImag(loc, lhs);
1055 mlir::Value rhsReal = builder.createComplexReal(loc, rhs);
1056 mlir::Value rhsImag = builder.createComplexImag(loc, rhs);
1057
1058 mlir::Value loweredResult =
1059 lowerComplexFDiv(*this, builder, loc, op, lhsReal, lhsImag, rhsReal,
1060 rhsImag, getContext(), getTargetInfo());
1061 op.replaceAllUsesWith(loweredResult);
1062 op.erase();
1063}
1064
1065void LoweringPreparePass::lowerComplexDivOp(cir::ComplexDivOp op) {
1066 cir::CIRBaseBuilderTy builder(getContext());
1067 builder.setInsertionPointAfter(op);
1068 mlir::Location loc = op.getLoc();
1069 mlir::TypedValue<cir::ComplexType> lhs = op.getLhs();
1070 mlir::TypedValue<cir::ComplexType> rhs = op.getRhs();
1071 mlir::Value lhsReal = builder.createComplexReal(loc, lhs);
1072 mlir::Value lhsImag = builder.createComplexImag(loc, lhs);
1073 mlir::Value rhsReal = builder.createComplexReal(loc, rhs);
1074 mlir::Value rhsImag = builder.createComplexImag(loc, rhs);
1075
1076 mlir::Value loweredResult = buildAlgebraicComplexDiv(
1077 builder, loc, lhsReal, lhsImag, rhsReal, rhsImag);
1078 op.replaceAllUsesWith(loweredResult);
1079 op.erase();
1080}
1081
1082static llvm::StringRef
1083getComplexMulLibCallName(llvm::APFloat::Semantics semantics) {
1084 switch (semantics) {
1085 case llvm::APFloat::S_IEEEhalf:
1086 return "__mulhc3";
1087 case llvm::APFloat::S_IEEEsingle:
1088 return "__mulsc3";
1089 case llvm::APFloat::S_IEEEdouble:
1090 return "__muldc3";
1091 case llvm::APFloat::S_PPCDoubleDouble:
1092 return "__multc3";
1093 case llvm::APFloat::S_x87DoubleExtended:
1094 return "__mulxc3";
1095 case llvm::APFloat::S_IEEEquad:
1096 return "__multc3";
1097 default:
1098 llvm_unreachable("unsupported floating point type");
1099 }
1100}
1101
1102static mlir::Value lowerComplexFMul(LoweringPreparePass &pass,
1103 CIRBaseBuilderTy &builder,
1104 mlir::Location loc, cir::ComplexFMulOp op,
1105 mlir::Value lhsReal, mlir::Value lhsImag,
1106 mlir::Value rhsReal, mlir::Value rhsImag) {
1107 // (a+bi) * (c+di) = (ac-bd) + (ad+bc)i
1108 mlir::Value resultRealLhs = builder.createFMul(loc, lhsReal, rhsReal); // ac
1109 mlir::Value resultRealRhs = builder.createFMul(loc, lhsImag, rhsImag); // bd
1110 mlir::Value resultImagLhs = builder.createFMul(loc, lhsReal, rhsImag); // ad
1111 mlir::Value resultImagRhs = builder.createFMul(loc, lhsImag, rhsReal); // bc
1112 mlir::Value resultReal =
1113 builder.createFSub(loc, resultRealLhs, resultRealRhs);
1114 mlir::Value resultImag =
1115 builder.createFAdd(loc, resultImagLhs, resultImagRhs);
1116 mlir::Value algebraicResult =
1117 builder.createComplexCreate(loc, resultReal, resultImag);
1118
1119 cir::ComplexType complexTy = op.getType();
1120 cir::ComplexRangeKind rangeKind = op.getRange();
1121 if (rangeKind == cir::ComplexRangeKind::Basic ||
1122 rangeKind == cir::ComplexRangeKind::Improved ||
1123 rangeKind == cir::ComplexRangeKind::Promoted)
1124 return algebraicResult;
1125
1127
1128 // Check whether the real part and the imaginary part of the result are both
1129 // NaN. If so, emit a library call to compute the multiplication instead.
1130 // We check a value against NaN by comparing the value against itself.
1131 mlir::Value resultRealIsNaN = builder.createIsNaN(loc, resultReal);
1132 mlir::Value resultImagIsNaN = builder.createIsNaN(loc, resultImag);
1133 mlir::Value resultRealAndImagAreNaN =
1134 builder.createLogicalAnd(loc, resultRealIsNaN, resultImagIsNaN);
1135
1136 return cir::TernaryOp::create(
1137 builder, loc, resultRealAndImagAreNaN,
1138 [&](mlir::OpBuilder &, mlir::Location) {
1139 mlir::Value libCallResult = buildComplexBinOpLibCall(
1140 pass, builder, &getComplexMulLibCallName, loc, complexTy,
1141 lhsReal, lhsImag, rhsReal, rhsImag);
1142 builder.createYield(loc, libCallResult);
1143 },
1144 [&](mlir::OpBuilder &, mlir::Location) {
1145 builder.createYield(loc, algebraicResult);
1146 })
1147 .getResult();
1148}
1149
1150void LoweringPreparePass::lowerComplexFMulOp(cir::ComplexFMulOp op) {
1151 cir::CIRBaseBuilderTy builder(getContext());
1152 builder.setInsertionPointAfter(op);
1153 mlir::Location loc = op.getLoc();
1154 mlir::TypedValue<cir::ComplexType> lhs = op.getLhs();
1155 mlir::TypedValue<cir::ComplexType> rhs = op.getRhs();
1156
1157 // (a+bi) * (c+di) = (ac-bd) + (ad+bc)i
1158 mlir::Value lhsReal = builder.createComplexReal(loc, lhs);
1159 mlir::Value lhsImag = builder.createComplexImag(loc, lhs);
1160 mlir::Value rhsReal = builder.createComplexReal(loc, rhs);
1161 mlir::Value rhsImag = builder.createComplexImag(loc, rhs);
1162 mlir::Value loweredResult = lowerComplexFMul(*this, builder, loc, op, lhsReal,
1163 lhsImag, rhsReal, rhsImag);
1164 op.replaceAllUsesWith(loweredResult);
1165 op.erase();
1166}
1167
1168void LoweringPreparePass::lowerComplexMulOp(cir::ComplexMulOp op) {
1169 cir::CIRBaseBuilderTy builder(getContext());
1170 builder.setInsertionPointAfter(op);
1171 mlir::Location loc = op.getLoc();
1172 mlir::TypedValue<cir::ComplexType> lhs = op.getLhs();
1173 mlir::TypedValue<cir::ComplexType> rhs = op.getRhs();
1174 mlir::Value lhsReal = builder.createComplexReal(loc, lhs);
1175 mlir::Value lhsImag = builder.createComplexImag(loc, lhs);
1176 mlir::Value rhsReal = builder.createComplexReal(loc, rhs);
1177 mlir::Value rhsImag = builder.createComplexImag(loc, rhs);
1178
1179 mlir::Value resultRealLhs = builder.createMul(loc, lhsReal, rhsReal); // ac
1180 mlir::Value resultRealRhs = builder.createMul(loc, lhsImag, rhsImag); // bd
1181 mlir::Value resultImagLhs = builder.createMul(loc, lhsReal, rhsImag); // ad
1182 mlir::Value resultImagRhs = builder.createMul(loc, lhsImag, rhsReal); // bc
1183 mlir::Value resultReal = builder.createSub(loc, resultRealLhs, resultRealRhs);
1184 mlir::Value resultImag = builder.createAdd(loc, resultImagLhs, resultImagRhs);
1185
1186 mlir::Value loweredResult =
1187 builder.createComplexCreate(loc, resultReal, resultImag);
1188
1189 op.replaceAllUsesWith(loweredResult);
1190 op.erase();
1191}
1192
1193void LoweringPreparePass::lowerComplexConjOp(cir::ComplexConjOp op) {
1194 mlir::Location loc = op.getLoc();
1195 CIRBaseBuilderTy builder(getContext());
1196 builder.setInsertionPointAfter(op);
1197
1198 mlir::Value operand = op.getOperand();
1199 mlir::Value operandReal = builder.createComplexReal(loc, operand);
1200 mlir::Value operandImag = builder.createComplexImag(loc, operand);
1201
1202 // The complex conjugate is formed by negating the imaginary component.
1203 const bool isFP = cir::isFPOrVectorOfFPType(operandReal.getType());
1204 mlir::Value resultImag = isFP ? builder.createFNeg(loc, operandImag)
1205 : builder.createMinus(loc, operandImag);
1206
1207 mlir::Value result =
1208 builder.createComplexCreate(loc, operandReal, resultImag);
1209 op->replaceAllUsesWith(mlir::ValueRange{result});
1210 op->erase();
1211}
1212
1213cir::FuncOp
1214LoweringPreparePass::buildGlobalArrayDtorFunc(CIRBaseBuilderTy &builder,
1215 mlir::Operation *op) {
1216 mlir::OpBuilder::InsertionGuard guard(builder);
1217 builder.setInsertionPointAfter(op);
1218 SmallString<256> fnName("__cxx_global_array_dtor");
1219 uint32_t cnt = dynamicInitializerNames[fnName]++;
1220 if (cnt)
1221 fnName += "." + std::to_string(cnt);
1222
1223 auto fnType =
1224 cir::FuncType::get({builder.getVoidPtrTy()}, builder.getVoidTy());
1225 cir::FuncOp dtorFunc =
1226 buildRuntimeFunction(builder, fnName, op->getLoc(), fnType,
1227 cir::GlobalLinkageKind::InternalLinkage);
1228
1229 SmallVector<mlir::NamedAttribute> paramAttrs;
1230 paramAttrs.push_back(
1231 builder.getNamedAttr("llvm.noundef", builder.getUnitAttr()));
1232 SmallVector<mlir::Attribute> argAttrDicts;
1233 argAttrDicts.push_back(
1234 mlir::DictionaryAttr::get(builder.getContext(), paramAttrs));
1235 dtorFunc.setArgAttrsAttr(
1236 mlir::ArrayAttr::get(builder.getContext(), argAttrDicts));
1237
1238 dtorFunc.addEntryBlock();
1239 return dtorFunc;
1240}
1241
1242void LoweringPreparePass::lowerRegisterExitDtorOp(cir::RegisterExitDtorOp op) {
1243 CIRBaseBuilderTy builder(getContext());
1244 mlir::Location loc = op.getLoc();
1245 auto object = symbolTables.lookupNearestSymbolFrom<cir::GlobalOp>(
1246 op, op.getObjectAttr());
1247 assert(object && "register_exit_dtor must reference a cir.global");
1248 mlir::Block &body = op.getBody().front();
1249 builder.setInsertionPoint(op);
1250
1251 // When the body is a single call on the object's address, register the
1252 // callee directly with that address.
1253 cir::FuncOp dtorFunc;
1254 mlir::Value arg;
1255 if (body.getOperations().size() == 3) {
1256 auto getGlobal = mlir::dyn_cast<cir::GetGlobalOp>(body.front());
1257 auto call = mlir::dyn_cast<cir::CallOp>(*std::next(body.begin()));
1258 if (getGlobal && call && getGlobal.getName() == op.getObject() &&
1259 call.getNumArgOperands() == 1 &&
1260 call.getArgOperand(0) == getGlobal.getResult()) {
1261 dtorFunc = getCalledFunction(call);
1262 if (dtorFunc)
1263 arg = builder.clone(*getGlobal)->getResult(0);
1264 }
1265 }
1266
1267 // Otherwise the body moves into a helper that ignores its argument, and the
1268 // helper is registered with a null argument.
1269 if (!dtorFunc) {
1270 dtorFunc = buildGlobalArrayDtorFunc(builder, object);
1271 mlir::Block *entryBB = &dtorFunc.getBody().front();
1272 entryBB->getOperations().splice(entryBB->end(), body.getOperations());
1273 auto yieldOp = mlir::cast<cir::YieldOp>(entryBB->getTerminator());
1274 {
1275 mlir::OpBuilder::InsertionGuard guard(builder);
1276 builder.setInsertionPoint(yieldOp);
1277 cir::ReturnOp::create(builder, yieldOp.getLoc());
1278 }
1279 yieldOp->erase();
1280 arg = builder.getNullPtr(builder.getVoidPtrTy(), loc);
1281 }
1282
1283 cir::CallOp atExitCall = emitAtExitCall(builder, loc, dtorFunc, arg,
1284 object.getTlsModel().has_value());
1285 // The atexit functions do not throw, and marking the call so keeps it out of
1286 // an invoke inside the initializer.
1287 atExitCall.setNothrowAttr(builder.getUnitAttr());
1288 op->erase();
1289}
1290
1291cir::FuncOp LoweringPreparePass::getOrCreateDtorFunc(CIRBaseBuilderTy &builder,
1292 cir::GlobalOp op,
1293 mlir::Region &dtorRegion,
1294 cir::CallOp &dtorCall) {
1295 mlir::OpBuilder::InsertionGuard guard(builder);
1297
1298 // Look for operations in dtorBlock
1299 mlir::Block &dtorBlock = dtorRegion.front();
1300
1301 // The first operation should be a get_global to retrieve the address
1302 // of the global variable we're destroying.
1303 auto opIt = dtorBlock.getOperations().begin();
1304 cir::GetGlobalOp ggop = mlir::cast<cir::GetGlobalOp>(*opIt);
1305
1306 // The simple case is just a call to a destructor, like this:
1307 //
1308 // %0 = cir.get_global %globalS : !cir.ptr<!rec_S>
1309 // cir.call %_ZN1SD1Ev(%0) : (!cir.ptr<!rec_S>) -> ()
1310 // (implicit cir.yield)
1311 //
1312 // That is, if the second operation is a call that takes the get_global
1313 // result as its only operand, and the only other operation is a yield, then
1314 // we can just return the called function.
1315 if (dtorBlock.getOperations().size() == 3) {
1316 auto callOp = mlir::dyn_cast<cir::CallOp>(&*(++opIt));
1317 auto yieldOp = mlir::dyn_cast<cir::YieldOp>(&*(++opIt));
1318 if (yieldOp && callOp && callOp.getNumOperands() == 1 &&
1319 callOp.getArgOperand(0) == ggop) {
1320 dtorCall = callOp;
1321 return getCalledFunction(callOp);
1322 }
1323 }
1324
1325 // Otherwise, we need to create a helper function to replace the dtor region.
1326 cir::FuncOp dtorFunc = buildGlobalArrayDtorFunc(builder, op);
1327 mlir::Block *entryBB = &dtorFunc.getBody().front();
1328
1329 // Move everything from the dtor region into the helper function.
1330 entryBB->getOperations().splice(entryBB->begin(), dtorBlock.getOperations(),
1331 dtorBlock.begin(), dtorBlock.end());
1332
1333 // Before erasing this, clone it back into the dtor region
1334 cir::GetGlobalOp dtorGGop =
1335 mlir::cast<cir::GetGlobalOp>(entryBB->getOperations().front());
1336 builder.setInsertionPointToStart(&dtorBlock);
1337 builder.clone(*dtorGGop.getOperation());
1338
1339 // Replace all uses of the help function's get_global with the function
1340 // argument.
1341 mlir::Value dtorArg = entryBB->getArgument(0);
1342 // The arg is a void*, but the destructor needs the variables type, so we
1343 // might need to introduce a cast.
1344 if (dtorArg.getType() != dtorGGop.getType()) {
1345 builder.setInsertionPointToStart(entryBB);
1346 dtorArg =
1347 builder.createBitcast(dtorGGop.getLoc(), dtorArg, dtorGGop.getType());
1348 }
1349 dtorGGop.replaceAllUsesWith(dtorArg);
1350 dtorGGop.erase();
1351
1352 // Replace the yield in the final block with a return
1353 mlir::Block &finalBlock = dtorFunc.getBody().back();
1354 auto yieldOp = cast<cir::YieldOp>(finalBlock.getTerminator());
1355 builder.setInsertionPoint(yieldOp);
1356 cir::ReturnOp::create(builder, yieldOp->getLoc());
1357 yieldOp->erase();
1358
1359 // Create a call to the helper function, passing the original get_global op
1360 // as the argument.
1361 cir::GetGlobalOp origGGop =
1362 mlir::cast<cir::GetGlobalOp>(dtorBlock.getOperations().front());
1363 builder.setInsertionPointAfter(origGGop);
1364 mlir::Value ggopResult = origGGop.getResult();
1365 dtorCall = builder.createCallOp(op.getLoc(), dtorFunc, ggopResult,
1366 /*attrs=*/{}, /*argAttrs=*/{},
1367 /*resAttrs=*/{}, dtorFunc.getCallingConv());
1368
1369 // Add a yield after the call.
1370 auto finalYield = cir::YieldOp::create(builder, op.getLoc());
1371
1372 // Erase everything after the yield.
1373 dtorBlock.getOperations().erase(std::next(mlir::Block::iterator(finalYield)),
1374 dtorBlock.end());
1375 dtorRegion.getBlocks().erase(std::next(dtorRegion.begin()), dtorRegion.end());
1376
1377 return dtorFunc;
1378}
1379
1380cir::FuncOp
1381LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
1382 // TODO(cir): Store this in the GlobalOp.
1383 // This should come from the MangleContext, but for now I'm hardcoding it.
1384 SmallString<256> fnName("__cxx_global_var_init");
1385 // Get a unique name
1386 uint32_t cnt = dynamicInitializerNames[fnName]++;
1387 if (cnt)
1388 fnName += "." + std::to_string(cnt);
1389
1390 // Create a variable initialization function.
1391 CIRBaseBuilderTy builder(getContext());
1392 builder.setInsertionPointAfter(op);
1393 cir::VoidType voidTy = builder.getVoidTy();
1394 auto fnType = cir::FuncType::get({}, voidTy);
1395 FuncOp f = buildRuntimeFunction(builder, fnName, op.getLoc(), fnType,
1396 cir::GlobalLinkageKind::InternalLinkage);
1397
1398 // Forward the constrained floating-point marker recorded on the global by
1399 // CodeGen onto the generated initializer function. The marker on the global
1400 // is no longer meaningful once its regions have been moved out, so clear it.
1401 if (op.getStrictfp()) {
1402 f->setAttr(cir::CIRDialect::getStrictFPAttrName(),
1403 mlir::UnitAttr::get(&getContext()));
1404 op.setStrictfp(false);
1405 }
1406
1407 // Move over the initialization code of the ctor region.
1408 // The ctor region may have multiple blocks when exception handling
1409 // scaffolding creates extra blocks (e.g., unreachable/trap blocks).
1410 // We move all operations from the first block (minus the yield) into
1411 // the function entry, and discard extra blocks (which contain only
1412 // unreachable terminators from EH cleanup paths).
1413 mlir::Block *entryBB = f.addEntryBlock();
1414 builder.setInsertionPointToStart(entryBB);
1415
1416 // If this is a global TLS variable (that is, declared at namespace scope), we
1417 // have to emit the guard variable here.
1418 bool needsTlsGuard = op.getTlsRefs() && op.getTlsRefs()->getGuardName();
1419
1420 // Non-TLS globals (such as weak/linkonce_odr/inline-ns scope variables) use
1421 // the __cxa_guard_acquire/release instead of the TLS guard.
1422 if (!needsTlsGuard && op.getDynamicInitGuard()) {
1423 if (!op.getDynamicInitInfo()) {
1424 op->emitError(
1425 "guarded global with a guard is missing 'dynamic_init_info'");
1426 return {};
1427 }
1428
1429 if (op.getDynamicInitInfo()->getLocal()) {
1430 op->emitError("guarded global is marked local");
1431 return {};
1432 }
1433 emitGuardedInit(builder, op, op.getCtorRegion(), op.getDtorRegion());
1434 builder.setInsertionPointToEnd(entryBB);
1435 cir::ReturnOp::create(builder, op.getLoc());
1436 return f;
1437 }
1438
1439 cir::IfOp guardIf;
1440 if (needsTlsGuard) {
1441 guardIf = buildInitGuardCheck(
1442 builder, op.getLoc(),
1443 getOrCreateStaticLocalDeclGuardAddress(
1444 builder, op, op.getTlsRefs()->getGuardName().getValue(),
1445 /*isLocalVarDecl=*/false,
1446 /*useInt8GuardVariable=*/op.hasInternalLinkage()),
1447 /*threadLocal=*/true);
1448 builder.setInsertionPointToEnd(&guardIf.getThenRegion().front());
1449 }
1450
1451 if (!op.getCtorRegion().empty()) {
1452 mlir::Block &block = op.getCtorRegion().front();
1453 mlir::Block *insertBlock = builder.getBlock();
1454 insertBlock->getOperations().splice(insertBlock->end(),
1455 block.getOperations(), block.begin(),
1456 std::prev(block.end()));
1457 }
1458
1459 // Register the destructor call with __cxa_atexit
1460 mlir::Region &dtorRegion = op.getDtorRegion();
1461 if (!dtorRegion.empty()) {
1463
1464 emitGlobalGuardedDtorRegion(builder, op, dtorRegion,
1465 op.getTlsModel().has_value(),
1466 *builder.getBlock());
1467 }
1468
1469 // If we're actually in the 'if' above, create a yield.
1470 if (needsTlsGuard) {
1471 builder.setInsertionPointToEnd(&guardIf.getThenRegion().back());
1472 cir::YieldOp::create(builder, op.getLoc());
1473 }
1474
1475 // Replace cir.yield with cir.return
1476 builder.setInsertionPointToEnd(entryBB);
1477 mlir::Operation *yieldOp = nullptr;
1478 if (!op.getCtorRegion().empty()) {
1479 mlir::Block &block = op.getCtorRegion().front();
1480 yieldOp = &block.getOperations().back();
1481 } else {
1482 assert(!dtorRegion.empty());
1483 mlir::Block &block = dtorRegion.front();
1484 yieldOp = &block.getOperations().back();
1485 }
1486
1487 assert(isa<cir::YieldOp>(*yieldOp));
1488 cir::ReturnOp::create(builder, yieldOp->getLoc());
1489 return f;
1490}
1491
1492cir::FuncOp
1493LoweringPreparePass::getGuardAcquireFn(cir::PointerType guardPtrTy) {
1494 // int __cxa_guard_acquire(__guard *guard_object);
1495 CIRBaseBuilderTy builder(getContext());
1496 mlir::OpBuilder::InsertionGuard ipGuard{builder};
1497 builder.setInsertionPointToStart(mlirModule.getBody());
1498 mlir::Location loc = mlirModule.getLoc();
1499 cir::IntType intTy = cir::IntType::get(&getContext(), 32, /*isSigned=*/true);
1500 auto fnType = cir::FuncType::get({guardPtrTy}, intTy);
1501 return buildRuntimeFunction(builder, "__cxa_guard_acquire", loc, fnType);
1502}
1503
1504cir::FuncOp
1505LoweringPreparePass::getGuardReleaseFn(cir::PointerType guardPtrTy) {
1506 // void __cxa_guard_release(__guard *guard_object);
1507 CIRBaseBuilderTy builder(getContext());
1508 mlir::OpBuilder::InsertionGuard ipGuard{builder};
1509 builder.setInsertionPointToStart(mlirModule.getBody());
1510 mlir::Location loc = mlirModule.getLoc();
1511 cir::VoidType voidTy = cir::VoidType::get(&getContext());
1512 auto fnType = cir::FuncType::get({guardPtrTy}, voidTy);
1513 return buildRuntimeFunction(builder, "__cxa_guard_release", loc, fnType);
1514}
1515
1516cir::FuncOp LoweringPreparePass::getGuardAbortFn(cir::PointerType guardPtrTy) {
1517 // void __cxa_guard_abort(__guard *guard_object);
1518 CIRBaseBuilderTy builder(getContext());
1519 mlir::OpBuilder::InsertionGuard ipGuard{builder};
1520 builder.setInsertionPointToStart(mlirModule.getBody());
1521 mlir::Location loc = mlirModule.getLoc();
1522 cir::VoidType voidTy = cir::VoidType::get(&getContext());
1523 auto fnType = cir::FuncType::get({guardPtrTy}, voidTy);
1524 return buildRuntimeFunction(builder, "__cxa_guard_abort", loc, fnType);
1525}
1526
1527cir::FuncOp LoweringPreparePass::getTlsInitFn() {
1528 // void __tls_init(void);
1529 CIRBaseBuilderTy builder(getContext());
1530 mlir::OpBuilder::InsertionGuard _{builder};
1531 builder.setInsertionPointToStart(mlirModule.getBody());
1532 mlir::Location loc = mlirModule.getLoc();
1533 auto fnType = builder.getVoidFnTy();
1534 return buildRuntimeFunction(builder, "__tls_init", loc, fnType,
1535 cir::GlobalLinkageKind::InternalLinkage);
1536}
1537
1538cir::GlobalOp LoweringPreparePass::createGuardGlobalOp(
1539 CIRBaseBuilderTy &builder, mlir::Location loc, llvm::StringRef name,
1540 cir::IntType guardTy, cir::GlobalLinkageKind linkage) {
1541 mlir::OpBuilder::InsertionGuard guard(builder);
1542 builder.setInsertionPointToStart(mlirModule.getBody());
1543 cir::GlobalOp g = cir::GlobalOp::create(builder, loc, name, guardTy);
1544 g.setLinkageAttr(
1545 cir::GlobalLinkageKindAttr::get(builder.getContext(), linkage));
1546 mlir::SymbolTable::setSymbolVisibility(
1547 g, mlir::SymbolTable::Visibility::Private);
1548 return g;
1549}
1550
1551void LoweringPreparePass::addUsedGlobal(cir::GlobalOp global) {
1552 CIRBaseBuilderTy builder(getContext());
1553 mlir::Attribute entry = cir::GlobalViewAttr::get(
1554 builder.getVoidPtrTy(),
1555 mlir::FlatSymbolRefAttr::get(global.getSymNameAttr()));
1556
1557 mlir::StringAttr usedName = builder.getStringAttr("llvm.used");
1558 cir::GlobalOp used =
1559 symbolTables.lookupSymbolIn<cir::GlobalOp>(mlirModule, usedName);
1560
1561 llvm::SmallVector<mlir::Attribute, 4> elements;
1562 if (used) {
1563 auto existing = mlir::cast<cir::ConstArrayAttr>(*used.getInitialValue());
1564 llvm::append_range(elements,
1565 mlir::cast<mlir::ArrayAttr>(existing.getElts()));
1566 }
1567 elements.push_back(entry);
1568
1569 cir::ArrayType arrayTy =
1570 cir::ArrayType::get(builder.getVoidPtrTy(), elements.size());
1571 cir::ConstArrayAttr initAttr = cir::ConstArrayAttr::get(
1572 arrayTy, mlir::ArrayAttr::get(&getContext(), elements));
1573
1574 if (used) {
1575 used.setSymType(arrayTy);
1576 used.setInitialValueAttr(initAttr);
1577 return;
1578 }
1579
1580 builder.setInsertionPointToStart(mlirModule.getBody());
1581 used = cir::GlobalOp::create(builder, mlirModule.getLoc(),
1582 usedName.getValue(), arrayTy);
1583 used.setLinkage(cir::GlobalLinkageKind::AppendingLinkage);
1584 used.setInitialValueAttr(initAttr);
1585 used.setSectionAttr(builder.getStringAttr("llvm.metadata"));
1586 symbolTables.getSymbolTable(mlirModule).insert(used);
1587}
1588
1589void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp,
1590 cir::LocalInitOp localInitOp) {
1591 CIRBaseBuilderTy builder(getContext());
1592
1593 builder.setInsertionPointAfter(localInitOp);
1594 mlir::Block *localInitBlock = builder.getInsertionBlock();
1595
1596 // Remove the terminator temporarily - we'll add it back at the end.
1597 mlir::Operation *ret = localInitBlock->getTerminator();
1598 ret->remove();
1599 // Note: These two insert-point-after sets are necessary, as the 'trailing'
1600 // operation has changed thanks to the terminator removal.
1601 builder.setInsertionPointAfter(localInitOp);
1602
1603 emitGuardedInit(builder, globalOp, localInitOp.getCtorRegion(),
1604 localInitOp.getDtorRegion());
1605
1606 // Insert the removed terminator back.
1607 builder.getInsertionBlock()->push_back(ret);
1608}
1609
1610void LoweringPreparePass::emitGuardedInit(CIRBaseBuilderTy &builder,
1611 cir::GlobalOp globalOp,
1612 mlir::Region &ctorRegion,
1613 mlir::Region &dtorRegion) {
1614 // Dynamic-initialization facts are materialized into a serializable
1615 // attribute by CIRGen, so this pass does not need a live ASTContext to
1616 // read them.
1617 std::optional<cir::DynamicInitInfoAttr> infoOption =
1618 globalOp.getDynamicInitInfo();
1619
1620 // The GlobalOp verifier requires 'dynamic_init_info' whenever a
1621 // 'dynamic_init_guard' is present, so verified IR reaching here has it.
1622 // Emit an error instead of asserting so malformed .cir gets a diagnostic.
1623 if (!infoOption.has_value()) {
1624 globalOp->emitError(
1625 "dynamic-init-guarded global is missing 'dynamic_init_info'");
1626 return;
1627 }
1628
1629 cir::DynamicInitInfoAttr info = infoOption.value();
1630
1631 // Inline variables that weren't instantiated from variable templates have
1632 // partially-ordered initialization within their translation unit.
1633 bool nonTemplateInline =
1634 info.getIsInline() && !info.isTemplateInstantiation();
1635
1636 // We only need to use thread-safe statics for local non-TLS variables and
1637 // inline variables; other global initialization is always single-threaded
1638 // or (through lazy dynamic loading in multiple threads) unsequenced.
1639 bool threadsafe = getLangOpts().ThreadsafeStatics &&
1640 (info.getLocal() || nonTemplateInline) &&
1641 info.getTls() == cir::TLSKind::None;
1642
1643 // If we have a global variable with internal linkage and thread-safe statics
1644 // are disabled, we can just let the guard variable be of type i8.
1645 bool useInt8GuardVariable = !threadsafe && globalOp.hasInternalLinkage();
1646
1647 // Create the guard variable if we don't already have it.
1648 cir::GlobalOp guard = getOrCreateStaticLocalDeclGuardAddress(
1649 builder, globalOp, globalOp.getDynamicInitGuard()->getName().getValue(),
1650 info.getLocal(), useInt8GuardVariable);
1651 if (!guard) {
1652 return;
1653 }
1654
1655 mlir::Value guardPtr =
1656 builder.createGetGlobal(guard, info.getTls() != cir::TLSKind::None);
1657
1658 // Test whether the variable has completed initialization.
1659 //
1660 // Itanium C++ ABI 3.3.2:
1661 // The following is pseudo-code showing how these functions can be used:
1662 // if (obj_guard.first_byte == 0) {
1663 // if ( __cxa_guard_acquire (&obj_guard) ) {
1664 // try {
1665 // ... initialize the object ...;
1666 // } catch (...) {
1667 // __cxa_guard_abort (&obj_guard);
1668 // throw;
1669 // }
1670 // ... queue object destructor with __cxa_atexit() ...;
1671 // __cxa_guard_release (&obj_guard);
1672 // }
1673 // }
1674 //
1675 // If threadsafe statics are enabled, but we don't have inline atomics, just
1676 // call __cxa_guard_acquire unconditionally. The "inline" check isn't
1677 // actually inline, and the user might not expect calls to __atomic libcalls.
1678 unsigned maxInlineWidthInBits = getTargetInfo().getMaxAtomicInlineWidth();
1679
1680 if (!threadsafe || maxInlineWidthInBits) {
1681 // Load the first byte of the guard variable.
1682 auto bytePtrTy = cir::PointerType::get(builder.getSIntNTy(8));
1683 mlir::Value bytePtr = builder.createBitcast(guardPtr, bytePtrTy);
1684 mlir::Value guardLoad = builder.createAlignedLoad(
1685 globalOp.getLoc(), bytePtr, *guard.getAlignment());
1686
1687 // Itanium ABI:
1688 // An implementation supporting thread-safety on multiprocessor
1689 // systems must also guarantee that references to the initialized
1690 // object do not occur before the load of the initialization flag.
1691 //
1692 // In LLVM, we do this by marking the load Acquire.
1693 if (threadsafe) {
1694 auto loadOp = mlir::cast<cir::LoadOp>(guardLoad.getDefiningOp());
1695 loadOp.setMemOrder(cir::MemOrder::Acquire);
1696 loadOp.setSyncScope(cir::SyncScopeKind::System);
1697 }
1698
1699 // For ARM, we should only check the first bit, rather than the entire byte:
1700 //
1701 // ARM C++ ABI 3.2.3.1:
1702 // To support the potential use of initialization guard variables
1703 // as semaphores that are the target of ARM SWP and LDREX/STREX
1704 // synchronizing instructions we define a static initialization
1705 // guard variable to be a 4-byte aligned, 4-byte word with the
1706 // following inline access protocol.
1707 // #define INITIALIZED 1
1708 // if ((obj_guard & INITIALIZED) != INITIALIZED) {
1709 // if (__cxa_guard_acquire(&obj_guard))
1710 // ...
1711 // }
1712 //
1713 // and similarly for ARM64:
1714 //
1715 // ARM64 C++ ABI 3.2.2:
1716 // This ABI instead only specifies the value bit 0 of the static guard
1717 // variable; all other bits are platform defined. Bit 0 shall be 0 when
1718 // the variable is not initialized and 1 when it is.
1719 if (useARMGuardVarABI() && !useInt8GuardVariable) {
1720 auto one = builder.getConstantInt(
1721 globalOp.getLoc(), mlir::cast<cir::IntType>(guardLoad.getType()), 1);
1722 guardLoad = builder.createAnd(globalOp.getLoc(), guardLoad, one);
1723 }
1724
1725 // Check if the first byte of the guard variable is zero.
1726 auto zero = builder.getConstantInt(
1727 globalOp.getLoc(), mlir::cast<cir::IntType>(guardLoad.getType()), 0);
1728 auto needsInit = builder.createCompare(globalOp.getLoc(),
1729 cir::CmpOpKind::eq, guardLoad, zero);
1730
1731 // Build the guarded initialization inside an if block.
1732 cir::IfOp::create(
1733 builder, globalOp.getLoc(), needsInit,
1734 /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location) {
1735 emitCXXGuardedInitIf(
1736 builder, globalOp, ctorRegion, dtorRegion, info.getLocal(),
1737 guardPtr, builder.getPointerTo(guard.getSymType()), threadsafe);
1738 });
1739 } else {
1740 // Threadsafe statics without inline atomics - call __cxa_guard_acquire
1741 // unconditionally without the initial guard byte check.
1742 globalOp->emitError("NYI: guarded init without inline atomics support");
1743 return;
1744 }
1745}
1746
1747void LoweringPreparePass::lowerLocalInitOp(cir::LocalInitOp initOp) {
1748
1749 // If we don't actually need to initialize anything anymore, we're done here.
1750 if (initOp.getCtorRegion().empty() && initOp.getDtorRegion().empty()) {
1751 initOp.erase();
1752 return;
1753 }
1754
1755 cir::GlobalOp globalOp = initOp.getReferencedGlobal(symbolTables);
1756 assert(globalOp && "No global-op found");
1757
1758 handleStaticLocal(globalOp, initOp);
1759
1760 // Remove the init local op, now that we've done everything we need with it.
1761 initOp.erase();
1762}
1763static bool isThreadWrapperReplaceable(const clang::TargetInfo &targetInfo) {
1764 // Note: Classic codegen needs to check that the VarDecl.getTLSKind() ==
1765 // TLS_Dynamic, but we don't attempt to emit the thread wrapper unless that is
1766 // already the case. So the only thing that matters here is whether it is
1767 // darwin.
1768 return targetInfo.getTriple().isOSDarwin();
1769}
1770
1771static cir::GlobalLinkageKind
1772getThreadLocalWrapperLinkage(GlobalOp op, const clang::TargetInfo &targetInfo) {
1773 if (isLocalLinkage(op.getLinkage()))
1774 return op.getLinkage();
1775
1776 if (isThreadWrapperReplaceable(targetInfo))
1777 if (!isLinkOnceLinkage(op.getLinkage()) &&
1778 !isWeakODRLinkage(op.getLinkage()))
1779 return op.getLinkage();
1780
1781 // If this isn't a TU in which this variable is defined, the thread wrapper is
1782 // discardable.
1783 if (op.isDeclaration())
1784 return cir::GlobalLinkageKind::LinkOnceODRLinkage;
1785 return cir::GlobalLinkageKind::WeakODRLinkage;
1786}
1787
1788cir::FuncOp
1789LoweringPreparePass::getOrCreateThreadLocalWrapper(CIRBaseBuilderTy &builder,
1790 GlobalOp op) {
1791 mlir::OpBuilder::InsertionGuard insertGuard(builder);
1792 builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
1793
1794 mlir::StringAttr wrapperName = op.getTlsRefs()->getWrapperName();
1795
1796 auto existingWrapperIter = threadLocalWrappers.find(wrapperName.getValue());
1797 if (existingWrapperIter != threadLocalWrappers.end())
1798 return existingWrapperIter->second;
1799
1800 // type is ptr-to-global-type(void);
1801 auto funcType = cir::FuncType::get({}, builder.getPointerTo(op.getSymType()));
1802 cir::FuncOp func =
1803 cir::FuncOp::create(builder, op.getLoc(), wrapperName, funcType);
1804
1805 cir::GlobalLinkageKind linkageKind =
1806 getThreadLocalWrapperLinkage(op, getTargetInfo());
1807 func.setLinkageAttr(
1808 cir::GlobalLinkageKindAttr::get(&getContext(), linkageKind));
1809
1810 if (getTargetInfo().getTriple().supportsCOMDAT() && func.isWeakForLinker())
1811 func.setSelfComdat();
1812
1813 mlir::SymbolTable::setSymbolVisibility(
1814 func, mlir::SymbolTable::Visibility::Private);
1815
1816 if (!isLocalLinkage(linkageKind)) {
1817 if (!isThreadWrapperReplaceable(getTargetInfo()) ||
1818 isLinkOnceLinkage(linkageKind) || isWeakODRLinkage(linkageKind) ||
1819 op.getGlobalVisibility() == cir::VisibilityKind::Hidden)
1820 func.setGlobalVisibility(cir::VisibilityKind::Hidden);
1821 }
1822 if (isThreadWrapperReplaceable(getTargetInfo()))
1823 op->emitError("Unhandled thread wrapper attributes for CC and Nounwind");
1824
1825 threadLocalWrappers.insert({wrapperName.getValue(), func});
1826 return func;
1827}
1828
1829void LoweringPreparePass::defineGlobalThreadLocalWrapper(cir::GlobalOp op,
1830 cir::FuncOp initAlias,
1831 bool isVarDefinition) {
1832 CIRBaseBuilderTy builder(getContext());
1833 cir::FuncOp wrapper = getOrCreateThreadLocalWrapper(builder, op);
1834 mlir::Block *entryBB = wrapper.addEntryBlock();
1835 builder.setInsertionPointToStart(entryBB);
1836 // If we are a situation where we have/need one, emit a call to the init
1837 // function.
1838 if (initAlias) {
1839 mlir::Location aliasLoc = initAlias.getLoc();
1840 if (!isVarDefinition) {
1841 // If this isn't a definition, we have to check that the alias exists.
1842 mlir::Value funcLoad = cir::GetGlobalOp::create(
1843 builder, aliasLoc, cir::PointerType::get(initAlias.getFunctionType()),
1844 initAlias.getSymName());
1845 mlir::Value nullCheck =
1846 builder.getNullValue(funcLoad.getType(), aliasLoc);
1847 mlir::Value cmp = cir::CmpOp::create(
1848 builder, aliasLoc, cir::CmpOpKind::ne, funcLoad, nullCheck);
1849 cir::IfOp::create(builder, aliasLoc, cmp, /*withElseRegion=*/false,
1850 [&](mlir::OpBuilder &, mlir::Location loc) {
1851 builder.createCallOp(aliasLoc, initAlias, {},
1852 /*attrs=*/{},
1853 /*argAttrs=*/{}, /*resAttrs=*/{},
1854 initAlias.getCallingConv());
1855 cir::YieldOp::create(builder, aliasLoc);
1856 });
1857 } else {
1858 // If this IS a definition, we know the alias exists, so we can just emit
1859 // a call to it.
1860 builder.createCallOp(aliasLoc, initAlias, {}, /*attrs=*/{},
1861 /*argAttrs=*/{}, /*resAttrs=*/{},
1862 initAlias.getCallingConv());
1863 }
1864 }
1865 cir::GetGlobalOp get = builder.createGetGlobal(op, /*tls=*/true);
1866 cir::ReturnOp::create(builder, op.getLoc(), {get});
1867}
1868
1869cir::FuncOp
1870LoweringPreparePass::defineGlobalThreadLocalInitAlias(cir::GlobalOp op,
1871 cir::FuncOp aliasee) {
1872 CIRBaseBuilderTy builder(getContext());
1873 mlir::OpBuilder::InsertionGuard insertGuard(builder);
1874 builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
1875 mlir::StringAttr aliasName = op.getTlsRefs()->getInitName();
1876 auto existingAliasIter = threadLocalInitAliases.find(aliasName.getValue());
1877
1878 if (existingAliasIter != threadLocalInitAliases.end())
1879 return existingAliasIter->second;
1880
1881 auto funcType = builder.getVoidFnTy();
1882 cir::FuncOp alias =
1883 cir::FuncOp::create(builder, op.getLoc(), aliasName, funcType);
1884 alias.setLinkage(op.getLinkage());
1885
1886 if (aliasee) {
1887 alias.setAliasee(aliasee.getSymName());
1888 } else {
1889 // If we don't have anything to alias (because this isn't a variable
1890 // definition!), we set this as just a function definition with no alias,
1891 // and extern-weak.
1892 alias.setLinkage(cir::GlobalLinkageKind::ExternalWeakLinkage);
1893 mlir::SymbolTable::setSymbolVisibility(
1894 alias, mlir::SymbolTable::Visibility::Private);
1895 }
1896
1897 threadLocalInitAliases.insert({aliasName.getValue(), alias});
1898 return alias;
1899}
1900
1901void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
1902 // Static locals are handled separately via guard variables.
1903 if (op.getDynamicInitGuard() &&
1904 (!op.getDynamicInitInfo() || op.getDynamicInitInfo()->getLocal()))
1905 return;
1906
1907 mlir::Region &ctorRegion = op.getCtorRegion();
1908 mlir::Region &dtorRegion = op.getDtorRegion();
1909 cir::FuncOp initAlias;
1910
1911 if (!ctorRegion.empty() || !dtorRegion.empty()) {
1912 // Build a variable initialization function and move the initialzation code
1913 // in the ctor region over.
1914 cir::FuncOp f = buildCXXGlobalVarDeclInitFunc(op);
1915
1916 // Clear the ctor and dtor region
1917 ctorRegion.getBlocks().clear();
1918 dtorRegion.getBlocks().clear();
1919
1921 if (op.getTlsModel()) {
1922 // There are two types of global TLS variables: 'ordered' and 'unordered'.
1923 // 'ordered' are the common case. A call to any of them causes all of the
1924 // initializers for all other 'ordered' ones to be called, via a
1925 // `__tls_init` function. So the 'init alias' that gets called in the
1926 // wrapper for these goes directly to `__tls_init`.
1927
1928 // 'Unordered' values are the case for variable templates. In this case,
1929 // their init alias goes directly to their init function. The FE generates
1930 // a guard variable for them (since they cannot use the global guard), so
1931 // we differentiate them that way.
1932
1933 if (op.getTlsRefs()->getGuardName()) {
1934 // Unordered: the alias is the function we just generated.
1935 initAlias = defineGlobalThreadLocalInitAlias(op, f);
1936 } else {
1937 // Ordered: Get the __tls_init, and make the alias to that.
1938 initAlias = defineGlobalThreadLocalInitAlias(op, getTlsInitFn());
1939 // Ordered inits also need to get called from the __tls_init function,
1940 // so we add the init function to the list, so that we can add them to
1941 // it later.
1942 globalThreadLocalInitializers.push_back(f);
1943 }
1944 } else if (std::optional<uint32_t> priority = op.getInitPriority()) {
1945 prioritizedDynamicInitializers[*priority].push_back(f);
1946 } else if (op.getDynamicInitInfo() &&
1947 (op.getDynamicInitInfo()->getIsInline() ||
1948 op.getDynamicInitInfo()->isTemplateInstantiation()) &&
1949 getTargetInfo().getTriple().supportsCOMDAT() &&
1950 !op.hasInternalLinkage()) {
1951 // Vague-linkage globals (inline variables and template instantiations)
1952 // get their own `llvm.global_ctors` entry keyed to the variable via
1953 // COMDAT associated data, rather than being folded into
1954 // `_GLOBAL__sub_I_*`. This ensures at most one initializer per DSO runs,
1955 // and lets the linker drop the initializer call along with a discarded
1956 // duplicate definition of the variable. Other guarded globals, such as a
1957 // plain weak variable, stay ordered with the rest of the translation
1958 // unit's initializers. Matches classic CodeGen's
1959 // EmitCXXGlobalVarDeclInitFunc.
1960 assert(!cir::MissingFeatures::msabi());
1961 globalCtorList.emplace_back(f.getSymName(),
1962 cir::GlobalCtorAttr::getDefaultPriority(),
1963 op.getSymName());
1964 // When COMDAT is used on ELF or in the MS C++ ABI, the key must be in
1965 // `llvm.used` to prevent the linker from garbage-collecting it (and,
1966 // with it, the global ctor entry keyed to it). Matches classic
1967 // CodeGen's EmitCXXGlobalVarDeclInitFunc.
1968 if (getTargetInfo().getTriple().isOSBinFormatELF() ||
1969 getTargetInfo().getCXXABI().isMicrosoft())
1970 addUsedGlobal(op);
1971
1972 // Since the global ctor entry is keyed to the variable, the init function
1973 // can be discarded along with it, so put it in the variable's COMDAT.
1974 std::optional<llvm::StringRef> comdat = op.getComdat();
1975 if (comdat && (getTargetInfo().getTriple().isOSBinFormatELF() ||
1976 getTargetInfo().getTriple().isOSBinFormatWasm()))
1977 f.setComdat(comdat->empty() ? op.getSymName() : *comdat);
1978 } else {
1979 dynamicInitializers.push_back(f);
1980 }
1981 } else if (op.getTlsModel() && op.getTlsRefs() && op.isDeclaration()) {
1982 // If this is a declaration and has no init function, we probably DO have to
1983 // create an alias that needs checking, so create it as extern-weak.
1984 initAlias = defineGlobalThreadLocalInitAlias(op, {});
1985 }
1986
1987 // We need a wrapper for TLS globals that MIGHT have a non-constant
1988 // initialization. The FE will have generated the TlsRefs for any with
1989 // known dynamic init, or unknown (extern) init.
1990 if (op.getTlsModel() && op.getTlsRefs())
1991 defineGlobalThreadLocalWrapper(op, initAlias, !op.isDeclaration());
1992
1994}
1995
1996void LoweringPreparePass::lowerGetGlobalOp(GetGlobalOp op) {
1997 if (!op.getTls())
1998 return;
1999 auto globalOp = mlir::cast<cir::GlobalOp>(
2000 symbolTables.lookupNearestSymbolFrom(op, op.getNameAttr()));
2001
2002 // Only global/namespace scope thread local variables need to have their
2003 // get-global operations rewritten to be calls to a wrapper function. If
2004 // we're not in a dynamic TLS (or one without the TLS markers), we can leave
2005 // this one as a get-global and return early.
2006 if (!globalOp.getTlsModel() || !globalOp.getTlsRefs())
2007 return;
2008
2009 // If this is a global TLS, we need to replace the call to 'get_global' with a
2010 // call to the wrapper function. Classic codegen figures out some cases where
2011 // we can omit this, but for now we're going to always put it in, as it is
2012 // effectively a no-op.
2013
2014 // The first 'GetGlobalOp' at the beginning of a ctor/dtor region on one of
2015 // these is for the purpose of creating/destroying. We want to skip replacing
2016 // THAT one, but leave all other get-global-ops in place, else
2017 // self-referential ops won't work right.
2018
2019 // Note that ctors/dtors are removed during this pass. We get away with these
2020 // checks because the only time that these situations can actually be true
2021 // (that is, the ctor/dtor region exist) is if we're in the process of
2022 // converting the ctor/dtor for this. If we're NOT doing that, the ctor/dtor
2023 // will have already disappeared.
2024 mlir::Operation *parentOp = op->getParentOp();
2025 if (parentOp == globalOp) {
2026 mlir::Region *ctorRegion = &globalOp.getCtorRegion();
2027 mlir::Region *dtorRegion = &globalOp.getDtorRegion();
2028
2029 if (!ctorRegion->empty() && &*ctorRegion->op_begin() == op.getOperation())
2030 return;
2031 if (!dtorRegion->empty() && &*dtorRegion->op_begin() == op.getOperation())
2032 return;
2033 }
2034
2035 CIRBaseBuilderTy builder(getContext());
2036 cir::FuncOp wrapperFunc = getOrCreateThreadLocalWrapper(builder, globalOp);
2037
2038 builder.setInsertionPoint(op);
2039 cir::CallOp call = builder.createCallOp(
2040 wrapperFunc.getLoc(),
2041 mlir::FlatSymbolRefAttr::get(wrapperFunc.getSymNameAttr()),
2042 wrapperFunc.getFunctionType().getReturnType(), {}, /*attrs=*/{},
2043 /*argAttrs=*/{}, /*resAttrs=*/{}, wrapperFunc.getCallingConv());
2044 op->replaceAllUsesWith(call);
2045 op.erase();
2046}
2047
2048void LoweringPreparePass::lowerThreeWayCmpOp(CmpThreeWayOp op) {
2049 CIRBaseBuilderTy builder(getContext());
2050 builder.setInsertionPointAfter(op);
2051
2052 mlir::Location loc = op->getLoc();
2053 cir::CmpThreeWayInfoAttr cmpInfo = op.getInfo();
2054
2055 mlir::Value ltRes =
2056 builder.getConstantInt(loc, op.getType(), cmpInfo.getLt());
2057 mlir::Value eqRes =
2058 builder.getConstantInt(loc, op.getType(), cmpInfo.getEq());
2059 mlir::Value gtRes =
2060 builder.getConstantInt(loc, op.getType(), cmpInfo.getGt());
2061
2062 mlir::Value transformedResult;
2063 if (cmpInfo.getOrdering() != CmpOrdering::Partial) {
2064 // Total ordering
2065 mlir::Value lt =
2066 builder.createCompare(loc, CmpOpKind::lt, op.getLhs(), op.getRhs());
2067 mlir::Value selectOnLt = builder.createSelect(loc, lt, ltRes, gtRes);
2068 mlir::Value eq =
2069 builder.createCompare(loc, CmpOpKind::eq, op.getLhs(), op.getRhs());
2070 transformedResult = builder.createSelect(loc, eq, eqRes, selectOnLt);
2071 } else {
2072 // Partial ordering
2073 cir::ConstantOp unorderedRes = builder.getConstantInt(
2074 loc, op.getType(), cmpInfo.getUnordered().value());
2075
2076 mlir::Value eq =
2077 builder.createCompare(loc, CmpOpKind::eq, op.getLhs(), op.getRhs());
2078 mlir::Value selectOnEq = builder.createSelect(loc, eq, eqRes, unorderedRes);
2079 mlir::Value gt =
2080 builder.createCompare(loc, CmpOpKind::gt, op.getLhs(), op.getRhs());
2081 mlir::Value selectOnGt = builder.createSelect(loc, gt, gtRes, selectOnEq);
2082 mlir::Value lt =
2083 builder.createCompare(loc, CmpOpKind::lt, op.getLhs(), op.getRhs());
2084 transformedResult = builder.createSelect(loc, lt, ltRes, selectOnGt);
2085 }
2086
2087 op.replaceAllUsesWith(transformedResult);
2088 op.erase();
2089}
2090
2091template <typename AttributeTy>
2092static llvm::SmallVector<mlir::Attribute>
2093prepareCtorDtorAttrList(mlir::MLIRContext *context,
2096 for (const auto &entry : list) {
2097 mlir::FlatSymbolRefAttr associated;
2098 if (!entry.associated.empty())
2099 associated = mlir::FlatSymbolRefAttr::get(context, entry.associated);
2100 attrs.push_back(
2101 AttributeTy::get(context, entry.name, entry.priority, associated));
2102 }
2103 return attrs;
2104}
2105
2106void LoweringPreparePass::buildGlobalCtorDtorList() {
2107 if (!globalCtorList.empty()) {
2108 llvm::SmallVector<mlir::Attribute> globalCtors =
2110 globalCtorList);
2111
2112 mlirModule->setAttr(cir::CIRDialect::getGlobalCtorsAttrName(),
2113 mlir::ArrayAttr::get(&getContext(), globalCtors));
2114 }
2115
2116 if (!globalDtorList.empty()) {
2117 llvm::SmallVector<mlir::Attribute> globalDtors =
2119 globalDtorList);
2120 mlirModule->setAttr(cir::CIRDialect::getGlobalDtorsAttrName(),
2121 mlir::ArrayAttr::get(&getContext(), globalDtors));
2122 }
2123}
2124
2125cir::GlobalOp
2126LoweringPreparePass::createGlobalThreadLocalGuard(CIRBaseBuilderTy &builder,
2127 mlir::Location loc) {
2128 mlir::OpBuilder::InsertionGuard guard(builder);
2129 builder.setInsertionPointToStart(mlirModule.getBody());
2130
2131 // The TLS Guard is always an Int8Ty.
2132 cir::IntType guardTy = builder.getSIntNTy(8);
2133 auto g = cir::GlobalOp::create(builder, loc, "__tls_guard", guardTy);
2134 g.setLinkageAttr(cir::GlobalLinkageKindAttr::get(
2135 builder.getContext(), cir::GlobalLinkageKind::InternalLinkage));
2136 g.setAlignment(clang::CharUnits::One().getAsAlign().value());
2137
2138 if (auto defTlsModel = mlirModule->getAttrOfType<TLSModelAttr>(
2139 cir::CIRDialect::getDefaultTlsModelAttrName())) {
2140 g.setTlsModel(defTlsModel.getValue());
2141 } else {
2142 // Default value, unless overridden in the IR/by the frontend.
2143 g.setTlsModel(TLSModel::GeneralDynamic);
2144 }
2145
2146 g.setInitialValueAttr(cir::IntAttr::get(guardTy, 0));
2147 return g;
2148}
2149
2150cir::IfOp LoweringPreparePass::buildInitGuardCheck(CIRBaseBuilderTy &builder,
2151 mlir::Location loc,
2152 cir::GlobalOp guard,
2153 bool threadLocal) {
2154 cir::GetGlobalOp getGuard = builder.createGetGlobal(guard, threadLocal);
2155 mlir::Value getGuardValue = getGuard;
2156
2157 // Classic codegen always just loads the first byte of the guard instead of
2158 // the whole thing. __tls_guard is already only 8 bits, but for the case of
2159 // unordered TLS, it gets created as 64 bits.
2160 if (guard.getSymType() != builder.getSIntNTy(8))
2161 getGuardValue = builder.createBitcast(
2162 getGuard, cir::PointerType::get(builder.getSIntNTy(8)));
2163
2164 mlir::Value guardLoad =
2165 builder.createAlignedLoad(loc, getGuardValue, *guard.getAlignment());
2166 auto zero = builder.getConstantInt(loc, builder.getSIntNTy(8), 0);
2167 cir::CmpOp compare =
2168 builder.createCompare(loc, cir::CmpOpKind::eq, guardLoad, zero);
2169 return cir::IfOp::create(
2170 builder, loc, compare,
2171 /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location loc) {
2172 // Classic codegen still does this store as a i8, but it doesn't seem
2173 // reasonable to do an i8 store into a 64 bit value?
2174 builder.createStore(
2175 loc, builder.getConstantInt(loc, guard.getSymType(), 1), getGuard);
2176 });
2177}
2178
2179void LoweringPreparePass::buildCXXGlobalTlsFunc() {
2180 if (globalThreadLocalInitializers.empty())
2181 return;
2182
2183 // The global-ordered-init function for TLS variables just calls each of the
2184 // init-functions in order after doing a guard.
2185
2186 cir::FuncOp tlsInit = getTlsInitFn();
2187 mlir::Location loc = tlsInit.getLoc();
2188 CIRBaseBuilderTy builder(getContext());
2189 mlir::Block *entryBB = tlsInit.addEntryBlock();
2190 builder.setInsertionPointToStart(entryBB);
2191
2192 cir::IfOp ifOperation = buildInitGuardCheck(
2193 builder, loc, createGlobalThreadLocalGuard(builder, loc),
2194 /*threadLocal=*/true);
2195
2196 // Emit the body of the guarded spot.
2197 builder.setInsertionPointToEnd(&ifOperation.getThenRegion().front());
2198 for (cir::FuncOp initFunc : globalThreadLocalInitializers)
2199 builder.createCallOp(loc, initFunc, {}, /*attrs=*/{}, /*argAttrs=*/{},
2200 /*resAttrs=*/{}, initFunc.getCallingConv());
2201 cir::YieldOp::create(builder, loc);
2202
2203 builder.setInsertionPointAfter(ifOperation);
2204 cir::ReturnOp::create(builder, loc);
2205}
2206
2207/// Compute the zero-padded priority suffix used to name priority-specific
2208/// global init functions, so that the function names also sort in priority
2209/// order (e.g. 200 -> "000200").
2210static std::string getPrioritySuffix(unsigned priority) {
2211 assert(priority <= 65535 && "Priority should always be <= 65535.");
2212 std::string prioritySuffix = llvm::utostr(priority);
2213 assert(prioritySuffix.size() < 6);
2214 prioritySuffix = std::string(6 - prioritySuffix.size(), '0') + prioritySuffix;
2215 return prioritySuffix;
2216}
2217
2218cir::FuncOp LoweringPreparePass::buildGlobalInitCallerFunc(
2219 llvm::StringRef fnName, cir::GlobalLinkageKind linkage,
2220 llvm::ArrayRef<cir::FuncOp> initializers, uint32_t priority,
2221 cir::GlobalOp guard) {
2222 CIRBaseBuilderTy builder(getContext());
2223 builder.setInsertionPointToEnd(&mlirModule.getBodyRegion().back());
2224 auto fnType = cir::FuncType::get({}, builder.getVoidTy());
2225 cir::FuncOp fn = buildRuntimeFunction(builder, fnName, mlirModule.getLoc(),
2226 fnType, linkage);
2227 builder.setInsertionPointToStart(fn.addEntryBlock());
2228 mlir::Location loc = fn.getLoc();
2229 auto emitCalls = [&] {
2230 for (cir::FuncOp init : initializers)
2231 builder.createCallOp(init.getLoc(), init, {}, /*attrs=*/{},
2232 /*argAttrs=*/{}, /*resAttrs=*/{},
2233 init.getCallingConv());
2234 };
2235 if (guard) {
2236 cir::IfOp guardIf =
2237 buildInitGuardCheck(builder, loc, guard, /*threadLocal=*/false);
2238 builder.setInsertionPointToEnd(&guardIf.getThenRegion().front());
2239 emitCalls();
2240 cir::YieldOp::create(builder, loc);
2241 builder.setInsertionPointAfter(guardIf);
2242 } else {
2243 emitCalls();
2244 }
2245 cir::ReturnOp::create(builder, loc);
2246 globalCtorList.emplace_back(fnName, priority);
2247 return fn;
2248}
2249
2250void LoweringPreparePass::buildCXXGlobalPriorityInitFuncs(
2251 llvm::SmallVectorImpl<cir::FuncOp> &importedInits) {
2252 // std::map keeps priorities in ascending order, so each group is already
2253 // ready to emit into its own function, named after its priority so that
2254 // the functions are naturally ordered relative to one another.
2255 for (const auto &[priority, initializers] : prioritizedDynamicInitializers) {
2256 SmallString<256> fnName;
2257 fnName += "_GLOBAL__I_";
2258 fnName += getPrioritySuffix(priority);
2259
2260 llvm::SmallVector<cir::FuncOp> calls(importedInits.begin(),
2261 importedInits.end());
2262 calls.append(initializers.begin(), initializers.end());
2263 importedInits.clear();
2264 buildGlobalInitCallerFunc(fnName, cir::GlobalLinkageKind::InternalLinkage,
2265 calls, priority);
2266 }
2267}
2268
2269void LoweringPreparePass::buildCXXGlobalInitFunc() {
2270 CIRBaseBuilderTy builder(getContext());
2271 builder.setInsertionPointToEnd(&mlirModule.getBodyRegion().back());
2272 mlir::Location loc = mlirModule.getLoc();
2273
2274 // The initializers of the imported C++20 named modules, recorded by CIRGen
2275 // as the mangled names of functions this translation unit declares, at the
2276 // location of the import, and calls before its own initializers.
2277 llvm::SmallVector<cir::FuncOp> importedInits;
2278 if (auto inits = mlirModule->getAttrOfType<mlir::ArrayAttr>(
2279 cir::CIRDialect::getCXXModuleImportedInitsAttrName()))
2280 for (auto init : inits.getAsRange<cir::CXXModuleInitAttr>())
2281 importedInits.push_back(
2282 buildRuntimeFunction(builder, init.getName().getValue(),
2283 init.getLoc(), builder.getVoidFnTy()));
2284
2285 // The C++20 named-module init function name is precomputed by CIRGen and
2286 // stored as a module-level attribute. Its presence is what marks this
2287 // module as a named-module interface unit, so the name and the external
2288 // linkage that goes with it both come from the attribute and this pass needs
2289 // no live ASTContext. Modules built directly from textual CIR can opt in to
2290 // the module-init form by setting the same attribute.
2291 if (auto moduleInitFnName = mlirModule->getAttrOfType<mlir::StringAttr>(
2292 cir::CIRDialect::getCXXModuleInitFnNameAttrName())) {
2293 // As in classic CodeGen's EmitCXXModuleInitFunc, an interface unit gets
2294 // one initializer function, even with nothing to run, since importing
2295 // translation units may call it. It runs the imported modules'
2296 // initializers, then the unit's own in priority order, which get no
2297 // `_GLOBAL__I_<priority>` function of their own. The global ctor entry
2298 // and every importing translation unit call it, so a guard byte makes
2299 // the initializers run once; there is none when there is nothing to run.
2300 llvm::SmallVector<cir::FuncOp> initializers(importedInits.begin(),
2301 importedInits.end());
2302 for (const auto &[priority, inits] : prioritizedDynamicInitializers)
2303 initializers.append(inits.begin(), inits.end());
2304 initializers.append(dynamicInitializers.begin(), dynamicInitializers.end());
2305
2306 llvm::StringRef fnName = moduleInitFnName.getValue();
2307 cir::GlobalOp guard;
2308 if (!initializers.empty()) {
2309 cir::IntType guardTy = builder.getSIntNTy(8);
2310 guard =
2311 createGuardGlobalOp(builder, loc, (fnName + "__in_chrg").str(),
2312 guardTy, cir::GlobalLinkageKind::InternalLinkage);
2313 guard.setAlignment(clang::CharUnits::One().getAsAlign().value());
2314 guard.setInitialValueAttr(cir::IntAttr::get(guardTy, 0));
2315 }
2316 buildGlobalInitCallerFunc(fnName, cir::GlobalLinkageKind::ExternalLinkage,
2317 initializers,
2318 cir::GlobalCtorAttr::getDefaultPriority(), guard);
2319 return;
2320 }
2321
2322 // Any other translation unit, as in classic CodeGen's
2323 // EmitCXXGlobalInitFunc: one function per priority, the imported modules'
2324 // initializers in front of the first, then the default-priority function
2325 // for the rest, only when there is something left to call.
2326 buildCXXGlobalPriorityInitFuncs(importedInits);
2327 if (importedInits.empty() && dynamicInitializers.empty())
2328 return;
2329
2330 llvm::SmallVector<cir::FuncOp> initializers(importedInits.begin(),
2331 importedInits.end());
2332 initializers.append(dynamicInitializers.begin(), dynamicInitializers.end());
2333
2334 // Include the filename in the symbol name. Including "sub_" matches gcc
2335 // and makes sure these symbols appear lexicographically behind the symbols
2336 // with priority (TBD). Module implementation units behave the same
2337 // way as a non-modular TU with imports.
2338 SmallString<256> fnName;
2339 fnName += "_GLOBAL__sub_I_";
2340 fnName += getTransformedFileName(mlirModule);
2341 buildGlobalInitCallerFunc(fnName, cir::GlobalLinkageKind::InternalLinkage,
2342 initializers,
2343 cir::GlobalCtorAttr::getDefaultPriority());
2344}
2345
2346/// Lower a cir.array.ctor or cir.array.dtor into a do-while loop that
2347/// iterates over every element. For cir.array.ctor ops whose partial_dtor
2348/// region is non-empty, the ctor loop is wrapped in a cir.cleanup.scope whose
2349/// EH cleanup performs a reverse destruction loop using the partial dtor body.
2351 const clang::TargetInfo &targetInfo,
2352 mlir::Operation *op, mlir::Type eltTy,
2353 mlir::Value addr,
2354 mlir::Value numElements,
2355 uint64_t arrayLen, bool isCtor) {
2356 mlir::Location loc = op->getLoc();
2357 bool isDynamic = numElements != nullptr;
2358
2359 // TODO: instead of getting the size from the target, create alias for
2360 // PtrDiffTy and unify with CIRGen stuff.
2361 const unsigned sizeTypeSize =
2362 targetInfo.getTypeWidth(targetInfo.getSignedSizeType());
2363
2364 // Both constructors and destructors use end = begin + numElements.
2365 // Constructors iterate forward [begin, end). Destructors iterate backward
2366 // from end, decrementing before calling the destructor on each element.
2367 mlir::Value begin, end;
2368 if (isDynamic) {
2369 begin = addr;
2370 end = cir::PtrStrideOp::create(builder, loc, eltTy, begin, numElements);
2371 } else {
2372 mlir::Value endOffsetVal =
2373 builder.getUnsignedInt(loc, arrayLen, sizeTypeSize);
2374 begin = cir::CastOp::create(builder, loc, eltTy,
2375 cir::CastKind::array_to_ptrdecay, addr);
2376 end = cir::PtrStrideOp::create(builder, loc, eltTy, begin, endOffsetVal);
2377 }
2378
2379 mlir::Value start = isCtor ? begin : end;
2380 mlir::Value stop = isCtor ? end : begin;
2381
2382 // For dynamic destructors, guard against zero elements.
2383 // This places the destructor loop emitted below inside the if block.
2384 cir::IfOp ifOp;
2385 if (isDynamic) {
2386 mlir::Value guardCond;
2387 if (isCtor) {
2388 mlir::Value zero = builder.getUnsignedInt(loc, 0, sizeTypeSize);
2389 guardCond = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
2390 numElements, zero);
2391 } else {
2392 // We could check for numElements != 0 in this case too, but this matches
2393 // what classic codegen does.
2394 guardCond =
2395 cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne, start, stop);
2396 }
2397 ifOp = cir::IfOp::create(builder, loc, guardCond,
2398 /*withElseRegion=*/false,
2399 [&](mlir::OpBuilder &, mlir::Location) {});
2400 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
2401 }
2402
2403 mlir::Value tmpAddr =
2404 builder.createAlloca(loc, /*addr type*/ builder.getPointerTo(eltTy),
2405 "__array_idx", builder.getAlignmentAttr(1));
2406 builder.createStore(loc, start, tmpAddr);
2407
2408 mlir::Block *bodyBlock = &op->getRegion(0).front();
2409
2410 // Clone the region body (ctor/dtor call and any setup ops like per-element
2411 // zero-init) into the loop, remapping the block argument to the current
2412 // element pointer.
2413 auto cloneRegionBodyInto = [&](mlir::Block *srcBlock,
2414 mlir::Value replacement) {
2415 mlir::IRMapping map;
2416 map.map(srcBlock->getArgument(0), replacement);
2417 for (mlir::Operation &regionOp : *srcBlock) {
2418 if (!mlir::isa<cir::YieldOp>(&regionOp))
2419 builder.clone(regionOp, map);
2420 }
2421 };
2422
2423 mlir::Block *partialDtorBlock = nullptr;
2424 if (auto arrayCtor = mlir::dyn_cast<cir::ArrayCtor>(op)) {
2425 mlir::Region &partialDtor = arrayCtor.getPartialDtor();
2426 if (!partialDtor.empty())
2427 partialDtorBlock = &partialDtor.front();
2428 } else if (auto arrayDtor = mlir::dyn_cast<cir::ArrayDtor>(op)) {
2429 // When the element destructor may throw, reuse the body block as the
2430 // partial-dtor block so that an exception thrown by an element's dtor
2431 // continues the reverse-destruction loop in the EH cleanup region. The
2432 // body block already stores the next element pointer to `tmpAddr`
2433 // before invoking the dtor, so when an exception unwinds from the
2434 // dtor call `tmpAddr` already points at the element that threw, and
2435 // the cleanup loop picks up from `tmpAddr - 1` and walks back to
2436 // `begin`.
2437 if (arrayDtor.getDtorMayThrow())
2438 partialDtorBlock = bodyBlock;
2439 }
2440
2441 auto emitCtorDtorLoop = [&]() {
2442 builder.createDoWhile(
2443 loc,
2444 /*condBuilder=*/
2445 [&](mlir::OpBuilder &b, mlir::Location loc) {
2446 auto currentElement = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
2447 auto cmp = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
2448 currentElement, stop);
2449 builder.createCondition(cmp);
2450 },
2451 /*bodyBuilder=*/
2452 [&](mlir::OpBuilder &b, mlir::Location loc) {
2453 auto currentElement = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
2454 if (isCtor) {
2455 cloneRegionBodyInto(bodyBlock, currentElement);
2456 mlir::Value stride = builder.getUnsignedInt(loc, 1, sizeTypeSize);
2457 auto nextElement = cir::PtrStrideOp::create(builder, loc, eltTy,
2458 currentElement, stride);
2459 builder.createStore(loc, nextElement, tmpAddr);
2460 } else {
2461 mlir::Value stride = builder.getSignedInt(loc, -1, sizeTypeSize);
2462 auto prevElement = cir::PtrStrideOp::create(builder, loc, eltTy,
2463 currentElement, stride);
2464 builder.createStore(loc, prevElement, tmpAddr);
2465 cloneRegionBodyInto(bodyBlock, prevElement);
2466 }
2467
2468 cir::YieldOp::create(b, loc);
2469 });
2470 };
2471
2472 if (partialDtorBlock) {
2473 cir::CleanupScopeOp::create(
2474 builder, loc, cir::CleanupKind::EH,
2475 /*bodyBuilder=*/
2476 [&](mlir::OpBuilder &b, mlir::Location loc) {
2477 emitCtorDtorLoop();
2478 cir::YieldOp::create(b, loc);
2479 },
2480 /*cleanupBuilder=*/
2481 [&](mlir::OpBuilder &b, mlir::Location loc) {
2482 auto cur = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
2483 auto cmp =
2484 cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne, cur, begin);
2485 cir::IfOp::create(
2486 builder, loc, cmp, /*withElseRegion=*/false,
2487 [&](mlir::OpBuilder &b, mlir::Location loc) {
2488 builder.createDoWhile(
2489 loc,
2490 /*condBuilder=*/
2491 [&](mlir::OpBuilder &b, mlir::Location loc) {
2492 auto el = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
2493 auto neq = cir::CmpOp::create(
2494 builder, loc, cir::CmpOpKind::ne, el, begin);
2495 builder.createCondition(neq);
2496 },
2497 /*bodyBuilder=*/
2498 [&](mlir::OpBuilder &b, mlir::Location loc) {
2499 auto el = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
2500 mlir::Value negOne =
2501 builder.getSignedInt(loc, -1, sizeTypeSize);
2502 auto prev = cir::PtrStrideOp::create(builder, loc, eltTy,
2503 el, negOne);
2504 builder.createStore(loc, prev, tmpAddr);
2505 cloneRegionBodyInto(partialDtorBlock, prev);
2506 builder.createYield(loc);
2507 });
2508 cir::YieldOp::create(builder, loc);
2509 });
2510 cir::YieldOp::create(b, loc);
2511 });
2512 } else {
2513 emitCtorDtorLoop();
2514 }
2515
2516 if (ifOp)
2517 cir::YieldOp::create(builder, loc);
2518
2519 op->erase();
2520}
2521
2522void LoweringPreparePass::lowerArrayDtor(cir::ArrayDtor op) {
2523 CIRBaseBuilderTy builder(getContext());
2524 builder.setInsertionPointAfter(op.getOperation());
2525
2526 mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
2527
2528 if (op.getNumElements()) {
2529 lowerArrayDtorCtorIntoLoop(builder, getTargetInfo(), op, eltTy,
2530 op.getAddr(), op.getNumElements(),
2531 /*arrayLen=*/0,
2532 /*isCtor=*/false);
2533 return;
2534 }
2535
2536 auto arrayLen =
2537 mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
2538 lowerArrayDtorCtorIntoLoop(builder, getTargetInfo(), op, eltTy, op.getAddr(),
2539 /*numElements=*/nullptr, arrayLen,
2540 /*isCtor=*/false);
2541}
2542
2543void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
2544 cir::CIRBaseBuilderTy builder(getContext());
2545 builder.setInsertionPointAfter(op.getOperation());
2546
2547 mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
2548
2549 if (op.getNumElements()) {
2550 lowerArrayDtorCtorIntoLoop(builder, getTargetInfo(), op, eltTy,
2551 op.getAddr(), op.getNumElements(),
2552 /*arrayLen=*/0,
2553 /*isCtor=*/true);
2554 return;
2555 }
2556
2557 auto arrayLen =
2558 mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
2559 lowerArrayDtorCtorIntoLoop(builder, getTargetInfo(), op, eltTy, op.getAddr(),
2560 /*numElements=*/nullptr, arrayLen,
2561 /*isCtor=*/true);
2562}
2563
2564cir::FuncOp LoweringPreparePass::getCalledFunction(cir::CallOp callOp) {
2565 mlir::SymbolRefAttr sym = llvm::dyn_cast_if_present<mlir::SymbolRefAttr>(
2566 callOp.getCallableForCallee());
2567 if (!sym)
2568 return nullptr;
2569 return symbolTables.lookupNearestSymbolFrom<cir::FuncOp>(callOp, sym);
2570}
2571
2572void LoweringPreparePass::lowerTrivialCopyCall(cir::CallOp op) {
2573 cir::FuncOp funcOp = getCalledFunction(op);
2574 if (!funcOp)
2575 return;
2576
2577 std::optional<cir::CtorKind> ctorKind = funcOp.getCxxConstructorKind();
2578 if (ctorKind && *ctorKind == cir::CtorKind::Copy &&
2579 funcOp.isCxxTrivialMemberFunction()) {
2580 // Replace the trivial copy constructor call with a `CopyOp`
2581 CIRBaseBuilderTy builder(getContext());
2582 mlir::ValueRange operands = op.getOperands();
2583 mlir::Value dest = operands[0];
2584 mlir::Value src = operands[1];
2585 builder.setInsertionPoint(op);
2586 builder.createCopy(dest, src);
2587 op.erase();
2588 }
2589}
2590
2591cir::GlobalOp LoweringPreparePass::getOrCreateConstAggregateGlobal(
2592 CIRBaseBuilderTy &builder, mlir::Location loc, llvm::StringRef baseName,
2593 mlir::Type ty, mlir::TypedAttr constant, uint64_t alignment) {
2594 // Look up (and lazily populate) the per-base-name cache.
2595 llvm::SmallVector<cir::GlobalOp, 1> &versions =
2596 constAggregateGlobals[baseName];
2597
2598 // First, check globals we've already discovered for this base name.
2599 for (cir::GlobalOp gv : versions) {
2600 if (gv.getSymType() == ty && gv.getInitialValue() == constant &&
2601 gv.getAlignment() == alignment)
2602 return gv;
2603 }
2604
2605 // No cached match. Scan the module's symbol table starting from the next
2606 // unscanned version. In practice this should usually exit on the first
2607 // iteration, but it's possible that some other pass or a previous
2608 // invocation of this pass created globals using this same logic.
2609 llvm::SmallString<128> name(baseName);
2610 size_t baseLen = name.size();
2611 unsigned version = versions.size();
2612 while (true) {
2613 name.resize(baseLen);
2614 if (version != 0) {
2615 name.push_back('.');
2616 llvm::Twine(version).toVector(name);
2617 }
2618 auto existingGv = symbolTables.lookupSymbolIn<cir::GlobalOp>(
2619 mlirModule, mlir::StringAttr::get(&getContext(), name));
2620 if (!existingGv)
2621 break;
2622 versions.push_back(existingGv);
2623 if (existingGv.getSymType() == ty &&
2624 existingGv.getInitialValue() == constant &&
2625 existingGv.getAlignment() == alignment)
2626 return existingGv;
2627 ++version;
2628 }
2629
2630 // No match found, create a new global. The loop above found an unused name.
2631 mlir::OpBuilder::InsertionGuard guard(builder);
2632 builder.setInsertionPointToStart(mlirModule.getBody());
2633 auto gv =
2634 cir::GlobalOp::create(builder, loc, name, ty,
2635 /*isConstant=*/true,
2636 cir::LangAddressSpaceAttr::get(
2637 &getContext(), cir::LangAddressSpace::Default),
2638 cir::GlobalLinkageKind::PrivateLinkage);
2639 mlir::SymbolTable::setSymbolVisibility(
2640 gv, mlir::SymbolTable::Visibility::Private);
2641 gv.setInitialValueAttr(constant);
2642 gv.setAlignment(alignment);
2643
2644 // Keep the cached symbol table in sync with the new global so subsequent
2645 // lookups for other base names find it.
2646 symbolTables.getSymbolTable(mlirModule).insert(gv);
2647
2648 versions.push_back(gv);
2649 return gv;
2650}
2651
2652void LoweringPreparePass::lowerStoreOfConstAggregate(cir::StoreOp op) {
2653 // Check if the value operand is a cir.const with aggregate type.
2654 auto constOp = op.getValue().getDefiningOp<cir::ConstantOp>();
2655 if (!constOp)
2656 return;
2657
2658 mlir::Type ty = constOp.getType();
2659 if (!mlir::isa<cir::ArrayType, cir::RecordType>(ty))
2660 return;
2661
2662 // Only transform stores to local variables (backed by cir.alloca).
2663 // Stores to other addresses (e.g. base_class_addr) should not be
2664 // transformed as they may be partial initializations.
2665 auto alloca = op.getAddr().getDefiningOp<cir::AllocaOp>();
2666 if (!alloca)
2667 return;
2668
2669 mlir::TypedAttr constant = constOp.getValue();
2670
2671 // OG implements several optimization tiers for constant aggregate
2672 // initialization. For now we always create a global constant + memcpy
2673 // (shouldCreateMemCpyFromGlobal). Future work can add the intermediate
2674 // tiers.
2678
2679 // Get function name from parent cir.func.
2680 auto func = op->getParentOfType<cir::FuncOp>();
2681 if (!func)
2682 return;
2683 llvm::StringRef funcName = func.getSymName();
2684
2685 // Get variable name from the alloca.
2686 llvm::StringRef varName = alloca.getName();
2687
2688 // Build base name: __const.<func>.<var>
2689 std::string baseName = ("__const." + funcName + "." + varName).str();
2690 CIRBaseBuilderTy builder(getContext());
2691
2692 // Check for existing globals and create a new global with a unique name
2693 // if no match is found.
2694 cir::GlobalOp gv = getOrCreateConstAggregateGlobal(
2695 builder, op.getLoc(), baseName, ty, constant, alloca.getAlignment());
2696
2697 // Now replace the store with get_global + copy.
2698 builder.setInsertionPoint(op);
2699
2700 auto ptrTy = cir::PointerType::get(ty);
2701 mlir::Value globalPtr =
2702 cir::GetGlobalOp::create(builder, op.getLoc(), ptrTy, gv.getSymName());
2703
2704 // Replace store with copy.
2705 cir::CopyOp copyOp = builder.createCopy(op.getAddr(), globalPtr);
2706
2707 cir::CIRDataLayout dataLayout(mlirModule);
2708 uint64_t naturalAlign = dataLayout.getABITypeAlign(ty).value();
2709 if (alloca.getAlignment() != naturalAlign)
2710 copyOp.setDstAlignment(alloca.getAlignment());
2711 uint64_t srcAlign = gv.getAlignment().value_or(naturalAlign);
2712 if (srcAlign != naturalAlign)
2713 copyOp.setSrcAlignment(srcAlign);
2714
2715 // Erase the original store.
2716 op.erase();
2717
2718 // Erase the cir.const if it has no remaining users.
2719 if (constOp.use_empty())
2720 constOp.erase();
2721}
2722
2723// Every raised operation carries the original callee, the operands, and the
2724// attributes of the call, so this one function lowers any of them back to an
2725// equivalent plain call.
2726void LoweringPreparePass::lowerStdOp(cir::StdOpInterface typedOp) {
2727 mlir::Operation *op = typedOp.getOperation();
2728 cir::CIRBaseBuilderTy builder(getContext());
2729 builder.setInsertionPointAfter(op);
2730 mlir::Type resultType;
2731 if (op->getNumResults())
2732 resultType = op->getResult(0).getType();
2733 cir::CallOp call = builder.createCallOp(
2734 op->getLoc(), typedOp.getOriginalFnAttr(), resultType, op->getOperands());
2735
2736 // IdiomRecognizer stores both the inherent and discardable attributes of the
2737 // original call as discardable attributes on the raised operation because
2738 // the cir.std operations do not share CallOp's property schema. Reconstruct
2739 // the original storage class from the destination CallOp schema here.
2740 //
2741 // This intentionally uses getInherentAttr as a schema query, not as a test
2742 // that `call` currently has the attribute: generated property accessors
2743 // return an engaged optional with a null attribute for a recognized but unset
2744 // optional property. An empty optional means the name is not inherent to
2745 // CallOp and must remain discardable.
2746 // TODO: Replace this transport encoding with shared call properties on the
2747 // raised operations.
2748 for (mlir::NamedAttribute attr : op->getDiscardableAttrs()) {
2749 if (call->getInherentAttr(attr.getName()).has_value())
2750 call->setInherentAttr(attr.getName(), attr.getValue());
2751 else
2752 call->setDiscardableAttr(attr.getName(), attr.getValue());
2753 }
2754
2755 op->replaceAllUsesWith(call);
2756 op->erase();
2757}
2758
2759void LoweringPreparePass::runOnOp(mlir::Operation *op) {
2760 if (auto arrayCtor = dyn_cast<cir::ArrayCtor>(op)) {
2761 lowerArrayCtor(arrayCtor);
2762 } else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op)) {
2763 lowerArrayDtor(arrayDtor);
2764 } else if (auto registerOp = dyn_cast<cir::RegisterExitDtorOp>(op)) {
2765 lowerRegisterExitDtorOp(registerOp);
2766 } else if (auto stdOp = mlir::dyn_cast<cir::StdOpInterface>(op)) {
2767 lowerStdOp(stdOp);
2768 } else if (auto cast = mlir::dyn_cast<cir::CastOp>(op)) {
2769 lowerCastOp(cast);
2770 } else if (auto complexConj = mlir::dyn_cast<cir::ComplexConjOp>(op)) {
2771 lowerComplexConjOp(complexConj);
2772 } else if (auto complexDiv = mlir::dyn_cast<cir::ComplexFDivOp>(op)) {
2773 lowerComplexFDivOp(complexDiv);
2774 } else if (auto complexMul = mlir::dyn_cast<cir::ComplexFMulOp>(op)) {
2775 lowerComplexFMulOp(complexMul);
2776 } else if (auto complexDiv = mlir::dyn_cast<cir::ComplexDivOp>(op)) {
2777 lowerComplexDivOp(complexDiv);
2778 } else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op)) {
2779 lowerComplexMulOp(complexMul);
2780 } else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op)) {
2781 lowerGlobalOp(glob);
2782 if (auto regAttr = glob->getAttrOfType<CUDAVarRegistrationInfoAttr>(
2783 CUDAVarRegistrationInfoAttr::getMnemonic()))
2784 cudaDeviceVars.emplace_back(glob, regAttr);
2785 } else if (auto getGlob = mlir::dyn_cast<cir::GetGlobalOp>(op)) {
2786 lowerGetGlobalOp(getGlob);
2787 } else if (auto callOp = dyn_cast<cir::CallOp>(op)) {
2788 lowerTrivialCopyCall(callOp);
2789 } else if (auto storeOp = dyn_cast<cir::StoreOp>(op)) {
2790 lowerStoreOfConstAggregate(storeOp);
2791 } else if (auto fnOp = dyn_cast<cir::FuncOp>(op)) {
2792 if (auto globalCtor = fnOp.getGlobalCtorPriority())
2793 globalCtorList.emplace_back(fnOp.getName(), globalCtor.value());
2794 else if (auto globalDtor = fnOp.getGlobalDtorPriority())
2795 globalDtorList.emplace_back(fnOp.getName(), globalDtor.value());
2796
2797 if (mlir::Attribute attr =
2798 fnOp->getAttr(cir::CUDAKernelNameAttr::getMnemonic())) {
2799 auto kernelNameAttr = dyn_cast<CUDAKernelNameAttr>(attr);
2800 llvm::StringRef kernelName = kernelNameAttr.getKernelName();
2801 cudaKernelMap[kernelName] = fnOp;
2802 }
2803 } else if (auto threeWayCmp = dyn_cast<cir::CmpThreeWayOp>(op)) {
2804 lowerThreeWayCmpOp(threeWayCmp);
2805 } else if (auto initOp = dyn_cast<cir::LocalInitOp>(op)) {
2806 lowerLocalInitOp(initOp);
2807 }
2808}
2809
2810static llvm::StringRef getCUDAPrefix(const clang::LangOptions &langOpts) {
2811 if (langOpts.HIP)
2812 return "hip";
2813 return "cuda";
2814}
2815
2816static std::string addUnderscoredPrefix(llvm::StringRef prefix,
2817 llvm::StringRef name) {
2818 return ("__" + prefix + name).str();
2819}
2820
2821/// Creates a global constructor function for the module:
2822///
2823/// For CUDA:
2824/// \code
2825/// void __cuda_module_ctor() {
2826/// Handle = __cudaRegisterFatBinary(GpuBinaryBlob);
2827/// __cuda_register_globals(Handle);
2828/// }
2829/// \endcode
2830///
2831/// For HIP:
2832/// \code
2833/// void __hip_module_ctor() {
2834/// if (__hip_gpubin_handle == 0) {
2835/// __hip_gpubin_handle = __hipRegisterFatBinary(GpuBinaryBlob);
2836/// __hip_register_globals(__hip_gpubin_handle);
2837/// }
2838/// }
2839/// \endcode
2840void LoweringPreparePass::buildCUDAModuleCtor() {
2841 bool isHIP = getLangOpts().HIP;
2842
2843 if (getLangOpts().GPURelocatableDeviceCode)
2844 llvm_unreachable("GPU RDC NYI");
2845
2846 // For CUDA without -fgpu-rdc, it's safe to stop generating ctor
2847 // if there's nothing to register.
2848 if (cudaKernelMap.empty() && cudaDeviceVars.empty())
2849 return;
2850
2851 // There's no device-side binary, so no need to proceed for CUDA.
2852 // HIP has to create an external symbol in this case, which is NYI.
2853 auto deviceBinaryAttr = mlirModule->getAttrOfType<mlir::StringAttr>(
2854 CIRDialect::getCUDADeviceBinaryAttrName());
2855 if (!deviceBinaryAttr) {
2856 if (isHIP)
2858 return;
2859 }
2860
2861 // Set up common types and builder.
2862 llvm::StringRef cudaPrefix = getCUDAPrefix(getLangOpts());
2863 mlir::Location loc = mlirModule->getLoc();
2864 CIRBaseBuilderTy builder(getContext());
2865 builder.setInsertionPointToStart(mlirModule.getBody());
2866
2867 Type voidTy = builder.getVoidTy();
2868 PointerType voidPtrTy = builder.getVoidPtrTy();
2869 PointerType voidPtrPtrTy = builder.getPointerTo(voidPtrTy);
2870 IntType intTy = builder.getSIntNTy(32);
2871
2872 // --- Create fatbin globals ---
2873
2874 // The section names are different for MAC OS X.
2875 llvm::StringRef fatbinConstName =
2876 getLangOpts().HIP ? ".hip_fatbin" : ".nv_fatbin";
2877
2878 llvm::StringRef fatbinSectionName =
2879 getLangOpts().HIP ? ".hipFatBinSegment" : ".nvFatBinSegment";
2880
2881 // Create the fatbin string constant with GPU binary contents.
2882 // The dialect verifier guarantees the attribute is typed as the array.
2883 auto fatbinType = mlir::cast<ArrayType>(deviceBinaryAttr.getType());
2884 std::string fatbinStrName = addUnderscoredPrefix(cudaPrefix, "_fatbin_str");
2885 GlobalOp fatbinStr = GlobalOp::create(builder, loc, fatbinStrName, fatbinType,
2886 /*isConstant=*/true, {},
2887 GlobalLinkageKind::PrivateLinkage);
2888 if (isHIP) {
2889 const unsigned HIPCodeObjectAlign = 4096;
2890 fatbinStr.setAlignment(HIPCodeObjectAlign);
2891 } else {
2892 fatbinStr.setAlignment(8);
2893 }
2894
2895 fatbinStr.setInitialValueAttr(
2896 cir::ConstArrayAttr::get(fatbinType, deviceBinaryAttr));
2897 fatbinStr.setSection(fatbinConstName);
2898 fatbinStr.setPrivate();
2899
2900 // Create the fatbin wrapper struct:
2901 // struct { int magic; int version; void *fatbin; void *unused; };
2902 mlir::Type fatbinWrapperMembers[] = {intTy, intTy, voidPtrTy, voidPtrTy};
2903 auto fatbinWrapperType = cir::StructType::get(
2904 &getContext(), fatbinWrapperMembers, /*packed=*/false, /*is_class=*/false,
2905 cir::RecordType::getAllDataKinds(fatbinWrapperMembers));
2906 std::string fatbinWrapperName =
2907 addUnderscoredPrefix(cudaPrefix, "_fatbin_wrapper");
2908 GlobalOp fatbinWrapper = GlobalOp::create(
2909 builder, loc, fatbinWrapperName, fatbinWrapperType,
2910 /*isConstant=*/true, {}, GlobalLinkageKind::PrivateLinkage);
2911 fatbinWrapper.setSection(fatbinSectionName);
2912
2913 constexpr unsigned cudaFatMagic = 0x466243b1;
2914 constexpr unsigned hipFatMagic = 0x48495046;
2915 unsigned fatMagic = isHIP ? hipFatMagic : cudaFatMagic;
2916
2917 auto magicInit = IntAttr::get(intTy, fatMagic);
2918 auto versionInit = IntAttr::get(intTy, 1);
2919 auto fatbinStrSymbol =
2920 mlir::FlatSymbolRefAttr::get(fatbinStr.getSymNameAttr());
2921 auto fatbinInit = GlobalViewAttr::get(voidPtrTy, fatbinStrSymbol);
2922 mlir::TypedAttr unusedInit = builder.getConstNullPtrAttr(voidPtrTy);
2923 fatbinWrapper.setInitialValueAttr(cir::ConstRecordAttr::get(
2924 fatbinWrapperType,
2925 mlir::ArrayAttr::get(&getContext(),
2926 {magicInit, versionInit, fatbinInit, unusedInit})));
2927
2928 // Create the GPU binary handle global variable.
2929 std::string gpubinHandleName =
2930 addUnderscoredPrefix(cudaPrefix, "_gpubin_handle");
2931
2932 GlobalOp gpuBinHandle = GlobalOp::create(
2933 builder, loc, gpubinHandleName, voidPtrPtrTy,
2934 /*isConstant=*/false, {}, cir::GlobalLinkageKind::InternalLinkage);
2935 gpuBinHandle.setInitialValueAttr(builder.getConstNullPtrAttr(voidPtrPtrTy));
2936 gpuBinHandle.setPrivate();
2937
2938 // Declare this function:
2939 // void **__{cuda|hip}RegisterFatBinary(void *);
2940
2941 std::string regFuncName =
2942 addUnderscoredPrefix(cudaPrefix, "RegisterFatBinary");
2943 FuncType regFuncType = FuncType::get({voidPtrTy}, voidPtrPtrTy);
2944 cir::FuncOp regFunc =
2945 buildRuntimeFunction(builder, regFuncName, loc, regFuncType);
2946
2947 std::string moduleCtorName = addUnderscoredPrefix(cudaPrefix, "_module_ctor");
2948 cir::FuncOp moduleCtor = buildRuntimeFunction(
2949 builder, moduleCtorName, loc, FuncType::get({}, voidTy),
2950 GlobalLinkageKind::InternalLinkage);
2951
2952 globalCtorList.emplace_back(moduleCtorName,
2953 cir::GlobalCtorAttr::getDefaultPriority());
2954 builder.setInsertionPointToStart(moduleCtor.addEntryBlock());
2956 if (isHIP) {
2957 // --- Create HIP CTOR ---
2958 // if (__hip_gpubin_handle == nullptr)
2959 // __hip_gpubin_handle = __hipRegisterFatBinary(&fatbinWrapper);
2960 // __hip_register_globals(__hip_gpubin_handle);
2961 // atexit(__hip_module_dtor);
2962 mlir::Block *entryBlock = builder.getInsertionBlock();
2963 mlir::Region *parent = entryBlock->getParent();
2964 mlir::Block *ifBlock = builder.createBlock(parent);
2965 mlir::Block *exitBlock = builder.createBlock(parent);
2966 {
2967 mlir::OpBuilder::InsertionGuard guard(builder);
2968 builder.setInsertionPointToEnd(entryBlock);
2969 mlir::Value handle =
2970 builder.createLoad(loc, builder.createGetGlobal(gpuBinHandle));
2971 auto handlePtrTy = mlir::cast<cir::PointerType>(handle.getType());
2972 mlir::Value nullPtr = builder.getNullPtr(handlePtrTy, loc);
2973 mlir::Value isNull =
2974 builder.createCompare(loc, cir::CmpOpKind::eq, handle, nullPtr);
2975 cir::BrCondOp::create(builder, loc, isNull, ifBlock, exitBlock);
2976 }
2977 {
2978 // Handle is null: load the fatbin and register it.
2979 mlir::OpBuilder::InsertionGuard guard(builder);
2980 builder.setInsertionPointToStart(ifBlock);
2981 mlir::Value wrapper = builder.createGetGlobal(fatbinWrapper);
2982 mlir::Value fatbinVoidPtr = builder.createBitcast(wrapper, voidPtrTy);
2983 cir::CallOp gpuBinaryHandleCall =
2984 builder.createCallOp(loc, regFunc, fatbinVoidPtr);
2985 mlir::Value gpuBinaryHandle = gpuBinaryHandleCall.getResult();
2986 // Store the value back to the global `__hip_gpubin_handle`.
2987 mlir::Value gpuBinaryHandleGlobal = builder.createGetGlobal(gpuBinHandle);
2988 builder.createStore(loc, gpuBinaryHandle, gpuBinaryHandleGlobal);
2989 cir::BrOp::create(builder, loc, exitBlock);
2990 }
2991 {
2992 // Exit block: load the (possibly newly-registered) handle, call
2993 // __hip_register_globals, and register the module dtor with atexit().
2994 mlir::OpBuilder::InsertionGuard guard(builder);
2995 builder.setInsertionPointToStart(exitBlock);
2996 mlir::Value gHandle =
2997 builder.createLoad(loc, builder.createGetGlobal(gpuBinHandle));
2998
2999 if (std::optional<FuncOp> regGlobal = buildCUDARegisterGlobals())
3000 builder.createCallOp(loc, *regGlobal, gHandle);
3001
3002 if (std::optional<FuncOp> dtor = buildHIPModuleDtor()) {
3003 cir::CIRBaseBuilderTy globalBuilder(getContext());
3004 globalBuilder.setInsertionPointToStart(mlirModule.getBody());
3005 FuncOp atexit = buildRuntimeFunction(
3006 globalBuilder, "atexit", loc,
3007 FuncType::get(PointerType::get(dtor->getFunctionType()), intTy));
3008 mlir::Value dtorFunc = GetGlobalOp::create(
3009 builder, loc, PointerType::get(dtor->getFunctionType()),
3010 mlir::FlatSymbolRefAttr::get(dtor->getSymNameAttr()));
3011 builder.createCallOp(loc, atexit, dtorFunc);
3012 }
3013 cir::ReturnOp::create(builder, loc);
3014 }
3015 return;
3016 }
3017 if (!getLangOpts().GPURelocatableDeviceCode) {
3018
3019 // --- Create CUDA CTOR-DTOR ---
3020 // Register binary with CUDA runtime. This is substantially different in
3021 // default mode vs. separate compilation.
3022 // Corresponding code:
3023 // gpuBinaryHandle = __cudaRegisterFatBinary(&fatbinWrapper);
3024 mlir::Value wrapper = builder.createGetGlobal(fatbinWrapper);
3025 mlir::Value fatbinVoidPtr = builder.createBitcast(wrapper, voidPtrTy);
3026 cir::CallOp gpuBinaryHandleCall =
3027 builder.createCallOp(loc, regFunc, fatbinVoidPtr);
3028 mlir::Value gpuBinaryHandle = gpuBinaryHandleCall.getResult();
3029 // Store the value back to the global `__cuda_gpubin_handle`.
3030 mlir::Value gpuBinaryHandleGlobal = builder.createGetGlobal(gpuBinHandle);
3031 builder.createStore(loc, gpuBinaryHandle, gpuBinaryHandleGlobal);
3032
3033 // --- Generate __cuda_register_globals and call it ---
3034 if (std::optional<FuncOp> regGlobal = buildCUDARegisterGlobals()) {
3035 builder.createCallOp(loc, *regGlobal, gpuBinaryHandle);
3036 }
3037
3038 // From CUDA 10.1 onwards, we must call this function to end registration:
3039 // void __cudaRegisterFatBinaryEnd(void **fatbinHandle);
3040 // This is CUDA-specific, so no need to use `addUnderscoredPrefix`.
3041 std::optional<llvm::VersionTuple> sdkVersion = getSDKVersion();
3042 if (!sdkVersion)
3043 return;
3046 cir::CIRBaseBuilderTy globalBuilder(getContext());
3047 globalBuilder.setInsertionPointToStart(mlirModule.getBody());
3048 FuncOp endFunc =
3049 buildRuntimeFunction(globalBuilder, "__cudaRegisterFatBinaryEnd", loc,
3050 FuncType::get({voidPtrPtrTy}, voidTy));
3051 builder.createCallOp(loc, endFunc, gpuBinaryHandle);
3052 }
3053 } else
3054 llvm_unreachable("GPU RDC NYI");
3055
3056 // Create destructor and register it with atexit() the way NVCC does it. Doing
3057 // it during regular destructor phase worked in CUDA before 9.2 but results in
3058 // double-free in 9.2.
3059 if (std::optional<FuncOp> dtor = buildCUDAModuleDtor()) {
3060
3061 // extern "C" int atexit(void (*f)(void));
3062 cir::CIRBaseBuilderTy globalBuilder(getContext());
3063 globalBuilder.setInsertionPointToStart(mlirModule.getBody());
3064 FuncOp atexit = buildRuntimeFunction(
3065 globalBuilder, "atexit", loc,
3066 FuncType::get(PointerType::get(dtor->getFunctionType()), intTy));
3067 mlir::Value dtorFunc = GetGlobalOp::create(
3068 builder, loc, PointerType::get(dtor->getFunctionType()),
3069 mlir::FlatSymbolRefAttr::get(dtor->getSymNameAttr()));
3070 builder.createCallOp(loc, atexit, dtorFunc);
3071 }
3072 cir::ReturnOp::create(builder, loc);
3073}
3074
3075std::optional<FuncOp> LoweringPreparePass::buildCUDAModuleDtor() {
3076 if (!mlirModule->getAttr(CIRDialect::getCUDADeviceBinaryAttrName()))
3077 return {};
3078
3079 llvm::StringRef prefix = getCUDAPrefix(getLangOpts());
3080
3081 VoidType voidTy = VoidType::get(&getContext());
3082 PointerType voidPtrPtrTy = PointerType::get(PointerType::get(voidTy));
3083
3084 mlir::Location loc = mlirModule.getLoc();
3085
3086 cir::CIRBaseBuilderTy builder(getContext());
3087 builder.setInsertionPointToStart(mlirModule.getBody());
3088
3089 // define: void __cudaUnregisterFatBinary(void ** handle);
3090 std::string unregisterFuncName =
3091 addUnderscoredPrefix(prefix, "UnregisterFatBinary");
3092 FuncOp unregisterFunc = buildRuntimeFunction(
3093 builder, unregisterFuncName, loc, FuncType::get({voidPtrPtrTy}, voidTy));
3094
3095 // void __cuda_module_dtor();
3096 // Despite the name, OG doesn't treat it as a destructor, so it shouldn't be
3097 // put into globalDtorList. If it were a real dtor, then it would cause
3098 // double free above CUDA 9.2. The way to use it is to manually call
3099 // atexit() at end of module ctor.
3100 std::string dtorName = addUnderscoredPrefix(prefix, "_module_dtor");
3101 FuncOp dtor =
3102 buildRuntimeFunction(builder, dtorName, loc, FuncType::get({}, voidTy),
3103 GlobalLinkageKind::InternalLinkage);
3104
3105 builder.setInsertionPointToStart(dtor.addEntryBlock());
3106
3107 // For dtor, we only need to call:
3108 // __cudaUnregisterFatBinary(__cuda_gpubin_handle);
3109
3110 std::string gpubinName = addUnderscoredPrefix(prefix, "_gpubin_handle");
3111 GlobalOp gpubinGlobal = cast<GlobalOp>(mlirModule.lookupSymbol(gpubinName));
3112 mlir::Value gpubinAddress = builder.createGetGlobal(gpubinGlobal);
3113 mlir::Value gpubin = builder.createLoad(loc, gpubinAddress);
3114 builder.createCallOp(loc, unregisterFunc, gpubin);
3115 ReturnOp::create(builder, loc);
3116
3117 return dtor;
3118}
3119
3120/// Build the HIP module dtor:
3121///
3122/// void __hip_module_dtor() {
3123/// if (__hip_gpubin_handle != nullptr) {
3124/// __hipUnregisterFatBinary(__hip_gpubin_handle);
3125/// __hip_gpubin_handle = nullptr;
3126/// }
3127/// }
3128///
3129/// Despite the name, OG doesn't treat this as a real destructor: putting it on
3130/// the dtor list would cause a double-free. It is meant to be registered via
3131/// atexit() at the end of the module ctor.
3132std::optional<FuncOp> LoweringPreparePass::buildHIPModuleDtor() {
3133 if (!mlirModule->getAttr(CIRDialect::getCUDADeviceBinaryAttrName()))
3134 return {};
3135
3136 llvm::StringRef prefix = getCUDAPrefix(getLangOpts());
3137
3138 VoidType voidTy = VoidType::get(&getContext());
3139 PointerType voidPtrPtrTy = PointerType::get(PointerType::get(voidTy));
3140
3141 mlir::Location loc = mlirModule.getLoc();
3142
3143 cir::CIRBaseBuilderTy builder(getContext());
3144 builder.setInsertionPointToStart(mlirModule.getBody());
3145
3146 // void __hipUnregisterFatBinary(void ** handle);
3147 std::string unregisterFuncName =
3148 addUnderscoredPrefix(prefix, "UnregisterFatBinary");
3149 FuncOp unregisterFunc = buildRuntimeFunction(
3150 builder, unregisterFuncName, loc, FuncType::get({voidPtrPtrTy}, voidTy));
3151
3152 std::string dtorName = addUnderscoredPrefix(prefix, "_module_dtor");
3153 FuncOp dtor =
3154 buildRuntimeFunction(builder, dtorName, loc, FuncType::get({}, voidTy),
3155 GlobalLinkageKind::InternalLinkage);
3156
3157 std::string gpubinName = addUnderscoredPrefix(prefix, "_gpubin_handle");
3158 GlobalOp gpuBinGlobal = cast<GlobalOp>(mlirModule.lookupSymbol(gpubinName));
3159
3160 mlir::Block *entryBlock = dtor.addEntryBlock();
3161 mlir::Block *ifBlock = builder.createBlock(&dtor.getBody());
3162 mlir::Block *exitBlock = builder.createBlock(&dtor.getBody());
3163
3164 mlir::OpBuilder::InsertionGuard guard(builder);
3165 builder.setInsertionPointToEnd(entryBlock);
3166 mlir::Value handle =
3167 builder.createLoad(loc, builder.createGetGlobal(gpuBinGlobal));
3168 auto handlePtrTy = mlir::cast<cir::PointerType>(handle.getType());
3169 mlir::Value nullPtr = builder.getNullPtr(handlePtrTy, loc);
3170 mlir::Value isNotNull =
3171 builder.createCompare(loc, cir::CmpOpKind::ne, handle, nullPtr);
3172 cir::BrCondOp::create(builder, loc, isNotNull, ifBlock, exitBlock);
3173
3174 {
3175 // Handle is non-null: unregister and clear it.
3176 mlir::OpBuilder::InsertionGuard ifGuard(builder);
3177 builder.setInsertionPointToStart(ifBlock);
3178 builder.createCallOp(loc, unregisterFunc, handle);
3179 builder.createStore(loc, nullPtr, builder.createGetGlobal(gpuBinGlobal));
3180 cir::BrOp::create(builder, loc, exitBlock);
3181 }
3182 {
3183 mlir::OpBuilder::InsertionGuard exitGuard(builder);
3184 builder.setInsertionPointToStart(exitBlock);
3185 cir::ReturnOp::create(builder, loc);
3186 }
3187
3188 return dtor;
3189}
3190
3191std::optional<FuncOp> LoweringPreparePass::buildCUDARegisterGlobals() {
3192 if (cudaKernelMap.empty() && cudaDeviceVars.empty())
3193 return {};
3194
3195 cir::CIRBaseBuilderTy builder(getContext());
3196 builder.setInsertionPointToStart(mlirModule.getBody());
3197
3198 mlir::Location loc = mlirModule.getLoc();
3199 llvm::StringRef cudaPrefix = getCUDAPrefix(getLangOpts());
3200
3201 auto voidTy = VoidType::get(&getContext());
3202 auto voidPtrTy = PointerType::get(voidTy);
3203 auto voidPtrPtrTy = PointerType::get(voidPtrTy);
3204
3205 // Create the function:
3206 // void __cuda_register_globals(void **fatbinHandle)
3207 std::string regGlobalFuncName =
3208 addUnderscoredPrefix(cudaPrefix, "_register_globals");
3209 auto regGlobalFuncTy = FuncType::get({voidPtrPtrTy}, voidTy);
3210 FuncOp regGlobalFunc =
3211 buildRuntimeFunction(builder, regGlobalFuncName, loc, regGlobalFuncTy,
3212 /*linkage=*/GlobalLinkageKind::InternalLinkage);
3213 builder.setInsertionPointToStart(regGlobalFunc.addEntryBlock());
3214
3215 buildCUDARegisterGlobalFunctions(builder, regGlobalFunc);
3216 buildCUDARegisterVars(builder, regGlobalFunc);
3217
3218 ReturnOp::create(builder, loc);
3219 return regGlobalFunc;
3220}
3221
3222void LoweringPreparePass::buildCUDARegisterGlobalFunctions(
3223 cir::CIRBaseBuilderTy &builder, FuncOp regGlobalFunc) {
3224 mlir::Location loc = mlirModule.getLoc();
3225 llvm::StringRef cudaPrefix = getCUDAPrefix(getLangOpts());
3226 cir::CIRDataLayout dataLayout(mlirModule);
3227
3228 auto voidTy = VoidType::get(&getContext());
3229 auto voidPtrTy = PointerType::get(voidTy);
3230 auto voidPtrPtrTy = PointerType::get(voidPtrTy);
3231 IntType intTy = builder.getSIntNTy(32);
3232 IntType charTy =
3233 cir::IntType::get(&getContext(), getTargetInfo().getCharWidth(),
3234 /*isSigned=*/false);
3235
3236 // Extract the GPU binary handle argument.
3237 mlir::Value fatbinHandle = *regGlobalFunc.args_begin();
3238
3239 cir::CIRBaseBuilderTy globalBuilder(getContext());
3240 globalBuilder.setInsertionPointToStart(mlirModule.getBody());
3241
3242 // Declare CUDA internal functions:
3243 // int __cudaRegisterFunction(
3244 // void **fatbinHandle,
3245 // const char *hostFunc,
3246 // char *deviceFunc,
3247 // const char *deviceName,
3248 // int threadLimit,
3249 // uint3 *tid, uint3 *bid, dim3 *bDim, dim3 *gDim,
3250 // int *wsize
3251 // )
3252 // OG doesn't care about the types at all. They're treated as void*.
3253
3254 FuncOp cudaRegisterFunction = buildRuntimeFunction(
3255 globalBuilder, addUnderscoredPrefix(cudaPrefix, "RegisterFunction"), loc,
3256 FuncType::get({voidPtrPtrTy, voidPtrTy, voidPtrTy, voidPtrTy, intTy,
3257 voidPtrTy, voidPtrTy, voidPtrTy, voidPtrTy, voidPtrTy},
3258 intTy));
3259
3260 auto makeConstantString = [&](llvm::StringRef str) -> GlobalOp {
3261 auto strType = ArrayType::get(&getContext(), charTy, 1 + str.size());
3262 auto tmpString = cir::GlobalOp::create(
3263 globalBuilder, loc, (".str" + str).str(), strType,
3264 /*isConstant=*/true, {},
3265 /*linkage=*/cir::GlobalLinkageKind::PrivateLinkage);
3266
3267 // We must make the string zero-terminated.
3268 tmpString.setInitialValueAttr(
3269 ConstArrayAttr::get(strType, StringAttr::get(str + "\0", strType)));
3270 tmpString.setPrivate();
3271 return tmpString;
3272 };
3273
3274 cir::ConstantOp cirNullPtr = builder.getNullPtr(voidPtrTy, loc);
3275 bool isHIP = getLangOpts().HIP;
3276 for (auto kernelName : cudaKernelMap.keys()) {
3277 FuncOp deviceStub = cudaKernelMap[kernelName];
3278 GlobalOp deviceFuncStr = makeConstantString(kernelName);
3279 mlir::Value deviceFunc = builder.createBitcast(
3280 builder.createGetGlobal(deviceFuncStr), voidPtrTy);
3281
3282 mlir::Value hostFunc;
3283 if (isHIP) {
3284 // Under HIP, the kernel-handle is a GlobalOp shadow created by CIR
3285 // codegen and named with the kernel-reference mangled name (e.g.
3286 // `@_Z2fnv` pointing at the device-stub function
3287 // `_Z17__device_stub__fnv`). The CUDAKernelNameAttr on the device-stub
3288 // uses the same name, so we can resolve the shadow by symbol lookup.
3289 auto funcHandle = cast<GlobalOp>(mlirModule.lookupSymbol(kernelName));
3290 hostFunc =
3291 builder.createBitcast(builder.createGetGlobal(funcHandle), voidPtrTy);
3292 } else {
3293 hostFunc = builder.createBitcast(
3294 GetGlobalOp::create(
3295 builder, loc, PointerType::get(deviceStub.getFunctionType()),
3296 mlir::FlatSymbolRefAttr::get(deviceStub.getSymNameAttr())),
3297 voidPtrTy);
3298 }
3299 builder.createCallOp(
3300 loc, cudaRegisterFunction,
3301 {fatbinHandle, hostFunc, deviceFunc, deviceFunc,
3302 ConstantOp::create(builder, loc, IntAttr::get(intTy, -1)), cirNullPtr,
3303 cirNullPtr, cirNullPtr, cirNullPtr, cirNullPtr});
3304 }
3305}
3306
3307// Emit `__{cuda|hip}RegisterVar` calls inside `__{cuda|hip}_register_globals`
3308// for every device-side shadow that carries a `cu.var_registration` attribute
3309// (attached by `CIRGenNVCUDARuntime::handleVarRegistration`).
3310void LoweringPreparePass::buildCUDARegisterVars(cir::CIRBaseBuilderTy &builder,
3311 FuncOp regGlobalFunc) {
3312 mlir::Location loc = mlirModule.getLoc();
3313 llvm::StringRef cudaPrefix = getCUDAPrefix(getLangOpts());
3314 cir::CIRDataLayout dataLayout(mlirModule);
3315
3316 PointerType voidPtrTy = builder.getVoidPtrTy();
3317 PointerType voidPtrPtrTy = builder.getPointerTo(voidPtrTy);
3318 IntType intTy = builder.getSIntNTy(32);
3319 IntType sizeTy = builder.getUIntNTy(getTargetInfo().getMaxPointerWidth());
3320 IntType charTy =
3321 cir::IntType::get(&getContext(), getTargetInfo().getCharWidth(),
3322 /*isSigned=*/false);
3323
3324 if (cudaDeviceVars.empty())
3325 return;
3326
3327 cir::CIRBaseBuilderTy globalBuilder(getContext());
3328 globalBuilder.setInsertionPointToStart(mlirModule.getBody());
3329
3330 cir::VoidType voidTy = builder.getVoidTy();
3331 FuncOp cudaRegisterVar;
3332 FuncOp cudaRegisterManagedVar;
3333
3334 auto makeConstantString = [&](llvm::StringRef str) -> GlobalOp {
3335 auto strType = ArrayType::get(&getContext(), charTy, 1 + str.size());
3336 auto tmpString = cir::GlobalOp::create(
3337 globalBuilder, loc, (".str" + str).str(), strType,
3338 /*isConstant=*/true, {},
3339 /*linkage=*/cir::GlobalLinkageKind::PrivateLinkage);
3340 tmpString.setInitialValueAttr(
3341 ConstArrayAttr::get(strType, StringAttr::get(str + "\0", strType)));
3342 tmpString.setPrivate();
3343 return tmpString;
3344 };
3345
3346 mlir::Value fatbinHandle = *regGlobalFunc.args_begin();
3347
3348 for (auto &[global, regAttr] : cudaDeviceVars) {
3349 switch (regAttr.getKind()) {
3350 case cir::CUDADeviceVarKind::Variable:
3351 break;
3352 case cir::CUDADeviceVarKind::Surface:
3353 llvm_unreachable("Surface registration NYI");
3354 case cir::CUDADeviceVarKind::Texture:
3355 llvm_unreachable("Texture registration NYI");
3356 }
3357
3358 // External managed variables are registered by their defining TU.
3359 if (regAttr.getIsManaged() && global.isDeclaration())
3360 continue;
3361
3362 GlobalOp deviceNameStr = makeConstantString(regAttr.getDeviceSideName());
3363 mlir::Value deviceName = builder.createBitcast(
3364 builder.createGetGlobal(deviceNameStr), voidPtrTy);
3365 mlir::Value hostVar =
3366 builder.createBitcast(builder.createGetGlobal(global), voidPtrTy);
3367
3368 llvm::TypeSize size = dataLayout.getTypeAllocSize(global.getSymType());
3369
3370 if (regAttr.getIsManaged()) {
3371 // void __hipRegisterManagedVar(void **fatbinHandle, char *managedVar,
3372 // char *var, const char *deviceName,
3373 // size_t size, unsigned align);
3374 if (!cudaRegisterManagedVar)
3375 cudaRegisterManagedVar = buildRuntimeFunction(
3376 globalBuilder,
3377 addUnderscoredPrefix(cudaPrefix, "RegisterManagedVar"), loc,
3378 FuncType::get(
3379 {voidPtrPtrTy, voidPtrTy, voidPtrTy, voidPtrTy, sizeTy, intTy},
3380 voidTy));
3381 llvm::StringRef name = global.getSymName();
3382 assert(name.ends_with(".managed") &&
3383 "HIP managed variables not transformed");
3384 auto managedVar = mlirModule.lookupSymbol<GlobalOp>(
3385 name.drop_back(llvm::StringRef(".managed").size()));
3386 mlir::Value managedPtr =
3387 builder.createBitcast(builder.createGetGlobal(managedVar), voidPtrTy);
3388 auto varSize = ConstantOp::create(
3389 builder, loc, IntAttr::get(sizeTy, size.getFixedValue()));
3390 auto align = ConstantOp::create(
3391 builder, loc, IntAttr::get(intTy, global.getAlignment().value_or(1)));
3392 builder.createCallOp(
3393 loc, cudaRegisterManagedVar,
3394 {fatbinHandle, managedPtr, hostVar, deviceName, varSize, align});
3395 } else {
3396 // void __{cuda|hip}RegisterVar(void **fatbinHandle,
3397 // char *hostVar, char *deviceAddress,
3398 // const char *deviceName, int ext,
3399 // size_t size, int constant,
3400 // int normalized);
3401 // OG ignores parameter types, treating pointers as void*.
3402 if (!cudaRegisterVar)
3403 cudaRegisterVar = buildRuntimeFunction(
3404 globalBuilder, addUnderscoredPrefix(cudaPrefix, "RegisterVar"), loc,
3405 FuncType::get({voidPtrPtrTy, voidPtrTy, voidPtrTy, voidPtrTy, intTy,
3406 sizeTy, intTy, intTy},
3407 voidTy));
3408 auto isExtern = ConstantOp::create(
3409 builder, loc, IntAttr::get(intTy, regAttr.getIsExtern() ? 1 : 0));
3410 auto varSize = ConstantOp::create(
3411 builder, loc, IntAttr::get(sizeTy, size.getFixedValue()));
3412 auto isConstant = ConstantOp::create(
3413 builder, loc, IntAttr::get(intTy, regAttr.getIsConstant() ? 1 : 0));
3414 auto normalized =
3415 ConstantOp::create(builder, loc, IntAttr::get(intTy, 0));
3416 builder.createCallOp(loc, cudaRegisterVar,
3417 {fatbinHandle, hostVar, deviceName, deviceName,
3418 isExtern, varSize, isConstant, normalized});
3419 }
3420 }
3421}
3422
3423void LoweringPreparePass::runOnOperation() {
3424 mlirModule = getOperation();
3425
3426 lowerModule = cir::createLowerModule(mlirModule);
3427 if (!lowerModule) {
3428 mlirModule.emitError() << getArgument()
3429 << " requires a module with a triple";
3430 return signalPassFailure();
3431 }
3432
3433 llvm::SmallVector<mlir::Operation *> opsToTransform;
3434
3435 mlirModule->walk([&](mlir::Operation *op) {
3436 if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
3437 cir::ComplexConjOp, cir::ComplexMulOp, cir::ComplexDivOp,
3438 cir::ComplexFMulOp, cir::ComplexFDivOp, cir::DynamicCastOp,
3439 cir::FuncOp, cir::CallOp, cir::GetGlobalOp, cir::GlobalOp,
3440 cir::StoreOp, cir::CmpThreeWayOp, cir::LocalInitOp,
3441 cir::RegisterExitDtorOp, cir::StdOpInterface>(op))
3442 opsToTransform.push_back(op);
3443 });
3444
3445 for (mlir::Operation *o : opsToTransform)
3446 runOnOp(o);
3447
3448 buildCXXGlobalInitFunc();
3449 buildCXXGlobalTlsFunc();
3450 if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice) {
3451 buildCUDAModuleCtor();
3452 // The fatbin global now references the same attribute; drop the module's
3453 // reference so an emitted .cir doesn't print the bytes twice. This has to
3454 // happen out here because the ctor and both dtor builders test the
3455 // attribute to decide whether a device-side binary exists at all.
3456 mlirModule->removeAttr(CIRDialect::getCUDADeviceBinaryAttrName());
3457 }
3458
3459 buildGlobalCtorDtorList();
3460}
3461
3462std::unique_ptr<Pass> mlir::createLoweringPreparePass() {
3463 return std::make_unique<LoweringPreparePass>();
3464}
static void emitBody(CodeGenFunction &CGF, const Stmt *S, const Stmt *NextLoop, int MaxLevel, int Level=0)
static llvm::FunctionCallee getGuardReleaseFn(CodeGenModule &CGM, llvm::PointerType *GuardPtrTy)
static llvm::FunctionCallee getGuardAbortFn(CodeGenModule &CGM, llvm::PointerType *GuardPtrTy)
static llvm::FunctionCallee getGuardAcquireFn(CodeGenModule &CGM, llvm::PointerType *GuardPtrTy)
static unsigned getCharWidth(tok::TokenKind kind, const TargetInfo &Target)
static mlir::Value buildRangeReductionComplexDiv(CIRBaseBuilderTy &builder, mlir::Location loc, mlir::Value lhsReal, mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag)
static cir::GlobalLinkageKind getThreadLocalWrapperLinkage(GlobalOp op, const clang::TargetInfo &targetInfo)
static llvm::StringRef getComplexDivLibCallName(llvm::APFloat::Semantics semantics)
static llvm::StringRef getComplexMulLibCallName(llvm::APFloat::Semantics semantics)
static std::string getPrioritySuffix(unsigned priority)
Compute the zero-padded priority suffix used to name priority-specific global init functions,...
static bool isThreadWrapperReplaceable(const clang::TargetInfo &targetInfo)
static mlir::Value buildComplexBinOpLibCall(LoweringPreparePass &pass, CIRBaseBuilderTy &builder, llvm::StringRef(*libFuncNameGetter)(llvm::APFloat::Semantics), mlir::Location loc, cir::ComplexType ty, mlir::Value lhsReal, mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag)
static std::string addUnderscoredPrefix(llvm::StringRef prefix, llvm::StringRef name)
static SmallString< 128 > getTransformedFileName(mlir::ModuleOp mlirModule)
static mlir::Value lowerComplexToComplexCast(mlir::MLIRContext &ctx, cir::CastOp op, cir::CastKind scalarCastKind)
static llvm::SmallVector< mlir::Attribute > prepareCtorDtorAttrList(mlir::MLIRContext *context, llvm::ArrayRef< cir::GlobalCtorDtorEntry > list)
static mlir::Value lowerComplexToScalarCast(mlir::MLIRContext &ctx, cir::CastOp op, cir::CastKind elemToBoolKind)
static mlir::Value buildAlgebraicComplexDiv(CIRBaseBuilderTy &builder, mlir::Location loc, mlir::Value lhsReal, mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag)
static mlir::Value lowerComplexFDiv(LoweringPreparePass &pass, CIRBaseBuilderTy &builder, mlir::Location loc, cir::ComplexFDivOp op, mlir::Value lhsReal, mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag, mlir::MLIRContext &mlirCx, const clang::TargetInfo &targetInfo)
static llvm::StringRef getCUDAPrefix(const clang::LangOptions &langOpts)
static mlir::Type higherPrecisionElementTypeForComplexArithmetic(mlir::MLIRContext &context, const clang::TargetInfo &targetInfo, const clang::LangOptions &langOpts, CIRBaseBuilderTy &builder, mlir::Type elementType)
static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder, const clang::TargetInfo &targetInfo, mlir::Operation *op, mlir::Type eltTy, mlir::Value addr, mlir::Value numElements, uint64_t arrayLen, bool isCtor)
Lower a cir.array.ctor or cir.array.dtor into a do-while loop that iterates over every element.
static mlir::Value lowerScalarToComplexCast(mlir::MLIRContext &ctx, cir::CastOp op)
static mlir::Value lowerComplexFMul(LoweringPreparePass &pass, CIRBaseBuilderTy &builder, mlir::Location loc, cir::ComplexFMulOp op, mlir::Value lhsReal, mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag)
static StringRef getTriple(const Command &Job)
static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y)
Defines the SourceManager interface.
Defines various enumerations that describe declaration and type specifiers.
Defines the TargetCXXABI class, which abstracts details of the C++ ABI that we're targeting.
static const TemplateArgument & getArgument(const TemplateArgument &A)
mlir::Value createDiv(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
mlir::TypedAttr getConstNullPtrAttr(mlir::Type t)
mlir::Value createLogicalOr(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
mlir::Value createSub(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, OverflowBehavior ob=OverflowBehavior::None)
cir::ConditionOp createCondition(mlir::Value condition)
Create a loop condition.
cir::CopyOp createCopy(mlir::Value dst, mlir::Value src, bool isVolatile=false, bool skipTailPadding=false)
Create a copy with inferred length.
cir::VoidType getVoidTy()
cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc)
mlir::Value createCast(mlir::Location loc, cir::CastKind kind, mlir::Value src, mlir::Type newTy)
cir::PointerType getVoidFnPtrTy(mlir::TypeRange argTypes={})
Returns void (*)(T...) as a cir::PointerType.
mlir::Value createFDiv(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
mlir::Value createAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, OverflowBehavior ob=OverflowBehavior::None)
cir::PointerType getPointerTo(mlir::Type ty)
mlir::Value createFNeg(mlir::Location loc, mlir::Value operand)
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, mlir::Type returnType, mlir::ValueRange operands, llvm::ArrayRef< mlir::NamedAttribute > attrs={}, llvm::ArrayRef< mlir::NamedAttrList > argAttrs={}, llvm::ArrayRef< mlir::NamedAttribute > resAttrs={}, cir::CallingConv callingConv=cir::CallingConv::C)
mlir::Value createFAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
mlir::Value createComplexImag(mlir::Location loc, mlir::Value operand)
cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc)
cir::IntType getUIntNTy(int n)
cir::DoWhileOp createDoWhile(mlir::Location loc, llvm::function_ref< void(mlir::OpBuilder &, mlir::Location)> condBuilder, llvm::function_ref< void(mlir::OpBuilder &, mlir::Location)> bodyBuilder)
Create a do-while operation.
cir::GetGlobalOp createGetGlobal(mlir::Location loc, cir::GlobalOp global, bool threadLocal=false)
mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType, llvm::StringRef name, mlir::IntegerAttr alignment, mlir::Value dynAllocSize)
cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr, bool isVolatile=false, uint64_t alignment=0, bool isNontemporal=false)
mlir::Value getSignedInt(mlir::Location loc, int64_t val, unsigned numBits)
mlir::Value createAnd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
mlir::Value createBitcast(mlir::Value src, mlir::Type newTy)
mlir::Value createFMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
cir::FuncType getVoidFnTy(mlir::TypeRange argTypes={})
Returns void (T...) as a cir::FuncType.
cir::CmpOp createCompare(mlir::Location loc, cir::CmpOpKind kind, mlir::Value lhs, mlir::Value rhs)
mlir::IntegerAttr getAlignmentAttr(clang::CharUnits alignment)
mlir::Value createSelect(mlir::Location loc, mlir::Value condition, mlir::Value trueValue, mlir::Value falseValue)
mlir::Value createMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, OverflowBehavior ob=OverflowBehavior::None)
mlir::Value createMinus(mlir::Location loc, mlir::Value input, bool nsw=false)
cir::ConstantOp getConstantInt(mlir::Location loc, mlir::Type ty, int64_t value)
mlir::Value createComplexCreate(mlir::Location loc, mlir::Value real, mlir::Value imag)
cir::PointerType getVoidPtrTy(clang::LangAS langAS=clang::LangAS::Default)
mlir::Value createIsNaN(mlir::Location loc, mlir::Value operand)
cir::IntType getSIntNTy(int n)
mlir::Value createAlignedLoad(mlir::Location loc, mlir::Value ptr, uint64_t alignment)
cir::YieldOp createYield(mlir::Location loc, mlir::ValueRange value={})
Create a yield operation.
mlir::Value createLogicalAnd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
mlir::Value createFSub(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
cir::StoreOp createStore(mlir::Location loc, mlir::Value val, mlir::Value dst, bool isVolatile=false, bool isNontemporal=false, mlir::IntegerAttr align={}, cir::SyncScopeKindAttr scope={}, cir::MemOrderAttr order={})
cir::BoolType getBoolTy()
mlir::Value getUnsignedInt(mlir::Location loc, uint64_t val, unsigned numBits)
mlir::Value createComplexReal(mlir::Location loc, mlir::Value operand)
static llvm::SmallVector< RecordMemberKind > getAllDataKinds(llvm::ArrayRef< mlir::Type > members)
One Data kind per member.
Definition CIRTypes.cpp:162
llvm::Align getAsAlign() const
Returns Quantity as a valid llvm::Align, Beware llvm::Align assumes power of two 8-bit bytes.
Definition CharUnits.h:159
QuantityType getQuantity() const
Get the raw integer representation of this quantity.
Definition CharUnits.h:155
static CharUnits One()
Construct a CharUnits quantity of one.
Definition CharUnits.h:55
static CharUnits fromQuantity(QuantityType Quantity)
Construct a CharUnits quantity from a raw integer type.
Definition CharUnits.h:58
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Exposes information about the current target.
Definition TargetInfo.h:228
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
unsigned getTypeWidth(IntType T) const
Return the width (in bits) of the specified integer type enum.
IntType getSignedSizeType() const
Definition TargetInfo.h:397
Defines the clang::TargetInfo interface.
static bool isLocalLinkage(GlobalLinkageKind linkage)
Definition CIROpsEnums.h:51
static bool isWeakODRLinkage(GlobalLinkageKind linkage)
Definition CIROpsEnums.h:39
static bool isLinkOnceLinkage(GlobalLinkageKind linkage)
Definition CIROpsEnums.h:33
std::unique_ptr< LowerModule > createLowerModule(mlir::ModuleOp module)
const internal::VariadicAllOfMatcher< Attr > attr
bool isHIP(ID Id)
isHIP - Is this a HIP input.
Definition Types.cpp:317
constexpr size_t align(size_t Size)
Aligns a size to the pointer alignment.
Definition PrimType.h:218
void info(bool Verbose, unsigned Level, const char *Fmt, Ts &&...Args)
Prints an indented note to stderr when Verbose is set.
Definition Utils.h:57
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
bool CudaFeatureEnabled(llvm::VersionTuple, CudaFeature)
Definition Cuda.cpp:119
LLVM_READONLY bool isPreprocessingNumberBody(unsigned char c)
Return true if this is the body character of a C preprocessing number, which is [a-zA-Z0-9_.
Definition CharInfo.h:168
@ CUDA_USES_FATBIN_REGISTER_END
Definition Cuda.h:84
inits_range inits()
unsigned long uint64_t
std::unique_ptr< Pass > createLoweringPreparePass()
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)
static bool hipModuleCtor()
static bool opGlobalAnnotations()
static bool opGlobalCtorPriority()
static bool shouldSplitConstantStore()
static bool shouldUseMemSetToInitialize()
static bool opFuncExtraAttrs()
static bool shouldUseBZeroPlusStoresToInitialize()
static bool opFuncCallingConv()
static bool fastMathFlags()
static bool astVarDeclInterface()