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
9#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
10#include "mlir/IR/Builders.h"
11#include "mlir/IR/DialectImplementation.h"
12#include "llvm/ADT/ScopeExit.h"
13#include "llvm/ADT/SetVector.h"
14#include "llvm/ADT/TypeSwitch.h"
15
16using namespace mlir;
17using 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.
26static void dispatchPrint(AsmPrinter &printer, Type type) {
27 if (isCompatibleType(type) &&
28 !llvm::isa<IntegerType, FloatType, VectorType>(Val: 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.
34static 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.
56static void printStructType(AsmPrinter &printer, LLVMStructType type) {
57 FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrint;
58
59 printer << "<";
60 if (type.isIdentified()) {
61 cyclicPrint = printer.tryStartCyclicPrint(attrOrType: 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(result: 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(c: type.getBody(), os&: printer.getStream(),
84 each_fn: [&](Type subtype) { dispatchPrint(printer, type: 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.
98void 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
119static 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.
124static 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(loc: &dimPos) ||
130 parser.parseDimensionList(dimensions&: dims, /*allowDynamic=*/true) ||
131 parser.getCurrentLocation(loc: &typePos) ||
132 dispatchParse(parser, type&: 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(loc: 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(loc: 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.
161static LLVMStructType trySetStructBody(LLVMStructType type,
162 ArrayRef<Type> subtypes, bool isPacked,
163 AsmParser &parser, SMLoc subtypesLoc) {
164 for (Type t : subtypes) {
165 if (!LLVMStructType::isValidElementType(type: t)) {
166 parser.emitError(loc: subtypesLoc)
167 << "invalid LLVM structure element type: " << t;
168 return LLVMStructType();
169 }
170 }
171
172 if (succeeded(result: type.setBody(types: subtypes, isPacked)))
173 return type;
174
175 parser.emitError(loc: 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>`
185static LLVMStructType parseStructType(AsmParser &parser) {
186 Location loc = parser.getEncodedSourceLoc(loc: parser.getCurrentLocation());
187
188 if (failed(result: 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(result: parser.parseOptionalString(string: &name));
196 if (isIdentified) {
197 SMLoc greaterLoc = parser.getCurrentLocation();
198 if (succeeded(result: parser.parseOptionalGreater())) {
199 auto type = LLVMStructType::getIdentifiedChecked(
200 emitError: [loc] { return emitError(loc); }, context: loc.getContext(), name);
201 if (succeeded(result: parser.tryStartCyclicParse(attrOrType: type))) {
202 parser.emitError(
203 loc: greaterLoc,
204 message: "struct without a body only allowed in a recursive struct");
205 return nullptr;
206 }
207
208 return type;
209 }
210 if (failed(result: parser.parseComma()))
211 return LLVMStructType();
212 }
213
214 // Handle intentionally opaque structs.
215 SMLoc kwLoc = parser.getCurrentLocation();
216 if (succeeded(result: parser.parseOptionalKeyword(keyword: "opaque"))) {
217 if (!isIdentified)
218 return parser.emitError(loc: kwLoc, message: "only identified structs can be opaque"),
219 LLVMStructType();
220 if (failed(result: parser.parseGreater()))
221 return LLVMStructType();
222 auto type = LLVMStructType::getOpaqueChecked(
223 emitError: [loc] { return emitError(loc); }, context: loc.getContext(), name);
224 if (!type.isOpaque()) {
225 parser.emitError(loc: kwLoc, message: "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(attrOrType: LLVMStructType::getIdentifiedChecked(
235 emitError: [loc] { return emitError(loc); }, context: loc.getContext(), name));
236 if (failed(result: cyclicParse)) {
237 parser.emitError(loc: kwLoc,
238 message: "identifier already used for an enclosing struct");
239 return nullptr;
240 }
241 }
242
243 // Check for packedness.
244 bool isPacked = succeeded(result: parser.parseOptionalKeyword(keyword: "packed"));
245 if (failed(result: parser.parseLParen()))
246 return LLVMStructType();
247
248 // Fast pass for structs with zero subtypes.
249 if (succeeded(result: parser.parseOptionalRParen())) {
250 if (failed(result: parser.parseGreater()))
251 return LLVMStructType();
252 if (!isIdentified)
253 return LLVMStructType::getLiteralChecked(emitError: [loc] { return emitError(loc); },
254 context: loc.getContext(), types: {}, isPacked);
255 auto type = LLVMStructType::getIdentifiedChecked(
256 emitError: [loc] { return emitError(loc); }, context: loc.getContext(), name);
257 return trySetStructBody(type, subtypes: {}, isPacked, parser, subtypesLoc: 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(Elt: type);
269 } while (succeeded(result: 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 emitError: [loc] { return emitError(loc); }, context: loc.getContext(), types: subtypes, isPacked);
278 auto type = LLVMStructType::getIdentifiedChecked(
279 emitError: [loc] { return emitError(loc); }, context: 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.
287static 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(result&: type);
293 if (result.has_value()) {
294 if (failed(result: result.value()))
295 return nullptr;
296 if (!allowAny) {
297 parser.emitError(loc: 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(result: parser.parseKeyword(keyword: &key)))
306 return Type();
307
308 MLIRContext *ctx = parser.getContext();
309 return StringSwitch<function_ref<Type()>>(key)
310 .Case(S: "void", Value: [&] { return LLVMVoidType::get(ctx); })
311 .Case(S: "ppc_fp128", Value: [&] { return LLVMPPCFP128Type::get(ctx); })
312 .Case(S: "x86_mmx", Value: [&] { return LLVMX86MMXType::get(ctx); })
313 .Case(S: "token", Value: [&] { return LLVMTokenType::get(ctx); })
314 .Case(S: "label", Value: [&] { return LLVMLabelType::get(ctx); })
315 .Case(S: "metadata", Value: [&] { return LLVMMetadataType::get(ctx); })
316 .Case("func", [&] { return LLVMFunctionType::parse(parser); })
317 .Case("ptr", [&] { return LLVMPointerType::parse(parser); })
318 .Case(S: "vec", Value: [&] { 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(Value: [&] {
323 parser.emitError(loc: keyLoc) << "unknown LLVM type: " << key;
324 return Type();
325 })();
326}
327
328/// Helper to use in parse lists.
329static ParseResult dispatchParse(AsmParser &parser, Type &type) {
330 type = dispatchParse(parser);
331 return success(isSuccess: type != nullptr);
332}
333
334/// Parses one of the LLVM dialect types.
335Type 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
347ParseResult LLVM::parsePrettyLLVMType(AsmParser &p, Type &type) {
348 return dispatchParse(parser&: p, type);
349}
350
351void LLVM::printPrettyLLVMType(AsmPrinter &p, Type type) {
352 return dispatchPrint(printer&: p, type);
353}
354

source code of mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp