clang 22.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
34template <typename Op>
35class CIROpCXXABILoweringPattern : public mlir::OpConversionPattern<Op> {
36protected:
37 mlir::DataLayout *dataLayout;
38 cir::LowerModule *lowerModule;
39
40public:
41 CIROpCXXABILoweringPattern(mlir::MLIRContext *context,
42 const mlir::TypeConverter &typeConverter,
43 mlir::DataLayout &dataLayout,
44 cir::LowerModule &lowerModule)
45 : mlir::OpConversionPattern<Op>(typeConverter, context),
46 dataLayout(&dataLayout), lowerModule(&lowerModule) {}
47};
48
49// TODO(cir): Use TableGen to generate these patterns.
50#define CIR_CXXABI_LOWERING_PATTERN(name, operation) \
51 struct name : CIROpCXXABILoweringPattern<operation> { \
52 using CIROpCXXABILoweringPattern<operation>::CIROpCXXABILoweringPattern; \
53 \
54 mlir::LogicalResult \
55 matchAndRewrite(operation op, OpAdaptor adaptor, \
56 mlir::ConversionPatternRewriter &rewriter) const override; \
57 }
58
59CIR_CXXABI_LOWERING_PATTERN(CIRAllocaOpABILowering, cir::AllocaOp);
60CIR_CXXABI_LOWERING_PATTERN(CIRConstantOpABILowering, cir::ConstantOp);
61CIR_CXXABI_LOWERING_PATTERN(CIRFuncOpABILowering, cir::FuncOp);
62CIR_CXXABI_LOWERING_PATTERN(CIRGetRuntimeMemberOpABILowering,
63 cir::GetRuntimeMemberOp);
64CIR_CXXABI_LOWERING_PATTERN(CIRGlobalOpABILowering, cir::GlobalOp);
65#undef CIR_CXXABI_LOWERING_PATTERN
66
67struct CXXABILoweringPass
68 : public impl::CXXABILoweringBase<CXXABILoweringPass> {
69 CXXABILoweringPass() = default;
70 void runOnOperation() override;
71};
72
73/// A generic ABI lowering rewrite pattern. This conversion pattern matches any
74/// CIR dialect operations with at least one operand or result of an
75/// ABI-dependent type. This conversion pattern rewrites the matched operation
76/// by replacing all its ABI-dependent operands and results with their
77/// lowered counterparts.
78class CIRGenericCXXABILoweringPattern : public mlir::ConversionPattern {
79public:
80 CIRGenericCXXABILoweringPattern(mlir::MLIRContext *context,
81 const mlir::TypeConverter &typeConverter)
82 : mlir::ConversionPattern(typeConverter, MatchAnyOpTypeTag(),
83 /*benefit=*/1, context) {}
84
85 mlir::LogicalResult
86 matchAndRewrite(mlir::Operation *op, llvm::ArrayRef<mlir::Value> operands,
87 mlir::ConversionPatternRewriter &rewriter) const override {
88 // Do not match on operations that have dedicated ABI lowering rewrite rules
89 if (llvm::isa<cir::AllocaOp, cir::ConstantOp, cir::FuncOp,
90 cir::GetRuntimeMemberOp, cir::GlobalOp>(op))
91 return mlir::failure();
92
93 const mlir::TypeConverter *typeConverter = getTypeConverter();
94 assert(typeConverter &&
95 "CIRGenericCXXABILoweringPattern requires a type converter");
96 bool operandsAndResultsLegal = typeConverter->isLegal(op);
97 bool regionsLegal =
98 std::all_of(op->getRegions().begin(), op->getRegions().end(),
99 [typeConverter](mlir::Region &region) {
100 return typeConverter->isLegal(&region);
101 });
102 if (operandsAndResultsLegal && regionsLegal) {
103 // The operation does not have any CXXABI-dependent operands or results,
104 // the match fails.
105 return mlir::failure();
106 }
107
108 assert(op->getNumRegions() == 0 && "CIRGenericCXXABILoweringPattern cannot "
109 "deal with operations with regions");
110
111 mlir::OperationState loweredOpState(op->getLoc(), op->getName());
112 loweredOpState.addOperands(operands);
113 loweredOpState.addAttributes(op->getAttrs());
114 loweredOpState.addSuccessors(op->getSuccessors());
115
116 // Lower all result types
117 llvm::SmallVector<mlir::Type> loweredResultTypes;
118 loweredResultTypes.reserve(op->getNumResults());
119 for (mlir::Type result : op->getResultTypes())
120 loweredResultTypes.push_back(typeConverter->convertType(result));
121 loweredOpState.addTypes(loweredResultTypes);
122
123 // Lower all regions
124 for (mlir::Region &region : op->getRegions()) {
125 mlir::Region *loweredRegion = loweredOpState.addRegion();
126 rewriter.inlineRegionBefore(region, *loweredRegion, loweredRegion->end());
127 if (mlir::failed(
128 rewriter.convertRegionTypes(loweredRegion, *getTypeConverter())))
129 return mlir::failure();
130 }
131
132 // Clone the operation with lowered operand types and result types
133 mlir::Operation *loweredOp = rewriter.create(loweredOpState);
134
135 rewriter.replaceOp(op, loweredOp);
136 return mlir::success();
137 }
138};
139
140} // namespace
141
142mlir::LogicalResult CIRAllocaOpABILowering::matchAndRewrite(
143 cir::AllocaOp op, OpAdaptor adaptor,
144 mlir::ConversionPatternRewriter &rewriter) const {
145 mlir::Type allocaPtrTy = op.getType();
146 mlir::Type allocaTy = op.getAllocaType();
147 mlir::Type loweredAllocaPtrTy = getTypeConverter()->convertType(allocaPtrTy);
148 mlir::Type loweredAllocaTy = getTypeConverter()->convertType(allocaTy);
149
150 cir::AllocaOp loweredOp = cir::AllocaOp::create(
151 rewriter, op.getLoc(), loweredAllocaPtrTy, loweredAllocaTy, op.getName(),
152 op.getAlignmentAttr(), /*dynAllocSize=*/adaptor.getDynAllocSize());
153 loweredOp.setInit(op.getInit());
154 loweredOp.setConstant(op.getConstant());
155 loweredOp.setAnnotationsAttr(op.getAnnotationsAttr());
156
157 rewriter.replaceOp(op, loweredOp);
158 return mlir::success();
159}
160
161mlir::LogicalResult CIRConstantOpABILowering::matchAndRewrite(
162 cir::ConstantOp op, OpAdaptor adaptor,
163 mlir::ConversionPatternRewriter &rewriter) const {
164
165 if (mlir::isa<cir::DataMemberType>(op.getType())) {
166 auto dataMember = mlir::cast<cir::DataMemberAttr>(op.getValue());
167 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
168 mlir::TypedAttr abiValue = lowerModule->getCXXABI().lowerDataMemberConstant(
169 dataMember, layout, *getTypeConverter());
170 rewriter.replaceOpWithNewOp<ConstantOp>(op, abiValue);
171 return mlir::success();
172 }
173
174 llvm_unreachable("constant operand is not an CXXABI-dependent type");
175}
176
177mlir::LogicalResult CIRFuncOpABILowering::matchAndRewrite(
178 cir::FuncOp op, OpAdaptor adaptor,
179 mlir::ConversionPatternRewriter &rewriter) const {
180 cir::FuncType opFuncType = op.getFunctionType();
181 mlir::TypeConverter::SignatureConversion signatureConversion(
182 opFuncType.getNumInputs());
183
184 for (const auto &[i, argType] : llvm::enumerate(opFuncType.getInputs())) {
185 mlir::Type loweredArgType = getTypeConverter()->convertType(argType);
186 if (!loweredArgType)
187 return mlir::failure();
188 signatureConversion.addInputs(i, loweredArgType);
189 }
190
191 mlir::Type loweredResultType =
192 getTypeConverter()->convertType(opFuncType.getReturnType());
193 if (!loweredResultType)
194 return mlir::failure();
195
196 auto loweredFuncType =
197 cir::FuncType::get(signatureConversion.getConvertedTypes(),
198 loweredResultType, /*isVarArg=*/opFuncType.isVarArg());
199
200 // Create a new cir.func operation for the CXXABI-lowered function.
201 cir::FuncOp loweredFuncOp = rewriter.cloneWithoutRegions(op);
202 loweredFuncOp.setFunctionType(loweredFuncType);
203 rewriter.inlineRegionBefore(op.getBody(), loweredFuncOp.getBody(),
204 loweredFuncOp.end());
205 if (mlir::failed(rewriter.convertRegionTypes(
206 &loweredFuncOp.getBody(), *getTypeConverter(), &signatureConversion)))
207 return mlir::failure();
208
209 rewriter.eraseOp(op);
210 return mlir::success();
211}
212
213mlir::LogicalResult CIRGlobalOpABILowering::matchAndRewrite(
214 cir::GlobalOp op, OpAdaptor adaptor,
215 mlir::ConversionPatternRewriter &rewriter) const {
216 mlir::Type ty = op.getSymType();
217 mlir::Type loweredTy = getTypeConverter()->convertType(ty);
218 if (!loweredTy)
219 return mlir::failure();
220
221 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
222
223 mlir::Attribute loweredInit;
224 if (mlir::isa<cir::DataMemberType>(ty)) {
225 cir::DataMemberAttr init =
226 mlir::cast_if_present<cir::DataMemberAttr>(op.getInitialValueAttr());
227 loweredInit = lowerModule->getCXXABI().lowerDataMemberConstant(
228 init, layout, *getTypeConverter());
229 } else {
230 llvm_unreachable(
231 "inputs to cir.global in ABI lowering must be data member or method");
232 }
233
234 auto newOp = mlir::cast<cir::GlobalOp>(rewriter.clone(*op.getOperation()));
235 newOp.setInitialValueAttr(loweredInit);
236 newOp.setSymType(loweredTy);
237 rewriter.replaceOp(op, newOp);
238 return mlir::success();
239}
240
241mlir::LogicalResult CIRGetRuntimeMemberOpABILowering::matchAndRewrite(
242 cir::GetRuntimeMemberOp op, OpAdaptor adaptor,
243 mlir::ConversionPatternRewriter &rewriter) const {
244 mlir::Type resTy = getTypeConverter()->convertType(op.getType());
245 mlir::Operation *newOp = lowerModule->getCXXABI().lowerGetRuntimeMember(
246 op, resTy, adaptor.getAddr(), adaptor.getMember(), rewriter);
247 rewriter.replaceOp(op, newOp);
248 return mlir::success();
249}
250
251// Prepare the type converter for the CXXABI lowering pass.
252// Even though this is a CIR-to-CIR pass, we are eliminating some CIR types.
253static void prepareCXXABITypeConverter(mlir::TypeConverter &converter,
254 mlir::DataLayout &dataLayout,
255 cir::LowerModule &lowerModule) {
256 converter.addConversion([&](mlir::Type type) -> mlir::Type { return type; });
257 // This is necessary in order to convert CIR pointer types that are pointing
258 // to CIR types that we are lowering in this pass.
259 converter.addConversion([&](cir::PointerType type) -> mlir::Type {
260 mlir::Type loweredPointeeType = converter.convertType(type.getPointee());
261 if (!loweredPointeeType)
262 return {};
263 return cir::PointerType::get(type.getContext(), loweredPointeeType,
264 type.getAddrSpace());
265 });
266 converter.addConversion([&](cir::DataMemberType type) -> mlir::Type {
267 mlir::Type abiType =
268 lowerModule.getCXXABI().lowerDataMemberType(type, converter);
269 return converter.convertType(abiType);
270 });
271 // This is necessary in order to convert CIR function types that have argument
272 // or return types that use CIR types that we are lowering in this pass.
273 converter.addConversion([&](cir::FuncType type) -> mlir::Type {
274 llvm::SmallVector<mlir::Type> loweredInputTypes;
275 loweredInputTypes.reserve(type.getNumInputs());
276 if (mlir::failed(
277 converter.convertTypes(type.getInputs(), loweredInputTypes)))
278 return {};
279
280 mlir::Type loweredReturnType = converter.convertType(type.getReturnType());
281 if (!loweredReturnType)
282 return {};
283
284 return cir::FuncType::get(loweredInputTypes, loweredReturnType,
285 /*isVarArg=*/type.getVarArg());
286 });
287}
288
289static void
290populateCXXABIConversionTarget(mlir::ConversionTarget &target,
291 const mlir::TypeConverter &typeConverter) {
292 target.addLegalOp<mlir::ModuleOp>();
293
294 // The ABI lowering pass is interested in CIR operations with operands or
295 // results of CXXABI-dependent types, or CIR operations with regions whose
296 // block arguments are of CXXABI-dependent types.
297 target.addDynamicallyLegalDialect<cir::CIRDialect>(
298 [&typeConverter](mlir::Operation *op) {
299 if (!typeConverter.isLegal(op))
300 return false;
301 return std::all_of(op->getRegions().begin(), op->getRegions().end(),
302 [&typeConverter](mlir::Region &region) {
303 return typeConverter.isLegal(&region);
304 });
305 });
306
307 // Some CIR ops needs special checking for legality
308 target.addDynamicallyLegalOp<cir::FuncOp>([&typeConverter](cir::FuncOp op) {
309 return typeConverter.isLegal(op.getFunctionType());
310 });
311 target.addDynamicallyLegalOp<cir::GlobalOp>(
312 [&typeConverter](cir::GlobalOp op) {
313 return typeConverter.isLegal(op.getSymType());
314 });
315}
316
317//===----------------------------------------------------------------------===//
318// The Pass
319//===----------------------------------------------------------------------===//
320
321void CXXABILoweringPass::runOnOperation() {
322 auto module = mlir::cast<mlir::ModuleOp>(getOperation());
323 mlir::MLIRContext *ctx = module.getContext();
324
325 // If the triple is not present, e.g. CIR modules parsed from text, we
326 // cannot init LowerModule properly.
328 // If no target triple is available, skip the ABI lowering pass.
329 if (!module->hasAttr(cir::CIRDialect::getTripleAttrName()))
330 return;
331
332 mlir::PatternRewriter rewriter(ctx);
333 std::unique_ptr<cir::LowerModule> lowerModule =
334 cir::createLowerModule(module, rewriter);
335
336 mlir::DataLayout dataLayout(module);
337 mlir::TypeConverter typeConverter;
338 prepareCXXABITypeConverter(typeConverter, dataLayout, *lowerModule);
339
340 mlir::RewritePatternSet patterns(ctx);
341 patterns.add<CIRGenericCXXABILoweringPattern>(patterns.getContext(),
342 typeConverter);
343 patterns.add<
344 // clang-format off
345 CIRAllocaOpABILowering,
346 CIRConstantOpABILowering,
347 CIRFuncOpABILowering,
348 CIRGetRuntimeMemberOpABILowering,
349 CIRGlobalOpABILowering
350 // clang-format on
351 >(patterns.getContext(), typeConverter, dataLayout, *lowerModule);
352
353 mlir::ConversionTarget target(*ctx);
354 populateCXXABIConversionTarget(target, typeConverter);
355
356 if (failed(mlir::applyPartialConversion(module, target, std::move(patterns))))
357 signalPassFailure();
358}
359
360std::unique_ptr<Pass> mlir::createCXXABILoweringPass() {
361 return std::make_unique<CXXABILoweringPass>();
362}
static void populateCXXABIConversionTarget(mlir::ConversionTarget &target, const mlir::TypeConverter &typeConverter)
static void prepareCXXABITypeConverter(mlir::TypeConverter &converter, mlir::DataLayout &dataLayout, cir::LowerModule &lowerModule)
#define CIR_CXXABI_LOWERING_PATTERN(name, operation)
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:45
Defines the clang::TargetInfo interface.
std::unique_ptr< LowerModule > createLowerModule(mlir::ModuleOp module, mlir::PatternRewriter &rewriter)
std::unique_ptr< Pass > createCXXABILoweringPass()
static bool makeTripleAlwaysPresent()