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