2018-06-23 01:39:19 +08:00
|
|
|
//===- Parser.cpp - MLIR Parser Implementation ----------------------------===//
|
|
|
|
//
|
|
|
|
// Copyright 2019 The MLIR Authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
// =============================================================================
|
|
|
|
//
|
|
|
|
// This file implements the parser for the MLIR textual form.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "mlir/Parser.h"
|
|
|
|
#include "Lexer.h"
|
2018-06-30 09:09:29 +08:00
|
|
|
#include "mlir/IR/AffineExpr.h"
|
2018-06-28 02:03:08 +08:00
|
|
|
#include "mlir/IR/AffineMap.h"
|
2018-07-05 11:45:39 +08:00
|
|
|
#include "mlir/IR/Attributes.h"
|
2018-07-09 11:51:38 +08:00
|
|
|
#include "mlir/IR/Builders.h"
|
2018-10-11 05:23:30 +08:00
|
|
|
#include "mlir/IR/BuiltinOps.h"
|
2018-08-29 06:26:20 +08:00
|
|
|
#include "mlir/IR/IntegerSet.h"
|
2018-08-28 12:05:16 +08:00
|
|
|
#include "mlir/IR/Location.h"
|
2018-08-21 23:42:19 +08:00
|
|
|
#include "mlir/IR/MLIRContext.h"
|
2018-07-07 01:46:19 +08:00
|
|
|
#include "mlir/IR/Module.h"
|
2018-07-26 02:15:20 +08:00
|
|
|
#include "mlir/IR/OpImplementation.h"
|
2018-08-21 23:42:19 +08:00
|
|
|
#include "mlir/IR/StmtVisitor.h"
|
2018-06-23 13:03:48 +08:00
|
|
|
#include "mlir/IR/Types.h"
|
2018-10-19 04:54:44 +08:00
|
|
|
#include "mlir/Support/STLExtras.h"
|
2018-11-15 06:26:47 +08:00
|
|
|
#include "mlir/Transforms/Utils.h"
|
2018-10-19 04:54:44 +08:00
|
|
|
#include "llvm/ADT/APInt.h"
|
2018-08-03 16:54:46 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
2018-09-03 22:38:31 +08:00
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
2018-08-22 08:55:22 +08:00
|
|
|
#include "llvm/Support/PrettyStackTrace.h"
|
2018-09-03 22:38:31 +08:00
|
|
|
#include "llvm/Support/SMLoc.h"
|
2018-08-03 16:54:46 +08:00
|
|
|
#include "llvm/Support/SourceMgr.h"
|
2018-10-24 04:44:04 +08:00
|
|
|
#include <algorithm>
|
2018-09-03 22:38:31 +08:00
|
|
|
|
2018-06-23 01:39:19 +08:00
|
|
|
using namespace mlir;
|
2018-09-03 22:38:31 +08:00
|
|
|
using llvm::MemoryBuffer;
|
2018-06-24 07:03:42 +08:00
|
|
|
using llvm::SMLoc;
|
2018-07-24 07:56:32 +08:00
|
|
|
using llvm::SourceMgr;
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2018-06-23 13:03:48 +08:00
|
|
|
/// Simple enum to make code read better in cases that would otherwise return a
|
|
|
|
/// bool value. Failure is "true" in a boolean context.
|
2018-07-24 07:56:32 +08:00
|
|
|
enum ParseResult { ParseSuccess, ParseFailure };
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
namespace {
|
|
|
|
class Parser;
|
|
|
|
|
|
|
|
/// This class refers to all of the state maintained globally by the parser,
|
|
|
|
/// such as the current lexer position etc. The Parser base class provides
|
|
|
|
/// methods to access this.
|
|
|
|
class ParserState {
|
2018-06-29 11:45:33 +08:00
|
|
|
public:
|
2018-09-09 09:37:27 +08:00
|
|
|
ParserState(const llvm::SourceMgr &sourceMgr, Module *module)
|
2018-09-03 13:01:45 +08:00
|
|
|
: context(module->getContext()), module(module), lex(sourceMgr, context),
|
2018-10-22 10:49:31 +08:00
|
|
|
curToken(lex.lexToken()) {}
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2018-10-10 05:40:41 +08:00
|
|
|
~ParserState() {
|
|
|
|
// Destroy the forward references upon error.
|
|
|
|
for (auto forwardRef : functionForwardRefs)
|
2018-12-28 03:07:34 +08:00
|
|
|
delete forwardRef.second;
|
2018-10-10 05:40:41 +08:00
|
|
|
functionForwardRefs.clear();
|
|
|
|
}
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
// A map from affine map identifier to AffineMap.
|
2018-10-10 07:39:24 +08:00
|
|
|
llvm::StringMap<AffineMap> affineMapDefinitions;
|
2018-08-20 12:17:22 +08:00
|
|
|
|
2018-08-08 05:24:38 +08:00
|
|
|
// A map from integer set identifier to IntegerSet.
|
2018-10-11 00:45:59 +08:00
|
|
|
llvm::StringMap<IntegerSet> integerSetDefinitions;
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2018-08-20 12:17:22 +08:00
|
|
|
// This keeps track of all forward references to functions along with the
|
2018-09-08 00:08:13 +08:00
|
|
|
// temporary function used to represent them.
|
|
|
|
llvm::DenseMap<Identifier, Function *> functionForwardRefs;
|
2018-08-20 12:17:22 +08:00
|
|
|
|
2018-06-23 01:39:19 +08:00
|
|
|
private:
|
2018-07-10 10:05:38 +08:00
|
|
|
ParserState(const ParserState &) = delete;
|
|
|
|
void operator=(const ParserState &) = delete;
|
|
|
|
|
|
|
|
friend class Parser;
|
|
|
|
|
|
|
|
// The context we're parsing into.
|
2018-07-11 01:08:27 +08:00
|
|
|
MLIRContext *const context;
|
|
|
|
|
|
|
|
// This is the module we are parsing into.
|
|
|
|
Module *const module;
|
2018-06-23 13:03:48 +08:00
|
|
|
|
|
|
|
// The lexer for the source file we're parsing.
|
2018-06-23 01:39:19 +08:00
|
|
|
Lexer lex;
|
|
|
|
|
|
|
|
// This is the next token that hasn't been consumed yet.
|
|
|
|
Token curToken;
|
2018-07-10 10:05:38 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2018-10-25 02:05:18 +08:00
|
|
|
using CreateOperationFunction =
|
|
|
|
std::function<Operation *(const OperationState &)>;
|
2018-07-17 02:47:09 +08:00
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
/// This class implement support for parsing global entities like types and
|
|
|
|
/// shared entities like SSA names. It is intended to be subclassed by
|
|
|
|
/// specialized subparsers that include state, e.g. when a local symbol table.
|
|
|
|
class Parser {
|
|
|
|
public:
|
2018-07-11 01:08:27 +08:00
|
|
|
Builder builder;
|
|
|
|
|
|
|
|
Parser(ParserState &state) : builder(state.context), state(state) {}
|
2018-06-28 02:03:08 +08:00
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
// Helper methods to get stuff from the parser-global state.
|
|
|
|
ParserState &getState() const { return state; }
|
2018-07-10 10:05:38 +08:00
|
|
|
MLIRContext *getContext() const { return state.context; }
|
2018-07-11 01:08:27 +08:00
|
|
|
Module *getModule() { return state.module; }
|
2018-09-09 09:37:27 +08:00
|
|
|
const llvm::SourceMgr &getSourceMgr() { return state.lex.getSourceMgr(); }
|
2018-07-10 10:05:38 +08:00
|
|
|
|
|
|
|
/// Return the current token the parser is inspecting.
|
|
|
|
const Token &getToken() const { return state.curToken; }
|
|
|
|
StringRef getTokenSpelling() const { return state.curToken.getSpelling(); }
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2018-08-24 05:32:25 +08:00
|
|
|
/// Encode the specified source location information into an attribute for
|
|
|
|
/// attachment to the IR.
|
2018-11-09 04:28:35 +08:00
|
|
|
Location getEncodedSourceLocation(llvm::SMLoc loc) {
|
2018-09-03 13:01:45 +08:00
|
|
|
return state.lex.getEncodedSourceLocation(loc);
|
|
|
|
}
|
2018-08-24 05:32:25 +08:00
|
|
|
|
2018-06-23 01:39:19 +08:00
|
|
|
/// Emit an error and return failure.
|
2018-06-24 07:03:42 +08:00
|
|
|
ParseResult emitError(const Twine &message) {
|
2018-07-10 10:05:38 +08:00
|
|
|
return emitError(state.curToken.getLoc(), message);
|
2018-06-24 07:03:42 +08:00
|
|
|
}
|
|
|
|
ParseResult emitError(SMLoc loc, const Twine &message);
|
2018-06-23 01:39:19 +08:00
|
|
|
|
|
|
|
/// Advance the current lexer onto the next token.
|
|
|
|
void consumeToken() {
|
2018-07-10 10:05:38 +08:00
|
|
|
assert(state.curToken.isNot(Token::eof, Token::error) &&
|
2018-06-23 01:39:19 +08:00
|
|
|
"shouldn't advance past EOF or errors");
|
2018-07-10 10:05:38 +08:00
|
|
|
state.curToken = state.lex.lexToken();
|
2018-06-23 01:39:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Advance the current lexer onto the next token, asserting what the expected
|
|
|
|
/// current token is. This is preferred to the above method because it leads
|
|
|
|
/// to more self-documenting code with better checking.
|
2018-06-30 02:15:56 +08:00
|
|
|
void consumeToken(Token::Kind kind) {
|
2018-07-10 10:05:38 +08:00
|
|
|
assert(state.curToken.is(kind) && "consumed an unexpected token");
|
2018-06-23 01:39:19 +08:00
|
|
|
consumeToken();
|
|
|
|
}
|
|
|
|
|
2018-06-23 06:52:02 +08:00
|
|
|
/// If the current token has the specified kind, consume it and return true.
|
|
|
|
/// If not, return false.
|
2018-06-30 02:15:56 +08:00
|
|
|
bool consumeIf(Token::Kind kind) {
|
2018-07-10 10:05:38 +08:00
|
|
|
if (state.curToken.isNot(kind))
|
2018-06-23 06:52:02 +08:00
|
|
|
return false;
|
|
|
|
consumeToken(kind);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
/// Consume the specified token if present and return success. On failure,
|
|
|
|
/// output a diagnostic and return failure.
|
|
|
|
ParseResult parseToken(Token::Kind expectedToken, const Twine &message);
|
|
|
|
|
2018-07-22 05:32:09 +08:00
|
|
|
/// Parse a comma-separated list of elements up until the specified end token.
|
|
|
|
ParseResult
|
|
|
|
parseCommaSeparatedListUntil(Token::Kind rightToken,
|
|
|
|
const std::function<ParseResult()> &parseElement,
|
|
|
|
bool allowEmptyList = true);
|
|
|
|
|
|
|
|
/// Parse a comma separated list of elements that must have at least one entry
|
|
|
|
/// in it.
|
|
|
|
ParseResult
|
|
|
|
parseCommaSeparatedList(const std::function<ParseResult()> &parseElement);
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-06-23 13:03:48 +08:00
|
|
|
// We have two forms of parsing methods - those that return a non-null
|
|
|
|
// pointer on success, and those that return a ParseResult to indicate whether
|
|
|
|
// they returned a failure. The second class fills in by-reference arguments
|
|
|
|
// as the results of their action.
|
|
|
|
|
2018-06-23 01:39:19 +08:00
|
|
|
// Type parsing.
|
2018-10-31 05:59:22 +08:00
|
|
|
VectorType parseVectorType();
|
2018-09-14 01:43:35 +08:00
|
|
|
ParseResult parseXInDimensionList();
|
2018-06-23 06:52:02 +08:00
|
|
|
ParseResult parseDimensionListRanked(SmallVectorImpl<int> &dimensions);
|
2018-10-31 05:59:22 +08:00
|
|
|
Type parseTensorType();
|
|
|
|
Type parseMemRefType();
|
|
|
|
Type parseFunctionType();
|
|
|
|
Type parseType();
|
|
|
|
ParseResult parseTypeListNoParens(SmallVectorImpl<Type> &elements);
|
|
|
|
ParseResult parseTypeList(SmallVectorImpl<Type> &elements);
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
// Attribute parsing.
|
2018-08-22 08:55:22 +08:00
|
|
|
Function *resolveFunctionReference(StringRef nameStr, SMLoc nameLoc,
|
2018-10-31 05:59:22 +08:00
|
|
|
FunctionType type);
|
2018-11-16 09:53:51 +08:00
|
|
|
Attribute parseAttribute(Type type = {});
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
ParseResult parseAttributeDict(SmallVectorImpl<NamedAttribute> &attributes);
|
|
|
|
|
2018-07-04 11:16:08 +08:00
|
|
|
// Polyhedral structures.
|
2018-10-26 09:33:42 +08:00
|
|
|
void parseAffineStructureInline(AffineMap *map, IntegerSet *set);
|
2018-10-26 13:13:03 +08:00
|
|
|
void parseAffineStructureReference(AffineMap *map, IntegerSet *set);
|
2018-10-10 07:39:24 +08:00
|
|
|
AffineMap parseAffineMapInline();
|
|
|
|
AffineMap parseAffineMapReference();
|
2018-10-11 00:45:59 +08:00
|
|
|
IntegerSet parseIntegerSetInline();
|
|
|
|
IntegerSet parseIntegerSetReference();
|
2018-10-31 05:59:22 +08:00
|
|
|
DenseElementsAttr parseDenseElementsAttr(VectorOrTensorType type);
|
|
|
|
DenseElementsAttr parseDenseElementsAttr(Type eltType, bool isVector);
|
|
|
|
VectorOrTensorType parseVectorOrTensorType();
|
2018-06-28 02:03:08 +08:00
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
private:
|
|
|
|
// The Parser is subclassed and reinstantiated. Do not add additional
|
|
|
|
// non-trivial state here, add it to the ParserState class.
|
|
|
|
ParserState &state;
|
2018-06-23 01:39:19 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Helper methods.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-06-24 07:03:42 +08:00
|
|
|
ParseResult Parser::emitError(SMLoc loc, const Twine &message) {
|
2018-06-23 06:52:02 +08:00
|
|
|
// If we hit a parse error in response to a lexer error, then the lexer
|
2018-06-25 10:17:35 +08:00
|
|
|
// already reported the error.
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().is(Token::error))
|
2018-06-23 06:52:02 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
2018-12-08 01:30:25 +08:00
|
|
|
getContext()->emitError(getEncodedSourceLocation(loc), message);
|
2018-06-23 01:39:19 +08:00
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
/// Consume the specified token if present and return success. On failure,
|
|
|
|
/// output a diagnostic and return failure.
|
|
|
|
ParseResult Parser::parseToken(Token::Kind expectedToken,
|
|
|
|
const Twine &message) {
|
|
|
|
if (consumeIf(expectedToken))
|
|
|
|
return ParseSuccess;
|
|
|
|
return emitError(message);
|
|
|
|
}
|
|
|
|
|
2018-07-22 05:32:09 +08:00
|
|
|
/// Parse a comma separated list of elements that must have at least one entry
|
|
|
|
/// in it.
|
|
|
|
ParseResult Parser::parseCommaSeparatedList(
|
|
|
|
const std::function<ParseResult()> &parseElement) {
|
|
|
|
// Non-empty case starts with an element.
|
|
|
|
if (parseElement())
|
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
// Otherwise we have a list of comma separated elements.
|
|
|
|
while (consumeIf(Token::comma)) {
|
|
|
|
if (parseElement())
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-06-23 06:52:02 +08:00
|
|
|
/// Parse a comma-separated list of elements, terminated with an arbitrary
|
|
|
|
/// token. This allows empty lists if allowEmptyList is true.
|
|
|
|
///
|
|
|
|
/// abstract-list ::= rightToken // if allowEmptyList == true
|
|
|
|
/// abstract-list ::= element (',' element)* rightToken
|
|
|
|
///
|
2018-07-22 05:32:09 +08:00
|
|
|
ParseResult Parser::parseCommaSeparatedListUntil(
|
|
|
|
Token::Kind rightToken, const std::function<ParseResult()> &parseElement,
|
|
|
|
bool allowEmptyList) {
|
2018-06-23 06:52:02 +08:00
|
|
|
// Handle the empty case.
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().is(rightToken)) {
|
2018-06-23 06:52:02 +08:00
|
|
|
if (!allowEmptyList)
|
|
|
|
return emitError("expected list element");
|
|
|
|
consumeToken(rightToken);
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseCommaSeparatedList(parseElement) ||
|
|
|
|
parseToken(rightToken, "expected ',' or '" +
|
|
|
|
Token::getTokenSpelling(rightToken) + "'"))
|
2018-06-23 06:52:02 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
2018-06-23 01:39:19 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Type Parsing
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
/// Parse an arbitrary type.
|
|
|
|
///
|
|
|
|
/// type ::= integer-type
|
Enable arithmetics for index types.
Arithmetic and comparison instructions are necessary to implement, e.g.,
control flow when lowering MLFunctions to CFGFunctions. (While it is possible
to replace some of the arithmetics by affine_apply instructions for loop
bounds, it is still necessary for loop bounds checking, steps, if-conditions,
non-trivial memref subscripts, etc.) Furthermore, working with indirect
accesses in, e.g., lookup tables for large embeddings, may require operating on
tensors of indexes. For example, the equivalents to C code "LUT[Index[i]]" or
"ResultIndex[i] = i + j" where i, j are loop induction variables require the
arithmetics on indices as well as the possibility to operate on tensors
thereof. Allow arithmetic and comparison operations to apply to index types by
declaring them integer-like. Allow tensors whose element type is index for
indirection purposes.
The absence of vectors with "index" element type is explicitly tested, but the
only justification for this restriction in the CL introducing the test is
"because we don't need them". Do NOT enable vectors of index types, although
it makes vector and tensor types inconsistent with respect to allowed element
types.
PiperOrigin-RevId: 220614055
2018-11-08 20:04:32 +08:00
|
|
|
/// | index-type
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
/// | float-type
|
|
|
|
/// | other-type
|
|
|
|
/// | vector-type
|
|
|
|
/// | tensor-type
|
|
|
|
/// | memref-type
|
|
|
|
/// | function-type
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
Enable arithmetics for index types.
Arithmetic and comparison instructions are necessary to implement, e.g.,
control flow when lowering MLFunctions to CFGFunctions. (While it is possible
to replace some of the arithmetics by affine_apply instructions for loop
bounds, it is still necessary for loop bounds checking, steps, if-conditions,
non-trivial memref subscripts, etc.) Furthermore, working with indirect
accesses in, e.g., lookup tables for large embeddings, may require operating on
tensors of indexes. For example, the equivalents to C code "LUT[Index[i]]" or
"ResultIndex[i] = i + j" where i, j are loop induction variables require the
arithmetics on indices as well as the possibility to operate on tensors
thereof. Allow arithmetic and comparison operations to apply to index types by
declaring them integer-like. Allow tensors whose element type is index for
indirection purposes.
The absence of vectors with "index" element type is explicitly tested, but the
only justification for this restriction in the CL introducing the test is
"because we don't need them". Do NOT enable vectors of index types, although
it makes vector and tensor types inconsistent with respect to allowed element
types.
PiperOrigin-RevId: 220614055
2018-11-08 20:04:32 +08:00
|
|
|
/// index-type ::= `index`
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
/// float-type ::= `f16` | `bf16` | `f32` | `f64`
|
Enable arithmetics for index types.
Arithmetic and comparison instructions are necessary to implement, e.g.,
control flow when lowering MLFunctions to CFGFunctions. (While it is possible
to replace some of the arithmetics by affine_apply instructions for loop
bounds, it is still necessary for loop bounds checking, steps, if-conditions,
non-trivial memref subscripts, etc.) Furthermore, working with indirect
accesses in, e.g., lookup tables for large embeddings, may require operating on
tensors of indexes. For example, the equivalents to C code "LUT[Index[i]]" or
"ResultIndex[i] = i + j" where i, j are loop induction variables require the
arithmetics on indices as well as the possibility to operate on tensors
thereof. Allow arithmetic and comparison operations to apply to index types by
declaring them integer-like. Allow tensors whose element type is index for
indirection purposes.
The absence of vectors with "index" element type is explicitly tested, but the
only justification for this restriction in the CL introducing the test is
"because we don't need them". Do NOT enable vectors of index types, although
it makes vector and tensor types inconsistent with respect to allowed element
types.
PiperOrigin-RevId: 220614055
2018-11-08 20:04:32 +08:00
|
|
|
/// other-type ::= `tf_control`
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
2018-10-31 05:59:22 +08:00
|
|
|
Type Parser::parseType() {
|
2018-07-10 10:05:38 +08:00
|
|
|
switch (getToken().getKind()) {
|
2018-06-23 13:03:48 +08:00
|
|
|
default:
|
|
|
|
return (emitError("expected type"), nullptr);
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
case Token::kw_memref:
|
|
|
|
return parseMemRefType();
|
|
|
|
case Token::kw_tensor:
|
|
|
|
return parseTensorType();
|
|
|
|
case Token::kw_vector:
|
|
|
|
return parseVectorType();
|
|
|
|
case Token::l_paren:
|
|
|
|
return parseFunctionType();
|
|
|
|
// integer-type
|
|
|
|
case Token::inttype: {
|
|
|
|
auto width = getToken().getIntTypeBitwidth();
|
|
|
|
if (!width.hasValue())
|
|
|
|
return (emitError("invalid integer width"), nullptr);
|
2018-11-13 07:12:09 +08:00
|
|
|
auto loc = getEncodedSourceLocation(getToken().getLoc());
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
consumeToken(Token::inttype);
|
2018-11-13 07:12:09 +08:00
|
|
|
return IntegerType::getChecked(width.getValue(), builder.getContext(), loc);
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// float-type
|
2018-06-23 06:52:02 +08:00
|
|
|
case Token::kw_bf16:
|
|
|
|
consumeToken(Token::kw_bf16);
|
2018-07-09 11:51:38 +08:00
|
|
|
return builder.getBF16Type();
|
2018-06-23 06:52:02 +08:00
|
|
|
case Token::kw_f16:
|
|
|
|
consumeToken(Token::kw_f16);
|
2018-07-09 11:51:38 +08:00
|
|
|
return builder.getF16Type();
|
2018-06-23 06:52:02 +08:00
|
|
|
case Token::kw_f32:
|
|
|
|
consumeToken(Token::kw_f32);
|
2018-07-09 11:51:38 +08:00
|
|
|
return builder.getF32Type();
|
2018-06-23 06:52:02 +08:00
|
|
|
case Token::kw_f64:
|
|
|
|
consumeToken(Token::kw_f64);
|
2018-07-09 11:51:38 +08:00
|
|
|
return builder.getF64Type();
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
|
Enable arithmetics for index types.
Arithmetic and comparison instructions are necessary to implement, e.g.,
control flow when lowering MLFunctions to CFGFunctions. (While it is possible
to replace some of the arithmetics by affine_apply instructions for loop
bounds, it is still necessary for loop bounds checking, steps, if-conditions,
non-trivial memref subscripts, etc.) Furthermore, working with indirect
accesses in, e.g., lookup tables for large embeddings, may require operating on
tensors of indexes. For example, the equivalents to C code "LUT[Index[i]]" or
"ResultIndex[i] = i + j" where i, j are loop induction variables require the
arithmetics on indices as well as the possibility to operate on tensors
thereof. Allow arithmetic and comparison operations to apply to index types by
declaring them integer-like. Allow tensors whose element type is index for
indirection purposes.
The absence of vectors with "index" element type is explicitly tested, but the
only justification for this restriction in the CL introducing the test is
"because we don't need them". Do NOT enable vectors of index types, although
it makes vector and tensor types inconsistent with respect to allowed element
types.
PiperOrigin-RevId: 220614055
2018-11-08 20:04:32 +08:00
|
|
|
// index-type
|
2018-10-07 08:21:53 +08:00
|
|
|
case Token::kw_index:
|
|
|
|
consumeToken(Token::kw_index);
|
|
|
|
return builder.getIndexType();
|
Enable arithmetics for index types.
Arithmetic and comparison instructions are necessary to implement, e.g.,
control flow when lowering MLFunctions to CFGFunctions. (While it is possible
to replace some of the arithmetics by affine_apply instructions for loop
bounds, it is still necessary for loop bounds checking, steps, if-conditions,
non-trivial memref subscripts, etc.) Furthermore, working with indirect
accesses in, e.g., lookup tables for large embeddings, may require operating on
tensors of indexes. For example, the equivalents to C code "LUT[Index[i]]" or
"ResultIndex[i] = i + j" where i, j are loop induction variables require the
arithmetics on indices as well as the possibility to operate on tensors
thereof. Allow arithmetic and comparison operations to apply to index types by
declaring them integer-like. Allow tensors whose element type is index for
indirection purposes.
The absence of vectors with "index" element type is explicitly tested, but the
only justification for this restriction in the CL introducing the test is
"because we don't need them". Do NOT enable vectors of index types, although
it makes vector and tensor types inconsistent with respect to allowed element
types.
PiperOrigin-RevId: 220614055
2018-11-08 20:04:32 +08:00
|
|
|
|
|
|
|
// other-type
|
2018-07-28 02:07:12 +08:00
|
|
|
case Token::kw_tf_control:
|
|
|
|
consumeToken(Token::kw_tf_control);
|
|
|
|
return builder.getTFControlType();
|
2018-09-20 01:28:46 +08:00
|
|
|
case Token::kw_tf_resource:
|
|
|
|
consumeToken(Token::kw_tf_resource);
|
|
|
|
return builder.getTFResourceType();
|
2018-09-20 12:15:43 +08:00
|
|
|
case Token::kw_tf_variant:
|
|
|
|
consumeToken(Token::kw_tf_variant);
|
|
|
|
return builder.getTFVariantType();
|
2018-09-21 02:59:17 +08:00
|
|
|
case Token::kw_tf_complex64:
|
|
|
|
consumeToken(Token::kw_tf_complex64);
|
|
|
|
return builder.getTFComplex64Type();
|
|
|
|
case Token::kw_tf_complex128:
|
|
|
|
consumeToken(Token::kw_tf_complex128);
|
|
|
|
return builder.getTFComplex128Type();
|
2018-08-02 03:55:27 +08:00
|
|
|
case Token::kw_tf_string:
|
|
|
|
consumeToken(Token::kw_tf_string);
|
|
|
|
return builder.getTFStringType();
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a vector type.
|
|
|
|
///
|
|
|
|
/// vector-type ::= `vector` `<` const-dimension-list primitive-type `>`
|
|
|
|
/// const-dimension-list ::= (integer-literal `x`)+
|
|
|
|
///
|
2018-10-31 05:59:22 +08:00
|
|
|
VectorType Parser::parseVectorType() {
|
2018-06-23 06:52:02 +08:00
|
|
|
consumeToken(Token::kw_vector);
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::less, "expected '<' in vector type"))
|
|
|
|
return nullptr;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().isNot(Token::integer))
|
2018-06-23 13:03:48 +08:00
|
|
|
return (emitError("expected dimension size in vector type"), nullptr);
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-10-10 07:49:39 +08:00
|
|
|
SmallVector<int, 4> dimensions;
|
2018-07-10 10:05:38 +08:00
|
|
|
while (getToken().is(Token::integer)) {
|
2018-06-23 06:52:02 +08:00
|
|
|
// Make sure this integer value is in bound and valid.
|
2018-07-10 10:05:38 +08:00
|
|
|
auto dimension = getToken().getUnsignedIntegerValue();
|
2018-06-23 06:52:02 +08:00
|
|
|
if (!dimension.hasValue())
|
2018-06-23 13:03:48 +08:00
|
|
|
return (emitError("invalid dimension in vector type"), nullptr);
|
2018-10-10 07:49:39 +08:00
|
|
|
dimensions.push_back((int)dimension.getValue());
|
2018-06-23 06:52:02 +08:00
|
|
|
|
|
|
|
consumeToken(Token::integer);
|
|
|
|
|
|
|
|
// Make sure we have an 'x' or something like 'xbf32'.
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().isNot(Token::bare_identifier) ||
|
|
|
|
getTokenSpelling()[0] != 'x')
|
2018-06-23 13:03:48 +08:00
|
|
|
return (emitError("expected 'x' in vector dimension list"), nullptr);
|
2018-06-23 06:52:02 +08:00
|
|
|
|
|
|
|
// If we had a prefix of 'x', lex the next token immediately after the 'x'.
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getTokenSpelling().size() != 1)
|
|
|
|
state.lex.resetPointer(getTokenSpelling().data() + 1);
|
2018-06-23 06:52:02 +08:00
|
|
|
|
|
|
|
// Consume the 'x'.
|
|
|
|
consumeToken(Token::bare_identifier);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the element type.
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
auto typeLoc = getToken().getLoc();
|
2018-10-31 05:59:22 +08:00
|
|
|
auto elementType = parseType();
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
if (!elementType || parseToken(Token::greater, "expected '>' in vector type"))
|
2018-06-23 13:03:48 +08:00
|
|
|
return nullptr;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-11-09 05:41:21 +08:00
|
|
|
return VectorType::getChecked(dimensions, elementType,
|
|
|
|
getEncodedSourceLocation(typeLoc));
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
2018-09-14 01:43:35 +08:00
|
|
|
/// Parse an 'x' token in a dimension list, handling the case where the x is
|
|
|
|
/// juxtaposed with an element type, as in "xf32", leaving the "f32" as the next
|
|
|
|
/// token.
|
|
|
|
ParseResult Parser::parseXInDimensionList() {
|
|
|
|
if (getToken().isNot(Token::bare_identifier) || getTokenSpelling()[0] != 'x')
|
|
|
|
return emitError("expected 'x' in dimension list");
|
|
|
|
|
|
|
|
// If we had a prefix of 'x', lex the next token immediately after the 'x'.
|
|
|
|
if (getTokenSpelling().size() != 1)
|
|
|
|
state.lex.resetPointer(getTokenSpelling().data() + 1);
|
|
|
|
|
|
|
|
// Consume the 'x'.
|
|
|
|
consumeToken(Token::bare_identifier);
|
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-06-23 06:52:02 +08:00
|
|
|
/// Parse a dimension list of a tensor or memref type. This populates the
|
|
|
|
/// dimension list, returning -1 for the '?' dimensions.
|
|
|
|
///
|
|
|
|
/// dimension-list-ranked ::= (dimension `x`)*
|
|
|
|
/// dimension ::= `?` | integer-literal
|
|
|
|
///
|
|
|
|
ParseResult Parser::parseDimensionListRanked(SmallVectorImpl<int> &dimensions) {
|
2018-07-10 10:05:38 +08:00
|
|
|
while (getToken().isAny(Token::integer, Token::question)) {
|
2018-06-23 06:52:02 +08:00
|
|
|
if (consumeIf(Token::question)) {
|
|
|
|
dimensions.push_back(-1);
|
|
|
|
} else {
|
|
|
|
// Make sure this integer value is in bound and valid.
|
2018-07-10 10:05:38 +08:00
|
|
|
auto dimension = getToken().getUnsignedIntegerValue();
|
2018-06-23 06:52:02 +08:00
|
|
|
if (!dimension.hasValue() || (int)dimension.getValue() < 0)
|
|
|
|
return emitError("invalid dimension");
|
|
|
|
dimensions.push_back((int)dimension.getValue());
|
|
|
|
consumeToken(Token::integer);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure we have an 'x' or something like 'xbf32'.
|
2018-09-14 01:43:35 +08:00
|
|
|
if (parseXInDimensionList())
|
|
|
|
return ParseFailure;
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a tensor type.
|
|
|
|
///
|
|
|
|
/// tensor-type ::= `tensor` `<` dimension-list element-type `>`
|
2018-09-14 01:43:35 +08:00
|
|
|
/// dimension-list ::= dimension-list-ranked | `*x`
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
2018-10-31 05:59:22 +08:00
|
|
|
Type Parser::parseTensorType() {
|
2018-06-23 06:52:02 +08:00
|
|
|
consumeToken(Token::kw_tensor);
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::less, "expected '<' in tensor type"))
|
|
|
|
return nullptr;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
|
|
|
bool isUnranked;
|
|
|
|
SmallVector<int, 4> dimensions;
|
|
|
|
|
2018-09-14 01:43:35 +08:00
|
|
|
if (consumeIf(Token::star)) {
|
|
|
|
// This is an unranked tensor type.
|
2018-06-23 06:52:02 +08:00
|
|
|
isUnranked = true;
|
2018-09-14 01:43:35 +08:00
|
|
|
|
|
|
|
if (parseXInDimensionList())
|
|
|
|
return nullptr;
|
|
|
|
|
2018-06-23 06:52:02 +08:00
|
|
|
} else {
|
|
|
|
isUnranked = false;
|
|
|
|
if (parseDimensionListRanked(dimensions))
|
2018-06-23 13:03:48 +08:00
|
|
|
return nullptr;
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the element type.
|
2018-11-09 05:47:19 +08:00
|
|
|
auto typeLocation = getEncodedSourceLocation(getToken().getLoc());
|
2018-10-31 05:59:22 +08:00
|
|
|
auto elementType = parseType();
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
if (!elementType || parseToken(Token::greater, "expected '>' in tensor type"))
|
2018-06-23 13:03:48 +08:00
|
|
|
return nullptr;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-06-24 09:09:09 +08:00
|
|
|
if (isUnranked)
|
2018-11-09 05:47:19 +08:00
|
|
|
return UnrankedTensorType::getChecked(elementType, typeLocation);
|
|
|
|
return RankedTensorType::getChecked(dimensions, elementType, typeLocation);
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a memref type.
|
|
|
|
///
|
|
|
|
/// memref-type ::= `memref` `<` dimension-list-ranked element-type
|
|
|
|
/// (`,` semi-affine-map-composition)? (`,` memory-space)? `>`
|
|
|
|
///
|
|
|
|
/// semi-affine-map-composition ::= (semi-affine-map `,` )* semi-affine-map
|
|
|
|
/// memory-space ::= integer-literal /* | TODO: address-space-id */
|
|
|
|
///
|
2018-10-31 05:59:22 +08:00
|
|
|
Type Parser::parseMemRefType() {
|
2018-06-23 06:52:02 +08:00
|
|
|
consumeToken(Token::kw_memref);
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::less, "expected '<' in memref type"))
|
|
|
|
return nullptr;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
|
|
|
SmallVector<int, 4> dimensions;
|
|
|
|
if (parseDimensionListRanked(dimensions))
|
2018-06-23 13:03:48 +08:00
|
|
|
return nullptr;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
|
|
|
// Parse the element type.
|
Eliminate "primitive" types from being a thing, splitting them into FloatType
and OtherType. Other type is now the thing that holds AffineInt, Control,
eventually Resource, Variant, String, etc. FloatType holds the floating point
types, and allows convenient query of isa<FloatType>().
This fixes issues where we allowed control to be the element type of tensor,
memref, vector. At the same time, ban AffineInt from being an element of a
vector/memref/tensor as well since we don't need it.
I updated the spec to match this as well.
PiperOrigin-RevId: 206361942
2018-07-28 04:09:58 +08:00
|
|
|
auto typeLoc = getToken().getLoc();
|
2018-10-31 05:59:22 +08:00
|
|
|
auto elementType = parseType();
|
2018-06-23 13:03:48 +08:00
|
|
|
if (!elementType)
|
|
|
|
return nullptr;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-07-17 00:45:22 +08:00
|
|
|
// Parse semi-affine-map-composition.
|
2018-10-10 07:39:24 +08:00
|
|
|
SmallVector<AffineMap, 2> affineMapComposition;
|
2018-07-26 03:55:50 +08:00
|
|
|
unsigned memorySpace = 0;
|
2018-07-17 00:45:22 +08:00
|
|
|
bool parsedMemorySpace = false;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-07-17 00:45:22 +08:00
|
|
|
auto parseElt = [&]() -> ParseResult {
|
|
|
|
if (getToken().is(Token::integer)) {
|
|
|
|
// Parse memory space.
|
|
|
|
if (parsedMemorySpace)
|
|
|
|
return emitError("multiple memory spaces specified in memref type");
|
|
|
|
auto v = getToken().getUnsignedIntegerValue();
|
|
|
|
if (!v.hasValue())
|
|
|
|
return emitError("invalid memory space in memref type");
|
|
|
|
memorySpace = v.getValue();
|
|
|
|
consumeToken(Token::integer);
|
|
|
|
parsedMemorySpace = true;
|
|
|
|
} else {
|
|
|
|
// Parse affine map.
|
|
|
|
if (parsedMemorySpace)
|
|
|
|
return emitError("affine map after memory space in memref type");
|
2018-10-10 07:39:24 +08:00
|
|
|
auto affineMap = parseAffineMapReference();
|
|
|
|
if (!affineMap)
|
2018-07-17 00:45:22 +08:00
|
|
|
return ParseFailure;
|
|
|
|
affineMapComposition.push_back(affineMap);
|
|
|
|
}
|
|
|
|
return ParseSuccess;
|
|
|
|
};
|
|
|
|
|
2018-07-26 03:55:50 +08:00
|
|
|
// Parse a list of mappings and address space if present.
|
|
|
|
if (consumeIf(Token::comma)) {
|
|
|
|
// Parse comma separated list of affine maps, followed by memory space.
|
|
|
|
if (parseCommaSeparatedListUntil(Token::greater, parseElt,
|
|
|
|
/*allowEmptyList=*/false)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (parseToken(Token::greater, "expected ',' or '>' in memref type"))
|
|
|
|
return nullptr;
|
2018-07-17 00:45:22 +08:00
|
|
|
}
|
|
|
|
|
2018-11-02 16:48:22 +08:00
|
|
|
return MemRefType::getChecked(dimensions, elementType, affineMapComposition,
|
|
|
|
memorySpace, getEncodedSourceLocation(typeLoc));
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a function type.
|
|
|
|
///
|
|
|
|
/// function-type ::= type-list-parens `->` type-list
|
|
|
|
///
|
2018-10-31 05:59:22 +08:00
|
|
|
Type Parser::parseFunctionType() {
|
2018-07-10 10:05:38 +08:00
|
|
|
assert(getToken().is(Token::l_paren));
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
SmallVector<Type, 4> arguments, results;
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseTypeList(arguments) ||
|
|
|
|
parseToken(Token::arrow, "expected '->' in function type") ||
|
|
|
|
parseTypeList(results))
|
2018-06-23 13:03:48 +08:00
|
|
|
return nullptr;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-07-09 11:51:38 +08:00
|
|
|
return builder.getFunctionType(arguments, results);
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
2018-07-23 23:42:19 +08:00
|
|
|
/// Parse a list of types without an enclosing parenthesis. The list must have
|
|
|
|
/// at least one member.
|
|
|
|
///
|
|
|
|
/// type-list-no-parens ::= type (`,` type)*
|
|
|
|
///
|
2018-10-31 05:59:22 +08:00
|
|
|
ParseResult Parser::parseTypeListNoParens(SmallVectorImpl<Type> &elements) {
|
2018-07-23 23:42:19 +08:00
|
|
|
auto parseElt = [&]() -> ParseResult {
|
|
|
|
auto elt = parseType();
|
|
|
|
elements.push_back(elt);
|
|
|
|
return elt ? ParseSuccess : ParseFailure;
|
|
|
|
};
|
|
|
|
|
|
|
|
return parseCommaSeparatedList(parseElt);
|
|
|
|
}
|
|
|
|
|
2018-06-23 06:52:02 +08:00
|
|
|
/// Parse a "type list", which is a singular type, or a parenthesized list of
|
|
|
|
/// types.
|
|
|
|
///
|
|
|
|
/// type-list ::= type-list-parens | type
|
|
|
|
/// type-list-parens ::= `(` `)`
|
2018-07-23 23:42:19 +08:00
|
|
|
/// | `(` type-list-no-parens `)`
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
2018-10-31 05:59:22 +08:00
|
|
|
ParseResult Parser::parseTypeList(SmallVectorImpl<Type> &elements) {
|
2018-06-23 13:03:48 +08:00
|
|
|
auto parseElt = [&]() -> ParseResult {
|
|
|
|
auto elt = parseType();
|
|
|
|
elements.push_back(elt);
|
|
|
|
return elt ? ParseSuccess : ParseFailure;
|
|
|
|
};
|
|
|
|
|
2018-06-23 06:52:02 +08:00
|
|
|
// If there is no parens, then it must be a singular type.
|
|
|
|
if (!consumeIf(Token::l_paren))
|
2018-06-23 13:03:48 +08:00
|
|
|
return parseElt();
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2018-07-22 05:32:09 +08:00
|
|
|
if (parseCommaSeparatedListUntil(Token::r_paren, parseElt))
|
2018-06-23 06:52:02 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Attribute parsing.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
namespace {
|
|
|
|
class TensorLiteralParser {
|
|
|
|
public:
|
2018-10-31 05:59:22 +08:00
|
|
|
TensorLiteralParser(Parser &p, Type eltTy)
|
2018-12-18 02:05:56 +08:00
|
|
|
: p(p), eltTy(eltTy), currBitPos(0) {}
|
2018-10-19 04:54:44 +08:00
|
|
|
|
|
|
|
ParseResult parse() { return parseList(shape); }
|
|
|
|
|
|
|
|
ArrayRef<char> getValues() const {
|
|
|
|
return {reinterpret_cast<const char *>(storage.data()), storage.size() * 8};
|
|
|
|
}
|
|
|
|
|
|
|
|
ArrayRef<int> getShape() const { return shape; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// Parse either a single element or a list of elements. Return the dimensions
|
|
|
|
/// of the parsed sub-tensor in dims.
|
|
|
|
ParseResult parseElementOrList(llvm::SmallVectorImpl<int> &dims);
|
|
|
|
|
|
|
|
/// Parse a list of either lists or elements, returning the dimensions of the
|
|
|
|
/// parsed sub-tensors in dims. For example:
|
|
|
|
/// parseList([1, 2, 3]) -> Success, [3]
|
|
|
|
/// parseList([[1, 2], [3, 4]]) -> Success, [2, 2]
|
|
|
|
/// parseList([[1, 2], 3]) -> Failure
|
|
|
|
/// parseList([[1, [2, 3]], [4, [5]]]) -> Failure
|
|
|
|
ParseResult parseList(llvm::SmallVectorImpl<int> &dims);
|
|
|
|
|
|
|
|
void addToStorage(uint64_t value) {
|
2018-12-18 02:05:56 +08:00
|
|
|
// Only tensors of integers or floats are supported.
|
2018-12-18 21:25:17 +08:00
|
|
|
// FIXME: use full word to store BF16 as double because APFloat, which we
|
|
|
|
// use to work with floats, does not have support for BF16 yet.
|
|
|
|
size_t bitWidth = eltTy.isBF16() ? 64 : eltTy.getIntOrFloatBitWidth();
|
2018-12-18 02:05:56 +08:00
|
|
|
|
|
|
|
if (bitWidth == 64)
|
2018-10-19 04:54:44 +08:00
|
|
|
storage.push_back(value);
|
|
|
|
|
2018-12-18 02:05:56 +08:00
|
|
|
if (currBitPos + bitWidth > storage.size() * 64)
|
2018-10-19 04:54:44 +08:00
|
|
|
storage.push_back(0L);
|
|
|
|
|
|
|
|
auto *rawData = reinterpret_cast<char *>(storage.data());
|
2018-12-18 21:25:17 +08:00
|
|
|
DenseElementsAttr::writeBits(rawData, currBitPos, bitWidth, value);
|
2018-12-18 02:05:56 +08:00
|
|
|
currBitPos += bitWidth;
|
2018-10-19 04:54:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Parser &p;
|
2018-10-31 05:59:22 +08:00
|
|
|
Type eltTy;
|
2018-10-19 04:54:44 +08:00
|
|
|
size_t currBitPos;
|
|
|
|
SmallVector<int, 4> shape;
|
|
|
|
std::vector<uint64_t> storage;
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
/// Parse either a single element or a list of elements. Return the dimensions
|
|
|
|
/// of the parsed sub-tensor in dims.
|
|
|
|
ParseResult
|
|
|
|
TensorLiteralParser::parseElementOrList(llvm::SmallVectorImpl<int> &dims) {
|
|
|
|
switch (p.getToken().getKind()) {
|
|
|
|
case Token::l_square:
|
|
|
|
return parseList(dims);
|
|
|
|
case Token::floatliteral:
|
|
|
|
case Token::integer:
|
|
|
|
case Token::minus: {
|
2018-12-17 23:19:53 +08:00
|
|
|
auto result = p.parseAttribute(eltTy);
|
2018-10-19 04:54:44 +08:00
|
|
|
if (!result)
|
2018-12-17 23:19:53 +08:00
|
|
|
return ParseResult::ParseFailure;
|
2018-10-19 04:54:44 +08:00
|
|
|
// check result matches the element type.
|
2018-10-31 05:59:22 +08:00
|
|
|
switch (eltTy.getKind()) {
|
2018-10-19 04:54:44 +08:00
|
|
|
case Type::Kind::BF16:
|
|
|
|
case Type::Kind::F16:
|
2018-12-18 21:25:17 +08:00
|
|
|
case Type::Kind::F32:
|
2018-10-19 04:54:44 +08:00
|
|
|
case Type::Kind::F64: {
|
2018-12-18 21:25:17 +08:00
|
|
|
// Bitcast the APFloat value to APInt and store the bit representation.
|
|
|
|
auto fpAttrResult = result.dyn_cast<FloatAttr>();
|
|
|
|
if (!fpAttrResult)
|
2018-12-17 23:19:53 +08:00
|
|
|
return p.emitError(
|
2018-12-18 21:25:17 +08:00
|
|
|
"expected tensor literal element with floating point type");
|
|
|
|
auto apInt = fpAttrResult.getValue().bitcastToAPInt();
|
|
|
|
|
|
|
|
// FIXME: using 64 bits and double semantics for BF16 because APFloat does
|
|
|
|
// not support BF16 directly.
|
|
|
|
size_t bitWidth = eltTy.isBF16() ? 64 : eltTy.getIntOrFloatBitWidth();
|
|
|
|
assert(apInt.getBitWidth() == bitWidth);
|
|
|
|
(void)bitWidth;
|
|
|
|
|
|
|
|
addToStorage(apInt.getRawData()[0]);
|
2018-10-19 04:54:44 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Type::Kind::Integer: {
|
2018-10-26 06:46:10 +08:00
|
|
|
if (!result.isa<IntegerAttr>())
|
2018-10-19 04:54:44 +08:00
|
|
|
return p.emitError("expected tensor literal element has integer type");
|
2018-10-26 06:46:10 +08:00
|
|
|
auto value = result.cast<IntegerAttr>().getValue();
|
2018-12-18 02:05:56 +08:00
|
|
|
auto bitWidth = eltTy.getIntOrFloatBitWidth();
|
|
|
|
if (value.getMinSignedBits() > bitWidth)
|
2018-10-19 04:54:44 +08:00
|
|
|
return p.emitError("tensor literal element has more bits than that "
|
|
|
|
"specified in the type");
|
2018-11-12 22:33:22 +08:00
|
|
|
addToStorage(value.getSExtValue());
|
2018-10-19 04:54:44 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return p.emitError("expected integer or float tensor element");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return p.emitError("expected '[' or scalar constant inside tensor literal");
|
|
|
|
}
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a list of either lists or elements, returning the dimensions of the
|
|
|
|
/// parsed sub-tensors in dims. For example:
|
|
|
|
/// parseList([1, 2, 3]) -> Success, [3]
|
|
|
|
/// parseList([[1, 2], [3, 4]]) -> Success, [2, 2]
|
|
|
|
/// parseList([[1, 2], 3]) -> Failure
|
|
|
|
/// parseList([[1, [2, 3]], [4, [5]]]) -> Failure
|
|
|
|
ParseResult TensorLiteralParser::parseList(llvm::SmallVectorImpl<int> &dims) {
|
|
|
|
p.consumeToken(Token::l_square);
|
|
|
|
|
|
|
|
auto checkDims = [&](const llvm::SmallVectorImpl<int> &prevDims,
|
|
|
|
const llvm::SmallVectorImpl<int> &newDims) {
|
|
|
|
if (prevDims == newDims)
|
|
|
|
return ParseSuccess;
|
|
|
|
return p.emitError("tensor literal is invalid; ranks are not consistent "
|
|
|
|
"between elements");
|
|
|
|
};
|
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
llvm::SmallVector<int, 4> newDims;
|
|
|
|
unsigned size = 0;
|
|
|
|
auto parseCommaSeparatedList = [&]() {
|
|
|
|
llvm::SmallVector<int, 4> thisDims;
|
|
|
|
if (parseElementOrList(thisDims))
|
|
|
|
return ParseFailure;
|
|
|
|
++size;
|
|
|
|
if (!first)
|
|
|
|
return checkDims(newDims, thisDims);
|
|
|
|
newDims = thisDims;
|
|
|
|
first = false;
|
|
|
|
return ParseSuccess;
|
|
|
|
};
|
|
|
|
if (p.parseCommaSeparatedListUntil(Token::r_square, parseCommaSeparatedList))
|
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
// Return the sublists' dimensions with 'size' prepended.
|
|
|
|
dims.clear();
|
|
|
|
dims.push_back(size);
|
|
|
|
dims.insert(dims.end(), newDims.begin(), newDims.end());
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-08-22 08:55:22 +08:00
|
|
|
/// Given a parsed reference to a function name like @foo and a type that it
|
|
|
|
/// corresponds to, resolve it to a concrete function object (possibly
|
|
|
|
/// synthesizing a forward reference) or emit an error and return null on
|
|
|
|
/// failure.
|
|
|
|
Function *Parser::resolveFunctionReference(StringRef nameStr, SMLoc nameLoc,
|
2018-10-31 05:59:22 +08:00
|
|
|
FunctionType type) {
|
2018-08-22 08:55:22 +08:00
|
|
|
Identifier name = builder.getIdentifier(nameStr.drop_front());
|
|
|
|
|
|
|
|
// See if the function has already been defined in the module.
|
|
|
|
Function *function = getModule()->getNamedFunction(name);
|
|
|
|
|
|
|
|
// If not, get or create a forward reference to one.
|
|
|
|
if (!function) {
|
|
|
|
auto &entry = state.functionForwardRefs[name];
|
2018-09-08 00:08:13 +08:00
|
|
|
if (!entry)
|
2018-12-28 03:07:34 +08:00
|
|
|
entry = new Function(Function::Kind::ExtFunc,
|
|
|
|
getEncodedSourceLocation(nameLoc), name, type,
|
|
|
|
/*attrs=*/{});
|
2018-09-08 00:08:13 +08:00
|
|
|
function = entry;
|
2018-08-22 08:55:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (function->getType() != type)
|
|
|
|
return (emitError(nameLoc, "reference to function with mismatched type"),
|
|
|
|
nullptr);
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
/// Attribute parsing.
|
|
|
|
///
|
|
|
|
/// attribute-value ::= bool-literal
|
2018-11-16 09:53:51 +08:00
|
|
|
/// | integer-literal (`:` integer-type)
|
|
|
|
/// | float-literal (`:` float-type)
|
2018-07-05 11:45:39 +08:00
|
|
|
/// | string-literal
|
2018-08-03 16:54:46 +08:00
|
|
|
/// | type
|
2018-07-05 11:45:39 +08:00
|
|
|
/// | `[` (attribute-value (`,` attribute-value)*)? `]`
|
2018-08-20 12:17:22 +08:00
|
|
|
/// | function-id `:` function-type
|
2018-10-19 04:54:44 +08:00
|
|
|
/// | (`splat<` | `dense<`) (tensor-type | vector-type)`,`
|
2018-10-10 23:57:51 +08:00
|
|
|
/// attribute-value `>`
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
/// | `sparse<` (tensor-type | vector-type)`,`
|
|
|
|
/// attribute-value`, ` attribute-value `>`
|
2018-07-05 11:45:39 +08:00
|
|
|
///
|
2018-11-16 09:53:51 +08:00
|
|
|
Attribute Parser::parseAttribute(Type type) {
|
2018-07-10 10:05:38 +08:00
|
|
|
switch (getToken().getKind()) {
|
2018-07-05 11:45:39 +08:00
|
|
|
case Token::kw_true:
|
|
|
|
consumeToken(Token::kw_true);
|
2018-07-11 01:59:53 +08:00
|
|
|
return builder.getBoolAttr(true);
|
2018-07-05 11:45:39 +08:00
|
|
|
case Token::kw_false:
|
|
|
|
consumeToken(Token::kw_false);
|
2018-07-11 01:59:53 +08:00
|
|
|
return builder.getBoolAttr(false);
|
2018-07-05 11:45:39 +08:00
|
|
|
|
2018-08-01 08:15:15 +08:00
|
|
|
case Token::floatliteral: {
|
|
|
|
auto val = getToken().getFloatingPointValue();
|
|
|
|
if (!val.hasValue())
|
|
|
|
return (emitError("floating point value too large for attribute"),
|
|
|
|
nullptr);
|
|
|
|
consumeToken(Token::floatliteral);
|
2018-11-16 09:53:51 +08:00
|
|
|
if (!type) {
|
|
|
|
if (consumeIf(Token::colon)) {
|
|
|
|
if (!(type = parseType()))
|
|
|
|
return nullptr;
|
|
|
|
} else {
|
2018-12-17 23:19:53 +08:00
|
|
|
// Default to F64 when no type is specified.
|
|
|
|
type = builder.getF64Type();
|
2018-11-16 09:53:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!type.isa<FloatType>())
|
|
|
|
return (emitError("floating point value not valid for specified type"),
|
|
|
|
nullptr);
|
2018-12-17 23:19:53 +08:00
|
|
|
return builder.getFloatAttr(type, val.getValue());
|
2018-08-01 08:15:15 +08:00
|
|
|
}
|
2018-07-05 11:45:39 +08:00
|
|
|
case Token::integer: {
|
2018-07-10 10:05:38 +08:00
|
|
|
auto val = getToken().getUInt64IntegerValue();
|
2018-07-05 11:45:39 +08:00
|
|
|
if (!val.hasValue() || (int64_t)val.getValue() < 0)
|
2018-12-17 23:19:53 +08:00
|
|
|
return (emitError("integer constant out of range for attribute"),
|
|
|
|
nullptr);
|
2018-07-05 11:45:39 +08:00
|
|
|
consumeToken(Token::integer);
|
2018-11-16 09:53:51 +08:00
|
|
|
if (!type) {
|
|
|
|
if (consumeIf(Token::colon)) {
|
|
|
|
if (!(type = parseType()))
|
|
|
|
return nullptr;
|
|
|
|
} else {
|
|
|
|
// Default to i64 if not type is specified.
|
|
|
|
type = builder.getIntegerType(64);
|
|
|
|
}
|
|
|
|
}
|
2018-12-05 20:31:59 +08:00
|
|
|
if (!type.isIntOrIndex())
|
2018-11-16 09:53:51 +08:00
|
|
|
return (emitError("integer value not valid for specified type"), nullptr);
|
2018-12-18 02:05:56 +08:00
|
|
|
int width = type.isIndex() ? 64 : type.getIntOrFloatBitWidth();
|
2018-12-17 23:19:53 +08:00
|
|
|
APInt apInt(width, val.getValue());
|
|
|
|
if (apInt != *val)
|
|
|
|
return emitError("integer constant out of range for attribute"), nullptr;
|
|
|
|
return builder.getIntegerAttr(type, apInt);
|
2018-07-05 11:45:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case Token::minus: {
|
|
|
|
consumeToken(Token::minus);
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().is(Token::integer)) {
|
|
|
|
auto val = getToken().getUInt64IntegerValue();
|
2018-07-05 11:45:39 +08:00
|
|
|
if (!val.hasValue() || (int64_t)-val.getValue() >= 0)
|
2018-12-17 23:19:53 +08:00
|
|
|
return (emitError("integer constant out of range for attribute"),
|
|
|
|
nullptr);
|
2018-07-05 11:45:39 +08:00
|
|
|
consumeToken(Token::integer);
|
2018-11-16 09:53:51 +08:00
|
|
|
if (!type) {
|
|
|
|
if (consumeIf(Token::colon)) {
|
|
|
|
if (!(type = parseType()))
|
|
|
|
return nullptr;
|
|
|
|
} else {
|
|
|
|
// Default to i64 if not type is specified.
|
|
|
|
type = builder.getIntegerType(64);
|
|
|
|
}
|
|
|
|
}
|
2018-12-05 20:31:59 +08:00
|
|
|
if (!type.isIntOrIndex())
|
2018-11-16 09:53:51 +08:00
|
|
|
return (emitError("integer value not valid for type"), nullptr);
|
2018-12-18 02:05:56 +08:00
|
|
|
int width = type.isIndex() ? 64 : type.getIntOrFloatBitWidth();
|
2018-12-17 23:19:53 +08:00
|
|
|
APInt apInt(width, *val, /*isSigned=*/true);
|
|
|
|
if (apInt != *val)
|
|
|
|
return (emitError("integer constant out of range for attribute"),
|
|
|
|
nullptr);
|
|
|
|
return builder.getIntegerAttr(type, -apInt);
|
2018-07-05 11:45:39 +08:00
|
|
|
}
|
2018-08-01 08:15:15 +08:00
|
|
|
if (getToken().is(Token::floatliteral)) {
|
|
|
|
auto val = getToken().getFloatingPointValue();
|
|
|
|
if (!val.hasValue())
|
|
|
|
return (emitError("floating point value too large for attribute"),
|
|
|
|
nullptr);
|
|
|
|
consumeToken(Token::floatliteral);
|
2018-11-16 09:53:51 +08:00
|
|
|
if (!type) {
|
|
|
|
if (consumeIf(Token::colon)) {
|
|
|
|
if (!(type = parseType()))
|
|
|
|
return nullptr;
|
|
|
|
} else {
|
|
|
|
// Default to F32 when no type is specified.
|
|
|
|
type = builder.getF32Type();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!type.isa<FloatType>())
|
|
|
|
return (emitError("floating point value not valid for type"), nullptr);
|
2018-12-17 23:19:53 +08:00
|
|
|
return builder.getFloatAttr(type, -val.getValue());
|
2018-08-01 08:15:15 +08:00
|
|
|
}
|
2018-07-05 11:45:39 +08:00
|
|
|
|
|
|
|
return (emitError("expected constant integer or floating point value"),
|
|
|
|
nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
case Token::string: {
|
2018-07-10 10:05:38 +08:00
|
|
|
auto val = getToken().getStringValue();
|
2018-07-05 11:45:39 +08:00
|
|
|
consumeToken(Token::string);
|
2018-07-11 01:59:53 +08:00
|
|
|
return builder.getStringAttr(val);
|
2018-07-05 11:45:39 +08:00
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
case Token::l_square: {
|
|
|
|
consumeToken(Token::l_square);
|
2018-10-26 06:46:10 +08:00
|
|
|
SmallVector<Attribute, 4> elements;
|
2018-07-05 11:45:39 +08:00
|
|
|
|
|
|
|
auto parseElt = [&]() -> ParseResult {
|
|
|
|
elements.push_back(parseAttribute());
|
|
|
|
return elements.back() ? ParseSuccess : ParseFailure;
|
|
|
|
};
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
if (parseCommaSeparatedListUntil(Token::r_square, parseElt))
|
2018-07-05 11:45:39 +08:00
|
|
|
return nullptr;
|
2018-07-11 01:59:53 +08:00
|
|
|
return builder.getArrayAttr(elements);
|
2018-07-05 11:45:39 +08:00
|
|
|
}
|
2018-08-03 16:54:46 +08:00
|
|
|
case Token::hash_identifier:
|
|
|
|
case Token::l_paren: {
|
2018-10-26 13:13:03 +08:00
|
|
|
// Try to parse an affine map or an integer set reference.
|
|
|
|
AffineMap map;
|
|
|
|
IntegerSet set;
|
|
|
|
parseAffineStructureReference(&map, &set);
|
|
|
|
if (map)
|
|
|
|
return builder.getAffineMapAttr(map);
|
|
|
|
if (set)
|
|
|
|
return builder.getIntegerSetAttr(set);
|
|
|
|
return (emitError("expected affine map or integer set attribute value"),
|
|
|
|
nullptr);
|
2018-07-05 11:45:39 +08:00
|
|
|
}
|
2018-08-20 12:17:22 +08:00
|
|
|
|
|
|
|
case Token::at_identifier: {
|
|
|
|
auto nameLoc = getToken().getLoc();
|
2018-08-22 08:55:22 +08:00
|
|
|
auto nameStr = getTokenSpelling();
|
2018-08-20 12:17:22 +08:00
|
|
|
consumeToken(Token::at_identifier);
|
|
|
|
|
|
|
|
if (parseToken(Token::colon, "expected ':' and function type"))
|
|
|
|
return nullptr;
|
|
|
|
auto typeLoc = getToken().getLoc();
|
2018-10-31 05:59:22 +08:00
|
|
|
Type type = parseType();
|
2018-08-20 12:17:22 +08:00
|
|
|
if (!type)
|
|
|
|
return nullptr;
|
2018-10-31 05:59:22 +08:00
|
|
|
auto fnType = type.dyn_cast<FunctionType>();
|
2018-08-20 12:17:22 +08:00
|
|
|
if (!fnType)
|
|
|
|
return (emitError(typeLoc, "expected function type"), nullptr);
|
|
|
|
|
2018-08-22 08:55:22 +08:00
|
|
|
auto *function = resolveFunctionReference(nameStr, nameLoc, fnType);
|
|
|
|
return function ? builder.getFunctionAttr(function) : nullptr;
|
2018-08-20 12:17:22 +08:00
|
|
|
}
|
2018-10-24 04:44:04 +08:00
|
|
|
case Token::kw_opaque: {
|
|
|
|
consumeToken(Token::kw_opaque);
|
|
|
|
if (parseToken(Token::less, "expected '<' after 'opaque'"))
|
|
|
|
return nullptr;
|
2018-10-31 05:59:22 +08:00
|
|
|
auto type = parseVectorOrTensorType();
|
2018-10-24 04:44:04 +08:00
|
|
|
if (!type)
|
|
|
|
return nullptr;
|
|
|
|
auto val = getToken().getStringValue();
|
|
|
|
if (val.size() < 2 || val[0] != '0' || val[1] != 'x')
|
|
|
|
return (emitError("opaque string should start with '0x'"), nullptr);
|
|
|
|
val = val.substr(2);
|
|
|
|
if (!std::all_of(val.begin(), val.end(),
|
|
|
|
[](char c) { return llvm::isHexDigit(c); })) {
|
|
|
|
return (emitError("opaque string only contains hex digits"), nullptr);
|
|
|
|
}
|
|
|
|
consumeToken(Token::string);
|
|
|
|
if (parseToken(Token::greater, "expected '>'"))
|
|
|
|
return nullptr;
|
|
|
|
return builder.getOpaqueElementsAttr(type, llvm::fromHex(val));
|
|
|
|
}
|
2018-10-10 23:57:51 +08:00
|
|
|
case Token::kw_splat: {
|
|
|
|
consumeToken(Token::kw_splat);
|
2018-10-19 04:54:44 +08:00
|
|
|
if (parseToken(Token::less, "expected '<' after 'splat'"))
|
2018-10-10 23:57:51 +08:00
|
|
|
return nullptr;
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
auto type = parseVectorOrTensorType();
|
2018-10-19 04:54:44 +08:00
|
|
|
if (!type)
|
2018-10-10 23:57:51 +08:00
|
|
|
return nullptr;
|
|
|
|
switch (getToken().getKind()) {
|
|
|
|
case Token::floatliteral:
|
|
|
|
case Token::integer:
|
|
|
|
case Token::minus: {
|
2018-11-16 09:53:51 +08:00
|
|
|
auto scalar = parseAttribute(type.getElementType());
|
2018-10-10 23:57:51 +08:00
|
|
|
if (parseToken(Token::greater, "expected '>'"))
|
|
|
|
return nullptr;
|
|
|
|
return builder.getSplatElementsAttr(type, scalar);
|
|
|
|
}
|
|
|
|
default:
|
2018-10-19 04:54:44 +08:00
|
|
|
return (emitError("expected scalar constant inside tensor literal"),
|
|
|
|
nullptr);
|
2018-10-10 23:57:51 +08:00
|
|
|
}
|
|
|
|
}
|
2018-10-19 04:54:44 +08:00
|
|
|
case Token::kw_dense: {
|
|
|
|
consumeToken(Token::kw_dense);
|
|
|
|
if (parseToken(Token::less, "expected '<' after 'dense'"))
|
|
|
|
return nullptr;
|
2018-10-10 23:57:51 +08:00
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
auto type = parseVectorOrTensorType();
|
2018-10-19 04:54:44 +08:00
|
|
|
if (!type)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
switch (getToken().getKind()) {
|
|
|
|
case Token::l_square: {
|
|
|
|
auto attr = parseDenseElementsAttr(type);
|
|
|
|
if (!attr)
|
|
|
|
return nullptr;
|
|
|
|
if (parseToken(Token::greater, "expected '>'"))
|
|
|
|
return nullptr;
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return (emitError("expected '[' to start dense tensor literal"), nullptr);
|
|
|
|
}
|
|
|
|
}
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
case Token::kw_sparse: {
|
|
|
|
consumeToken(Token::kw_sparse);
|
|
|
|
if (parseToken(Token::less, "Expected '<' after 'sparse'"))
|
|
|
|
return nullptr;
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
auto type = parseVectorOrTensorType();
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
if (!type)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
switch (getToken().getKind()) {
|
|
|
|
case Token::l_square: {
|
|
|
|
/// Parse indices
|
2018-10-31 05:59:22 +08:00
|
|
|
auto indicesEltType = builder.getIntegerType(32);
|
2018-10-26 06:46:10 +08:00
|
|
|
auto indices =
|
2018-10-31 05:59:22 +08:00
|
|
|
parseDenseElementsAttr(indicesEltType, type.isa<VectorType>());
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
|
|
|
|
if (parseToken(Token::comma, "expected ','"))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
/// Parse values.
|
2018-10-31 05:59:22 +08:00
|
|
|
auto valuesEltType = type.getElementType();
|
2018-10-26 06:46:10 +08:00
|
|
|
auto values =
|
2018-10-31 05:59:22 +08:00
|
|
|
parseDenseElementsAttr(valuesEltType, type.isa<VectorType>());
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
|
|
|
|
/// Sanity check.
|
2018-10-31 05:59:22 +08:00
|
|
|
auto indicesType = indices.getType();
|
|
|
|
auto valuesType = values.getType();
|
|
|
|
auto sameShape = (indicesType.getRank() == 1) ||
|
|
|
|
(type.getRank() == indicesType.getDimSize(1));
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
auto sameElementNum =
|
2018-10-31 05:59:22 +08:00
|
|
|
indicesType.getDimSize(0) == valuesType.getDimSize(0);
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
if (!sameShape || !sameElementNum) {
|
|
|
|
std::string str;
|
|
|
|
llvm::raw_string_ostream s(str);
|
|
|
|
s << "expected shape ([";
|
2018-10-31 05:59:22 +08:00
|
|
|
interleaveComma(type.getShape(), s);
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
s << "]); inferred shape of indices literal ([";
|
2018-10-31 05:59:22 +08:00
|
|
|
interleaveComma(indicesType.getShape(), s);
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
s << "]); inferred shape of values literal ([";
|
2018-10-31 05:59:22 +08:00
|
|
|
interleaveComma(valuesType.getShape(), s);
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
s << "])";
|
|
|
|
return (emitError(s.str()), nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parseToken(Token::greater, "expected '>'"))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
// Build the sparse elements attribute by the indices and values.
|
|
|
|
return builder.getSparseElementsAttr(
|
2018-10-26 06:46:10 +08:00
|
|
|
type, indices.cast<DenseIntElementsAttr>(), values);
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return (emitError("expected '[' to start sparse tensor literal"),
|
|
|
|
nullptr);
|
|
|
|
}
|
|
|
|
return (emitError("expected elements literal has a tensor or vector type"),
|
|
|
|
nullptr);
|
|
|
|
}
|
2018-08-03 16:54:46 +08:00
|
|
|
default: {
|
2018-10-31 05:59:22 +08:00
|
|
|
if (Type type = parseType())
|
2018-08-03 16:54:46 +08:00
|
|
|
return builder.getTypeAttr(type);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
2018-07-05 11:45:39 +08:00
|
|
|
}
|
|
|
|
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
/// Dense elements attribute.
|
|
|
|
///
|
|
|
|
/// dense-attr-list ::= `[` attribute-value `]`
|
|
|
|
/// attribute-value ::= integer-literal
|
|
|
|
/// | float-literal
|
|
|
|
/// | `[` (attribute-value (`,` attribute-value)*)? `]`
|
|
|
|
///
|
|
|
|
/// This method returns a constructed dense elements attribute with the shape
|
|
|
|
/// from the parsing result.
|
2018-10-31 05:59:22 +08:00
|
|
|
DenseElementsAttr Parser::parseDenseElementsAttr(Type eltType, bool isVector) {
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
TensorLiteralParser literalParser(*this, eltType);
|
|
|
|
if (literalParser.parse())
|
|
|
|
return nullptr;
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
VectorOrTensorType type;
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
if (isVector) {
|
|
|
|
type = builder.getVectorType(literalParser.getShape(), eltType);
|
|
|
|
} else {
|
|
|
|
type = builder.getTensorType(literalParser.getShape(), eltType);
|
|
|
|
}
|
2018-10-26 06:46:10 +08:00
|
|
|
return builder.getDenseElementsAttr(type, literalParser.getValues())
|
|
|
|
.cast<DenseElementsAttr>();
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Dense elements attribute.
|
|
|
|
///
|
|
|
|
/// dense-attr-list ::= `[` attribute-value `]`
|
|
|
|
/// attribute-value ::= integer-literal
|
|
|
|
/// | float-literal
|
|
|
|
/// | `[` (attribute-value (`,` attribute-value)*)? `]`
|
|
|
|
///
|
|
|
|
/// This method compares the shapes from the parsing result and that from the
|
|
|
|
/// input argument. It returns a constructed dense elements attribute if both
|
|
|
|
/// match.
|
2018-10-31 05:59:22 +08:00
|
|
|
DenseElementsAttr Parser::parseDenseElementsAttr(VectorOrTensorType type) {
|
|
|
|
auto eltTy = type.getElementType();
|
2018-10-19 04:54:44 +08:00
|
|
|
TensorLiteralParser literalParser(*this, eltTy);
|
|
|
|
if (literalParser.parse())
|
|
|
|
return nullptr;
|
2018-10-31 05:59:22 +08:00
|
|
|
if (literalParser.getShape() != type.getShape()) {
|
2018-10-19 04:54:44 +08:00
|
|
|
std::string str;
|
|
|
|
llvm::raw_string_ostream s(str);
|
|
|
|
s << "inferred shape of elements literal ([";
|
|
|
|
interleaveComma(literalParser.getShape(), s);
|
|
|
|
s << "]) does not match type ([";
|
2018-10-31 05:59:22 +08:00
|
|
|
interleaveComma(type.getShape(), s);
|
2018-10-19 04:54:44 +08:00
|
|
|
s << "])";
|
|
|
|
return (emitError(s.str()), nullptr);
|
|
|
|
}
|
2018-10-26 06:46:10 +08:00
|
|
|
return builder.getDenseElementsAttr(type, literalParser.getValues())
|
|
|
|
.cast<DenseElementsAttr>();
|
2018-10-19 04:54:44 +08:00
|
|
|
}
|
|
|
|
|
Add support to constant sparse tensor / vector attribute
The SparseElementsAttr uses (COO) Coordinate List encoding to represents a
sparse tensor / vector. Specifically, the coordinates and values are stored as
two dense elements attributes. The first dense elements attribute is a 2-D
attribute with shape [N, ndims], which contains the indices of the elements
with nonzero values in the constant vector/tensor. The second elements
attribute is a 1-D attribute list with shape [N], which supplies the values for
each element in the first elements attribute. ndims is the rank of the
vector/tensor and N is the total nonzero elements.
The syntax is:
`sparse<` (tensor-type | vector-type)`, ` indices-attribute-list, values-attribute-list `>`
Example: a sparse tensor
sparse<vector<3x4xi32>, [[0, 0], [1, 2]], [1, 2]> represents the dense tensor
[[1, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 0]]
PiperOrigin-RevId: 217764319
2018-10-19 05:02:20 +08:00
|
|
|
/// Vector or tensor type for elements attribute.
|
|
|
|
///
|
|
|
|
/// vector-or-tensor-type ::= vector-type | tensor-type
|
|
|
|
///
|
|
|
|
/// This method also checks the type has static shape and ranked.
|
2018-10-31 05:59:22 +08:00
|
|
|
VectorOrTensorType Parser::parseVectorOrTensorType() {
|
2018-11-10 13:24:37 +08:00
|
|
|
auto elementType = parseType();
|
|
|
|
if (!elementType)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto type = elementType.dyn_cast<VectorOrTensorType>();
|
2018-10-19 04:54:44 +08:00
|
|
|
if (!type) {
|
|
|
|
return (emitError("expected elements literal has a tensor or vector type"),
|
|
|
|
nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parseToken(Token::comma, "expected ','"))
|
|
|
|
return nullptr;
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
if (!type.hasStaticShape() || type.getRank() == -1) {
|
2018-10-19 04:54:44 +08:00
|
|
|
return (emitError("tensor literals must be ranked and have static shape"),
|
|
|
|
nullptr);
|
|
|
|
}
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
/// Attribute dictionary.
|
|
|
|
///
|
2018-07-10 00:00:25 +08:00
|
|
|
/// attribute-dict ::= `{` `}`
|
|
|
|
/// | `{` attribute-entry (`,` attribute-entry)* `}`
|
|
|
|
/// attribute-entry ::= bare-id `:` attribute-value
|
2018-07-05 11:45:39 +08:00
|
|
|
///
|
2018-07-24 07:56:32 +08:00
|
|
|
ParseResult
|
|
|
|
Parser::parseAttributeDict(SmallVectorImpl<NamedAttribute> &attributes) {
|
2018-10-14 22:55:29 +08:00
|
|
|
if (!consumeIf(Token::l_brace))
|
|
|
|
return ParseFailure;
|
2018-07-05 11:45:39 +08:00
|
|
|
|
|
|
|
auto parseElt = [&]() -> ParseResult {
|
|
|
|
// We allow keywords as attribute names.
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().isNot(Token::bare_identifier, Token::inttype) &&
|
|
|
|
!getToken().isKeyword())
|
2018-07-05 11:45:39 +08:00
|
|
|
return emitError("expected attribute name");
|
2018-07-11 01:59:53 +08:00
|
|
|
auto nameId = builder.getIdentifier(getTokenSpelling());
|
2018-07-05 11:45:39 +08:00
|
|
|
consumeToken();
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::colon, "expected ':' in attribute list"))
|
|
|
|
return ParseFailure;
|
2018-07-05 11:45:39 +08:00
|
|
|
|
|
|
|
auto attr = parseAttribute();
|
2018-07-24 07:56:32 +08:00
|
|
|
if (!attr)
|
|
|
|
return ParseFailure;
|
2018-07-05 11:45:39 +08:00
|
|
|
|
|
|
|
attributes.push_back({nameId, attr});
|
|
|
|
return ParseSuccess;
|
|
|
|
};
|
|
|
|
|
2018-07-22 05:32:09 +08:00
|
|
|
if (parseCommaSeparatedListUntil(Token::r_brace, parseElt))
|
2018-07-05 11:45:39 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-06-28 02:03:08 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Polyhedral structures.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
/// Lower precedence ops (all at the same precedence level). LNoOp is false in
|
|
|
|
/// the boolean sense.
|
|
|
|
enum AffineLowPrecOp {
|
|
|
|
/// Null value.
|
|
|
|
LNoOp,
|
|
|
|
Add,
|
|
|
|
Sub
|
|
|
|
};
|
2018-06-28 02:03:08 +08:00
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
/// Higher precedence ops - all at the same precedence level. HNoOp is false
|
|
|
|
/// in the boolean sense.
|
2018-07-11 01:08:27 +08:00
|
|
|
enum AffineHighPrecOp {
|
|
|
|
/// Null value.
|
|
|
|
HNoOp,
|
|
|
|
Mul,
|
|
|
|
FloorDiv,
|
|
|
|
CeilDiv,
|
|
|
|
Mod
|
|
|
|
};
|
2018-06-30 09:09:29 +08:00
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
namespace {
|
2018-08-08 05:24:38 +08:00
|
|
|
/// This is a specialized parser for affine structures (affine maps, affine
|
|
|
|
/// expressions, and integer sets), maintaining the state transient to their
|
|
|
|
/// bodies.
|
|
|
|
class AffineParser : public Parser {
|
2018-07-11 01:08:27 +08:00
|
|
|
public:
|
2018-08-08 05:24:38 +08:00
|
|
|
explicit AffineParser(ParserState &state) : Parser(state) {}
|
2018-07-05 11:45:39 +08:00
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
void parseAffineStructureInline(AffineMap *map, IntegerSet *set);
|
|
|
|
AffineMap parseAffineMapRange(unsigned numDims, unsigned numSymbols);
|
|
|
|
IntegerSet parseIntegerSetConstraints(unsigned numDims, unsigned numSymbols);
|
2018-07-05 11:45:39 +08:00
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
private:
|
|
|
|
// Binary affine op parsing.
|
|
|
|
AffineLowPrecOp consumeIfLowPrecOp();
|
|
|
|
AffineHighPrecOp consumeIfHighPrecOp();
|
2018-06-28 02:03:08 +08:00
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
// Identifier lists for polyhedral structures.
|
2018-07-26 03:55:50 +08:00
|
|
|
ParseResult parseDimIdList(unsigned &numDims);
|
|
|
|
ParseResult parseSymbolIdList(unsigned &numSymbols);
|
2018-10-09 04:47:18 +08:00
|
|
|
ParseResult parseIdentifierDefinition(AffineExpr idExpr);
|
|
|
|
|
|
|
|
AffineExpr parseAffineExpr();
|
|
|
|
AffineExpr parseParentheticalExpr();
|
|
|
|
AffineExpr parseNegateExpression(AffineExpr lhs);
|
|
|
|
AffineExpr parseIntegerExpr();
|
|
|
|
AffineExpr parseBareIdExpr();
|
|
|
|
|
|
|
|
AffineExpr getBinaryAffineOpExpr(AffineHighPrecOp op, AffineExpr lhs,
|
|
|
|
AffineExpr rhs, SMLoc opLoc);
|
|
|
|
AffineExpr getBinaryAffineOpExpr(AffineLowPrecOp op, AffineExpr lhs,
|
|
|
|
AffineExpr rhs);
|
|
|
|
AffineExpr parseAffineOperandExpr(AffineExpr lhs);
|
|
|
|
AffineExpr parseAffineLowPrecOpExpr(AffineExpr llhs, AffineLowPrecOp llhsOp);
|
|
|
|
AffineExpr parseAffineHighPrecOpExpr(AffineExpr llhs, AffineHighPrecOp llhsOp,
|
|
|
|
SMLoc llhsOpLoc);
|
|
|
|
AffineExpr parseAffineConstraint(bool *isEq);
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
private:
|
2018-10-09 04:47:18 +08:00
|
|
|
SmallVector<std::pair<StringRef, AffineExpr>, 4> dimsAndSymbols;
|
2018-07-11 01:08:27 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
2018-07-04 11:16:08 +08:00
|
|
|
|
2018-07-21 05:57:21 +08:00
|
|
|
/// Create an affine binary high precedence op expression (mul's, div's, mod).
|
|
|
|
/// opLoc is the location of the op token to be used to report errors
|
|
|
|
/// for non-conforming expressions.
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::getBinaryAffineOpExpr(AffineHighPrecOp op,
|
|
|
|
AffineExpr lhs, AffineExpr rhs,
|
|
|
|
SMLoc opLoc) {
|
2018-07-12 12:31:07 +08:00
|
|
|
// TODO: make the error location info accurate.
|
2018-07-04 11:16:08 +08:00
|
|
|
switch (op) {
|
|
|
|
case Mul:
|
2018-10-10 01:59:27 +08:00
|
|
|
if (!lhs.isSymbolicOrConstant() && !rhs.isSymbolicOrConstant()) {
|
2018-07-21 05:57:21 +08:00
|
|
|
emitError(opLoc, "non-affine expression: at least one of the multiply "
|
|
|
|
"operands has to be either a constant or symbolic");
|
2018-07-10 00:00:25 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
2018-10-10 01:59:27 +08:00
|
|
|
return lhs * rhs;
|
2018-07-04 11:16:08 +08:00
|
|
|
case FloorDiv:
|
2018-10-10 01:59:27 +08:00
|
|
|
if (!rhs.isSymbolicOrConstant()) {
|
2018-07-21 05:57:21 +08:00
|
|
|
emitError(opLoc, "non-affine expression: right operand of floordiv "
|
|
|
|
"has to be either a constant or symbolic");
|
2018-07-10 00:00:25 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
2018-10-10 01:59:27 +08:00
|
|
|
return lhs.floorDiv(rhs);
|
2018-07-04 11:16:08 +08:00
|
|
|
case CeilDiv:
|
2018-10-10 01:59:27 +08:00
|
|
|
if (!rhs.isSymbolicOrConstant()) {
|
2018-07-21 05:57:21 +08:00
|
|
|
emitError(opLoc, "non-affine expression: right operand of ceildiv "
|
|
|
|
"has to be either a constant or symbolic");
|
2018-07-10 00:00:25 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
2018-10-10 01:59:27 +08:00
|
|
|
return lhs.ceilDiv(rhs);
|
2018-07-04 11:16:08 +08:00
|
|
|
case Mod:
|
2018-10-10 01:59:27 +08:00
|
|
|
if (!rhs.isSymbolicOrConstant()) {
|
2018-07-21 05:57:21 +08:00
|
|
|
emitError(opLoc, "non-affine expression: right operand of mod "
|
|
|
|
"has to be either a constant or symbolic");
|
2018-07-10 00:00:25 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
2018-10-10 01:59:27 +08:00
|
|
|
return lhs % rhs;
|
2018-07-04 11:16:08 +08:00
|
|
|
case HNoOp:
|
|
|
|
llvm_unreachable("can't create affine expression for null high prec op");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-10 00:00:25 +08:00
|
|
|
/// Create an affine binary low precedence op expression (add, sub).
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::getBinaryAffineOpExpr(AffineLowPrecOp op,
|
|
|
|
AffineExpr lhs, AffineExpr rhs) {
|
2018-07-04 11:16:08 +08:00
|
|
|
switch (op) {
|
|
|
|
case AffineLowPrecOp::Add:
|
2018-10-10 01:59:27 +08:00
|
|
|
return lhs + rhs;
|
2018-07-04 11:16:08 +08:00
|
|
|
case AffineLowPrecOp::Sub:
|
2018-10-10 01:59:27 +08:00
|
|
|
return lhs - rhs;
|
2018-07-04 11:16:08 +08:00
|
|
|
case AffineLowPrecOp::LNoOp:
|
|
|
|
llvm_unreachable("can't create affine expression for null low prec op");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
/// Consume this token if it is a lower precedence affine op (there are only
|
|
|
|
/// two precedence levels).
|
2018-08-08 05:24:38 +08:00
|
|
|
AffineLowPrecOp AffineParser::consumeIfLowPrecOp() {
|
2018-07-10 10:05:38 +08:00
|
|
|
switch (getToken().getKind()) {
|
2018-07-04 11:16:08 +08:00
|
|
|
case Token::plus:
|
|
|
|
consumeToken(Token::plus);
|
|
|
|
return AffineLowPrecOp::Add;
|
|
|
|
case Token::minus:
|
|
|
|
consumeToken(Token::minus);
|
|
|
|
return AffineLowPrecOp::Sub;
|
|
|
|
default:
|
|
|
|
return AffineLowPrecOp::LNoOp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Consume this token if it is a higher precedence affine op (there are only
|
|
|
|
/// two precedence levels)
|
2018-08-08 05:24:38 +08:00
|
|
|
AffineHighPrecOp AffineParser::consumeIfHighPrecOp() {
|
2018-07-10 10:05:38 +08:00
|
|
|
switch (getToken().getKind()) {
|
2018-07-04 11:16:08 +08:00
|
|
|
case Token::star:
|
|
|
|
consumeToken(Token::star);
|
|
|
|
return Mul;
|
|
|
|
case Token::kw_floordiv:
|
|
|
|
consumeToken(Token::kw_floordiv);
|
|
|
|
return FloorDiv;
|
|
|
|
case Token::kw_ceildiv:
|
|
|
|
consumeToken(Token::kw_ceildiv);
|
|
|
|
return CeilDiv;
|
|
|
|
case Token::kw_mod:
|
|
|
|
consumeToken(Token::kw_mod);
|
|
|
|
return Mod;
|
|
|
|
default:
|
|
|
|
return HNoOp;
|
|
|
|
}
|
2018-06-30 09:09:29 +08:00
|
|
|
}
|
|
|
|
|
2018-07-10 00:00:25 +08:00
|
|
|
/// Parse a high precedence op expression list: mul, div, and mod are high
|
|
|
|
/// precedence binary ops, i.e., parse a
|
|
|
|
/// expr_1 op_1 expr_2 op_2 ... expr_n
|
|
|
|
/// where op_1, op_2 are all a AffineHighPrecOp (mul, div, mod).
|
|
|
|
/// All affine binary ops are left associative.
|
|
|
|
/// Given llhs, returns (llhs llhsOp lhs) op rhs, or (lhs op rhs) if llhs is
|
|
|
|
/// null. If no rhs can be found, returns (llhs llhsOp lhs) or lhs if llhs is
|
2018-07-21 05:57:21 +08:00
|
|
|
/// null. llhsOpLoc is the location of the llhsOp token that will be used to
|
|
|
|
/// report an error for non-conforming expressions.
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::parseAffineHighPrecOpExpr(AffineExpr llhs,
|
|
|
|
AffineHighPrecOp llhsOp,
|
|
|
|
SMLoc llhsOpLoc) {
|
|
|
|
AffineExpr lhs = parseAffineOperandExpr(llhs);
|
2018-07-10 00:00:25 +08:00
|
|
|
if (!lhs)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
// Found an LHS. Parse the remaining expression.
|
2018-07-21 05:57:21 +08:00
|
|
|
auto opLoc = getToken().getLoc();
|
2018-07-11 01:08:27 +08:00
|
|
|
if (AffineHighPrecOp op = consumeIfHighPrecOp()) {
|
2018-07-10 00:00:25 +08:00
|
|
|
if (llhs) {
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr expr = getBinaryAffineOpExpr(llhsOp, llhs, lhs, opLoc);
|
2018-07-10 00:00:25 +08:00
|
|
|
if (!expr)
|
|
|
|
return nullptr;
|
2018-07-21 05:57:21 +08:00
|
|
|
return parseAffineHighPrecOpExpr(expr, op, opLoc);
|
2018-07-10 00:00:25 +08:00
|
|
|
}
|
|
|
|
// No LLHS, get RHS
|
2018-07-21 05:57:21 +08:00
|
|
|
return parseAffineHighPrecOpExpr(lhs, op, opLoc);
|
2018-07-10 00:00:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// This is the last operand in this expression.
|
|
|
|
if (llhs)
|
2018-07-21 05:57:21 +08:00
|
|
|
return getBinaryAffineOpExpr(llhsOp, llhs, lhs, llhsOpLoc);
|
2018-07-10 00:00:25 +08:00
|
|
|
|
|
|
|
// No llhs, 'lhs' itself is the expression.
|
|
|
|
return lhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse an affine expression inside parentheses.
|
|
|
|
///
|
|
|
|
/// affine-expr ::= `(` affine-expr `)`
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::parseParentheticalExpr() {
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::l_paren, "expected '('"))
|
|
|
|
return nullptr;
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().is(Token::r_paren))
|
2018-07-10 00:00:25 +08:00
|
|
|
return (emitError("no expression inside parentheses"), nullptr);
|
2018-07-24 08:30:01 +08:00
|
|
|
|
2018-10-08 23:09:50 +08:00
|
|
|
auto expr = parseAffineExpr();
|
2018-07-10 00:00:25 +08:00
|
|
|
if (!expr)
|
|
|
|
return nullptr;
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::r_paren, "expected ')'"))
|
|
|
|
return nullptr;
|
|
|
|
|
2018-07-10 00:00:25 +08:00
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse the negation expression.
|
|
|
|
///
|
|
|
|
/// affine-expr ::= `-` affine-expr
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::parseNegateExpression(AffineExpr lhs) {
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::minus, "expected '-'"))
|
|
|
|
return nullptr;
|
2018-07-10 00:00:25 +08:00
|
|
|
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr operand = parseAffineOperandExpr(lhs);
|
2018-07-10 00:00:25 +08:00
|
|
|
// Since negation has the highest precedence of all ops (including high
|
|
|
|
// precedence ops) but lower than parentheses, we are only going to use
|
|
|
|
// parseAffineOperandExpr instead of parseAffineExpr here.
|
|
|
|
if (!operand)
|
|
|
|
// Extra error message although parseAffineOperandExpr would have
|
|
|
|
// complained. Leads to a better diagnostic.
|
|
|
|
return (emitError("missing operand of negation"), nullptr);
|
2018-10-10 01:59:27 +08:00
|
|
|
return (-1) * operand;
|
2018-07-10 00:00:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a bare id that may appear in an affine expression.
|
|
|
|
///
|
|
|
|
/// affine-expr ::= bare-id
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::parseBareIdExpr() {
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().isNot(Token::bare_identifier))
|
2018-07-10 00:00:25 +08:00
|
|
|
return (emitError("expected bare identifier"), nullptr);
|
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
StringRef sRef = getTokenSpelling();
|
2018-07-26 03:55:50 +08:00
|
|
|
for (auto entry : dimsAndSymbols) {
|
2018-07-26 05:08:16 +08:00
|
|
|
if (entry.first == sRef) {
|
|
|
|
consumeToken(Token::bare_identifier);
|
|
|
|
return entry.second;
|
|
|
|
}
|
2018-07-10 00:00:25 +08:00
|
|
|
}
|
2018-07-12 12:31:07 +08:00
|
|
|
|
|
|
|
return (emitError("use of undeclared identifier"), nullptr);
|
2018-07-10 00:00:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a positive integral constant appearing in an affine expression.
|
|
|
|
///
|
|
|
|
/// affine-expr ::= integer-literal
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::parseIntegerExpr() {
|
2018-07-10 10:05:38 +08:00
|
|
|
auto val = getToken().getUInt64IntegerValue();
|
2018-08-21 23:42:19 +08:00
|
|
|
if (!val.hasValue() || (int64_t)val.getValue() < 0)
|
2018-10-07 08:21:53 +08:00
|
|
|
return (emitError("constant too large for index"), nullptr);
|
2018-08-21 23:42:19 +08:00
|
|
|
|
2018-07-10 00:00:25 +08:00
|
|
|
consumeToken(Token::integer);
|
2018-10-09 01:20:25 +08:00
|
|
|
return builder.getAffineConstantExpr((int64_t)val.getValue());
|
2018-07-10 00:00:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses an expression that can be a valid operand of an affine expression.
|
2018-07-10 04:47:52 +08:00
|
|
|
/// lhs: if non-null, lhs is an affine expression that is the lhs of a binary
|
|
|
|
/// operator, the rhs of which is being parsed. This is used to determine
|
|
|
|
/// whether an error should be emitted for a missing right operand.
|
2018-07-10 00:00:25 +08:00
|
|
|
// Eg: for an expression without parentheses (like i + j + k + l), each
|
|
|
|
// of the four identifiers is an operand. For i + j*k + l, j*k is not an
|
|
|
|
// operand expression, it's an op expression and will be parsed via
|
2018-10-19 04:54:44 +08:00
|
|
|
// parseAffineHighPrecOpExpression(). However, for i + (j*k) + -l, (j*k) and
|
|
|
|
// -l are valid operands that will be parsed by this function.
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::parseAffineOperandExpr(AffineExpr lhs) {
|
2018-07-10 10:05:38 +08:00
|
|
|
switch (getToken().getKind()) {
|
2018-07-10 00:00:25 +08:00
|
|
|
case Token::bare_identifier:
|
2018-07-11 01:08:27 +08:00
|
|
|
return parseBareIdExpr();
|
2018-07-10 00:00:25 +08:00
|
|
|
case Token::integer:
|
2018-07-11 01:08:27 +08:00
|
|
|
return parseIntegerExpr();
|
2018-07-10 00:00:25 +08:00
|
|
|
case Token::l_paren:
|
2018-07-11 01:08:27 +08:00
|
|
|
return parseParentheticalExpr();
|
2018-07-10 00:00:25 +08:00
|
|
|
case Token::minus:
|
2018-07-11 01:08:27 +08:00
|
|
|
return parseNegateExpression(lhs);
|
2018-07-10 04:47:52 +08:00
|
|
|
case Token::kw_ceildiv:
|
|
|
|
case Token::kw_floordiv:
|
|
|
|
case Token::kw_mod:
|
|
|
|
case Token::plus:
|
|
|
|
case Token::star:
|
|
|
|
if (lhs)
|
|
|
|
emitError("missing right operand of binary operator");
|
|
|
|
else
|
|
|
|
emitError("missing left operand of binary operator");
|
|
|
|
return nullptr;
|
2018-07-10 00:00:25 +08:00
|
|
|
default:
|
|
|
|
if (lhs)
|
2018-07-10 04:47:52 +08:00
|
|
|
emitError("missing right operand of binary operator");
|
2018-07-10 00:00:25 +08:00
|
|
|
else
|
|
|
|
emitError("expected affine expression");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-04 11:16:08 +08:00
|
|
|
/// Parse affine expressions that are bare-id's, integer constants,
|
|
|
|
/// parenthetical affine expressions, and affine op expressions that are a
|
|
|
|
/// composition of those.
|
2018-06-30 09:09:29 +08:00
|
|
|
///
|
2018-07-04 11:16:08 +08:00
|
|
|
/// All binary op's associate from left to right.
|
|
|
|
///
|
|
|
|
/// {add, sub} have lower precedence than {mul, div, and mod}.
|
|
|
|
///
|
2018-07-10 04:47:52 +08:00
|
|
|
/// Add, sub'are themselves at the same precedence level. Mul, floordiv,
|
|
|
|
/// ceildiv, and mod are at the same higher precedence level. Negation has
|
|
|
|
/// higher precedence than any binary op.
|
2018-07-04 11:16:08 +08:00
|
|
|
///
|
|
|
|
/// llhs: the affine expression appearing on the left of the one being parsed.
|
2018-07-10 00:00:25 +08:00
|
|
|
/// This function will return ((llhs llhsOp lhs) op rhs) if llhs is non null,
|
2018-10-19 04:54:44 +08:00
|
|
|
/// and lhs op rhs otherwise; if there is no rhs, llhs llhsOp lhs is returned
|
|
|
|
/// if llhs is non-null; otherwise lhs is returned. This is to deal with left
|
2018-07-04 11:16:08 +08:00
|
|
|
/// associativity.
|
|
|
|
///
|
|
|
|
/// Eg: when the expression is e1 + e2*e3 + e4, with e1 as llhs, this function
|
2018-10-19 04:54:44 +08:00
|
|
|
/// will return the affine expr equivalent of (e1 + (e2*e3)) + e4, where
|
|
|
|
/// (e2*e3) will be parsed using parseAffineHighPrecOpExpr().
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::parseAffineLowPrecOpExpr(AffineExpr llhs,
|
|
|
|
AffineLowPrecOp llhsOp) {
|
|
|
|
AffineExpr lhs;
|
2018-07-11 01:08:27 +08:00
|
|
|
if (!(lhs = parseAffineOperandExpr(llhs)))
|
2018-07-10 00:00:25 +08:00
|
|
|
return nullptr;
|
2018-07-04 11:16:08 +08:00
|
|
|
|
|
|
|
// Found an LHS. Deal with the ops.
|
2018-07-11 01:08:27 +08:00
|
|
|
if (AffineLowPrecOp lOp = consumeIfLowPrecOp()) {
|
2018-07-04 11:16:08 +08:00
|
|
|
if (llhs) {
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr sum = getBinaryAffineOpExpr(llhsOp, llhs, lhs);
|
2018-07-11 01:08:27 +08:00
|
|
|
return parseAffineLowPrecOpExpr(sum, lOp);
|
2018-07-04 11:16:08 +08:00
|
|
|
}
|
|
|
|
// No LLHS, get RHS and form the expression.
|
2018-07-11 01:08:27 +08:00
|
|
|
return parseAffineLowPrecOpExpr(lhs, lOp);
|
2018-07-10 00:00:25 +08:00
|
|
|
}
|
2018-07-21 05:57:21 +08:00
|
|
|
auto opLoc = getToken().getLoc();
|
2018-07-11 01:08:27 +08:00
|
|
|
if (AffineHighPrecOp hOp = consumeIfHighPrecOp()) {
|
2018-07-04 11:16:08 +08:00
|
|
|
// We have a higher precedence op here. Get the rhs operand for the llhs
|
|
|
|
// through parseAffineHighPrecOpExpr.
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr highRes = parseAffineHighPrecOpExpr(lhs, hOp, opLoc);
|
2018-07-10 00:00:25 +08:00
|
|
|
if (!highRes)
|
|
|
|
return nullptr;
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2018-07-04 11:16:08 +08:00
|
|
|
// If llhs is null, the product forms the first operand of the yet to be
|
2018-07-10 00:00:25 +08:00
|
|
|
// found expression. If non-null, the op to associate with llhs is llhsOp.
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr expr =
|
2018-07-09 11:51:38 +08:00
|
|
|
llhs ? getBinaryAffineOpExpr(llhsOp, llhs, highRes) : highRes;
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2018-07-10 00:00:25 +08:00
|
|
|
// Recurse for subsequent low prec op's after the affine high prec op
|
|
|
|
// expression.
|
2018-07-11 01:08:27 +08:00
|
|
|
if (AffineLowPrecOp nextOp = consumeIfLowPrecOp())
|
|
|
|
return parseAffineLowPrecOpExpr(expr, nextOp);
|
2018-07-04 11:16:08 +08:00
|
|
|
return expr;
|
|
|
|
}
|
2018-07-10 00:00:25 +08:00
|
|
|
// Last operand in the expression list.
|
|
|
|
if (llhs)
|
|
|
|
return getBinaryAffineOpExpr(llhsOp, llhs, lhs);
|
|
|
|
// No llhs, 'lhs' itself is the expression.
|
|
|
|
return lhs;
|
2018-07-04 11:16:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse an affine expression.
|
2018-07-10 00:00:25 +08:00
|
|
|
/// affine-expr ::= `(` affine-expr `)`
|
|
|
|
/// | `-` affine-expr
|
|
|
|
/// | affine-expr `+` affine-expr
|
|
|
|
/// | affine-expr `-` affine-expr
|
|
|
|
/// | affine-expr `*` affine-expr
|
|
|
|
/// | affine-expr `floordiv` affine-expr
|
|
|
|
/// | affine-expr `ceildiv` affine-expr
|
|
|
|
/// | affine-expr `mod` affine-expr
|
|
|
|
/// | bare-id
|
|
|
|
/// | integer-literal
|
|
|
|
///
|
2018-10-19 04:54:44 +08:00
|
|
|
/// Additional conditions are checked depending on the production. For eg.,
|
|
|
|
/// one of the operands for `*` has to be either constant/symbolic; the second
|
2018-07-10 00:00:25 +08:00
|
|
|
/// operand for floordiv, ceildiv, and mod has to be a positive integer.
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::parseAffineExpr() {
|
2018-07-11 01:08:27 +08:00
|
|
|
return parseAffineLowPrecOpExpr(nullptr, AffineLowPrecOp::LNoOp);
|
2018-06-30 09:09:29 +08:00
|
|
|
}
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
/// Parse a dim or symbol from the lists appearing before the actual
|
|
|
|
/// expressions of the affine map. Update our state to store the
|
|
|
|
/// dimensional/symbolic identifier.
|
2018-10-09 04:47:18 +08:00
|
|
|
ParseResult AffineParser::parseIdentifierDefinition(AffineExpr idExpr) {
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().isNot(Token::bare_identifier))
|
2018-07-04 11:16:08 +08:00
|
|
|
return emitError("expected bare identifier");
|
2018-07-26 03:55:50 +08:00
|
|
|
|
|
|
|
auto name = getTokenSpelling();
|
|
|
|
for (auto entry : dimsAndSymbols) {
|
|
|
|
if (entry.first == name)
|
|
|
|
return emitError("redefinition of identifier '" + Twine(name) + "'");
|
|
|
|
}
|
2018-06-30 09:09:29 +08:00
|
|
|
consumeToken(Token::bare_identifier);
|
2018-07-26 03:55:50 +08:00
|
|
|
|
|
|
|
dimsAndSymbols.push_back({name, idExpr});
|
2018-07-04 11:16:08 +08:00
|
|
|
return ParseSuccess;
|
2018-06-30 09:09:29 +08:00
|
|
|
}
|
|
|
|
|
2018-07-04 11:16:08 +08:00
|
|
|
/// Parse the list of symbolic identifiers to an affine map.
|
2018-08-08 05:24:38 +08:00
|
|
|
ParseResult AffineParser::parseSymbolIdList(unsigned &numSymbols) {
|
2018-07-26 03:55:50 +08:00
|
|
|
consumeToken(Token::l_square);
|
|
|
|
auto parseElt = [&]() -> ParseResult {
|
2018-10-09 01:20:25 +08:00
|
|
|
auto symbol = getAffineSymbolExpr(numSymbols++, getContext());
|
2018-07-26 03:55:50 +08:00
|
|
|
return parseIdentifierDefinition(symbol);
|
|
|
|
};
|
2018-07-26 02:15:20 +08:00
|
|
|
return parseCommaSeparatedListUntil(Token::r_square, parseElt);
|
2018-06-30 09:09:29 +08:00
|
|
|
}
|
|
|
|
|
2018-07-04 11:16:08 +08:00
|
|
|
/// Parse the list of dimensional identifiers to an affine map.
|
2018-08-08 05:24:38 +08:00
|
|
|
ParseResult AffineParser::parseDimIdList(unsigned &numDims) {
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::l_paren,
|
|
|
|
"expected '(' at start of dimensional identifiers list"))
|
|
|
|
return ParseFailure;
|
2018-06-30 09:09:29 +08:00
|
|
|
|
2018-07-26 03:55:50 +08:00
|
|
|
auto parseElt = [&]() -> ParseResult {
|
2018-10-09 01:20:25 +08:00
|
|
|
auto dimension = getAffineDimExpr(numDims++, getContext());
|
2018-07-26 03:55:50 +08:00
|
|
|
return parseIdentifierDefinition(dimension);
|
|
|
|
};
|
2018-07-22 05:32:09 +08:00
|
|
|
return parseCommaSeparatedListUntil(Token::r_paren, parseElt);
|
2018-06-30 09:09:29 +08:00
|
|
|
}
|
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
/// Parses either an affine map or an integer set definition inline. If both
|
|
|
|
/// 'map' and 'set' are non-null, parses either an affine map or an integer set.
|
|
|
|
/// If 'map' is set to nullptr, parses an integer set. If 'set' is set to
|
|
|
|
/// nullptr, parses an affine map. 'map'/'set' are set to the parsed structure.
|
2018-06-30 09:09:29 +08:00
|
|
|
///
|
2018-07-10 00:00:25 +08:00
|
|
|
/// affine-map-inline ::= dim-and-symbol-id-lists `->` multi-dim-affine-expr
|
|
|
|
/// (`size` `(` dim-size (`,` dim-size)* `)`)?
|
|
|
|
/// dim-size ::= affine-expr | `min` `(` affine-expr ( `,` affine-expr)+ `)`
|
2018-06-30 09:09:29 +08:00
|
|
|
///
|
2018-07-10 00:00:25 +08:00
|
|
|
/// multi-dim-affine-expr ::= `(` affine-expr (`,` affine-expr)* `)
|
2018-10-26 09:33:42 +08:00
|
|
|
///
|
|
|
|
///
|
|
|
|
/// integer-set-inline
|
|
|
|
/// ::= dim-and-symbol-id-lists `:`
|
|
|
|
/// affine-constraint-conjunction
|
|
|
|
/// affine-constraint-conjunction ::= /*empty*/
|
|
|
|
/// | affine-constraint (`,`
|
|
|
|
/// affine-constraint)*
|
|
|
|
///
|
|
|
|
void AffineParser::parseAffineStructureInline(AffineMap *map, IntegerSet *set) {
|
|
|
|
assert((map || set) && "one of map or set expected to be non-null");
|
|
|
|
|
2018-07-26 03:55:50 +08:00
|
|
|
unsigned numDims = 0, numSymbols = 0;
|
|
|
|
|
2018-06-30 09:09:29 +08:00
|
|
|
// List of dimensional identifiers.
|
2018-10-26 09:33:42 +08:00
|
|
|
if (parseDimIdList(numDims)) {
|
|
|
|
if (map)
|
|
|
|
*map = AffineMap::Null();
|
|
|
|
if (set)
|
|
|
|
*set = IntegerSet::Null();
|
|
|
|
return;
|
|
|
|
}
|
2018-06-30 09:09:29 +08:00
|
|
|
|
|
|
|
// Symbols are optional.
|
2018-07-26 02:15:20 +08:00
|
|
|
if (getToken().is(Token::l_square)) {
|
2018-10-26 09:33:42 +08:00
|
|
|
if (parseSymbolIdList(numSymbols)) {
|
|
|
|
if (map)
|
|
|
|
*map = AffineMap::Null();
|
|
|
|
if (set)
|
|
|
|
*set = IntegerSet::Null();
|
|
|
|
return;
|
|
|
|
}
|
2018-06-30 09:09:29 +08:00
|
|
|
}
|
2018-07-24 08:30:01 +08:00
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
// This is needed for parsing attributes as we wouldn't know whether we would
|
|
|
|
// be parsing an integer set attribute or an affine map attribute.
|
|
|
|
if (map && set && getToken().isNot(Token::arrow) &&
|
|
|
|
getToken().isNot(Token::colon)) {
|
|
|
|
emitError("expected '->' or ':' or '['");
|
|
|
|
*map = AffineMap::Null();
|
|
|
|
*set = IntegerSet::Null();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (map && (!set || getToken().is(Token::arrow))) {
|
|
|
|
// Parse an affine map.
|
|
|
|
if (parseToken(Token::arrow, "expected '->' or '['")) {
|
|
|
|
*map = AffineMap::Null();
|
|
|
|
if (set)
|
|
|
|
*set = IntegerSet::Null();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
*map = parseAffineMapRange(numDims, numSymbols);
|
|
|
|
if (set)
|
|
|
|
*set = IntegerSet::Null();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (set && (!map || getToken().is(Token::colon))) {
|
|
|
|
// Parse an integer set.
|
|
|
|
if (parseToken(Token::colon, "expected ':' or '['")) {
|
|
|
|
*set = IntegerSet::Null();
|
|
|
|
if (map)
|
|
|
|
*map = AffineMap::Null();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
*set = parseIntegerSetConstraints(numDims, numSymbols);
|
|
|
|
if (map)
|
|
|
|
*map = AffineMap::Null();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse the range and sizes affine map definition inline.
|
|
|
|
///
|
|
|
|
/// affine-map-inline ::= dim-and-symbol-id-lists `->` multi-dim-affine-expr
|
|
|
|
/// (`size` `(` dim-size (`,` dim-size)* `)`)?
|
|
|
|
/// dim-size ::= affine-expr | `min` `(` affine-expr ( `,` affine-expr)+ `)`
|
|
|
|
///
|
|
|
|
/// multi-dim-affine-expr ::= `(` affine-expr (`,` affine-expr)* `)
|
|
|
|
AffineMap AffineParser::parseAffineMapRange(unsigned numDims,
|
|
|
|
unsigned numSymbols) {
|
|
|
|
parseToken(Token::l_paren, "expected '(' at start of affine map range");
|
2018-06-30 09:09:29 +08:00
|
|
|
|
2018-10-09 04:47:18 +08:00
|
|
|
SmallVector<AffineExpr, 4> exprs;
|
2018-06-30 09:09:29 +08:00
|
|
|
auto parseElt = [&]() -> ParseResult {
|
2018-10-08 23:09:50 +08:00
|
|
|
auto elt = parseAffineExpr();
|
2018-06-30 09:09:29 +08:00
|
|
|
ParseResult res = elt ? ParseSuccess : ParseFailure;
|
|
|
|
exprs.push_back(elt);
|
|
|
|
return res;
|
|
|
|
};
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
// Parse a multi-dimensional affine expression (a comma-separated list of
|
|
|
|
// 1-d affine expressions); the list cannot be empty. Grammar:
|
|
|
|
// multi-dim-affine-expr ::= `(` affine-expr (`,` affine-expr)* `)
|
2018-07-22 05:32:09 +08:00
|
|
|
if (parseCommaSeparatedListUntil(Token::r_paren, parseElt, false))
|
2018-10-25 23:33:02 +08:00
|
|
|
return AffineMap::Null();
|
2018-06-30 09:09:29 +08:00
|
|
|
|
2018-07-12 12:31:07 +08:00
|
|
|
// Parse optional range sizes.
|
2018-07-13 09:04:04 +08:00
|
|
|
// range-sizes ::= (`size` `(` dim-size (`,` dim-size)* `)`)?
|
|
|
|
// dim-size ::= affine-expr | `min` `(` affine-expr (`,` affine-expr)+ `)`
|
|
|
|
// TODO(bondhugula): support for min of several affine expressions.
|
2018-07-12 12:31:07 +08:00
|
|
|
// TODO: check if sizes are non-negative whenever they are constant.
|
2018-10-09 04:47:18 +08:00
|
|
|
SmallVector<AffineExpr, 4> rangeSizes;
|
2018-07-12 12:31:07 +08:00
|
|
|
if (consumeIf(Token::kw_size)) {
|
|
|
|
// Location of the l_paren token (if it exists) for error reporting later.
|
|
|
|
auto loc = getToken().getLoc();
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::l_paren, "expected '(' at start of affine map range"))
|
2018-10-25 23:33:02 +08:00
|
|
|
return AffineMap::Null();
|
2018-07-12 12:31:07 +08:00
|
|
|
|
|
|
|
auto parseRangeSize = [&]() -> ParseResult {
|
2018-07-26 03:55:50 +08:00
|
|
|
auto loc = getToken().getLoc();
|
2018-10-08 23:09:50 +08:00
|
|
|
auto elt = parseAffineExpr();
|
2018-07-26 03:55:50 +08:00
|
|
|
if (!elt)
|
|
|
|
return ParseFailure;
|
|
|
|
|
2018-10-10 01:59:27 +08:00
|
|
|
if (!elt.isSymbolicOrConstant())
|
2018-07-26 03:55:50 +08:00
|
|
|
return emitError(loc,
|
|
|
|
"size expressions cannot refer to dimension values");
|
|
|
|
|
2018-07-12 12:31:07 +08:00
|
|
|
rangeSizes.push_back(elt);
|
2018-07-26 03:55:50 +08:00
|
|
|
return ParseSuccess;
|
2018-07-12 12:31:07 +08:00
|
|
|
};
|
|
|
|
|
2018-07-22 05:32:09 +08:00
|
|
|
if (parseCommaSeparatedListUntil(Token::r_paren, parseRangeSize, false))
|
2018-10-25 23:33:02 +08:00
|
|
|
return AffineMap::Null();
|
2018-07-12 12:31:07 +08:00
|
|
|
if (exprs.size() > rangeSizes.size())
|
|
|
|
return (emitError(loc, "fewer range sizes than range expressions"),
|
2018-10-25 23:33:02 +08:00
|
|
|
AffineMap::Null());
|
2018-07-12 12:31:07 +08:00
|
|
|
if (exprs.size() < rangeSizes.size())
|
|
|
|
return (emitError(loc, "more range sizes than range expressions"),
|
2018-10-25 23:33:02 +08:00
|
|
|
AffineMap::Null());
|
2018-07-12 12:31:07 +08:00
|
|
|
}
|
|
|
|
|
2018-07-04 11:16:08 +08:00
|
|
|
// Parsed a valid affine map.
|
2018-07-26 03:55:50 +08:00
|
|
|
return builder.getAffineMap(numDims, numSymbols, exprs, rangeSizes);
|
2018-06-28 02:03:08 +08:00
|
|
|
}
|
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
void Parser::parseAffineStructureInline(AffineMap *map, IntegerSet *set) {
|
|
|
|
AffineParser(state).parseAffineStructureInline(map, set);
|
|
|
|
}
|
|
|
|
|
2018-10-10 07:39:24 +08:00
|
|
|
AffineMap Parser::parseAffineMapInline() {
|
2018-10-26 09:33:42 +08:00
|
|
|
AffineMap map;
|
|
|
|
AffineParser(state).parseAffineStructureInline(&map, nullptr);
|
|
|
|
return map;
|
2018-07-11 01:08:27 +08:00
|
|
|
}
|
|
|
|
|
2018-10-26 13:13:03 +08:00
|
|
|
/// Parse either an affine map reference or integer set reference.
|
|
|
|
///
|
|
|
|
/// affine-structure ::= affine-structure-id | affine-structure-inline
|
|
|
|
/// affine-structure-id ::= `#` suffix-id
|
|
|
|
///
|
|
|
|
/// affine-structure ::= affine-map | integer-set
|
|
|
|
///
|
|
|
|
void Parser::parseAffineStructureReference(AffineMap *map, IntegerSet *set) {
|
|
|
|
assert((map || set) && "both map and set are non-null");
|
|
|
|
if (getToken().isNot(Token::hash_identifier)) {
|
|
|
|
// Try to parse inline affine map or integer set.
|
|
|
|
return parseAffineStructureInline(map, set);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse affine map / integer set identifier and verify that it exists.
|
|
|
|
// Note that an id can't be in both affineMapDefinitions and
|
|
|
|
// integerSetDefinitions since they use the same sigil '#'.
|
|
|
|
StringRef affineStructId = getTokenSpelling().drop_front();
|
|
|
|
if (getState().affineMapDefinitions.count(affineStructId) > 0) {
|
|
|
|
consumeToken(Token::hash_identifier);
|
|
|
|
if (map)
|
|
|
|
*map = getState().affineMapDefinitions[affineStructId];
|
|
|
|
if (set)
|
|
|
|
*set = IntegerSet::Null();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getState().integerSetDefinitions.count(affineStructId) > 0) {
|
2018-07-17 00:45:22 +08:00
|
|
|
consumeToken(Token::hash_identifier);
|
2018-10-26 13:13:03 +08:00
|
|
|
if (set)
|
|
|
|
*set = getState().integerSetDefinitions[affineStructId];
|
|
|
|
if (map)
|
|
|
|
*map = AffineMap::Null();
|
|
|
|
return;
|
2018-07-17 00:45:22 +08:00
|
|
|
}
|
2018-10-26 13:13:03 +08:00
|
|
|
|
|
|
|
// The id isn't among any of the recorded definitions.
|
|
|
|
// Emit the right message depending on what the caller expected.
|
|
|
|
if (map && !set)
|
|
|
|
emitError("undefined affine map id '" + affineStructId + "'");
|
|
|
|
else if (set && !map)
|
|
|
|
emitError("undefined integer set id '" + affineStructId + "'");
|
|
|
|
else if (set && map)
|
|
|
|
emitError("undefined affine map or integer set id '" + affineStructId +
|
|
|
|
"'");
|
|
|
|
|
|
|
|
if (map)
|
|
|
|
*map = AffineMap::Null();
|
|
|
|
if (set)
|
|
|
|
*set = IntegerSet::Null();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a reference to an integer set.
|
|
|
|
/// affine-map ::= affine-map-id | affine-map-inline
|
|
|
|
/// affine-map-id ::= `#` suffix-id
|
|
|
|
///
|
|
|
|
AffineMap Parser::parseAffineMapReference() {
|
|
|
|
AffineMap map;
|
|
|
|
parseAffineStructureReference(&map, nullptr);
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a reference to an integer set.
|
|
|
|
/// integer-set ::= integer-set-id | integer-set-inline
|
|
|
|
/// integer-set-id ::= `#` suffix-id
|
|
|
|
///
|
|
|
|
IntegerSet Parser::parseIntegerSetReference() {
|
|
|
|
IntegerSet set;
|
|
|
|
parseAffineStructureReference(nullptr, &set);
|
|
|
|
return set;
|
2018-07-17 00:45:22 +08:00
|
|
|
}
|
|
|
|
|
2018-06-23 01:39:19 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-07-19 23:35:28 +08:00
|
|
|
// FunctionParser
|
2018-06-23 01:39:19 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
namespace {
|
2018-10-19 04:54:44 +08:00
|
|
|
/// This class contains parser state that is common across CFG and ML
|
|
|
|
/// functions, notably for dealing with operations and SSA values.
|
2018-07-19 23:35:28 +08:00
|
|
|
class FunctionParser : public Parser {
|
|
|
|
public:
|
2018-07-27 09:09:20 +08:00
|
|
|
enum class Kind { CFGFunc, MLFunc };
|
|
|
|
|
|
|
|
Kind getKind() const { return kind; }
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2018-07-21 09:41:34 +08:00
|
|
|
/// After the function is finished parsing, this function checks to see if
|
|
|
|
/// there are any remaining issues.
|
2018-07-22 05:32:09 +08:00
|
|
|
ParseResult finalizeFunction(Function *func, SMLoc loc);
|
2018-07-21 09:41:34 +08:00
|
|
|
|
|
|
|
/// This represents a use of an SSA value in the program. The first two
|
|
|
|
/// entries in the tuple are the name and result number of a reference. The
|
2018-10-19 04:54:44 +08:00
|
|
|
/// third is the location of the reference, which is used in case this ends
|
|
|
|
/// up being a use of an undefined value.
|
2018-07-21 09:41:34 +08:00
|
|
|
struct SSAUseInfo {
|
|
|
|
StringRef name; // Value name, e.g. %42 or %abc
|
|
|
|
unsigned number; // Number, specified with #12
|
|
|
|
SMLoc loc; // Location of first definition or use.
|
|
|
|
};
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
/// Given a reference to an SSA value and its type, return a reference. This
|
2018-07-19 23:35:28 +08:00
|
|
|
/// returns null on failure.
|
2018-10-31 05:59:22 +08:00
|
|
|
SSAValue *resolveSSAUse(SSAUseInfo useInfo, Type type);
|
2018-07-19 23:35:28 +08:00
|
|
|
|
|
|
|
/// Register a definition of a value with the symbol table.
|
|
|
|
ParseResult addDefinition(SSAUseInfo useInfo, SSAValue *value);
|
|
|
|
|
|
|
|
// SSA parsing productions.
|
|
|
|
ParseResult parseSSAUse(SSAUseInfo &result);
|
2018-07-22 05:32:09 +08:00
|
|
|
ParseResult parseOptionalSSAUseList(SmallVectorImpl<SSAUseInfo> &results);
|
2018-07-23 06:45:24 +08:00
|
|
|
|
|
|
|
template <typename ResultType>
|
|
|
|
ResultType parseSSADefOrUseAndType(
|
2018-10-31 05:59:22 +08:00
|
|
|
const std::function<ResultType(SSAUseInfo, Type)> &action);
|
2018-07-23 06:45:24 +08:00
|
|
|
|
|
|
|
SSAValue *parseSSAUseAndType() {
|
|
|
|
return parseSSADefOrUseAndType<SSAValue *>(
|
2018-10-31 05:59:22 +08:00
|
|
|
[&](SSAUseInfo useInfo, Type type) -> SSAValue * {
|
2018-07-23 06:45:24 +08:00
|
|
|
return resolveSSAUse(useInfo, type);
|
|
|
|
});
|
|
|
|
}
|
2018-07-22 05:32:09 +08:00
|
|
|
|
|
|
|
template <typename ValueTy>
|
2018-07-19 23:35:28 +08:00
|
|
|
ParseResult
|
2018-11-13 06:58:53 +08:00
|
|
|
parseOptionalSSAUseAndTypeList(SmallVectorImpl<ValueTy *> &results);
|
2018-07-19 23:35:28 +08:00
|
|
|
|
|
|
|
// Operations
|
|
|
|
ParseResult parseOperation(const CreateOperationFunction &createOpFunc);
|
2018-07-26 02:15:20 +08:00
|
|
|
Operation *parseVerboseOperation(const CreateOperationFunction &createOpFunc);
|
|
|
|
Operation *parseCustomOperation(const CreateOperationFunction &createOpFunc);
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2018-11-16 01:56:06 +08:00
|
|
|
/// Parse a single operation successor and it's operand list.
|
|
|
|
virtual bool
|
|
|
|
parseSuccessorAndUseList(BasicBlock *&dest,
|
|
|
|
SmallVectorImpl<SSAValue *> &operands) = 0;
|
|
|
|
|
2018-07-27 09:09:20 +08:00
|
|
|
protected:
|
|
|
|
FunctionParser(ParserState &state, Kind kind) : Parser(state), kind(kind) {}
|
|
|
|
|
2018-11-16 01:56:06 +08:00
|
|
|
virtual ~FunctionParser();
|
2018-10-14 22:55:29 +08:00
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
private:
|
2018-07-27 09:09:20 +08:00
|
|
|
/// Kind indicates if this is CFG or ML function parser.
|
|
|
|
Kind kind;
|
2018-07-19 23:35:28 +08:00
|
|
|
/// This keeps track of all of the SSA values we are tracking, indexed by
|
2018-07-21 09:41:34 +08:00
|
|
|
/// their name. This has one entry per result number.
|
|
|
|
llvm::StringMap<SmallVector<std::pair<SSAValue *, SMLoc>, 1>> values;
|
|
|
|
|
|
|
|
/// These are all of the placeholders we've made along with the location of
|
|
|
|
/// their first reference, to allow checking for use of undefined values.
|
|
|
|
DenseMap<SSAValue *, SMLoc> forwardReferencePlaceholders;
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
SSAValue *createForwardReferencePlaceholder(SMLoc loc, Type type);
|
2018-07-21 09:41:34 +08:00
|
|
|
|
|
|
|
/// Return true if this is a forward reference.
|
|
|
|
bool isForwardReferencePlaceholder(SSAValue *value) {
|
|
|
|
return forwardReferencePlaceholders.count(value);
|
|
|
|
}
|
2018-07-19 23:35:28 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2018-07-21 09:41:34 +08:00
|
|
|
/// Create and remember a new placeholder for a forward reference.
|
|
|
|
SSAValue *FunctionParser::createForwardReferencePlaceholder(SMLoc loc,
|
2018-10-31 05:59:22 +08:00
|
|
|
Type type) {
|
2018-07-21 09:41:34 +08:00
|
|
|
// Forward references are always created as instructions, even in ML
|
|
|
|
// functions, because we just need something with a def/use chain.
|
|
|
|
//
|
2018-10-19 04:54:44 +08:00
|
|
|
// We create these placeholders as having an empty name, which we know
|
|
|
|
// cannot be created through normal user input, allowing us to distinguish
|
|
|
|
// them.
|
2018-10-10 13:08:52 +08:00
|
|
|
auto name = OperationName("placeholder", getContext());
|
2018-12-28 03:07:34 +08:00
|
|
|
auto *inst = OperationInst::create(getEncodedSourceLocation(loc), name,
|
|
|
|
/*operands=*/{}, type,
|
|
|
|
/*attributes=*/{},
|
|
|
|
/*successors=*/{}, getContext());
|
2018-07-21 09:41:34 +08:00
|
|
|
forwardReferencePlaceholders[inst->getResult(0)] = loc;
|
|
|
|
return inst->getResult(0);
|
|
|
|
}
|
|
|
|
|
2018-07-27 09:09:20 +08:00
|
|
|
/// Given an unbound reference to an SSA value and its type, return the value
|
2018-07-19 23:35:28 +08:00
|
|
|
/// it specifies. This returns null on failure.
|
2018-10-31 05:59:22 +08:00
|
|
|
SSAValue *FunctionParser::resolveSSAUse(SSAUseInfo useInfo, Type type) {
|
2018-07-21 09:41:34 +08:00
|
|
|
auto &entries = values[useInfo.name];
|
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
// If we have already seen a value of this name, return it.
|
2018-07-21 09:41:34 +08:00
|
|
|
if (useInfo.number < entries.size() && entries[useInfo.number].first) {
|
|
|
|
auto *result = entries[useInfo.number].first;
|
2018-07-19 23:35:28 +08:00
|
|
|
// Check that the type matches the other uses.
|
|
|
|
if (result->getType() == type)
|
|
|
|
return result;
|
|
|
|
|
2018-07-21 09:41:34 +08:00
|
|
|
emitError(useInfo.loc, "use of value '" + useInfo.name.str() +
|
|
|
|
"' expects different type than prior uses");
|
|
|
|
emitError(entries[useInfo.number].second, "prior use here");
|
2018-07-19 23:35:28 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-07-21 09:41:34 +08:00
|
|
|
// Make sure we have enough slots for this.
|
|
|
|
if (entries.size() <= useInfo.number)
|
|
|
|
entries.resize(useInfo.number + 1);
|
|
|
|
|
|
|
|
// If the value has already been defined and this is an overly large result
|
|
|
|
// number, diagnose that.
|
|
|
|
if (entries[0].first && !isForwardReferencePlaceholder(entries[0].first))
|
|
|
|
return (emitError(useInfo.loc, "reference to invalid result number"),
|
|
|
|
nullptr);
|
|
|
|
|
2018-08-01 14:14:16 +08:00
|
|
|
// Otherwise, this is a forward reference. If we are in ML function return
|
|
|
|
// an error. In CFG function, create a placeholder and remember
|
|
|
|
// that we did so.
|
2018-07-27 09:09:20 +08:00
|
|
|
if (getKind() == Kind::MLFunc)
|
|
|
|
return (
|
|
|
|
emitError(useInfo.loc, "use of undefined SSA value " + useInfo.name),
|
|
|
|
nullptr);
|
|
|
|
|
2018-07-21 09:41:34 +08:00
|
|
|
auto *result = createForwardReferencePlaceholder(useInfo.loc, type);
|
|
|
|
entries[useInfo.number].first = result;
|
|
|
|
entries[useInfo.number].second = useInfo.loc;
|
|
|
|
return result;
|
2018-07-19 23:35:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Register a definition of a value with the symbol table.
|
|
|
|
ParseResult FunctionParser::addDefinition(SSAUseInfo useInfo, SSAValue *value) {
|
2018-07-21 09:41:34 +08:00
|
|
|
auto &entries = values[useInfo.name];
|
|
|
|
|
|
|
|
// Make sure there is a slot for this value.
|
|
|
|
if (entries.size() <= useInfo.number)
|
|
|
|
entries.resize(useInfo.number + 1);
|
|
|
|
|
|
|
|
// If we already have an entry for this, check to see if it was a definition
|
|
|
|
// or a forward reference.
|
|
|
|
if (auto *existing = entries[useInfo.number].first) {
|
|
|
|
if (!isForwardReferencePlaceholder(existing)) {
|
|
|
|
emitError(useInfo.loc,
|
|
|
|
"redefinition of SSA value '" + useInfo.name + "'");
|
|
|
|
return emitError(entries[useInfo.number].second,
|
|
|
|
"previously defined here");
|
|
|
|
}
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
// If it was a forward reference, update everything that used it to use
|
|
|
|
// the actual definition instead, delete the forward ref, and remove it
|
|
|
|
// from our set of forward references we track.
|
2018-07-21 09:41:34 +08:00
|
|
|
existing->replaceAllUsesWith(value);
|
|
|
|
existing->getDefiningInst()->destroy();
|
|
|
|
forwardReferencePlaceholders.erase(existing);
|
|
|
|
}
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2018-07-21 09:41:34 +08:00
|
|
|
entries[useInfo.number].first = value;
|
|
|
|
entries[useInfo.number].second = useInfo.loc;
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// After the function is finished parsing, this function checks to see if
|
|
|
|
/// there are any remaining issues.
|
2018-07-22 05:32:09 +08:00
|
|
|
ParseResult FunctionParser::finalizeFunction(Function *func, SMLoc loc) {
|
2018-10-19 04:54:44 +08:00
|
|
|
// Check for any forward references that are left. If we find any, error
|
|
|
|
// out.
|
2018-07-21 09:41:34 +08:00
|
|
|
if (!forwardReferencePlaceholders.empty()) {
|
|
|
|
SmallVector<std::pair<const char *, SSAValue *>, 4> errors;
|
2018-10-14 22:55:29 +08:00
|
|
|
// Iteration over the map isn't deterministic, so sort by source location.
|
2018-07-21 09:41:34 +08:00
|
|
|
for (auto entry : forwardReferencePlaceholders)
|
|
|
|
errors.push_back({entry.second.getPointer(), entry.first});
|
|
|
|
llvm::array_pod_sort(errors.begin(), errors.end());
|
|
|
|
|
2018-10-14 22:55:29 +08:00
|
|
|
for (auto entry : errors) {
|
|
|
|
auto loc = SMLoc::getFromPointer(entry.first);
|
|
|
|
emitError(loc, "use of undeclared SSA value name");
|
|
|
|
}
|
2018-07-21 09:41:34 +08:00
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ParseSuccess;
|
2018-07-19 23:35:28 +08:00
|
|
|
}
|
|
|
|
|
2018-10-14 22:55:29 +08:00
|
|
|
FunctionParser::~FunctionParser() {
|
|
|
|
for (auto &fwd : forwardReferencePlaceholders) {
|
|
|
|
// Drop all uses of undefined forward declared reference and destroy
|
|
|
|
// defining instruction.
|
|
|
|
for (auto &use : fwd.first->getUses())
|
|
|
|
use.drop();
|
|
|
|
fwd.first->getDefiningInst()->destroy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-08 06:48:26 +08:00
|
|
|
/// Parse a SSA operand for an instruction or statement.
|
|
|
|
///
|
2018-07-23 06:45:24 +08:00
|
|
|
/// ssa-use ::= ssa-id
|
2018-07-08 06:48:26 +08:00
|
|
|
///
|
2018-07-19 23:35:28 +08:00
|
|
|
ParseResult FunctionParser::parseSSAUse(SSAUseInfo &result) {
|
2018-07-21 09:41:34 +08:00
|
|
|
result.name = getTokenSpelling();
|
|
|
|
result.number = 0;
|
|
|
|
result.loc = getToken().getLoc();
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::percent_identifier, "expected SSA operand"))
|
|
|
|
return ParseFailure;
|
2018-07-21 09:41:34 +08:00
|
|
|
|
|
|
|
// If we have an affine map ID, it is a result number.
|
|
|
|
if (getToken().is(Token::hash_identifier)) {
|
|
|
|
if (auto value = getToken().getHashIdentifierNumber())
|
|
|
|
result.number = value.getValue();
|
|
|
|
else
|
|
|
|
return emitError("invalid SSA value result number");
|
|
|
|
consumeToken(Token::hash_identifier);
|
|
|
|
}
|
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
return ParseSuccess;
|
2018-07-08 06:48:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a (possibly empty) list of SSA operands.
|
|
|
|
///
|
|
|
|
/// ssa-use-list ::= ssa-use (`,` ssa-use)*
|
|
|
|
/// ssa-use-list-opt ::= ssa-use-list?
|
|
|
|
///
|
2018-07-19 23:35:28 +08:00
|
|
|
ParseResult
|
2018-07-22 05:32:09 +08:00
|
|
|
FunctionParser::parseOptionalSSAUseList(SmallVectorImpl<SSAUseInfo> &results) {
|
2018-07-26 02:15:20 +08:00
|
|
|
if (getToken().isNot(Token::percent_identifier))
|
2018-07-22 05:32:09 +08:00
|
|
|
return ParseSuccess;
|
|
|
|
return parseCommaSeparatedList([&]() -> ParseResult {
|
2018-07-19 23:35:28 +08:00
|
|
|
SSAUseInfo result;
|
|
|
|
if (parseSSAUse(result))
|
|
|
|
return ParseFailure;
|
|
|
|
results.push_back(result);
|
|
|
|
return ParseSuccess;
|
|
|
|
});
|
2018-07-08 06:48:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse an SSA use with an associated type.
|
|
|
|
///
|
|
|
|
/// ssa-use-and-type ::= ssa-use `:` type
|
2018-07-23 06:45:24 +08:00
|
|
|
template <typename ResultType>
|
|
|
|
ResultType FunctionParser::parseSSADefOrUseAndType(
|
2018-10-31 05:59:22 +08:00
|
|
|
const std::function<ResultType(SSAUseInfo, Type)> &action) {
|
2018-07-24 08:30:01 +08:00
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
SSAUseInfo useInfo;
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseSSAUse(useInfo) ||
|
|
|
|
parseToken(Token::colon, "expected ':' and type for SSA operand"))
|
2018-07-19 23:35:28 +08:00
|
|
|
return nullptr;
|
2018-07-08 06:48:26 +08:00
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
auto type = parseType();
|
2018-07-19 23:35:28 +08:00
|
|
|
if (!type)
|
|
|
|
return nullptr;
|
2018-07-08 06:48:26 +08:00
|
|
|
|
2018-07-23 06:45:24 +08:00
|
|
|
return action(useInfo, type);
|
2018-07-08 06:48:26 +08:00
|
|
|
}
|
|
|
|
|
2018-07-24 02:56:17 +08:00
|
|
|
/// Parse a (possibly empty) list of SSA operands, followed by a colon, then
|
2018-11-13 06:58:53 +08:00
|
|
|
/// followed by a type list.
|
2018-07-08 06:48:26 +08:00
|
|
|
///
|
2018-11-13 06:58:53 +08:00
|
|
|
/// ssa-use-and-type-list
|
2018-07-24 02:56:17 +08:00
|
|
|
/// ::= ssa-use-list ':' type-list-no-parens
|
2018-07-08 06:48:26 +08:00
|
|
|
///
|
2018-07-22 05:32:09 +08:00
|
|
|
template <typename ValueTy>
|
2018-07-19 23:35:28 +08:00
|
|
|
ParseResult FunctionParser::parseOptionalSSAUseAndTypeList(
|
2018-11-13 06:58:53 +08:00
|
|
|
SmallVectorImpl<ValueTy *> &results) {
|
2018-07-24 02:56:17 +08:00
|
|
|
SmallVector<SSAUseInfo, 4> valueIDs;
|
|
|
|
if (parseOptionalSSAUseList(valueIDs))
|
2018-07-19 23:35:28 +08:00
|
|
|
return ParseFailure;
|
2018-07-24 02:56:17 +08:00
|
|
|
|
|
|
|
// If there were no operands, then there is no colon or type lists.
|
|
|
|
if (valueIDs.empty())
|
|
|
|
return ParseSuccess;
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
SmallVector<Type, 4> types;
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::colon, "expected ':' in operand list") ||
|
|
|
|
parseTypeListNoParens(types))
|
2018-07-24 02:56:17 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
if (valueIDs.size() != types.size())
|
|
|
|
return emitError("expected " + Twine(valueIDs.size()) +
|
|
|
|
" types to match operand list");
|
|
|
|
|
|
|
|
results.reserve(valueIDs.size());
|
|
|
|
for (unsigned i = 0, e = valueIDs.size(); i != e; ++i) {
|
|
|
|
if (auto *value = resolveSSAUse(valueIDs[i], types[i]))
|
|
|
|
results.push_back(cast<ValueTy>(value));
|
|
|
|
else
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ParseSuccess;
|
2018-07-08 06:48:26 +08:00
|
|
|
}
|
|
|
|
|
2018-07-17 02:47:09 +08:00
|
|
|
/// Parse the CFG or MLFunc operation.
|
|
|
|
///
|
|
|
|
/// operation ::=
|
|
|
|
/// (ssa-id `=`)? string '(' ssa-use-list? ')' attribute-dict?
|
|
|
|
/// `:` function-type
|
|
|
|
///
|
|
|
|
ParseResult
|
2018-07-19 23:35:28 +08:00
|
|
|
FunctionParser::parseOperation(const CreateOperationFunction &createOpFunc) {
|
2018-07-17 02:47:09 +08:00
|
|
|
auto loc = getToken().getLoc();
|
|
|
|
|
|
|
|
StringRef resultID;
|
|
|
|
if (getToken().is(Token::percent_identifier)) {
|
2018-07-19 23:35:28 +08:00
|
|
|
resultID = getTokenSpelling();
|
2018-07-17 02:47:09 +08:00
|
|
|
consumeToken(Token::percent_identifier);
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::equal, "expected '=' after SSA name"))
|
|
|
|
return ParseFailure;
|
2018-07-17 02:47:09 +08:00
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
Operation *op;
|
|
|
|
if (getToken().is(Token::bare_identifier) || getToken().isKeyword())
|
|
|
|
op = parseCustomOperation(createOpFunc);
|
|
|
|
else if (getToken().is(Token::string))
|
|
|
|
op = parseVerboseOperation(createOpFunc);
|
|
|
|
else
|
2018-07-17 02:47:09 +08:00
|
|
|
return emitError("expected operation name in quotes");
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
// If parsing of the basic operation failed, then this whole thing fails.
|
|
|
|
if (!op)
|
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
// We just parsed an operation. If it is a recognized one, verify that it
|
|
|
|
// is structurally as we expect. If not, produce an error with a reasonable
|
|
|
|
// source location.
|
2018-08-02 01:18:59 +08:00
|
|
|
if (auto *opInfo = op->getAbstractOperation()) {
|
2018-11-16 04:32:21 +08:00
|
|
|
// We don't wan't to verify branching terminators at this time because
|
|
|
|
// the successors may not have been fully parsed yet.
|
|
|
|
if (!(op->isTerminator() && op->getNumSuccessors() != 0) &&
|
|
|
|
opInfo->verifyInvariants(op))
|
2018-09-10 11:40:23 +08:00
|
|
|
return ParseFailure;
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the instruction had a name, register it.
|
|
|
|
if (!resultID.empty()) {
|
2018-07-27 09:09:20 +08:00
|
|
|
if (op->getNumResults() == 0)
|
|
|
|
return emitError(loc, "cannot name an operation with no results");
|
|
|
|
|
|
|
|
for (unsigned i = 0, e = op->getNumResults(); i != e; ++i)
|
2018-08-07 05:19:46 +08:00
|
|
|
if (addDefinition({resultID, i, loc}, op->getResult(i)))
|
|
|
|
return ParseFailure;
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
Operation *FunctionParser::parseVerboseOperation(
|
|
|
|
const CreateOperationFunction &createOpFunc) {
|
2018-08-24 05:32:25 +08:00
|
|
|
|
|
|
|
// Get location information for the operation.
|
2018-11-09 04:28:35 +08:00
|
|
|
auto srcLocation = getEncodedSourceLocation(getToken().getLoc());
|
2018-08-24 05:32:25 +08:00
|
|
|
|
2018-07-17 02:47:09 +08:00
|
|
|
auto name = getToken().getStringValue();
|
|
|
|
if (name.empty())
|
2018-07-26 02:15:20 +08:00
|
|
|
return (emitError("empty operation name is invalid"), nullptr);
|
2018-10-15 22:09:18 +08:00
|
|
|
if (name.find('\0') != StringRef::npos)
|
|
|
|
return (emitError("null character not allowed in operation name"), nullptr);
|
2018-07-17 02:47:09 +08:00
|
|
|
|
|
|
|
consumeToken(Token::string);
|
|
|
|
|
2018-08-24 05:32:25 +08:00
|
|
|
OperationState result(builder.getContext(), srcLocation, name);
|
2018-08-08 03:02:37 +08:00
|
|
|
|
2018-07-17 02:47:09 +08:00
|
|
|
// Parse the operand list.
|
2018-07-19 23:35:28 +08:00
|
|
|
SmallVector<SSAUseInfo, 8> operandInfos;
|
2018-07-22 05:32:09 +08:00
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::l_paren, "expected '(' to start operand list") ||
|
|
|
|
parseOptionalSSAUseList(operandInfos) ||
|
|
|
|
parseToken(Token::r_paren, "expected ')' to end operand list")) {
|
2018-07-26 02:15:20 +08:00
|
|
|
return nullptr;
|
2018-07-24 08:30:01 +08:00
|
|
|
}
|
2018-07-17 02:47:09 +08:00
|
|
|
|
|
|
|
if (getToken().is(Token::l_brace)) {
|
2018-08-08 03:02:37 +08:00
|
|
|
if (parseAttributeDict(result.attributes))
|
2018-07-26 02:15:20 +08:00
|
|
|
return nullptr;
|
2018-07-17 02:47:09 +08:00
|
|
|
}
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::colon, "expected ':' followed by instruction type"))
|
2018-07-26 02:15:20 +08:00
|
|
|
return nullptr;
|
2018-07-19 06:31:25 +08:00
|
|
|
|
|
|
|
auto typeLoc = getToken().getLoc();
|
|
|
|
auto type = parseType();
|
|
|
|
if (!type)
|
2018-07-26 02:15:20 +08:00
|
|
|
return nullptr;
|
2018-10-31 05:59:22 +08:00
|
|
|
auto fnType = type.dyn_cast<FunctionType>();
|
2018-07-19 06:31:25 +08:00
|
|
|
if (!fnType)
|
2018-07-26 02:15:20 +08:00
|
|
|
return (emitError(typeLoc, "expected function type"), nullptr);
|
2018-07-19 06:31:25 +08:00
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
result.addTypes(fnType.getResults());
|
2018-08-08 03:02:37 +08:00
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
// Check that we have the right number of types for the operands.
|
2018-10-31 05:59:22 +08:00
|
|
|
auto operandTypes = fnType.getInputs();
|
2018-07-19 23:35:28 +08:00
|
|
|
if (operandTypes.size() != operandInfos.size()) {
|
|
|
|
auto plural = "s"[operandInfos.size() == 1];
|
2018-07-26 02:15:20 +08:00
|
|
|
return (emitError(typeLoc, "expected " + llvm::utostr(operandInfos.size()) +
|
|
|
|
" operand type" + plural + " but had " +
|
|
|
|
llvm::utostr(operandTypes.size())),
|
|
|
|
nullptr);
|
2018-07-19 23:35:28 +08:00
|
|
|
}
|
2018-07-17 02:47:09 +08:00
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
// Resolve all of the operands.
|
|
|
|
for (unsigned i = 0, e = operandInfos.size(); i != e; ++i) {
|
2018-08-08 03:02:37 +08:00
|
|
|
result.operands.push_back(resolveSSAUse(operandInfos[i], operandTypes[i]));
|
|
|
|
if (!result.operands.back())
|
2018-07-26 02:15:20 +08:00
|
|
|
return nullptr;
|
2018-07-19 23:35:28 +08:00
|
|
|
}
|
2018-07-17 02:47:09 +08:00
|
|
|
|
2018-08-08 03:02:37 +08:00
|
|
|
return createOpFunc(result);
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
2018-07-17 02:47:09 +08:00
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
namespace {
|
|
|
|
class CustomOpAsmParser : public OpAsmParser {
|
|
|
|
public:
|
|
|
|
CustomOpAsmParser(SMLoc nameLoc, StringRef opName, FunctionParser &parser)
|
|
|
|
: nameLoc(nameLoc), opName(opName), parser(parser) {}
|
|
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// High level parsing methods.
|
|
|
|
//===--------------------------------------------------------------------===//
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2018-08-22 08:55:22 +08:00
|
|
|
bool getCurrentLocation(llvm::SMLoc *loc) override {
|
|
|
|
*loc = parser.getToken().getLoc();
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-24 05:58:27 +08:00
|
|
|
bool parseComma() override {
|
2018-07-26 02:15:20 +08:00
|
|
|
return parser.parseToken(Token::comma, "expected ','");
|
|
|
|
}
|
|
|
|
|
2018-11-16 09:53:51 +08:00
|
|
|
bool parseType(Type &result) override {
|
|
|
|
return !(result = parser.parseType());
|
|
|
|
}
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
bool parseColonType(Type &result) override {
|
2018-08-24 05:58:27 +08:00
|
|
|
return parser.parseToken(Token::colon, "expected ':'") ||
|
|
|
|
!(result = parser.parseType());
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
bool parseColonTypeList(SmallVectorImpl<Type> &result) override {
|
2018-08-24 05:58:27 +08:00
|
|
|
if (parser.parseToken(Token::colon, "expected ':'"))
|
2018-07-26 02:15:20 +08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
do {
|
2018-10-31 05:59:22 +08:00
|
|
|
if (auto type = parser.parseType())
|
2018-07-26 02:15:20 +08:00
|
|
|
result.push_back(type);
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} while (parser.consumeIf(Token::comma));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-12-06 07:30:25 +08:00
|
|
|
bool parseTrailingOperandList(SmallVectorImpl<OperandType> &result,
|
|
|
|
int requiredOperandCount,
|
|
|
|
Delimiter delimiter) override {
|
|
|
|
if (parser.getToken().is(Token::comma)) {
|
|
|
|
parseComma();
|
|
|
|
return parseOperandList(result, requiredOperandCount, delimiter);
|
|
|
|
}
|
|
|
|
if (requiredOperandCount != -1)
|
|
|
|
return emitError(parser.getToken().getLoc(),
|
|
|
|
"expected " + Twine(requiredOperandCount) + " operands");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-14 00:16:32 +08:00
|
|
|
/// Parse a keyword followed by a type.
|
2018-10-31 05:59:22 +08:00
|
|
|
bool parseKeywordType(const char *keyword, Type &result) override {
|
2018-09-14 00:16:32 +08:00
|
|
|
if (parser.getTokenSpelling() != keyword)
|
|
|
|
return parser.emitError("expected '" + Twine(keyword) + "'");
|
|
|
|
parser.consumeToken();
|
|
|
|
return !(result = parser.parseType());
|
|
|
|
}
|
|
|
|
|
2018-11-16 09:53:51 +08:00
|
|
|
/// Parse an arbitrary attribute of a given type and return it in result. This
|
|
|
|
/// also adds the attribute to the specified attribute list with the specified
|
|
|
|
/// name.
|
|
|
|
bool parseAttribute(Attribute &result, Type type, const char *attrName,
|
2018-08-24 05:58:27 +08:00
|
|
|
SmallVectorImpl<NamedAttribute> &attrs) override {
|
2018-11-16 09:53:51 +08:00
|
|
|
result = parser.parseAttribute(type);
|
2018-08-03 07:54:36 +08:00
|
|
|
if (!result)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
attrs.push_back(
|
|
|
|
NamedAttribute(parser.builder.getIdentifier(attrName), result));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-11-16 09:53:51 +08:00
|
|
|
/// Parse an arbitrary attribute and return it in result. This also adds
|
|
|
|
/// the attribute to the specified attribute list with the specified name.
|
|
|
|
bool parseAttribute(Attribute &result, const char *attrName,
|
|
|
|
SmallVectorImpl<NamedAttribute> &attrs) override {
|
|
|
|
return parseAttribute(result, Type(), attrName, attrs);
|
|
|
|
}
|
|
|
|
|
2018-08-03 07:54:36 +08:00
|
|
|
/// If a named attribute list is present, parse is into result.
|
2018-08-24 05:58:27 +08:00
|
|
|
bool
|
|
|
|
parseOptionalAttributeDict(SmallVectorImpl<NamedAttribute> &result) override {
|
2018-08-03 07:54:36 +08:00
|
|
|
if (parser.getToken().isNot(Token::l_brace))
|
|
|
|
return false;
|
|
|
|
return parser.parseAttributeDict(result) == ParseFailure;
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2018-08-22 08:55:22 +08:00
|
|
|
/// Parse a function name like '@foo' and return the name in a form that can
|
|
|
|
/// be passed to resolveFunctionName when a function type is available.
|
|
|
|
virtual bool parseFunctionName(StringRef &result, llvm::SMLoc &loc) {
|
|
|
|
loc = parser.getToken().getLoc();
|
|
|
|
|
|
|
|
if (parser.getToken().isNot(Token::at_identifier))
|
|
|
|
return emitError(loc, "expected function name");
|
|
|
|
|
|
|
|
result = parser.getTokenSpelling();
|
|
|
|
parser.consumeToken(Token::at_identifier);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
bool parseOperand(OperandType &result) override {
|
|
|
|
FunctionParser::SSAUseInfo useInfo;
|
|
|
|
if (parser.parseSSAUse(useInfo))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
result = {useInfo.loc, useInfo.name, useInfo.number};
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-11-16 01:56:06 +08:00
|
|
|
bool
|
|
|
|
parseSuccessorAndUseList(BasicBlock *&dest,
|
|
|
|
SmallVectorImpl<SSAValue *> &operands) override {
|
|
|
|
// Defer successor parsing to the function parsers.
|
|
|
|
return parser.parseSuccessorAndUseList(dest, operands);
|
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
bool parseOperandList(SmallVectorImpl<OperandType> &result,
|
|
|
|
int requiredOperandCount = -1,
|
2018-08-03 07:54:36 +08:00
|
|
|
Delimiter delimiter = Delimiter::None) override {
|
2018-07-26 02:15:20 +08:00
|
|
|
auto startLoc = parser.getToken().getLoc();
|
|
|
|
|
2018-08-03 07:54:36 +08:00
|
|
|
// Handle delimiters.
|
|
|
|
switch (delimiter) {
|
|
|
|
case Delimiter::None:
|
2018-09-10 08:59:22 +08:00
|
|
|
// Don't check for the absence of a delimiter if the number of operands
|
|
|
|
// is unknown (and hence the operand list could be empty).
|
|
|
|
if (requiredOperandCount == -1)
|
|
|
|
break;
|
|
|
|
// Token already matches an identifier and so can't be a delimiter.
|
|
|
|
if (parser.getToken().is(Token::percent_identifier))
|
|
|
|
break;
|
|
|
|
// Test against known delimiters.
|
|
|
|
if (parser.getToken().is(Token::l_paren) ||
|
|
|
|
parser.getToken().is(Token::l_square))
|
|
|
|
return emitError(startLoc, "unexpected delimiter");
|
2018-09-10 23:26:58 +08:00
|
|
|
return emitError(startLoc, "invalid operand");
|
2018-08-03 07:54:36 +08:00
|
|
|
case Delimiter::OptionalParen:
|
2018-07-29 00:36:25 +08:00
|
|
|
if (parser.getToken().isNot(Token::l_paren))
|
|
|
|
return false;
|
|
|
|
LLVM_FALLTHROUGH;
|
2018-08-03 07:54:36 +08:00
|
|
|
case Delimiter::Paren:
|
2018-07-26 02:15:20 +08:00
|
|
|
if (parser.parseToken(Token::l_paren, "expected '(' in operand list"))
|
|
|
|
return true;
|
|
|
|
break;
|
2018-08-03 07:54:36 +08:00
|
|
|
case Delimiter::OptionalSquare:
|
2018-07-29 00:36:25 +08:00
|
|
|
if (parser.getToken().isNot(Token::l_square))
|
|
|
|
return false;
|
|
|
|
LLVM_FALLTHROUGH;
|
2018-08-03 07:54:36 +08:00
|
|
|
case Delimiter::Square:
|
2018-07-26 02:15:20 +08:00
|
|
|
if (parser.parseToken(Token::l_square, "expected '[' in operand list"))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for zero operands.
|
|
|
|
if (parser.getToken().is(Token::percent_identifier)) {
|
|
|
|
do {
|
|
|
|
OperandType operand;
|
|
|
|
if (parseOperand(operand))
|
|
|
|
return true;
|
|
|
|
result.push_back(operand);
|
|
|
|
} while (parser.consumeIf(Token::comma));
|
|
|
|
}
|
|
|
|
|
2018-08-03 07:54:36 +08:00
|
|
|
// Handle delimiters. If we reach here, the optional delimiters were
|
2018-07-29 00:36:25 +08:00
|
|
|
// present, so we need to parse their closing one.
|
2018-08-03 07:54:36 +08:00
|
|
|
switch (delimiter) {
|
|
|
|
case Delimiter::None:
|
2018-07-26 02:15:20 +08:00
|
|
|
break;
|
2018-08-03 07:54:36 +08:00
|
|
|
case Delimiter::OptionalParen:
|
|
|
|
case Delimiter::Paren:
|
2018-07-26 02:15:20 +08:00
|
|
|
if (parser.parseToken(Token::r_paren, "expected ')' in operand list"))
|
|
|
|
return true;
|
|
|
|
break;
|
2018-08-03 07:54:36 +08:00
|
|
|
case Delimiter::OptionalSquare:
|
|
|
|
case Delimiter::Square:
|
2018-07-26 02:15:20 +08:00
|
|
|
if (parser.parseToken(Token::r_square, "expected ']' in operand list"))
|
|
|
|
return true;
|
|
|
|
break;
|
2018-07-19 23:35:28 +08:00
|
|
|
}
|
2018-07-26 02:15:20 +08:00
|
|
|
|
|
|
|
if (requiredOperandCount != -1 && result.size() != requiredOperandCount)
|
2018-09-10 04:41:19 +08:00
|
|
|
return emitError(startLoc,
|
|
|
|
"expected " + Twine(requiredOperandCount) + " operands");
|
2018-07-26 02:15:20 +08:00
|
|
|
return false;
|
2018-07-19 23:35:28 +08:00
|
|
|
}
|
|
|
|
|
2018-08-22 08:55:22 +08:00
|
|
|
/// Resolve a parse function name and a type into a function reference.
|
2018-10-31 05:59:22 +08:00
|
|
|
virtual bool resolveFunctionName(StringRef name, FunctionType type,
|
2018-08-22 08:55:22 +08:00
|
|
|
llvm::SMLoc loc, Function *&result) {
|
|
|
|
result = parser.resolveFunctionReference(name, loc, type);
|
|
|
|
return result == nullptr;
|
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Methods for interacting with the parser
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
Builder &getBuilder() const override { return parser.builder; }
|
|
|
|
|
|
|
|
llvm::SMLoc getNameLoc() const override { return nameLoc; }
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
bool resolveOperand(const OperandType &operand, Type type,
|
2018-08-09 02:02:58 +08:00
|
|
|
SmallVectorImpl<SSAValue *> &result) override {
|
2018-07-26 02:15:20 +08:00
|
|
|
FunctionParser::SSAUseInfo operandInfo = {operand.name, operand.number,
|
|
|
|
operand.location};
|
2018-08-09 02:02:58 +08:00
|
|
|
if (auto *value = parser.resolveSSAUse(operandInfo, type)) {
|
|
|
|
result.push_back(value);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2018-08-08 00:12:35 +08:00
|
|
|
/// Emit a diagnostic at the specified location and return true.
|
|
|
|
bool emitError(llvm::SMLoc loc, const Twine &message) override {
|
2018-07-26 02:15:20 +08:00
|
|
|
parser.emitError(loc, "custom op '" + Twine(opName) + "' " + message);
|
|
|
|
emittedError = true;
|
2018-08-08 00:12:35 +08:00
|
|
|
return true;
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool didEmitError() const { return emittedError; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
SMLoc nameLoc;
|
|
|
|
StringRef opName;
|
|
|
|
FunctionParser &parser;
|
|
|
|
bool emittedError = false;
|
|
|
|
};
|
|
|
|
} // end anonymous namespace.
|
|
|
|
|
|
|
|
Operation *FunctionParser::parseCustomOperation(
|
|
|
|
const CreateOperationFunction &createOpFunc) {
|
|
|
|
auto opLoc = getToken().getLoc();
|
|
|
|
auto opName = getTokenSpelling();
|
|
|
|
CustomOpAsmParser opAsmParser(opLoc, opName, *this);
|
|
|
|
|
2018-10-22 10:49:31 +08:00
|
|
|
auto *opDefinition = AbstractOperation::lookup(opName, getContext());
|
2018-07-26 02:15:20 +08:00
|
|
|
if (!opDefinition) {
|
|
|
|
opAsmParser.emitError(opLoc, "is unknown");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
consumeToken();
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
// If the custom op parser crashes, produce some indication to help
|
|
|
|
// debugging.
|
2018-08-22 08:55:22 +08:00
|
|
|
std::string opNameStr = opName.str();
|
|
|
|
llvm::PrettyStackTraceFormat fmt("MLIR Parser: custom op parser '%s'",
|
|
|
|
opNameStr.c_str());
|
|
|
|
|
2018-08-24 05:32:25 +08:00
|
|
|
// Get location information for the operation.
|
2018-11-09 04:28:35 +08:00
|
|
|
auto srcLocation = getEncodedSourceLocation(opLoc);
|
2018-08-24 05:32:25 +08:00
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
// Have the op implementation take a crack and parsing this.
|
2018-08-24 05:32:25 +08:00
|
|
|
OperationState opState(builder.getContext(), srcLocation, opName);
|
2018-08-08 00:12:35 +08:00
|
|
|
if (opDefinition->parseAssembly(&opAsmParser, &opState))
|
|
|
|
return nullptr;
|
2018-07-26 02:15:20 +08:00
|
|
|
|
|
|
|
// If it emitted an error, we failed.
|
|
|
|
if (opAsmParser.didEmitError())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
// Otherwise, we succeeded. Use the state it parsed as our op information.
|
2018-08-08 03:02:37 +08:00
|
|
|
return createOpFunc(opState);
|
2018-07-17 02:47:09 +08:00
|
|
|
}
|
2018-06-24 07:03:42 +08:00
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// CFG Functions
|
|
|
|
//===----------------------------------------------------------------------===//
|
2018-06-24 07:03:42 +08:00
|
|
|
|
|
|
|
namespace {
|
2018-10-14 22:55:29 +08:00
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
/// This is a specialized parser for CFGFunction's, maintaining the state
|
|
|
|
/// transient to their bodies.
|
2018-07-19 23:35:28 +08:00
|
|
|
class CFGFunctionParser : public FunctionParser {
|
2018-07-09 11:51:38 +08:00
|
|
|
public:
|
2018-07-11 01:08:27 +08:00
|
|
|
CFGFunctionParser(ParserState &state, CFGFunction *function)
|
2018-07-27 09:09:20 +08:00
|
|
|
: FunctionParser(state, Kind::CFGFunc), function(function),
|
|
|
|
builder(function) {}
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
ParseResult parseFunctionBody();
|
|
|
|
|
2018-11-16 01:56:06 +08:00
|
|
|
bool parseSuccessorAndUseList(BasicBlock *&dest,
|
|
|
|
SmallVectorImpl<SSAValue *> &operands);
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
private:
|
2018-06-25 02:18:29 +08:00
|
|
|
CFGFunction *function;
|
2018-07-24 07:56:32 +08:00
|
|
|
llvm::StringMap<std::pair<BasicBlock *, SMLoc>> blocksByName;
|
2018-10-14 22:55:29 +08:00
|
|
|
DenseMap<BasicBlock *, SMLoc> forwardRef;
|
2018-07-10 10:05:38 +08:00
|
|
|
|
|
|
|
/// This builder intentionally shadows the builder in the base class, with a
|
|
|
|
/// more specific builder type.
|
2018-07-09 11:51:38 +08:00
|
|
|
CFGFuncBuilder builder;
|
2018-06-25 02:18:29 +08:00
|
|
|
|
2018-06-24 07:03:42 +08:00
|
|
|
/// Get the basic block with the specified name, creating it if it doesn't
|
2018-06-25 02:18:29 +08:00
|
|
|
/// already exist. The location specified is the point of use, which allows
|
|
|
|
/// us to diagnose references to blocks that are not defined precisely.
|
|
|
|
BasicBlock *getBlockNamed(StringRef name, SMLoc loc) {
|
|
|
|
auto &blockAndLoc = blocksByName[name];
|
|
|
|
if (!blockAndLoc.first) {
|
2018-07-02 11:28:00 +08:00
|
|
|
blockAndLoc.first = new BasicBlock();
|
2018-10-14 22:55:29 +08:00
|
|
|
forwardRef[blockAndLoc.first] = loc;
|
|
|
|
function->push_back(blockAndLoc.first);
|
2018-06-25 02:18:29 +08:00
|
|
|
blockAndLoc.second = loc;
|
2018-06-24 07:03:42 +08:00
|
|
|
}
|
2018-10-14 22:55:29 +08:00
|
|
|
|
|
|
|
return blockAndLoc.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Define the basic block with the specified name. Returns the BasicBlock* or
|
|
|
|
// nullptr in the case of redefinition.
|
|
|
|
BasicBlock *defineBlockNamed(StringRef name, SMLoc loc) {
|
|
|
|
auto &blockAndLoc = blocksByName[name];
|
|
|
|
if (!blockAndLoc.first) {
|
|
|
|
blockAndLoc.first = builder.createBlock();
|
|
|
|
blockAndLoc.second = loc;
|
|
|
|
return blockAndLoc.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Forward declarations are removed once defined, so if we are defining a
|
|
|
|
// existing block and it is not a forward declaration, then it is a
|
|
|
|
// redeclaration.
|
|
|
|
if (!forwardRef.erase(blockAndLoc.first))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
// Move the block to the end of the function. Forward ref'd blocks are
|
|
|
|
// inserted wherever they happen to be referenced.
|
|
|
|
function->getBlocks().splice(function->end(), function->getBlocks(),
|
|
|
|
blockAndLoc.first);
|
2018-06-25 02:18:29 +08:00
|
|
|
return blockAndLoc.first;
|
2018-06-24 07:03:42 +08:00
|
|
|
}
|
2018-07-10 10:05:38 +08:00
|
|
|
|
2018-07-23 06:45:24 +08:00
|
|
|
ParseResult
|
|
|
|
parseOptionalBasicBlockArgList(SmallVectorImpl<BBArgument *> &results,
|
|
|
|
BasicBlock *owner);
|
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
ParseResult parseBasicBlock();
|
2018-06-24 07:03:42 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2018-11-16 01:56:06 +08:00
|
|
|
/// Parse a single operation successor and it's operand list.
|
|
|
|
///
|
|
|
|
/// successor ::= bb-id branch-use-list?
|
|
|
|
/// branch-use-list ::= `(` ssa-use-list ':' type-list-no-parens `)`
|
|
|
|
///
|
|
|
|
bool CFGFunctionParser::parseSuccessorAndUseList(
|
|
|
|
BasicBlock *&dest, SmallVectorImpl<SSAValue *> &operands) {
|
|
|
|
// Verify branch is identifier and get the matching block.
|
|
|
|
if (!getToken().is(Token::bare_identifier))
|
|
|
|
return emitError("expected basic block name");
|
|
|
|
dest = getBlockNamed(getTokenSpelling(), getToken().getLoc());
|
|
|
|
consumeToken();
|
|
|
|
|
|
|
|
// Handle optional arguments.
|
|
|
|
if (consumeIf(Token::l_paren) &&
|
|
|
|
(parseOptionalSSAUseAndTypeList(operands) ||
|
|
|
|
parseToken(Token::r_paren, "expected ')' to close argument list"))) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-23 06:45:24 +08:00
|
|
|
/// Parse a (possibly empty) list of SSA operands with types as basic block
|
2018-07-24 02:56:17 +08:00
|
|
|
/// arguments.
|
2018-07-23 06:45:24 +08:00
|
|
|
///
|
|
|
|
/// ssa-id-and-type-list ::= ssa-id-and-type (`,` ssa-id-and-type)*
|
|
|
|
///
|
|
|
|
ParseResult CFGFunctionParser::parseOptionalBasicBlockArgList(
|
|
|
|
SmallVectorImpl<BBArgument *> &results, BasicBlock *owner) {
|
|
|
|
if (getToken().is(Token::r_brace))
|
|
|
|
return ParseSuccess;
|
|
|
|
|
|
|
|
return parseCommaSeparatedList([&]() -> ParseResult {
|
2018-10-31 05:59:22 +08:00
|
|
|
auto type = parseSSADefOrUseAndType<Type>(
|
|
|
|
[&](SSAUseInfo useInfo, Type type) -> Type {
|
2018-07-23 06:45:24 +08:00
|
|
|
BBArgument *arg = owner->addArgument(type);
|
2018-08-07 05:19:46 +08:00
|
|
|
if (addDefinition(useInfo, arg))
|
2018-10-31 05:59:22 +08:00
|
|
|
return {};
|
2018-07-23 06:45:24 +08:00
|
|
|
return type;
|
|
|
|
});
|
|
|
|
return type ? ParseSuccess : ParseFailure;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
ParseResult CFGFunctionParser::parseFunctionBody() {
|
2018-07-22 05:32:09 +08:00
|
|
|
auto braceLoc = getToken().getLoc();
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::l_brace, "expected '{' in CFG function"))
|
|
|
|
return ParseFailure;
|
2018-07-10 10:05:38 +08:00
|
|
|
|
2018-06-24 07:03:42 +08:00
|
|
|
// Make sure we have at least one block.
|
2018-07-10 10:05:38 +08:00
|
|
|
if (getToken().is(Token::r_brace))
|
2018-06-24 07:03:42 +08:00
|
|
|
return emitError("CFG functions must have at least one basic block");
|
|
|
|
|
|
|
|
// Parse the list of blocks.
|
|
|
|
while (!consumeIf(Token::r_brace))
|
2018-07-10 10:05:38 +08:00
|
|
|
if (parseBasicBlock())
|
2018-06-24 07:03:42 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
2018-10-14 22:55:29 +08:00
|
|
|
// Verify that all referenced blocks were defined.
|
|
|
|
if (!forwardRef.empty()) {
|
|
|
|
SmallVector<std::pair<const char *, BasicBlock *>, 4> errors;
|
|
|
|
// Iteration over the map isn't deterministic, so sort by source location.
|
|
|
|
for (auto entry : forwardRef)
|
|
|
|
errors.push_back({entry.second.getPointer(), entry.first});
|
|
|
|
llvm::array_pod_sort(errors.begin(), errors.end());
|
|
|
|
|
|
|
|
for (auto entry : errors) {
|
|
|
|
auto loc = SMLoc::getFromPointer(entry.first);
|
|
|
|
emitError(loc, "reference to an undefined basic block");
|
|
|
|
}
|
|
|
|
return ParseFailure;
|
2018-06-25 02:18:29 +08:00
|
|
|
}
|
|
|
|
|
2018-11-16 04:32:21 +08:00
|
|
|
// Now that the function body has been fully parsed we check the invariants
|
|
|
|
// of any branching terminators.
|
|
|
|
for (auto &block : *function) {
|
|
|
|
auto *term = block.getTerminator();
|
|
|
|
auto *abstractOp = term->getAbstractOperation();
|
|
|
|
if (term->getNumSuccessors() != 0 && abstractOp)
|
|
|
|
abstractOp->verifyInvariants(term);
|
|
|
|
}
|
|
|
|
|
2018-07-22 05:32:09 +08:00
|
|
|
return finalizeFunction(function, braceLoc);
|
2018-06-24 07:03:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Basic block declaration.
|
|
|
|
///
|
|
|
|
/// basic-block ::= bb-label instruction* terminator-stmt
|
|
|
|
/// bb-label ::= bb-id bb-arg-list? `:`
|
|
|
|
/// bb-id ::= bare-id
|
|
|
|
/// bb-arg-list ::= `(` ssa-id-and-type-list? `)`
|
|
|
|
///
|
2018-07-10 10:05:38 +08:00
|
|
|
ParseResult CFGFunctionParser::parseBasicBlock() {
|
|
|
|
SMLoc nameLoc = getToken().getLoc();
|
|
|
|
auto name = getTokenSpelling();
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::bare_identifier, "expected basic block name"))
|
|
|
|
return ParseFailure;
|
2018-06-25 02:18:29 +08:00
|
|
|
|
2018-10-14 22:55:29 +08:00
|
|
|
auto *block = defineBlockNamed(name, nameLoc);
|
2018-06-24 07:03:42 +08:00
|
|
|
|
2018-10-14 22:55:29 +08:00
|
|
|
// Fail if redefinition.
|
|
|
|
if (!block)
|
2018-06-25 02:18:29 +08:00
|
|
|
return emitError(nameLoc, "redefinition of block '" + name.str() + "'");
|
|
|
|
|
2018-07-08 06:48:26 +08:00
|
|
|
// If an argument list is present, parse it.
|
|
|
|
if (consumeIf(Token::l_paren)) {
|
2018-07-23 06:45:24 +08:00
|
|
|
SmallVector<BBArgument *, 8> bbArgs;
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseOptionalBasicBlockArgList(bbArgs, block) ||
|
|
|
|
parseToken(Token::r_paren, "expected ')' to end argument list"))
|
2018-07-08 06:48:26 +08:00
|
|
|
return ParseFailure;
|
|
|
|
}
|
2018-06-24 07:03:42 +08:00
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::colon, "expected ':' after basic block name"))
|
|
|
|
return ParseFailure;
|
2018-06-24 07:03:42 +08:00
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
// Set the insertion point to the block we want to insert new operations
|
|
|
|
// into.
|
2018-07-10 10:05:38 +08:00
|
|
|
builder.setInsertionPoint(block);
|
2018-07-09 11:51:38 +08:00
|
|
|
|
2018-08-08 03:02:37 +08:00
|
|
|
auto createOpFunc = [&](const OperationState &result) -> Operation * {
|
|
|
|
return builder.createOperation(result);
|
2018-07-17 02:47:09 +08:00
|
|
|
};
|
|
|
|
|
2018-06-29 11:45:33 +08:00
|
|
|
// Parse the list of operations that make up the body of the block.
|
2018-07-25 06:01:27 +08:00
|
|
|
while (getToken().isNot(Token::kw_return, Token::kw_br, Token::kw_cond_br)) {
|
2018-07-17 02:47:09 +08:00
|
|
|
if (parseOperation(createOpFunc))
|
2018-06-29 11:45:33 +08:00
|
|
|
return ParseFailure;
|
|
|
|
}
|
2018-06-24 07:03:42 +08:00
|
|
|
|
2018-11-16 04:32:21 +08:00
|
|
|
// Parse the terminator operation.
|
|
|
|
if (parseOperation(createOpFunc))
|
2018-07-25 06:01:27 +08:00
|
|
|
return ParseFailure;
|
2018-10-14 22:55:29 +08:00
|
|
|
|
2018-07-25 06:01:27 +08:00
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-07-10 10:05:38 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ML Functions
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// Refined parser for MLFunction bodies.
|
2018-07-19 23:35:28 +08:00
|
|
|
class MLFunctionParser : public FunctionParser {
|
2018-07-10 10:05:38 +08:00
|
|
|
public:
|
|
|
|
MLFunctionParser(ParserState &state, MLFunction *function)
|
2018-07-27 09:09:20 +08:00
|
|
|
: FunctionParser(state, Kind::MLFunc), function(function),
|
2018-12-27 03:21:53 +08:00
|
|
|
builder(function->getBody()) {}
|
2018-07-10 10:05:38 +08:00
|
|
|
|
|
|
|
ParseResult parseFunctionBody();
|
2018-07-14 04:03:13 +08:00
|
|
|
|
|
|
|
private:
|
2018-07-17 02:47:09 +08:00
|
|
|
MLFunction *function;
|
|
|
|
|
|
|
|
/// This builder intentionally shadows the builder in the base class, with a
|
|
|
|
/// more specific builder type.
|
|
|
|
MLFuncBuilder builder;
|
|
|
|
|
|
|
|
ParseResult parseForStmt();
|
2018-08-25 14:38:14 +08:00
|
|
|
ParseResult parseIntConstant(int64_t &val);
|
|
|
|
ParseResult parseDimAndSymbolList(SmallVectorImpl<MLValue *> &operands,
|
2018-08-29 06:26:20 +08:00
|
|
|
unsigned numDims, unsigned numOperands,
|
|
|
|
const char *affineStructName);
|
2018-10-10 07:39:24 +08:00
|
|
|
ParseResult parseBound(SmallVectorImpl<MLValue *> &operands, AffineMap &map,
|
2018-08-25 14:38:14 +08:00
|
|
|
bool isLower);
|
2018-07-17 02:47:09 +08:00
|
|
|
ParseResult parseIfStmt();
|
2018-12-27 03:21:53 +08:00
|
|
|
ParseResult parseElseClause(StmtBlock *elseClause);
|
2018-07-17 02:47:09 +08:00
|
|
|
ParseResult parseStatements(StmtBlock *block);
|
2018-07-14 04:03:13 +08:00
|
|
|
ParseResult parseStmtBlock(StmtBlock *block);
|
2018-11-16 01:56:06 +08:00
|
|
|
|
|
|
|
bool parseSuccessorAndUseList(BasicBlock *&dest,
|
|
|
|
SmallVectorImpl<SSAValue *> &operands) {
|
|
|
|
assert(false && "MLFunctions do not have terminators with successors.");
|
|
|
|
return true;
|
|
|
|
}
|
2018-07-10 10:05:38 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
ParseResult MLFunctionParser::parseFunctionBody() {
|
2018-07-22 05:32:09 +08:00
|
|
|
auto braceLoc = getToken().getLoc();
|
2018-06-29 08:02:32 +08:00
|
|
|
|
2018-08-10 03:28:58 +08:00
|
|
|
// Parse statements in this function.
|
2018-12-27 03:21:53 +08:00
|
|
|
if (parseStmtBlock(function->getBody()))
|
2018-07-24 08:30:01 +08:00
|
|
|
return ParseFailure;
|
2018-07-22 05:32:09 +08:00
|
|
|
|
|
|
|
return finalizeFunction(function, braceLoc);
|
2018-06-29 08:02:32 +08:00
|
|
|
}
|
|
|
|
|
2018-07-04 08:51:28 +08:00
|
|
|
/// For statement.
|
|
|
|
///
|
2018-07-10 10:05:38 +08:00
|
|
|
/// ml-for-stmt ::= `for` ssa-id `=` lower-bound `to` upper-bound
|
|
|
|
/// (`step` integer-literal)? `{` ml-stmt* `}`
|
2018-07-04 08:51:28 +08:00
|
|
|
///
|
2018-07-17 02:47:09 +08:00
|
|
|
ParseResult MLFunctionParser::parseForStmt() {
|
2018-07-04 08:51:28 +08:00
|
|
|
consumeToken(Token::kw_for);
|
|
|
|
|
Extend loop unrolling to unroll by a given factor; add builder for affine
apply op.
- add builder for AffineApplyOp (first one for an operation that has
non-zero operands)
- add support for loop unrolling by a given factor; uses the affine apply op
builder.
While on this, change 'step' of ForStmt to be 'unsigned' instead of
AffineConstantExpr *. Add setters for ForStmt lb, ub, step.
Sample Input:
// CHECK-LABEL: mlfunc @loop_nest_unroll_cleanup() {
mlfunc @loop_nest_unroll_cleanup() {
for %i = 1 to 100 {
for %j = 0 to 17 {
%x = "addi32"(%j, %j) : (affineint, affineint) -> i32
%y = "addi32"(%x, %x) : (i32, i32) -> i32
}
}
return
}
Output:
$ mlir-opt -loop-unroll -unroll-factor=4 /tmp/single2.mlir
#map0 = (d0) -> (d0 + 1)
#map1 = (d0) -> (d0 + 2)
#map2 = (d0) -> (d0 + 3)
mlfunc @loop_nest_unroll_cleanup() {
for %i0 = 1 to 100 {
for %i1 = 0 to 17 step 4 {
%0 = "addi32"(%i1, %i1) : (affineint, affineint) -> i32
%1 = "addi32"(%0, %0) : (i32, i32) -> i32
%2 = affine_apply #map0(%i1)
%3 = "addi32"(%2, %2) : (affineint, affineint) -> i32
%4 = affine_apply #map1(%i1)
%5 = "addi32"(%4, %4) : (affineint, affineint) -> i32
%6 = affine_apply #map2(%i1)
%7 = "addi32"(%6, %6) : (affineint, affineint) -> i32
}
for %i2 = 16 to 17 {
%8 = "addi32"(%i2, %i2) : (affineint, affineint) -> i32
%9 = "addi32"(%8, %8) : (i32, i32) -> i32
}
}
return
}
PiperOrigin-RevId: 209676220
2018-08-22 07:01:23 +08:00
|
|
|
// Parse induction variable.
|
2018-07-20 00:52:39 +08:00
|
|
|
if (getToken().isNot(Token::percent_identifier))
|
|
|
|
return emitError("expected SSA identifier for the loop variable");
|
|
|
|
|
2018-07-31 06:18:10 +08:00
|
|
|
auto loc = getToken().getLoc();
|
2018-08-01 14:14:16 +08:00
|
|
|
StringRef inductionVariableName = getTokenSpelling();
|
2018-07-20 00:52:39 +08:00
|
|
|
consumeToken(Token::percent_identifier);
|
|
|
|
|
2018-08-10 03:28:58 +08:00
|
|
|
if (parseToken(Token::equal, "expected '='"))
|
2018-07-24 08:30:01 +08:00
|
|
|
return ParseFailure;
|
2018-07-20 00:52:39 +08:00
|
|
|
|
2018-08-25 14:38:14 +08:00
|
|
|
// Parse lower bound.
|
|
|
|
SmallVector<MLValue *, 4> lbOperands;
|
2018-10-10 07:39:24 +08:00
|
|
|
AffineMap lbMap;
|
2018-08-25 14:38:14 +08:00
|
|
|
if (parseBound(lbOperands, lbMap, /*isLower*/ true))
|
2018-07-20 00:52:39 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::kw_to, "expected 'to' between bounds"))
|
|
|
|
return ParseFailure;
|
2018-07-20 00:52:39 +08:00
|
|
|
|
2018-08-25 14:38:14 +08:00
|
|
|
// Parse upper bound.
|
|
|
|
SmallVector<MLValue *, 4> ubOperands;
|
2018-10-10 07:39:24 +08:00
|
|
|
AffineMap ubMap;
|
2018-08-25 14:38:14 +08:00
|
|
|
if (parseBound(ubOperands, ubMap, /*isLower*/ false))
|
2018-07-20 00:52:39 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
Extend loop unrolling to unroll by a given factor; add builder for affine
apply op.
- add builder for AffineApplyOp (first one for an operation that has
non-zero operands)
- add support for loop unrolling by a given factor; uses the affine apply op
builder.
While on this, change 'step' of ForStmt to be 'unsigned' instead of
AffineConstantExpr *. Add setters for ForStmt lb, ub, step.
Sample Input:
// CHECK-LABEL: mlfunc @loop_nest_unroll_cleanup() {
mlfunc @loop_nest_unroll_cleanup() {
for %i = 1 to 100 {
for %j = 0 to 17 {
%x = "addi32"(%j, %j) : (affineint, affineint) -> i32
%y = "addi32"(%x, %x) : (i32, i32) -> i32
}
}
return
}
Output:
$ mlir-opt -loop-unroll -unroll-factor=4 /tmp/single2.mlir
#map0 = (d0) -> (d0 + 1)
#map1 = (d0) -> (d0 + 2)
#map2 = (d0) -> (d0 + 3)
mlfunc @loop_nest_unroll_cleanup() {
for %i0 = 1 to 100 {
for %i1 = 0 to 17 step 4 {
%0 = "addi32"(%i1, %i1) : (affineint, affineint) -> i32
%1 = "addi32"(%0, %0) : (i32, i32) -> i32
%2 = affine_apply #map0(%i1)
%3 = "addi32"(%2, %2) : (affineint, affineint) -> i32
%4 = affine_apply #map1(%i1)
%5 = "addi32"(%4, %4) : (affineint, affineint) -> i32
%6 = affine_apply #map2(%i1)
%7 = "addi32"(%6, %6) : (affineint, affineint) -> i32
}
for %i2 = 16 to 17 {
%8 = "addi32"(%i2, %i2) : (affineint, affineint) -> i32
%9 = "addi32"(%8, %8) : (i32, i32) -> i32
}
}
return
}
PiperOrigin-RevId: 209676220
2018-08-22 07:01:23 +08:00
|
|
|
// Parse step.
|
|
|
|
int64_t step = 1;
|
2018-08-25 14:38:14 +08:00
|
|
|
if (consumeIf(Token::kw_step) && parseIntConstant(step))
|
|
|
|
return ParseFailure;
|
2018-07-20 00:52:39 +08:00
|
|
|
|
2018-10-07 08:21:53 +08:00
|
|
|
// The loop step is a positive integer constant. Since index is stored as an
|
|
|
|
// int64_t type, we restrict step to be in the set of positive integers that
|
|
|
|
// int64_t can represent.
|
2018-09-28 09:03:27 +08:00
|
|
|
if (step < 1) {
|
|
|
|
return emitError("step has to be a positive integer");
|
|
|
|
}
|
|
|
|
|
2018-07-20 00:52:39 +08:00
|
|
|
// Create for statement.
|
2018-08-25 14:38:14 +08:00
|
|
|
ForStmt *forStmt =
|
|
|
|
builder.createFor(getEncodedSourceLocation(loc), lbOperands, lbMap,
|
|
|
|
ubOperands, ubMap, step);
|
2018-07-31 06:18:10 +08:00
|
|
|
|
|
|
|
// Create SSA value definition for the induction variable.
|
2018-08-07 05:19:46 +08:00
|
|
|
if (addDefinition({inductionVariableName, 0, loc}, forStmt))
|
|
|
|
return ParseFailure;
|
2018-07-17 02:47:09 +08:00
|
|
|
|
2018-07-20 00:52:39 +08:00
|
|
|
// If parsing of the for statement body fails,
|
|
|
|
// MLIR contains for statement with those nested statements that have been
|
|
|
|
// successfully parsed.
|
2018-12-24 00:17:48 +08:00
|
|
|
if (parseStmtBlock(forStmt->getBody()))
|
2018-07-17 02:47:09 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
2018-07-31 06:18:10 +08:00
|
|
|
// Reset insertion point to the current block.
|
2018-08-09 02:14:57 +08:00
|
|
|
builder.setInsertionPointToEnd(forStmt->getBlock());
|
2018-08-25 14:38:14 +08:00
|
|
|
|
2018-07-17 02:47:09 +08:00
|
|
|
return ParseSuccess;
|
2018-07-04 08:51:28 +08:00
|
|
|
}
|
|
|
|
|
2018-08-25 14:38:14 +08:00
|
|
|
/// Parse integer constant as affine constant expression.
|
|
|
|
ParseResult MLFunctionParser::parseIntConstant(int64_t &val) {
|
|
|
|
bool negate = consumeIf(Token::minus);
|
|
|
|
|
2018-07-20 00:52:39 +08:00
|
|
|
if (getToken().isNot(Token::integer))
|
2018-08-25 14:38:14 +08:00
|
|
|
return emitError("expected integer");
|
2018-07-20 00:52:39 +08:00
|
|
|
|
2018-08-25 14:38:14 +08:00
|
|
|
auto uval = getToken().getUInt64IntegerValue();
|
|
|
|
|
|
|
|
if (!uval.hasValue() || (int64_t)uval.getValue() < 0) {
|
2018-10-07 08:21:53 +08:00
|
|
|
return emitError("bound or step is too large for index");
|
2018-07-20 00:52:39 +08:00
|
|
|
}
|
2018-08-25 14:38:14 +08:00
|
|
|
|
|
|
|
val = (int64_t)uval.getValue();
|
|
|
|
if (negate)
|
|
|
|
val = -val;
|
|
|
|
consumeToken();
|
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Dimensions and symbol use list.
|
|
|
|
///
|
|
|
|
/// dim-use-list ::= `(` ssa-use-list? `)`
|
|
|
|
/// symbol-use-list ::= `[` ssa-use-list? `]`
|
|
|
|
/// dim-and-symbol-use-list ::= dim-use-list symbol-use-list?
|
|
|
|
///
|
|
|
|
ParseResult
|
|
|
|
MLFunctionParser::parseDimAndSymbolList(SmallVectorImpl<MLValue *> &operands,
|
2018-08-29 06:26:20 +08:00
|
|
|
unsigned numDims, unsigned numOperands,
|
|
|
|
const char *affineStructName) {
|
2018-08-25 14:38:14 +08:00
|
|
|
if (parseToken(Token::l_paren, "expected '('"))
|
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
SmallVector<SSAUseInfo, 4> opInfo;
|
|
|
|
parseOptionalSSAUseList(opInfo);
|
|
|
|
|
|
|
|
if (parseToken(Token::r_paren, "expected ')'"))
|
|
|
|
return ParseFailure;
|
|
|
|
|
2018-08-29 06:26:20 +08:00
|
|
|
if (numDims != opInfo.size())
|
|
|
|
return emitError("dim operand count and " + Twine(affineStructName) +
|
|
|
|
" dim count must match");
|
2018-08-25 14:38:14 +08:00
|
|
|
|
|
|
|
if (consumeIf(Token::l_square)) {
|
|
|
|
parseOptionalSSAUseList(opInfo);
|
|
|
|
if (parseToken(Token::r_square, "expected ']'"))
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
|
2018-08-29 06:26:20 +08:00
|
|
|
if (numOperands != opInfo.size())
|
|
|
|
return emitError("symbol operand count and " + Twine(affineStructName) +
|
|
|
|
" symbol count must match");
|
2018-08-25 14:38:14 +08:00
|
|
|
|
|
|
|
// Resolve SSA uses.
|
2018-10-31 05:59:22 +08:00
|
|
|
Type indexType = builder.getIndexType();
|
2018-08-25 14:38:14 +08:00
|
|
|
for (unsigned i = 0, e = opInfo.size(); i != e; ++i) {
|
2018-10-07 08:21:53 +08:00
|
|
|
SSAValue *sval = resolveSSAUse(opInfo[i], indexType);
|
2018-08-25 14:38:14 +08:00
|
|
|
if (!sval)
|
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
auto *v = cast<MLValue>(sval);
|
|
|
|
if (i < numDims && !v->isValidDim())
|
|
|
|
return emitError(opInfo[i].loc, "value '" + opInfo[i].name.str() +
|
2018-08-29 06:26:20 +08:00
|
|
|
"' cannot be used as a dimension id");
|
2018-08-25 14:38:14 +08:00
|
|
|
if (i >= numDims && !v->isValidSymbol())
|
|
|
|
return emitError(opInfo[i].loc, "value '" + opInfo[i].name.str() +
|
2018-08-29 06:26:20 +08:00
|
|
|
"' cannot be used as a symbol");
|
2018-08-25 14:38:14 +08:00
|
|
|
operands.push_back(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop bound.
|
|
|
|
///
|
2018-10-19 04:54:44 +08:00
|
|
|
/// lower-bound ::= `max`? affine-map dim-and-symbol-use-list |
|
|
|
|
/// shorthand-bound upper-bound ::= `min`? affine-map dim-and-symbol-use-list
|
|
|
|
/// | shorthand-bound shorthand-bound ::= ssa-id | `-`? integer-literal
|
2018-08-25 14:38:14 +08:00
|
|
|
///
|
|
|
|
ParseResult MLFunctionParser::parseBound(SmallVectorImpl<MLValue *> &operands,
|
2018-10-10 07:39:24 +08:00
|
|
|
AffineMap &map, bool isLower) {
|
2018-08-25 14:38:14 +08:00
|
|
|
// 'min' / 'max' prefixes are syntactic sugar. Ignore them.
|
|
|
|
if (isLower)
|
|
|
|
consumeIf(Token::kw_max);
|
|
|
|
else
|
|
|
|
consumeIf(Token::kw_min);
|
|
|
|
|
|
|
|
// Parse full form - affine map followed by dim and symbol list.
|
|
|
|
if (getToken().isAny(Token::hash_identifier, Token::l_paren)) {
|
|
|
|
map = parseAffineMapReference();
|
|
|
|
if (!map)
|
|
|
|
return ParseFailure;
|
|
|
|
|
2018-10-10 07:39:24 +08:00
|
|
|
if (parseDimAndSymbolList(operands, map.getNumDims(), map.getNumInputs(),
|
2018-09-13 01:21:23 +08:00
|
|
|
"affine map"))
|
2018-08-25 14:38:14 +08:00
|
|
|
return ParseFailure;
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse shorthand form.
|
|
|
|
if (getToken().isAny(Token::minus, Token::integer)) {
|
|
|
|
int64_t val;
|
|
|
|
if (!parseIntConstant(val)) {
|
2018-09-13 23:12:38 +08:00
|
|
|
map = builder.getConstantAffineMap(val);
|
2018-08-25 14:38:14 +08:00
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse ssa-id as identity map.
|
|
|
|
SSAUseInfo opInfo;
|
|
|
|
if (parseSSAUse(opInfo))
|
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
// TODO: improve error message when SSA value is not an affine integer.
|
|
|
|
// Currently it is 'use of value ... expects different type than prior uses'
|
2018-10-07 08:21:53 +08:00
|
|
|
if (auto *value = resolveSSAUse(opInfo, builder.getIndexType()))
|
2018-08-25 14:38:14 +08:00
|
|
|
operands.push_back(cast<MLValue>(value));
|
|
|
|
else
|
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
// Create an identity map using dim id for an induction variable and
|
|
|
|
// symbol otherwise. This representation is optimized for storage.
|
|
|
|
// Analysis passes may expand it into a multi-dimensional map if desired.
|
|
|
|
if (isa<ForStmt>(operands[0]))
|
|
|
|
map = builder.getDimIdentityMap();
|
|
|
|
else
|
|
|
|
map = builder.getSymbolIdentityMap();
|
|
|
|
|
|
|
|
return ParseSuccess;
|
2018-07-20 00:52:39 +08:00
|
|
|
}
|
|
|
|
|
2018-08-08 05:24:38 +08:00
|
|
|
/// Parse an affine constraint.
|
|
|
|
/// affine-constraint ::= affine-expr `>=` `0`
|
|
|
|
/// | affine-expr `==` `0`
|
|
|
|
///
|
2018-10-19 04:54:44 +08:00
|
|
|
/// isEq is set to true if the parsed constraint is an equality, false if it
|
|
|
|
/// is an inequality (greater than or equal).
|
2018-08-08 05:24:38 +08:00
|
|
|
///
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr AffineParser::parseAffineConstraint(bool *isEq) {
|
|
|
|
AffineExpr expr = parseAffineExpr();
|
2018-08-08 05:24:38 +08:00
|
|
|
if (!expr)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (consumeIf(Token::greater) && consumeIf(Token::equal) &&
|
|
|
|
getToken().is(Token::integer)) {
|
|
|
|
auto dim = getToken().getUnsignedIntegerValue();
|
|
|
|
if (dim.hasValue() && dim.getValue() == 0) {
|
|
|
|
consumeToken(Token::integer);
|
|
|
|
*isEq = false;
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
return (emitError("expected '0' after '>='"), nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (consumeIf(Token::equal) && consumeIf(Token::equal) &&
|
|
|
|
getToken().is(Token::integer)) {
|
|
|
|
auto dim = getToken().getUnsignedIntegerValue();
|
|
|
|
if (dim.hasValue() && dim.getValue() == 0) {
|
|
|
|
consumeToken(Token::integer);
|
|
|
|
*isEq = true;
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
return (emitError("expected '0' after '=='"), nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (emitError("expected '== 0' or '>= 0' at end of affine constraint"),
|
|
|
|
nullptr);
|
|
|
|
}
|
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
/// Parse the constraints that are part of an integer set definition.
|
2018-08-08 05:24:38 +08:00
|
|
|
/// integer-set-inline
|
2018-10-19 04:54:44 +08:00
|
|
|
/// ::= dim-and-symbol-id-lists `:`
|
|
|
|
/// affine-constraint-conjunction
|
2018-08-08 05:24:38 +08:00
|
|
|
/// affine-constraint-conjunction ::= /*empty*/
|
2018-10-19 04:54:44 +08:00
|
|
|
/// | affine-constraint (`,`
|
|
|
|
/// affine-constraint)*
|
2018-08-08 05:24:38 +08:00
|
|
|
///
|
2018-10-26 09:33:42 +08:00
|
|
|
IntegerSet AffineParser::parseIntegerSetConstraints(unsigned numDims,
|
|
|
|
unsigned numSymbols) {
|
2018-08-08 05:24:38 +08:00
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
parseToken(Token::l_paren,
|
|
|
|
"expected '(' at start of integer set constraint list");
|
2018-10-09 04:47:18 +08:00
|
|
|
SmallVector<AffineExpr, 4> constraints;
|
2018-08-08 05:24:38 +08:00
|
|
|
SmallVector<bool, 4> isEqs;
|
|
|
|
auto parseElt = [&]() -> ParseResult {
|
|
|
|
bool isEq;
|
2018-10-08 23:09:50 +08:00
|
|
|
auto elt = parseAffineConstraint(&isEq);
|
2018-08-08 05:24:38 +08:00
|
|
|
ParseResult res = elt ? ParseSuccess : ParseFailure;
|
|
|
|
if (elt) {
|
|
|
|
constraints.push_back(elt);
|
|
|
|
isEqs.push_back(isEq);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Parse a list of affine constraints (comma-separated) .
|
|
|
|
// Grammar: affine-constraint-conjunct ::= `(` affine-constraint (`,`
|
|
|
|
// affine-constraint)* `)
|
|
|
|
if (parseCommaSeparatedListUntil(Token::r_paren, parseElt, true))
|
2018-10-11 00:45:59 +08:00
|
|
|
return IntegerSet();
|
2018-08-08 05:24:38 +08:00
|
|
|
|
|
|
|
// Parsed a valid integer set.
|
|
|
|
return builder.getIntegerSet(numDims, numSymbols, constraints, isEqs);
|
|
|
|
}
|
|
|
|
|
2018-10-11 00:45:59 +08:00
|
|
|
IntegerSet Parser::parseIntegerSetInline() {
|
2018-10-26 09:33:42 +08:00
|
|
|
IntegerSet set;
|
|
|
|
AffineParser(state).parseAffineStructureInline(nullptr, &set);
|
|
|
|
return set;
|
2018-08-08 05:24:38 +08:00
|
|
|
}
|
|
|
|
|
2018-07-04 08:51:28 +08:00
|
|
|
/// If statement.
|
|
|
|
///
|
2018-07-10 10:05:38 +08:00
|
|
|
/// ml-if-head ::= `if` ml-if-cond `{` ml-stmt* `}`
|
|
|
|
/// | ml-if-head `else` `if` ml-if-cond `{` ml-stmt* `}`
|
|
|
|
/// ml-if-stmt ::= ml-if-head
|
|
|
|
/// | ml-if-head `else` `{` ml-stmt* `}`
|
2018-07-04 08:51:28 +08:00
|
|
|
///
|
2018-07-17 02:47:09 +08:00
|
|
|
ParseResult MLFunctionParser::parseIfStmt() {
|
2018-08-24 05:32:25 +08:00
|
|
|
auto loc = getToken().getLoc();
|
2018-07-04 08:51:28 +08:00
|
|
|
consumeToken(Token::kw_if);
|
2018-08-08 05:24:38 +08:00
|
|
|
|
2018-10-11 00:45:59 +08:00
|
|
|
IntegerSet set = parseIntegerSetReference();
|
2018-08-29 06:26:20 +08:00
|
|
|
if (!set)
|
2018-07-24 08:30:01 +08:00
|
|
|
return ParseFailure;
|
2018-07-04 08:51:28 +08:00
|
|
|
|
2018-08-29 06:26:20 +08:00
|
|
|
SmallVector<MLValue *, 4> operands;
|
2018-10-11 00:45:59 +08:00
|
|
|
if (parseDimAndSymbolList(operands, set.getNumDims(), set.getNumOperands(),
|
2018-08-29 06:26:20 +08:00
|
|
|
"integer set"))
|
2018-08-08 05:24:38 +08:00
|
|
|
return ParseFailure;
|
2018-07-14 04:03:13 +08:00
|
|
|
|
2018-08-29 06:26:20 +08:00
|
|
|
IfStmt *ifStmt =
|
|
|
|
builder.createIf(getEncodedSourceLocation(loc), operands, set);
|
2018-07-14 04:03:13 +08:00
|
|
|
|
2018-12-27 03:21:53 +08:00
|
|
|
StmtBlock *thenClause = ifStmt->getThen();
|
2018-07-17 02:47:09 +08:00
|
|
|
|
2018-07-20 00:52:39 +08:00
|
|
|
// When parsing of an if statement body fails, the IR contains
|
|
|
|
// the if statement with the portion of the body that has been
|
|
|
|
// successfully parsed.
|
2018-07-17 02:47:09 +08:00
|
|
|
if (parseStmtBlock(thenClause))
|
|
|
|
return ParseFailure;
|
2018-06-29 08:02:32 +08:00
|
|
|
|
2018-07-14 04:03:13 +08:00
|
|
|
if (consumeIf(Token::kw_else)) {
|
2018-08-09 02:14:57 +08:00
|
|
|
auto *elseClause = ifStmt->createElse();
|
2018-07-17 02:47:09 +08:00
|
|
|
if (parseElseClause(elseClause))
|
|
|
|
return ParseFailure;
|
2018-07-04 08:51:28 +08:00
|
|
|
}
|
|
|
|
|
2018-07-31 06:18:10 +08:00
|
|
|
// Reset insertion point to the current block.
|
2018-08-09 02:14:57 +08:00
|
|
|
builder.setInsertionPointToEnd(ifStmt->getBlock());
|
2018-07-31 06:18:10 +08:00
|
|
|
|
2018-07-17 02:47:09 +08:00
|
|
|
return ParseSuccess;
|
2018-07-14 04:03:13 +08:00
|
|
|
}
|
|
|
|
|
2018-12-27 03:21:53 +08:00
|
|
|
ParseResult MLFunctionParser::parseElseClause(StmtBlock *elseClause) {
|
2018-07-14 04:03:13 +08:00
|
|
|
if (getToken().is(Token::kw_if)) {
|
2018-08-09 02:14:57 +08:00
|
|
|
builder.setInsertionPointToEnd(elseClause);
|
2018-07-17 02:47:09 +08:00
|
|
|
return parseIfStmt();
|
2018-07-14 04:03:13 +08:00
|
|
|
}
|
|
|
|
|
2018-07-17 02:47:09 +08:00
|
|
|
return parseStmtBlock(elseClause);
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Parse a list of statements ending with `return` or `}`
|
|
|
|
///
|
|
|
|
ParseResult MLFunctionParser::parseStatements(StmtBlock *block) {
|
2018-08-08 03:02:37 +08:00
|
|
|
auto createOpFunc = [&](const OperationState &state) -> Operation * {
|
|
|
|
return builder.createOperation(state);
|
2018-07-17 02:47:09 +08:00
|
|
|
};
|
|
|
|
|
2018-08-09 02:14:57 +08:00
|
|
|
builder.setInsertionPointToEnd(block);
|
2018-07-17 02:47:09 +08:00
|
|
|
|
2018-08-10 03:28:58 +08:00
|
|
|
// Parse statements till we see '}' or 'return'.
|
|
|
|
// Return statement is parsed separately to emit a more intuitive error
|
|
|
|
// when '}' is missing after the return statement.
|
|
|
|
while (getToken().isNot(Token::r_brace, Token::kw_return)) {
|
2018-07-17 02:47:09 +08:00
|
|
|
switch (getToken().getKind()) {
|
|
|
|
default:
|
|
|
|
if (parseOperation(createOpFunc))
|
|
|
|
return ParseFailure;
|
|
|
|
break;
|
|
|
|
case Token::kw_for:
|
|
|
|
if (parseForStmt())
|
|
|
|
return ParseFailure;
|
|
|
|
break;
|
|
|
|
case Token::kw_if:
|
|
|
|
if (parseIfStmt())
|
|
|
|
return ParseFailure;
|
|
|
|
break;
|
|
|
|
} // end switch
|
|
|
|
}
|
2018-07-14 04:03:13 +08:00
|
|
|
|
2018-08-10 03:28:58 +08:00
|
|
|
// Parse the return statement.
|
|
|
|
if (getToken().is(Token::kw_return))
|
|
|
|
if (parseOperation(createOpFunc))
|
|
|
|
return ParseFailure;
|
|
|
|
|
2018-07-14 04:03:13 +08:00
|
|
|
return ParseSuccess;
|
2018-07-04 08:51:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Parse `{` ml-stmt* `}`
|
|
|
|
///
|
2018-07-14 04:03:13 +08:00
|
|
|
ParseResult MLFunctionParser::parseStmtBlock(StmtBlock *block) {
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::l_brace, "expected '{' before statement list") ||
|
|
|
|
parseStatements(block) ||
|
2018-08-10 03:28:58 +08:00
|
|
|
parseToken(Token::r_brace, "expected '}' after statement list"))
|
2018-07-17 02:47:09 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
2018-07-04 08:51:28 +08:00
|
|
|
return ParseSuccess;
|
2018-06-29 08:02:32 +08:00
|
|
|
}
|
2018-06-25 02:18:29 +08:00
|
|
|
|
2018-06-24 07:03:42 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Top-level entity parsing.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
namespace {
|
|
|
|
/// This parser handles entities that are only valid at the top level of the
|
|
|
|
/// file.
|
|
|
|
class ModuleParser : public Parser {
|
|
|
|
public:
|
|
|
|
explicit ModuleParser(ParserState &state) : Parser(state) {}
|
|
|
|
|
|
|
|
ParseResult parseModule();
|
|
|
|
|
|
|
|
private:
|
2018-08-20 12:17:22 +08:00
|
|
|
ParseResult finalizeModule();
|
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
ParseResult parseAffineStructureDef();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
// Functions.
|
2018-10-31 05:59:22 +08:00
|
|
|
ParseResult parseMLArgumentList(SmallVectorImpl<Type> &argTypes,
|
2018-07-20 00:52:39 +08:00
|
|
|
SmallVectorImpl<StringRef> &argNames);
|
2018-10-31 05:59:22 +08:00
|
|
|
ParseResult parseFunctionSignature(StringRef &name, FunctionType &type,
|
2018-07-20 00:52:39 +08:00
|
|
|
SmallVectorImpl<StringRef> *argNames);
|
2018-09-19 07:36:26 +08:00
|
|
|
ParseResult parseFunctionAttribute(SmallVectorImpl<NamedAttribute> &attrs);
|
2018-07-11 01:08:27 +08:00
|
|
|
ParseResult parseExtFunc();
|
|
|
|
ParseResult parseCFGFunc();
|
|
|
|
ParseResult parseMLFunc();
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
/// Parses either an affine map declaration or an integer set declaration.
|
|
|
|
///
|
2018-07-11 01:08:27 +08:00
|
|
|
/// Affine map declaration.
|
|
|
|
///
|
|
|
|
/// affine-map-def ::= affine-map-id `=` affine-map-inline
|
|
|
|
///
|
2018-10-26 09:33:42 +08:00
|
|
|
/// Integer set declaration.
|
|
|
|
///
|
|
|
|
/// integer-set-decl ::= integer-set-id `=` integer-set-inline
|
|
|
|
///
|
|
|
|
ParseResult ModuleParser::parseAffineStructureDef() {
|
2018-07-11 01:08:27 +08:00
|
|
|
assert(getToken().is(Token::hash_identifier));
|
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
StringRef affineStructureId = getTokenSpelling().drop_front();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
// Check for redefinitions.
|
2018-10-26 09:33:42 +08:00
|
|
|
if (getState().affineMapDefinitions.count(affineStructureId) > 0)
|
|
|
|
return emitError("redefinition of affine map id '" + affineStructureId +
|
|
|
|
"'");
|
|
|
|
if (getState().integerSetDefinitions.count(affineStructureId) > 0)
|
|
|
|
return emitError("redefinition of integer set id '" + affineStructureId +
|
|
|
|
"'");
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
consumeToken(Token::hash_identifier);
|
|
|
|
|
|
|
|
// Parse the '='
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::equal,
|
|
|
|
"expected '=' in affine map outlined definition"))
|
|
|
|
return ParseFailure;
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
AffineMap map;
|
|
|
|
IntegerSet set;
|
|
|
|
parseAffineStructureInline(&map, &set);
|
|
|
|
if (!map && !set)
|
2018-08-08 05:24:38 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
2018-10-26 09:33:42 +08:00
|
|
|
if (map)
|
|
|
|
getState().affineMapDefinitions[affineStructureId] = map;
|
|
|
|
else
|
|
|
|
getState().integerSetDefinitions[affineStructureId] = set;
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-07-20 00:52:39 +08:00
|
|
|
/// Parse a (possibly empty) list of MLFunction arguments with types.
|
|
|
|
///
|
|
|
|
/// ml-argument ::= ssa-id `:` type
|
|
|
|
/// ml-argument-list ::= ml-argument (`,` ml-argument)* | /*empty*/
|
|
|
|
///
|
|
|
|
ParseResult
|
2018-10-31 05:59:22 +08:00
|
|
|
ModuleParser::parseMLArgumentList(SmallVectorImpl<Type> &argTypes,
|
2018-07-20 00:52:39 +08:00
|
|
|
SmallVectorImpl<StringRef> &argNames) {
|
2018-07-24 08:30:01 +08:00
|
|
|
consumeToken(Token::l_paren);
|
|
|
|
|
2018-07-20 00:52:39 +08:00
|
|
|
auto parseElt = [&]() -> ParseResult {
|
|
|
|
// Parse argument name
|
|
|
|
if (getToken().isNot(Token::percent_identifier))
|
|
|
|
return emitError("expected SSA identifier");
|
|
|
|
|
2018-08-07 02:54:39 +08:00
|
|
|
StringRef name = getTokenSpelling();
|
2018-07-20 00:52:39 +08:00
|
|
|
consumeToken(Token::percent_identifier);
|
|
|
|
argNames.push_back(name);
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::colon, "expected ':'"))
|
|
|
|
return ParseFailure;
|
2018-07-20 00:52:39 +08:00
|
|
|
|
|
|
|
// Parse argument type
|
|
|
|
auto elt = parseType();
|
|
|
|
if (!elt)
|
|
|
|
return ParseFailure;
|
|
|
|
argTypes.push_back(elt);
|
|
|
|
|
|
|
|
return ParseSuccess;
|
|
|
|
};
|
|
|
|
|
2018-07-22 05:32:09 +08:00
|
|
|
return parseCommaSeparatedListUntil(Token::r_paren, parseElt);
|
2018-07-20 00:52:39 +08:00
|
|
|
}
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
/// Parse a function signature, starting with a name and including the
|
|
|
|
/// parameter list.
|
2018-07-11 01:08:27 +08:00
|
|
|
///
|
2018-07-20 00:52:39 +08:00
|
|
|
/// argument-list ::= type (`,` type)* | /*empty*/ | ml-argument-list
|
2018-10-19 04:54:44 +08:00
|
|
|
/// function-signature ::= function-id `(` argument-list `)` (`->`
|
|
|
|
/// type-list)?
|
2018-07-11 01:08:27 +08:00
|
|
|
///
|
2018-07-20 00:52:39 +08:00
|
|
|
ParseResult
|
2018-10-31 05:59:22 +08:00
|
|
|
ModuleParser::parseFunctionSignature(StringRef &name, FunctionType &type,
|
2018-07-20 00:52:39 +08:00
|
|
|
SmallVectorImpl<StringRef> *argNames) {
|
2018-07-11 01:08:27 +08:00
|
|
|
if (getToken().isNot(Token::at_identifier))
|
|
|
|
return emitError("expected a function identifier like '@foo'");
|
|
|
|
|
|
|
|
name = getTokenSpelling().drop_front();
|
|
|
|
consumeToken(Token::at_identifier);
|
|
|
|
|
|
|
|
if (getToken().isNot(Token::l_paren))
|
|
|
|
return emitError("expected '(' in function signature");
|
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
SmallVector<Type, 4> argTypes;
|
2018-07-20 00:52:39 +08:00
|
|
|
ParseResult parseResult;
|
|
|
|
|
|
|
|
if (argNames)
|
|
|
|
parseResult = parseMLArgumentList(argTypes, *argNames);
|
|
|
|
else
|
|
|
|
parseResult = parseTypeList(argTypes);
|
|
|
|
|
|
|
|
if (parseResult)
|
2018-07-11 01:08:27 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
|
|
|
// Parse the return type if present.
|
2018-10-31 05:59:22 +08:00
|
|
|
SmallVector<Type, 4> results;
|
2018-07-11 01:08:27 +08:00
|
|
|
if (consumeIf(Token::arrow)) {
|
|
|
|
if (parseTypeList(results))
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
2018-07-20 00:52:39 +08:00
|
|
|
type = builder.getFunctionType(argTypes, results);
|
2018-07-11 01:08:27 +08:00
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-09-19 07:36:26 +08:00
|
|
|
/// Parse function attributes, starting with keyword "attributes".
|
|
|
|
///
|
|
|
|
/// function-attribute ::= (`attributes` attribute-dict)?
|
|
|
|
///
|
|
|
|
ParseResult
|
|
|
|
ModuleParser::parseFunctionAttribute(SmallVectorImpl<NamedAttribute> &attrs) {
|
|
|
|
if (consumeIf(Token::kw_attributes)) {
|
|
|
|
if (parseAttributeDict(attrs)) {
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
/// External function declarations.
|
|
|
|
///
|
|
|
|
/// ext-func ::= `extfunc` function-signature
|
2018-09-19 07:36:26 +08:00
|
|
|
/// (`attributes` attribute-dict)?
|
2018-07-11 01:08:27 +08:00
|
|
|
///
|
|
|
|
ParseResult ModuleParser::parseExtFunc() {
|
|
|
|
consumeToken(Token::kw_extfunc);
|
2018-08-18 07:49:42 +08:00
|
|
|
auto loc = getToken().getLoc();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
StringRef name;
|
2018-10-31 05:59:22 +08:00
|
|
|
FunctionType type;
|
2018-07-20 00:52:39 +08:00
|
|
|
if (parseFunctionSignature(name, type, /*arguments*/ nullptr))
|
2018-07-11 01:08:27 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
2018-09-19 07:36:26 +08:00
|
|
|
SmallVector<NamedAttribute, 8> attrs;
|
|
|
|
if (parseFunctionAttribute(attrs)) {
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
// Okay, the external function definition was parsed correctly.
|
2018-09-19 07:36:26 +08:00
|
|
|
auto *function =
|
2018-12-28 03:07:34 +08:00
|
|
|
new Function(Function::Kind::ExtFunc, getEncodedSourceLocation(loc), name,
|
|
|
|
type, attrs);
|
2018-08-18 07:49:42 +08:00
|
|
|
getModule()->getFunctions().push_back(function);
|
|
|
|
|
|
|
|
// Verify no name collision / redefinition.
|
2018-08-20 12:17:22 +08:00
|
|
|
if (function->getName() != name)
|
2018-08-18 07:49:42 +08:00
|
|
|
return emitError(loc,
|
|
|
|
"redefinition of function named '" + name.str() + "'");
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// CFG function declarations.
|
|
|
|
///
|
2018-09-19 07:36:26 +08:00
|
|
|
/// cfg-func ::= `cfgfunc` function-signature
|
|
|
|
/// (`attributes` attribute-dict)? `{` basic-block+ `}`
|
2018-07-11 01:08:27 +08:00
|
|
|
///
|
|
|
|
ParseResult ModuleParser::parseCFGFunc() {
|
|
|
|
consumeToken(Token::kw_cfgfunc);
|
2018-08-18 07:49:42 +08:00
|
|
|
auto loc = getToken().getLoc();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
StringRef name;
|
2018-10-31 05:59:22 +08:00
|
|
|
FunctionType type;
|
2018-07-20 00:52:39 +08:00
|
|
|
if (parseFunctionSignature(name, type, /*arguments*/ nullptr))
|
2018-07-11 01:08:27 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
2018-09-19 07:36:26 +08:00
|
|
|
SmallVector<NamedAttribute, 8> attrs;
|
|
|
|
if (parseFunctionAttribute(attrs)) {
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
// Okay, the CFG function signature was parsed correctly, create the
|
|
|
|
// function.
|
2018-09-19 07:36:26 +08:00
|
|
|
auto *function =
|
2018-12-28 03:07:34 +08:00
|
|
|
new Function(Function::Kind::CFGFunc, getEncodedSourceLocation(loc), name,
|
|
|
|
type, attrs);
|
2018-08-18 07:49:42 +08:00
|
|
|
getModule()->getFunctions().push_back(function);
|
|
|
|
|
|
|
|
// Verify no name collision / redefinition.
|
2018-08-20 12:17:22 +08:00
|
|
|
if (function->getName() != name)
|
2018-08-18 07:49:42 +08:00
|
|
|
return emitError(loc,
|
|
|
|
"redefinition of function named '" + name.str() + "'");
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
return CFGFunctionParser(getState(), function).parseFunctionBody();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// ML function declarations.
|
|
|
|
///
|
2018-09-19 07:36:26 +08:00
|
|
|
/// ml-func ::= `mlfunc` ml-func-signature
|
2018-10-19 04:54:44 +08:00
|
|
|
/// (`attributes` attribute-dict)? `{` ml-stmt* ml-return-stmt
|
|
|
|
/// `}`
|
2018-07-11 01:08:27 +08:00
|
|
|
///
|
|
|
|
ParseResult ModuleParser::parseMLFunc() {
|
|
|
|
consumeToken(Token::kw_mlfunc);
|
|
|
|
|
|
|
|
StringRef name;
|
2018-10-31 05:59:22 +08:00
|
|
|
FunctionType type;
|
2018-07-20 00:52:39 +08:00
|
|
|
SmallVector<StringRef, 4> argNames;
|
|
|
|
|
2018-08-07 02:54:39 +08:00
|
|
|
auto loc = getToken().getLoc();
|
2018-07-20 00:52:39 +08:00
|
|
|
if (parseFunctionSignature(name, type, &argNames))
|
2018-07-11 01:08:27 +08:00
|
|
|
return ParseFailure;
|
|
|
|
|
2018-09-19 07:36:26 +08:00
|
|
|
SmallVector<NamedAttribute, 8> attrs;
|
|
|
|
if (parseFunctionAttribute(attrs)) {
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
// Okay, the ML function signature was parsed correctly, create the
|
|
|
|
// function.
|
2018-12-28 03:07:34 +08:00
|
|
|
auto *function = new Function(
|
|
|
|
Function::Kind::MLFunc, getEncodedSourceLocation(loc), name, type, attrs);
|
2018-08-18 07:49:42 +08:00
|
|
|
getModule()->getFunctions().push_back(function);
|
|
|
|
|
|
|
|
// Verify no name collision / redefinition.
|
2018-08-20 12:17:22 +08:00
|
|
|
if (function->getName() != name)
|
2018-08-18 07:49:42 +08:00
|
|
|
return emitError(loc,
|
|
|
|
"redefinition of function named '" + name.str() + "'");
|
2018-08-07 02:54:39 +08:00
|
|
|
|
|
|
|
// Create the parser.
|
|
|
|
auto parser = MLFunctionParser(getState(), function);
|
|
|
|
|
|
|
|
// Add definitions of the function arguments.
|
|
|
|
for (unsigned i = 0, e = function->getNumArguments(); i != e; ++i) {
|
|
|
|
if (parser.addDefinition({argNames[i], 0, loc}, function->getArgument(i)))
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2018-08-07 02:54:39 +08:00
|
|
|
return parser.parseFunctionBody();
|
2018-07-11 01:08:27 +08:00
|
|
|
}
|
|
|
|
|
2018-08-20 12:17:22 +08:00
|
|
|
/// Finish the end of module parsing - when the result is valid, do final
|
|
|
|
/// checking.
|
|
|
|
ParseResult ModuleParser::finalizeModule() {
|
|
|
|
|
2018-08-21 23:42:19 +08:00
|
|
|
// Resolve all forward references, building a remapping table of attributes.
|
2018-10-26 06:46:10 +08:00
|
|
|
DenseMap<Attribute, FunctionAttr> remappingTable;
|
2018-08-20 12:17:22 +08:00
|
|
|
for (auto forwardRef : getState().functionForwardRefs) {
|
|
|
|
auto name = forwardRef.first;
|
|
|
|
|
|
|
|
// Resolve the reference.
|
|
|
|
auto *resolvedFunction = getModule()->getNamedFunction(name);
|
2018-09-08 00:08:13 +08:00
|
|
|
if (!resolvedFunction) {
|
|
|
|
forwardRef.second->emitError("reference to undefined function '" +
|
|
|
|
name.str() + "'");
|
|
|
|
return ParseFailure;
|
|
|
|
}
|
2018-08-20 12:17:22 +08:00
|
|
|
|
2018-09-08 00:08:13 +08:00
|
|
|
remappingTable[builder.getFunctionAttr(forwardRef.second)] =
|
2018-08-21 23:42:19 +08:00
|
|
|
builder.getFunctionAttr(resolvedFunction);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there was nothing to remap, then we're done.
|
|
|
|
if (remappingTable.empty())
|
|
|
|
return ParseSuccess;
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
// Otherwise, walk the entire module replacing uses of one attribute set
|
|
|
|
// with the correct ones.
|
2018-11-15 06:26:47 +08:00
|
|
|
remapFunctionAttrs(*getModule(), remappingTable);
|
2018-08-20 12:17:22 +08:00
|
|
|
|
2018-08-21 23:42:19 +08:00
|
|
|
// Now that all references to the forward definition placeholders are
|
|
|
|
// resolved, we can deallocate the placeholders.
|
|
|
|
for (auto forwardRef : getState().functionForwardRefs)
|
2018-12-28 03:07:34 +08:00
|
|
|
delete forwardRef.second;
|
2018-10-10 05:40:41 +08:00
|
|
|
getState().functionForwardRefs.clear();
|
2018-08-20 12:17:22 +08:00
|
|
|
return ParseSuccess;
|
|
|
|
}
|
|
|
|
|
2018-06-23 01:39:19 +08:00
|
|
|
/// This is the top-level module parser.
|
2018-07-11 01:08:27 +08:00
|
|
|
ParseResult ModuleParser::parseModule() {
|
2018-06-23 01:39:19 +08:00
|
|
|
while (1) {
|
2018-07-10 10:05:38 +08:00
|
|
|
switch (getToken().getKind()) {
|
2018-06-23 01:39:19 +08:00
|
|
|
default:
|
|
|
|
emitError("expected a top level entity");
|
2018-07-11 01:08:27 +08:00
|
|
|
return ParseFailure;
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2018-07-04 11:16:08 +08:00
|
|
|
// If we got to the end of the file, then we're done.
|
2018-06-23 01:39:19 +08:00
|
|
|
case Token::eof:
|
2018-08-20 12:17:22 +08:00
|
|
|
return finalizeModule();
|
2018-06-23 01:39:19 +08:00
|
|
|
|
|
|
|
// If we got an error token, then the lexer already emitted an error, just
|
2018-10-19 04:54:44 +08:00
|
|
|
// stop. Someday we could introduce error recovery if there was demand
|
|
|
|
// for it.
|
2018-06-23 01:39:19 +08:00
|
|
|
case Token::error:
|
2018-07-11 01:08:27 +08:00
|
|
|
return ParseFailure;
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
case Token::hash_identifier:
|
2018-10-26 09:33:42 +08:00
|
|
|
if (parseAffineStructureDef())
|
2018-08-08 05:24:38 +08:00
|
|
|
return ParseFailure;
|
|
|
|
break;
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
case Token::kw_extfunc:
|
|
|
|
if (parseExtFunc())
|
|
|
|
return ParseFailure;
|
2018-06-23 01:39:19 +08:00
|
|
|
break;
|
2018-07-04 11:16:08 +08:00
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
case Token::kw_cfgfunc:
|
|
|
|
if (parseCFGFunc())
|
|
|
|
return ParseFailure;
|
2018-06-28 02:03:08 +08:00
|
|
|
break;
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2018-06-29 08:02:32 +08:00
|
|
|
case Token::kw_mlfunc:
|
2018-07-11 01:08:27 +08:00
|
|
|
if (parseMLFunc())
|
|
|
|
return ParseFailure;
|
2018-06-29 08:02:32 +08:00
|
|
|
break;
|
2018-06-23 01:39:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// This parses the file specified by the indicated SourceMgr and returns an
|
2018-10-19 04:54:44 +08:00
|
|
|
/// MLIR module if it was valid. If not, it emits diagnostics and returns
|
|
|
|
/// null.
|
2018-09-09 09:37:27 +08:00
|
|
|
Module *mlir::parseSourceFile(const llvm::SourceMgr &sourceMgr,
|
2018-09-03 13:01:45 +08:00
|
|
|
MLIRContext *context) {
|
2018-08-21 23:42:19 +08:00
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
// This is the result module we are parsing into.
|
|
|
|
std::unique_ptr<Module> module(new Module(context));
|
|
|
|
|
2018-09-03 13:01:45 +08:00
|
|
|
ParserState state(sourceMgr, module.get());
|
2018-08-21 23:42:19 +08:00
|
|
|
if (ModuleParser(state).parseModule()) {
|
2018-07-11 01:08:27 +08:00
|
|
|
return nullptr;
|
2018-08-21 23:42:19 +08:00
|
|
|
}
|
2018-07-07 01:46:19 +08:00
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
// Make sure the parse module has no other structural problems detected by
|
|
|
|
// the verifier.
|
2018-09-08 00:08:13 +08:00
|
|
|
if (module->verify())
|
2018-08-21 23:42:19 +08:00
|
|
|
return nullptr;
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
return module.release();
|
2018-06-23 01:39:19 +08:00
|
|
|
}
|
2018-09-03 22:38:31 +08:00
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
/// This parses the program string to a MLIR module if it was valid. If not,
|
|
|
|
/// it emits diagnostics and returns null.
|
2018-09-03 22:38:31 +08:00
|
|
|
Module *mlir::parseSourceString(StringRef moduleStr, MLIRContext *context) {
|
|
|
|
auto memBuffer = MemoryBuffer::getMemBuffer(moduleStr);
|
|
|
|
if (!memBuffer)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
SourceMgr sourceMgr;
|
|
|
|
sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
|
|
|
|
return parseSourceFile(sourceMgr, context);
|
|
|
|
}
|