clang 24.0.0git
TargetLowering.cpp
Go to the documentation of this file.
1//===- TargetLowering.cpp -------------------------------------------------===//
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// This file implements the cir-target-lowering pass.
10//
11//===----------------------------------------------------------------------===//
12
13#include "RecordTypeConverter.h"
16
17#include "mlir/IR/AttrTypeSubElements.h"
18#include "mlir/IR/PatternMatch.h"
19#include "mlir/Support/LLVM.h"
20#include "mlir/Transforms/DialectConversion.h"
26
27using namespace mlir;
28using namespace cir;
29
30namespace mlir {
31#define GEN_PASS_DEF_TARGETLOWERING
32#include "clang/CIR/Dialect/Passes.h.inc"
33} // namespace mlir
34
35namespace {
36
37struct TargetLoweringPass
38 : public impl::TargetLoweringBase<TargetLoweringPass> {
39 TargetLoweringPass() = default;
40 void runOnOperation() override;
41};
42
43/// Converts LangAddressSpaceAttr → TargetAddressSpaceAttr inside types,
44/// rebuilding only records that reach a language address space.
45class TargetLoweringTypeConverter : public cir::RecordRewritingTypeConverter {
46public:
47 TargetLoweringTypeConverter(mlir::MLIRContext &ctx,
48 const cir::TargetLoweringInfo &targetInfo)
49 : RecordRewritingTypeConverter(ctx), targetInfo(targetInfo) {
50 addConversion([this](cir::PointerType type) -> mlir::Type {
51 mlir::Type pointee = convertType(type.getPointee());
52 if (!pointee)
53 return {};
54 mlir::ptr::MemorySpaceAttrInterface addrSpace = type.getAddrSpace();
55 if (auto langAS =
56 mlir::dyn_cast_if_present<cir::LangAddressSpaceAttr>(addrSpace))
57 addrSpace = lowerAddrSpace(type.getContext(), langAS);
58 return cir::PointerType::get(type.getContext(), pointee, addrSpace);
59 });
60 }
61
62 mlir::ptr::MemorySpaceAttrInterface
63 lowerAddrSpace(mlir::MLIRContext *ctx,
64 cir::LangAddressSpaceAttr langAS) const {
65 unsigned targetAS =
66 targetInfo.getTargetAddrSpaceFromCIRAddrSpace(langAS.getValue());
67 if (targetAS == 0)
68 return {};
69 return cir::TargetAddressSpaceAttr::get(ctx, targetAS);
70 }
71
72 /// Convert the types nested in \p attr, e.g. the type of a constant.
73 mlir::Attribute convertAttr(mlir::Attribute attr) const {
74 return attrReplacer.replace(attr);
75 }
76
77protected:
78 bool typeMayChange(mlir::Type type) const override {
79 auto ptrTy = mlir::dyn_cast<cir::PointerType>(type);
80 return ptrTy && mlir::isa_and_present<cir::LangAddressSpaceAttr>(
81 ptrTy.getAddrSpace());
82 }
83
84private:
85 const cir::TargetLoweringInfo &targetInfo;
86 mutable mlir::AttrTypeReplacer attrReplacer = [this] {
87 mlir::AttrTypeReplacer replacer;
88 replacer.addReplacement(
89 [this](mlir::Type type)
90 -> std::optional<std::pair<mlir::Type, mlir::WalkResult>> {
91 mlir::Type converted = convertType(type);
92 if (!converted)
93 return std::nullopt;
94 // Already converted; don't walk into it.
95 return std::make_pair(converted, mlir::WalkResult::skip());
96 });
97 return replacer;
98 }();
99};
100
101/// A generic target lowering pattern that matches any CIR op whose operand or
102/// result types need address space conversion. Clones the op with converted
103/// types.
104class CIRGenericTargetLoweringPattern : public mlir::ConversionPattern {
105public:
106 CIRGenericTargetLoweringPattern(
107 mlir::MLIRContext *context,
108 const TargetLoweringTypeConverter &typeConverter)
109 : mlir::ConversionPattern(typeConverter, MatchAnyOpTypeTag(),
110 /*benefit=*/1, context) {}
111
112 mlir::LogicalResult
113 matchAndRewrite(mlir::Operation *op, llvm::ArrayRef<mlir::Value> operands,
114 mlir::ConversionPatternRewriter &rewriter) const override {
115 // Do not match on operations that have dedicated lowering patterns.
116 if (llvm::isa<cir::FuncOp, cir::GlobalOp>(op))
117 return mlir::failure();
118
119 const mlir::TypeConverter *typeConverter = getTypeConverter();
120 assert(typeConverter &&
121 "CIRGenericTargetLoweringPattern requires a type converter");
122 bool operandsAndResultsLegal = typeConverter->isLegal(op);
123 bool regionsLegal =
124 std::all_of(op->getRegions().begin(), op->getRegions().end(),
125 [typeConverter](mlir::Region &region) {
126 return typeConverter->isLegal(&region);
127 });
128 if (operandsAndResultsLegal && regionsLegal)
129 return mlir::failure();
130
131 assert(op->getNumRegions() == 0 && "CIRGenericTargetLoweringPattern cannot "
132 "deal with operations with regions");
133
134 mlir::OperationState loweredOpState(op->getLoc(), op->getName());
135 loweredOpState.addOperands(operands);
136
137 // Preserve auxiliary metadata verbatim.
138 loweredOpState.propertiesAttr = op->getPropertiesAsAttribute();
139 loweredOpState.addAttributes(op->getDiscardableAttrDictionary().getValue());
140
141 loweredOpState.addSuccessors(op->getSuccessors());
142
143 llvm::SmallVector<mlir::Type> loweredResultTypes;
144 loweredResultTypes.reserve(op->getNumResults());
145 for (mlir::Type result : op->getResultTypes())
146 loweredResultTypes.push_back(typeConverter->convertType(result));
147 loweredOpState.addTypes(loweredResultTypes);
148
149 // Convert all regions before moving any blocks.
150 for (mlir::Region &region : op->getRegions()) {
151 if (mlir::failed(
152 rewriter.convertRegionTypes(&region, *getTypeConverter())))
153 return mlir::failure();
154 }
155 for (mlir::Region &region : op->getRegions()) {
156 mlir::Region *loweredRegion = loweredOpState.addRegion();
157 rewriter.inlineRegionBefore(region, *loweredRegion, loweredRegion->end());
158 }
159
160 mlir::Operation *loweredOp = rewriter.create(loweredOpState);
161 // Keep inherent attributes (e.g. allocaType, constants) in sync with the
162 // converted types.
163 const auto *tlTypeConverter =
164 static_cast<const TargetLoweringTypeConverter *>(typeConverter);
165 loweredOp->getName().walkInherentAttrs(
166 loweredOp, [&](llvm::StringRef, mlir::Attribute &attr) {
167 attr = tlTypeConverter->convertAttr(attr);
168 });
169 rewriter.replaceOp(op, loweredOp);
170 return mlir::success();
171 }
172};
173
174/// Pattern to lower GlobalOp address space attributes. GlobalOp carries
175/// addr_space as a standalone attribute (not inside a type), so the
176/// TypeConverter won't reach it automatically.
177class CIRGlobalOpTargetLowering
178 : public mlir::OpConversionPattern<cir::GlobalOp> {
179public:
180 CIRGlobalOpTargetLowering(mlir::MLIRContext *context,
181 const TargetLoweringTypeConverter &typeConverter)
182 : mlir::OpConversionPattern<cir::GlobalOp>(typeConverter, context,
183 /*benefit=*/1) {}
184
185 mlir::LogicalResult
186 matchAndRewrite(cir::GlobalOp op, OpAdaptor adaptor,
187 mlir::ConversionPatternRewriter &rewriter) const override {
188 const auto *typeConverter =
189 static_cast<const TargetLoweringTypeConverter *>(getTypeConverter());
190 mlir::Type loweredSymTy = typeConverter->convertType(op.getSymType());
191 if (!loweredSymTy)
192 return mlir::failure();
193
194 // Convert the addr_space attribute.
195 mlir::ptr::MemorySpaceAttrInterface addrSpace = op.getAddrSpaceAttr();
196 if (auto langAS =
197 mlir::dyn_cast_if_present<cir::LangAddressSpaceAttr>(addrSpace))
198 addrSpace = typeConverter->lowerAddrSpace(op.getContext(), langAS);
199
200 // Only rewrite if something actually changed.
201 if (loweredSymTy == op.getSymType() && addrSpace == op.getAddrSpaceAttr())
202 return mlir::failure();
203
204 auto newOp = mlir::cast<cir::GlobalOp>(rewriter.clone(*op.getOperation()));
205 newOp.setSymType(loweredSymTy);
206 newOp.setAddrSpaceAttr(addrSpace);
207 if (mlir::Attribute init = newOp.getInitialValueAttr())
208 newOp.setInitialValueAttr(typeConverter->convertAttr(init));
209 rewriter.replaceOp(op, newOp);
210 return mlir::success();
211 }
212};
213
214/// Pattern to lower FuncOp types that contain address spaces.
215class CIRFuncOpTargetLowering : public mlir::OpConversionPattern<cir::FuncOp> {
216public:
217 using mlir::OpConversionPattern<cir::FuncOp>::OpConversionPattern;
218
219 mlir::LogicalResult
220 matchAndRewrite(cir::FuncOp op, OpAdaptor adaptor,
221 mlir::ConversionPatternRewriter &rewriter) const override {
222 cir::FuncType opFuncType = op.getFunctionType();
223 mlir::TypeConverter::SignatureConversion signatureConversion(
224 opFuncType.getNumInputs());
225
226 for (const auto &[i, argType] : llvm::enumerate(opFuncType.getInputs())) {
227 mlir::Type loweredArgType = getTypeConverter()->convertType(argType);
228 if (!loweredArgType)
229 return mlir::failure();
230 signatureConversion.addInputs(i, loweredArgType);
231 }
232
233 mlir::Type loweredReturnType =
234 getTypeConverter()->convertType(opFuncType.getReturnType());
235 if (!loweredReturnType)
236 return mlir::failure();
237
238 auto loweredFuncType = cir::FuncType::get(
239 signatureConversion.getConvertedTypes(), loweredReturnType,
240 /*isVarArg=*/opFuncType.getVarArg());
241
242 // Nothing changed, skip.
243 if (loweredFuncType == opFuncType)
244 return mlir::failure();
245
246 // Convert the body types before moving it.
247 if (mlir::failed(rewriter.convertRegionTypes(
248 &op.getBody(), *getTypeConverter(), &signatureConversion)))
249 return mlir::failure();
250
251 cir::FuncOp loweredFuncOp = rewriter.cloneWithoutRegions(op);
252 loweredFuncOp.setFunctionType(loweredFuncType);
253 rewriter.inlineRegionBefore(op.getBody(), loweredFuncOp.getBody(),
254 loweredFuncOp.end());
255
256 rewriter.eraseOp(op);
257 return mlir::success();
258 }
259};
260
261} // namespace
262
263static void convertSyncScopeIfPresent(mlir::Operation *op,
264 cir::LowerModule &lowerModule) {
265 auto syncScopeAttr =
266 mlir::cast_if_present<cir::SyncScopeKindAttr>(op->getAttr("sync_scope"));
267 if (syncScopeAttr) {
268 cir::SyncScopeKind convertedSyncScope =
270 syncScopeAttr.getValue());
271 op->setAttr("sync_scope", cir::SyncScopeKindAttr::get(op->getContext(),
272 convertedSyncScope));
273 }
274}
275
276static void
277populateTargetLoweringConversionTarget(mlir::ConversionTarget &target,
278 const mlir::TypeConverter &tc) {
279 target.addLegalOp<mlir::ModuleOp>();
280
281 target.addDynamicallyLegalDialect<cir::CIRDialect>(
282 [&tc](mlir::Operation *op) {
283 if (!tc.isLegal(op))
284 return false;
285 return std::all_of(
286 op->getRegions().begin(), op->getRegions().end(),
287 [&tc](mlir::Region &region) { return tc.isLegal(&region); });
288 });
289
290 target.addDynamicallyLegalOp<cir::FuncOp>(
291 [&tc](cir::FuncOp op) { return tc.isLegal(op.getFunctionType()); });
292
293 target.addDynamicallyLegalOp<cir::GlobalOp>([&tc](cir::GlobalOp op) {
294 if (!tc.isLegal(op.getSymType()))
295 return false;
296 return !mlir::isa_and_present<cir::LangAddressSpaceAttr>(
297 op.getAddrSpaceAttr());
298 });
299}
300
301void TargetLoweringPass::runOnOperation() {
302 auto mod = mlir::cast<mlir::ModuleOp>(getOperation());
303 std::unique_ptr<cir::LowerModule> lowerModule = cir::createLowerModule(mod);
304 // If lower module is not available, skip the target lowering pass.
305 if (!lowerModule) {
306 mod.emitWarning("Cannot create a CIR lower module, skipping the ")
307 << getName() << " pass";
308 return;
309 }
310
311 const auto &targetInfo = lowerModule->getTargetLoweringInfo();
312
313 mod->walk([&](mlir::Operation *op) {
314 if (mlir::isa<cir::LoadOp, cir::StoreOp, cir::AtomicXchgOp,
315 cir::AtomicCmpXchgOp, cir::AtomicFetchOp>(op))
316 convertSyncScopeIfPresent(op, *lowerModule);
317 });
318
319 // Address space conversion: LangAddressSpaceAttr → TargetAddressSpaceAttr.
320 TargetLoweringTypeConverter typeConverter(*mod.getContext(), targetInfo);
321
322 mlir::RewritePatternSet patterns(mod.getContext());
323 patterns.add<CIRGlobalOpTargetLowering>(mod.getContext(), typeConverter);
324 patterns.add<CIRFuncOpTargetLowering>(typeConverter, mod.getContext());
325 patterns.add<CIRGenericTargetLoweringPattern>(mod.getContext(),
326 typeConverter);
327
328 mlir::ConversionTarget target(*mod.getContext());
329 populateTargetLoweringConversionTarget(target, typeConverter);
330
331 llvm::SmallVector<mlir::Operation *> ops;
332 ops.push_back(mod);
333 cir::collectUnreachable(mod, ops);
334
335 // One-shot conversion (no pattern rollback): rewrites materialize
336 // immediately, skipping the IRRewrite/value-mapping bookkeeping.
337 mlir::ConversionConfig config;
338 config.allowPatternRollback = false;
339 if (failed(mlir::applyPartialConversion(ops, target, std::move(patterns),
340 config)))
341 signalPassFailure();
342
343 typeConverter.restoreRecordTypeNames();
344}
345
346std::unique_ptr<Pass> mlir::createTargetLoweringPass() {
347 return std::make_unique<TargetLoweringPass>();
348}
static void populateTargetLoweringConversionTarget(mlir::ConversionTarget &target, const mlir::TypeConverter &tc)
static void convertSyncScopeIfPresent(mlir::Operation *op, cir::LowerModule &lowerModule)
const TargetLoweringInfo & getTargetLoweringInfo()
A type converter that converts the types nested in pointers, arrays, functions and records.
virtual cir::SyncScopeKind convertSyncScope(cir::SyncScopeKind syncScope) const
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
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
StringRef getName(const HeaderType T)
Definition HeaderFile.h:38
std::unique_ptr< Pass > createTargetLoweringPass()