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