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 "mlir/Support/LogicalResult.h" |
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::parseNameOrFileLineColLocation(LocationAttr &loc) { |
102 | auto *ctx = getContext(); |
103 | auto str = getToken().getStringValue(); |
104 | consumeToken(kind: Token::string); |
105 | |
106 | // If the next token is ':' this is a filelinecol location. |
107 | if (consumeIf(kind: Token::colon)) { |
108 | // Parse the line number. |
109 | if (getToken().isNot(k: Token::integer)) |
110 | return emitWrongTokenError( |
111 | message: "expected integer line number in FileLineColLoc" ); |
112 | auto line = getToken().getUnsignedIntegerValue(); |
113 | if (!line) |
114 | return emitWrongTokenError( |
115 | message: "expected integer line number in FileLineColLoc" ); |
116 | consumeToken(kind: Token::integer); |
117 | |
118 | // Parse the ':'. |
119 | if (parseToken(expectedToken: Token::colon, message: "expected ':' in FileLineColLoc" )) |
120 | return failure(); |
121 | |
122 | // Parse the column number. |
123 | if (getToken().isNot(k: Token::integer)) |
124 | return emitWrongTokenError( |
125 | message: "expected integer column number in FileLineColLoc" ); |
126 | auto column = getToken().getUnsignedIntegerValue(); |
127 | if (!column.has_value()) |
128 | return emitError(message: "expected integer column number in FileLineColLoc" ); |
129 | consumeToken(kind: Token::integer); |
130 | |
131 | loc = FileLineColLoc::get(ctx, str, *line, *column); |
132 | return success(); |
133 | } |
134 | |
135 | // Otherwise, this is a NameLoc. |
136 | |
137 | // Check for a child location. |
138 | if (consumeIf(kind: Token::l_paren)) { |
139 | // Parse the child location. |
140 | LocationAttr childLoc; |
141 | if (parseLocationInstance(loc&: childLoc)) |
142 | return failure(); |
143 | |
144 | loc = NameLoc::get(StringAttr::get(ctx, str), childLoc); |
145 | |
146 | // Parse the closing ')'. |
147 | if (parseToken(expectedToken: Token::r_paren, |
148 | message: "expected ')' after child location of NameLoc" )) |
149 | return failure(); |
150 | } else { |
151 | loc = NameLoc::get(StringAttr::get(ctx, str)); |
152 | } |
153 | |
154 | return success(); |
155 | } |
156 | |
157 | ParseResult Parser::parseLocationInstance(LocationAttr &loc) { |
158 | // Handle aliases. |
159 | if (getToken().is(k: Token::hash_identifier)) { |
160 | Attribute locAttr = parseExtendedAttr(type: Type()); |
161 | if (!locAttr) |
162 | return failure(); |
163 | if (!(loc = dyn_cast<LocationAttr>(Val&: locAttr))) |
164 | return emitError(message: "expected location attribute, but got" ) << locAttr; |
165 | return success(); |
166 | } |
167 | |
168 | // Handle either name or filelinecol locations. |
169 | if (getToken().is(k: Token::string)) |
170 | return parseNameOrFileLineColLocation(loc); |
171 | |
172 | // Bare tokens required for other cases. |
173 | if (!getToken().is(k: Token::bare_identifier)) |
174 | return emitWrongTokenError(message: "expected location instance" ); |
175 | |
176 | // Check for the 'callsite' signifying a callsite location. |
177 | if (getToken().getSpelling() == "callsite" ) |
178 | return parseCallSiteLocation(loc); |
179 | |
180 | // If the token is 'fused', then this is a fused location. |
181 | if (getToken().getSpelling() == "fused" ) |
182 | return parseFusedLocation(loc); |
183 | |
184 | // Check for a 'unknown' for an unknown location. |
185 | if (getToken().getSpelling() == "unknown" ) { |
186 | consumeToken(kind: Token::bare_identifier); |
187 | loc = UnknownLoc::get(getContext()); |
188 | return success(); |
189 | } |
190 | |
191 | return emitWrongTokenError(message: "expected location instance" ); |
192 | } |
193 | |