clang 24.0.0git
CXXABILowering.cpp
Go to the documentation of this file.
1//==- CXXABILowering.cpp - lower C++ operations to target-specific ABI form -=//
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 "RecordTypeConverter.h"
12
13#include "mlir/Dialect/OpenACC/OpenACCOpsDialect.h.inc"
14#include "mlir/Dialect/OpenMP/OpenMPOpsDialect.h.inc"
15#include "mlir/IR/PatternMatch.h"
16#include "mlir/Interfaces/DataLayoutInterfaces.h"
17#include "mlir/Pass/Pass.h"
18#include "mlir/Transforms/DialectConversion.h"
27
28#include "llvm/ADT/TypeSwitch.h"
29
30using namespace mlir;
31using namespace cir;
32
33namespace mlir {
34#define GEN_PASS_DEF_CXXABILOWERING
35#include "clang/CIR/Dialect/Passes.h.inc"
36} // namespace mlir
37
38namespace {
39// Check an attribute for legality. An attribute is only currently potentially
40// illegal if it contains a type, member pointers are our source of illegality
41// in regards to attributes.
42bool isCXXABIAttributeLegal(const mlir::TypeConverter &tc,
43 mlir::Attribute attr) {
44 // If we don't have an attribute, it can't have a type!
45 if (!attr)
46 return true;
47
48 // None of the OpenACC/OMP attributes contain a type of concern, so we can
49 // just treat them as legal.
50 if (isa<mlir::acc::OpenACCDialect, mlir::omp::OpenMPDialect>(
51 attr.getDialect()))
52 return true;
53
54 // These attributes either don't contain a type, or don't contain a type that
55 // can have a data member/method.
56 if (isa<mlir::DenseArrayAttr, mlir::FloatAttr, mlir::UnitAttr,
57 mlir::StringAttr, mlir::IntegerAttr, mlir::SymbolRefAttr,
58 cir::AnnotationAttr>(attr))
59 return true;
60
61 // Tablegen'ed always-legal attributes:
62 if (isa<
64#include "clang/CIR/Dialect/IR/CIRLowering.inc"
66 >(attr))
67 return true;
68
69 // Data Member and method are ALWAYS illegal.
70 if (isa<cir::DataMemberAttr, cir::DataMemberOffsetAttr, cir::MethodAttr>(
71 attr))
72 return false;
73
74 return llvm::TypeSwitch<mlir::Attribute, bool>(attr)
75 // These attributes just have a type, so they are legal if their type is.
76 .Case<cir::ZeroAttr>(
77 [&tc](cir::ZeroAttr za) { return tc.isLegal(za.getType()); })
78 .Case<cir::PoisonAttr>(
79 [&tc](cir::PoisonAttr pa) { return tc.isLegal(pa.getType()); })
80 .Case<cir::UndefAttr>(
81 [&tc](cir::UndefAttr uda) { return tc.isLegal(uda.getType()); })
82 .Case<mlir::TypeAttr>(
83 [&tc](mlir::TypeAttr ta) { return tc.isLegal(ta.getValue()); })
84 .Case<cir::ConstPtrAttr>(
85 [&tc](cir::ConstPtrAttr cpa) { return tc.isLegal(cpa.getType()); })
86 .Case<cir::CXXCtorAttr>(
87 [&tc](cir::CXXCtorAttr ca) { return tc.isLegal(ca.getType()); })
88 .Case<cir::CXXDtorAttr>(
89 [&tc](cir::CXXDtorAttr da) { return tc.isLegal(da.getType()); })
90 .Case<cir::CXXAssignAttr>(
91 [&tc](cir::CXXAssignAttr aa) { return tc.isLegal(aa.getType()); })
92
93 // Collection attributes are legal if ALL of the attributes in them are
94 // also legal.
95 .Case<mlir::ArrayAttr>([&tc](mlir::ArrayAttr array) {
96 return llvm::all_of(array.getValue(), [&tc](mlir::Attribute attr) {
97 return isCXXABIAttributeLegal(tc, attr);
98 });
99 })
100 .Case<mlir::DictionaryAttr>([&tc](mlir::DictionaryAttr dict) {
101 return llvm::all_of(dict.getValue(), [&tc](mlir::NamedAttribute na) {
102 return isCXXABIAttributeLegal(tc, na.getValue());
103 });
104 })
105 // These attributes have sub-attributes that we should check for legality.
106 .Case<cir::ConstArrayAttr>([&tc](cir::ConstArrayAttr array) {
107 return tc.isLegal(array.getType()) &&
108 isCXXABIAttributeLegal(tc, array.getElts());
109 })
110 .Case<cir::GlobalViewAttr>([&tc](cir::GlobalViewAttr gva) {
111 return tc.isLegal(gva.getType()) &&
112 isCXXABIAttributeLegal(tc, gva.getIndices());
113 })
114 .Case<cir::GlobalOffsetAttr>([&tc](cir::GlobalOffsetAttr goa) {
115 return tc.isLegal(goa.getType());
116 })
117 .Case<cir::VTableAttr>([&tc](cir::VTableAttr vta) {
118 return tc.isLegal(vta.getType()) &&
119 isCXXABIAttributeLegal(tc, vta.getData());
120 })
121 .Case<cir::TypeInfoAttr>([&tc](cir::TypeInfoAttr tia) {
122 return tc.isLegal(tia.getType()) &&
123 isCXXABIAttributeLegal(tc, tia.getData());
124 })
125 .Case<cir::DynamicCastInfoAttr>([&tc](cir::DynamicCastInfoAttr dcia) {
126 return isCXXABIAttributeLegal(tc, dcia.getSrcRtti()) &&
127 isCXXABIAttributeLegal(tc, dcia.getDestRtti()) &&
128 isCXXABIAttributeLegal(tc, dcia.getRuntimeFunc()) &&
129 isCXXABIAttributeLegal(tc, dcia.getBadCastFunc());
130 })
131 .Case<cir::ConstRecordAttr>([&tc](cir::ConstRecordAttr cra) {
132 return tc.isLegal(cra.getType()) &&
133 isCXXABIAttributeLegal(tc, cra.getMembers());
134 })
135 // We did an audit of all of our attributes (both in OpenACC and CIR), so
136 // it shouldn't be dangerous to consider everything we haven't considered
137 // 'illegal'. Any 'new' attributes will end up asserting in
138 // 'rewriteAttribute' to make sure we consider them here. Otherwise, we
139 // wouldn't discover a problematic new attribute until it contains a
140 // member/method.
141 .Default(false);
142}
143
144mlir::Attribute rewriteAttribute(const mlir::TypeConverter &tc,
145 mlir::MLIRContext *ctx, mlir::Attribute attr) {
146 // If the attribute is legal, there is no reason to rewrite it. This also
147 // filters out 'null' attributes.
148 if (isCXXABIAttributeLegal(tc, attr))
149 return attr;
150
151 // This switch needs to be kept in sync with the potentially-legal type switch
152 // from isCXXABIAttributeLegal. IF we miss any, this will end up causing
153 // verification/transformation issues later, often in the form of
154 // unrealized-conversion-casts.
155
156 return llvm::TypeSwitch<mlir::Attribute, mlir::Attribute>(attr)
157 // These attributes just have a type, so convert just the type.
158 .Case<cir::ZeroAttr>([&tc](cir::ZeroAttr za) {
159 return cir::ZeroAttr::get(tc.convertType(za.getType()));
160 })
161 .Case<cir::PoisonAttr>([&tc](cir::PoisonAttr pa) {
162 return cir::PoisonAttr::get(tc.convertType(pa.getType()));
163 })
164 .Case<cir::UndefAttr>([&tc](cir::UndefAttr uda) {
165 return cir::UndefAttr::get(tc.convertType(uda.getType()));
166 })
167 .Case<mlir::TypeAttr>([&tc](mlir::TypeAttr ta) {
168 return mlir::TypeAttr::get(tc.convertType(ta.getValue()));
169 })
170 .Case<cir::ConstPtrAttr>([&tc](cir::ConstPtrAttr cpa) {
171 return cir::ConstPtrAttr::get(tc.convertType(cpa.getType()),
172 cpa.getValue());
173 })
174 .Case<cir::CXXCtorAttr>([&tc](cir::CXXCtorAttr ca) {
175 return cir::CXXCtorAttr::get(tc.convertType(ca.getType()),
176 ca.getCtorKind(), ca.getIsTrivial());
177 })
178 .Case<cir::CXXDtorAttr>([&tc](cir::CXXDtorAttr da) {
179 return cir::CXXDtorAttr::get(tc.convertType(da.getType()),
180 da.getIsTrivial());
181 })
182 .Case<cir::CXXAssignAttr>([&tc](cir::CXXAssignAttr aa) {
183 return cir::CXXAssignAttr::get(tc.convertType(aa.getType()),
184 aa.getAssignKind(), aa.getIsTrivial());
185 })
186 // Collection attributes need to transform all of the attributes inside of
187 // them.
188 .Case<mlir::ArrayAttr>([&tc, ctx](mlir::ArrayAttr array) {
190 for (mlir::Attribute a : array.getValue())
191 elts.push_back(rewriteAttribute(tc, ctx, a));
192 return mlir::ArrayAttr::get(ctx, elts);
193 })
194 .Case<mlir::DictionaryAttr>([&tc, ctx](mlir::DictionaryAttr dict) {
196 for (mlir::NamedAttribute na : dict.getValue())
197 elts.emplace_back(na.getName(),
198 rewriteAttribute(tc, ctx, na.getValue()));
199
200 return mlir::DictionaryAttr::get(ctx, elts);
201 })
202 // These attributes have sub-attributes that need converting too.
203 .Case<cir::ConstArrayAttr>([&tc, ctx](cir::ConstArrayAttr array) {
204 return cir::ConstArrayAttr::get(
205 ctx, tc.convertType(array.getType()),
206 rewriteAttribute(tc, ctx, array.getElts()),
207 array.getTrailingZerosNum());
208 })
209 .Case<cir::GlobalViewAttr>([&tc, ctx](cir::GlobalViewAttr gva) {
210 return cir::GlobalViewAttr::get(
211 tc.convertType(gva.getType()), gva.getSymbol(),
212 mlir::cast<mlir::ArrayAttr>(
213 rewriteAttribute(tc, ctx, gva.getIndices())),
214 gva.getAddressPoint());
215 })
216 .Case<cir::GlobalOffsetAttr>([&tc](cir::GlobalOffsetAttr goa) {
217 return cir::GlobalOffsetAttr::get(tc.convertType(goa.getType()),
218 goa.getSymbol(), goa.getOffset());
219 })
220 .Case<cir::VTableAttr>([&tc, ctx](cir::VTableAttr vta) {
221 return cir::VTableAttr::get(
222 tc.convertType(vta.getType()),
223 mlir::cast<mlir::ArrayAttr>(
224 rewriteAttribute(tc, ctx, vta.getData())));
225 })
226 .Case<cir::TypeInfoAttr>([&tc, ctx](cir::TypeInfoAttr tia) {
227 return cir::TypeInfoAttr::get(
228 tc.convertType(tia.getType()),
229 mlir::cast<mlir::ArrayAttr>(
230 rewriteAttribute(tc, ctx, tia.getData())));
231 })
232 .Case<cir::DynamicCastInfoAttr>([&tc,
233 ctx](cir::DynamicCastInfoAttr dcia) {
234 return cir::DynamicCastInfoAttr::get(
235 mlir::cast<cir::GlobalViewAttr>(
236 rewriteAttribute(tc, ctx, dcia.getSrcRtti())),
237 mlir::cast<cir::GlobalViewAttr>(
238 rewriteAttribute(tc, ctx, dcia.getDestRtti())),
239 dcia.getRuntimeFunc(), dcia.getBadCastFunc(), dcia.getOffsetHint());
240 })
241 .Case<cir::ConstRecordAttr>([&tc, ctx](cir::ConstRecordAttr cra) {
242 return cir::ConstRecordAttr::get(
243 ctx, tc.convertType(cra.getType()),
244 mlir::cast<mlir::ArrayAttr>(
245 rewriteAttribute(tc, ctx, cra.getMembers())));
246 })
247 .DefaultUnreachable("unrewritten illegal attribute kind");
248}
249
250bool areCXXABIInherentAttrsLegal(mlir::Operation *op,
251 const mlir::TypeConverter &typeConverter) {
252 bool legal = true;
253 op->getName().walkInherentAttrs(
254 op, [&](llvm::StringRef, mlir::Attribute &attr) {
255 legal &= isCXXABIAttributeLegal(typeConverter, attr);
256 });
257 return legal;
258}
259
260#define GET_ABI_LOWERING_PATTERNS
261#include "clang/CIR/Dialect/IR/CIRLowering.inc"
262#undef GET_ABI_LOWERING_PATTERNS
263
264struct CXXABILoweringPass
265 : public impl::CXXABILoweringBase<CXXABILoweringPass> {
266 CXXABILoweringPass() = default;
267 void runOnOperation() override;
268};
269
270/// A generic ABI lowering rewrite pattern. This conversion pattern matches any
271/// CIR dialect operations with at least one operand or result of an
272/// ABI-dependent type. This conversion pattern rewrites the matched operation
273/// by replacing all its ABI-dependent operands and results with their
274/// lowered counterparts.
275class CIRGenericCXXABILoweringPattern : public mlir::ConversionPattern {
276public:
277 CIRGenericCXXABILoweringPattern(mlir::MLIRContext *context,
278 const mlir::TypeConverter &typeConverter)
279 : mlir::ConversionPattern(typeConverter, MatchAnyOpTypeTag(),
280 /*benefit=*/1, context) {}
281
282 mlir::LogicalResult
283 matchAndRewrite(mlir::Operation *op, llvm::ArrayRef<mlir::Value> operands,
284 mlir::ConversionPatternRewriter &rewriter) const override {
285 // Do not match on operations that have dedicated ABI lowering rewrite rules
286 if (llvm::isa<cir::AllocaOp, cir::BaseDataMemberOp, cir::BaseMethodOp,
287 cir::CastOp, cir::CmpOp, cir::ConstantOp, cir::DeleteArrayOp,
288 cir::DerivedDataMemberOp, cir::DerivedMethodOp, cir::FuncOp,
289 cir::GetMethodOp, cir::GetRuntimeMemberOp, cir::GlobalOp>(op))
290 return mlir::failure();
291
292 const mlir::TypeConverter *typeConverter = getTypeConverter();
293 assert(typeConverter &&
294 "CIRGenericCXXABILoweringPattern requires a type converter");
295 bool operandsAndResultsLegal = typeConverter->isLegal(op);
296 bool regionsLegal =
297 std::all_of(op->getRegions().begin(), op->getRegions().end(),
298 [typeConverter](mlir::Region &region) {
299 return typeConverter->isLegal(&region);
300 });
301 bool attrsLegal = areCXXABIInherentAttrsLegal(op, *typeConverter);
302
303 if (operandsAndResultsLegal && regionsLegal && attrsLegal) {
304 // The operation does not have any CXXABI-dependent operands or results,
305 // the match fails.
306 return mlir::failure();
307 }
308
309 mlir::OperationState loweredOpState(op->getLoc(), op->getName());
310 loweredOpState.addOperands(operands);
311 loweredOpState.addSuccessors(op->getSuccessors());
312
313 // Lower inherent attributes while preserving auxiliary metadata verbatim.
314 loweredOpState.propertiesAttr = op->getPropertiesAsAttribute();
315 loweredOpState.addAttributes(op->getDiscardableAttrDictionary().getValue());
316
317 // Lower all result types
318 llvm::SmallVector<mlir::Type> loweredResultTypes;
319 loweredResultTypes.reserve(op->getNumResults());
320 for (mlir::Type result : op->getResultTypes())
321 loweredResultTypes.push_back(typeConverter->convertType(result));
322 loweredOpState.addTypes(loweredResultTypes);
323
324 // Lower all regions
325 for (mlir::Region &region : op->getRegions()) {
326 mlir::Region *loweredRegion = loweredOpState.addRegion();
327 rewriter.inlineRegionBefore(region, *loweredRegion, loweredRegion->end());
328 if (mlir::failed(
329 rewriter.convertRegionTypes(loweredRegion, *getTypeConverter())))
330 return mlir::failure();
331 }
332
333 // Clone the operation with lowered operand types and result types
334 mlir::Operation *loweredOp = rewriter.create(loweredOpState);
335 loweredOp->getName().walkInherentAttrs(
336 loweredOp, [&](llvm::StringRef, mlir::Attribute &attr) {
337 attr = rewriteAttribute(*typeConverter, op->getContext(), attr);
338 });
339
340 rewriter.replaceOp(op, loweredOp);
341 return mlir::success();
342 }
343};
344
345} // namespace
346
347mlir::LogicalResult CIRAllocaOpABILowering::matchAndRewrite(
348 cir::AllocaOp op, OpAdaptor adaptor,
349 mlir::ConversionPatternRewriter &rewriter) const {
350 mlir::Type allocaPtrTy = op.getType();
351 mlir::Type loweredAllocaPtrTy = getTypeConverter()->convertType(allocaPtrTy);
352
353 cir::AllocaOp loweredOp = cir::AllocaOp::create(
354 rewriter, op.getLoc(), loweredAllocaPtrTy, op.getName(),
355 op.getAlignmentAttr(), /*dynAllocSize=*/adaptor.getDynAllocSize());
356 loweredOp.setInit(op.getInit());
357 loweredOp.setConstant(op.getConstant());
358 loweredOp.setAnnotationsAttr(op.getAnnotationsAttr());
359
360 rewriter.replaceOp(op, loweredOp);
361 return mlir::success();
362}
363
364mlir::LogicalResult CIRCastOpABILowering::matchAndRewrite(
365 cir::CastOp op, OpAdaptor adaptor,
366 mlir::ConversionPatternRewriter &rewriter) const {
367 mlir::Type srcTy = op.getSrc().getType();
368
369 if (mlir::isa<cir::DataMemberType, cir::MethodType>(srcTy)) {
370 switch (op.getKind()) {
371 case cir::CastKind::bitcast: {
372 mlir::Type destTy = getTypeConverter()->convertType(op.getType());
373 mlir::Value loweredResult;
374 if (mlir::isa<cir::DataMemberType>(srcTy))
375 loweredResult = lowerModule->getCXXABI().lowerDataMemberBitcast(
376 op, destTy, adaptor.getSrc(), rewriter);
377 else
378 loweredResult = lowerModule->getCXXABI().lowerMethodBitcast(
379 op, destTy, adaptor.getSrc(), rewriter);
380 rewriter.replaceOp(op, loweredResult);
381 return mlir::success();
382 }
383 case cir::CastKind::member_ptr_to_bool: {
384 mlir::Value loweredResult;
385 if (mlir::isa<cir::DataMemberType>(srcTy))
386 loweredResult = lowerModule->getCXXABI().lowerDataMemberToBoolCast(
387 op, adaptor.getSrc(), rewriter);
388 else
389 loweredResult = lowerModule->getCXXABI().lowerMethodToBoolCast(
390 op, adaptor.getSrc(), rewriter);
391 rewriter.replaceOp(op, loweredResult);
392 return mlir::success();
393 }
394 default:
395 break;
396 }
397 }
398
399 mlir::Value loweredResult = cir::CastOp::create(
400 rewriter, op.getLoc(), getTypeConverter()->convertType(op.getType()),
401 adaptor.getKind(), adaptor.getSrc());
402 rewriter.replaceOp(op, loweredResult);
403 return mlir::success();
404}
405
406// Helper function to lower a value for things like an initializer.
407static mlir::TypedAttr lowerInitialValue(const LowerModule *lowerModule,
408 const mlir::DataLayout &layout,
409 const mlir::TypeConverter &tc,
410 mlir::Type ty,
411 mlir::Attribute initVal) {
412 if (mlir::isa_and_present<cir::UndefAttr>(initVal))
413 return cir::UndefAttr::get(tc.convertType(ty));
414
415 if (mlir::isa_and_present<cir::PoisonAttr>(initVal))
416 return cir::PoisonAttr::get(tc.convertType(ty));
417
418 if (mlir::isa<cir::DataMemberType>(ty)) {
419 // Members without a CIR field index (e.g. no_unique_address empty fields)
420 // are represented by an explicit byte offset instead of a field path.
421 if (auto offsetVal =
422 mlir::dyn_cast_if_present<cir::DataMemberOffsetAttr>(initVal))
423 return lowerModule->getCXXABI().lowerDataMemberOffsetConstant(offsetVal,
424 layout, tc);
425 auto dataMemberVal = mlir::cast_if_present<cir::DataMemberAttr>(initVal);
426 return lowerModule->getCXXABI().lowerDataMemberConstant(dataMemberVal,
427 layout, tc);
428 }
429 if (mlir::isa<cir::MethodType>(ty)) {
430 auto methodVal = mlir::cast_if_present<cir::MethodAttr>(initVal);
431 return lowerModule->getCXXABI().lowerMethodConstant(methodVal, layout, tc);
432 }
433
434 if (auto arrTy = mlir::dyn_cast<cir::ArrayType>(ty)) {
435 auto loweredArrTy = mlir::cast<cir::ArrayType>(tc.convertType(arrTy));
436
437 if (!initVal)
438 return {};
439
440 if (auto zeroVal = mlir::dyn_cast_if_present<cir::ZeroAttr>(initVal))
441 return cir::ZeroAttr::get(loweredArrTy);
442
443 auto arrayVal = mlir::cast<cir::ConstArrayAttr>(initVal);
444
445 // String-literal arrays store their bytes as a StringAttr in `elts`. The
446 // backing i8 element type is never rewritten by the CXX ABI type
447 // converter, so the attribute is already legal and can be passed through
448 // unchanged.
449 if (mlir::isa<mlir::StringAttr>(arrayVal.getElts())) {
450 assert(loweredArrTy == arrTy &&
451 "string-literal array type should not change under CXX ABI");
452 return arrayVal;
453 }
454
455 auto arrayElts = mlir::cast<ArrayAttr>(arrayVal.getElts());
456 SmallVector<mlir::Attribute> loweredElements;
457 loweredElements.reserve(arrTy.getSize());
458 for (const mlir::Attribute &attr : arrayElts) {
459 auto typedAttr = cast<mlir::TypedAttr>(attr);
460 loweredElements.push_back(lowerInitialValue(
461 lowerModule, layout, tc, typedAttr.getType(), typedAttr));
462 }
463
464 return cir::ConstArrayAttr::get(
465 loweredArrTy, mlir::ArrayAttr::get(ty.getContext(), loweredElements),
466 arrayVal.getTrailingZerosNum());
467 }
468
469 if (auto recordTy = mlir::dyn_cast<cir::RecordType>(ty)) {
470 auto convertedTy =
471 mlir::dyn_cast<cir::RecordType>(tc.convertType(recordTy));
472 if (!convertedTy)
473 return {};
474
475 if (auto recVal = mlir::dyn_cast_if_present<cir::ZeroAttr>(initVal))
476 return cir::ZeroAttr::get(convertedTy);
477
478 if (auto recVal =
479 mlir::dyn_cast_if_present<cir::ConstRecordAttr>(initVal)) {
480 auto recordMembers = mlir::cast<ArrayAttr>(recVal.getMembers());
481
482 SmallVector<mlir::Attribute> loweredMembers;
483 loweredMembers.reserve(recordMembers.size());
484
485 for (const mlir::Attribute &attr : recordMembers) {
486 auto typedAttr = cast<mlir::TypedAttr>(attr);
487 loweredMembers.push_back(lowerInitialValue(
488 lowerModule, layout, tc, typedAttr.getType(), typedAttr));
489 }
490
491 return cir::ConstRecordAttr::get(
492 convertedTy, mlir::ArrayAttr::get(ty.getContext(), loweredMembers));
493 }
494
495 assert(!initVal && "Record init val type not handled");
496 return {};
497 }
498
499 // Pointers can contain record types, which can change.
500 if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(ty)) {
501 auto convertedTy = mlir::cast<cir::PointerType>(tc.convertType(ptrTy));
502 // pointers don't change other than their types.
503
504 if (auto gva = mlir::dyn_cast_if_present<cir::GlobalViewAttr>(initVal))
505 return cir::GlobalViewAttr::get(convertedTy, gva.getSymbol(),
506 gva.getIndices(), gva.getAddressPoint());
507
508 if (auto goa = mlir::dyn_cast_if_present<cir::GlobalOffsetAttr>(initVal))
509 return cir::GlobalOffsetAttr::get(convertedTy, goa.getSymbol(),
510 goa.getOffset());
511
512 if (auto blockAddr =
513 mlir::dyn_cast_if_present<cir::BlockAddrInfoAttr>(initVal)) {
514 assert(convertedTy == ptrTy && "BlockAddrInfo type should not change");
515 return blockAddr;
516 }
517
518 auto constPtr = mlir::cast_if_present<cir::ConstPtrAttr>(initVal);
519 if (!constPtr)
520 return {};
521 return cir::ConstPtrAttr::get(convertedTy, constPtr.getValue());
522 }
523
524 assert(ty == tc.convertType(ty) &&
525 "cir.global or constant operand is not an CXXABI-dependent type");
526
527 // Every other type can be left alone.
528 return cast<mlir::TypedAttr>(initVal);
529}
530
531mlir::LogicalResult CIRConstantOpABILowering::matchAndRewrite(
532 cir::ConstantOp op, OpAdaptor adaptor,
533 mlir::ConversionPatternRewriter &rewriter) const {
534
535 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
536 mlir::TypedAttr newValue = lowerInitialValue(
537 lowerModule, layout, *getTypeConverter(), op.getType(), op.getValue());
538 rewriter.replaceOpWithNewOp<ConstantOp>(op, newValue);
539 return mlir::success();
540}
541
542mlir::LogicalResult CIRCmpOpABILowering::matchAndRewrite(
543 cir::CmpOp op, OpAdaptor adaptor,
544 mlir::ConversionPatternRewriter &rewriter) const {
545 mlir::Type type = op.getLhs().getType();
546
547 mlir::Value loweredResult;
548 if (mlir::isa<cir::DataMemberType>(type))
549 loweredResult = lowerModule->getCXXABI().lowerDataMemberCmp(
550 op, adaptor.getLhs(), adaptor.getRhs(), rewriter);
551 else if (mlir::isa<cir::MethodType>(type))
552 loweredResult = lowerModule->getCXXABI().lowerMethodCmp(
553 op, adaptor.getLhs(), adaptor.getRhs(), rewriter);
554 else
555 loweredResult = cir::CmpOp::create(
556 rewriter, op.getLoc(), getTypeConverter()->convertType(op.getType()),
557 adaptor.getKind(), adaptor.getLhs(), adaptor.getRhs());
558
559 rewriter.replaceOp(op, loweredResult);
560 return mlir::success();
561}
562
563mlir::LogicalResult CIRFuncOpABILowering::matchAndRewrite(
564 cir::FuncOp op, OpAdaptor adaptor,
565 mlir::ConversionPatternRewriter &rewriter) const {
566 cir::FuncType opFuncType = op.getFunctionType();
567 mlir::TypeConverter::SignatureConversion signatureConversion(
568 opFuncType.getNumInputs());
569
570 for (const auto &[i, argType] : llvm::enumerate(opFuncType.getInputs())) {
571 mlir::Type loweredArgType = getTypeConverter()->convertType(argType);
572 if (!loweredArgType)
573 return mlir::failure();
574 signatureConversion.addInputs(i, loweredArgType);
575 }
576
577 mlir::Type loweredResultType =
578 getTypeConverter()->convertType(opFuncType.getReturnType());
579 if (!loweredResultType)
580 return mlir::failure();
581
582 auto loweredFuncType =
583 cir::FuncType::get(signatureConversion.getConvertedTypes(),
584 loweredResultType, /*isVarArg=*/opFuncType.isVarArg());
585
586 // Create a new cir.func operation for the CXXABI-lowered function.
587 cir::FuncOp loweredFuncOp = rewriter.cloneWithoutRegions(op);
588 loweredFuncOp.setFunctionType(loweredFuncType);
589
590 loweredFuncOp->getName().walkInherentAttrs(
591 loweredFuncOp, [&](llvm::StringRef, mlir::Attribute &attr) {
592 attr = rewriteAttribute(*getTypeConverter(), op->getContext(), attr);
593 });
594 rewriter.inlineRegionBefore(op.getBody(), loweredFuncOp.getBody(),
595 loweredFuncOp.end());
596 if (mlir::failed(rewriter.convertRegionTypes(
597 &loweredFuncOp.getBody(), *getTypeConverter(), &signatureConversion)))
598 return mlir::failure();
599
600 rewriter.eraseOp(op);
601 return mlir::success();
602}
603
604mlir::LogicalResult CIRGlobalOpABILowering::matchAndRewrite(
605 cir::GlobalOp op, OpAdaptor adaptor,
606 mlir::ConversionPatternRewriter &rewriter) const {
607 mlir::Type ty = op.getSymType();
608 mlir::Type loweredTy = getTypeConverter()->convertType(ty);
609 if (!loweredTy)
610 return mlir::failure();
611
612 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
613
614 mlir::Attribute loweredInit = lowerInitialValue(
615 lowerModule, layout, *getTypeConverter(), ty, op.getInitialValueAttr());
616
617 cir::GlobalOp newOp = rewriter.cloneWithoutRegions(op);
618 newOp.setInitialValueAttr(loweredInit);
619 newOp.setSymType(loweredTy);
620 // Regions have to be separately moved(rather than cloned), else we cause
621 // multi-block regions/eh stuff to be double-referenced, and thus can't be
622 // removed properly during collectUnreachable.
623 rewriter.inlineRegionBefore(op.getCtorRegion(), newOp.getCtorRegion(),
624 newOp.getCtorRegion().end());
625 rewriter.inlineRegionBefore(op.getDtorRegion(), newOp.getDtorRegion(),
626 newOp.getDtorRegion().end());
627 rewriter.replaceOp(op, newOp);
628 return mlir::success();
629}
630
631mlir::LogicalResult CIRBaseDataMemberOpABILowering::matchAndRewrite(
632 cir::BaseDataMemberOp op, OpAdaptor adaptor,
633 mlir::ConversionPatternRewriter &rewriter) const {
634 mlir::Value loweredResult = lowerModule->getCXXABI().lowerBaseDataMember(
635 op, adaptor.getSrc(), rewriter);
636 rewriter.replaceOp(op, loweredResult);
637 return mlir::success();
638}
639
640mlir::LogicalResult CIRBaseMethodOpABILowering::matchAndRewrite(
641 cir::BaseMethodOp op, OpAdaptor adaptor,
642 mlir::ConversionPatternRewriter &rewriter) const {
643 mlir::Value loweredResult =
644 lowerModule->getCXXABI().lowerBaseMethod(op, adaptor.getSrc(), rewriter);
645 rewriter.replaceOp(op, loweredResult);
646 return mlir::success();
647}
648
649mlir::LogicalResult CIRDeleteArrayOpABILowering::matchAndRewrite(
650 cir::DeleteArrayOp op, OpAdaptor adaptor,
651 mlir::ConversionPatternRewriter &rewriter) const {
652 mlir::FlatSymbolRefAttr deleteFn = op.getDeleteFnAttr();
653 mlir::Location loc = op->getLoc();
654 mlir::Value loweredAddress = adaptor.getAddress();
655
656 cir::UsualDeleteParamsAttr deleteParams = op.getDeleteParamsAttr();
657 if (!deleteParams)
658 deleteParams = cir::UsualDeleteParamsAttr::get(op.getContext(), false,
659 std::nullopt, false, false);
660 bool cookieRequired = deleteParams.getSize() || op.getElementDtorAttr();
661
662 assert(!deleteParams.getDestroyingDelete() &&
663 "destroying delete not legal on arrays");
664 assert(!deleteParams.getTypeAwareDelete() &&
665 "type-aware delete not legal on arrays");
666
667 const CIRCXXABI &cxxABI = lowerModule->getCXXABI();
668 CIRBaseBuilderTy cirBuilder(rewriter);
669
670 // Read the array cookie (or compute the void* pointer for the
671 // non-cookie case) before creating the cleanup scope. The cookie read
672 // produces values that are needed by both the destruction loop in the
673 // body region (numElements for the array.dtor) and the operator
674 // delete[] call in the cleanup region (deletePtr / numElements for the
675 // total-size computation), so it must dominate both regions.
676 mlir::Value deletePtr;
677 mlir::Value numElements;
678 cir::PointerType ptrTy;
679 clang::CharUnits cookieSize;
680 mlir::DataLayout dl(op->getParentOfType<mlir::ModuleOp>());
681 unsigned ptrWidth =
682 lowerModule->getTarget().getPointerWidth(clang::LangAS::Default);
683 cir::IntType sizeTy = cirBuilder.getUIntNTy(ptrWidth);
684
685 if (cookieRequired) {
686 ptrTy = mlir::cast<cir::PointerType>(loweredAddress.getType());
687 clang::CharUnits elementAlign =
688 clang::CharUnits::fromQuantity(op.getElementAlign());
689 cxxABI.readArrayCookie(loc, loweredAddress, elementAlign, dl, cirBuilder,
690 numElements, deletePtr, cookieSize);
691 } else {
692 deletePtr = cir::CastOp::create(rewriter, loc, cirBuilder.getVoidPtrTy(),
693 cir::CastKind::bitcast, loweredAddress);
694 }
695
696 // Create a cleanup scope to wrap the ArrayDtor operation (if needed) and
697 // call the array delete operator from the cleanup region. If no exceptions
698 // are thrown during the array dtor, the normal control flow will call the
699 // delete operator. The ArrayDtor operation will get its own cleanup region
700 // when it is expanded during LoweringPrepare. If an exception is thrown, the
701 // exception handling flow will be connected to the cleanup region here to
702 // call the delete operator on the exception path.
703 mlir::FlatSymbolRefAttr dtorFn = op.getElementDtorAttr();
704
705 // Make sure the calls and the callees agree on calling convention.
706 auto getCalleeCallingConv = [&](mlir::FlatSymbolRefAttr callee) {
707 if (auto fn =
708 mlir::SymbolTable::lookupNearestSymbolFrom<cir::FuncOp>(op, callee))
709 return fn.getCallingConv();
710 return cir::CallingConv::C;
711 };
712
713 cir::CleanupKind cleanupKind =
714 op.getDtorMayThrow() ? cir::CleanupKind::All : cir::CleanupKind::Normal;
715 cir::CleanupScopeOp::create(
716 rewriter, loc, cleanupKind,
717 /*bodyBuilder=*/
718 [&](mlir::OpBuilder &b, mlir::Location l) {
719 if (dtorFn) {
720 auto eltPtrTy = cir::PointerType::get(ptrTy.getPointee());
721 auto arrayDtor = cir::ArrayDtor::create(
722 b, l, loweredAddress, numElements,
723 [&](mlir::OpBuilder &bb, mlir::Location ll) {
724 mlir::Value arg =
725 bb.getInsertionBlock()->addArgument(eltPtrTy, ll);
726 auto dtorCall = cir::CallOp::create(
727 bb, ll, dtorFn, cir::VoidType(), mlir::ValueRange{arg});
728 dtorCall.setCallingConv(getCalleeCallingConv(dtorFn));
729 if (!op.getDtorMayThrow())
730 dtorCall.setNothrowAttr(bb.getUnitAttr());
731 cir::YieldOp::create(bb, ll);
732 });
733 if (op.getDtorMayThrow())
734 arrayDtor.setDtorMayThrow(true);
735 }
736 cir::YieldOp::create(b, l);
737 },
738 /*cleanupBuilder=*/
739 [&](mlir::OpBuilder &b, mlir::Location l) {
741 callArgs.push_back(deletePtr);
742 if (deleteParams.getSize()) {
743 uint64_t eltSizeBytes = dl.getTypeSizeInBits(ptrTy.getPointee()) / 8;
744 auto eltSizeVal = cir::ConstantOp::create(
745 b, l, cir::IntAttr::get(sizeTy, eltSizeBytes));
746 mlir::Value allocSize =
747 cir::MulOp::create(b, l, sizeTy, eltSizeVal, numElements);
748 auto cookieSizeVal = cir::ConstantOp::create(
749 b, l, cir::IntAttr::get(sizeTy, cookieSize.getQuantity()));
750 allocSize =
751 cir::AddOp::create(b, l, sizeTy, allocSize, cookieSizeVal);
752 callArgs.push_back(allocSize);
753 }
754 if (deleteParams.getAlignment()) {
755 auto alignVal = cir::ConstantOp::create(
756 b, l, cir::IntAttr::get(sizeTy, *deleteParams.getAlignment()));
757 callArgs.push_back(alignVal);
758 }
759
760 auto deleteCall =
761 cir::CallOp::create(b, l, deleteFn, cir::VoidType(), callArgs);
762 deleteCall.setCallingConv(getCalleeCallingConv(deleteFn));
763 // operator delete[] is implicitly nothrow per [basic.stc.dynamic],
764 // matching classic CodeGen's `nounwind` attribute on the call.
765 deleteCall.setNothrowAttr(b.getUnitAttr());
766 cir::YieldOp::create(b, l);
767 });
768
769 rewriter.eraseOp(op);
770 return mlir::success();
771}
772
773mlir::LogicalResult CIRDerivedDataMemberOpABILowering::matchAndRewrite(
774 cir::DerivedDataMemberOp op, OpAdaptor adaptor,
775 mlir::ConversionPatternRewriter &rewriter) const {
776 mlir::Value loweredResult = lowerModule->getCXXABI().lowerDerivedDataMember(
777 op, adaptor.getSrc(), rewriter);
778 rewriter.replaceOp(op, loweredResult);
779 return mlir::success();
780}
781
782mlir::LogicalResult CIRDerivedMethodOpABILowering::matchAndRewrite(
783 cir::DerivedMethodOp op, OpAdaptor adaptor,
784 mlir::ConversionPatternRewriter &rewriter) const {
785 mlir::Value loweredResult = lowerModule->getCXXABI().lowerDerivedMethod(
786 op, adaptor.getSrc(), rewriter);
787 rewriter.replaceOp(op, loweredResult);
788 return mlir::success();
789}
790
791mlir::LogicalResult CIRDynamicCastOpABILowering::matchAndRewrite(
792 cir::DynamicCastOp op, OpAdaptor adaptor,
793 mlir::ConversionPatternRewriter &rewriter) const {
794 mlir::Value loweredResult =
795 lowerModule->getCXXABI().lowerDynamicCast(op, rewriter);
796 rewriter.replaceOp(op, loweredResult);
797 return mlir::success();
798}
799
800mlir::LogicalResult CIRGetMethodOpABILowering::matchAndRewrite(
801 cir::GetMethodOp op, OpAdaptor adaptor,
802 mlir::ConversionPatternRewriter &rewriter) const {
803 mlir::Value callee;
804 mlir::Value thisArg;
805 lowerModule->getCXXABI().lowerGetMethod(
806 op, callee, thisArg, adaptor.getMethod(), adaptor.getObject(), rewriter);
807 rewriter.replaceOp(op, {callee, thisArg});
808 return mlir::success();
809}
810
811mlir::LogicalResult CIRGetRuntimeMemberOpABILowering::matchAndRewrite(
812 cir::GetRuntimeMemberOp op, OpAdaptor adaptor,
813 mlir::ConversionPatternRewriter &rewriter) const {
814 mlir::Type resTy = getTypeConverter()->convertType(op.getType());
815 mlir::Operation *newOp = lowerModule->getCXXABI().lowerGetRuntimeMember(
816 op, resTy, adaptor.getAddr(), adaptor.getMember(), rewriter);
817 rewriter.replaceOp(op, newOp);
818 return mlir::success();
819}
820
821mlir::LogicalResult CIRVTableGetTypeInfoOpABILowering::matchAndRewrite(
822 cir::VTableGetTypeInfoOp op, OpAdaptor adaptor,
823 mlir::ConversionPatternRewriter &rewriter) const {
824 mlir::Value loweredResult =
825 lowerModule->getCXXABI().lowerVTableGetTypeInfo(op, rewriter);
826 rewriter.replaceOp(op, loweredResult);
827 return mlir::success();
828}
829
830namespace {
831// A small type to handle type conversion for the the CXXABILoweringPass.
832// Even though this is a CIR-to-CIR pass, we are eliminating some CIR types.
833// The records that contain them are rebuilt by RecordRewritingTypeConverter.
834class CIRABITypeConverter : public cir::RecordRewritingTypeConverter {
835public:
836 CIRABITypeConverter(mlir::MLIRContext &ctx, mlir::DataLayout &dataLayout,
837 cir::LowerModule &lowerModule)
838 : RecordRewritingTypeConverter(ctx) {
839 addConversion([&](cir::DataMemberType type) -> mlir::Type {
840 mlir::Type abiType =
841 lowerModule.getCXXABI().lowerDataMemberType(type, *this);
842 return convertType(abiType);
843 });
844 addConversion([&](cir::MethodType type) -> mlir::Type {
845 mlir::Type abiType = lowerModule.getCXXABI().lowerMethodType(type, *this);
846 return convertType(abiType);
847 });
848 }
849
850protected:
851 // Only member pointer types are rewritten. Records that reach none of them
852 // are left alone.
853 bool typeMayChange(mlir::Type type) const override {
854 return mlir::isa<cir::DataMemberType, cir::MethodType>(type);
855 }
856};
857} // namespace
858
859static void
860populateCXXABIConversionTarget(mlir::ConversionTarget &target,
861 const mlir::TypeConverter &typeConverter) {
862 target.addLegalOp<mlir::ModuleOp>();
863
864 // The ABI lowering pass is interested in CIR operations with operands or
865 // results of CXXABI-dependent types, or CIR operations with regions whose
866 // block arguments are of CXXABI-dependent types.
867 target.addDynamicallyLegalDialect<cir::CIRDialect>(
868 [&typeConverter](mlir::Operation *op) {
869 if (!typeConverter.isLegal(op))
870 return false;
871
872 bool attrs = areCXXABIInherentAttrsLegal(op, typeConverter);
873
874 return attrs &&
875 std::all_of(op->getRegions().begin(), op->getRegions().end(),
876 [&typeConverter](mlir::Region &region) {
877 return typeConverter.isLegal(&region);
878 });
879 });
880
881 target.addDynamicallyLegalDialect<mlir::acc::OpenACCDialect>(
882 [&typeConverter](mlir::Operation *op) {
883 if (!typeConverter.isLegal(op))
884 return false;
885
886 bool attrs = areCXXABIInherentAttrsLegal(op, typeConverter);
887
888 return attrs &&
889 std::all_of(op->getRegions().begin(), op->getRegions().end(),
890 [&typeConverter](mlir::Region &region) {
891 return typeConverter.isLegal(&region);
892 });
893 });
894
895 // TargetLowering leaves same-type casts behind. Keep them illegal so the
896 // conversion driver folds them away.
897 target.addDynamicallyLegalOp<cir::CastOp>([&typeConverter](cir::CastOp op) {
898 if (op.getSrc().getType() == op.getType()) {
899 switch (op.getKind()) {
900 case cir::CastKind::bitcast:
901 case cir::CastKind::address_space:
902 case cir::CastKind::float_complex:
903 case cir::CastKind::int_complex:
904 return false;
905 default:
906 break;
907 }
908 }
909 if (!typeConverter.isLegal(op.getOperation()))
910 return false;
911 // CastOp has no regions, so operand/result types and inherent
912 // attributes are the whole legality check.
913 return areCXXABIInherentAttrsLegal(op, typeConverter);
914 });
915
916 // Some CIR ops needs special checking for legality
917 target.addDynamicallyLegalOp<cir::FuncOp>([&typeConverter](cir::FuncOp op) {
918 bool attrs = areCXXABIInherentAttrsLegal(op, typeConverter);
919
920 return attrs && typeConverter.isLegal(op.getFunctionType());
921 });
922 target.addDynamicallyLegalOp<cir::GlobalOp>(
923 [&typeConverter](cir::GlobalOp op) {
924 return typeConverter.isLegal(op.getSymType());
925 });
926 // Operations that do not use any special types must be explicitly marked as
927 // illegal to trigger processing here.
928 target.addIllegalOp<cir::DeleteArrayOp>();
929 target.addIllegalOp<cir::DynamicCastOp>();
930 target.addIllegalOp<cir::VTableGetTypeInfoOp>();
931}
932
933//===----------------------------------------------------------------------===//
934// The Pass
935//===----------------------------------------------------------------------===//
936
937void CXXABILoweringPass::runOnOperation() {
938 auto mod = mlir::cast<mlir::ModuleOp>(getOperation());
939 mlir::MLIRContext *ctx = mod.getContext();
940
941 std::unique_ptr<cir::LowerModule> lowerModule = cir::createLowerModule(mod);
942 // If lower module is not available, skip the ABI lowering pass.
943 if (!lowerModule) {
944 mod.emitWarning("Cannot create a CIR lower module, skipping the ")
945 << getName() << " pass";
946 return;
947 }
948
949 mlir::DataLayout dataLayout(mod);
950 CIRABITypeConverter typeConverter(*ctx, dataLayout, *lowerModule);
951
952 mlir::RewritePatternSet patterns(ctx);
953 patterns.add<CIRGenericCXXABILoweringPattern>(patterns.getContext(),
954 typeConverter);
955 patterns.add<
956#define GET_ABI_LOWERING_PATTERNS_LIST
957#include "clang/CIR/Dialect/IR/CIRLowering.inc"
958#undef GET_ABI_LOWERING_PATTERNS_LIST
959 >(patterns.getContext(), typeConverter, dataLayout, *lowerModule);
960
961 mlir::ConversionTarget target(*ctx);
962 populateCXXABIConversionTarget(target, typeConverter);
963
964 llvm::SmallVector<mlir::Operation *> ops;
965 ops.push_back(mod);
966 cir::collectUnreachable(mod, ops);
967
968 if (failed(mlir::applyPartialConversion(ops, target, std::move(patterns))))
969 signalPassFailure();
970
971 typeConverter.restoreRecordTypeNames();
972}
973
974std::unique_ptr<Pass> mlir::createCXXABILoweringPass() {
975 return std::make_unique<CXXABILoweringPass>();
976}
#define CXX_ABI_ALWAYS_LEGAL_ATTRS
static void populateCXXABIConversionTarget(mlir::ConversionTarget &target, const mlir::TypeConverter &typeConverter)
static mlir::TypedAttr lowerInitialValue(const LowerModule *lowerModule, const mlir::DataLayout &layout, const mlir::TypeConverter &tc, mlir::Type ty, mlir::Attribute initVal)
virtual mlir::Type lowerMethodType(cir::MethodType type, const mlir::TypeConverter &typeConverter) const =0
Lower the given member function pointer type to its ABI type.
virtual mlir::TypedAttr lowerDataMemberConstant(cir::DataMemberAttr attr, const mlir::DataLayout &layout, const mlir::TypeConverter &typeConverter) const =0
Lower the given data member pointer constant to a constant of the ABI type.
virtual mlir::TypedAttr lowerDataMemberOffsetConstant(cir::DataMemberOffsetAttr attr, const mlir::DataLayout &layout, const mlir::TypeConverter &typeConverter) const =0
Lower the given by-offset data member pointer constant (used for members with no CIR field index,...
virtual mlir::TypedAttr lowerMethodConstant(cir::MethodAttr attr, const mlir::DataLayout &layout, const mlir::TypeConverter &typeConverter) const =0
Lower the given member function pointer constant to a constant of the ABI type.
virtual mlir::Type lowerDataMemberType(cir::DataMemberType type, const mlir::TypeConverter &typeConverter) const =0
Lower the given data member pointer type to its ABI type.
void readArrayCookie(mlir::Location loc, mlir::Value elementPtr, clang::CharUnits elementAlign, const mlir::DataLayout &dataLayout, CIRBaseBuilderTy &builder, mlir::Value &numElements, mlir::Value &allocPtr, clang::CharUnits &cookieSize) const
Read the array cookie for a dynamically-allocated array whose first element is at elementPtr.
Definition CIRCXXABI.cpp:25
CIRCXXABI & getCXXABI() const
Definition LowerModule.h:53
A type converter that converts the types nested in pointers, arrays, functions and records.
This is an opaque type for sizes expressed in character units.
Definition CharUnits.h:38
QuantityType getQuantity() const
Get the raw integer representation of this quantity.
Definition CharUnits.h:155
static CharUnits fromQuantity(QuantityType Quantity)
Construct a CharUnits quantity from a raw integer type.
Definition CharUnits.h:58
void collectUnreachable(mlir::Operation *parent, llvm::SmallVectorImpl< mlir::Operation * > &ops)
Collect ops in blocks that are unreachable from their region's entry, appending them to ops.
std::unique_ptr< LowerModule > createLowerModule(mlir::ModuleOp module)
const internal::VariadicAllOfMatcher< Attr > attr
StringRef getName(const HeaderType T)
Definition HeaderFile.h:38
RangeSelector callArgs(std::string ID)
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ Default
Set to the current date and time.
std::unique_ptr< Pass > createCXXABILoweringPass()
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)