clang 23.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"
12#include "clang/AST/Mangle.h"
13#include "clang/Basic/Module.h"
25#include "llvm/Support/Path.h"
26
27#include <memory>
28
29using namespace mlir;
30using namespace cir;
31
32namespace mlir {
33#define GEN_PASS_DEF_LOWERINGPREPARE
34#include "clang/CIR/Dialect/Passes.h.inc"
35} // namespace mlir
36
37static SmallString<128> getTransformedFileName(mlir::ModuleOp mlirModule) {
38 SmallString<128> fileName;
39
40 if (mlirModule.getSymName())
41 fileName = llvm::sys::path::filename(mlirModule.getSymName()->str());
42
43 if (fileName.empty())
44 fileName = "<null>";
45
46 for (size_t i = 0; i < fileName.size(); ++i) {
47 // Replace everything that's not [a-zA-Z0-9._] with a _. This set happens
48 // to be the set of C preprocessing numbers.
49 if (!clang::isPreprocessingNumberBody(fileName[i]))
50 fileName[i] = '_';
51 }
52
53 return fileName;
54}
55
56/// Return the FuncOp called by `callOp`.
57static cir::FuncOp getCalledFunction(cir::CallOp callOp) {
58 mlir::SymbolRefAttr sym = llvm::dyn_cast_if_present<mlir::SymbolRefAttr>(
59 callOp.getCallableForCallee());
60 if (!sym)
61 return nullptr;
62 return dyn_cast_or_null<cir::FuncOp>(
63 mlir::SymbolTable::lookupNearestSymbolFrom(callOp, sym));
64}
65
66namespace {
67struct LoweringPreparePass
68 : public impl::LoweringPrepareBase<LoweringPreparePass> {
69 LoweringPreparePass() = default;
70 void runOnOperation() override;
71
72 void runOnOp(mlir::Operation *op);
73 void lowerCastOp(cir::CastOp op);
74 void lowerComplexDivOp(cir::ComplexDivOp op);
75 void lowerComplexMulOp(cir::ComplexMulOp op);
76 void lowerUnaryOp(cir::UnaryOp op);
77 void lowerGlobalOp(cir::GlobalOp op);
78 void lowerArrayDtor(cir::ArrayDtor op);
79 void lowerArrayCtor(cir::ArrayCtor op);
80 void lowerTrivialCopyCall(cir::CallOp op);
81 void lowerStoreOfConstAggregate(cir::StoreOp op);
82
83 /// Build the function that initializes the specified global
84 cir::FuncOp buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op);
85
86 /// Handle the dtor region by registering destructor with __cxa_atexit
87 cir::FuncOp getOrCreateDtorFunc(CIRBaseBuilderTy &builder, cir::GlobalOp op,
88 mlir::Region &dtorRegion,
89 cir::CallOp &dtorCall);
90
91 /// Build a module init function that calls all the dynamic initializers.
92 void buildCXXGlobalInitFunc();
93
94 /// Materialize global ctor/dtor list
95 void buildGlobalCtorDtorList();
96
97 cir::FuncOp buildRuntimeFunction(
98 mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
99 cir::FuncType type,
100 cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage);
101
102 cir::GlobalOp buildRuntimeVariable(
103 mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
104 mlir::Type type,
105 cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage,
106 cir::VisibilityKind visibility = cir::VisibilityKind::Default);
107
108 /// Handle static local variable initialization with guard variables.
109 void handleStaticLocal(cir::GlobalOp globalOp, cir::GetGlobalOp getGlobalOp);
110
111 /// Get or create __cxa_guard_acquire function.
112 cir::FuncOp getGuardAcquireFn(cir::PointerType guardPtrTy);
113
114 /// Get or create __cxa_guard_release function.
115 cir::FuncOp getGuardReleaseFn(cir::PointerType guardPtrTy);
116
117 /// Create a guard global variable for a static local.
118 cir::GlobalOp createGuardGlobalOp(CIRBaseBuilderTy &builder,
119 mlir::Location loc, llvm::StringRef name,
120 cir::IntType guardTy,
121 cir::GlobalLinkageKind linkage);
122
123 /// Get the guard variable for a static local declaration.
124 cir::GlobalOp getStaticLocalDeclGuardAddress(llvm::StringRef globalSymName) {
125 auto it = staticLocalDeclGuardMap.find(globalSymName);
126 if (it != staticLocalDeclGuardMap.end())
127 return it->second;
128 return nullptr;
129 }
130
131 /// Set the guard variable for a static local declaration.
132 void setStaticLocalDeclGuardAddress(llvm::StringRef globalSymName,
133 cir::GlobalOp guard) {
134 staticLocalDeclGuardMap[globalSymName] = guard;
135 }
136
137 /// Get or create the guard variable for a static local declaration.
138 cir::GlobalOp getOrCreateStaticLocalDeclGuardAddress(
139 CIRBaseBuilderTy &builder, cir::GlobalOp globalOp,
140 cir::ASTVarDeclInterface varDecl, cir::IntType guardTy,
141 clang::CharUnits guardAlignment) {
142 llvm::StringRef globalSymName = globalOp.getSymName();
143 cir::GlobalOp guard = getStaticLocalDeclGuardAddress(globalSymName);
144 if (!guard) {
145 // Get the guard name from the static_local attribute.
146 llvm::StringRef guardName =
147 globalOp.getStaticLocalGuard()->getName().getValue();
148
149 // Create the guard variable with a zero-initializer.
150 guard = createGuardGlobalOp(builder, globalOp->getLoc(), guardName,
151 guardTy, globalOp.getLinkage());
152 guard.setInitialValueAttr(cir::IntAttr::get(guardTy, 0));
153 guard.setDSOLocal(globalOp.getDsoLocal());
154 guard.setAlignment(guardAlignment.getAsAlign().value());
155
156 // The ABI says: "It is suggested that it be emitted in the same COMDAT
157 // group as the associated data object." In practice, this doesn't work
158 // for non-ELF and non-Wasm object formats, so only do it for ELF and
159 // Wasm.
160 bool hasComdat = globalOp.getComdat();
161 const llvm::Triple &triple = astCtx->getTargetInfo().getTriple();
162 if (!varDecl.isLocalVarDecl() && hasComdat &&
163 (triple.isOSBinFormatELF() || triple.isOSBinFormatWasm())) {
164 globalOp->emitError("NYI: guard COMDAT for non-local variables");
165 return {};
166 } else if (hasComdat && globalOp.isWeakForLinker()) {
167 globalOp->emitError("NYI: guard COMDAT for weak linkage");
168 return {};
169 }
170
171 setStaticLocalDeclGuardAddress(globalSymName, guard);
172 }
173 return guard;
174 }
175
176 ///
177 /// AST related
178 /// -----------
179
180 clang::ASTContext *astCtx;
181
182 /// Tracks current module.
183 mlir::ModuleOp mlirModule;
184
185 /// Tracks existing dynamic initializers.
186 llvm::StringMap<uint32_t> dynamicInitializerNames;
187 llvm::SmallVector<cir::FuncOp> dynamicInitializers;
188
189 /// Tracks guard variables for static locals (keyed by global symbol name).
190 llvm::StringMap<cir::GlobalOp> staticLocalDeclGuardMap;
191
192 /// List of ctors and their priorities to be called before main()
193 llvm::SmallVector<std::pair<std::string, uint32_t>, 4> globalCtorList;
194 /// List of dtors and their priorities to be called when unloading module.
195 llvm::SmallVector<std::pair<std::string, uint32_t>, 4> globalDtorList;
196
197 /// Returns true if the target uses ARM-style guard variables for static
198 /// local initialization (32-bit guard, check bit 0 only).
199 bool useARMGuardVarABI() const {
200 switch (astCtx->getCXXABIKind()) {
201 case clang::TargetCXXABI::GenericARM:
202 case clang::TargetCXXABI::iOS:
203 case clang::TargetCXXABI::WatchOS:
204 case clang::TargetCXXABI::GenericAArch64:
205 case clang::TargetCXXABI::WebAssembly:
206 return true;
207 default:
208 return false;
209 }
210 }
211
212 /// Emit the guarded initialization for a static local variable.
213 /// This handles the if/else structure after the guard byte check,
214 /// following OG's ItaniumCXXABI::EmitGuardedInit skeleton.
215 void emitCXXGuardedInitIf(CIRBaseBuilderTy &builder, cir::GlobalOp globalOp,
216 cir::ASTVarDeclInterface varDecl,
217 mlir::Value guardPtr, cir::PointerType guardPtrTy,
218 bool threadsafe) {
219 auto loc = globalOp->getLoc();
220
221 // The semantics of dynamic initialization of variables with static or
222 // thread storage duration depends on whether they are declared at
223 // block-scope. The initialization of such variables at block-scope can be
224 // aborted with an exception and later retried (per C++20 [stmt.dcl]p4),
225 // and recursive entry to their initialization has undefined behavior (also
226 // per C++20 [stmt.dcl]p4). For such variables declared at non-block scope,
227 // exceptions lead to termination (per C++20 [except.terminate]p1), and
228 // recursive references to the variables are governed only by the lifetime
229 // rules (per C++20 [class.cdtor]p2), which means such references are
230 // perfectly fine as long as they avoid touching memory. As a result,
231 // block-scope variables must not be marked as initialized until after
232 // initialization completes (unless the mark is reverted following an
233 // exception), but non-block-scope variables must be marked prior to
234 // initialization so that recursive accesses during initialization do not
235 // restart initialization.
236
237 // Variables used when coping with thread-safe statics and exceptions.
238 if (threadsafe) {
239 // Call __cxa_guard_acquire.
240 cir::CallOp acquireCall = builder.createCallOp(
241 loc, getGuardAcquireFn(guardPtrTy), mlir::ValueRange{guardPtr});
242 mlir::Value acquireResult = acquireCall.getResult();
243
244 auto acquireZero = builder.getConstantInt(
245 loc, mlir::cast<cir::IntType>(acquireResult.getType()), 0);
246 auto shouldInit = builder.createCompare(loc, cir::CmpOpKind::ne,
247 acquireResult, acquireZero);
248
249 // Create the IfOp for the shouldInit check.
250 // Pass an empty callback to avoid auto-creating a yield terminator.
251 auto ifOp =
252 cir::IfOp::create(builder, loc, shouldInit, /*withElseRegion=*/false,
253 [](mlir::OpBuilder &, mlir::Location) {});
254 mlir::OpBuilder::InsertionGuard insertGuard(builder);
255 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
256
257 // Call __cxa_guard_abort along the exceptional edge.
258 // OG: CGF.EHStack.pushCleanup<CallGuardAbort>(EHCleanup, guard);
260
261 // Emit the initializer and add a global destructor if appropriate.
262 auto &ctorRegion = globalOp.getCtorRegion();
263 assert(!ctorRegion.empty() && "This should never be empty here.");
264 if (!ctorRegion.hasOneBlock())
265 llvm_unreachable("Multiple blocks NYI");
266 mlir::Block &block = ctorRegion.front();
267 mlir::Block *insertBlock = builder.getInsertionBlock();
268 insertBlock->getOperations().splice(insertBlock->end(),
269 block.getOperations(), block.begin(),
270 std::prev(block.end()));
271 builder.setInsertionPointToEnd(insertBlock);
272 ctorRegion.getBlocks().clear();
273
274 // Pop the guard-abort cleanup if we pushed one.
275 // OG: CGF.PopCleanupBlock();
277
278 // Call __cxa_guard_release. This cannot throw.
279 builder.createCallOp(loc, getGuardReleaseFn(guardPtrTy),
280 mlir::ValueRange{guardPtr});
281
282 builder.createYield(loc);
283 } else if (!varDecl.isLocalVarDecl()) {
284 // For non-local variables, store 1 into the first byte of the guard
285 // variable before the object initialization begins so that references
286 // to the variable during initialization don't restart initialization.
287 // OG: Builder.CreateStore(llvm::ConstantInt::get(CGM.Int8Ty, 1), ...);
288 // Then: CGF.EmitCXXGlobalVarDeclInit(D, var, shouldPerformInit);
289 globalOp->emitError("NYI: non-threadsafe init for non-local variables");
290 return;
291 } else {
292 // For local variables, store 1 into the first byte of the guard variable
293 // after the object initialization completes so that initialization is
294 // retried if initialization is interrupted by an exception.
295 globalOp->emitError("NYI: non-threadsafe init for local variables");
296 return;
297 }
298
299 builder.createYield(loc); // Outermost IfOp
300 }
301
302 void setASTContext(clang::ASTContext *c) { astCtx = c; }
303};
304
305} // namespace
306
307cir::GlobalOp LoweringPreparePass::buildRuntimeVariable(
308 mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
309 mlir::Type type, cir::GlobalLinkageKind linkage,
310 cir::VisibilityKind visibility) {
311 cir::GlobalOp g = dyn_cast_or_null<cir::GlobalOp>(
312 mlir::SymbolTable::lookupNearestSymbolFrom(
313 mlirModule, mlir::StringAttr::get(mlirModule->getContext(), name)));
314 if (!g) {
315 g = cir::GlobalOp::create(builder, loc, name, type);
316 g.setLinkageAttr(
317 cir::GlobalLinkageKindAttr::get(builder.getContext(), linkage));
318 mlir::SymbolTable::setSymbolVisibility(
319 g, mlir::SymbolTable::Visibility::Private);
320 g.setGlobalVisibilityAttr(
321 cir::VisibilityAttr::get(builder.getContext(), visibility));
322 }
323 return g;
324}
325
326cir::FuncOp LoweringPreparePass::buildRuntimeFunction(
327 mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
328 cir::FuncType type, cir::GlobalLinkageKind linkage) {
329 cir::FuncOp f = dyn_cast_or_null<FuncOp>(SymbolTable::lookupNearestSymbolFrom(
330 mlirModule, StringAttr::get(mlirModule->getContext(), name)));
331 if (!f) {
332 f = cir::FuncOp::create(builder, loc, name, type);
333 f.setLinkageAttr(
334 cir::GlobalLinkageKindAttr::get(builder.getContext(), linkage));
335 mlir::SymbolTable::setSymbolVisibility(
336 f, mlir::SymbolTable::Visibility::Private);
337
339 }
340 return f;
341}
342
343static mlir::Value lowerScalarToComplexCast(mlir::MLIRContext &ctx,
344 cir::CastOp op) {
345 cir::CIRBaseBuilderTy builder(ctx);
346 builder.setInsertionPoint(op);
347
348 mlir::Value src = op.getSrc();
349 mlir::Value imag = builder.getNullValue(src.getType(), op.getLoc());
350 return builder.createComplexCreate(op.getLoc(), src, imag);
351}
352
353static mlir::Value lowerComplexToScalarCast(mlir::MLIRContext &ctx,
354 cir::CastOp op,
355 cir::CastKind elemToBoolKind) {
356 cir::CIRBaseBuilderTy builder(ctx);
357 builder.setInsertionPoint(op);
358
359 mlir::Value src = op.getSrc();
360 if (!mlir::isa<cir::BoolType>(op.getType()))
361 return builder.createComplexReal(op.getLoc(), src);
362
363 // Complex cast to bool: (bool)(a+bi) => (bool)a || (bool)b
364 mlir::Value srcReal = builder.createComplexReal(op.getLoc(), src);
365 mlir::Value srcImag = builder.createComplexImag(op.getLoc(), src);
366
367 cir::BoolType boolTy = builder.getBoolTy();
368 mlir::Value srcRealToBool =
369 builder.createCast(op.getLoc(), elemToBoolKind, srcReal, boolTy);
370 mlir::Value srcImagToBool =
371 builder.createCast(op.getLoc(), elemToBoolKind, srcImag, boolTy);
372 return builder.createLogicalOr(op.getLoc(), srcRealToBool, srcImagToBool);
373}
374
375static mlir::Value lowerComplexToComplexCast(mlir::MLIRContext &ctx,
376 cir::CastOp op,
377 cir::CastKind scalarCastKind) {
378 CIRBaseBuilderTy builder(ctx);
379 builder.setInsertionPoint(op);
380
381 mlir::Value src = op.getSrc();
382 auto dstComplexElemTy =
383 mlir::cast<cir::ComplexType>(op.getType()).getElementType();
384
385 mlir::Value srcReal = builder.createComplexReal(op.getLoc(), src);
386 mlir::Value srcImag = builder.createComplexImag(op.getLoc(), src);
387
388 mlir::Value dstReal = builder.createCast(op.getLoc(), scalarCastKind, srcReal,
389 dstComplexElemTy);
390 mlir::Value dstImag = builder.createCast(op.getLoc(), scalarCastKind, srcImag,
391 dstComplexElemTy);
392 return builder.createComplexCreate(op.getLoc(), dstReal, dstImag);
393}
394
395void LoweringPreparePass::lowerCastOp(cir::CastOp op) {
396 mlir::MLIRContext &ctx = getContext();
397 mlir::Value loweredValue = [&]() -> mlir::Value {
398 switch (op.getKind()) {
399 case cir::CastKind::float_to_complex:
400 case cir::CastKind::int_to_complex:
401 return lowerScalarToComplexCast(ctx, op);
402 case cir::CastKind::float_complex_to_real:
403 case cir::CastKind::int_complex_to_real:
404 return lowerComplexToScalarCast(ctx, op, op.getKind());
405 case cir::CastKind::float_complex_to_bool:
406 return lowerComplexToScalarCast(ctx, op, cir::CastKind::float_to_bool);
407 case cir::CastKind::int_complex_to_bool:
408 return lowerComplexToScalarCast(ctx, op, cir::CastKind::int_to_bool);
409 case cir::CastKind::float_complex:
410 return lowerComplexToComplexCast(ctx, op, cir::CastKind::floating);
411 case cir::CastKind::float_complex_to_int_complex:
412 return lowerComplexToComplexCast(ctx, op, cir::CastKind::float_to_int);
413 case cir::CastKind::int_complex:
414 return lowerComplexToComplexCast(ctx, op, cir::CastKind::integral);
415 case cir::CastKind::int_complex_to_float_complex:
416 return lowerComplexToComplexCast(ctx, op, cir::CastKind::int_to_float);
417 default:
418 return nullptr;
419 }
420 }();
421
422 if (loweredValue) {
423 op.replaceAllUsesWith(loweredValue);
424 op.erase();
425 }
426}
427
428static mlir::Value buildComplexBinOpLibCall(
429 LoweringPreparePass &pass, CIRBaseBuilderTy &builder,
430 llvm::StringRef (*libFuncNameGetter)(llvm::APFloat::Semantics),
431 mlir::Location loc, cir::ComplexType ty, mlir::Value lhsReal,
432 mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag) {
433 cir::FPTypeInterface elementTy =
434 mlir::cast<cir::FPTypeInterface>(ty.getElementType());
435
436 llvm::StringRef libFuncName = libFuncNameGetter(
437 llvm::APFloat::SemanticsToEnum(elementTy.getFloatSemantics()));
438 llvm::SmallVector<mlir::Type, 4> libFuncInputTypes(4, elementTy);
439
440 cir::FuncType libFuncTy = cir::FuncType::get(libFuncInputTypes, ty);
441
442 // Insert a declaration for the runtime function to be used in Complex
443 // multiplication and division when needed
444 cir::FuncOp libFunc;
445 {
446 mlir::OpBuilder::InsertionGuard ipGuard{builder};
447 builder.setInsertionPointToStart(pass.mlirModule.getBody());
448 libFunc = pass.buildRuntimeFunction(builder, libFuncName, loc, libFuncTy);
449 }
450
451 cir::CallOp call =
452 builder.createCallOp(loc, libFunc, {lhsReal, lhsImag, rhsReal, rhsImag});
453 return call.getResult();
454}
455
456static llvm::StringRef
457getComplexDivLibCallName(llvm::APFloat::Semantics semantics) {
458 switch (semantics) {
459 case llvm::APFloat::S_IEEEhalf:
460 return "__divhc3";
461 case llvm::APFloat::S_IEEEsingle:
462 return "__divsc3";
463 case llvm::APFloat::S_IEEEdouble:
464 return "__divdc3";
465 case llvm::APFloat::S_PPCDoubleDouble:
466 return "__divtc3";
467 case llvm::APFloat::S_x87DoubleExtended:
468 return "__divxc3";
469 case llvm::APFloat::S_IEEEquad:
470 return "__divtc3";
471 default:
472 llvm_unreachable("unsupported floating point type");
473 }
474}
475
476static mlir::Value
477buildAlgebraicComplexDiv(CIRBaseBuilderTy &builder, mlir::Location loc,
478 mlir::Value lhsReal, mlir::Value lhsImag,
479 mlir::Value rhsReal, mlir::Value rhsImag) {
480 // (a+bi) / (c+di) = ((ac+bd)/(cc+dd)) + ((bc-ad)/(cc+dd))i
481 mlir::Value &a = lhsReal;
482 mlir::Value &b = lhsImag;
483 mlir::Value &c = rhsReal;
484 mlir::Value &d = rhsImag;
485
486 mlir::Value ac = builder.createMul(loc, a, c); // a*c
487 mlir::Value bd = builder.createMul(loc, b, d); // b*d
488 mlir::Value cc = builder.createMul(loc, c, c); // c*c
489 mlir::Value dd = builder.createMul(loc, d, d); // d*d
490 mlir::Value acbd = builder.createAdd(loc, ac, bd); // ac+bd
491 mlir::Value ccdd = builder.createAdd(loc, cc, dd); // cc+dd
492 mlir::Value resultReal = builder.createDiv(loc, acbd, ccdd);
493
494 mlir::Value bc = builder.createMul(loc, b, c); // b*c
495 mlir::Value ad = builder.createMul(loc, a, d); // a*d
496 mlir::Value bcad = builder.createSub(loc, bc, ad); // bc-ad
497 mlir::Value resultImag = builder.createDiv(loc, bcad, ccdd);
498 return builder.createComplexCreate(loc, resultReal, resultImag);
499}
500
501static mlir::Value
503 mlir::Value lhsReal, mlir::Value lhsImag,
504 mlir::Value rhsReal, mlir::Value rhsImag) {
505 // Implements Smith's algorithm for complex division.
506 // SMITH, R. L. Algorithm 116: Complex division. Commun. ACM 5, 8 (1962).
507
508 // Let:
509 // - lhs := a+bi
510 // - rhs := c+di
511 // - result := lhs / rhs = e+fi
512 //
513 // The algorithm pseudocode looks like follows:
514 // if fabs(c) >= fabs(d):
515 // r := d / c
516 // tmp := c + r*d
517 // e = (a + b*r) / tmp
518 // f = (b - a*r) / tmp
519 // else:
520 // r := c / d
521 // tmp := d + r*c
522 // e = (a*r + b) / tmp
523 // f = (b*r - a) / tmp
524
525 mlir::Value &a = lhsReal;
526 mlir::Value &b = lhsImag;
527 mlir::Value &c = rhsReal;
528 mlir::Value &d = rhsImag;
529
530 auto trueBranchBuilder = [&](mlir::OpBuilder &, mlir::Location) {
531 mlir::Value r = builder.createDiv(loc, d, c); // r := d / c
532 mlir::Value rd = builder.createMul(loc, r, d); // r*d
533 mlir::Value tmp = builder.createAdd(loc, c, rd); // tmp := c + r*d
534
535 mlir::Value br = builder.createMul(loc, b, r); // b*r
536 mlir::Value abr = builder.createAdd(loc, a, br); // a + b*r
537 mlir::Value e = builder.createDiv(loc, abr, tmp);
538
539 mlir::Value ar = builder.createMul(loc, a, r); // a*r
540 mlir::Value bar = builder.createSub(loc, b, ar); // b - a*r
541 mlir::Value f = builder.createDiv(loc, bar, tmp);
542
543 mlir::Value result = builder.createComplexCreate(loc, e, f);
544 builder.createYield(loc, result);
545 };
546
547 auto falseBranchBuilder = [&](mlir::OpBuilder &, mlir::Location) {
548 mlir::Value r = builder.createDiv(loc, c, d); // r := c / d
549 mlir::Value rc = builder.createMul(loc, r, c); // r*c
550 mlir::Value tmp = builder.createAdd(loc, d, rc); // tmp := d + r*c
551
552 mlir::Value ar = builder.createMul(loc, a, r); // a*r
553 mlir::Value arb = builder.createAdd(loc, ar, b); // a*r + b
554 mlir::Value e = builder.createDiv(loc, arb, tmp);
555
556 mlir::Value br = builder.createMul(loc, b, r); // b*r
557 mlir::Value bra = builder.createSub(loc, br, a); // b*r - a
558 mlir::Value f = builder.createDiv(loc, bra, tmp);
559
560 mlir::Value result = builder.createComplexCreate(loc, e, f);
561 builder.createYield(loc, result);
562 };
563
564 auto cFabs = cir::FAbsOp::create(builder, loc, c);
565 auto dFabs = cir::FAbsOp::create(builder, loc, d);
566 cir::CmpOp cmpResult =
567 builder.createCompare(loc, cir::CmpOpKind::ge, cFabs, dFabs);
568 auto ternary = cir::TernaryOp::create(builder, loc, cmpResult,
569 trueBranchBuilder, falseBranchBuilder);
570
571 return ternary.getResult();
572}
573
575 mlir::MLIRContext &context, clang::ASTContext &cc,
576 CIRBaseBuilderTy &builder, mlir::Type elementType) {
577
578 auto getHigherPrecisionFPType = [&context](mlir::Type type) -> mlir::Type {
579 if (mlir::isa<cir::FP16Type>(type))
580 return cir::SingleType::get(&context);
581
582 if (mlir::isa<cir::SingleType>(type) || mlir::isa<cir::BF16Type>(type))
583 return cir::DoubleType::get(&context);
584
585 if (mlir::isa<cir::DoubleType>(type))
586 return cir::LongDoubleType::get(&context, type);
587
588 return type;
589 };
590
591 auto getFloatTypeSemantics =
592 [&cc](mlir::Type type) -> const llvm::fltSemantics & {
593 const clang::TargetInfo &info = cc.getTargetInfo();
594 if (mlir::isa<cir::FP16Type>(type))
595 return info.getHalfFormat();
596
597 if (mlir::isa<cir::BF16Type>(type))
598 return info.getBFloat16Format();
599
600 if (mlir::isa<cir::SingleType>(type))
601 return info.getFloatFormat();
602
603 if (mlir::isa<cir::DoubleType>(type))
604 return info.getDoubleFormat();
605
606 if (mlir::isa<cir::LongDoubleType>(type)) {
607 if (cc.getLangOpts().OpenMP && cc.getLangOpts().OpenMPIsTargetDevice)
608 llvm_unreachable("NYI Float type semantics with OpenMP");
609 return info.getLongDoubleFormat();
610 }
611
612 if (mlir::isa<cir::FP128Type>(type)) {
613 if (cc.getLangOpts().OpenMP && cc.getLangOpts().OpenMPIsTargetDevice)
614 llvm_unreachable("NYI Float type semantics with OpenMP");
615 return info.getFloat128Format();
616 }
617
618 llvm_unreachable("Unsupported float type semantics");
619 };
620
621 const mlir::Type higherElementType = getHigherPrecisionFPType(elementType);
622 const llvm::fltSemantics &elementTypeSemantics =
623 getFloatTypeSemantics(elementType);
624 const llvm::fltSemantics &higherElementTypeSemantics =
625 getFloatTypeSemantics(higherElementType);
626
627 // Check that the promoted type can handle the intermediate values without
628 // overflowing. This can be interpreted as:
629 // (SmallerType.LargestFiniteVal * SmallerType.LargestFiniteVal) * 2 <=
630 // LargerType.LargestFiniteVal.
631 // In terms of exponent it gives this formula:
632 // (SmallerType.LargestFiniteVal * SmallerType.LargestFiniteVal
633 // doubles the exponent of SmallerType.LargestFiniteVal)
634 if (llvm::APFloat::semanticsMaxExponent(elementTypeSemantics) * 2 + 1 <=
635 llvm::APFloat::semanticsMaxExponent(higherElementTypeSemantics)) {
636 return higherElementType;
637 }
638
639 // The intermediate values can't be represented in the promoted type
640 // without overflowing.
641 return {};
642}
643
644static mlir::Value
645lowerComplexDiv(LoweringPreparePass &pass, CIRBaseBuilderTy &builder,
646 mlir::Location loc, cir::ComplexDivOp op, mlir::Value lhsReal,
647 mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag,
648 mlir::MLIRContext &mlirCx, clang::ASTContext &cc) {
649 cir::ComplexType complexTy = op.getType();
650 if (mlir::isa<cir::FPTypeInterface>(complexTy.getElementType())) {
651 cir::ComplexRangeKind range = op.getRange();
652 if (range == cir::ComplexRangeKind::Improved)
653 return buildRangeReductionComplexDiv(builder, loc, lhsReal, lhsImag,
654 rhsReal, rhsImag);
655
656 if (range == cir::ComplexRangeKind::Full)
658 loc, complexTy, lhsReal, lhsImag, rhsReal,
659 rhsImag);
660
661 if (range == cir::ComplexRangeKind::Promoted) {
662 mlir::Type originalElementType = complexTy.getElementType();
663 mlir::Type higherPrecisionElementType =
665 originalElementType);
666
667 if (!higherPrecisionElementType)
668 return buildRangeReductionComplexDiv(builder, loc, lhsReal, lhsImag,
669 rhsReal, rhsImag);
670
671 cir::CastKind floatingCastKind = cir::CastKind::floating;
672 lhsReal = builder.createCast(floatingCastKind, lhsReal,
673 higherPrecisionElementType);
674 lhsImag = builder.createCast(floatingCastKind, lhsImag,
675 higherPrecisionElementType);
676 rhsReal = builder.createCast(floatingCastKind, rhsReal,
677 higherPrecisionElementType);
678 rhsImag = builder.createCast(floatingCastKind, rhsImag,
679 higherPrecisionElementType);
680
681 mlir::Value algebraicResult = buildAlgebraicComplexDiv(
682 builder, loc, lhsReal, lhsImag, rhsReal, rhsImag);
683
684 mlir::Value resultReal = builder.createComplexReal(loc, algebraicResult);
685 mlir::Value resultImag = builder.createComplexImag(loc, algebraicResult);
686
687 mlir::Value finalReal =
688 builder.createCast(floatingCastKind, resultReal, originalElementType);
689 mlir::Value finalImag =
690 builder.createCast(floatingCastKind, resultImag, originalElementType);
691 return builder.createComplexCreate(loc, finalReal, finalImag);
692 }
693 }
694
695 return buildAlgebraicComplexDiv(builder, loc, lhsReal, lhsImag, rhsReal,
696 rhsImag);
697}
698
699void LoweringPreparePass::lowerComplexDivOp(cir::ComplexDivOp op) {
700 cir::CIRBaseBuilderTy builder(getContext());
701 builder.setInsertionPointAfter(op);
702 mlir::Location loc = op.getLoc();
703 mlir::TypedValue<cir::ComplexType> lhs = op.getLhs();
704 mlir::TypedValue<cir::ComplexType> rhs = op.getRhs();
705 mlir::Value lhsReal = builder.createComplexReal(loc, lhs);
706 mlir::Value lhsImag = builder.createComplexImag(loc, lhs);
707 mlir::Value rhsReal = builder.createComplexReal(loc, rhs);
708 mlir::Value rhsImag = builder.createComplexImag(loc, rhs);
709
710 mlir::Value loweredResult =
711 lowerComplexDiv(*this, builder, loc, op, lhsReal, lhsImag, rhsReal,
712 rhsImag, getContext(), *astCtx);
713 op.replaceAllUsesWith(loweredResult);
714 op.erase();
715}
716
717static llvm::StringRef
718getComplexMulLibCallName(llvm::APFloat::Semantics semantics) {
719 switch (semantics) {
720 case llvm::APFloat::S_IEEEhalf:
721 return "__mulhc3";
722 case llvm::APFloat::S_IEEEsingle:
723 return "__mulsc3";
724 case llvm::APFloat::S_IEEEdouble:
725 return "__muldc3";
726 case llvm::APFloat::S_PPCDoubleDouble:
727 return "__multc3";
728 case llvm::APFloat::S_x87DoubleExtended:
729 return "__mulxc3";
730 case llvm::APFloat::S_IEEEquad:
731 return "__multc3";
732 default:
733 llvm_unreachable("unsupported floating point type");
734 }
735}
736
737static mlir::Value lowerComplexMul(LoweringPreparePass &pass,
738 CIRBaseBuilderTy &builder,
739 mlir::Location loc, cir::ComplexMulOp op,
740 mlir::Value lhsReal, mlir::Value lhsImag,
741 mlir::Value rhsReal, mlir::Value rhsImag) {
742 // (a+bi) * (c+di) = (ac-bd) + (ad+bc)i
743 mlir::Value resultRealLhs = builder.createMul(loc, lhsReal, rhsReal); // ac
744 mlir::Value resultRealRhs = builder.createMul(loc, lhsImag, rhsImag); // bd
745 mlir::Value resultImagLhs = builder.createMul(loc, lhsReal, rhsImag); // ad
746 mlir::Value resultImagRhs = builder.createMul(loc, lhsImag, rhsReal); // bc
747 mlir::Value resultReal = builder.createSub(loc, resultRealLhs, resultRealRhs);
748 mlir::Value resultImag = builder.createAdd(loc, resultImagLhs, resultImagRhs);
749 mlir::Value algebraicResult =
750 builder.createComplexCreate(loc, resultReal, resultImag);
751
752 cir::ComplexType complexTy = op.getType();
753 cir::ComplexRangeKind rangeKind = op.getRange();
754 if (mlir::isa<cir::IntType>(complexTy.getElementType()) ||
755 rangeKind == cir::ComplexRangeKind::Basic ||
756 rangeKind == cir::ComplexRangeKind::Improved ||
757 rangeKind == cir::ComplexRangeKind::Promoted)
758 return algebraicResult;
759
761
762 // Check whether the real part and the imaginary part of the result are both
763 // NaN. If so, emit a library call to compute the multiplication instead.
764 // We check a value against NaN by comparing the value against itself.
765 mlir::Value resultRealIsNaN = builder.createIsNaN(loc, resultReal);
766 mlir::Value resultImagIsNaN = builder.createIsNaN(loc, resultImag);
767 mlir::Value resultRealAndImagAreNaN =
768 builder.createLogicalAnd(loc, resultRealIsNaN, resultImagIsNaN);
769
770 return cir::TernaryOp::create(
771 builder, loc, resultRealAndImagAreNaN,
772 [&](mlir::OpBuilder &, mlir::Location) {
773 mlir::Value libCallResult = buildComplexBinOpLibCall(
774 pass, builder, &getComplexMulLibCallName, loc, complexTy,
775 lhsReal, lhsImag, rhsReal, rhsImag);
776 builder.createYield(loc, libCallResult);
777 },
778 [&](mlir::OpBuilder &, mlir::Location) {
779 builder.createYield(loc, algebraicResult);
780 })
781 .getResult();
782}
783
784void LoweringPreparePass::lowerComplexMulOp(cir::ComplexMulOp op) {
785 cir::CIRBaseBuilderTy builder(getContext());
786 builder.setInsertionPointAfter(op);
787 mlir::Location loc = op.getLoc();
788 mlir::TypedValue<cir::ComplexType> lhs = op.getLhs();
789 mlir::TypedValue<cir::ComplexType> rhs = op.getRhs();
790 mlir::Value lhsReal = builder.createComplexReal(loc, lhs);
791 mlir::Value lhsImag = builder.createComplexImag(loc, lhs);
792 mlir::Value rhsReal = builder.createComplexReal(loc, rhs);
793 mlir::Value rhsImag = builder.createComplexImag(loc, rhs);
794 mlir::Value loweredResult = lowerComplexMul(*this, builder, loc, op, lhsReal,
795 lhsImag, rhsReal, rhsImag);
796 op.replaceAllUsesWith(loweredResult);
797 op.erase();
798}
799
800void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
801 mlir::Type ty = op.getType();
802 if (!mlir::isa<cir::ComplexType>(ty))
803 return;
804
805 mlir::Location loc = op.getLoc();
806 cir::UnaryOpKind opKind = op.getKind();
807
808 CIRBaseBuilderTy builder(getContext());
809 builder.setInsertionPointAfter(op);
810
811 mlir::Value operand = op.getInput();
812 mlir::Value operandReal = builder.createComplexReal(loc, operand);
813 mlir::Value operandImag = builder.createComplexImag(loc, operand);
814
815 mlir::Value resultReal;
816 mlir::Value resultImag;
817
818 switch (opKind) {
819 case cir::UnaryOpKind::Inc:
820 case cir::UnaryOpKind::Dec:
821 resultReal = builder.createUnaryOp(loc, opKind, operandReal);
822 resultImag = operandImag;
823 break;
824
825 case cir::UnaryOpKind::Plus:
826 case cir::UnaryOpKind::Minus:
827 resultReal = builder.createUnaryOp(loc, opKind, operandReal);
828 resultImag = builder.createUnaryOp(loc, opKind, operandImag);
829 break;
830
831 case cir::UnaryOpKind::Not:
832 resultReal = operandReal;
833 resultImag =
834 builder.createUnaryOp(loc, cir::UnaryOpKind::Minus, operandImag);
835 break;
836 }
837
838 mlir::Value result = builder.createComplexCreate(loc, resultReal, resultImag);
839 op.replaceAllUsesWith(result);
840 op.erase();
841}
842
843cir::FuncOp LoweringPreparePass::getOrCreateDtorFunc(CIRBaseBuilderTy &builder,
844 cir::GlobalOp op,
845 mlir::Region &dtorRegion,
846 cir::CallOp &dtorCall) {
847 mlir::OpBuilder::InsertionGuard guard(builder);
850
851 cir::VoidType voidTy = builder.getVoidTy();
852 auto voidPtrTy = cir::PointerType::get(voidTy);
853
854 // Look for operations in dtorBlock
855 mlir::Block &dtorBlock = dtorRegion.front();
856
857 // The first operation should be a get_global to retrieve the address
858 // of the global variable we're destroying.
859 auto opIt = dtorBlock.getOperations().begin();
860 cir::GetGlobalOp ggop = mlir::cast<cir::GetGlobalOp>(*opIt);
861
862 // The simple case is just a call to a destructor, like this:
863 //
864 // %0 = cir.get_global %globalS : !cir.ptr<!rec_S>
865 // cir.call %_ZN1SD1Ev(%0) : (!cir.ptr<!rec_S>) -> ()
866 // (implicit cir.yield)
867 //
868 // That is, if the second operation is a call that takes the get_global result
869 // as its only operand, and the only other operation is a yield, then we can
870 // just return the called function.
871 if (dtorBlock.getOperations().size() == 3) {
872 auto callOp = mlir::dyn_cast<cir::CallOp>(&*(++opIt));
873 auto yieldOp = mlir::dyn_cast<cir::YieldOp>(&*(++opIt));
874 if (yieldOp && callOp && callOp.getNumOperands() == 1 &&
875 callOp.getArgOperand(0) == ggop) {
876 dtorCall = callOp;
877 return getCalledFunction(callOp);
878 }
879 }
880
881 // Otherwise, we need to create a helper function to replace the dtor region.
882 // This name is kind of arbitrary, but it matches the name that classic
883 // codegen uses, based on the expected case that gets us here.
884 builder.setInsertionPointAfter(op);
885 SmallString<256> fnName("__cxx_global_array_dtor");
886 uint32_t cnt = dynamicInitializerNames[fnName]++;
887 if (cnt)
888 fnName += "." + std::to_string(cnt);
889
890 // Create the helper function.
891 auto fnType = cir::FuncType::get({voidPtrTy}, voidTy);
892 cir::FuncOp dtorFunc =
893 buildRuntimeFunction(builder, fnName, op.getLoc(), fnType,
894 cir::GlobalLinkageKind::InternalLinkage);
895 mlir::Block *entryBB = dtorFunc.addEntryBlock();
896
897 // Move everything from the dtor region into the helper function.
898 entryBB->getOperations().splice(entryBB->begin(), dtorBlock.getOperations(),
899 dtorBlock.begin(), dtorBlock.end());
900
901 // Before erasing this, clone it back into the dtor region
902 cir::GetGlobalOp dtorGGop =
903 mlir::cast<cir::GetGlobalOp>(entryBB->getOperations().front());
904 builder.setInsertionPointToStart(&dtorBlock);
905 builder.clone(*dtorGGop.getOperation());
906
907 // Replace all uses of the help function's get_global with the function
908 // argument.
909 mlir::Value dtorArg = entryBB->getArgument(0);
910 dtorGGop.replaceAllUsesWith(dtorArg);
911 dtorGGop.erase();
912
913 // Replace the yield in the final block with a return
914 mlir::Block &finalBlock = dtorFunc.getBody().back();
915 auto yieldOp = cast<cir::YieldOp>(finalBlock.getTerminator());
916 builder.setInsertionPoint(yieldOp);
917 cir::ReturnOp::create(builder, yieldOp->getLoc());
918 yieldOp->erase();
919
920 // Create a call to the helper function, passing the original get_global op
921 // as the argument.
922 cir::GetGlobalOp origGGop =
923 mlir::cast<cir::GetGlobalOp>(dtorBlock.getOperations().front());
924 builder.setInsertionPointAfter(origGGop);
925 mlir::Value ggopResult = origGGop.getResult();
926 dtorCall = builder.createCallOp(op.getLoc(), dtorFunc, ggopResult);
927
928 // Add a yield after the call.
929 auto finalYield = cir::YieldOp::create(builder, op.getLoc());
930
931 // Erase everything after the yield.
932 dtorBlock.getOperations().erase(std::next(mlir::Block::iterator(finalYield)),
933 dtorBlock.end());
934 dtorRegion.getBlocks().erase(std::next(dtorRegion.begin()), dtorRegion.end());
935
936 return dtorFunc;
937}
938
939cir::FuncOp
940LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
941 // TODO(cir): Store this in the GlobalOp.
942 // This should come from the MangleContext, but for now I'm hardcoding it.
943 SmallString<256> fnName("__cxx_global_var_init");
944 // Get a unique name
945 uint32_t cnt = dynamicInitializerNames[fnName]++;
946 if (cnt)
947 fnName += "." + std::to_string(cnt);
948
949 // Create a variable initialization function.
950 CIRBaseBuilderTy builder(getContext());
951 builder.setInsertionPointAfter(op);
952 cir::VoidType voidTy = builder.getVoidTy();
953 auto fnType = cir::FuncType::get({}, voidTy);
954 FuncOp f = buildRuntimeFunction(builder, fnName, op.getLoc(), fnType,
955 cir::GlobalLinkageKind::InternalLinkage);
956
957 // Move over the initialzation code of the ctor region.
958 mlir::Block *entryBB = f.addEntryBlock();
959 if (!op.getCtorRegion().empty()) {
960 mlir::Block &block = op.getCtorRegion().front();
961 entryBB->getOperations().splice(entryBB->begin(), block.getOperations(),
962 block.begin(), std::prev(block.end()));
963 }
964
965 // Register the destructor call with __cxa_atexit
966 mlir::Region &dtorRegion = op.getDtorRegion();
967 if (!dtorRegion.empty()) {
970
971 // Create a variable that binds the atexit to this shared object.
972 builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
973 cir::GlobalOp handle = buildRuntimeVariable(
974 builder, "__dso_handle", op.getLoc(), builder.getI8Type(),
975 cir::GlobalLinkageKind::ExternalLinkage, cir::VisibilityKind::Hidden);
976
977 // If this is a simple call to a destructor, get the called function.
978 // Otherwise, create a helper function for the entire dtor region,
979 // replacing the current dtor region body with a call to the helper
980 // function.
981 cir::CallOp dtorCall;
982 cir::FuncOp dtorFunc =
983 getOrCreateDtorFunc(builder, op, dtorRegion, dtorCall);
984
985 // Create a runtime helper function:
986 // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
987 auto voidPtrTy = cir::PointerType::get(voidTy);
988 auto voidFnTy = cir::FuncType::get({voidPtrTy}, voidTy);
989 auto voidFnPtrTy = cir::PointerType::get(voidFnTy);
990 auto handlePtrTy = cir::PointerType::get(handle.getSymType());
991 auto fnAtExitType =
992 cir::FuncType::get({voidFnPtrTy, voidPtrTy, handlePtrTy}, voidTy);
993 const char *nameAtExit = "__cxa_atexit";
994 cir::FuncOp fnAtExit =
995 buildRuntimeFunction(builder, nameAtExit, op.getLoc(), fnAtExitType);
996
997 // Replace the dtor (or helper) call with a call to
998 // __cxa_atexit(&dtor, &var, &__dso_handle)
999 builder.setInsertionPointAfter(dtorCall);
1000 mlir::Value args[3];
1001 auto dtorPtrTy = cir::PointerType::get(dtorFunc.getFunctionType());
1002 // dtorPtrTy
1003 args[0] = cir::GetGlobalOp::create(builder, dtorCall.getLoc(), dtorPtrTy,
1004 dtorFunc.getSymName());
1005 args[0] = cir::CastOp::create(builder, dtorCall.getLoc(), voidFnPtrTy,
1006 cir::CastKind::bitcast, args[0]);
1007 args[1] =
1008 cir::CastOp::create(builder, dtorCall.getLoc(), voidPtrTy,
1009 cir::CastKind::bitcast, dtorCall.getArgOperand(0));
1010 args[2] = cir::GetGlobalOp::create(builder, handle.getLoc(), handlePtrTy,
1011 handle.getSymName());
1012 builder.createCallOp(dtorCall.getLoc(), fnAtExit, args);
1013 dtorCall->erase();
1014 mlir::Block &dtorBlock = dtorRegion.front();
1015 entryBB->getOperations().splice(entryBB->end(), dtorBlock.getOperations(),
1016 dtorBlock.begin(),
1017 std::prev(dtorBlock.end()));
1018 }
1019
1020 // Replace cir.yield with cir.return
1021 builder.setInsertionPointToEnd(entryBB);
1022 mlir::Operation *yieldOp = nullptr;
1023 if (!op.getCtorRegion().empty()) {
1024 mlir::Block &block = op.getCtorRegion().front();
1025 yieldOp = &block.getOperations().back();
1026 } else {
1027 assert(!dtorRegion.empty());
1028 mlir::Block &block = dtorRegion.front();
1029 yieldOp = &block.getOperations().back();
1030 }
1031
1032 assert(isa<cir::YieldOp>(*yieldOp));
1033 cir::ReturnOp::create(builder, yieldOp->getLoc());
1034 return f;
1035}
1036
1037cir::FuncOp
1038LoweringPreparePass::getGuardAcquireFn(cir::PointerType guardPtrTy) {
1039 // int __cxa_guard_acquire(__guard *guard_object);
1040 CIRBaseBuilderTy builder(getContext());
1041 mlir::OpBuilder::InsertionGuard ipGuard{builder};
1042 builder.setInsertionPointToStart(mlirModule.getBody());
1043 mlir::Location loc = mlirModule.getLoc();
1044 cir::IntType intTy = cir::IntType::get(&getContext(), 32, /*isSigned=*/true);
1045 auto fnType = cir::FuncType::get({guardPtrTy}, intTy);
1046 return buildRuntimeFunction(builder, "__cxa_guard_acquire", loc, fnType);
1047}
1048
1049cir::FuncOp
1050LoweringPreparePass::getGuardReleaseFn(cir::PointerType guardPtrTy) {
1051 // void __cxa_guard_release(__guard *guard_object);
1052 CIRBaseBuilderTy builder(getContext());
1053 mlir::OpBuilder::InsertionGuard ipGuard{builder};
1054 builder.setInsertionPointToStart(mlirModule.getBody());
1055 mlir::Location loc = mlirModule.getLoc();
1056 cir::VoidType voidTy = cir::VoidType::get(&getContext());
1057 auto fnType = cir::FuncType::get({guardPtrTy}, voidTy);
1058 return buildRuntimeFunction(builder, "__cxa_guard_release", loc, fnType);
1059}
1060
1061cir::GlobalOp LoweringPreparePass::createGuardGlobalOp(
1062 CIRBaseBuilderTy &builder, mlir::Location loc, llvm::StringRef name,
1063 cir::IntType guardTy, cir::GlobalLinkageKind linkage) {
1064 mlir::OpBuilder::InsertionGuard guard(builder);
1065 builder.setInsertionPointToStart(mlirModule.getBody());
1066 cir::GlobalOp g = cir::GlobalOp::create(builder, loc, name, guardTy);
1067 g.setLinkageAttr(
1068 cir::GlobalLinkageKindAttr::get(builder.getContext(), linkage));
1069 mlir::SymbolTable::setSymbolVisibility(
1070 g, mlir::SymbolTable::Visibility::Private);
1071 return g;
1072}
1073
1074void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp,
1075 cir::GetGlobalOp getGlobalOp) {
1076 CIRBaseBuilderTy builder(getContext());
1077
1078 std::optional<cir::ASTVarDeclInterface> astOption = globalOp.getAst();
1079 assert(astOption.has_value());
1080 cir::ASTVarDeclInterface varDecl = astOption.value();
1081
1082 builder.setInsertionPointAfter(getGlobalOp);
1083 mlir::Block *getGlobalOpBlock = builder.getInsertionBlock();
1084
1085 // Remove the terminator temporarily - we'll add it back at the end.
1086 mlir::Operation *ret = getGlobalOpBlock->getTerminator();
1087 ret->remove();
1088 builder.setInsertionPointAfter(getGlobalOp);
1089
1090 // Inline variables that weren't instantiated from variable templates have
1091 // partially-ordered initialization within their translation unit.
1092 bool nonTemplateInline =
1093 varDecl.isInline() &&
1094 !clang::isTemplateInstantiation(varDecl.getTemplateSpecializationKind());
1095
1096 // Inline namespace-scope variables require guarded initialization in a
1097 // __cxx_global_var_init function. This is not yet implemented.
1098 if (nonTemplateInline) {
1099 globalOp->emitError(
1100 "NYI: guarded initialization for inline namespace-scope variables");
1101 return;
1102 }
1103
1104 // We only need to use thread-safe statics for local non-TLS variables and
1105 // inline variables; other global initialization is always single-threaded
1106 // or (through lazy dynamic loading in multiple threads) unsequenced.
1107 bool threadsafe = astCtx->getLangOpts().ThreadsafeStatics &&
1108 (varDecl.isLocalVarDecl() || nonTemplateInline) &&
1109 !varDecl.getTLSKind();
1110
1111 // TLS variables need special handling - the guard must also be thread-local.
1112 if (varDecl.getTLSKind()) {
1113 globalOp->emitError("NYI: guarded initialization for thread-local statics");
1114 return;
1115 }
1116
1117 // If we have a global variable with internal linkage and thread-safe statics
1118 // are disabled, we can just let the guard variable be of type i8.
1119 bool useInt8GuardVariable = !threadsafe && globalOp.hasInternalLinkage();
1120 if (useInt8GuardVariable) {
1121 globalOp->emitError("NYI: int8 guard variables for non-threadsafe statics");
1122 return;
1123 }
1124
1125 // Guard variables are 64 bits in the generic ABI and size width on ARM
1126 // (i.e. 32-bit on AArch32, 64-bit on AArch64).
1127 if (useARMGuardVarABI()) {
1128 globalOp->emitError("NYI: ARM-style guard variables for static locals");
1129 return;
1130 }
1131 cir::IntType guardTy =
1132 cir::IntType::get(&getContext(), 64, /*isSigned=*/true);
1133 cir::CIRDataLayout dataLayout(mlirModule);
1134 clang::CharUnits guardAlignment =
1135 clang::CharUnits::fromQuantity(dataLayout.getABITypeAlign(guardTy));
1136 auto guardPtrTy = cir::PointerType::get(guardTy);
1137
1138 // Create the guard variable if we don't already have it.
1139 cir::GlobalOp guard = getOrCreateStaticLocalDeclGuardAddress(
1140 builder, globalOp, varDecl, guardTy, guardAlignment);
1141 if (!guard) {
1142 // Error was already emitted, just restore the terminator and return.
1143 getGlobalOpBlock->push_back(ret);
1144 return;
1145 }
1146
1147 mlir::Value guardPtr = builder.createGetGlobal(guard, /*threadLocal*/ false);
1148
1149 // Test whether the variable has completed initialization.
1150 //
1151 // Itanium C++ ABI 3.3.2:
1152 // The following is pseudo-code showing how these functions can be used:
1153 // if (obj_guard.first_byte == 0) {
1154 // if ( __cxa_guard_acquire (&obj_guard) ) {
1155 // try {
1156 // ... initialize the object ...;
1157 // } catch (...) {
1158 // __cxa_guard_abort (&obj_guard);
1159 // throw;
1160 // }
1161 // ... queue object destructor with __cxa_atexit() ...;
1162 // __cxa_guard_release (&obj_guard);
1163 // }
1164 // }
1165 //
1166 // If threadsafe statics are enabled, but we don't have inline atomics, just
1167 // call __cxa_guard_acquire unconditionally. The "inline" check isn't
1168 // actually inline, and the user might not expect calls to __atomic libcalls.
1169 unsigned maxInlineWidthInBits =
1171
1172 if (!threadsafe || maxInlineWidthInBits) {
1173 // Load the first byte of the guard variable.
1174 auto bytePtrTy = cir::PointerType::get(builder.getSIntNTy(8));
1175 mlir::Value bytePtr = builder.createBitcast(guardPtr, bytePtrTy);
1176 mlir::Value guardLoad = builder.createAlignedLoad(
1177 getGlobalOp.getLoc(), bytePtr, guardAlignment.getAsAlign().value());
1178
1179 // Itanium ABI:
1180 // An implementation supporting thread-safety on multiprocessor
1181 // systems must also guarantee that references to the initialized
1182 // object do not occur before the load of the initialization flag.
1183 //
1184 // In LLVM, we do this by marking the load Acquire.
1185 if (threadsafe) {
1186 auto loadOp = mlir::cast<cir::LoadOp>(guardLoad.getDefiningOp());
1187 loadOp.setMemOrder(cir::MemOrder::Acquire);
1188 loadOp.setSyncScope(cir::SyncScopeKind::System);
1189 }
1190
1191 // For ARM, we should only check the first bit, rather than the entire byte:
1192 //
1193 // ARM C++ ABI 3.2.3.1:
1194 // To support the potential use of initialization guard variables
1195 // as semaphores that are the target of ARM SWP and LDREX/STREX
1196 // synchronizing instructions we define a static initialization
1197 // guard variable to be a 4-byte aligned, 4-byte word with the
1198 // following inline access protocol.
1199 // #define INITIALIZED 1
1200 // if ((obj_guard & INITIALIZED) != INITIALIZED) {
1201 // if (__cxa_guard_acquire(&obj_guard))
1202 // ...
1203 // }
1204 //
1205 // and similarly for ARM64:
1206 //
1207 // ARM64 C++ ABI 3.2.2:
1208 // This ABI instead only specifies the value bit 0 of the static guard
1209 // variable; all other bits are platform defined. Bit 0 shall be 0 when
1210 // the variable is not initialized and 1 when it is.
1211 if (useARMGuardVarABI()) {
1212 globalOp->emitError(
1213 "NYI: ARM-style guard variable check (bit 0 only) for static locals");
1214 return;
1215 }
1216
1217 // Check if the first byte of the guard variable is zero.
1218 auto zero = builder.getConstantInt(
1219 getGlobalOp.getLoc(), mlir::cast<cir::IntType>(guardLoad.getType()), 0);
1220 auto needsInit = builder.createCompare(getGlobalOp.getLoc(),
1221 cir::CmpOpKind::eq, guardLoad, zero);
1222
1223 // Build the guarded initialization inside an if block.
1224 cir::IfOp::create(builder, globalOp.getLoc(), needsInit,
1225 /*withElseRegion=*/false,
1226 [&](mlir::OpBuilder &, mlir::Location) {
1227 emitCXXGuardedInitIf(builder, globalOp, varDecl,
1228 guardPtr, guardPtrTy, threadsafe);
1229 });
1230 } else {
1231 // Threadsafe statics without inline atomics - call __cxa_guard_acquire
1232 // unconditionally without the initial guard byte check.
1233 globalOp->emitError("NYI: guarded init without inline atomics support");
1234 return;
1235 }
1236
1237 // Insert the removed terminator back.
1238 builder.getInsertionBlock()->push_back(ret);
1239}
1240
1241void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
1242 // Static locals are handled separately via guard variables.
1243 if (op.getStaticLocalGuard())
1244 return;
1245
1246 mlir::Region &ctorRegion = op.getCtorRegion();
1247 mlir::Region &dtorRegion = op.getDtorRegion();
1248
1249 if (!ctorRegion.empty() || !dtorRegion.empty()) {
1250 // Build a variable initialization function and move the initialzation code
1251 // in the ctor region over.
1252 cir::FuncOp f = buildCXXGlobalVarDeclInitFunc(op);
1253
1254 // Clear the ctor and dtor region
1255 ctorRegion.getBlocks().clear();
1256 dtorRegion.getBlocks().clear();
1257
1259 dynamicInitializers.push_back(f);
1260 }
1261
1263}
1264
1265template <typename AttributeTy>
1266static llvm::SmallVector<mlir::Attribute>
1267prepareCtorDtorAttrList(mlir::MLIRContext *context,
1268 llvm::ArrayRef<std::pair<std::string, uint32_t>> list) {
1270 for (const auto &[name, priority] : list)
1271 attrs.push_back(AttributeTy::get(context, name, priority));
1272 return attrs;
1273}
1274
1275void LoweringPreparePass::buildGlobalCtorDtorList() {
1276 if (!globalCtorList.empty()) {
1277 llvm::SmallVector<mlir::Attribute> globalCtors =
1279 globalCtorList);
1280
1281 mlirModule->setAttr(cir::CIRDialect::getGlobalCtorsAttrName(),
1282 mlir::ArrayAttr::get(&getContext(), globalCtors));
1283 }
1284
1285 if (!globalDtorList.empty()) {
1286 llvm::SmallVector<mlir::Attribute> globalDtors =
1288 globalDtorList);
1289 mlirModule->setAttr(cir::CIRDialect::getGlobalDtorsAttrName(),
1290 mlir::ArrayAttr::get(&getContext(), globalDtors));
1291 }
1292}
1293
1294void LoweringPreparePass::buildCXXGlobalInitFunc() {
1295 if (dynamicInitializers.empty())
1296 return;
1297
1298 // TODO: handle globals with a user-specified initialzation priority.
1299 // TODO: handle default priority more nicely.
1301
1302 SmallString<256> fnName;
1303 // Include the filename in the symbol name. Including "sub_" matches gcc
1304 // and makes sure these symbols appear lexicographically behind the symbols
1305 // with priority (TBD). Module implementation units behave the same
1306 // way as a non-modular TU with imports.
1307 // TODO: check CXX20ModuleInits
1308 if (astCtx->getCurrentNamedModule() &&
1310 llvm::raw_svector_ostream out(fnName);
1311 std::unique_ptr<clang::MangleContext> mangleCtx(
1312 astCtx->createMangleContext());
1313 cast<clang::ItaniumMangleContext>(*mangleCtx)
1314 .mangleModuleInitializer(astCtx->getCurrentNamedModule(), out);
1315 } else {
1316 fnName += "_GLOBAL__sub_I_";
1317 fnName += getTransformedFileName(mlirModule);
1318 }
1319
1320 CIRBaseBuilderTy builder(getContext());
1321 builder.setInsertionPointToEnd(&mlirModule.getBodyRegion().back());
1322 auto fnType = cir::FuncType::get({}, builder.getVoidTy());
1323 cir::FuncOp f =
1324 buildRuntimeFunction(builder, fnName, mlirModule.getLoc(), fnType,
1325 cir::GlobalLinkageKind::ExternalLinkage);
1326 builder.setInsertionPointToStart(f.addEntryBlock());
1327 for (cir::FuncOp &f : dynamicInitializers)
1328 builder.createCallOp(f.getLoc(), f, {});
1329 // Add the global init function (not the individual ctor functions) to the
1330 // global ctor list.
1331 globalCtorList.emplace_back(fnName,
1332 cir::GlobalCtorAttr::getDefaultPriority());
1333
1334 cir::ReturnOp::create(builder, f.getLoc());
1335}
1336
1338 clang::ASTContext *astCtx,
1339 mlir::Operation *op, mlir::Type eltTy,
1340 mlir::Value arrayAddr, uint64_t arrayLen,
1341 bool isCtor) {
1342 // Generate loop to call into ctor/dtor for every element.
1343 mlir::Location loc = op->getLoc();
1344
1345 // TODO: instead of getting the size from the AST context, create alias for
1346 // PtrDiffTy and unify with CIRGen stuff.
1347 const unsigned sizeTypeSize =
1348 astCtx->getTypeSize(astCtx->getSignedSizeType());
1349 uint64_t endOffset = isCtor ? arrayLen : arrayLen - 1;
1350 mlir::Value endOffsetVal =
1351 builder.getUnsignedInt(loc, endOffset, sizeTypeSize);
1352
1353 auto begin = cir::CastOp::create(builder, loc, eltTy,
1354 cir::CastKind::array_to_ptrdecay, arrayAddr);
1355 mlir::Value end =
1356 cir::PtrStrideOp::create(builder, loc, eltTy, begin, endOffsetVal);
1357 mlir::Value start = isCtor ? begin : end;
1358 mlir::Value stop = isCtor ? end : begin;
1359
1360 mlir::Value tmpAddr = builder.createAlloca(
1361 loc, /*addr type*/ builder.getPointerTo(eltTy),
1362 /*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1));
1363 builder.createStore(loc, start, tmpAddr);
1364
1365 cir::DoWhileOp loop = builder.createDoWhile(
1366 loc,
1367 /*condBuilder=*/
1368 [&](mlir::OpBuilder &b, mlir::Location loc) {
1369 auto currentElement = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
1370 auto cmp = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
1371 currentElement, stop);
1372 builder.createCondition(cmp);
1373 },
1374 /*bodyBuilder=*/
1375 [&](mlir::OpBuilder &b, mlir::Location loc) {
1376 auto currentElement = cir::LoadOp::create(b, loc, eltTy, tmpAddr);
1377
1378 cir::CallOp ctorCall;
1379 op->walk([&](cir::CallOp c) { ctorCall = c; });
1380 assert(ctorCall && "expected ctor call");
1381
1382 // Array elements get constructed in order but destructed in reverse.
1383 mlir::Value stride;
1384 if (isCtor)
1385 stride = builder.getUnsignedInt(loc, 1, sizeTypeSize);
1386 else
1387 stride = builder.getSignedInt(loc, -1, sizeTypeSize);
1388
1389 ctorCall->moveBefore(stride.getDefiningOp());
1390 ctorCall->setOperand(0, currentElement);
1391 auto nextElement = cir::PtrStrideOp::create(builder, loc, eltTy,
1392 currentElement, stride);
1393
1394 // Store the element pointer to the temporary variable
1395 builder.createStore(loc, nextElement, tmpAddr);
1396 builder.createYield(loc);
1397 });
1398
1399 op->replaceAllUsesWith(loop);
1400 op->erase();
1401}
1402
1403void LoweringPreparePass::lowerArrayDtor(cir::ArrayDtor op) {
1404 CIRBaseBuilderTy builder(getContext());
1405 builder.setInsertionPointAfter(op.getOperation());
1406
1407 mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
1408 assert(!cir::MissingFeatures::vlas());
1409 auto arrayLen =
1410 mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
1411 lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
1412 false);
1413}
1414
1415void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
1416 cir::CIRBaseBuilderTy builder(getContext());
1417 builder.setInsertionPointAfter(op.getOperation());
1418
1419 mlir::Type eltTy = op->getRegion(0).getArgument(0).getType();
1420 assert(!cir::MissingFeatures::vlas());
1421 auto arrayLen =
1422 mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
1423 lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen,
1424 true);
1425}
1426
1427void LoweringPreparePass::lowerTrivialCopyCall(cir::CallOp op) {
1428 cir::FuncOp funcOp = getCalledFunction(op);
1429 if (!funcOp)
1430 return;
1431
1432 std::optional<cir::CtorKind> ctorKind = funcOp.getCxxConstructorKind();
1433 if (ctorKind && *ctorKind == cir::CtorKind::Copy &&
1434 funcOp.isCxxTrivialMemberFunction()) {
1435 // Replace the trivial copy constructor call with a `CopyOp`
1436 CIRBaseBuilderTy builder(getContext());
1437 mlir::ValueRange operands = op.getOperands();
1438 mlir::Value dest = operands[0];
1439 mlir::Value src = operands[1];
1440 builder.setInsertionPoint(op);
1441 builder.createCopy(dest, src);
1442 op.erase();
1443 }
1444}
1445
1446void LoweringPreparePass::lowerStoreOfConstAggregate(cir::StoreOp op) {
1447 // Check if the value operand is a cir.const with aggregate type.
1448 auto constOp = op.getValue().getDefiningOp<cir::ConstantOp>();
1449 if (!constOp)
1450 return;
1451
1452 mlir::Type ty = constOp.getType();
1453 if (!mlir::isa<cir::ArrayType, cir::RecordType>(ty))
1454 return;
1455
1456 // Only transform stores to local variables (backed by cir.alloca).
1457 // Stores to other addresses (e.g. base_class_addr) should not be
1458 // transformed as they may be partial initializations.
1459 auto alloca = op.getAddr().getDefiningOp<cir::AllocaOp>();
1460 if (!alloca)
1461 return;
1462
1463 mlir::TypedAttr constant = constOp.getValue();
1464
1465 // OG implements several optimization tiers for constant aggregate
1466 // initialization. For now we always create a global constant + memcpy
1467 // (shouldCreateMemCpyFromGlobal). Future work can add the intermediate
1468 // tiers.
1472
1473 // Get function name from parent cir.func.
1474 auto func = op->getParentOfType<cir::FuncOp>();
1475 if (!func)
1476 return;
1477 llvm::StringRef funcName = func.getSymName();
1478
1479 // Get variable name from the alloca.
1480 llvm::StringRef varName = alloca.getName();
1481
1482 // Build name: __const.<func>.<var>
1483 std::string name = ("__const." + funcName + "." + varName).str();
1484
1485 // Create the global constant.
1486 CIRBaseBuilderTy builder(getContext());
1487
1488 // Use InsertionGuard to create the global at module level.
1489 builder.setInsertionPointToStart(mlirModule.getBody());
1490
1491 // If a global with this name already exists (e.g. CIRGen materializes
1492 // constexpr locals as globals when their address is taken), reuse it.
1493 if (!mlir::SymbolTable::lookupSymbolIn(
1494 mlirModule, mlir::StringAttr::get(&getContext(), name))) {
1495 auto gv = cir::GlobalOp::create(builder, op.getLoc(), name, ty,
1496 /*isConstant=*/true,
1497 cir::GlobalLinkageKind::PrivateLinkage);
1498 mlir::SymbolTable::setSymbolVisibility(
1499 gv, mlir::SymbolTable::Visibility::Private);
1500 gv.setInitialValueAttr(constant);
1501 }
1502
1503 // Now replace the store with get_global + copy.
1504 builder.setInsertionPoint(op);
1505
1506 auto ptrTy = cir::PointerType::get(ty);
1507 mlir::Value globalPtr =
1508 cir::GetGlobalOp::create(builder, op.getLoc(), ptrTy, name);
1509
1510 // Replace store with copy.
1511 builder.createCopy(op.getAddr(), globalPtr);
1512
1513 // Erase the original store.
1514 op.erase();
1515
1516 // Erase the cir.const if it has no remaining users.
1517 if (constOp.use_empty())
1518 constOp.erase();
1519}
1520
1521void LoweringPreparePass::runOnOp(mlir::Operation *op) {
1522 if (auto arrayCtor = dyn_cast<cir::ArrayCtor>(op)) {
1523 lowerArrayCtor(arrayCtor);
1524 } else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op)) {
1525 lowerArrayDtor(arrayDtor);
1526 } else if (auto cast = mlir::dyn_cast<cir::CastOp>(op)) {
1527 lowerCastOp(cast);
1528 } else if (auto complexDiv = mlir::dyn_cast<cir::ComplexDivOp>(op)) {
1529 lowerComplexDivOp(complexDiv);
1530 } else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op)) {
1531 lowerComplexMulOp(complexMul);
1532 } else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op)) {
1533 lowerGlobalOp(glob);
1534 } else if (auto getGlobal = mlir::dyn_cast<cir::GetGlobalOp>(op)) {
1535 // Handle static local variables with guard variables.
1536 // Only process GetGlobalOps inside function bodies, not in GlobalOp
1537 // regions.
1538 if (getGlobal.getStaticLocal() &&
1539 getGlobal->getParentOfType<cir::FuncOp>()) {
1540 auto globalOp = mlir::dyn_cast_or_null<cir::GlobalOp>(
1541 mlir::SymbolTable::lookupNearestSymbolFrom(getGlobal,
1542 getGlobal.getNameAttr()));
1543 // Only process if the GlobalOp has static_local and the ctor region is
1544 // not empty. After handleStaticLocal processes a static local, the ctor
1545 // region is cleared. GetGlobalOps that were spliced from the ctor region
1546 // into the function will be skipped on subsequent iterations.
1547 if (globalOp && globalOp.getStaticLocalGuard() &&
1548 !globalOp.getCtorRegion().empty())
1549 handleStaticLocal(globalOp, getGlobal);
1550 }
1551 } else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op)) {
1552 lowerUnaryOp(unary);
1553 } else if (auto callOp = dyn_cast<cir::CallOp>(op)) {
1554 lowerTrivialCopyCall(callOp);
1555 } else if (auto storeOp = dyn_cast<cir::StoreOp>(op)) {
1556 lowerStoreOfConstAggregate(storeOp);
1557 } else if (auto fnOp = dyn_cast<cir::FuncOp>(op)) {
1558 if (auto globalCtor = fnOp.getGlobalCtorPriority())
1559 globalCtorList.emplace_back(fnOp.getName(), globalCtor.value());
1560 else if (auto globalDtor = fnOp.getGlobalDtorPriority())
1561 globalDtorList.emplace_back(fnOp.getName(), globalDtor.value());
1562 }
1563}
1564
1565void LoweringPreparePass::runOnOperation() {
1566 mlir::Operation *op = getOperation();
1567 if (isa<::mlir::ModuleOp>(op))
1568 mlirModule = cast<::mlir::ModuleOp>(op);
1569
1570 llvm::SmallVector<mlir::Operation *> opsToTransform;
1571
1572 op->walk([&](mlir::Operation *op) {
1573 if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
1574 cir::ComplexMulOp, cir::ComplexDivOp, cir::DynamicCastOp,
1575 cir::FuncOp, cir::CallOp, cir::GetGlobalOp, cir::GlobalOp,
1576 cir::StoreOp, cir::UnaryOp>(op))
1577 opsToTransform.push_back(op);
1578 });
1579
1580 for (mlir::Operation *o : opsToTransform)
1581 runOnOp(o);
1582
1583 buildCXXGlobalInitFunc();
1584 buildGlobalCtorDtorList();
1585}
1586
1587std::unique_ptr<Pass> mlir::createLoweringPreparePass() {
1588 return std::make_unique<LoweringPreparePass>();
1589}
1590
1591std::unique_ptr<Pass>
1593 auto pass = std::make_unique<LoweringPreparePass>();
1594 pass->setASTContext(astCtx);
1595 return std::move(pass);
1596}
Defines the clang::ASTContext interface.
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 void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder, clang::ASTContext *astCtx, mlir::Operation *op, mlir::Type eltTy, mlir::Value arrayAddr, uint64_t arrayLen, bool isCtor)
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 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 SmallString< 128 > getTransformedFileName(mlir::ModuleOp mlirModule)
static mlir::Value lowerComplexToComplexCast(mlir::MLIRContext &ctx, cir::CastOp op, cir::CastKind scalarCastKind)
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 cir::FuncOp getCalledFunction(cir::CallOp callOp)
Return the FuncOp called by callOp.
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.
Defines various enumerations that describe declaration and type specifiers.
Defines the TargetCXXABI class, which abstracts details of the C++ ABI that we're targeting.
__device__ __2f16 b
__device__ __2f16 float c
mlir::Value createDiv(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
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::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)
mlir::Value createAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, OverflowBehavior ob=OverflowBehavior::None)
cir::PointerType getPointerTo(mlir::Type ty)
mlir::Value createComplexImag(mlir::Location loc, mlir::Value operand)
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::CopyOp createCopy(mlir::Value dst, mlir::Value src, bool isVolatile=false)
Create a copy with inferred length.
mlir::Value getSignedInt(mlir::Location loc, int64_t val, unsigned numBits)
mlir::Value createBitcast(mlir::Value src, mlir::Type newTy)
mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global, bool threadLocal=false)
cir::CmpOp createCompare(mlir::Location loc, cir::CmpOpKind kind, mlir::Value lhs, mlir::Value rhs)
mlir::IntegerAttr getAlignmentAttr(clang::CharUnits alignment)
mlir::Value createMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, OverflowBehavior ob=OverflowBehavior::None)
cir::ConstantOp getConstantInt(mlir::Location loc, mlir::Type ty, int64_t value)
mlir::Value createComplexCreate(mlir::Location loc, mlir::Value real, mlir::Value imag)
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::StoreOp createStore(mlir::Location loc, mlir::Value val, mlir::Value dst, bool isVolatile=false, mlir::IntegerAttr align={}, cir::SyncScopeKindAttr scope={}, cir::MemOrderAttr order={})
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 createUnaryOp(mlir::Location loc, cir::UnaryOpKind kind, mlir::Value operand)
mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType, mlir::Type type, llvm::StringRef name, mlir::IntegerAttr alignment, mlir::Value dynAllocSize)
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:226
MangleContext * createMangleContext(const TargetInfo *T=nullptr)
If T is null pointer, assume the target in ASTContext.
const LangOptions & getLangOpts() const
Definition ASTContext.h:951
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
const TargetInfo & getTargetInfo() const
Definition ASTContext.h:916
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.
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
static CharUnits fromQuantity(QuantityType Quantity)
fromQuantity - Construct a CharUnits quantity from a raw integer type.
Definition CharUnits.h:63
bool isModuleImplementation() const
Is this a module implementation.
Definition Module.h:664
Exposes information about the current target.
Definition TargetInfo.h:226
unsigned getMaxAtomicInlineWidth() const
Return the maximum width lock-free atomic operation which can be inlined given the supported features...
Definition TargetInfo.h:858
const llvm::fltSemantics & getDoubleFormat() const
Definition TargetInfo.h:803
const llvm::fltSemantics & getHalfFormat() const
Definition TargetInfo.h:788
const llvm::fltSemantics & getBFloat16Format() const
Definition TargetInfo.h:798
const llvm::fltSemantics & getLongDoubleFormat() const
Definition TargetInfo.h:809
const llvm::fltSemantics & getFloatFormat() const
Definition TargetInfo.h:793
const llvm::fltSemantics & getFloat128Format() const
Definition TargetInfo.h:817
Defines the clang::TargetInfo interface.
const internal::VariadicDynCastAllOfMatcher< Decl, VarDecl > varDecl
Matches variable declarations.
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:212
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
unsigned int uint32_t
std::unique_ptr< Pass > createLoweringPreparePass()
static bool opGlobalThreadLocal()
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()