1 //===- AffineParser.cpp - MLIR Affine 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 // This file implements a parser for Affine structures.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "Parser.h"
14 #include "mlir/IR/AffineMap.h"
15 #include "mlir/IR/IntegerSet.h"
16 #include "llvm/Support/SourceMgr.h"
17
18 using namespace mlir;
19 using namespace mlir::detail;
20
21 namespace {
22
23 /// Lower precedence ops (all at the same precedence level). LNoOp is false in
24 /// the boolean sense.
25 enum AffineLowPrecOp {
26 /// Null value.
27 LNoOp,
28 Add,
29 Sub
30 };
31
32 /// Higher precedence ops - all at the same precedence level. HNoOp is false
33 /// in the boolean sense.
34 enum AffineHighPrecOp {
35 /// Null value.
36 HNoOp,
37 Mul,
38 FloorDiv,
39 CeilDiv,
40 Mod
41 };
42
43 /// This is a specialized parser for affine structures (affine maps, affine
44 /// expressions, and integer sets), maintaining the state transient to their
45 /// bodies.
46 class AffineParser : public Parser {
47 public:
AffineParser(ParserState & state,bool allowParsingSSAIds=false,function_ref<ParseResult (bool)> parseElement=nullptr)48 AffineParser(ParserState &state, bool allowParsingSSAIds = false,
49 function_ref<ParseResult(bool)> parseElement = nullptr)
50 : Parser(state), allowParsingSSAIds(allowParsingSSAIds),
51 parseElement(parseElement) {}
52
53 ParseResult parseAffineMapRange(unsigned numDims, unsigned numSymbols,
54 AffineMap &result);
55 ParseResult parseAffineMapOrIntegerSetInline(AffineMap &map, IntegerSet &set);
56 ParseResult parseIntegerSetConstraints(unsigned numDims, unsigned numSymbols,
57 IntegerSet &result);
58 ParseResult parseAffineMapOfSSAIds(AffineMap &map,
59 OpAsmParser::Delimiter delimiter);
60 ParseResult parseAffineExprOfSSAIds(AffineExpr &expr);
61 void getDimsAndSymbolSSAIds(SmallVectorImpl<StringRef> &dimAndSymbolSSAIds,
62 unsigned &numDims);
63
64 private:
65 // Binary affine op parsing.
66 AffineLowPrecOp consumeIfLowPrecOp();
67 AffineHighPrecOp consumeIfHighPrecOp();
68
69 // Identifier lists for polyhedral structures.
70 ParseResult parseDimIdList(unsigned &numDims);
71 ParseResult parseSymbolIdList(unsigned &numSymbols);
72 ParseResult parseDimAndOptionalSymbolIdList(unsigned &numDims,
73 unsigned &numSymbols);
74 ParseResult parseIdentifierDefinition(AffineExpr idExpr);
75
76 AffineExpr parseAffineExpr();
77 AffineExpr parseParentheticalExpr();
78 AffineExpr parseNegateExpression(AffineExpr lhs);
79 AffineExpr parseIntegerExpr();
80 AffineExpr parseBareIdExpr();
81 AffineExpr parseSSAIdExpr(bool isSymbol);
82 AffineExpr parseSymbolSSAIdExpr();
83
84 AffineExpr getAffineBinaryOpExpr(AffineHighPrecOp op, AffineExpr lhs,
85 AffineExpr rhs, SMLoc opLoc);
86 AffineExpr getAffineBinaryOpExpr(AffineLowPrecOp op, AffineExpr lhs,
87 AffineExpr rhs);
88 AffineExpr parseAffineOperandExpr(AffineExpr lhs);
89 AffineExpr parseAffineLowPrecOpExpr(AffineExpr llhs, AffineLowPrecOp llhsOp);
90 AffineExpr parseAffineHighPrecOpExpr(AffineExpr llhs, AffineHighPrecOp llhsOp,
91 SMLoc llhsOpLoc);
92 AffineExpr parseAffineConstraint(bool *isEq);
93
94 private:
95 bool allowParsingSSAIds;
96 function_ref<ParseResult(bool)> parseElement;
97 unsigned numDimOperands = 0;
98 unsigned numSymbolOperands = 0;
99 SmallVector<std::pair<StringRef, AffineExpr>, 4> dimsAndSymbols;
100 };
101 } // namespace
102
103 /// Create an affine binary high precedence op expression (mul's, div's, mod).
104 /// opLoc is the location of the op token to be used to report errors
105 /// for non-conforming expressions.
getAffineBinaryOpExpr(AffineHighPrecOp op,AffineExpr lhs,AffineExpr rhs,SMLoc opLoc)106 AffineExpr AffineParser::getAffineBinaryOpExpr(AffineHighPrecOp op,
107 AffineExpr lhs, AffineExpr rhs,
108 SMLoc opLoc) {
109 // TODO: make the error location info accurate.
110 switch (op) {
111 case Mul:
112 if (!lhs.isSymbolicOrConstant() && !rhs.isSymbolicOrConstant()) {
113 emitError(opLoc, "non-affine expression: at least one of the multiply "
114 "operands has to be either a constant or symbolic");
115 return nullptr;
116 }
117 return lhs * rhs;
118 case FloorDiv:
119 if (!rhs.isSymbolicOrConstant()) {
120 emitError(opLoc, "non-affine expression: right operand of floordiv "
121 "has to be either a constant or symbolic");
122 return nullptr;
123 }
124 return lhs.floorDiv(rhs);
125 case CeilDiv:
126 if (!rhs.isSymbolicOrConstant()) {
127 emitError(opLoc, "non-affine expression: right operand of ceildiv "
128 "has to be either a constant or symbolic");
129 return nullptr;
130 }
131 return lhs.ceilDiv(rhs);
132 case Mod:
133 if (!rhs.isSymbolicOrConstant()) {
134 emitError(opLoc, "non-affine expression: right operand of mod "
135 "has to be either a constant or symbolic");
136 return nullptr;
137 }
138 return lhs % rhs;
139 case HNoOp:
140 llvm_unreachable("can't create affine expression for null high prec op");
141 return nullptr;
142 }
143 llvm_unreachable("Unknown AffineHighPrecOp");
144 }
145
146 /// Create an affine binary low precedence op expression (add, sub).
getAffineBinaryOpExpr(AffineLowPrecOp op,AffineExpr lhs,AffineExpr rhs)147 AffineExpr AffineParser::getAffineBinaryOpExpr(AffineLowPrecOp op,
148 AffineExpr lhs, AffineExpr rhs) {
149 switch (op) {
150 case AffineLowPrecOp::Add:
151 return lhs + rhs;
152 case AffineLowPrecOp::Sub:
153 return lhs - rhs;
154 case AffineLowPrecOp::LNoOp:
155 llvm_unreachable("can't create affine expression for null low prec op");
156 return nullptr;
157 }
158 llvm_unreachable("Unknown AffineLowPrecOp");
159 }
160
161 /// Consume this token if it is a lower precedence affine op (there are only
162 /// two precedence levels).
consumeIfLowPrecOp()163 AffineLowPrecOp AffineParser::consumeIfLowPrecOp() {
164 switch (getToken().getKind()) {
165 case Token::plus:
166 consumeToken(Token::plus);
167 return AffineLowPrecOp::Add;
168 case Token::minus:
169 consumeToken(Token::minus);
170 return AffineLowPrecOp::Sub;
171 default:
172 return AffineLowPrecOp::LNoOp;
173 }
174 }
175
176 /// Consume this token if it is a higher precedence affine op (there are only
177 /// two precedence levels)
consumeIfHighPrecOp()178 AffineHighPrecOp AffineParser::consumeIfHighPrecOp() {
179 switch (getToken().getKind()) {
180 case Token::star:
181 consumeToken(Token::star);
182 return Mul;
183 case Token::kw_floordiv:
184 consumeToken(Token::kw_floordiv);
185 return FloorDiv;
186 case Token::kw_ceildiv:
187 consumeToken(Token::kw_ceildiv);
188 return CeilDiv;
189 case Token::kw_mod:
190 consumeToken(Token::kw_mod);
191 return Mod;
192 default:
193 return HNoOp;
194 }
195 }
196
197 /// Parse a high precedence op expression list: mul, div, and mod are high
198 /// precedence binary ops, i.e., parse a
199 /// expr_1 op_1 expr_2 op_2 ... expr_n
200 /// where op_1, op_2 are all a AffineHighPrecOp (mul, div, mod).
201 /// All affine binary ops are left associative.
202 /// Given llhs, returns (llhs llhsOp lhs) op rhs, or (lhs op rhs) if llhs is
203 /// null. If no rhs can be found, returns (llhs llhsOp lhs) or lhs if llhs is
204 /// null. llhsOpLoc is the location of the llhsOp token that will be used to
205 /// report an error for non-conforming expressions.
parseAffineHighPrecOpExpr(AffineExpr llhs,AffineHighPrecOp llhsOp,SMLoc llhsOpLoc)206 AffineExpr AffineParser::parseAffineHighPrecOpExpr(AffineExpr llhs,
207 AffineHighPrecOp llhsOp,
208 SMLoc llhsOpLoc) {
209 AffineExpr lhs = parseAffineOperandExpr(llhs);
210 if (!lhs)
211 return nullptr;
212
213 // Found an LHS. Parse the remaining expression.
214 auto opLoc = getToken().getLoc();
215 if (AffineHighPrecOp op = consumeIfHighPrecOp()) {
216 if (llhs) {
217 AffineExpr expr = getAffineBinaryOpExpr(llhsOp, llhs, lhs, opLoc);
218 if (!expr)
219 return nullptr;
220 return parseAffineHighPrecOpExpr(expr, op, opLoc);
221 }
222 // No LLHS, get RHS
223 return parseAffineHighPrecOpExpr(lhs, op, opLoc);
224 }
225
226 // This is the last operand in this expression.
227 if (llhs)
228 return getAffineBinaryOpExpr(llhsOp, llhs, lhs, llhsOpLoc);
229
230 // No llhs, 'lhs' itself is the expression.
231 return lhs;
232 }
233
234 /// Parse an affine expression inside parentheses.
235 ///
236 /// affine-expr ::= `(` affine-expr `)`
parseParentheticalExpr()237 AffineExpr AffineParser::parseParentheticalExpr() {
238 if (parseToken(Token::l_paren, "expected '('"))
239 return nullptr;
240 if (getToken().is(Token::r_paren))
241 return emitError("no expression inside parentheses"), nullptr;
242
243 auto expr = parseAffineExpr();
244 if (!expr || parseToken(Token::r_paren, "expected ')'"))
245 return nullptr;
246
247 return expr;
248 }
249
250 /// Parse the negation expression.
251 ///
252 /// affine-expr ::= `-` affine-expr
parseNegateExpression(AffineExpr lhs)253 AffineExpr AffineParser::parseNegateExpression(AffineExpr lhs) {
254 if (parseToken(Token::minus, "expected '-'"))
255 return nullptr;
256
257 AffineExpr operand = parseAffineOperandExpr(lhs);
258 // Since negation has the highest precedence of all ops (including high
259 // precedence ops) but lower than parentheses, we are only going to use
260 // parseAffineOperandExpr instead of parseAffineExpr here.
261 if (!operand)
262 // Extra error message although parseAffineOperandExpr would have
263 // complained. Leads to a better diagnostic.
264 return emitError("missing operand of negation"), nullptr;
265 return (-1) * operand;
266 }
267
268 /// Returns true if the given token can be represented as an identifier.
isIdentifier(const Token & token)269 static bool isIdentifier(const Token &token) {
270 // We include only `inttype` and `bare_identifier` here since they are the
271 // only non-keyword tokens that can be used to represent an identifier.
272 return token.isAny(Token::bare_identifier, Token::inttype) ||
273 token.isKeyword();
274 }
275
276 /// Parse a bare id that may appear in an affine expression.
277 ///
278 /// affine-expr ::= bare-id
parseBareIdExpr()279 AffineExpr AffineParser::parseBareIdExpr() {
280 if (!isIdentifier(getToken()))
281 return emitWrongTokenError("expected bare identifier"), nullptr;
282
283 StringRef sRef = getTokenSpelling();
284 for (auto entry : dimsAndSymbols) {
285 if (entry.first == sRef) {
286 consumeToken();
287 return entry.second;
288 }
289 }
290
291 return emitWrongTokenError("use of undeclared identifier"), nullptr;
292 }
293
294 /// Parse an SSA id which may appear in an affine expression.
parseSSAIdExpr(bool isSymbol)295 AffineExpr AffineParser::parseSSAIdExpr(bool isSymbol) {
296 if (!allowParsingSSAIds)
297 return emitWrongTokenError("unexpected ssa identifier"), nullptr;
298 if (getToken().isNot(Token::percent_identifier))
299 return emitWrongTokenError("expected ssa identifier"), nullptr;
300 auto name = getTokenSpelling();
301 // Check if we already parsed this SSA id.
302 for (auto entry : dimsAndSymbols) {
303 if (entry.first == name) {
304 consumeToken(Token::percent_identifier);
305 return entry.second;
306 }
307 }
308 // Parse the SSA id and add an AffineDim/SymbolExpr to represent it.
309 if (parseElement(isSymbol))
310 return nullptr;
311 auto idExpr = isSymbol
312 ? getAffineSymbolExpr(numSymbolOperands++, getContext())
313 : getAffineDimExpr(numDimOperands++, getContext());
314 dimsAndSymbols.push_back({name, idExpr});
315 return idExpr;
316 }
317
parseSymbolSSAIdExpr()318 AffineExpr AffineParser::parseSymbolSSAIdExpr() {
319 if (parseToken(Token::kw_symbol, "expected symbol keyword") ||
320 parseToken(Token::l_paren, "expected '(' at start of SSA symbol"))
321 return nullptr;
322 AffineExpr symbolExpr = parseSSAIdExpr(/*isSymbol=*/true);
323 if (!symbolExpr)
324 return nullptr;
325 if (parseToken(Token::r_paren, "expected ')' at end of SSA symbol"))
326 return nullptr;
327 return symbolExpr;
328 }
329
330 /// Parse a positive integral constant appearing in an affine expression.
331 ///
332 /// affine-expr ::= integer-literal
parseIntegerExpr()333 AffineExpr AffineParser::parseIntegerExpr() {
334 auto val = getToken().getUInt64IntegerValue();
335 if (!val.has_value() || (int64_t)val.value() < 0)
336 return emitError("constant too large for index"), nullptr;
337
338 consumeToken(Token::integer);
339 return builder.getAffineConstantExpr((int64_t)val.value());
340 }
341
342 /// Parses an expression that can be a valid operand of an affine expression.
343 /// lhs: if non-null, lhs is an affine expression that is the lhs of a binary
344 /// operator, the rhs of which is being parsed. This is used to determine
345 /// whether an error should be emitted for a missing right operand.
346 // Eg: for an expression without parentheses (like i + j + k + l), each
347 // of the four identifiers is an operand. For i + j*k + l, j*k is not an
348 // operand expression, it's an op expression and will be parsed via
349 // parseAffineHighPrecOpExpression(). However, for i + (j*k) + -l, (j*k) and
350 // -l are valid operands that will be parsed by this function.
parseAffineOperandExpr(AffineExpr lhs)351 AffineExpr AffineParser::parseAffineOperandExpr(AffineExpr lhs) {
352 switch (getToken().getKind()) {
353 case Token::kw_symbol:
354 return parseSymbolSSAIdExpr();
355 case Token::percent_identifier:
356 return parseSSAIdExpr(/*isSymbol=*/false);
357 case Token::integer:
358 return parseIntegerExpr();
359 case Token::l_paren:
360 return parseParentheticalExpr();
361 case Token::minus:
362 return parseNegateExpression(lhs);
363 case Token::kw_ceildiv:
364 case Token::kw_floordiv:
365 case Token::kw_mod:
366 // Try to treat these tokens as identifiers.
367 return parseBareIdExpr();
368 case Token::plus:
369 case Token::star:
370 if (lhs)
371 emitError("missing right operand of binary operator");
372 else
373 emitError("missing left operand of binary operator");
374 return nullptr;
375 default:
376 // If nothing matches, we try to treat this token as an identifier.
377 if (isIdentifier(getToken()))
378 return parseBareIdExpr();
379
380 if (lhs)
381 emitError("missing right operand of binary operator");
382 else
383 emitError("expected affine expression");
384 return nullptr;
385 }
386 }
387
388 /// Parse affine expressions that are bare-id's, integer constants,
389 /// parenthetical affine expressions, and affine op expressions that are a
390 /// composition of those.
391 ///
392 /// All binary op's associate from left to right.
393 ///
394 /// {add, sub} have lower precedence than {mul, div, and mod}.
395 ///
396 /// Add, sub'are themselves at the same precedence level. Mul, floordiv,
397 /// ceildiv, and mod are at the same higher precedence level. Negation has
398 /// higher precedence than any binary op.
399 ///
400 /// llhs: the affine expression appearing on the left of the one being parsed.
401 /// This function will return ((llhs llhsOp lhs) op rhs) if llhs is non null,
402 /// and lhs op rhs otherwise; if there is no rhs, llhs llhsOp lhs is returned
403 /// if llhs is non-null; otherwise lhs is returned. This is to deal with left
404 /// associativity.
405 ///
406 /// Eg: when the expression is e1 + e2*e3 + e4, with e1 as llhs, this function
407 /// will return the affine expr equivalent of (e1 + (e2*e3)) + e4, where
408 /// (e2*e3) will be parsed using parseAffineHighPrecOpExpr().
parseAffineLowPrecOpExpr(AffineExpr llhs,AffineLowPrecOp llhsOp)409 AffineExpr AffineParser::parseAffineLowPrecOpExpr(AffineExpr llhs,
410 AffineLowPrecOp llhsOp) {
411 AffineExpr lhs;
412 if (!(lhs = parseAffineOperandExpr(llhs)))
413 return nullptr;
414
415 // Found an LHS. Deal with the ops.
416 if (AffineLowPrecOp lOp = consumeIfLowPrecOp()) {
417 if (llhs) {
418 AffineExpr sum = getAffineBinaryOpExpr(llhsOp, llhs, lhs);
419 return parseAffineLowPrecOpExpr(sum, lOp);
420 }
421 // No LLHS, get RHS and form the expression.
422 return parseAffineLowPrecOpExpr(lhs, lOp);
423 }
424 auto opLoc = getToken().getLoc();
425 if (AffineHighPrecOp hOp = consumeIfHighPrecOp()) {
426 // We have a higher precedence op here. Get the rhs operand for the llhs
427 // through parseAffineHighPrecOpExpr.
428 AffineExpr highRes = parseAffineHighPrecOpExpr(lhs, hOp, opLoc);
429 if (!highRes)
430 return nullptr;
431
432 // If llhs is null, the product forms the first operand of the yet to be
433 // found expression. If non-null, the op to associate with llhs is llhsOp.
434 AffineExpr expr =
435 llhs ? getAffineBinaryOpExpr(llhsOp, llhs, highRes) : highRes;
436
437 // Recurse for subsequent low prec op's after the affine high prec op
438 // expression.
439 if (AffineLowPrecOp nextOp = consumeIfLowPrecOp())
440 return parseAffineLowPrecOpExpr(expr, nextOp);
441 return expr;
442 }
443 // Last operand in the expression list.
444 if (llhs)
445 return getAffineBinaryOpExpr(llhsOp, llhs, lhs);
446 // No llhs, 'lhs' itself is the expression.
447 return lhs;
448 }
449
450 /// Parse an affine expression.
451 /// affine-expr ::= `(` affine-expr `)`
452 /// | `-` affine-expr
453 /// | affine-expr `+` affine-expr
454 /// | affine-expr `-` affine-expr
455 /// | affine-expr `*` affine-expr
456 /// | affine-expr `floordiv` affine-expr
457 /// | affine-expr `ceildiv` affine-expr
458 /// | affine-expr `mod` affine-expr
459 /// | bare-id
460 /// | integer-literal
461 ///
462 /// Additional conditions are checked depending on the production. For eg.,
463 /// one of the operands for `*` has to be either constant/symbolic; the second
464 /// operand for floordiv, ceildiv, and mod has to be a positive integer.
parseAffineExpr()465 AffineExpr AffineParser::parseAffineExpr() {
466 return parseAffineLowPrecOpExpr(nullptr, AffineLowPrecOp::LNoOp);
467 }
468
469 /// Parse a dim or symbol from the lists appearing before the actual
470 /// expressions of the affine map. Update our state to store the
471 /// dimensional/symbolic identifier.
parseIdentifierDefinition(AffineExpr idExpr)472 ParseResult AffineParser::parseIdentifierDefinition(AffineExpr idExpr) {
473 if (!isIdentifier(getToken()))
474 return emitWrongTokenError("expected bare identifier");
475
476 auto name = getTokenSpelling();
477 for (auto entry : dimsAndSymbols) {
478 if (entry.first == name)
479 return emitError("redefinition of identifier '" + name + "'");
480 }
481 consumeToken();
482
483 dimsAndSymbols.push_back({name, idExpr});
484 return success();
485 }
486
487 /// Parse the list of dimensional identifiers to an affine map.
parseDimIdList(unsigned & numDims)488 ParseResult AffineParser::parseDimIdList(unsigned &numDims) {
489 auto parseElt = [&]() -> ParseResult {
490 auto dimension = getAffineDimExpr(numDims++, getContext());
491 return parseIdentifierDefinition(dimension);
492 };
493 return parseCommaSeparatedList(Delimiter::Paren, parseElt,
494 " in dimensional identifier list");
495 }
496
497 /// Parse the list of symbolic identifiers to an affine map.
parseSymbolIdList(unsigned & numSymbols)498 ParseResult AffineParser::parseSymbolIdList(unsigned &numSymbols) {
499 auto parseElt = [&]() -> ParseResult {
500 auto symbol = getAffineSymbolExpr(numSymbols++, getContext());
501 return parseIdentifierDefinition(symbol);
502 };
503 return parseCommaSeparatedList(Delimiter::Square, parseElt,
504 " in symbol list");
505 }
506
507 /// Parse the list of symbolic identifiers to an affine map.
508 ParseResult
parseDimAndOptionalSymbolIdList(unsigned & numDims,unsigned & numSymbols)509 AffineParser::parseDimAndOptionalSymbolIdList(unsigned &numDims,
510 unsigned &numSymbols) {
511 if (parseDimIdList(numDims)) {
512 return failure();
513 }
514 if (!getToken().is(Token::l_square)) {
515 numSymbols = 0;
516 return success();
517 }
518 return parseSymbolIdList(numSymbols);
519 }
520
521 /// Parses an ambiguous affine map or integer set definition inline.
parseAffineMapOrIntegerSetInline(AffineMap & map,IntegerSet & set)522 ParseResult AffineParser::parseAffineMapOrIntegerSetInline(AffineMap &map,
523 IntegerSet &set) {
524 unsigned numDims = 0, numSymbols = 0;
525
526 // List of dimensional and optional symbol identifiers.
527 if (parseDimAndOptionalSymbolIdList(numDims, numSymbols))
528 return failure();
529
530 if (consumeIf(Token::arrow))
531 return parseAffineMapRange(numDims, numSymbols, map);
532
533 if (parseToken(Token::colon, "expected '->' or ':'"))
534 return failure();
535 return parseIntegerSetConstraints(numDims, numSymbols, set);
536 }
537
538 /// Parse an AffineMap where the dim and symbol identifiers are SSA ids.
539 ParseResult
parseAffineMapOfSSAIds(AffineMap & map,OpAsmParser::Delimiter delimiter)540 AffineParser::parseAffineMapOfSSAIds(AffineMap &map,
541 OpAsmParser::Delimiter delimiter) {
542
543 SmallVector<AffineExpr, 4> exprs;
544 auto parseElt = [&]() -> ParseResult {
545 auto elt = parseAffineExpr();
546 exprs.push_back(elt);
547 return elt ? success() : failure();
548 };
549
550 // Parse a multi-dimensional affine expression (a comma-separated list of
551 // 1-d affine expressions); the list can be empty. Grammar:
552 // multi-dim-affine-expr ::= `(` `)`
553 // | `(` affine-expr (`,` affine-expr)* `)`
554 if (parseCommaSeparatedList(delimiter, parseElt, " in affine map"))
555 return failure();
556
557 // Parsed a valid affine map.
558 map = AffineMap::get(numDimOperands, dimsAndSymbols.size() - numDimOperands,
559 exprs, getContext());
560 return success();
561 }
562
563 /// Parse an AffineExpr where the dim and symbol identifiers are SSA ids.
parseAffineExprOfSSAIds(AffineExpr & expr)564 ParseResult AffineParser::parseAffineExprOfSSAIds(AffineExpr &expr) {
565 expr = parseAffineExpr();
566 return success(expr != nullptr);
567 }
568
569 /// Parse the range and sizes affine map definition inline.
570 ///
571 /// affine-map ::= dim-and-symbol-id-lists `->` multi-dim-affine-expr
572 ///
573 /// multi-dim-affine-expr ::= `(` `)`
574 /// multi-dim-affine-expr ::= `(` affine-expr (`,` affine-expr)* `)`
parseAffineMapRange(unsigned numDims,unsigned numSymbols,AffineMap & result)575 ParseResult AffineParser::parseAffineMapRange(unsigned numDims,
576 unsigned numSymbols,
577 AffineMap &result) {
578 SmallVector<AffineExpr, 4> exprs;
579 auto parseElt = [&]() -> ParseResult {
580 auto elt = parseAffineExpr();
581 ParseResult res = elt ? success() : failure();
582 exprs.push_back(elt);
583 return res;
584 };
585
586 // Parse a multi-dimensional affine expression (a comma-separated list of
587 // 1-d affine expressions). Grammar:
588 // multi-dim-affine-expr ::= `(` `)`
589 // | `(` affine-expr (`,` affine-expr)* `)`
590 if (parseCommaSeparatedList(Delimiter::Paren, parseElt,
591 " in affine map range"))
592 return failure();
593
594 // Parsed a valid affine map.
595 result = AffineMap::get(numDims, numSymbols, exprs, getContext());
596 return success();
597 }
598
599 /// Parse an affine constraint.
600 /// affine-constraint ::= affine-expr `>=` `affine-expr`
601 /// | affine-expr `<=` `affine-expr`
602 /// | affine-expr `==` `affine-expr`
603 ///
604 /// The constraint is normalized to
605 /// affine-constraint ::= affine-expr `>=` `0`
606 /// | affine-expr `==` `0`
607 /// before returning.
608 ///
609 /// isEq is set to true if the parsed constraint is an equality, false if it
610 /// is an inequality (greater than or equal).
611 ///
parseAffineConstraint(bool * isEq)612 AffineExpr AffineParser::parseAffineConstraint(bool *isEq) {
613 AffineExpr lhsExpr = parseAffineExpr();
614 if (!lhsExpr)
615 return nullptr;
616
617 // affine-constraint ::= `affine-expr` `>=` `affine-expr`
618 if (consumeIf(Token::greater) && consumeIf(Token::equal)) {
619 AffineExpr rhsExpr = parseAffineExpr();
620 if (!rhsExpr)
621 return nullptr;
622 *isEq = false;
623 return lhsExpr - rhsExpr;
624 }
625
626 // affine-constraint ::= `affine-expr` `<=` `affine-expr`
627 if (consumeIf(Token::less) && consumeIf(Token::equal)) {
628 AffineExpr rhsExpr = parseAffineExpr();
629 if (!rhsExpr)
630 return nullptr;
631 *isEq = false;
632 return rhsExpr - lhsExpr;
633 }
634
635 // affine-constraint ::= `affine-expr` `==` `affine-expr`
636 if (consumeIf(Token::equal) && consumeIf(Token::equal)) {
637 AffineExpr rhsExpr = parseAffineExpr();
638 if (!rhsExpr)
639 return nullptr;
640 *isEq = true;
641 return lhsExpr - rhsExpr;
642 }
643
644 return emitError("expected '== affine-expr' or '>= affine-expr' at end of "
645 "affine constraint"),
646 nullptr;
647 }
648
649 /// Parse the constraints that are part of an integer set definition.
650 /// integer-set-inline
651 /// ::= dim-and-symbol-id-lists `:`
652 /// '(' affine-constraint-conjunction? ')'
653 /// affine-constraint-conjunction ::= affine-constraint (`,`
654 /// affine-constraint)*
655 ///
parseIntegerSetConstraints(unsigned numDims,unsigned numSymbols,IntegerSet & result)656 ParseResult AffineParser::parseIntegerSetConstraints(unsigned numDims,
657 unsigned numSymbols,
658 IntegerSet &result) {
659 SmallVector<AffineExpr, 4> constraints;
660 SmallVector<bool, 4> isEqs;
661 auto parseElt = [&]() -> ParseResult {
662 bool isEq;
663 auto elt = parseAffineConstraint(&isEq);
664 ParseResult res = elt ? success() : failure();
665 if (elt) {
666 constraints.push_back(elt);
667 isEqs.push_back(isEq);
668 }
669 return res;
670 };
671
672 // Parse a list of affine constraints (comma-separated).
673 if (parseCommaSeparatedList(Delimiter::Paren, parseElt,
674 " in integer set constraint list"))
675 return failure();
676
677 // If no constraints were parsed, then treat this as a degenerate 'true' case.
678 if (constraints.empty()) {
679 /* 0 == 0 */
680 auto zero = getAffineConstantExpr(0, getContext());
681 result = IntegerSet::get(numDims, numSymbols, zero, true);
682 return success();
683 }
684
685 // Parsed a valid integer set.
686 result = IntegerSet::get(numDims, numSymbols, constraints, isEqs);
687 return success();
688 }
689
690 //===----------------------------------------------------------------------===//
691 // Parser
692 //===----------------------------------------------------------------------===//
693
694 /// Parse an ambiguous reference to either and affine map or an integer set.
parseAffineMapOrIntegerSetReference(AffineMap & map,IntegerSet & set)695 ParseResult Parser::parseAffineMapOrIntegerSetReference(AffineMap &map,
696 IntegerSet &set) {
697 return AffineParser(state).parseAffineMapOrIntegerSetInline(map, set);
698 }
parseAffineMapReference(AffineMap & map)699 ParseResult Parser::parseAffineMapReference(AffineMap &map) {
700 SMLoc curLoc = getToken().getLoc();
701 IntegerSet set;
702 if (parseAffineMapOrIntegerSetReference(map, set))
703 return failure();
704 if (set)
705 return emitError(curLoc, "expected AffineMap, but got IntegerSet");
706 return success();
707 }
parseIntegerSetReference(IntegerSet & set)708 ParseResult Parser::parseIntegerSetReference(IntegerSet &set) {
709 SMLoc curLoc = getToken().getLoc();
710 AffineMap map;
711 if (parseAffineMapOrIntegerSetReference(map, set))
712 return failure();
713 if (map)
714 return emitError(curLoc, "expected IntegerSet, but got AffineMap");
715 return success();
716 }
717
718 /// Parse an AffineMap of SSA ids. The callback 'parseElement' is used to
719 /// parse SSA value uses encountered while parsing affine expressions.
720 ParseResult
parseAffineMapOfSSAIds(AffineMap & map,function_ref<ParseResult (bool)> parseElement,OpAsmParser::Delimiter delimiter)721 Parser::parseAffineMapOfSSAIds(AffineMap &map,
722 function_ref<ParseResult(bool)> parseElement,
723 OpAsmParser::Delimiter delimiter) {
724 return AffineParser(state, /*allowParsingSSAIds=*/true, parseElement)
725 .parseAffineMapOfSSAIds(map, delimiter);
726 }
727
728 /// Parse an AffineExpr of SSA ids. The callback `parseElement` is used to parse
729 /// SSA value uses encountered while parsing.
730 ParseResult
parseAffineExprOfSSAIds(AffineExpr & expr,function_ref<ParseResult (bool)> parseElement)731 Parser::parseAffineExprOfSSAIds(AffineExpr &expr,
732 function_ref<ParseResult(bool)> parseElement) {
733 return AffineParser(state, /*allowParsingSSAIds=*/true, parseElement)
734 .parseAffineExprOfSSAIds(expr);
735 }
736
parseIntegerSet(StringRef inputStr,MLIRContext * context,bool printDiagnosticInfo)737 IntegerSet mlir::parseIntegerSet(StringRef inputStr, MLIRContext *context,
738 bool printDiagnosticInfo) {
739 llvm::SourceMgr sourceMgr;
740 auto memBuffer = llvm::MemoryBuffer::getMemBuffer(
741 inputStr, /*BufferName=*/"<mlir_parser_buffer>",
742 /*RequiresNullTerminator=*/false);
743 sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
744 SymbolState symbolState;
745 ParserConfig config(context);
746 ParserState state(sourceMgr, config, symbolState, /*asmState=*/nullptr,
747 /*codeCompleteContext=*/nullptr);
748 Parser parser(state);
749
750 raw_ostream &os = printDiagnosticInfo ? llvm::errs() : llvm::nulls();
751 SourceMgrDiagnosticHandler handler(sourceMgr, context, os);
752 IntegerSet set;
753 if (parser.parseIntegerSetReference(set))
754 return IntegerSet();
755
756 Token endTok = parser.getToken();
757 if (endTok.isNot(Token::eof)) {
758 parser.emitError(endTok.getLoc(), "encountered unexpected token");
759 return IntegerSet();
760 }
761
762 return set;
763 }
764