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(
151 llvm::StringRef OpName, llvm::StringRef PatternName, bool IsRecursive,
152 llvm::StringRef ExtraDecl, const Record *CustomCtorRec,
153 llvm::StringRef LLVMOp, llvm::StringRef ConstrainedLLVMIntrinsic,
154 bool ConstrainedHasRoundingMode, bool HasZeroResult) {
155 std::optional<CustomLoweringCtor> CustomCtor =
156 parseCustomLoweringCtor(R: CustomCtorRec);
157 std::string CodeBuffer;
158 llvm::raw_string_ostream Code(CodeBuffer);
159
160 Code << "class " << PatternName
161 << " : public mlir::OpConversionPattern<cir::" << OpName << "> {\n";
162 Code << " [[maybe_unused]] mlir::DataLayout const &dataLayout;\n";
163 Code << " [[maybe_unused]] mlir::SymbolTableCollection &symbolTables;\n";
164
165 if (CustomCtor) {
166 for (const CustomLoweringCtor::Param &P : CustomCtor->Params)
167 Code << " " << P.Type << " " << P.Name << ";\n";
168 }
169
170 Code << "\n";
171
172 Code << "public:\n";
173 Code << " using mlir::OpConversionPattern<cir::" << OpName
174 << ">::OpConversionPattern;\n";
175
176 // Constructor
177 Code << " " << PatternName
178 << "(const mlir::TypeConverter &typeConverter, "
179 "mlir::MLIRContext *context, const mlir::DataLayout &dataLayout, "
180 "mlir::SymbolTableCollection &symbolTables";
181
182 if (CustomCtor)
183 emitCustomParamList(Code, Params: CustomCtor->Params);
184
185 Code << ")\n";
186
187 Code << " : OpConversionPattern<cir::" << OpName
188 << ">(typeConverter, context), dataLayout(dataLayout), "
189 "symbolTables(symbolTables)";
190
191 if (CustomCtor)
192 emitCustomInitList(Code, Params: CustomCtor->Params);
193
194 Code << " {\n";
195
196 if (IsRecursive)
197 Code << " setHasBoundedRewriteRecursion();\n";
198
199 Code << " }\n\n";
200
201 if (!ConstrainedLLVMIntrinsic.empty()) {
202 // Generate a matchAndRewrite body for a floating-point operation that
203 // carries an optional `fenv` attribute. The shared `lowerConstrainableFPOp`
204 // helper lowers to the plain `llvmOp` when no `fenv` attribute is present
205 // and to a call to the constrained floating-point intrinsic when one is.
206 assert(!LLVMOp.empty() && "constrainedLLVMIntrinsic requires llvmOp");
207 Code
208 << " mlir::LogicalResult matchAndRewrite(cir::" << OpName
209 << " op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) "
210 "const override {\n";
211 Code << " return lowerConstrainableFPOp<mlir::LLVM::" << LLVMOp
212 << ">(\n";
213 Code << " op, adaptor.getOperands(), op.getFenvAttr(),\n";
214 Code << " *getTypeConverter(), rewriter, \""
215 << ConstrainedLLVMIntrinsic << "\",\n";
216 Code << " /*hasRoundingMode=*/"
217 << (ConstrainedHasRoundingMode ? "true" : "false") << ");\n";
218 Code << " }\n";
219 } else if (!LLVMOp.empty()) {
220 // Generate the matchAndRewrite body automatically.
221 Code
222 << " mlir::LogicalResult matchAndRewrite(cir::" << OpName
223 << " op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) "
224 "const override {\n";
225 std::string Properties =
226 "cir::getDefaultProperties<mlir::LLVM::" + LLVMOp.str() +
227 ">(op.getContext())";
228 if (HasZeroResult) {
229 Code << " rewriter.replaceOpWithNewOp<mlir::LLVM::" << LLVMOp
230 << ">(op, mlir::TypeRange{}, adaptor.getOperands(), " << Properties
231 << ");\n";
232 } else {
233 Code << " mlir::Type resTy = "
234 "typeConverter->convertType(op.getType());\n";
235 Code << " rewriter.replaceOpWithNewOp<mlir::LLVM::" << LLVMOp
236 << ">(op, mlir::TypeRange{resTy}, adaptor.getOperands(), "
237 << Properties << ");\n";
238 }
239 Code << " return mlir::success();\n";
240 Code << " }\n";
241 } else {
242 Code
243 << " mlir::LogicalResult matchAndRewrite(cir::" << OpName
244 << " op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) "
245 "const override;\n";
246 }
247
248 if (!ExtraDecl.empty()) {
249 Code << "\nprivate:\n";
250 Code << ExtraDecl << "\n";
251 }
252
253 Code << "};\n";
254
255 LLVMLoweringPatterns.push_back(x: std::move(CodeBuffer));
256}
257
258void Generate(const Record *OpRecord) {
259 std::string OpName = GetOpCppClassName(OpRecord);
260
261 if (OpRecord->getValueAsBit(FieldName: "hasCXXABILowering")) {
262 std::string PatternName = GetOpABILoweringPatternName(OpName);
263 GenerateABILoweringPattern(OpName, PatternName);
264 CXXABILoweringPatternsList.push_back(x: std::move(PatternName));
265 }
266
267 if (OpRecord->getValueAsBit(FieldName: "hasLLVMLowering")) {
268 std::string PatternName = GetOpLLVMLoweringPatternName(OpName);
269 bool IsRecursive = OpRecord->getValueAsBit(FieldName: "isLLVMLoweringRecursive");
270 const Record *CustomCtor =
271 OpRecord->getValueAsOptionalDef(FieldName: "customLLVMLoweringConstructorDecl");
272 llvm::StringRef ExtraDecl =
273 OpRecord->getValueAsString(FieldName: "extraLLVMLoweringPatternDecl");
274
275 llvm::StringRef LLVMOp = OpRecord->getValueAsString(FieldName: "llvmOp");
276 llvm::StringRef ConstrainedLLVMIntrinsic =
277 OpRecord->getValueAsString(FieldName: "constrainedLLVMIntrinsic");
278 bool ConstrainedHasRoundingMode =
279 OpRecord->getValueAsBit(FieldName: "constrainedLLVMIntrinsicHasRoundingMode");
280
281 if (!LLVMOp.empty() && CustomCtor)
282 PrintFatalError(ErrorLoc: OpRecord->getLoc(),
283 Msg: "op '" + OpName +
284 "' has both llvmOp and a custom lowering "
285 "constructor, which is not supported");
286
287 if (!ConstrainedLLVMIntrinsic.empty() && LLVMOp.empty())
288 PrintFatalError(ErrorLoc: OpRecord->getLoc(),
289 Msg: "op '" + OpName +
290 "' has constrainedLLVMIntrinsic set but no llvmOp, "
291 "which is not supported");
292
293 const DagInit *ResultsDag = OpRecord->getValueAsDag(FieldName: "results");
294 bool IsZeroResult = ResultsDag->getNumArgs() == 0;
295 GenerateLLVMLoweringPattern(OpName, PatternName, IsRecursive, ExtraDecl,
296 CustomCtorRec: CustomCtor, LLVMOp, ConstrainedLLVMIntrinsic,
297 ConstrainedHasRoundingMode, HasZeroResult: IsZeroResult);
298 // Only automatically register patterns that use the default constructor.
299 // Patterns with a custom constructor must be manually registered by the
300 // lowering pass.
301 if (!CustomCtor)
302 LLVMLoweringPatternsList.push_back(x: std::move(PatternName));
303 }
304}
305
306void GenerateCIREnumAttrs(const Record *Record) {
307 // EnumAttr is in a separate hierarchy, so we have to set these separately, as
308 // they never have an 'illegal' CXXABI type in them.
309 CXXABILoweringAttrAlwaysLegal.push_back(x: GetAttrCppClassRef(AttrRecord: Record));
310}
311
312void GenerateAttrToValueVisitor(const Record *Rec) {
313 std::string CppClassRef = GetAttrCppClassRef(AttrRecord: Rec);
314
315 std::string CodeBuffer;
316 llvm::raw_string_ostream Code(CodeBuffer);
317 Code << " mlir::Value visitCirAttr(" << CppClassRef << " attr);";
318 CIRAttrToValueVisitorDecls.push_back(x: std::move(CodeBuffer));
319 CIRAttrToValueVisitorCaseTypes.push_back(x: std::move(CppClassRef));
320}
321
322void GenerateAttrToValueVisitFunc() {
323 std::string CodeBuffer;
324 llvm::raw_string_ostream Code(CodeBuffer);
325 Code << " mlir::Value visit(mlir::Attribute attr) {\n"
326 << " return llvm::TypeSwitch<mlir::Attribute, mlir::Value>(attr)\n"
327 << " .Case<\n "
328 << llvm::join(R&: CIRAttrToValueVisitorCaseTypes, Separator: ",\n ")
329 << ">(\n"
330 << " [&](auto attrT) { return visitCirAttr(attrT); })\n"
331 << " .Default([this](mlir::Attribute attr) {\n"
332 << " mlir::emitError(parentOp->getLoc(), \"unsupported CIR "
333 "attribute in LLVM constant lowering\")\n"
334 << " << attr;\n"
335 << " return mlir::Value();\n"
336 << " });\n"
337 << " }\n";
338 CIRAttrToValueVisitFunc = std::move(CodeBuffer);
339}
340
341void GenerateCIRAttrs(const Record *Record) {
342 if (!Record->getValueAsBit(FieldName: "canHaveIllegalCXXABIType"))
343 CXXABILoweringAttrAlwaysLegal.push_back(x: GetAttrCppClassRef(AttrRecord: Record));
344 if (Record->getValueAsBit(FieldName: "hasAttrToValueLowering"))
345 GenerateAttrToValueVisitor(Rec: Record);
346}
347} // namespace
348
349void clang::EmitCIRLowering(const llvm::RecordKeeper &RK,
350 llvm::raw_ostream &OS) {
351 emitSourceFileHeader(Desc: "Lowering patterns for CIR operations", OS);
352 for (const auto *OpRecord : RK.getAllDerivedDefinitions(ClassName: "CIR_Op"))
353 Generate(OpRecord);
354 for (const auto *OpRecord : RK.getAllDerivedDefinitions(ClassName: "EnumAttr"))
355 GenerateCIREnumAttrs(Record: OpRecord);
356 for (const auto *OpRecord : RK.getAllDerivedDefinitions(ClassName: "CIR_Attr"))
357 GenerateCIRAttrs(Record: OpRecord);
358 GenerateAttrToValueVisitFunc();
359
360 OS << "#ifdef GET_CIR_ATTR_TO_VALUE_VISITOR_DECLS\n"
361 << CIRAttrToValueVisitFunc << "\n"
362 << llvm::join(R&: CIRAttrToValueVisitorDecls, Separator: "\n") << "\n"
363 << "#endif // GET_CIR_ATTR_TO_VALUE_VISITOR_DECLS\n\n";
364
365 OS << "#ifdef GET_ABI_LOWERING_PATTERNS\n"
366 << llvm::join(R&: CXXABILoweringPatterns, Separator: "\n") << "#endif\n\n";
367 OS << "#ifdef GET_ABI_LOWERING_PATTERNS_LIST\n"
368 << llvm::join(R&: CXXABILoweringPatternsList, Separator: ",\n") << "\n#endif\n\n";
369
370 OS << "#ifdef GET_LLVM_LOWERING_PATTERNS\n"
371 << llvm::join(R&: LLVMLoweringPatterns, Separator: "\n") << "#endif\n\n";
372 OS << "#ifdef GET_LLVM_LOWERING_PATTERNS_LIST\n"
373 << llvm::join(R&: LLVMLoweringPatternsList, Separator: ",\n") << "\n#endif\n\n";
374
375 OS << "#ifdef CXX_ABI_ALWAYS_LEGAL_ATTRS\n"
376 << llvm::join(R&: CXXABILoweringAttrAlwaysLegal, Separator: ",\n") << "\n#endif\n\n";
377}
378