blob: 8c44bbdb82512a28d9d80501e40c66b862c161dd [file] [log] [blame]
Chris Lattnere79379a2018-06-22 10:39:19 -07001//===- Parser.cpp - MLIR Parser Implementation ----------------------------===//
2//
3// Copyright 2019 The MLIR Authors.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16// =============================================================================
17//
18// This file implements the parser for the MLIR textual form.
19//
20//===----------------------------------------------------------------------===//
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -070021#include <stack>
Chris Lattnere79379a2018-06-22 10:39:19 -070022
23#include "mlir/Parser.h"
24#include "Lexer.h"
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -070025#include "mlir/IR/AffineExpr.h"
MLIR Teamf85a6262018-06-27 11:03:08 -070026#include "mlir/IR/AffineMap.h"
Chris Lattner4c95a502018-06-23 16:03:42 -070027#include "mlir/IR/CFGFunction.h"
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -070028#include "mlir/IR/Module.h"
Tatiana Shpeismanc96b5872018-06-28 17:02:32 -070029#include "mlir/IR/MLFunction.h"
Chris Lattnerf7e22732018-06-22 22:03:48 -070030#include "mlir/IR/Types.h"
Chris Lattnere79379a2018-06-22 10:39:19 -070031#include "llvm/Support/SourceMgr.h"
32using namespace mlir;
33using llvm::SourceMgr;
Chris Lattner4c95a502018-06-23 16:03:42 -070034using llvm::SMLoc;
Chris Lattnere79379a2018-06-22 10:39:19 -070035
36namespace {
Chris Lattner4c95a502018-06-23 16:03:42 -070037class CFGFunctionParserState;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -070038class AffineMapParserState;
Chris Lattner4c95a502018-06-23 16:03:42 -070039
Chris Lattnerf7e22732018-06-22 22:03:48 -070040/// Simple enum to make code read better in cases that would otherwise return a
41/// bool value. Failure is "true" in a boolean context.
Chris Lattnere79379a2018-06-22 10:39:19 -070042enum ParseResult {
43 ParseSuccess,
44 ParseFailure
45};
46
Uday Bondhugula015cbb12018-07-03 20:16:08 -070047/// Lower precedence ops (all at the same precedence level). LNoOp is false in
48/// the boolean sense.
49enum AffineLowPrecOp {
50 /// Null value.
51 LNoOp,
52 Add,
53 Sub
54};
55
56/// Higher precedence ops - all at the same precedence level. HNoOp is false in
57/// the boolean sense.
58enum AffineHighPrecOp {
59 /// Null value.
60 HNoOp,
61 Mul,
62 FloorDiv,
63 CeilDiv,
64 Mod
65};
66
Chris Lattnere79379a2018-06-22 10:39:19 -070067/// Main parser implementation.
68class Parser {
Chris Lattnered65a732018-06-28 20:45:33 -070069public:
Jacques Pienaar9c411be2018-06-24 19:17:35 -070070 Parser(llvm::SourceMgr &sourceMgr, MLIRContext *context,
Jacques Pienaar7b829702018-07-03 13:24:09 -070071 SMDiagnosticHandlerTy errorReporter)
72 : context(context), lex(sourceMgr, errorReporter),
73 curToken(lex.lexToken()), errorReporter(std::move(errorReporter)) {
Chris Lattnere79379a2018-06-22 10:39:19 -070074 module.reset(new Module());
75 }
76
77 Module *parseModule();
78private:
79 // State.
Chris Lattnerf7e22732018-06-22 22:03:48 -070080 MLIRContext *const context;
81
82 // The lexer for the source file we're parsing.
Chris Lattnere79379a2018-06-22 10:39:19 -070083 Lexer lex;
84
85 // This is the next token that hasn't been consumed yet.
86 Token curToken;
87
Jacques Pienaar9c411be2018-06-24 19:17:35 -070088 // The diagnostic error reporter.
Jacques Pienaar7b829702018-07-03 13:24:09 -070089 SMDiagnosticHandlerTy errorReporter;
Jacques Pienaar9c411be2018-06-24 19:17:35 -070090
Chris Lattnere79379a2018-06-22 10:39:19 -070091 // This is the result module we are parsing into.
92 std::unique_ptr<Module> module;
93
MLIR Teamf85a6262018-06-27 11:03:08 -070094 // A map from affine map identifier to AffineMap.
95 // TODO(andydavis) Remove use of unique_ptr when AffineMaps are bump pointer
96 // allocated.
97 llvm::StringMap<std::unique_ptr<AffineMap>> affineMaps;
98
Chris Lattnere79379a2018-06-22 10:39:19 -070099private:
100 // Helper methods.
101
102 /// Emit an error and return failure.
Chris Lattner4c95a502018-06-23 16:03:42 -0700103 ParseResult emitError(const Twine &message) {
104 return emitError(curToken.getLoc(), message);
105 }
106 ParseResult emitError(SMLoc loc, const Twine &message);
Chris Lattnere79379a2018-06-22 10:39:19 -0700107
108 /// Advance the current lexer onto the next token.
109 void consumeToken() {
110 assert(curToken.isNot(Token::eof, Token::error) &&
111 "shouldn't advance past EOF or errors");
112 curToken = lex.lexToken();
113 }
114
115 /// Advance the current lexer onto the next token, asserting what the expected
116 /// current token is. This is preferred to the above method because it leads
117 /// to more self-documenting code with better checking.
Chris Lattner8da0c282018-06-29 11:15:56 -0700118 void consumeToken(Token::Kind kind) {
Chris Lattnere79379a2018-06-22 10:39:19 -0700119 assert(curToken.is(kind) && "consumed an unexpected token");
120 consumeToken();
121 }
122
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700123 /// If the current token has the specified kind, consume it and return true.
124 /// If not, return false.
Chris Lattner8da0c282018-06-29 11:15:56 -0700125 bool consumeIf(Token::Kind kind) {
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700126 if (curToken.isNot(kind))
127 return false;
128 consumeToken(kind);
129 return true;
130 }
131
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700132 // Binary affine op parsing
133 AffineLowPrecOp consumeIfLowPrecOp();
134 AffineHighPrecOp consumeIfHighPrecOp();
135
Chris Lattner8da0c282018-06-29 11:15:56 -0700136 ParseResult parseCommaSeparatedList(Token::Kind rightToken,
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700137 const std::function<ParseResult()> &parseElement,
138 bool allowEmptyList = true);
139
Chris Lattnerf7e22732018-06-22 22:03:48 -0700140 // We have two forms of parsing methods - those that return a non-null
141 // pointer on success, and those that return a ParseResult to indicate whether
142 // they returned a failure. The second class fills in by-reference arguments
143 // as the results of their action.
144
Chris Lattnere79379a2018-06-22 10:39:19 -0700145 // Type parsing.
Chris Lattnerf958bbe2018-06-29 22:08:05 -0700146 Type *parsePrimitiveType();
Chris Lattnerf7e22732018-06-22 22:03:48 -0700147 Type *parseElementType();
148 VectorType *parseVectorType();
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700149 ParseResult parseDimensionListRanked(SmallVectorImpl<int> &dimensions);
Chris Lattnerf7e22732018-06-22 22:03:48 -0700150 Type *parseTensorType();
151 Type *parseMemRefType();
152 Type *parseFunctionType();
153 Type *parseType();
154 ParseResult parseTypeList(SmallVectorImpl<Type*> &elements);
Chris Lattnere79379a2018-06-22 10:39:19 -0700155
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700156 // Parsing identifiers' lists for polyhedral structures.
157 ParseResult parseDimIdList(AffineMapParserState &state);
158 ParseResult parseSymbolIdList(AffineMapParserState &state);
159 ParseResult parseDimOrSymbolId(AffineMapParserState &state, bool dim);
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700160
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700161 // Polyhedral structures.
MLIR Teamf85a6262018-06-27 11:03:08 -0700162 ParseResult parseAffineMapDef();
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700163 ParseResult parseAffineMapInline(StringRef mapId, AffineMap *&affineMap);
164 AffineExpr *parseAffineExpr(const AffineMapParserState &state);
165
166 AffineExpr *parseParentheticalExpr(const AffineMapParserState &state);
167 AffineExpr *parseIntegerExpr(const AffineMapParserState &state);
168 AffineExpr *parseBareIdExpr(const AffineMapParserState &state);
169
170 static AffineBinaryOpExpr *getBinaryAffineOpExpr(AffineHighPrecOp op,
171 AffineExpr *lhs,
172 AffineExpr *rhs,
173 MLIRContext *context);
174 static AffineBinaryOpExpr *getBinaryAffineOpExpr(AffineLowPrecOp op,
175 AffineExpr *lhs,
176 AffineExpr *rhs,
177 MLIRContext *context);
178 ParseResult parseAffineOperandExpr(const AffineMapParserState &state,
179 AffineExpr *&result);
180 ParseResult parseAffineLowPrecOpExpr(AffineExpr *llhs, AffineLowPrecOp llhsOp,
181 const AffineMapParserState &state,
182 AffineExpr *&result);
183 ParseResult parseAffineHighPrecOpExpr(AffineExpr *llhs,
184 AffineHighPrecOp llhsOp,
185 const AffineMapParserState &state,
186 AffineExpr *&result);
MLIR Teamf85a6262018-06-27 11:03:08 -0700187
Chris Lattner4c95a502018-06-23 16:03:42 -0700188 // Functions.
Chris Lattnerf7e22732018-06-22 22:03:48 -0700189 ParseResult parseFunctionSignature(StringRef &name, FunctionType *&type);
Chris Lattnere79379a2018-06-22 10:39:19 -0700190 ParseResult parseExtFunc();
Chris Lattner4c95a502018-06-23 16:03:42 -0700191 ParseResult parseCFGFunc();
Tatiana Shpeismanc96b5872018-06-28 17:02:32 -0700192 ParseResult parseMLFunc();
Chris Lattner4c95a502018-06-23 16:03:42 -0700193 ParseResult parseBasicBlock(CFGFunctionParserState &functionState);
Tatiana Shpeismanbf079c92018-07-03 17:51:28 -0700194 Statement *parseStatement(ParentType parent);
Chris Lattnerf6d80a02018-06-24 11:18:29 -0700195
Chris Lattner3a467cc2018-07-01 20:28:00 -0700196 OperationInst *parseCFGOperation(CFGFunctionParserState &functionState);
197 TerminatorInst *parseTerminator(CFGFunctionParserState &functionState);
Chris Lattnered65a732018-06-28 20:45:33 -0700198
Tatiana Shpeismanbf079c92018-07-03 17:51:28 -0700199 ForStmt *parseForStmt(ParentType parent);
200 IfStmt *parseIfStmt(ParentType parent);
201 ParseResult parseNestedStatements(NodeStmt *parent);
Chris Lattnere79379a2018-06-22 10:39:19 -0700202};
203} // end anonymous namespace
204
205//===----------------------------------------------------------------------===//
206// Helper methods.
207//===----------------------------------------------------------------------===//
208
Chris Lattner4c95a502018-06-23 16:03:42 -0700209ParseResult Parser::emitError(SMLoc loc, const Twine &message) {
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700210 // If we hit a parse error in response to a lexer error, then the lexer
Jacques Pienaar9c411be2018-06-24 19:17:35 -0700211 // already reported the error.
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700212 if (curToken.is(Token::error))
213 return ParseFailure;
214
Jacques Pienaar9c411be2018-06-24 19:17:35 -0700215 errorReporter(
216 lex.getSourceMgr().GetMessage(loc, SourceMgr::DK_Error, message));
Chris Lattnere79379a2018-06-22 10:39:19 -0700217 return ParseFailure;
218}
219
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700220/// Parse a comma-separated list of elements, terminated with an arbitrary
221/// token. This allows empty lists if allowEmptyList is true.
222///
223/// abstract-list ::= rightToken // if allowEmptyList == true
224/// abstract-list ::= element (',' element)* rightToken
225///
226ParseResult Parser::
Chris Lattner8da0c282018-06-29 11:15:56 -0700227parseCommaSeparatedList(Token::Kind rightToken,
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700228 const std::function<ParseResult()> &parseElement,
229 bool allowEmptyList) {
230 // Handle the empty case.
231 if (curToken.is(rightToken)) {
232 if (!allowEmptyList)
233 return emitError("expected list element");
234 consumeToken(rightToken);
235 return ParseSuccess;
236 }
237
238 // Non-empty case starts with an element.
239 if (parseElement())
240 return ParseFailure;
241
242 // Otherwise we have a list of comma separated elements.
243 while (consumeIf(Token::comma)) {
244 if (parseElement())
245 return ParseFailure;
246 }
247
248 // Consume the end character.
249 if (!consumeIf(rightToken))
Chris Lattner8da0c282018-06-29 11:15:56 -0700250 return emitError("expected ',' or '" + Token::getTokenSpelling(rightToken) +
251 "'");
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700252
253 return ParseSuccess;
254}
Chris Lattnere79379a2018-06-22 10:39:19 -0700255
256//===----------------------------------------------------------------------===//
257// Type Parsing
258//===----------------------------------------------------------------------===//
259
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700260/// Parse the low-level fixed dtypes in the system.
261///
Chris Lattnerf958bbe2018-06-29 22:08:05 -0700262/// primitive-type ::= `f16` | `bf16` | `f32` | `f64`
263/// primitive-type ::= integer-type
264/// primitive-type ::= `affineint`
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700265///
Chris Lattnerf958bbe2018-06-29 22:08:05 -0700266Type *Parser::parsePrimitiveType() {
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700267 switch (curToken.getKind()) {
Chris Lattnerf7e22732018-06-22 22:03:48 -0700268 default:
269 return (emitError("expected type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700270 case Token::kw_bf16:
271 consumeToken(Token::kw_bf16);
Chris Lattnerf7e22732018-06-22 22:03:48 -0700272 return Type::getBF16(context);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700273 case Token::kw_f16:
274 consumeToken(Token::kw_f16);
Chris Lattnerf7e22732018-06-22 22:03:48 -0700275 return Type::getF16(context);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700276 case Token::kw_f32:
277 consumeToken(Token::kw_f32);
Chris Lattnerf7e22732018-06-22 22:03:48 -0700278 return Type::getF32(context);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700279 case Token::kw_f64:
280 consumeToken(Token::kw_f64);
Chris Lattnerf7e22732018-06-22 22:03:48 -0700281 return Type::getF64(context);
Chris Lattnerf958bbe2018-06-29 22:08:05 -0700282 case Token::kw_affineint:
283 consumeToken(Token::kw_affineint);
284 return Type::getAffineInt(context);
285 case Token::inttype: {
286 auto width = curToken.getIntTypeBitwidth();
287 if (!width.hasValue())
288 return (emitError("invalid integer width"), nullptr);
289 consumeToken(Token::inttype);
290 return Type::getInt(width.getValue(), context);
291 }
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700292 }
293}
294
295/// Parse the element type of a tensor or memref type.
296///
297/// element-type ::= primitive-type | vector-type
298///
Chris Lattnerf7e22732018-06-22 22:03:48 -0700299Type *Parser::parseElementType() {
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700300 if (curToken.is(Token::kw_vector))
301 return parseVectorType();
302
303 return parsePrimitiveType();
304}
305
306/// Parse a vector type.
307///
308/// vector-type ::= `vector` `<` const-dimension-list primitive-type `>`
309/// const-dimension-list ::= (integer-literal `x`)+
310///
Chris Lattnerf7e22732018-06-22 22:03:48 -0700311VectorType *Parser::parseVectorType() {
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700312 consumeToken(Token::kw_vector);
313
314 if (!consumeIf(Token::less))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700315 return (emitError("expected '<' in vector type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700316
317 if (curToken.isNot(Token::integer))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700318 return (emitError("expected dimension size in vector type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700319
320 SmallVector<unsigned, 4> dimensions;
321 while (curToken.is(Token::integer)) {
322 // Make sure this integer value is in bound and valid.
323 auto dimension = curToken.getUnsignedIntegerValue();
324 if (!dimension.hasValue())
Chris Lattnerf7e22732018-06-22 22:03:48 -0700325 return (emitError("invalid dimension in vector type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700326 dimensions.push_back(dimension.getValue());
327
328 consumeToken(Token::integer);
329
330 // Make sure we have an 'x' or something like 'xbf32'.
331 if (curToken.isNot(Token::bare_identifier) ||
332 curToken.getSpelling()[0] != 'x')
Chris Lattnerf7e22732018-06-22 22:03:48 -0700333 return (emitError("expected 'x' in vector dimension list"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700334
335 // If we had a prefix of 'x', lex the next token immediately after the 'x'.
336 if (curToken.getSpelling().size() != 1)
337 lex.resetPointer(curToken.getSpelling().data()+1);
338
339 // Consume the 'x'.
340 consumeToken(Token::bare_identifier);
341 }
342
343 // Parse the element type.
Chris Lattnerf7e22732018-06-22 22:03:48 -0700344 auto *elementType = parsePrimitiveType();
345 if (!elementType)
346 return nullptr;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700347
348 if (!consumeIf(Token::greater))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700349 return (emitError("expected '>' in vector type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700350
Chris Lattnerf7e22732018-06-22 22:03:48 -0700351 return VectorType::get(dimensions, elementType);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700352}
353
354/// Parse a dimension list of a tensor or memref type. This populates the
355/// dimension list, returning -1 for the '?' dimensions.
356///
357/// dimension-list-ranked ::= (dimension `x`)*
358/// dimension ::= `?` | integer-literal
359///
360ParseResult Parser::parseDimensionListRanked(SmallVectorImpl<int> &dimensions) {
361 while (curToken.isAny(Token::integer, Token::question)) {
362 if (consumeIf(Token::question)) {
363 dimensions.push_back(-1);
364 } else {
365 // Make sure this integer value is in bound and valid.
366 auto dimension = curToken.getUnsignedIntegerValue();
367 if (!dimension.hasValue() || (int)dimension.getValue() < 0)
368 return emitError("invalid dimension");
369 dimensions.push_back((int)dimension.getValue());
370 consumeToken(Token::integer);
371 }
372
373 // Make sure we have an 'x' or something like 'xbf32'.
374 if (curToken.isNot(Token::bare_identifier) ||
375 curToken.getSpelling()[0] != 'x')
376 return emitError("expected 'x' in dimension list");
377
378 // If we had a prefix of 'x', lex the next token immediately after the 'x'.
379 if (curToken.getSpelling().size() != 1)
380 lex.resetPointer(curToken.getSpelling().data()+1);
381
382 // Consume the 'x'.
383 consumeToken(Token::bare_identifier);
384 }
385
386 return ParseSuccess;
387}
388
389/// Parse a tensor type.
390///
391/// tensor-type ::= `tensor` `<` dimension-list element-type `>`
392/// dimension-list ::= dimension-list-ranked | `??`
393///
Chris Lattnerf7e22732018-06-22 22:03:48 -0700394Type *Parser::parseTensorType() {
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700395 consumeToken(Token::kw_tensor);
396
397 if (!consumeIf(Token::less))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700398 return (emitError("expected '<' in tensor type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700399
400 bool isUnranked;
401 SmallVector<int, 4> dimensions;
402
403 if (consumeIf(Token::questionquestion)) {
404 isUnranked = true;
405 } else {
406 isUnranked = false;
407 if (parseDimensionListRanked(dimensions))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700408 return nullptr;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700409 }
410
411 // Parse the element type.
Chris Lattnerf7e22732018-06-22 22:03:48 -0700412 auto elementType = parseElementType();
413 if (!elementType)
414 return nullptr;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700415
416 if (!consumeIf(Token::greater))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700417 return (emitError("expected '>' in tensor type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700418
MLIR Team355ec862018-06-23 18:09:09 -0700419 if (isUnranked)
420 return UnrankedTensorType::get(elementType);
421 return RankedTensorType::get(dimensions, elementType);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700422}
423
424/// Parse a memref type.
425///
426/// memref-type ::= `memref` `<` dimension-list-ranked element-type
427/// (`,` semi-affine-map-composition)? (`,` memory-space)? `>`
428///
429/// semi-affine-map-composition ::= (semi-affine-map `,` )* semi-affine-map
430/// memory-space ::= integer-literal /* | TODO: address-space-id */
431///
Chris Lattnerf7e22732018-06-22 22:03:48 -0700432Type *Parser::parseMemRefType() {
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700433 consumeToken(Token::kw_memref);
434
435 if (!consumeIf(Token::less))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700436 return (emitError("expected '<' in memref type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700437
438 SmallVector<int, 4> dimensions;
439 if (parseDimensionListRanked(dimensions))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700440 return nullptr;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700441
442 // Parse the element type.
Chris Lattnerf7e22732018-06-22 22:03:48 -0700443 auto elementType = parseElementType();
444 if (!elementType)
445 return nullptr;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700446
447 // TODO: Parse semi-affine-map-composition.
448 // TODO: Parse memory-space.
449
450 if (!consumeIf(Token::greater))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700451 return (emitError("expected '>' in memref type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700452
Chris Lattnerf7e22732018-06-22 22:03:48 -0700453 // FIXME: Add an IR representation for memref types.
Chris Lattnerf958bbe2018-06-29 22:08:05 -0700454 return Type::getInt(1, context);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700455}
456
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700457/// Parse a function type.
458///
459/// function-type ::= type-list-parens `->` type-list
460///
Chris Lattnerf7e22732018-06-22 22:03:48 -0700461Type *Parser::parseFunctionType() {
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700462 assert(curToken.is(Token::l_paren));
463
Chris Lattnerf7e22732018-06-22 22:03:48 -0700464 SmallVector<Type*, 4> arguments;
465 if (parseTypeList(arguments))
466 return nullptr;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700467
468 if (!consumeIf(Token::arrow))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700469 return (emitError("expected '->' in function type"), nullptr);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700470
Chris Lattnerf7e22732018-06-22 22:03:48 -0700471 SmallVector<Type*, 4> results;
472 if (parseTypeList(results))
473 return nullptr;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700474
Chris Lattnerf7e22732018-06-22 22:03:48 -0700475 return FunctionType::get(arguments, results, context);
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700476}
477
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700478/// Parse an arbitrary type.
479///
480/// type ::= primitive-type
481/// | vector-type
482/// | tensor-type
483/// | memref-type
484/// | function-type
485/// element-type ::= primitive-type | vector-type
486///
Chris Lattnerf7e22732018-06-22 22:03:48 -0700487Type *Parser::parseType() {
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700488 switch (curToken.getKind()) {
489 case Token::kw_memref: return parseMemRefType();
490 case Token::kw_tensor: return parseTensorType();
491 case Token::kw_vector: return parseVectorType();
492 case Token::l_paren: return parseFunctionType();
493 default:
494 return parsePrimitiveType();
495 }
496}
497
498/// Parse a "type list", which is a singular type, or a parenthesized list of
499/// types.
500///
501/// type-list ::= type-list-parens | type
502/// type-list-parens ::= `(` `)`
503/// | `(` type (`,` type)* `)`
504///
Chris Lattnerf7e22732018-06-22 22:03:48 -0700505ParseResult Parser::parseTypeList(SmallVectorImpl<Type*> &elements) {
506 auto parseElt = [&]() -> ParseResult {
507 auto elt = parseType();
508 elements.push_back(elt);
509 return elt ? ParseSuccess : ParseFailure;
510 };
511
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700512 // If there is no parens, then it must be a singular type.
513 if (!consumeIf(Token::l_paren))
Chris Lattnerf7e22732018-06-22 22:03:48 -0700514 return parseElt();
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700515
Chris Lattnerf7e22732018-06-22 22:03:48 -0700516 if (parseCommaSeparatedList(Token::r_paren, parseElt))
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700517 return ParseFailure;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700518
Chris Lattnerbb8fafc2018-06-22 15:52:02 -0700519 return ParseSuccess;
520}
521
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700522namespace {
523/// This class represents the transient parser state while parsing an affine
524/// expression.
525class AffineMapParserState {
526 public:
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700527 explicit AffineMapParserState() {}
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700528
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700529 void addDim(StringRef sRef) { dims.insert({sRef, dims.size()}); }
530 void addSymbol(StringRef sRef) { symbols.insert({sRef, symbols.size()}); }
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700531
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700532 unsigned getNumDims() const { return dims.size(); }
533 unsigned getNumSymbols() const { return symbols.size(); }
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700534
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700535 // TODO(bondhugula): could just use an vector/ArrayRef and scan the numbers.
536 const llvm::StringMap<unsigned> &getDims() const { return dims; }
537 const llvm::StringMap<unsigned> &getSymbols() const { return symbols; }
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700538
539 private:
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700540 llvm::StringMap<unsigned> dims;
541 llvm::StringMap<unsigned> symbols;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700542};
543} // end anonymous namespace
544
Chris Lattner4c95a502018-06-23 16:03:42 -0700545//===----------------------------------------------------------------------===//
MLIR Teamf85a6262018-06-27 11:03:08 -0700546// Polyhedral structures.
547//===----------------------------------------------------------------------===//
548
549/// Affine map declaration.
550///
551/// affine-map-def ::= affine-map-id `=` affine-map-inline
MLIR Teamf85a6262018-06-27 11:03:08 -0700552///
553ParseResult Parser::parseAffineMapDef() {
Chris Lattner8da0c282018-06-29 11:15:56 -0700554 assert(curToken.is(Token::affine_map_identifier));
MLIR Teamf85a6262018-06-27 11:03:08 -0700555
556 StringRef affineMapId = curToken.getSpelling().drop_front();
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700557 consumeToken(Token::affine_map_identifier);
558
MLIR Teamf85a6262018-06-27 11:03:08 -0700559 // Check that 'affineMapId' is unique.
560 // TODO(andydavis) Add a unit test for this case.
561 if (affineMaps.count(affineMapId) > 0)
Chris Lattnered65a732018-06-28 20:45:33 -0700562 return emitError("redefinition of affine map id '" + affineMapId + "'");
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700563 // Parse the '='
564 if (!consumeIf(Token::equal))
565 return emitError("expected '=' in affine map outlined definition");
MLIR Teamf85a6262018-06-27 11:03:08 -0700566
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700567 AffineMap *affineMap = nullptr;
568 if (parseAffineMapInline(affineMapId, affineMap))
569 return ParseFailure;
MLIR Teamf85a6262018-06-27 11:03:08 -0700570
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700571 // TODO(bondhugula): Disable adding affineMapId to Parser::affineMaps for now;
572 // instead add to module for easy printing.
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700573 module->affineMapList.push_back(affineMap);
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700574
575 return ParseSuccess;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700576}
577
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700578/// Create an affine op expression
579AffineBinaryOpExpr *Parser::getBinaryAffineOpExpr(AffineHighPrecOp op,
580 AffineExpr *lhs,
581 AffineExpr *rhs,
582 MLIRContext *context) {
583 switch (op) {
584 case Mul:
585 return AffineMulExpr::get(lhs, rhs, context);
586 case FloorDiv:
587 return AffineFloorDivExpr::get(lhs, rhs, context);
588 case CeilDiv:
589 return AffineCeilDivExpr::get(lhs, rhs, context);
590 case Mod:
591 return AffineModExpr::get(lhs, rhs, context);
592 case HNoOp:
593 llvm_unreachable("can't create affine expression for null high prec op");
594 return nullptr;
595 }
596}
597
598AffineBinaryOpExpr *Parser::getBinaryAffineOpExpr(AffineLowPrecOp op,
599 AffineExpr *lhs,
600 AffineExpr *rhs,
601 MLIRContext *context) {
602 switch (op) {
603 case AffineLowPrecOp::Add:
604 return AffineAddExpr::get(lhs, rhs, context);
605 case AffineLowPrecOp::Sub:
606 return AffineSubExpr::get(lhs, rhs, context);
607 case AffineLowPrecOp::LNoOp:
608 llvm_unreachable("can't create affine expression for null low prec op");
609 return nullptr;
610 }
611}
612
613/// Parses an expression that can be a valid operand of an affine expression
614/// (where associativity may not have been specified through parentheses).
615// Eg: for an expression without parentheses (like i + j + k + l), each
616// of the four identifiers is an operand. For: i + j*k + l, j*k is not an
617// operand expression, it's an op expression and will be parsed via
618// parseAffineLowPrecOpExpression().
619ParseResult Parser::parseAffineOperandExpr(const AffineMapParserState &state,
620 AffineExpr *&result) {
621 result = parseParentheticalExpr(state);
622 if (!result)
623 result = parseBareIdExpr(state);
624 if (!result)
625 result = parseIntegerExpr(state);
626 return result ? ParseSuccess : ParseFailure;
627}
628
629/// Parse a high precedence op expression list: mul, div, and mod are high
630/// precedence binary ops, i.e., parse a
631/// expr_1 op_1 expr_2 op_2 ... expr_n
632/// where op_1, op_2 are all a AffineHighPrecOp (mul, div, mod).
633/// All affine binary ops are left associative.
634/// Given llhs, returns (llhs * lhs) * rhs, or (lhs * rhs) if llhs is null. If
635/// no rhs can be found, returns (llhs * lhs) or lhs if llhs is null.
636// TODO(bondhugula): check whether mul is w.r.t. a constant - otherwise, the
637/// map is semi-affine.
638ParseResult Parser::parseAffineHighPrecOpExpr(AffineExpr *llhs,
639 AffineHighPrecOp llhsOp,
640 const AffineMapParserState &state,
641 AffineExpr *&result) {
642 // FIXME: Assume for now that llhsOp is mul.
643 AffineExpr *lhs = nullptr;
644 if (parseAffineOperandExpr(state, lhs)) {
645 return ParseFailure;
646 }
647 AffineHighPrecOp op = HNoOp;
648 // Found an LHS. Parse the remaining expression.
649 if ((op = consumeIfHighPrecOp())) {
650 if (llhs) {
651 // TODO(bondhugula): check whether 'lhs' here is a constant (for affine
652 // maps); semi-affine maps allow symbols.
653 AffineExpr *expr =
654 Parser::getBinaryAffineOpExpr(llhsOp, llhs, lhs, context);
655 AffineExpr *subRes = nullptr;
656 if (parseAffineHighPrecOpExpr(expr, op, state, subRes)) {
657 if (!subRes)
658 emitError("missing right operand of multiply op");
659 // In spite of the error, setting result to prevent duplicate errors
660 // messages as the call stack unwinds. All of this due to left
661 // associativity.
662 result = expr;
663 return ParseFailure;
664 }
665 result = subRes ? subRes : expr;
666 return ParseSuccess;
667 }
668 // No LLHS, get RHS
669 AffineExpr *subRes = nullptr;
670 if (parseAffineHighPrecOpExpr(lhs, op, state, subRes)) {
671 // 'product' needs to be checked to prevent duplicate errors messages as
672 // the call stack unwinds. All of this due to left associativity.
673 if (!subRes)
674 emitError("missing right operand of multiply op");
675 return ParseFailure;
676 }
677 result = subRes;
678 return ParseSuccess;
679 }
680
681 // This is the last operand in this expression.
682 if (llhs) {
683 // TODO(bondhugula): check whether lhs here is a constant (for affine
684 // maps); semi-affine maps allow symbols.
685 result = Parser::getBinaryAffineOpExpr(llhsOp, llhs, lhs, context);
686 return ParseSuccess;
687 }
688
689 // No llhs, 'lhs' itself is the expression.
690 result = lhs;
691 return ParseSuccess;
692}
693
694/// Consume this token if it is a lower precedence affine op (there are only two
695/// precedence levels)
696AffineLowPrecOp Parser::consumeIfLowPrecOp() {
697 switch (curToken.getKind()) {
698 case Token::plus:
699 consumeToken(Token::plus);
700 return AffineLowPrecOp::Add;
701 case Token::minus:
702 consumeToken(Token::minus);
703 return AffineLowPrecOp::Sub;
704 default:
705 return AffineLowPrecOp::LNoOp;
706 }
707}
708
709/// Consume this token if it is a higher precedence affine op (there are only
710/// two precedence levels)
711AffineHighPrecOp Parser::consumeIfHighPrecOp() {
712 switch (curToken.getKind()) {
713 case Token::star:
714 consumeToken(Token::star);
715 return Mul;
716 case Token::kw_floordiv:
717 consumeToken(Token::kw_floordiv);
718 return FloorDiv;
719 case Token::kw_ceildiv:
720 consumeToken(Token::kw_ceildiv);
721 return CeilDiv;
722 case Token::kw_mod:
723 consumeToken(Token::kw_mod);
724 return Mod;
725 default:
726 return HNoOp;
727 }
728}
729
730/// Parse affine expressions that are bare-id's, integer constants,
731/// parenthetical affine expressions, and affine op expressions that are a
732/// composition of those.
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700733///
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700734/// All binary op's associate from left to right.
735///
736/// {add, sub} have lower precedence than {mul, div, and mod}.
737///
738/// Add, sub'are themselves at the same precedence level. mul, div, and mod are
739/// at the same higher precedence level.
740///
741/// llhs: the affine expression appearing on the left of the one being parsed.
742/// This function will return ((llhs + lhs) + rhs) if llhs is non null, and
743/// lhs + rhs otherwise; if there is no rhs, llhs + lhs is returned if llhs is
744/// non-null; otherwise lhs is returned. This is to deal with left
745/// associativity.
746///
747/// Eg: when the expression is e1 + e2*e3 + e4, with e1 as llhs, this function
748/// will return the affine expr equivalent of (e1 + (e2*e3)) + e4.
749///
750// TODO(bondhugula): add support for unary op negation. Assuming for now that
751// the op to associate with llhs is add.
752ParseResult Parser::parseAffineLowPrecOpExpr(AffineExpr *llhs,
753 AffineLowPrecOp llhsOp,
754 const AffineMapParserState &state,
755 AffineExpr *&result) {
756 AffineExpr *lhs = nullptr;
757 if (parseAffineOperandExpr(state, lhs))
758 return ParseFailure;
759
760 // Found an LHS. Deal with the ops.
761 AffineLowPrecOp lOp;
762 AffineHighPrecOp rOp;
763 if ((lOp = consumeIfLowPrecOp())) {
764 if (llhs) {
765 AffineExpr *sum =
766 Parser::getBinaryAffineOpExpr(llhsOp, llhs, lhs, context);
767 AffineExpr *recSum = nullptr;
768 parseAffineLowPrecOpExpr(sum, lOp, state, recSum);
769 result = recSum ? recSum : sum;
770 return ParseSuccess;
771 }
772 // No LLHS, get RHS and form the expression.
773 if (parseAffineLowPrecOpExpr(lhs, lOp, state, result)) {
774 if (!result)
775 emitError("missing right operand of add op");
776 return ParseFailure;
777 }
778 return ParseSuccess;
779 } else if ((rOp = consumeIfHighPrecOp())) {
780 // We have a higher precedence op here. Get the rhs operand for the llhs
781 // through parseAffineHighPrecOpExpr.
782 AffineExpr *highRes = nullptr;
783 if (parseAffineHighPrecOpExpr(lhs, rOp, state, highRes)) {
784 // 'product' needs to be checked to prevent duplicate errors messages as
785 // the call stack unwinds. All of this due to left associativity.
786 if (!highRes)
787 emitError("missing right operand of binary op");
788 return ParseFailure;
789 }
790 // If llhs is null, the product forms the first operand of the yet to be
791 // found expression. If non-null, assume for now that the op to associate
792 // with llhs is add.
793 AffineExpr *expr =
794 llhs ? getBinaryAffineOpExpr(llhsOp, llhs, highRes, context) : highRes;
795 // Recurse for subsequent add's after the affine mul expression
796 AffineLowPrecOp nextOp = consumeIfLowPrecOp();
797 if (nextOp) {
798 AffineExpr *sumProd = nullptr;
799 parseAffineLowPrecOpExpr(expr, nextOp, state, sumProd);
800 result = sumProd ? sumProd : expr;
801 } else {
802 result = expr;
803 }
804 return ParseSuccess;
805 } else {
806 // Last operand in the expression list.
807 if (llhs) {
808 result = Parser::getBinaryAffineOpExpr(llhsOp, llhs, lhs, context);
809 return ParseSuccess;
810 }
811 // No llhs, 'lhs' itself is the expression.
812 result = lhs;
813 return ParseSuccess;
814 }
815}
816
817/// Parse an affine expression inside parentheses.
818/// affine-expr ::= `(` affine-expr `)`
819AffineExpr *Parser::parseParentheticalExpr(const AffineMapParserState &state) {
820 if (!consumeIf(Token::l_paren)) {
821 return nullptr;
822 }
823 auto *expr = parseAffineExpr(state);
824 if (!consumeIf(Token::r_paren)) {
825 emitError("expected ')'");
826 return nullptr;
827 }
828 if (!expr)
829 emitError("no expression inside parentheses");
830 return expr;
831}
832
833/// Parse a bare id that may appear in an affine expression.
834/// affine-expr ::= bare-id
835AffineExpr *Parser::parseBareIdExpr(const AffineMapParserState &state) {
836 if (curToken.is(Token::bare_identifier)) {
837 StringRef sRef = curToken.getSpelling();
838 const auto &dims = state.getDims();
839 const auto &symbols = state.getSymbols();
840 if (dims.count(sRef)) {
841 consumeToken(Token::bare_identifier);
842 return AffineDimExpr::get(dims.lookup(sRef), context);
843 }
844 if (symbols.count(sRef)) {
845 consumeToken(Token::bare_identifier);
846 return AffineSymbolExpr::get(symbols.lookup(sRef), context);
847 }
848 return emitError("identifier is neither dimensional nor symbolic"), nullptr;
849 }
850 return nullptr;
851}
852
853/// Parse an integral constant appearing in an affine expression.
854/// affine-expr ::= `-`? integer-literal
855/// TODO(bondhugula): handle negative numbers.
856AffineExpr *Parser::parseIntegerExpr(const AffineMapParserState &state) {
857 if (curToken.is(Token::integer)) {
858 auto *expr = AffineConstantExpr::get(
859 curToken.getUnsignedIntegerValue().getValue(), context);
860 consumeToken(Token::integer);
861 return expr;
862 }
863 return nullptr;
864}
865
866/// Parse an affine expression.
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700867/// affine-expr ::= `(` affine-expr `)`
868/// | affine-expr `+` affine-expr
869/// | affine-expr `-` affine-expr
870/// | `-`? integer-literal `*` affine-expr
871/// | `ceildiv` `(` affine-expr `,` integer-literal `)`
872/// | `floordiv` `(` affine-expr `,` integer-literal `)`
873/// | affine-expr `mod` integer-literal
874/// | bare-id
875/// | `-`? integer-literal
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700876/// Use 'state' to check if valid identifiers appear.
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700877// TODO(bondhugula): check if mul, mod, div take integral constants
878AffineExpr *Parser::parseAffineExpr(const AffineMapParserState &state) {
879 switch (curToken.getKind()) {
880 case Token::l_paren:
881 case Token::kw_ceildiv:
882 case Token::kw_floordiv:
883 case Token::bare_identifier:
884 case Token::integer: {
885 AffineExpr *result = nullptr;
886 parseAffineLowPrecOpExpr(nullptr, AffineLowPrecOp::LNoOp, state, result);
887 return result;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700888 }
889
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700890 case Token::plus:
891 case Token::minus:
892 case Token::star:
893 emitError("left operand of binary op missing");
894 return nullptr;
895
896 default:
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700897 return nullptr;
898 }
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700899}
900
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700901/// Parse a dim or symbol from the lists appearing before the actual expressions
902/// of the affine map. Update state to store the dimensional/symbolic
903/// identifier. 'dim': whether it's the dim list or symbol list that is being
904/// parsed.
905ParseResult Parser::parseDimOrSymbolId(AffineMapParserState &state, bool dim) {
906 if (curToken.isNot(Token::bare_identifier))
907 return emitError("expected bare identifier");
908 auto sRef = curToken.getSpelling();
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700909 consumeToken(Token::bare_identifier);
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700910 if (state.getDims().count(sRef) == 1)
911 return emitError("dimensional identifier name reused");
912 if (state.getSymbols().count(sRef) == 1)
913 return emitError("symbolic identifier name reused");
914 if (dim)
915 state.addDim(sRef);
916 else
917 state.addSymbol(sRef);
918 return ParseSuccess;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700919}
920
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700921/// Parse the list of symbolic identifiers to an affine map.
922ParseResult Parser::parseSymbolIdList(AffineMapParserState &state) {
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700923 if (!consumeIf(Token::l_bracket)) return emitError("expected '['");
924
925 auto parseElt = [&]() -> ParseResult {
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700926 return parseDimOrSymbolId(state, false);
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700927 };
928 return parseCommaSeparatedList(Token::r_bracket, parseElt);
929}
930
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700931/// Parse the list of dimensional identifiers to an affine map.
932ParseResult Parser::parseDimIdList(AffineMapParserState &state) {
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700933 if (!consumeIf(Token::l_paren))
934 return emitError("expected '(' at start of dimensional identifiers list");
935
936 auto parseElt = [&]() -> ParseResult {
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700937 return parseDimOrSymbolId(state, true);
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700938 };
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700939 return parseCommaSeparatedList(Token::r_paren, parseElt);
940}
941
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700942/// Parse an affine map definition.
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700943///
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700944/// affine-map-inline ::= dim-and-symbol-id-lists `->` multi-dim-affine-expr
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700945/// ( `size` `(` dim-size (`,` dim-size)* `)` )?
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700946/// dim-size ::= affine-expr | `min` `(` affine-expr ( `,` affine-expr)+ `)`
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700947///
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700948/// multi-dim-affine-expr ::= `(` affine-expr (`,` affine-expr)* `)
949ParseResult Parser::parseAffineMapInline(StringRef mapId,
950 AffineMap *&affineMap) {
951 AffineMapParserState state;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700952
953 // List of dimensional identifiers.
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700954 if (parseDimIdList(state))
955 return ParseFailure;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700956
957 // Symbols are optional.
958 if (curToken.is(Token::l_bracket)) {
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700959 if (parseSymbolIdList(state))
960 return ParseFailure;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700961 }
962 if (!consumeIf(Token::arrow)) {
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700963 return (emitError("expected '->' or '['"), ParseFailure);
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700964 }
965 if (!consumeIf(Token::l_paren)) {
966 emitError("expected '(' at start of affine map range");
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700967 return ParseFailure;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700968 }
969
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700970 SmallVector<AffineExpr *, 4> exprs;
971 auto parseElt = [&]() -> ParseResult {
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700972 auto *elt = parseAffineExpr(state);
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700973 ParseResult res = elt ? ParseSuccess : ParseFailure;
974 exprs.push_back(elt);
975 return res;
976 };
977
978 // Parse a multi-dimensional affine expression (a comma-separated list of 1-d
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700979 // affine expressions); the list cannot be empty.
980 // Grammar: multi-dim-affine-expr ::= `(` affine-expr (`,` affine-expr)* `)
981 if (parseCommaSeparatedList(Token::r_paren, parseElt, false))
982 return ParseFailure;
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -0700983
Uday Bondhugula015cbb12018-07-03 20:16:08 -0700984 // Parsed a valid affine map.
985 affineMap =
986 AffineMap::get(state.getNumDims(), state.getNumSymbols(), exprs, context);
987 return ParseSuccess;
MLIR Teamf85a6262018-06-27 11:03:08 -0700988}
989
990//===----------------------------------------------------------------------===//
Chris Lattner4c95a502018-06-23 16:03:42 -0700991// Functions
992//===----------------------------------------------------------------------===//
Chris Lattnere79379a2018-06-22 10:39:19 -0700993
Chris Lattnere79379a2018-06-22 10:39:19 -0700994
995/// Parse a function signature, starting with a name and including the parameter
996/// list.
997///
998/// argument-list ::= type (`,` type)* | /*empty*/
999/// function-signature ::= function-id `(` argument-list `)` (`->` type-list)?
1000///
Chris Lattnerf7e22732018-06-22 22:03:48 -07001001ParseResult Parser::parseFunctionSignature(StringRef &name,
1002 FunctionType *&type) {
Chris Lattnere79379a2018-06-22 10:39:19 -07001003 if (curToken.isNot(Token::at_identifier))
1004 return emitError("expected a function identifier like '@foo'");
1005
1006 name = curToken.getSpelling().drop_front();
1007 consumeToken(Token::at_identifier);
1008
1009 if (curToken.isNot(Token::l_paren))
1010 return emitError("expected '(' in function signature");
Chris Lattnere79379a2018-06-22 10:39:19 -07001011
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -07001012 SmallVector<Type*, 4> arguments;
1013 if (parseTypeList(arguments))
Chris Lattnerbb8fafc2018-06-22 15:52:02 -07001014 return ParseFailure;
Chris Lattnere79379a2018-06-22 10:39:19 -07001015
Chris Lattnerbb8fafc2018-06-22 15:52:02 -07001016 // Parse the return type if present.
Chris Lattnerf7e22732018-06-22 22:03:48 -07001017 SmallVector<Type*, 4> results;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -07001018 if (consumeIf(Token::arrow)) {
Chris Lattnerf7e22732018-06-22 22:03:48 -07001019 if (parseTypeList(results))
Chris Lattnerbb8fafc2018-06-22 15:52:02 -07001020 return ParseFailure;
Chris Lattnerbb8fafc2018-06-22 15:52:02 -07001021 }
Chris Lattnerf7e22732018-06-22 22:03:48 -07001022 type = FunctionType::get(arguments, results, context);
Chris Lattnere79379a2018-06-22 10:39:19 -07001023 return ParseSuccess;
1024}
1025
Chris Lattnere79379a2018-06-22 10:39:19 -07001026/// External function declarations.
1027///
1028/// ext-func ::= `extfunc` function-signature
1029///
1030ParseResult Parser::parseExtFunc() {
1031 consumeToken(Token::kw_extfunc);
1032
1033 StringRef name;
Chris Lattnerf7e22732018-06-22 22:03:48 -07001034 FunctionType *type = nullptr;
1035 if (parseFunctionSignature(name, type))
Chris Lattnere79379a2018-06-22 10:39:19 -07001036 return ParseFailure;
1037
Chris Lattnere79379a2018-06-22 10:39:19 -07001038 // Okay, the external function definition was parsed correctly.
Chris Lattner4c95a502018-06-23 16:03:42 -07001039 module->functionList.push_back(new ExtFunction(name, type));
Chris Lattnere79379a2018-06-22 10:39:19 -07001040 return ParseSuccess;
1041}
1042
1043
Chris Lattner4c95a502018-06-23 16:03:42 -07001044namespace {
1045/// This class represents the transient parser state for the internals of a
1046/// function as we are parsing it, e.g. the names for basic blocks. It handles
1047/// forward references.
1048class CFGFunctionParserState {
Uday Bondhugulafaf37dd2018-06-29 18:09:29 -07001049 public:
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001050 CFGFunction *function;
1051 llvm::StringMap<std::pair<BasicBlock*, SMLoc>> blocksByName;
1052
Chris Lattner4c95a502018-06-23 16:03:42 -07001053 CFGFunctionParserState(CFGFunction *function) : function(function) {}
1054
1055 /// Get the basic block with the specified name, creating it if it doesn't
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001056 /// already exist. The location specified is the point of use, which allows
1057 /// us to diagnose references to blocks that are not defined precisely.
1058 BasicBlock *getBlockNamed(StringRef name, SMLoc loc) {
1059 auto &blockAndLoc = blocksByName[name];
1060 if (!blockAndLoc.first) {
Chris Lattner3a467cc2018-07-01 20:28:00 -07001061 blockAndLoc.first = new BasicBlock();
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001062 blockAndLoc.second = loc;
Chris Lattner4c95a502018-06-23 16:03:42 -07001063 }
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001064 return blockAndLoc.first;
Chris Lattner4c95a502018-06-23 16:03:42 -07001065 }
Chris Lattner4c95a502018-06-23 16:03:42 -07001066};
1067} // end anonymous namespace
1068
1069
1070/// CFG function declarations.
1071///
1072/// cfg-func ::= `cfgfunc` function-signature `{` basic-block+ `}`
1073///
1074ParseResult Parser::parseCFGFunc() {
1075 consumeToken(Token::kw_cfgfunc);
1076
1077 StringRef name;
1078 FunctionType *type = nullptr;
1079 if (parseFunctionSignature(name, type))
1080 return ParseFailure;
1081
1082 if (!consumeIf(Token::l_brace))
1083 return emitError("expected '{' in CFG function");
1084
1085 // Okay, the CFG function signature was parsed correctly, create the function.
1086 auto function = new CFGFunction(name, type);
1087
1088 // Make sure we have at least one block.
1089 if (curToken.is(Token::r_brace))
1090 return emitError("CFG functions must have at least one basic block");
1091
1092 CFGFunctionParserState functionState(function);
1093
1094 // Parse the list of blocks.
1095 while (!consumeIf(Token::r_brace))
1096 if (parseBasicBlock(functionState))
1097 return ParseFailure;
1098
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001099 // Verify that all referenced blocks were defined. Iteration over a
1100 // StringMap isn't determinstic, but this is good enough for our purposes.
1101 for (auto &elt : functionState.blocksByName) {
1102 auto *bb = elt.second.first;
Chris Lattner3a467cc2018-07-01 20:28:00 -07001103 if (!bb->getFunction())
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001104 return emitError(elt.second.second,
1105 "reference to an undefined basic block '" +
1106 elt.first() + "'");
1107 }
1108
Chris Lattner4c95a502018-06-23 16:03:42 -07001109 module->functionList.push_back(function);
1110 return ParseSuccess;
1111}
1112
1113/// Basic block declaration.
1114///
1115/// basic-block ::= bb-label instruction* terminator-stmt
1116/// bb-label ::= bb-id bb-arg-list? `:`
1117/// bb-id ::= bare-id
1118/// bb-arg-list ::= `(` ssa-id-and-type-list? `)`
1119///
1120ParseResult Parser::parseBasicBlock(CFGFunctionParserState &functionState) {
1121 SMLoc nameLoc = curToken.getLoc();
1122 auto name = curToken.getSpelling();
1123 if (!consumeIf(Token::bare_identifier))
1124 return emitError("expected basic block name");
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001125
1126 auto block = functionState.getBlockNamed(name, nameLoc);
Chris Lattner4c95a502018-06-23 16:03:42 -07001127
1128 // If this block has already been parsed, then this is a redefinition with the
1129 // same block name.
Chris Lattner3a467cc2018-07-01 20:28:00 -07001130 if (block->getFunction())
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001131 return emitError(nameLoc, "redefinition of block '" + name.str() + "'");
1132
Chris Lattner3a467cc2018-07-01 20:28:00 -07001133 // Add the block to the function.
1134 functionState.function->push_back(block);
Chris Lattner4c95a502018-06-23 16:03:42 -07001135
1136 // TODO: parse bb argument list.
1137
1138 if (!consumeIf(Token::colon))
1139 return emitError("expected ':' after basic block name");
1140
Chris Lattnered65a732018-06-28 20:45:33 -07001141 // Parse the list of operations that make up the body of the block.
1142 while (curToken.isNot(Token::kw_return, Token::kw_br)) {
Chris Lattner3a467cc2018-07-01 20:28:00 -07001143 auto *inst = parseCFGOperation(functionState);
1144 if (!inst)
Chris Lattnered65a732018-06-28 20:45:33 -07001145 return ParseFailure;
Chris Lattner3a467cc2018-07-01 20:28:00 -07001146
1147 block->getOperations().push_back(inst);
Chris Lattnered65a732018-06-28 20:45:33 -07001148 }
Chris Lattner4c95a502018-06-23 16:03:42 -07001149
Chris Lattner3a467cc2018-07-01 20:28:00 -07001150 auto *term = parseTerminator(functionState);
1151 if (!term)
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001152 return ParseFailure;
Chris Lattner3a467cc2018-07-01 20:28:00 -07001153 block->setTerminator(term);
Chris Lattner4c95a502018-06-23 16:03:42 -07001154
1155 return ParseSuccess;
1156}
1157
1158
Chris Lattnered65a732018-06-28 20:45:33 -07001159/// Parse the CFG operation.
1160///
1161/// TODO(clattner): This is a change from the MLIR spec as written, it is an
1162/// experiment that will eliminate "builtin" instructions as a thing.
1163///
1164/// cfg-operation ::=
1165/// (ssa-id `=`)? string '(' ssa-use-list? ')' attribute-dict?
1166/// `:` function-type
1167///
Chris Lattner3a467cc2018-07-01 20:28:00 -07001168OperationInst *Parser::
1169parseCFGOperation(CFGFunctionParserState &functionState) {
Chris Lattnered65a732018-06-28 20:45:33 -07001170
1171 // TODO: parse ssa-id.
1172
1173 if (curToken.isNot(Token::string))
Chris Lattner3a467cc2018-07-01 20:28:00 -07001174 return (emitError("expected operation name in quotes"), nullptr);
Chris Lattnered65a732018-06-28 20:45:33 -07001175
1176 auto name = curToken.getStringValue();
1177 if (name.empty())
Chris Lattner3a467cc2018-07-01 20:28:00 -07001178 return (emitError("empty operation name is invalid"), nullptr);
Chris Lattnered65a732018-06-28 20:45:33 -07001179
1180 consumeToken(Token::string);
1181
1182 if (!consumeIf(Token::l_paren))
Chris Lattner3a467cc2018-07-01 20:28:00 -07001183 return (emitError("expected '(' in operation"), nullptr);
Chris Lattnered65a732018-06-28 20:45:33 -07001184
1185 // TODO: Parse operands.
1186 if (!consumeIf(Token::r_paren))
Chris Lattner3a467cc2018-07-01 20:28:00 -07001187 return (emitError("expected '(' in operation"), nullptr);
Chris Lattnered65a732018-06-28 20:45:33 -07001188
1189 auto nameId = Identifier::get(name, context);
Chris Lattner3a467cc2018-07-01 20:28:00 -07001190 return new OperationInst(nameId);
Chris Lattnered65a732018-06-28 20:45:33 -07001191}
1192
1193
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001194/// Parse the terminator instruction for a basic block.
1195///
1196/// terminator-stmt ::= `br` bb-id branch-use-list?
1197/// branch-use-list ::= `(` ssa-use-and-type-list? `)`
1198/// terminator-stmt ::=
1199/// `cond_br` ssa-use `,` bb-id branch-use-list? `,` bb-id branch-use-list?
1200/// terminator-stmt ::= `return` ssa-use-and-type-list?
1201///
Chris Lattner3a467cc2018-07-01 20:28:00 -07001202TerminatorInst *Parser::parseTerminator(CFGFunctionParserState &functionState) {
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001203 switch (curToken.getKind()) {
1204 default:
Chris Lattner3a467cc2018-07-01 20:28:00 -07001205 return (emitError("expected terminator at end of basic block"), nullptr);
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001206
1207 case Token::kw_return:
1208 consumeToken(Token::kw_return);
Chris Lattner3a467cc2018-07-01 20:28:00 -07001209 return new ReturnInst();
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001210
1211 case Token::kw_br: {
1212 consumeToken(Token::kw_br);
1213 auto destBB = functionState.getBlockNamed(curToken.getSpelling(),
1214 curToken.getLoc());
1215 if (!consumeIf(Token::bare_identifier))
Chris Lattner3a467cc2018-07-01 20:28:00 -07001216 return (emitError("expected basic block name"), nullptr);
1217 return new BranchInst(destBB);
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001218 }
1219 }
1220}
1221
Tatiana Shpeismanc96b5872018-06-28 17:02:32 -07001222/// ML function declarations.
1223///
1224/// ml-func ::= `mlfunc` ml-func-signature `{` ml-stmt* ml-return-stmt `}`
1225///
1226ParseResult Parser::parseMLFunc() {
1227 consumeToken(Token::kw_mlfunc);
1228
1229 StringRef name;
1230 FunctionType *type = nullptr;
1231
1232 // FIXME: Parse ML function signature (args + types)
1233 // by passing pointer to SmallVector<identifier> into parseFunctionSignature
1234 if (parseFunctionSignature(name, type))
1235 return ParseFailure;
1236
1237 if (!consumeIf(Token::l_brace))
1238 return emitError("expected '{' in ML function");
1239
1240 // Okay, the ML function signature was parsed correctly, create the function.
1241 auto function = new MLFunction(name, type);
1242
1243 // Make sure we have at least one statement.
1244 if (curToken.is(Token::r_brace))
1245 return emitError("ML function must end with return statement");
1246
1247 // Parse the list of instructions.
1248 while (!consumeIf(Token::kw_return)) {
Tatiana Shpeismanbf079c92018-07-03 17:51:28 -07001249 auto *stmt = parseStatement(function);
Tatiana Shpeismanc96b5872018-06-28 17:02:32 -07001250 if (!stmt)
1251 return ParseFailure;
1252 function->stmtList.push_back(stmt);
1253 }
1254
1255 // TODO: parse return statement operands
1256 if (!consumeIf(Token::r_brace))
1257 emitError("expected '}' in ML function");
1258
1259 module->functionList.push_back(function);
1260
1261 return ParseSuccess;
1262}
1263
Tatiana Shpeismanbf079c92018-07-03 17:51:28 -07001264/// Statement.
Tatiana Shpeismanc96b5872018-06-28 17:02:32 -07001265///
Tatiana Shpeismanbf079c92018-07-03 17:51:28 -07001266/// ml-stmt ::= instruction | ml-for-stmt | ml-if-stmt
1267/// TODO: fix terminology in MLSpec document. ML functions
1268/// contain operation statements, not instructions.
1269///
1270Statement * Parser::parseStatement(ParentType parent) {
Tatiana Shpeismanc96b5872018-06-28 17:02:32 -07001271 switch (curToken.getKind()) {
1272 default:
Tatiana Shpeismanbf079c92018-07-03 17:51:28 -07001273 //TODO: parse OperationStmt
1274 return (emitError("expected statement"), nullptr);
Tatiana Shpeismanc96b5872018-06-28 17:02:32 -07001275
Tatiana Shpeismanbf079c92018-07-03 17:51:28 -07001276 case Token::kw_for:
1277 return parseForStmt(parent);
1278
1279 case Token::kw_if:
1280 return parseIfStmt(parent);
Tatiana Shpeismanc96b5872018-06-28 17:02:32 -07001281 }
1282}
Chris Lattnerf6d80a02018-06-24 11:18:29 -07001283
Tatiana Shpeismanbf079c92018-07-03 17:51:28 -07001284/// For statement.
1285///
1286/// ml-for-stmt ::= `for` ssa-id `=` lower-bound `to` upper-bound
1287/// (`step` integer-literal)? `{` ml-stmt* `}`
1288///
1289ForStmt * Parser::parseForStmt(ParentType parent) {
1290 consumeToken(Token::kw_for);
1291
1292 //TODO: parse loop header
1293 ForStmt *stmt = new ForStmt(parent);
1294 if (parseNestedStatements(stmt)) {
1295 delete stmt;
1296 return nullptr;
1297 }
1298 return stmt;
1299}
1300
1301/// If statement.
1302///
1303/// ml-if-head ::= `if` ml-if-cond `{` ml-stmt* `}`
1304/// | ml-if-head `else` `if` ml-if-cond `{` ml-stmt* `}`
1305/// ml-if-stmt ::= ml-if-head
1306/// | ml-if-head `else` `{` ml-stmt* `}`
1307///
1308IfStmt * Parser::parseIfStmt(PointerUnion<MLFunction *, NodeStmt *> parent) {
1309 consumeToken(Token::kw_if);
1310
1311 //TODO: parse condition
1312 IfStmt *stmt = new IfStmt(parent);
1313 if (parseNestedStatements(stmt)) {
1314 delete stmt;
1315 return nullptr;
1316 }
1317
1318 int clauseNum = 0;
1319 while (consumeIf(Token::kw_else)) {
1320 if (consumeIf(Token::kw_if)) {
1321 //TODO: parse condition
1322 }
1323 ElseClause * clause = new ElseClause(stmt, clauseNum);
1324 ++clauseNum;
1325 if (parseNestedStatements(clause)) {
1326 delete clause;
1327 return nullptr;
1328 }
1329 }
1330
1331 return stmt;
1332}
1333
1334///
1335/// Parse `{` ml-stmt* `}`
1336///
1337ParseResult Parser::parseNestedStatements(NodeStmt *parent) {
1338 if (!consumeIf(Token::l_brace))
1339 return emitError("expected '{' before statement list");
1340
1341 if (consumeIf(Token::r_brace)) {
1342 // TODO: parse OperationStmt
1343 return ParseSuccess;
1344 }
1345
1346 while (!consumeIf(Token::r_brace)) {
1347 auto *stmt = parseStatement(parent);
1348 if (!stmt)
1349 return ParseFailure;
1350 parent->children.push_back(stmt);
1351 }
1352
1353 return ParseSuccess;
1354}
1355
Chris Lattner4c95a502018-06-23 16:03:42 -07001356//===----------------------------------------------------------------------===//
1357// Top-level entity parsing.
1358//===----------------------------------------------------------------------===//
1359
Chris Lattnere79379a2018-06-22 10:39:19 -07001360/// This is the top-level module parser.
1361Module *Parser::parseModule() {
1362 while (1) {
1363 switch (curToken.getKind()) {
1364 default:
1365 emitError("expected a top level entity");
1366 return nullptr;
1367
Uday Bondhugula015cbb12018-07-03 20:16:08 -07001368 // If we got to the end of the file, then we're done.
Chris Lattnere79379a2018-06-22 10:39:19 -07001369 case Token::eof:
1370 return module.release();
1371
1372 // If we got an error token, then the lexer already emitted an error, just
1373 // stop. Someday we could introduce error recovery if there was demand for
1374 // it.
1375 case Token::error:
1376 return nullptr;
1377
1378 case Token::kw_extfunc:
Chris Lattner4c95a502018-06-23 16:03:42 -07001379 if (parseExtFunc()) return nullptr;
Chris Lattnere79379a2018-06-22 10:39:19 -07001380 break;
1381
Chris Lattner4c95a502018-06-23 16:03:42 -07001382 case Token::kw_cfgfunc:
1383 if (parseCFGFunc()) return nullptr;
1384 break;
Uday Bondhugula015cbb12018-07-03 20:16:08 -07001385
Chris Lattner8da0c282018-06-29 11:15:56 -07001386 case Token::affine_map_identifier:
MLIR Teamf85a6262018-06-27 11:03:08 -07001387 if (parseAffineMapDef()) return nullptr;
1388 break;
Chris Lattner4c95a502018-06-23 16:03:42 -07001389
Tatiana Shpeismanc96b5872018-06-28 17:02:32 -07001390 case Token::kw_mlfunc:
1391 if (parseMLFunc()) return nullptr;
1392 break;
1393
Uday Bondhugula015cbb12018-07-03 20:16:08 -07001394 // TODO: affine entity declarations, etc.
Chris Lattnere79379a2018-06-22 10:39:19 -07001395 }
1396 }
1397}
1398
1399//===----------------------------------------------------------------------===//
1400
Jacques Pienaar7b829702018-07-03 13:24:09 -07001401void mlir::defaultErrorReporter(const llvm::SMDiagnostic &error) {
1402 const auto &sourceMgr = *error.getSourceMgr();
1403 sourceMgr.PrintMessage(error.getLoc(), error.getKind(), error.getMessage());
1404}
1405
Chris Lattnere79379a2018-06-22 10:39:19 -07001406/// This parses the file specified by the indicated SourceMgr and returns an
1407/// MLIR module if it was valid. If not, it emits diagnostics and returns null.
Jacques Pienaar9c411be2018-06-24 19:17:35 -07001408Module *mlir::parseSourceFile(llvm::SourceMgr &sourceMgr, MLIRContext *context,
Jacques Pienaar7b829702018-07-03 13:24:09 -07001409 SMDiagnosticHandlerTy errorReporter) {
1410 return Parser(sourceMgr, context,
1411 errorReporter ? std::move(errorReporter) : defaultErrorReporter)
1412 .parseModule();
Chris Lattnere79379a2018-06-22 10:39:19 -07001413}