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