1//===-- CIRLoweringEmitter.cpp - Generate CIR lowering patterns -----------===//
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 TableGen backend emits CIR operation lowering patterns.
10//
11//===----------------------------------------------------------------------===//
12
13#include "TableGenBackends.h"
14#include "llvm/TableGen/Error.h"
15#include "llvm/TableGen/Record.h"
16#include "llvm/TableGen/TableGenBackend.h"
17#include <string>
18#include <utility>
19#include <vector>
20
21using namespace llvm;
22using namespace clang;
23
24namespace {
25std::vector<std::string> CXXABILoweringPatterns;
26std::vector<std::string> CXXABILoweringPatternsList;
27std::vector<std::string> CXXABILoweringAttrAlwaysLegal;
28std::vector<std::string> LLVMLoweringPatterns;
29std::vector<std::string> LLVMLoweringPatternsList;
30std::string CIRAttrToValueVisitFunc;
31std::vector<std::string> CIRAttrToValueVisitorCaseTypes;
32std::vector<std::string> CIRAttrToValueVisitorDecls;
33
34struct CustomLoweringCtor {
35 struct Param {
36 std::string Type;
37 std::string Name;
38 };
39
40 std::vector<Param> Params;
41};
42
43// Adapted from mlir/lib/TableGen/Operator.cpp
44// Returns the C++ class name of the operation, which is the name of the
45// operation with the dialect prefix removed and the first underscore removed.
46// If the operation name starts with an underscore, the underscore is considered
47// part of the class name.
48std::string GetOpCppClassName(const Record *OpRecord) {
49 StringRef Name = OpRecord->getName();
50 StringRef Prefix;
51 StringRef CppClassName;
52 std::tie(args&: Prefix, args&: CppClassName) = Name.split(Separator: '_');
53 if (Prefix.empty()) {
54 // Class name with a leading underscore and without dialect prefix
55 return Name.str();
56 }
57 if (CppClassName.empty()) {
58 // Class name without dialect prefix
59 return Prefix.str();
60 }
61
62 return CppClassName.str();
63}
64
65// Returns the namespace-qualified C++ class name of an attribute. Unlike
66// operations, attributes carry the authoritative name in cppClassName, so the
67// def name is free to differ from it.
68std::string GetAttrCppClassRef(const Record *AttrRecord) {
69 StringRef Ns =
70 AttrRecord->getValueAsDef(FieldName: "dialect")->getValueAsString(FieldName: "cppNamespace");
71 Ns.consume_front(Prefix: "::");
72 std::string CppClassRef = Ns.str();
73 CppClassRef += "::";
74 CppClassRef += AttrRecord->getValueAsString(FieldName: "cppClassName");
75 return CppClassRef;
76}
77
78std::string GetOpABILoweringPatternName(llvm::StringRef OpName) {
79 std::string Name = "CIR";
80 Name += OpName;
81 Name += "ABILowering";
82 return Name;
83}
84
85std::string GetOpLLVMLoweringPatternName(llvm::StringRef OpName) {
86 std::string Name = "CIRToLLVM";
87 Name += OpName;
88 Name += "Lowering";
89 return Name;
90}
91std::optional<CustomLoweringCtor> parseCustomLoweringCtor(const Record *R) {
92 if (!R)
93 return std::nullopt;
94
95 CustomLoweringCtor Ctor;
96 const DagInit *Args = R->getValueAsDag(FieldName: "dagParams");
97
98 for (const auto &[Arg, Name] : Args->getArgAndNames()) {
99 Ctor.Params.push_back(
100 x: {.Type: Arg->getAsUnquotedString(), .Name: Name->getAsUnquotedString()});
101 }
102
103 return Ctor;
104}
105
106void emitCustomParamList(raw_ostream &Code,
107 ArrayRef<CustomLoweringCtor::Param> Params) {
108 for (const CustomLoweringCtor::Param &Param : Params) {
109 Code << ", ";
110 Code << Param.Type << " " << Param.Name;
111 }
112}
113
114void emitCustomInitList(raw_ostream &Code,
115 ArrayRef<CustomLoweringCtor::Param> Params) {
116 for (const CustomLoweringCtor::Param &P : Params)
117 Code << ", " << P.Name << "(" << P.Name << ")";
118}
119
120void GenerateABILoweringPattern(llvm::StringRef OpName,
121 llvm::StringRef PatternName) {
122 std::string CodeBuffer;
123 llvm::raw_string_ostream Code(CodeBuffer);
124
125 Code << "class " << PatternName
126 << " : public mlir::OpConversionPattern<cir::" << OpName << "> {\n";
127 Code << " [[maybe_unused]] mlir::DataLayout *dataLayout;\n";
128 Code << " [[maybe_unused]] cir::LowerModule *lowerModule;\n";
129 Code << "\n";
130
131 Code << "public:\n";
132 Code << " " << PatternName
133 << "(mlir::MLIRContext *context, const mlir::TypeConverter "
134 "&typeConverter, mlir::DataLayout &dataLayout, cir::LowerModule "
135 "&lowerModule)\n";
136 Code << " : OpConversionPattern<cir::" << OpName
137 << ">(typeConverter, context), dataLayout(&dataLayout), "
138 "lowerModule(&lowerModule) {}\n";
139 Code << "\n";
140
141 Code << " mlir::LogicalResult matchAndRewrite(cir::" << OpName
142 << " op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) "
143 "const override;\n";
144
145 Code << "};\n";
146
147 CXXABILoweringPatterns.push_back(x: std::move(CodeBuffer));
148}
149
150void GenerateLLVMLoweringPattern(llvm::StringRef OpName,
151 llvm::StringRef PatternName, bool IsRecursive,
152 llvm::StringRef ExtraDecl,
153 const Record *CustomCtorRec,
154 llvm::StringRef LLVMOp,
155 llvm::StringRef ConstrainedLLVMIntrinsic,
156 bool ConstrainedHasRoundingMode,
157 bool HasFastMathFlags, bool HasZeroResult) {
158 std::optional<CustomLoweringCtor> CustomCtor =
159 parseCustomLoweringCtor(R: CustomCtorRec);
160 std::string CodeBuffer;
161 llvm::raw_string_ostream Code(CodeBuffer);
162
163 Code << "class " << PatternName
164 << " : public mlir::OpConversionPattern<cir::" << OpName << "> {\n";
165 Code << " [[maybe_unused]] mlir::DataLayout const &dataLayout;\n";
166 Code << " [[maybe_unused]] mlir::SymbolTableCollection &symbolTables;\n";
167
168 if (CustomCtor) {
169 for (const CustomLoweringCtor::Param &P : CustomCtor->Params)
170 Code << " " << P.Type << " " << P.Name << ";\n";
171 }
172
173 Code << "\n";
174
175 Code << "public:\n";
176 Code << " using mlir::OpConversionPattern<cir::" << OpName
177 << ">::OpConversionPattern;\n";
178
179 // Constructor
180 Code << " " << PatternName
181 << "(const mlir::TypeConverter &typeConverter, "
182 "mlir::MLIRContext *context, const mlir::DataLayout &dataLayout, "
183 "mlir::SymbolTableCollection &symbolTables";
184
185 if (CustomCtor)
186 emitCustomParamList(Code, Params: CustomCtor->Params);
187
188 Code << ")\n";
189
190 Code << " : OpConversionPattern<cir::" << OpName
191 << ">(typeConverter, context), dataLayout(dataLayout), "
192 "symbolTables(symbolTables)";
193
194 if (CustomCtor)
195 emitCustomInitList(Code, Params: CustomCtor->Params);
196
197 Code << " {\n";
198
199 if (IsRecursive)
200 Code << " setHasBoundedRewriteRecursion();\n";
201
202 Code << " }\n\n";
203
204 if (!ConstrainedLLVMIntrinsic.empty()) {
205 // Generate a matchAndRewrite body for a floating-point operation that
206 // carries an optional `fenv` attribute. The shared `lowerConstrainableFPOp`
207 // helper lowers to the plain `llvmOp` when no `fenv` attribute is present
208 // and to a call to the constrained floating-point intrinsic when one is.
209 assert(!LLVMOp.empty() && "constrainedLLVMIntrinsic requires llvmOp");
210 Code
211 << " mlir::LogicalResult matchAndRewrite(cir::" << OpName
212 << " op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) "
213 "const override {\n";
214 Code << " return lowerConstrainableFPOp<mlir::LLVM::" << LLVMOp
215 << ">(\n";
216 Code << " op, adaptor.getOperands(), op.getFenvAttr(),\n";
217 Code << " "
218 << (HasFastMathFlags ? "op.getFastmathFlagsAttr()"
219 : "cir::FastMathFlagsAttr{}")
220 << ",\n";
221 Code << " *getTypeConverter(), rewriter, \""
222 << ConstrainedLLVMIntrinsic << "\",\n";
223 Code << " /*hasRoundingMode=*/"
224 << (ConstrainedHasRoundingMode ? "true" : "false") << ");\n";
225 Code << " }\n";
226 } else if (!LLVMOp.empty()) {
227 // Generate the matchAndRewrite body automatically.
228 Code
229 << " mlir::LogicalResult matchAndRewrite(cir::" << OpName
230 << " op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) "
231 "const override {\n";
232 std::string Properties =
233 "cir::getDefaultProperties<mlir::LLVM::" + LLVMOp.str() +
234 ">(op.getContext())";
235 if (HasZeroResult) {
236 Code << " rewriter.replaceOpWithNewOp<mlir::LLVM::" << LLVMOp
237 << ">(op, mlir::TypeRange{}, adaptor.getOperands(), " << Properties
238 << ");\n";
239 } else {
240 Code << " mlir::Type resTy = "
241 "typeConverter->convertType(op.getType());\n";
242 Code << " rewriter.replaceOpWithNewOp<mlir::LLVM::" << LLVMOp
243 << ">(op, mlir::TypeRange{resTy}, adaptor.getOperands(), "
244 << Properties << ");\n";
245 }
246 Code << " return mlir::success();\n";
247 Code << " }\n";
248 } else {
249 Code
250 << " mlir::LogicalResult matchAndRewrite(cir::" << OpName
251 << " op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) "
252 "const override;\n";
253 }
254
255 if (!ExtraDecl.empty()) {
256 Code << "\nprivate:\n";
257 Code << ExtraDecl << "\n";
258 }
259
260 Code << "};\n";
261
262 LLVMLoweringPatterns.push_back(x: std::move(CodeBuffer));
263}
264
265void Generate(const Record *OpRecord) {
266 std::string OpName = GetOpCppClassName(OpRecord);
267
268 if (OpRecord->getValueAsBit(FieldName: "hasCXXABILowering")) {
269 std::string PatternName = GetOpABILoweringPatternName(OpName);
270 GenerateABILoweringPattern(OpName, PatternName);
271 CXXABILoweringPatternsList.push_back(x: std::move(PatternName));
272 }
273
274 if (OpRecord->getValueAsBit(FieldName: "hasLLVMLowering")) {
275 std::string PatternName = GetOpLLVMLoweringPatternName(OpName);
276 bool IsRecursive = OpRecord->getValueAsBit(FieldName: "isLLVMLoweringRecursive");
277 const Record *CustomCtor =
278 OpRecord->getValueAsOptionalDef(FieldName: "customLLVMLoweringConstructorDecl");
279 llvm::StringRef ExtraDecl =
280 OpRecord->getValueAsString(FieldName: "extraLLVMLoweringPatternDecl");
281
282 llvm::StringRef LLVMOp = OpRecord->getValueAsString(FieldName: "llvmOp");
283 llvm::StringRef ConstrainedLLVMIntrinsic =
284 OpRecord->getValueAsString(FieldName: "constrainedLLVMIntrinsic");
285 bool ConstrainedHasRoundingMode =
286 OpRecord->getValueAsBit(FieldName: "constrainedLLVMIntrinsicHasRoundingMode");
287 const DagInit *ArgsDag = OpRecord->getValueAsDag(FieldName: "arguments");
288 bool HasFastMathFlags =
289 llvm::any_of(Range: ArgsDag->getArgNames(), P: [](const StringInit *Name) {
290 return Name && Name->getValue() == "fastmath_flags";
291 });
292
293 if (!LLVMOp.empty() && CustomCtor)
294 PrintFatalError(ErrorLoc: OpRecord->getLoc(),
295 Msg: "op '" + OpName +
296 "' has both llvmOp and a custom lowering "
297 "constructor, which is not supported");
298
299 if (!ConstrainedLLVMIntrinsic.empty() && LLVMOp.empty())
300 PrintFatalError(ErrorLoc: OpRecord->getLoc(),
301 Msg: "op '" + OpName +
302 "' has constrainedLLVMIntrinsic set but no llvmOp, "
303 "which is not supported");
304
305 const DagInit *ResultsDag = OpRecord->getValueAsDag(FieldName: "results");
306 bool IsZeroResult = ResultsDag->getNumArgs() == 0;
307 GenerateLLVMLoweringPattern(OpName, PatternName, IsRecursive, ExtraDecl,
308 CustomCtorRec: CustomCtor, LLVMOp, ConstrainedLLVMIntrinsic,
309 ConstrainedHasRoundingMode, HasFastMathFlags,
310 HasZeroResult: IsZeroResult);
311 // Only automatically register patterns that use the default constructor.
312 // Patterns with a custom constructor must be manually registered by the
313 // lowering pass.
314 if (!CustomCtor)
315 LLVMLoweringPatternsList.push_back(x: std::move(PatternName));
316 }
317}
318
319void GenerateCIREnumAttrs(const Record *Record) {
320 // EnumAttr is in a separate hierarchy, so we have to set these separately, as
321 // they never have an 'illegal' CXXABI type in them.
322 CXXABILoweringAttrAlwaysLegal.push_back(x: GetAttrCppClassRef(AttrRecord: Record));
323}
324
325void GenerateAttrToValueVisitor(const Record *Rec) {
326 std::string CppClassRef = GetAttrCppClassRef(AttrRecord: Rec);
327
328 std::string CodeBuffer;
329 llvm::raw_string_ostream Code(CodeBuffer);
330 Code << " mlir::Value visitCirAttr(" << CppClassRef << " attr);";
331 CIRAttrToValueVisitorDecls.push_back(x: std::move(CodeBuffer));
332 CIRAttrToValueVisitorCaseTypes.push_back(x: std::move(CppClassRef));
333}
334
335void GenerateAttrToValueVisitFunc() {
336 std::string CodeBuffer;
337 llvm::raw_string_ostream Code(CodeBuffer);
338 Code << " mlir::Value visit(mlir::Attribute attr) {\n"
339 << " return llvm::TypeSwitch<mlir::Attribute, mlir::Value>(attr)\n"
340 << " .Case<\n "
341 << llvm::join(R&: CIRAttrToValueVisitorCaseTypes, Separator: ",\n ")
342 << ">(\n"
343 << " [&](auto attrT) { return visitCirAttr(attrT); })\n"
344 << " .Default([this](mlir::Attribute attr) {\n"
345 << " mlir::emitError(parentOp->getLoc(), \"unsupported CIR "
346 "attribute in LLVM constant lowering\")\n"
347 << " << attr;\n"
348 << " return mlir::Value();\n"
349 << " });\n"
350 << " }\n";
351 CIRAttrToValueVisitFunc = std::move(CodeBuffer);
352}
353
354void GenerateCIRAttrs(const Record *Record) {
355 if (!Record->getValueAsBit(FieldName: "canHaveIllegalCXXABIType"))
356 CXXABILoweringAttrAlwaysLegal.push_back(x: GetAttrCppClassRef(AttrRecord: Record));
357 if (Record->getValueAsBit(FieldName: "hasAttrToValueLowering"))
358 GenerateAttrToValueVisitor(Rec: Record);
359}
360} // namespace
361
362void clang::EmitCIRLowering(const llvm::RecordKeeper &RK,
363 llvm::raw_ostream &OS) {
364 emitSourceFileHeader(Desc: "Lowering patterns for CIR operations", OS);
365 for (const auto *OpRecord : RK.getAllDerivedDefinitions(ClassName: "CIR_Op"))
366 Generate(OpRecord);
367 for (const auto *OpRecord : RK.getAllDerivedDefinitions(ClassName: "EnumAttr"))
368 GenerateCIREnumAttrs(Record: OpRecord);
369 for (const auto *OpRecord : RK.getAllDerivedDefinitions(ClassName: "CIR_Attr"))
370 GenerateCIRAttrs(Record: OpRecord);
371 GenerateAttrToValueVisitFunc();
372
373 OS << "#ifdef GET_CIR_ATTR_TO_VALUE_VISITOR_DECLS\n"
374 << CIRAttrToValueVisitFunc << "\n"
375 << llvm::join(R&: CIRAttrToValueVisitorDecls, Separator: "\n") << "\n"
376 << "#endif // GET_CIR_ATTR_TO_VALUE_VISITOR_DECLS\n\n";
377
378 OS << "#ifdef GET_ABI_LOWERING_PATTERNS\n"
379 << llvm::join(R&: CXXABILoweringPatterns, Separator: "\n") << "#endif\n\n";
380 OS << "#ifdef GET_ABI_LOWERING_PATTERNS_LIST\n"
381 << llvm::join(R&: CXXABILoweringPatternsList, Separator: ",\n") << "\n#endif\n\n";
382
383 OS << "#ifdef GET_LLVM_LOWERING_PATTERNS\n"
384 << llvm::join(R&: LLVMLoweringPatterns, Separator: "\n") << "#endif\n\n";
385 OS << "#ifdef GET_LLVM_LOWERING_PATTERNS_LIST\n"
386 << llvm::join(R&: LLVMLoweringPatternsList, Separator: ",\n") << "\n#endif\n\n";
387
388 OS << "#ifdef CXX_ABI_ALWAYS_LEGAL_ATTRS\n"
389 << llvm::join(R&: CXXABILoweringAttrAlwaysLegal, Separator: ",\n") << "\n#endif\n\n";
390}
391