MLIR  19.0.0git
LLVMTypeSyntax.cpp
Go to the documentation of this file.
1 //===- LLVMTypeSyntax.cpp - Parsing/printing for MLIR LLVM Dialect types --===//
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 
10 #include "mlir/IR/Builders.h"
12 #include "llvm/ADT/ScopeExit.h"
13 #include "llvm/ADT/SetVector.h"
14 #include "llvm/ADT/TypeSwitch.h"
15 
16 using namespace mlir;
17 using namespace mlir::LLVM;
18 
19 //===----------------------------------------------------------------------===//
20 // Printing.
21 //===----------------------------------------------------------------------===//
22 
23 /// If the given type is compatible with the LLVM dialect, prints it using
24 /// internal functions to avoid getting a verbose `!llvm` prefix. Otherwise
25 /// prints it as usual.
26 static void dispatchPrint(AsmPrinter &printer, Type type) {
27  if (isCompatibleType(type) &&
28  !llvm::isa<IntegerType, FloatType, VectorType>(type))
29  return mlir::LLVM::detail::printType(type, printer);
30  printer.printType(type);
31 }
32 
33 /// Returns the keyword to use for the given type.
34 static StringRef getTypeKeyword(Type type) {
35  return TypeSwitch<Type, StringRef>(type)
36  .Case<LLVMVoidType>([&](Type) { return "void"; })
37  .Case<LLVMPPCFP128Type>([&](Type) { return "ppc_fp128"; })
38  .Case<LLVMX86MMXType>([&](Type) { return "x86_mmx"; })
39  .Case<LLVMTokenType>([&](Type) { return "token"; })
40  .Case<LLVMLabelType>([&](Type) { return "label"; })
41  .Case<LLVMMetadataType>([&](Type) { return "metadata"; })
42  .Case<LLVMFunctionType>([&](Type) { return "func"; })
43  .Case<LLVMPointerType>([&](Type) { return "ptr"; })
44  .Case<LLVMFixedVectorType, LLVMScalableVectorType>(
45  [&](Type) { return "vec"; })
46  .Case<LLVMArrayType>([&](Type) { return "array"; })
47  .Case<LLVMStructType>([&](Type) { return "struct"; })
48  .Case<LLVMTargetExtType>([&](Type) { return "target"; })
49  .Default([](Type) -> StringRef {
50  llvm_unreachable("unexpected 'llvm' type kind");
51  });
52 }
53 
54 /// Prints a structure type. Keeps track of known struct names to handle self-
55 /// or mutually-referring structs without falling into infinite recursion.
56 static void printStructType(AsmPrinter &printer, LLVMStructType type) {
58 
59  printer << "<";
60  if (type.isIdentified()) {
61  cyclicPrint = printer.tryStartCyclicPrint(type);
62 
63  printer << '"' << type.getName() << '"';
64  // If we are printing a reference to one of the enclosing structs, just
65  // print the name and stop to avoid infinitely long output.
66  if (failed(cyclicPrint)) {
67  printer << '>';
68  return;
69  }
70  printer << ", ";
71  }
72 
73  if (type.isIdentified() && type.isOpaque()) {
74  printer << "opaque>";
75  return;
76  }
77 
78  if (type.isPacked())
79  printer << "packed ";
80 
81  // Put the current type on stack to avoid infinite recursion.
82  printer << '(';
83  llvm::interleaveComma(type.getBody(), printer.getStream(),
84  [&](Type subtype) { dispatchPrint(printer, subtype); });
85  printer << ')';
86  printer << '>';
87 }
88 
89 /// Prints the given LLVM dialect type recursively. This leverages closedness of
90 /// the LLVM dialect type system to avoid printing the dialect prefix
91 /// repeatedly. For recursive structures, only prints the name of the structure
92 /// when printing a self-reference. Note that this does not apply to sibling
93 /// references. For example,
94 /// struct<"a", (ptr<struct<"a">>)>
95 /// struct<"c", (ptr<struct<"b", (ptr<struct<"c">>)>>,
96 /// ptr<struct<"b", (ptr<struct<"c">>)>>)>
97 /// note that "b" is printed twice.
98 void mlir::LLVM::detail::printType(Type type, AsmPrinter &printer) {
99  if (!type) {
100  printer << "<<NULL-TYPE>>";
101  return;
102  }
103 
104  printer << getTypeKeyword(type);
105 
106  llvm::TypeSwitch<Type>(type)
107  .Case<LLVMPointerType, LLVMArrayType, LLVMFixedVectorType,
108  LLVMScalableVectorType, LLVMFunctionType, LLVMTargetExtType>(
109  [&](auto type) { type.print(printer); })
110  .Case([&](LLVMStructType structType) {
111  printStructType(printer, structType);
112  });
113 }
114 
115 //===----------------------------------------------------------------------===//
116 // Parsing.
117 //===----------------------------------------------------------------------===//
118 
119 static ParseResult dispatchParse(AsmParser &parser, Type &type);
120 
121 /// Parses an LLVM dialect vector type.
122 /// llvm-type ::= `vec<` `? x`? integer `x` llvm-type `>`
123 /// Supports both fixed and scalable vectors.
124 static Type parseVectorType(AsmParser &parser) {
125  SmallVector<int64_t, 2> dims;
126  SMLoc dimPos, typePos;
127  Type elementType;
128  SMLoc loc = parser.getCurrentLocation();
129  if (parser.parseLess() || parser.getCurrentLocation(&dimPos) ||
130  parser.parseDimensionList(dims, /*allowDynamic=*/true) ||
131  parser.getCurrentLocation(&typePos) ||
132  dispatchParse(parser, elementType) || parser.parseGreater())
133  return Type();
134 
135  // We parsed a generic dimension list, but vectors only support two forms:
136  // - single non-dynamic entry in the list (fixed vector);
137  // - two elements, the first dynamic (indicated by ShapedType::kDynamic)
138  // and the second
139  // non-dynamic (scalable vector).
140  if (dims.empty() || dims.size() > 2 ||
141  ((dims.size() == 2) ^ (ShapedType::isDynamic(dims[0]))) ||
142  (dims.size() == 2 && ShapedType::isDynamic(dims[1]))) {
143  parser.emitError(dimPos)
144  << "expected '? x <integer> x <type>' or '<integer> x <type>'";
145  return Type();
146  }
147 
148  bool isScalable = dims.size() == 2;
149  if (isScalable)
150  return parser.getChecked<LLVMScalableVectorType>(loc, elementType, dims[1]);
151  if (elementType.isSignlessIntOrFloat()) {
152  parser.emitError(typePos)
153  << "cannot use !llvm.vec for built-in primitives, use 'vector' instead";
154  return Type();
155  }
156  return parser.getChecked<LLVMFixedVectorType>(loc, elementType, dims[0]);
157 }
158 
159 /// Attempts to set the body of an identified structure type. Reports a parsing
160 /// error at `subtypesLoc` in case of failure.
161 static LLVMStructType trySetStructBody(LLVMStructType type,
162  ArrayRef<Type> subtypes, bool isPacked,
163  AsmParser &parser, SMLoc subtypesLoc) {
164  for (Type t : subtypes) {
165  if (!LLVMStructType::isValidElementType(t)) {
166  parser.emitError(subtypesLoc)
167  << "invalid LLVM structure element type: " << t;
168  return LLVMStructType();
169  }
170  }
171 
172  if (succeeded(type.setBody(subtypes, isPacked)))
173  return type;
174 
175  parser.emitError(subtypesLoc)
176  << "identified type already used with a different body";
177  return LLVMStructType();
178 }
179 
180 /// Parses an LLVM dialect structure type.
181 /// llvm-type ::= `struct<` (string-literal `,`)? `packed`?
182 /// `(` llvm-type-list `)` `>`
183 /// | `struct<` string-literal `>`
184 /// | `struct<` string-literal `, opaque>`
185 static LLVMStructType parseStructType(AsmParser &parser) {
186  Location loc = parser.getEncodedSourceLoc(parser.getCurrentLocation());
187 
188  if (failed(parser.parseLess()))
189  return LLVMStructType();
190 
191  // If we are parsing a self-reference to a recursive struct, i.e. the parsing
192  // stack already contains a struct with the same identifier, bail out after
193  // the name.
194  std::string name;
195  bool isIdentified = succeeded(parser.parseOptionalString(&name));
196  if (isIdentified) {
197  SMLoc greaterLoc = parser.getCurrentLocation();
198  if (succeeded(parser.parseOptionalGreater())) {
199  auto type = LLVMStructType::getIdentifiedChecked(
200  [loc] { return emitError(loc); }, loc.getContext(), name);
201  if (succeeded(parser.tryStartCyclicParse(type))) {
202  parser.emitError(
203  greaterLoc,
204  "struct without a body only allowed in a recursive struct");
205  return nullptr;
206  }
207 
208  return type;
209  }
210  if (failed(parser.parseComma()))
211  return LLVMStructType();
212  }
213 
214  // Handle intentionally opaque structs.
215  SMLoc kwLoc = parser.getCurrentLocation();
216  if (succeeded(parser.parseOptionalKeyword("opaque"))) {
217  if (!isIdentified)
218  return parser.emitError(kwLoc, "only identified structs can be opaque"),
219  LLVMStructType();
220  if (failed(parser.parseGreater()))
221  return LLVMStructType();
222  auto type = LLVMStructType::getOpaqueChecked(
223  [loc] { return emitError(loc); }, loc.getContext(), name);
224  if (!type.isOpaque()) {
225  parser.emitError(kwLoc, "redeclaring defined struct as opaque");
226  return LLVMStructType();
227  }
228  return type;
229  }
230 
231  FailureOr<AsmParser::CyclicParseReset> cyclicParse;
232  if (isIdentified) {
233  cyclicParse =
234  parser.tryStartCyclicParse(LLVMStructType::getIdentifiedChecked(
235  [loc] { return emitError(loc); }, loc.getContext(), name));
236  if (failed(cyclicParse)) {
237  parser.emitError(kwLoc,
238  "identifier already used for an enclosing struct");
239  return nullptr;
240  }
241  }
242 
243  // Check for packedness.
244  bool isPacked = succeeded(parser.parseOptionalKeyword("packed"));
245  if (failed(parser.parseLParen()))
246  return LLVMStructType();
247 
248  // Fast pass for structs with zero subtypes.
249  if (succeeded(parser.parseOptionalRParen())) {
250  if (failed(parser.parseGreater()))
251  return LLVMStructType();
252  if (!isIdentified)
253  return LLVMStructType::getLiteralChecked([loc] { return emitError(loc); },
254  loc.getContext(), {}, isPacked);
255  auto type = LLVMStructType::getIdentifiedChecked(
256  [loc] { return emitError(loc); }, loc.getContext(), name);
257  return trySetStructBody(type, {}, isPacked, parser, kwLoc);
258  }
259 
260  // Parse subtypes. For identified structs, put the identifier of the struct on
261  // the stack to support self-references in the recursive calls.
262  SmallVector<Type, 4> subtypes;
263  SMLoc subtypesLoc = parser.getCurrentLocation();
264  do {
265  Type type;
266  if (dispatchParse(parser, type))
267  return LLVMStructType();
268  subtypes.push_back(type);
269  } while (succeeded(parser.parseOptionalComma()));
270 
271  if (parser.parseRParen() || parser.parseGreater())
272  return LLVMStructType();
273 
274  // Construct the struct with body.
275  if (!isIdentified)
276  return LLVMStructType::getLiteralChecked(
277  [loc] { return emitError(loc); }, loc.getContext(), subtypes, isPacked);
278  auto type = LLVMStructType::getIdentifiedChecked(
279  [loc] { return emitError(loc); }, loc.getContext(), name);
280  return trySetStructBody(type, subtypes, isPacked, parser, subtypesLoc);
281 }
282 
283 /// Parses a type appearing inside another LLVM dialect-compatible type. This
284 /// will try to parse any type in full form (including types with the `!llvm`
285 /// prefix), and on failure fall back to parsing the short-hand version of the
286 /// LLVM dialect types without the `!llvm` prefix.
287 static Type dispatchParse(AsmParser &parser, bool allowAny = true) {
288  SMLoc keyLoc = parser.getCurrentLocation();
289 
290  // Try parsing any MLIR type.
291  Type type;
292  OptionalParseResult result = parser.parseOptionalType(type);
293  if (result.has_value()) {
294  if (failed(result.value()))
295  return nullptr;
296  if (!allowAny) {
297  parser.emitError(keyLoc) << "unexpected type, expected keyword";
298  return nullptr;
299  }
300  return type;
301  }
302 
303  // If no type found, fallback to the shorthand form.
304  StringRef key;
305  if (failed(parser.parseKeyword(&key)))
306  return Type();
307 
308  MLIRContext *ctx = parser.getContext();
309  return StringSwitch<function_ref<Type()>>(key)
310  .Case("void", [&] { return LLVMVoidType::get(ctx); })
311  .Case("ppc_fp128", [&] { return LLVMPPCFP128Type::get(ctx); })
312  .Case("x86_mmx", [&] { return LLVMX86MMXType::get(ctx); })
313  .Case("token", [&] { return LLVMTokenType::get(ctx); })
314  .Case("label", [&] { return LLVMLabelType::get(ctx); })
315  .Case("metadata", [&] { return LLVMMetadataType::get(ctx); })
316  .Case("func", [&] { return LLVMFunctionType::parse(parser); })
317  .Case("ptr", [&] { return LLVMPointerType::parse(parser); })
318  .Case("vec", [&] { return parseVectorType(parser); })
319  .Case("array", [&] { return LLVMArrayType::parse(parser); })
320  .Case("struct", [&] { return parseStructType(parser); })
321  .Case("target", [&] { return LLVMTargetExtType::parse(parser); })
322  .Default([&] {
323  parser.emitError(keyLoc) << "unknown LLVM type: " << key;
324  return Type();
325  })();
326 }
327 
328 /// Helper to use in parse lists.
329 static ParseResult dispatchParse(AsmParser &parser, Type &type) {
330  type = dispatchParse(parser);
331  return success(type != nullptr);
332 }
333 
334 /// Parses one of the LLVM dialect types.
335 Type mlir::LLVM::detail::parseType(DialectAsmParser &parser) {
336  SMLoc loc = parser.getCurrentLocation();
337  Type type = dispatchParse(parser, /*allowAny=*/false);
338  if (!type)
339  return type;
340  if (!isCompatibleOuterType(type)) {
341  parser.emitError(loc) << "unexpected type, expected keyword";
342  return nullptr;
343  }
344  return type;
345 }
346 
347 ParseResult LLVM::parsePrettyLLVMType(AsmParser &p, Type &type) {
348  return dispatchParse(p, type);
349 }
350 
351 void LLVM::printPrettyLLVMType(AsmPrinter &p, Type type) {
352  return dispatchPrint(p, type);
353 }
static void printStructType(AsmPrinter &printer, LLVMStructType type)
Prints a structure type.
static void dispatchPrint(AsmPrinter &printer, Type type)
If the given type is compatible with the LLVM dialect, prints it using internal functions to avoid ge...
static StringRef getTypeKeyword(Type type)
Returns the keyword to use for the given type.
This base class exposes generic asm printer hooks, usable across the various derived printers.
FailureOr< CyclicPrintReset > tryStartCyclicPrint(AttrOrTypeT attrOrType)
Attempts to start a cyclic printing region for attrOrType.
virtual void printType(Type type)
This class provides support for representing a failure result, or a valid value of type T.
Definition: LogicalResult.h:78
LLVM dialect structure type representing a collection of different-typed elements manipulated togethe...
Definition: LLVMTypes.h:109
StringRef getName()
Returns the name of an identified struct.
Definition: LLVMTypes.cpp:489
bool isIdentified() const
Checks if a struct is identified.
Definition: LLVMTypes.cpp:483
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
void printType(Type type, AsmPrinter &printer)
Prints an LLVM Dialect type.
bool isCompatibleType(Type type)
Returns true if the given type is compatible with the LLVM dialect.
Definition: LLVMTypes.cpp:862
Include the generated interface declarations.
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72