clang 23.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"
11
12#include "mlir/IR/PatternMatch.h"
13#include "mlir/Interfaces/DataLayoutInterfaces.h"
14#include "mlir/Pass/Pass.h"
15#include "mlir/Transforms/DialectConversion.h"
23
24using namespace mlir;
25using namespace cir;
26
27namespace mlir {
28#define GEN_PASS_DEF_CXXABILOWERING
29#include "clang/CIR/Dialect/Passes.h.inc"
30} // namespace mlir
31
32namespace {
33
34#define GET_ABI_LOWERING_PATTERNS
35#include "clang/CIR/Dialect/IR/CIRLowering.inc"
36#undef GET_ABI_LOWERING_PATTERNS
37
38struct CXXABILoweringPass
39 : public impl::CXXABILoweringBase<CXXABILoweringPass> {
40 CXXABILoweringPass() = default;
41 void runOnOperation() override;
42};
43
44/// A generic ABI lowering rewrite pattern. This conversion pattern matches any
45/// CIR dialect operations with at least one operand or result of an
46/// ABI-dependent type. This conversion pattern rewrites the matched operation
47/// by replacing all its ABI-dependent operands and results with their
48/// lowered counterparts.
49class CIRGenericCXXABILoweringPattern : public mlir::ConversionPattern {
50public:
51 CIRGenericCXXABILoweringPattern(mlir::MLIRContext *context,
52 const mlir::TypeConverter &typeConverter)
53 : mlir::ConversionPattern(typeConverter, MatchAnyOpTypeTag(),
54 /*benefit=*/1, context) {}
55
56 mlir::LogicalResult
57 matchAndRewrite(mlir::Operation *op, llvm::ArrayRef<mlir::Value> operands,
58 mlir::ConversionPatternRewriter &rewriter) const override {
59 // Do not match on operations that have dedicated ABI lowering rewrite rules
60 if (llvm::isa<cir::AllocaOp, cir::BaseDataMemberOp, cir::BaseMethodOp,
61 cir::CastOp, cir::CmpOp, cir::ConstantOp,
62 cir::DerivedDataMemberOp, cir::DerivedMethodOp, cir::FuncOp,
63 cir::GetMethodOp, cir::GetRuntimeMemberOp, cir::GlobalOp>(op))
64 return mlir::failure();
65
66 const mlir::TypeConverter *typeConverter = getTypeConverter();
67 assert(typeConverter &&
68 "CIRGenericCXXABILoweringPattern requires a type converter");
69 bool operandsAndResultsLegal = typeConverter->isLegal(op);
70 bool regionsLegal =
71 std::all_of(op->getRegions().begin(), op->getRegions().end(),
72 [typeConverter](mlir::Region &region) {
73 return typeConverter->isLegal(&region);
74 });
75 if (operandsAndResultsLegal && regionsLegal) {
76 // The operation does not have any CXXABI-dependent operands or results,
77 // the match fails.
78 return mlir::failure();
79 }
80
81 assert(op->getNumRegions() == 0 && "CIRGenericCXXABILoweringPattern cannot "
82 "deal with operations with regions");
83
84 mlir::OperationState loweredOpState(op->getLoc(), op->getName());
85 loweredOpState.addOperands(operands);
86 loweredOpState.addAttributes(op->getAttrs());
87 loweredOpState.addSuccessors(op->getSuccessors());
88
89 // Lower all result types
90 llvm::SmallVector<mlir::Type> loweredResultTypes;
91 loweredResultTypes.reserve(op->getNumResults());
92 for (mlir::Type result : op->getResultTypes())
93 loweredResultTypes.push_back(typeConverter->convertType(result));
94 loweredOpState.addTypes(loweredResultTypes);
95
96 // Lower all regions
97 for (mlir::Region &region : op->getRegions()) {
98 mlir::Region *loweredRegion = loweredOpState.addRegion();
99 rewriter.inlineRegionBefore(region, *loweredRegion, loweredRegion->end());
100 if (mlir::failed(
101 rewriter.convertRegionTypes(loweredRegion, *getTypeConverter())))
102 return mlir::failure();
103 }
104
105 // Clone the operation with lowered operand types and result types
106 mlir::Operation *loweredOp = rewriter.create(loweredOpState);
107
108 rewriter.replaceOp(op, loweredOp);
109 return mlir::success();
110 }
111};
112
113} // namespace
114
115mlir::LogicalResult CIRAllocaOpABILowering::matchAndRewrite(
116 cir::AllocaOp op, OpAdaptor adaptor,
117 mlir::ConversionPatternRewriter &rewriter) const {
118 mlir::Type allocaPtrTy = op.getType();
119 mlir::Type allocaTy = op.getAllocaType();
120 mlir::Type loweredAllocaPtrTy = getTypeConverter()->convertType(allocaPtrTy);
121 mlir::Type loweredAllocaTy = getTypeConverter()->convertType(allocaTy);
122
123 cir::AllocaOp loweredOp = cir::AllocaOp::create(
124 rewriter, op.getLoc(), loweredAllocaPtrTy, loweredAllocaTy, op.getName(),
125 op.getAlignmentAttr(), /*dynAllocSize=*/adaptor.getDynAllocSize());
126 loweredOp.setInit(op.getInit());
127 loweredOp.setConstant(op.getConstant());
128 loweredOp.setAnnotationsAttr(op.getAnnotationsAttr());
129
130 rewriter.replaceOp(op, loweredOp);
131 return mlir::success();
132}
133
134mlir::LogicalResult CIRCastOpABILowering::matchAndRewrite(
135 cir::CastOp op, OpAdaptor adaptor,
136 mlir::ConversionPatternRewriter &rewriter) const {
137 mlir::Type srcTy = op.getSrc().getType();
138 assert((mlir::isa<cir::DataMemberType, cir::MethodType>(srcTy)) &&
139 "input to bitcast in ABI lowering must be a data member or method");
140
141 switch (op.getKind()) {
142 case cir::CastKind::bitcast: {
143 mlir::Type destTy = getTypeConverter()->convertType(op.getType());
144 mlir::Value loweredResult;
145 if (mlir::isa<cir::DataMemberType>(srcTy))
146 loweredResult = lowerModule->getCXXABI().lowerDataMemberBitcast(
147 op, destTy, adaptor.getSrc(), rewriter);
148 else
149 loweredResult = lowerModule->getCXXABI().lowerMethodBitcast(
150 op, destTy, adaptor.getSrc(), rewriter);
151 rewriter.replaceOp(op, loweredResult);
152 return mlir::success();
153 }
154 case cir::CastKind::member_ptr_to_bool: {
155 mlir::Value loweredResult;
156 if (mlir::isa<cir::MethodType>(srcTy))
157 loweredResult = lowerModule->getCXXABI().lowerMethodToBoolCast(
158 op, adaptor.getSrc(), rewriter);
159 else
160 loweredResult = lowerModule->getCXXABI().lowerDataMemberToBoolCast(
161 op, adaptor.getSrc(), rewriter);
162 rewriter.replaceOp(op, loweredResult);
163 return mlir::success();
164 }
165 default:
166 break;
167 }
168
169 return mlir::failure();
170}
171
172mlir::LogicalResult CIRConstantOpABILowering::matchAndRewrite(
173 cir::ConstantOp op, OpAdaptor adaptor,
174 mlir::ConversionPatternRewriter &rewriter) const {
175
176 if (mlir::isa<cir::DataMemberType>(op.getType())) {
177 auto dataMember = mlir::cast<cir::DataMemberAttr>(op.getValue());
178 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
179 mlir::TypedAttr abiValue = lowerModule->getCXXABI().lowerDataMemberConstant(
180 dataMember, layout, *getTypeConverter());
181 rewriter.replaceOpWithNewOp<ConstantOp>(op, abiValue);
182 return mlir::success();
183 }
184
185 if (mlir::isa<cir::MethodType>(op.getType())) {
186 auto method = mlir::cast<cir::MethodAttr>(op.getValue());
187 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
188 mlir::TypedAttr abiValue = lowerModule->getCXXABI().lowerMethodConstant(
189 method, layout, *getTypeConverter());
190 rewriter.replaceOpWithNewOp<ConstantOp>(op, abiValue);
191 return mlir::success();
192 }
193
194 llvm_unreachable("constant operand is not an CXXABI-dependent type");
195}
196
197mlir::LogicalResult CIRCmpOpABILowering::matchAndRewrite(
198 cir::CmpOp op, OpAdaptor adaptor,
199 mlir::ConversionPatternRewriter &rewriter) const {
200 mlir::Type type = op.getLhs().getType();
201 assert((mlir::isa<cir::DataMemberType, cir::MethodType>(type)) &&
202 "input to cmp in ABI lowering must be a data member or method");
203
204 mlir::Value loweredResult;
205 if (mlir::isa<cir::DataMemberType>(type))
206 loweredResult = lowerModule->getCXXABI().lowerDataMemberCmp(
207 op, adaptor.getLhs(), adaptor.getRhs(), rewriter);
208 else
209 loweredResult = lowerModule->getCXXABI().lowerMethodCmp(
210 op, adaptor.getLhs(), adaptor.getRhs(), rewriter);
211
212 rewriter.replaceOp(op, loweredResult);
213 return mlir::success();
214}
215
216mlir::LogicalResult CIRFuncOpABILowering::matchAndRewrite(
217 cir::FuncOp op, OpAdaptor adaptor,
218 mlir::ConversionPatternRewriter &rewriter) const {
219 cir::FuncType opFuncType = op.getFunctionType();
220 mlir::TypeConverter::SignatureConversion signatureConversion(
221 opFuncType.getNumInputs());
222
223 for (const auto &[i, argType] : llvm::enumerate(opFuncType.getInputs())) {
224 mlir::Type loweredArgType = getTypeConverter()->convertType(argType);
225 if (!loweredArgType)
226 return mlir::failure();
227 signatureConversion.addInputs(i, loweredArgType);
228 }
229
230 mlir::Type loweredResultType =
231 getTypeConverter()->convertType(opFuncType.getReturnType());
232 if (!loweredResultType)
233 return mlir::failure();
234
235 auto loweredFuncType =
236 cir::FuncType::get(signatureConversion.getConvertedTypes(),
237 loweredResultType, /*isVarArg=*/opFuncType.isVarArg());
238
239 // Create a new cir.func operation for the CXXABI-lowered function.
240 cir::FuncOp loweredFuncOp = rewriter.cloneWithoutRegions(op);
241 loweredFuncOp.setFunctionType(loweredFuncType);
242 rewriter.inlineRegionBefore(op.getBody(), loweredFuncOp.getBody(),
243 loweredFuncOp.end());
244 if (mlir::failed(rewriter.convertRegionTypes(
245 &loweredFuncOp.getBody(), *getTypeConverter(), &signatureConversion)))
246 return mlir::failure();
247
248 rewriter.eraseOp(op);
249 return mlir::success();
250}
251
252mlir::LogicalResult CIRGlobalOpABILowering::matchAndRewrite(
253 cir::GlobalOp op, OpAdaptor adaptor,
254 mlir::ConversionPatternRewriter &rewriter) const {
255 mlir::Type ty = op.getSymType();
256 mlir::Type loweredTy = getTypeConverter()->convertType(ty);
257 if (!loweredTy)
258 return mlir::failure();
259
260 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
261
262 mlir::Attribute loweredInit;
263 if (mlir::isa<cir::DataMemberType>(ty)) {
264 cir::DataMemberAttr init =
265 mlir::cast_if_present<cir::DataMemberAttr>(op.getInitialValueAttr());
266 loweredInit = lowerModule->getCXXABI().lowerDataMemberConstant(
267 init, layout, *getTypeConverter());
268 } else if (mlir::isa<cir::MethodType>(ty)) {
269 cir::MethodAttr init =
270 mlir::cast_if_present<cir::MethodAttr>(op.getInitialValueAttr());
271 loweredInit = lowerModule->getCXXABI().lowerMethodConstant(
272 init, layout, *getTypeConverter());
273 } else if (auto arrTy = mlir::dyn_cast<cir::ArrayType>(ty)) {
274 auto init = mlir::cast<cir::ConstArrayAttr>(op.getInitialValueAttr());
275 auto arrayElts = mlir::cast<ArrayAttr>(init.getElts());
276 SmallVector<mlir::Attribute> loweredElements;
277 loweredElements.reserve(arrTy.getSize());
278 for (const mlir::Attribute &attr : arrayElts) {
279 if (auto methodAttr = mlir::dyn_cast<cir::MethodAttr>(attr)) {
280 mlir::Attribute loweredElt =
281 lowerModule->getCXXABI().lowerMethodConstant(methodAttr, layout,
282 *getTypeConverter());
283 loweredElements.push_back(loweredElt);
284 } else {
285 llvm_unreachable("array of data member lowering is NYI");
286 }
287 }
288 auto loweredArrTy =
289 mlir::cast<cir::ArrayType>(getTypeConverter()->convertType(arrTy));
290 loweredInit = cir::ConstArrayAttr::get(
291 loweredArrTy,
292 mlir::ArrayAttr::get(rewriter.getContext(), loweredElements));
293 } else {
294 llvm_unreachable(
295 "inputs to cir.global in ABI lowering must be data member or method");
296 }
297
298 auto newOp = mlir::cast<cir::GlobalOp>(rewriter.clone(*op.getOperation()));
299 newOp.setInitialValueAttr(loweredInit);
300 newOp.setSymType(loweredTy);
301 rewriter.replaceOp(op, newOp);
302 return mlir::success();
303}
304
305mlir::LogicalResult CIRBaseDataMemberOpABILowering::matchAndRewrite(
306 cir::BaseDataMemberOp op, OpAdaptor adaptor,
307 mlir::ConversionPatternRewriter &rewriter) const {
308 mlir::Value loweredResult = lowerModule->getCXXABI().lowerBaseDataMember(
309 op, adaptor.getSrc(), rewriter);
310 rewriter.replaceOp(op, loweredResult);
311 return mlir::success();
312}
313
314mlir::LogicalResult CIRBaseMethodOpABILowering::matchAndRewrite(
315 cir::BaseMethodOp op, OpAdaptor adaptor,
316 mlir::ConversionPatternRewriter &rewriter) const {
317 mlir::Value loweredResult =
318 lowerModule->getCXXABI().lowerBaseMethod(op, adaptor.getSrc(), rewriter);
319 rewriter.replaceOp(op, loweredResult);
320 return mlir::success();
321}
322
323mlir::LogicalResult CIRDerivedDataMemberOpABILowering::matchAndRewrite(
324 cir::DerivedDataMemberOp op, OpAdaptor adaptor,
325 mlir::ConversionPatternRewriter &rewriter) const {
326 mlir::Value loweredResult = lowerModule->getCXXABI().lowerDerivedDataMember(
327 op, adaptor.getSrc(), rewriter);
328 rewriter.replaceOp(op, loweredResult);
329 return mlir::success();
330}
331
332mlir::LogicalResult CIRDerivedMethodOpABILowering::matchAndRewrite(
333 cir::DerivedMethodOp op, OpAdaptor adaptor,
334 mlir::ConversionPatternRewriter &rewriter) const {
335 mlir::Value loweredResult = lowerModule->getCXXABI().lowerDerivedMethod(
336 op, adaptor.getSrc(), rewriter);
337 rewriter.replaceOp(op, loweredResult);
338 return mlir::success();
339}
340
341mlir::LogicalResult CIRDynamicCastOpABILowering::matchAndRewrite(
342 cir::DynamicCastOp op, OpAdaptor adaptor,
343 mlir::ConversionPatternRewriter &rewriter) const {
344 mlir::Value loweredResult =
345 lowerModule->getCXXABI().lowerDynamicCast(op, rewriter);
346 rewriter.replaceOp(op, loweredResult);
347 return mlir::success();
348}
349
350mlir::LogicalResult CIRGetMethodOpABILowering::matchAndRewrite(
351 cir::GetMethodOp op, OpAdaptor adaptor,
352 mlir::ConversionPatternRewriter &rewriter) const {
353 mlir::Value callee;
354 mlir::Value thisArg;
355 lowerModule->getCXXABI().lowerGetMethod(
356 op, callee, thisArg, adaptor.getMethod(), adaptor.getObject(), rewriter);
357 rewriter.replaceOp(op, {callee, thisArg});
358 return mlir::success();
359}
360
361mlir::LogicalResult CIRGetRuntimeMemberOpABILowering::matchAndRewrite(
362 cir::GetRuntimeMemberOp op, OpAdaptor adaptor,
363 mlir::ConversionPatternRewriter &rewriter) const {
364 mlir::Type resTy = getTypeConverter()->convertType(op.getType());
365 mlir::Operation *newOp = lowerModule->getCXXABI().lowerGetRuntimeMember(
366 op, resTy, adaptor.getAddr(), adaptor.getMember(), rewriter);
367 rewriter.replaceOp(op, newOp);
368 return mlir::success();
369}
370
371// Prepare the type converter for the CXXABI lowering pass.
372// Even though this is a CIR-to-CIR pass, we are eliminating some CIR types.
373static void prepareCXXABITypeConverter(mlir::TypeConverter &converter,
374 mlir::DataLayout &dataLayout,
375 cir::LowerModule &lowerModule) {
376 converter.addConversion([&](mlir::Type type) -> mlir::Type { return type; });
377 // This is necessary in order to convert CIR pointer types that are pointing
378 // to CIR types that we are lowering in this pass.
379 converter.addConversion([&](cir::PointerType type) -> mlir::Type {
380 mlir::Type loweredPointeeType = converter.convertType(type.getPointee());
381 if (!loweredPointeeType)
382 return {};
383 return cir::PointerType::get(type.getContext(), loweredPointeeType,
384 type.getAddrSpace());
385 });
386 converter.addConversion([&](cir::ArrayType type) -> mlir::Type {
387 mlir::Type loweredElementType =
388 converter.convertType(type.getElementType());
389 if (!loweredElementType)
390 return {};
391 return cir::ArrayType::get(loweredElementType, type.getSize());
392 });
393
394 converter.addConversion([&](cir::DataMemberType type) -> mlir::Type {
395 mlir::Type abiType =
396 lowerModule.getCXXABI().lowerDataMemberType(type, converter);
397 return converter.convertType(abiType);
398 });
399 converter.addConversion([&](cir::MethodType type) -> mlir::Type {
400 mlir::Type abiType =
401 lowerModule.getCXXABI().lowerMethodType(type, converter);
402 return converter.convertType(abiType);
403 });
404 // This is necessary in order to convert CIR function types that have argument
405 // or return types that use CIR types that we are lowering in this pass.
406 converter.addConversion([&](cir::FuncType type) -> mlir::Type {
407 llvm::SmallVector<mlir::Type> loweredInputTypes;
408 loweredInputTypes.reserve(type.getNumInputs());
409 if (mlir::failed(
410 converter.convertTypes(type.getInputs(), loweredInputTypes)))
411 return {};
412
413 mlir::Type loweredReturnType = converter.convertType(type.getReturnType());
414 if (!loweredReturnType)
415 return {};
416
417 return cir::FuncType::get(loweredInputTypes, loweredReturnType,
418 /*isVarArg=*/type.getVarArg());
419 });
420}
421
422static void
423populateCXXABIConversionTarget(mlir::ConversionTarget &target,
424 const mlir::TypeConverter &typeConverter) {
425 target.addLegalOp<mlir::ModuleOp>();
426
427 // The ABI lowering pass is interested in CIR operations with operands or
428 // results of CXXABI-dependent types, or CIR operations with regions whose
429 // block arguments are of CXXABI-dependent types.
430 target.addDynamicallyLegalDialect<cir::CIRDialect>(
431 [&typeConverter](mlir::Operation *op) {
432 if (!typeConverter.isLegal(op))
433 return false;
434 return std::all_of(op->getRegions().begin(), op->getRegions().end(),
435 [&typeConverter](mlir::Region &region) {
436 return typeConverter.isLegal(&region);
437 });
438 });
439
440 // Some CIR ops needs special checking for legality
441 target.addDynamicallyLegalOp<cir::FuncOp>([&typeConverter](cir::FuncOp op) {
442 return typeConverter.isLegal(op.getFunctionType());
443 });
444 target.addDynamicallyLegalOp<cir::GlobalOp>(
445 [&typeConverter](cir::GlobalOp op) {
446 return typeConverter.isLegal(op.getSymType());
447 });
448 target.addIllegalOp<cir::DynamicCastOp>();
449}
450
451//===----------------------------------------------------------------------===//
452// The Pass
453//===----------------------------------------------------------------------===//
454
455void CXXABILoweringPass::runOnOperation() {
456 auto module = mlir::cast<mlir::ModuleOp>(getOperation());
457 mlir::MLIRContext *ctx = module.getContext();
458
459 // If the triple is not present, e.g. CIR modules parsed from text, we
460 // cannot init LowerModule properly.
462 // If no target triple is available, skip the ABI lowering pass.
463 if (!module->hasAttr(cir::CIRDialect::getTripleAttrName()))
464 return;
465
466 mlir::PatternRewriter rewriter(ctx);
467 std::unique_ptr<cir::LowerModule> lowerModule =
468 cir::createLowerModule(module, rewriter);
469
470 mlir::DataLayout dataLayout(module);
471 mlir::TypeConverter typeConverter;
472 prepareCXXABITypeConverter(typeConverter, dataLayout, *lowerModule);
473
474 mlir::RewritePatternSet patterns(ctx);
475 patterns.add<CIRGenericCXXABILoweringPattern>(patterns.getContext(),
476 typeConverter);
477 patterns.add<
478#define GET_ABI_LOWERING_PATTERNS_LIST
479#include "clang/CIR/Dialect/IR/CIRLowering.inc"
480#undef GET_ABI_LOWERING_PATTERNS_LIST
481 >(patterns.getContext(), typeConverter, dataLayout, *lowerModule);
482
483 mlir::ConversionTarget target(*ctx);
484 populateCXXABIConversionTarget(target, typeConverter);
485
486 if (failed(mlir::applyPartialConversion(module, target, std::move(patterns))))
487 signalPassFailure();
488}
489
490std::unique_ptr<Pass> mlir::createCXXABILoweringPass() {
491 return std::make_unique<CXXABILoweringPass>();
492}
static void populateCXXABIConversionTarget(mlir::ConversionTarget &target, const mlir::TypeConverter &typeConverter)
static void prepareCXXABITypeConverter(mlir::TypeConverter &converter, mlir::DataLayout &dataLayout, cir::LowerModule &lowerModule)
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::Type lowerDataMemberType(cir::DataMemberType type, const mlir::TypeConverter &typeConverter) const =0
Lower the given data member pointer type to its ABI type.
CIRCXXABI & getCXXABI() const
Definition LowerModule.h:47
Defines the clang::TargetInfo interface.
std::unique_ptr< LowerModule > createLowerModule(mlir::ModuleOp module, mlir::PatternRewriter &rewriter)
std::unique_ptr< Pass > createCXXABILoweringPass()
static bool makeTripleAlwaysPresent()