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