1 | //===- LLVMIRConversionGen.cpp - MLIR LLVM IR builder generator -----------===// |
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 uses tablegen definitions of the LLVM IR Dialect operations to |
10 | // generate the code building the LLVM IR from it. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "mlir/Support/LogicalResult.h" |
15 | #include "mlir/TableGen/Argument.h" |
16 | #include "mlir/TableGen/Attribute.h" |
17 | #include "mlir/TableGen/GenInfo.h" |
18 | #include "mlir/TableGen/Operator.h" |
19 | |
20 | #include "llvm/ADT/Sequence.h" |
21 | #include "llvm/ADT/StringExtras.h" |
22 | #include "llvm/ADT/Twine.h" |
23 | #include "llvm/Support/FormatVariadic.h" |
24 | #include "llvm/Support/raw_ostream.h" |
25 | #include "llvm/TableGen/Error.h" |
26 | #include "llvm/TableGen/Record.h" |
27 | #include "llvm/TableGen/TableGenBackend.h" |
28 | |
29 | using namespace llvm; |
30 | using namespace mlir; |
31 | |
32 | static LogicalResult emitError(const Record &record, const Twine &message) { |
33 | PrintError(Rec: &record, Msg: message); |
34 | return failure(); |
35 | } |
36 | |
37 | namespace { |
38 | // Helper structure to return a position of the substring in a string. |
39 | struct StringLoc { |
40 | size_t pos; |
41 | size_t length; |
42 | |
43 | // Take a substring identified by this location in the given string. |
44 | StringRef in(StringRef str) const { return str.substr(Start: pos, N: length); } |
45 | |
46 | // A location is invalid if its position is outside the string. |
47 | explicit operator bool() { return pos != std::string::npos; } |
48 | }; |
49 | } // namespace |
50 | |
51 | // Find the next TableGen variable in the given pattern. These variables start |
52 | // with a `$` character and can contain alphanumeric characters or underscores. |
53 | // Return the position of the variable in the pattern and its length, including |
54 | // the `$` character. The escape syntax `$$` is also detected and returned. |
55 | static StringLoc findNextVariable(StringRef str) { |
56 | size_t startPos = str.find(C: '$'); |
57 | if (startPos == std::string::npos) |
58 | return {.pos: startPos, .length: 0}; |
59 | |
60 | // If we see "$$", return immediately. |
61 | if (startPos != str.size() - 1 && str[startPos + 1] == '$') |
62 | return {.pos: startPos, .length: 2}; |
63 | |
64 | // Otherwise, the symbol spans until the first character that is not |
65 | // alphanumeric or '_'. |
66 | size_t endPos = str.find_if_not(F: [](char c) { return isAlnum(C: c) || c == '_'; }, |
67 | From: startPos + 1); |
68 | if (endPos == std::string::npos) |
69 | endPos = str.size(); |
70 | |
71 | return {.pos: startPos, .length: endPos - startPos}; |
72 | } |
73 | |
74 | // Check if `name` is a variadic operand of `op`. Seach all operands since the |
75 | // MLIR and LLVM IR operand order may differ and only for the latter the |
76 | // variadic operand is guaranteed to be at the end of the operands list. |
77 | static bool isVariadicOperandName(const tblgen::Operator &op, StringRef name) { |
78 | for (int i = 0, e = op.getNumOperands(); i < e; ++i) |
79 | if (op.getOperand(index: i).name == name) |
80 | return op.getOperand(index: i).isVariadic(); |
81 | return false; |
82 | } |
83 | |
84 | // Check if `result` is a known name of a result of `op`. |
85 | static bool isResultName(const tblgen::Operator &op, StringRef name) { |
86 | for (int i = 0, e = op.getNumResults(); i < e; ++i) |
87 | if (op.getResultName(index: i) == name) |
88 | return true; |
89 | return false; |
90 | } |
91 | |
92 | // Check if `name` is a known name of an attribute of `op`. |
93 | static bool isAttributeName(const tblgen::Operator &op, StringRef name) { |
94 | return llvm::any_of( |
95 | Range: op.getAttributes(), |
96 | P: [name](const tblgen::NamedAttribute &attr) { return attr.name == name; }); |
97 | } |
98 | |
99 | // Check if `name` is a known name of an operand of `op`. |
100 | static bool isOperandName(const tblgen::Operator &op, StringRef name) { |
101 | for (int i = 0, e = op.getNumOperands(); i < e; ++i) |
102 | if (op.getOperand(index: i).name == name) |
103 | return true; |
104 | return false; |
105 | } |
106 | |
107 | // Return the `op` argument index of the argument with the given `name`. |
108 | static FailureOr<int> getArgumentIndex(const tblgen::Operator &op, |
109 | StringRef name) { |
110 | for (int i = 0, e = op.getNumArgs(); i != e; ++i) |
111 | if (op.getArgName(index: i) == name) |
112 | return i; |
113 | return failure(); |
114 | } |
115 | |
116 | // Emit to `os` the operator-name driven check and the call to LLVM IRBuilder |
117 | // for one definition of an LLVM IR Dialect operation. |
118 | static LogicalResult emitOneBuilder(const Record &record, raw_ostream &os) { |
119 | auto op = tblgen::Operator(record); |
120 | |
121 | if (!record.getValue(Name: "llvmBuilder" )) |
122 | return emitError(record, message: "expected 'llvmBuilder' field" ); |
123 | |
124 | // Return early if there is no builder specified. |
125 | StringRef builderStrRef = record.getValueAsString(FieldName: "llvmBuilder" ); |
126 | if (builderStrRef.empty()) |
127 | return success(); |
128 | |
129 | // Progressively create the builder string by replacing $-variables with |
130 | // value lookups. Keep only the not-yet-traversed part of the builder pattern |
131 | // to avoid re-traversing the string multiple times. |
132 | std::string builder; |
133 | llvm::raw_string_ostream bs(builder); |
134 | while (StringLoc loc = findNextVariable(str: builderStrRef)) { |
135 | auto name = loc.in(str: builderStrRef).drop_front(); |
136 | auto getterName = op.getGetterName(name); |
137 | // First, insert the non-matched part as is. |
138 | bs << builderStrRef.substr(Start: 0, N: loc.pos); |
139 | // Then, rewrite the name based on its kind. |
140 | bool isVariadicOperand = isVariadicOperandName(op, name); |
141 | if (isOperandName(op, name)) { |
142 | auto result = |
143 | isVariadicOperand |
144 | ? formatv(Fmt: "moduleTranslation.lookupValues(op.{0}())" , Vals&: getterName) |
145 | : formatv(Fmt: "moduleTranslation.lookupValue(op.{0}())" , Vals&: getterName); |
146 | bs << result; |
147 | } else if (isAttributeName(op, name)) { |
148 | bs << formatv(Fmt: "op.{0}()" , Vals&: getterName); |
149 | } else if (isResultName(op, name)) { |
150 | bs << formatv(Fmt: "moduleTranslation.mapValue(op.{0}())" , Vals&: getterName); |
151 | } else if (name == "_resultType" ) { |
152 | bs << "moduleTranslation.convertType(op.getResult().getType())" ; |
153 | } else if (name == "_hasResult" ) { |
154 | bs << "opInst.getNumResults() == 1" ; |
155 | } else if (name == "_location" ) { |
156 | bs << "opInst.getLoc()" ; |
157 | } else if (name == "_numOperands" ) { |
158 | bs << "opInst.getNumOperands()" ; |
159 | } else if (name == "$" ) { |
160 | bs << '$'; |
161 | } else { |
162 | return emitError( |
163 | record, message: "expected keyword, argument, or result, but got " + name); |
164 | } |
165 | // Finally, only keep the untraversed part of the string. |
166 | builderStrRef = builderStrRef.substr(Start: loc.pos + loc.length); |
167 | } |
168 | |
169 | // Output the check and the rewritten builder string. |
170 | os << "if (auto op = dyn_cast<" << op.getQualCppClassName() |
171 | << ">(opInst)) {\n" ; |
172 | os << bs.str() << builderStrRef << "\n" ; |
173 | os << " return success();\n" ; |
174 | os << "}\n" ; |
175 | |
176 | return success(); |
177 | } |
178 | |
179 | // Emit all builders. Returns false on success because of the generator |
180 | // registration requirements. |
181 | static bool emitBuilders(const RecordKeeper &recordKeeper, raw_ostream &os) { |
182 | for (const Record *def : |
183 | recordKeeper.getAllDerivedDefinitions(ClassName: "LLVM_OpBase" )) { |
184 | if (failed(result: emitOneBuilder(record: *def, os))) |
185 | return true; |
186 | } |
187 | return false; |
188 | } |
189 | |
190 | using ConditionFn = mlir::function_ref<llvm::Twine(const Record &record)>; |
191 | |
192 | // Emit a conditional call to the MLIR builder of the LLVM dialect operation to |
193 | // build for the given LLVM IR instruction. A condition function `conditionFn` |
194 | // emits a check to verify the opcode or intrinsic identifier of the LLVM IR |
195 | // instruction matches the LLVM dialect operation to build. |
196 | static LogicalResult emitOneMLIRBuilder(const Record &record, raw_ostream &os, |
197 | ConditionFn conditionFn) { |
198 | auto op = tblgen::Operator(record); |
199 | |
200 | if (!record.getValue(Name: "mlirBuilder" )) |
201 | return emitError(record, message: "expected 'mlirBuilder' field" ); |
202 | |
203 | // Return early if there is no builder specified. |
204 | StringRef builderStrRef = record.getValueAsString(FieldName: "mlirBuilder" ); |
205 | if (builderStrRef.empty()) |
206 | return success(); |
207 | |
208 | // Access the argument index array that maps argument indices to LLVM IR |
209 | // operand indices. If the operation defines no custom mapping, set the array |
210 | // to the identity permutation. |
211 | std::vector<int64_t> llvmArgIndices = |
212 | record.getValueAsListOfInts(FieldName: "llvmArgIndices" ); |
213 | if (llvmArgIndices.empty()) |
214 | append_range(C&: llvmArgIndices, R: seq<int64_t>(Begin: 0, End: op.getNumArgs())); |
215 | if (llvmArgIndices.size() != static_cast<size_t>(op.getNumArgs())) { |
216 | return emitError( |
217 | record, |
218 | message: "expected 'llvmArgIndices' size to match the number of arguments" ); |
219 | } |
220 | |
221 | // Progressively create the builder string by replacing $-variables. Keep only |
222 | // the not-yet-traversed part of the builder pattern to avoid re-traversing |
223 | // the string multiple times. Additionally, emit an argument string |
224 | // immediately before the builder string. This argument string converts all |
225 | // operands used by the builder to MLIR values and returns failure if one of |
226 | // the conversions fails. |
227 | std::string arguments, builder; |
228 | llvm::raw_string_ostream as(arguments), bs(builder); |
229 | while (StringLoc loc = findNextVariable(str: builderStrRef)) { |
230 | auto name = loc.in(str: builderStrRef).drop_front(); |
231 | // First, insert the non-matched part as is. |
232 | bs << builderStrRef.substr(Start: 0, N: loc.pos); |
233 | // Then, rewrite the name based on its kind. |
234 | FailureOr<int> argIndex = getArgumentIndex(op, name); |
235 | if (succeeded(result: argIndex)) { |
236 | // Access the LLVM IR operand that maps to the given argument index using |
237 | // the provided argument indices mapping. |
238 | int64_t idx = llvmArgIndices[*argIndex]; |
239 | if (idx < 0) { |
240 | return emitError( |
241 | record, message: "expected non-negative operand index for argument " + name); |
242 | } |
243 | if (isAttributeName(op, name)) { |
244 | bs << formatv(Fmt: "llvmOperands[{0}]" , Vals&: idx); |
245 | } else { |
246 | if (isVariadicOperandName(op, name)) { |
247 | as << formatv( |
248 | Fmt: "FailureOr<SmallVector<Value>> _llvmir_gen_operand_{0} = " |
249 | "moduleImport.convertValues(llvmOperands.drop_front({1}));\n" , |
250 | Vals&: name, Vals&: idx); |
251 | } else { |
252 | as << formatv(Fmt: "FailureOr<Value> _llvmir_gen_operand_{0} = " |
253 | "moduleImport.convertValue(llvmOperands[{1}]);\n" , |
254 | Vals&: name, Vals&: idx); |
255 | } |
256 | as << formatv(Fmt: "if (failed(_llvmir_gen_operand_{0}))\n" |
257 | " return failure();\n" , |
258 | Vals&: name); |
259 | bs << formatv(Fmt: "*_llvmir_gen_operand_{0}" , Vals&: name); |
260 | } |
261 | } else if (isResultName(op, name)) { |
262 | if (op.getNumResults() != 1) |
263 | return emitError(record, message: "expected op to have one result" ); |
264 | bs << "moduleImport.mapValue(inst)" ; |
265 | } else if (name == "_op" ) { |
266 | bs << "moduleImport.mapNoResultOp(inst)" ; |
267 | } else if (name == "_int_attr" ) { |
268 | bs << "moduleImport.matchIntegerAttr" ; |
269 | } else if (name == "_float_attr" ) { |
270 | bs << "moduleImport.matchFloatAttr" ; |
271 | } else if (name == "_var_attr" ) { |
272 | bs << "moduleImport.matchLocalVariableAttr" ; |
273 | } else if (name == "_label_attr" ) { |
274 | bs << "moduleImport.matchLabelAttr" ; |
275 | } else if (name == "_fpExceptionBehavior_attr" ) { |
276 | bs << "moduleImport.matchFPExceptionBehaviorAttr" ; |
277 | } else if (name == "_roundingMode_attr" ) { |
278 | bs << "moduleImport.matchRoundingModeAttr" ; |
279 | } else if (name == "_resultType" ) { |
280 | bs << "moduleImport.convertType(inst->getType())" ; |
281 | } else if (name == "_location" ) { |
282 | bs << "moduleImport.translateLoc(inst->getDebugLoc())" ; |
283 | } else if (name == "_builder" ) { |
284 | bs << "odsBuilder" ; |
285 | } else if (name == "_qualCppClassName" ) { |
286 | bs << op.getQualCppClassName(); |
287 | } else if (name == "$" ) { |
288 | bs << '$'; |
289 | } else { |
290 | return emitError( |
291 | record, message: "expected keyword, argument, or result, but got " + name); |
292 | } |
293 | // Finally, only keep the untraversed part of the string. |
294 | builderStrRef = builderStrRef.substr(Start: loc.pos + loc.length); |
295 | } |
296 | |
297 | // Output the check, the argument conversion, and the builder string. |
298 | os << "if (" << conditionFn(record) << ") {\n" ; |
299 | os << as.str() << "\n" ; |
300 | os << bs.str() << builderStrRef << "\n" ; |
301 | os << " return success();\n" ; |
302 | os << "}\n" ; |
303 | |
304 | return success(); |
305 | } |
306 | |
307 | // Emit all intrinsic MLIR builders. Returns false on success because of the |
308 | // generator registration requirements. |
309 | static bool emitIntrMLIRBuilders(const RecordKeeper &recordKeeper, |
310 | raw_ostream &os) { |
311 | // Emit condition to check if "llvmEnumName" matches the intrinsic id. |
312 | auto emitIntrCond = [](const Record &record) { |
313 | return "intrinsicID == llvm::Intrinsic::" + |
314 | record.getValueAsString(FieldName: "llvmEnumName" ); |
315 | }; |
316 | for (const Record *def : |
317 | recordKeeper.getAllDerivedDefinitions(ClassName: "LLVM_IntrOpBase" )) { |
318 | if (failed(result: emitOneMLIRBuilder(record: *def, os, conditionFn: emitIntrCond))) |
319 | return true; |
320 | } |
321 | return false; |
322 | } |
323 | |
324 | // Emit all op builders. Returns false on success because of the |
325 | // generator registration requirements. |
326 | static bool emitOpMLIRBuilders(const RecordKeeper &recordKeeper, |
327 | raw_ostream &os) { |
328 | // Emit condition to check if "llvmInstName" matches the instruction opcode. |
329 | auto emitOpcodeCond = [](const Record &record) { |
330 | return "inst->getOpcode() == llvm::Instruction::" + |
331 | record.getValueAsString(FieldName: "llvmInstName" ); |
332 | }; |
333 | for (const Record *def : |
334 | recordKeeper.getAllDerivedDefinitions(ClassName: "LLVM_OpBase" )) { |
335 | if (failed(result: emitOneMLIRBuilder(record: *def, os, conditionFn: emitOpcodeCond))) |
336 | return true; |
337 | } |
338 | return false; |
339 | } |
340 | |
341 | namespace { |
342 | // Wrapper class around a Tablegen definition of an LLVM enum attribute case. |
343 | class : public tblgen::EnumAttrCase { |
344 | public: |
345 | using tblgen::EnumAttrCase::EnumAttrCase; |
346 | |
347 | // Constructs a case from a non LLVM-specific enum attribute case. |
348 | explicit (const tblgen::EnumAttrCase &other) |
349 | : tblgen::EnumAttrCase(&other.getDef()) {} |
350 | |
351 | // Returns the C++ enumerant for the LLVM API. |
352 | StringRef () const { |
353 | return def->getValueAsString(FieldName: "llvmEnumerant" ); |
354 | } |
355 | }; |
356 | |
357 | // Wraper class around a Tablegen definition of an LLVM enum attribute. |
358 | class : public tblgen::EnumAttr { |
359 | public: |
360 | using tblgen::EnumAttr::EnumAttr; |
361 | |
362 | // Returns the C++ enum name for the LLVM API. |
363 | StringRef () const { |
364 | return def->getValueAsString(FieldName: "llvmClassName" ); |
365 | } |
366 | |
367 | // Returns all associated cases viewed as LLVM-specific enum cases. |
368 | std::vector<LLVMEnumAttrCase> () const { |
369 | std::vector<LLVMEnumAttrCase> cases; |
370 | |
371 | for (auto &c : tblgen::EnumAttr::getAllCases()) |
372 | cases.emplace_back(args&: c); |
373 | |
374 | return cases; |
375 | } |
376 | |
377 | std::vector<LLVMEnumAttrCase> () const { |
378 | const auto *inits = def->getValueAsListInit(FieldName: "unsupported" ); |
379 | |
380 | std::vector<LLVMEnumAttrCase> cases; |
381 | cases.reserve(n: inits->size()); |
382 | |
383 | for (const llvm::Init *init : *inits) |
384 | cases.emplace_back(args: cast<llvm::DefInit>(Val: init)); |
385 | |
386 | return cases; |
387 | } |
388 | }; |
389 | |
390 | // Wraper class around a Tablegen definition of a C-style LLVM enum attribute. |
391 | class LLVMCEnumAttr : public tblgen::EnumAttr { |
392 | public: |
393 | using tblgen::EnumAttr::EnumAttr; |
394 | |
395 | // Returns the C++ enum name for the LLVM API. |
396 | StringRef getLLVMClassName() const { |
397 | return def->getValueAsString(FieldName: "llvmClassName" ); |
398 | } |
399 | |
400 | // Returns all associated cases viewed as LLVM-specific enum cases. |
401 | std::vector<LLVMEnumAttrCase> getAllCases() const { |
402 | std::vector<LLVMEnumAttrCase> cases; |
403 | |
404 | for (auto &c : tblgen::EnumAttr::getAllCases()) |
405 | cases.emplace_back(args&: c); |
406 | |
407 | return cases; |
408 | } |
409 | }; |
410 | } // namespace |
411 | |
412 | // Emits conversion function "LLVMClass convertEnumToLLVM(Enum)" and containing |
413 | // switch-based logic to convert from the MLIR LLVM dialect enum attribute case |
414 | // (Enum) to the corresponding LLVM API enumerant |
415 | static void emitOneEnumToConversion(const llvm::Record *record, |
416 | raw_ostream &os) { |
417 | LLVMEnumAttr enumAttr(record); |
418 | StringRef llvmClass = enumAttr.getLLVMClassName(); |
419 | StringRef cppClassName = enumAttr.getEnumClassName(); |
420 | StringRef cppNamespace = enumAttr.getCppNamespace(); |
421 | |
422 | // Emit the function converting the enum attribute to its LLVM counterpart. |
423 | os << formatv( |
424 | Fmt: "static LLVM_ATTRIBUTE_UNUSED {0} convert{1}ToLLVM({2}::{1} value) {{\n" , |
425 | Vals&: llvmClass, Vals&: cppClassName, Vals&: cppNamespace); |
426 | os << " switch (value) {\n" ; |
427 | |
428 | for (const auto &enumerant : enumAttr.getAllCases()) { |
429 | StringRef = enumerant.getLLVMEnumerant(); |
430 | StringRef cppEnumerant = enumerant.getSymbol(); |
431 | os << formatv(Fmt: " case {0}::{1}::{2}:\n" , Vals&: cppNamespace, Vals&: cppClassName, |
432 | Vals&: cppEnumerant); |
433 | os << formatv(Fmt: " return {0}::{1};\n" , Vals&: llvmClass, Vals&: llvmEnumerant); |
434 | } |
435 | |
436 | os << " }\n" ; |
437 | os << formatv(Fmt: " llvm_unreachable(\"unknown {0} type\");\n" , |
438 | Vals: enumAttr.getEnumClassName()); |
439 | os << "}\n\n" ; |
440 | } |
441 | |
442 | // Emits conversion function "LLVMClass convertEnumToLLVM(Enum)" and containing |
443 | // switch-based logic to convert from the MLIR LLVM dialect enum attribute case |
444 | // (Enum) to the corresponding LLVM API C-style enumerant |
445 | static void emitOneCEnumToConversion(const llvm::Record *record, |
446 | raw_ostream &os) { |
447 | LLVMCEnumAttr enumAttr(record); |
448 | StringRef llvmClass = enumAttr.getLLVMClassName(); |
449 | StringRef cppClassName = enumAttr.getEnumClassName(); |
450 | StringRef cppNamespace = enumAttr.getCppNamespace(); |
451 | |
452 | // Emit the function converting the enum attribute to its LLVM counterpart. |
453 | os << formatv(Fmt: "static LLVM_ATTRIBUTE_UNUSED int64_t " |
454 | "convert{0}ToLLVM({1}::{0} value) {{\n" , |
455 | Vals&: cppClassName, Vals&: cppNamespace); |
456 | os << " switch (value) {\n" ; |
457 | |
458 | for (const auto &enumerant : enumAttr.getAllCases()) { |
459 | StringRef = enumerant.getLLVMEnumerant(); |
460 | StringRef cppEnumerant = enumerant.getSymbol(); |
461 | os << formatv(Fmt: " case {0}::{1}::{2}:\n" , Vals&: cppNamespace, Vals&: cppClassName, |
462 | Vals&: cppEnumerant); |
463 | os << formatv(Fmt: " return static_cast<int64_t>({0}::{1});\n" , Vals&: llvmClass, |
464 | Vals&: llvmEnumerant); |
465 | } |
466 | |
467 | os << " }\n" ; |
468 | os << formatv(Fmt: " llvm_unreachable(\"unknown {0} type\");\n" , |
469 | Vals: enumAttr.getEnumClassName()); |
470 | os << "}\n\n" ; |
471 | } |
472 | |
473 | // Emits conversion function "Enum convertEnumFromLLVM(LLVMClass)" and |
474 | // containing switch-based logic to convert from the LLVM API enumerant to MLIR |
475 | // LLVM dialect enum attribute (Enum). |
476 | static void emitOneEnumFromConversion(const llvm::Record *record, |
477 | raw_ostream &os) { |
478 | LLVMEnumAttr enumAttr(record); |
479 | StringRef llvmClass = enumAttr.getLLVMClassName(); |
480 | StringRef cppClassName = enumAttr.getEnumClassName(); |
481 | StringRef cppNamespace = enumAttr.getCppNamespace(); |
482 | |
483 | // Emit the function converting the enum attribute from its LLVM counterpart. |
484 | os << formatv(Fmt: "inline LLVM_ATTRIBUTE_UNUSED {0}::{1} convert{1}FromLLVM({2} " |
485 | "value) {{\n" , |
486 | Vals&: cppNamespace, Vals&: cppClassName, Vals&: llvmClass); |
487 | os << " switch (value) {\n" ; |
488 | |
489 | for (const auto &enumerant : enumAttr.getAllCases()) { |
490 | StringRef = enumerant.getLLVMEnumerant(); |
491 | StringRef cppEnumerant = enumerant.getSymbol(); |
492 | os << formatv(Fmt: " case {0}::{1}:\n" , Vals&: llvmClass, Vals&: llvmEnumerant); |
493 | os << formatv(Fmt: " return {0}::{1}::{2};\n" , Vals&: cppNamespace, Vals&: cppClassName, |
494 | Vals&: cppEnumerant); |
495 | } |
496 | for (const auto &enumerant : enumAttr.getAllUnsupportedCases()) { |
497 | StringRef = enumerant.getLLVMEnumerant(); |
498 | os << formatv(Fmt: " case {0}::{1}:\n" , Vals&: llvmClass, Vals&: llvmEnumerant); |
499 | os << formatv(Fmt: " llvm_unreachable(\"unsupported case {0}::{1}\");\n" , |
500 | Vals: enumAttr.getLLVMClassName(), Vals&: llvmEnumerant); |
501 | } |
502 | |
503 | os << " }\n" ; |
504 | os << formatv(Fmt: " llvm_unreachable(\"unknown {0} type\");" , |
505 | Vals: enumAttr.getLLVMClassName()); |
506 | os << "}\n\n" ; |
507 | } |
508 | |
509 | // Emits conversion function "Enum convertEnumFromLLVM(LLVMEnum)" and |
510 | // containing switch-based logic to convert from the LLVM API C-style enumerant |
511 | // to MLIR LLVM dialect enum attribute (Enum). |
512 | static void emitOneCEnumFromConversion(const llvm::Record *record, |
513 | raw_ostream &os) { |
514 | LLVMCEnumAttr enumAttr(record); |
515 | StringRef llvmClass = enumAttr.getLLVMClassName(); |
516 | StringRef cppClassName = enumAttr.getEnumClassName(); |
517 | StringRef cppNamespace = enumAttr.getCppNamespace(); |
518 | |
519 | // Emit the function converting the enum attribute from its LLVM counterpart. |
520 | os << formatv( |
521 | Fmt: "inline LLVM_ATTRIBUTE_UNUSED {0}::{1} convert{1}FromLLVM(int64_t " |
522 | "value) {{\n" , |
523 | Vals&: cppNamespace, Vals&: cppClassName, Vals&: llvmClass); |
524 | os << " switch (value) {\n" ; |
525 | |
526 | for (const auto &enumerant : enumAttr.getAllCases()) { |
527 | StringRef = enumerant.getLLVMEnumerant(); |
528 | StringRef cppEnumerant = enumerant.getSymbol(); |
529 | os << formatv(Fmt: " case static_cast<int64_t>({0}::{1}):\n" , Vals&: llvmClass, |
530 | Vals&: llvmEnumerant); |
531 | os << formatv(Fmt: " return {0}::{1}::{2};\n" , Vals&: cppNamespace, Vals&: cppClassName, |
532 | Vals&: cppEnumerant); |
533 | } |
534 | |
535 | os << " }\n" ; |
536 | os << formatv(Fmt: " llvm_unreachable(\"unknown {0} type\");" , |
537 | Vals: enumAttr.getLLVMClassName()); |
538 | os << "}\n\n" ; |
539 | } |
540 | |
541 | // Emits conversion functions between MLIR enum attribute case and corresponding |
542 | // LLVM API enumerants for all registered LLVM dialect enum attributes. |
543 | template <bool ConvertTo> |
544 | static bool emitEnumConversionDefs(const RecordKeeper &recordKeeper, |
545 | raw_ostream &os) { |
546 | for (const Record *def : |
547 | recordKeeper.getAllDerivedDefinitions(ClassName: "LLVM_EnumAttr" )) |
548 | if (ConvertTo) |
549 | emitOneEnumToConversion(record: def, os); |
550 | else |
551 | emitOneEnumFromConversion(record: def, os); |
552 | |
553 | for (const Record *def : |
554 | recordKeeper.getAllDerivedDefinitions(ClassName: "LLVM_CEnumAttr" )) |
555 | if (ConvertTo) |
556 | emitOneCEnumToConversion(record: def, os); |
557 | else |
558 | emitOneCEnumFromConversion(record: def, os); |
559 | |
560 | return false; |
561 | } |
562 | |
563 | static void emitOneIntrinsic(const Record &record, raw_ostream &os) { |
564 | auto op = tblgen::Operator(record); |
565 | os << "llvm::Intrinsic::" << record.getValueAsString(FieldName: "llvmEnumName" ) << ",\n" ; |
566 | } |
567 | |
568 | // Emit the list of LLVM IR intrinsics identifiers that are convertible to a |
569 | // matching MLIR LLVM dialect intrinsic operation. |
570 | static bool emitConvertibleIntrinsics(const RecordKeeper &recordKeeper, |
571 | raw_ostream &os) { |
572 | for (const Record *def : |
573 | recordKeeper.getAllDerivedDefinitions(ClassName: "LLVM_IntrOpBase" )) |
574 | emitOneIntrinsic(record: *def, os); |
575 | |
576 | return false; |
577 | } |
578 | |
579 | static mlir::GenRegistration |
580 | genLLVMIRConversions("gen-llvmir-conversions" , |
581 | "Generate LLVM IR conversions" , emitBuilders); |
582 | |
583 | static mlir::GenRegistration genOpFromLLVMIRConversions( |
584 | "gen-op-from-llvmir-conversions" , |
585 | "Generate conversions of operations from LLVM IR" , emitOpMLIRBuilders); |
586 | |
587 | static mlir::GenRegistration genIntrFromLLVMIRConversions( |
588 | "gen-intr-from-llvmir-conversions" , |
589 | "Generate conversions of intrinsics from LLVM IR" , emitIntrMLIRBuilders); |
590 | |
591 | static mlir::GenRegistration |
592 | genEnumToLLVMConversion("gen-enum-to-llvmir-conversions" , |
593 | "Generate conversions of EnumAttrs to LLVM IR" , |
594 | emitEnumConversionDefs</*ConvertTo=*/true>); |
595 | |
596 | static mlir::GenRegistration |
597 | genEnumFromLLVMConversion("gen-enum-from-llvmir-conversions" , |
598 | "Generate conversions of EnumAttrs from LLVM IR" , |
599 | emitEnumConversionDefs</*ConvertTo=*/false>); |
600 | |
601 | static mlir::GenRegistration genConvertibleLLVMIRIntrinsics( |
602 | "gen-convertible-llvmir-intrinsics" , |
603 | "Generate list of convertible LLVM IR intrinsics" , |
604 | emitConvertibleIntrinsics); |
605 | |