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/StringExtras.h"
15#include "llvm/ADT/TypeSwitch.h"
16
17using namespace mlir;
18using namespace mlir::LLVM;
19
20//===----------------------------------------------------------------------===//
21// Printing.
22//===----------------------------------------------------------------------===//
23
24/// If the given type is compatible with the LLVM dialect, prints it using
25/// internal functions to avoid getting a verbose `!llvm` prefix. Otherwise
26/// prints it as usual.
27static void dispatchPrint(AsmPrinter &printer, Type type) {
28 if (isCompatibleType(type) &&
29 !llvm::isa<IntegerType, FloatType, VectorType>(Val: type))
30 return mlir::LLVM::detail::printType(type, printer);
31 printer.printType(type);
32}
33
34/// Returns the keyword to use for the given type.
35static StringRef getTypeKeyword(Type type) {
36 return TypeSwitch<Type, StringRef>(type)
37 .Case<LLVMVoidType>([&](Type) { return "void"; })
38 .Case<LLVMPPCFP128Type>([&](Type) { return "ppc_fp128"; })
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<LLVMArrayType>([&](Type) { return "array"; })
45 .Case<LLVMStructType>([&](Type) { return "struct"; })
46 .Case<LLVMTargetExtType>([&](Type) { return "target"; })
47 .Case<LLVMX86AMXType>([&](Type) { return "x86_amx"; })
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.
55void LLVMStructType::print(AsmPrinter &printer) const {
56 FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrint;
57
58 printer << "<";
59 if (isIdentified()) {
60 cyclicPrint = printer.tryStartCyclicPrint(*this);
61
62 printer << '"';
63 llvm::printEscapedString(getName(), printer.getStream());
64 printer << '"';
65 // If we are printing a reference to one of the enclosing structs, just
66 // print the name and stop to avoid infinitely long output.
67 if (failed(cyclicPrint)) {
68 printer << '>';
69 return;
70 }
71 printer << ", ";
72 }
73
74 if (isIdentified() && isOpaque()) {
75 printer << "opaque>";
76 return;
77 }
78
79 if (isPacked())
80 printer << "packed ";
81
82 // Put the current type on stack to avoid infinite recursion.
83 printer << '(';
84 llvm::interleaveComma(getBody(), printer.getStream(),
85 [&](Type subtype) { dispatchPrint(printer, subtype); });
86 printer << ')';
87 printer << '>';
88}
89
90/// Prints the given LLVM dialect type recursively. This leverages closedness of
91/// the LLVM dialect type system to avoid printing the dialect prefix
92/// repeatedly. For recursive structures, only prints the name of the structure
93/// when printing a self-reference. Note that this does not apply to sibling
94/// references. For example,
95/// struct<"a", (ptr<struct<"a">>)>
96/// struct<"c", (ptr<struct<"b", (ptr<struct<"c">>)>>,
97/// ptr<struct<"b", (ptr<struct<"c">>)>>)>
98/// note that "b" is printed twice.
99void mlir::LLVM::detail::printType(Type type, AsmPrinter &printer) {
100 if (!type) {
101 printer << "<<NULL-TYPE>>";
102 return;
103 }
104
105 printer << getTypeKeyword(type);
106
107 llvm::TypeSwitch<Type>(type)
108 .Case<LLVMPointerType, LLVMArrayType, LLVMFunctionType, LLVMTargetExtType,
109 LLVMStructType>([&](auto type) { type.print(printer); });
110}
111
112//===----------------------------------------------------------------------===//
113// Parsing.
114//===----------------------------------------------------------------------===//
115
116static ParseResult dispatchParse(AsmParser &parser, Type &type);
117
118/// Attempts to set the body of an identified structure type. Reports a parsing
119/// error at `subtypesLoc` in case of failure.
120static LLVMStructType trySetStructBody(LLVMStructType type,
121 ArrayRef<Type> subtypes, bool isPacked,
122 AsmParser &parser, SMLoc subtypesLoc) {
123 for (Type t : subtypes) {
124 if (!LLVMStructType::isValidElementType(t)) {
125 parser.emitError(loc: subtypesLoc)
126 << "invalid LLVM structure element type: " << t;
127 return LLVMStructType();
128 }
129 }
130
131 if (succeeded(type.setBody(subtypes, isPacked)))
132 return type;
133
134 parser.emitError(loc: subtypesLoc)
135 << "identified type already used with a different body";
136 return LLVMStructType();
137}
138
139/// Parses an LLVM dialect structure type.
140/// llvm-type ::= `struct<` (string-literal `,`)? `packed`?
141/// `(` llvm-type-list `)` `>`
142/// | `struct<` string-literal `>`
143/// | `struct<` string-literal `, opaque>`
144Type LLVMStructType::parse(AsmParser &parser) {
145 Location loc = parser.getEncodedSourceLoc(parser.getCurrentLocation());
146
147 if (failed(parser.parseLess()))
148 return LLVMStructType();
149
150 // If we are parsing a self-reference to a recursive struct, i.e. the parsing
151 // stack already contains a struct with the same identifier, bail out after
152 // the name.
153 std::string name;
154 bool isIdentified = succeeded(parser.parseOptionalString(&name));
155 if (isIdentified) {
156 SMLoc greaterLoc = parser.getCurrentLocation();
157 if (succeeded(parser.parseOptionalGreater())) {
158 auto type = LLVMStructType::getIdentifiedChecked(
159 [loc] { return emitError(loc); }, loc.getContext(), name);
160 if (succeeded(parser.tryStartCyclicParse(type))) {
161 parser.emitError(
162 greaterLoc,
163 "struct without a body only allowed in a recursive struct");
164 return nullptr;
165 }
166
167 return type;
168 }
169 if (failed(parser.parseComma()))
170 return LLVMStructType();
171 }
172
173 // Handle intentionally opaque structs.
174 SMLoc kwLoc = parser.getCurrentLocation();
175 if (succeeded(parser.parseOptionalKeyword("opaque"))) {
176 if (!isIdentified)
177 return parser.emitError(kwLoc, "only identified structs can be opaque"),
178 LLVMStructType();
179 if (failed(parser.parseGreater()))
180 return LLVMStructType();
181 auto type = LLVMStructType::getOpaqueChecked(
182 [loc] { return emitError(loc); }, loc.getContext(), name);
183 if (!type.isOpaque()) {
184 parser.emitError(kwLoc, "redeclaring defined struct as opaque");
185 return LLVMStructType();
186 }
187 return type;
188 }
189
190 FailureOr<AsmParser::CyclicParseReset> cyclicParse;
191 if (isIdentified) {
192 cyclicParse =
193 parser.tryStartCyclicParse(LLVMStructType::getIdentifiedChecked(
194 [loc] { return emitError(loc); }, loc.getContext(), name));
195 if (failed(cyclicParse)) {
196 parser.emitError(kwLoc,
197 "identifier already used for an enclosing struct");
198 return nullptr;
199 }
200 }
201
202 // Check for packedness.
203 bool isPacked = succeeded(parser.parseOptionalKeyword("packed"));
204 if (failed(parser.parseLParen()))
205 return LLVMStructType();
206
207 // Fast pass for structs with zero subtypes.
208 if (succeeded(parser.parseOptionalRParen())) {
209 if (failed(parser.parseGreater()))
210 return LLVMStructType();
211 if (!isIdentified)
212 return LLVMStructType::getLiteralChecked([loc] { return emitError(loc); },
213 loc.getContext(), {}, isPacked);
214 auto type = LLVMStructType::getIdentifiedChecked(
215 [loc] { return emitError(loc); }, loc.getContext(), name);
216 return trySetStructBody(type, {}, isPacked, parser, kwLoc);
217 }
218
219 // Parse subtypes. For identified structs, put the identifier of the struct on
220 // the stack to support self-references in the recursive calls.
221 SmallVector<Type, 4> subtypes;
222 SMLoc subtypesLoc = parser.getCurrentLocation();
223 do {
224 Type type;
225 if (dispatchParse(parser, type))
226 return LLVMStructType();
227 subtypes.push_back(type);
228 } while (succeeded(parser.parseOptionalComma()));
229
230 if (parser.parseRParen() || parser.parseGreater())
231 return LLVMStructType();
232
233 // Construct the struct with body.
234 if (!isIdentified)
235 return LLVMStructType::getLiteralChecked(
236 [loc] { return emitError(loc); }, loc.getContext(), subtypes, isPacked);
237 auto type = LLVMStructType::getIdentifiedChecked(
238 [loc] { return emitError(loc); }, loc.getContext(), name);
239 return trySetStructBody(type, subtypes, isPacked, parser, subtypesLoc);
240}
241
242/// Parses a type appearing inside another LLVM dialect-compatible type. This
243/// will try to parse any type in full form (including types with the `!llvm`
244/// prefix), and on failure fall back to parsing the short-hand version of the
245/// LLVM dialect types without the `!llvm` prefix.
246static Type dispatchParse(AsmParser &parser, bool allowAny = true) {
247 SMLoc keyLoc = parser.getCurrentLocation();
248
249 // Try parsing any MLIR type.
250 Type type;
251 OptionalParseResult result = parser.parseOptionalType(result&: type);
252 if (result.has_value()) {
253 if (failed(Result: result.value()))
254 return nullptr;
255 if (!allowAny) {
256 parser.emitError(loc: keyLoc) << "unexpected type, expected keyword";
257 return nullptr;
258 }
259 return type;
260 }
261
262 // If no type found, fallback to the shorthand form.
263 StringRef key;
264 if (failed(Result: parser.parseKeyword(keyword: &key)))
265 return Type();
266
267 MLIRContext *ctx = parser.getContext();
268 return StringSwitch<function_ref<Type()>>(key)
269 .Case(S: "void", Value: [&] { return LLVMVoidType::get(ctx); })
270 .Case("ppc_fp128", [&] { return LLVMPPCFP128Type::get(ctx); })
271 .Case(S: "token", Value: [&] { return LLVMTokenType::get(ctx); })
272 .Case(S: "label", Value: [&] { return LLVMLabelType::get(ctx); })
273 .Case(S: "metadata", Value: [&] { return LLVMMetadataType::get(ctx); })
274 .Case("func", [&] { return LLVMFunctionType::parse(parser); })
275 .Case("ptr", [&] { return LLVMPointerType::parse(parser); })
276 .Case("array", [&] { return LLVMArrayType::parse(parser); })
277 .Case("struct", [&] { return LLVMStructType::parse(parser); })
278 .Case("target", [&] { return LLVMTargetExtType::parse(parser); })
279 .Case("x86_amx", [&] { return LLVMX86AMXType::get(ctx); })
280 .Default(Value: [&] {
281 parser.emitError(loc: keyLoc) << "unknown LLVM type: " << key;
282 return Type();
283 })();
284}
285
286/// Helper to use in parse lists.
287static ParseResult dispatchParse(AsmParser &parser, Type &type) {
288 type = dispatchParse(parser);
289 return success(IsSuccess: type != nullptr);
290}
291
292/// Parses one of the LLVM dialect types.
293Type mlir::LLVM::detail::parseType(DialectAsmParser &parser) {
294 SMLoc loc = parser.getCurrentLocation();
295 Type type = dispatchParse(parser, /*allowAny=*/false);
296 if (!type)
297 return type;
298 if (!isCompatibleOuterType(type)) {
299 parser.emitError(loc) << "unexpected type, expected keyword";
300 return nullptr;
301 }
302 return type;
303}
304
305ParseResult LLVM::parsePrettyLLVMType(AsmParser &p, Type &type) {
306 return dispatchParse(parser&: p, type);
307}
308
309void LLVM::printPrettyLLVMType(AsmPrinter &p, Type type) {
310 return dispatchPrint(printer&: p, type);
311}
312

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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