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