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