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