1 | //===- LocationParser.cpp - MLIR Location Parser -------------------------===// |
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 "Parser.h" |
10 | #include "Token.h" |
11 | #include "mlir/IR/Attributes.h" |
12 | #include "mlir/IR/BuiltinAttributes.h" |
13 | #include "mlir/IR/Location.h" |
14 | #include "mlir/Support/LLVM.h" |
15 | #include <optional> |
16 | |
17 | using namespace mlir; |
18 | using namespace mlir::detail; |
19 | |
20 | /// Specific location instances. |
21 | /// |
22 | /// location-inst ::= filelinecol-location | |
23 | /// name-location | |
24 | /// callsite-location | |
25 | /// fused-location | |
26 | /// unknown-location |
27 | /// filelinecol-location ::= string-literal ':' integer-literal |
28 | /// ':' integer-literal |
29 | /// name-location ::= string-literal |
30 | /// callsite-location ::= 'callsite' '(' location-inst 'at' location-inst ')' |
31 | /// fused-location ::= fused ('<' attribute-value '>')? |
32 | /// '[' location-inst (location-inst ',')* ']' |
33 | /// unknown-location ::= 'unknown' |
34 | /// |
35 | ParseResult Parser::parseCallSiteLocation(LocationAttr &loc) { |
36 | consumeToken(kind: Token::bare_identifier); |
37 | |
38 | // Parse the '('. |
39 | if (parseToken(expectedToken: Token::l_paren, message: "expected '(' in callsite location" )) |
40 | return failure(); |
41 | |
42 | // Parse the callee location. |
43 | LocationAttr calleeLoc; |
44 | if (parseLocationInstance(loc&: calleeLoc)) |
45 | return failure(); |
46 | |
47 | // Parse the 'at'. |
48 | if (getToken().isNot(k: Token::bare_identifier) || |
49 | getToken().getSpelling() != "at" ) |
50 | return emitWrongTokenError(message: "expected 'at' in callsite location" ); |
51 | consumeToken(kind: Token::bare_identifier); |
52 | |
53 | // Parse the caller location. |
54 | LocationAttr callerLoc; |
55 | if (parseLocationInstance(loc&: callerLoc)) |
56 | return failure(); |
57 | |
58 | // Parse the ')'. |
59 | if (parseToken(expectedToken: Token::r_paren, message: "expected ')' in callsite location" )) |
60 | return failure(); |
61 | |
62 | // Return the callsite location. |
63 | loc = CallSiteLoc::get(calleeLoc, callerLoc); |
64 | return success(); |
65 | } |
66 | |
67 | ParseResult Parser::parseFusedLocation(LocationAttr &loc) { |
68 | consumeToken(kind: Token::bare_identifier); |
69 | |
70 | // Try to parse the optional metadata. |
71 | Attribute metadata; |
72 | if (consumeIf(kind: Token::less)) { |
73 | metadata = parseAttribute(); |
74 | if (!metadata) |
75 | return failure(); |
76 | |
77 | // Parse the '>' token. |
78 | if (parseToken(expectedToken: Token::greater, |
79 | message: "expected '>' after fused location metadata" )) |
80 | return failure(); |
81 | } |
82 | |
83 | SmallVector<Location, 4> locations; |
84 | auto parseElt = [&] { |
85 | LocationAttr newLoc; |
86 | if (parseLocationInstance(loc&: newLoc)) |
87 | return failure(); |
88 | locations.push_back(Elt: newLoc); |
89 | return success(); |
90 | }; |
91 | |
92 | if (parseCommaSeparatedList(delimiter: Delimiter::Square, parseElementFn: parseElt, |
93 | contextMessage: " in fused location" )) |
94 | return failure(); |
95 | |
96 | // Return the fused location. |
97 | loc = FusedLoc::get(locations, metadata, getContext()); |
98 | return success(); |
99 | } |
100 | |
101 | ParseResult Parser::parseNameOrFileLineColRange(LocationAttr &loc) { |
102 | auto *ctx = getContext(); |
103 | auto str = getToken().getStringValue(); |
104 | consumeToken(kind: Token::string); |
105 | |
106 | std::optional<unsigned> startLine, startColumn, endLine, endColumn; |
107 | |
108 | // If the next token is ':' this is a filelinecol location. |
109 | if (consumeIf(kind: Token::colon)) { |
110 | // Parse the line number. |
111 | if (getToken().isNot(k: Token::integer)) |
112 | return emitWrongTokenError( |
113 | message: "expected integer line number in FileLineColRange" ); |
114 | startLine = getToken().getUnsignedIntegerValue(); |
115 | if (!startLine) |
116 | return emitWrongTokenError( |
117 | message: "expected integer line number in FileLineColRange" ); |
118 | consumeToken(kind: Token::integer); |
119 | |
120 | // Parse the ':'. |
121 | if (getToken().isNot(k: Token::colon)) { |
122 | loc = FileLineColRange::get(StringAttr::get(ctx, str), *startLine); |
123 | return success(); |
124 | } |
125 | consumeToken(kind: Token::colon); |
126 | |
127 | // Parse the column number. |
128 | if (getToken().isNot(k: Token::integer)) { |
129 | return emitWrongTokenError( |
130 | message: "expected integer column number in FileLineColRange" ); |
131 | } |
132 | startColumn = getToken().getUnsignedIntegerValue(); |
133 | if (!startColumn.has_value()) |
134 | return emitError(message: "expected integer column number in FileLineColRange" ); |
135 | consumeToken(kind: Token::integer); |
136 | |
137 | if (!isCurrentTokenAKeyword() || getTokenSpelling() != "to" ) { |
138 | loc = FileLineColLoc::get(context: ctx, fileName: str, line: *startLine, column: *startColumn); |
139 | return success(); |
140 | } |
141 | consumeToken(); |
142 | |
143 | // Parse the line number. |
144 | if (getToken().is(k: Token::integer)) { |
145 | endLine = getToken().getUnsignedIntegerValue(); |
146 | if (!endLine) { |
147 | return emitWrongTokenError( |
148 | message: "expected integer line number in FileLineColRange" ); |
149 | } |
150 | consumeToken(kind: Token::integer); |
151 | } |
152 | |
153 | // Parse the ':'. |
154 | if (getToken().isNot(k: Token::colon)) { |
155 | return emitWrongTokenError( |
156 | message: "expected either integer or `:` post `to` in FileLineColRange" ); |
157 | } |
158 | consumeToken(kind: Token::colon); |
159 | |
160 | // Parse the column number. |
161 | if (getToken().isNot(k: Token::integer)) { |
162 | return emitWrongTokenError( |
163 | message: "expected integer column number in FileLineColRange" ); |
164 | } |
165 | endColumn = getToken().getUnsignedIntegerValue(); |
166 | if (!endColumn.has_value()) |
167 | return emitError(message: "expected integer column number in FileLineColRange" ); |
168 | consumeToken(kind: Token::integer); |
169 | |
170 | if (endLine.has_value()) { |
171 | loc = FileLineColRange::get(StringAttr::get(ctx, str), *startLine, |
172 | *startColumn, *endLine, *endColumn); |
173 | } else { |
174 | loc = FileLineColRange::get(StringAttr::get(ctx, str), *startLine, |
175 | *startColumn, *endColumn); |
176 | } |
177 | return success(); |
178 | } |
179 | |
180 | // Otherwise, this is a NameLoc. |
181 | |
182 | // Check for a child location. |
183 | if (consumeIf(kind: Token::l_paren)) { |
184 | // Parse the child location. |
185 | LocationAttr childLoc; |
186 | if (parseLocationInstance(loc&: childLoc)) |
187 | return failure(); |
188 | |
189 | loc = NameLoc::get(StringAttr::get(ctx, str), childLoc); |
190 | |
191 | // Parse the closing ')'. |
192 | if (parseToken(expectedToken: Token::r_paren, |
193 | message: "expected ')' after child location of NameLoc" )) |
194 | return failure(); |
195 | } else { |
196 | loc = NameLoc::get(StringAttr::get(ctx, str)); |
197 | } |
198 | |
199 | return success(); |
200 | } |
201 | |
202 | ParseResult Parser::parseLocationInstance(LocationAttr &loc) { |
203 | // Handle aliases. |
204 | if (getToken().is(k: Token::hash_identifier)) { |
205 | Attribute locAttr = parseExtendedAttr(type: Type()); |
206 | if (!locAttr) |
207 | return failure(); |
208 | if (!(loc = dyn_cast<LocationAttr>(Val&: locAttr))) |
209 | return emitError(message: "expected location attribute, but got" ) << locAttr; |
210 | return success(); |
211 | } |
212 | |
213 | // Handle either name or filelinecol locations. |
214 | if (getToken().is(k: Token::string)) |
215 | return parseNameOrFileLineColRange(loc); |
216 | |
217 | // Bare tokens required for other cases. |
218 | if (!getToken().is(k: Token::bare_identifier)) |
219 | return emitWrongTokenError(message: "expected location instance" ); |
220 | |
221 | // Check for the 'callsite' signifying a callsite location. |
222 | if (getToken().getSpelling() == "callsite" ) |
223 | return parseCallSiteLocation(loc); |
224 | |
225 | // If the token is 'fused', then this is a fused location. |
226 | if (getToken().getSpelling() == "fused" ) |
227 | return parseFusedLocation(loc); |
228 | |
229 | // Check for a 'unknown' for an unknown location. |
230 | if (getToken().getSpelling() == "unknown" ) { |
231 | consumeToken(kind: Token::bare_identifier); |
232 | loc = UnknownLoc::get(getContext()); |
233 | return success(); |
234 | } |
235 | |
236 | return emitWrongTokenError(message: "expected location instance" ); |
237 | } |
238 | |