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-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"
|
2019-01-04 06:29:52 +08:00
|
|
|
#include "mlir/IR/StandardTypes.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-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-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
|
|
|
|
2019-05-07 01:36:32 +08:00
|
|
|
// A map from attribute alias identifier to Attribute.
|
|
|
|
llvm::StringMap<Attribute> attributeAliasDefinitions;
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2019-01-08 10:42:04 +08:00
|
|
|
// A map from type alias identifier to Type.
|
|
|
|
llvm::StringMap<Type> typeAliasDefinitions;
|
|
|
|
|
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 {
|
|
|
|
|
|
|
|
/// 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.
|
2019-05-09 01:29:50 +08:00
|
|
|
InFlightDiagnostic 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
|
|
|
}
|
2019-05-09 01:29:50 +08:00
|
|
|
InFlightDiagnostic 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();
|
2019-02-08 00:36:50 +08:00
|
|
|
ParseResult parseDimensionListRanked(SmallVectorImpl<int64_t> &dimensions,
|
|
|
|
bool allowDynamic);
|
2019-01-08 10:42:04 +08:00
|
|
|
Type parseExtendedType();
|
2019-05-16 00:10:52 +08:00
|
|
|
ParseResult parsePrettyDialectSymbolName(StringRef &prettyName);
|
2018-10-31 05:59:22 +08:00
|
|
|
Type parseTensorType();
|
2019-03-30 13:23:34 +08:00
|
|
|
Type parseComplexType();
|
2019-03-20 01:59:02 +08:00
|
|
|
Type parseTupleType();
|
2018-10-31 05:59:22 +08:00
|
|
|
Type parseMemRefType();
|
|
|
|
Type parseFunctionType();
|
2019-02-06 03:47:02 +08:00
|
|
|
Type parseNonFunctionType();
|
2018-10-31 05:59:22 +08:00
|
|
|
Type parseType();
|
|
|
|
ParseResult parseTypeListNoParens(SmallVectorImpl<Type> &elements);
|
2019-02-06 03:47:02 +08:00
|
|
|
ParseResult parseTypeListParens(SmallVectorImpl<Type> &elements);
|
|
|
|
ParseResult parseFunctionResultTypes(SmallVectorImpl<Type> &elements);
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
// Attribute parsing.
|
2019-05-16 00:10:52 +08:00
|
|
|
Attribute parseExtendedAttribute(Type 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.
|
2019-01-27 02:41:17 +08:00
|
|
|
ParseResult parseAffineMapOrIntegerSetReference(AffineMap &map,
|
|
|
|
IntegerSet &set);
|
2019-05-16 15:12:45 +08:00
|
|
|
DenseElementsAttr parseDenseElementsAttr(ShapedType type);
|
2019-02-08 02:13:50 +08:00
|
|
|
DenseElementsAttr parseDenseElementsAttrAsTensor(Type eltType);
|
2019-05-30 04:46:41 +08:00
|
|
|
ShapedType parseElementsLiteralType();
|
2018-06-28 02:03:08 +08:00
|
|
|
|
2019-01-24 05:11:23 +08:00
|
|
|
// Location Parsing.
|
|
|
|
|
|
|
|
/// Trailing locations.
|
|
|
|
///
|
|
|
|
/// trailing-location ::= location?
|
|
|
|
///
|
|
|
|
template <typename Owner>
|
|
|
|
ParseResult parseOptionalTrailingLocation(Owner *owner) {
|
|
|
|
// If there is a 'loc' we parse a trailing location.
|
|
|
|
if (!getToken().is(Token::kw_loc))
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
|
|
|
// Parse the location.
|
|
|
|
llvm::Optional<Location> directLoc;
|
|
|
|
if (parseLocation(&directLoc))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
owner->setLoc(*directLoc);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-24 05:11:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse an inline location.
|
|
|
|
ParseResult parseLocation(llvm::Optional<Location> *loc);
|
|
|
|
|
|
|
|
/// Parse a raw location instance.
|
|
|
|
ParseResult parseLocationInstance(llvm::Optional<Location> *loc);
|
|
|
|
|
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.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-05-09 01:29:50 +08:00
|
|
|
InFlightDiagnostic Parser::emitError(SMLoc loc, const Twine &message) {
|
|
|
|
auto diag = getContext()->emitError(getEncodedSourceLocation(loc), 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))
|
2019-05-09 01:29:50 +08:00
|
|
|
diag.abandon();
|
|
|
|
return diag;
|
2018-06-23 01:39:19 +08:00
|
|
|
}
|
|
|
|
|
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))
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-24 08:30:01 +08:00
|
|
|
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())
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-22 05:32:09 +08:00
|
|
|
|
|
|
|
// Otherwise we have a list of comma separated elements.
|
|
|
|
while (consumeIf(Token::comma)) {
|
|
|
|
if (parseElement())
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-22 05:32:09 +08:00
|
|
|
}
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-22 05:32:09 +08:00
|
|
|
}
|
|
|
|
|
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);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseCommaSeparatedList(parseElement) ||
|
|
|
|
parseToken(rightToken, "expected ',' or '" +
|
|
|
|
Token::getTokenSpelling(rightToken) + "'"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
2018-06-23 01:39:19 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Type Parsing
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-02-06 03:47:02 +08:00
|
|
|
/// Parse any type except the function 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
|
|
|
///
|
2019-02-06 03:47:02 +08:00
|
|
|
/// non-function-type ::= integer-type
|
|
|
|
/// | index-type
|
|
|
|
/// | float-type
|
|
|
|
/// | extended-type
|
|
|
|
/// | vector-type
|
|
|
|
/// | tensor-type
|
|
|
|
/// | memref-type
|
2019-03-30 13:23:34 +08:00
|
|
|
/// | complex-type
|
2019-03-20 01:59:02 +08:00
|
|
|
/// | tuple-type
|
2019-04-28 09:35:04 +08:00
|
|
|
/// | none-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`
|
2019-04-28 09:35:04 +08:00
|
|
|
/// none-type ::= `none`
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
2019-02-06 03:47:02 +08:00
|
|
|
Type Parser::parseNonFunctionType() {
|
2018-07-10 10:05:38 +08:00
|
|
|
switch (getToken().getKind()) {
|
2018-06-23 13:03:48 +08:00
|
|
|
default:
|
2019-02-06 03:47:02 +08:00
|
|
|
return (emitError("expected non-function 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();
|
2019-03-30 13:23:34 +08:00
|
|
|
case Token::kw_complex:
|
|
|
|
return parseComplexType();
|
2019-03-20 01:59:02 +08:00
|
|
|
case Token::kw_tuple:
|
|
|
|
return parseTupleType();
|
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_vector:
|
|
|
|
return parseVectorType();
|
|
|
|
// 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
|
|
|
|
2019-04-28 09:35:04 +08:00
|
|
|
// none-type
|
|
|
|
case Token::kw_none:
|
|
|
|
consumeToken(Token::kw_none);
|
|
|
|
return builder.getNoneType();
|
|
|
|
|
2019-01-08 10:42:04 +08:00
|
|
|
// extended type
|
2019-01-03 06:16:40 +08:00
|
|
|
case Token::exclamation_identifier:
|
2019-01-08 10:42:04 +08:00
|
|
|
return parseExtendedType();
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-06 03:47:02 +08:00
|
|
|
/// Parse an arbitrary type.
|
|
|
|
///
|
|
|
|
/// type ::= function-type
|
|
|
|
/// | non-function-type
|
|
|
|
///
|
|
|
|
Type Parser::parseType() {
|
|
|
|
if (getToken().is(Token::l_paren))
|
|
|
|
return parseFunctionType();
|
|
|
|
return parseNonFunctionType();
|
|
|
|
}
|
|
|
|
|
2018-06-23 06:52:02 +08:00
|
|
|
/// Parse a vector type.
|
|
|
|
///
|
2019-02-08 00:36:50 +08:00
|
|
|
/// vector-type ::= `vector` `<` static-dimension-list primitive-type `>`
|
|
|
|
/// static-dimension-list ::= (decimal-literal `x`)+
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
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
|
|
|
|
2019-01-24 06:39:45 +08:00
|
|
|
SmallVector<int64_t, 4> dimensions;
|
2019-02-08 00:36:50 +08:00
|
|
|
if (parseDimensionListRanked(dimensions, /*allowDynamic=*/false))
|
|
|
|
return nullptr;
|
|
|
|
if (dimensions.empty())
|
|
|
|
return (emitError("expected dimension size in vector type"), 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();
|
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);
|
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-09-14 01:43:35 +08:00
|
|
|
}
|
|
|
|
|
2018-06-23 06:52:02 +08:00
|
|
|
/// Parse a dimension list of a tensor or memref type. This populates the
|
2019-02-08 00:36:50 +08:00
|
|
|
/// dimension list, using -1 for the `?` dimensions if `allowDynamic` is set and
|
|
|
|
/// errors out on `?` otherwise.
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
|
|
|
/// dimension-list-ranked ::= (dimension `x`)*
|
2019-02-08 00:36:50 +08:00
|
|
|
/// dimension ::= `?` | decimal-literal
|
|
|
|
///
|
|
|
|
/// When `allowDynamic` is not set, this can be also used to parse
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
2019-02-08 00:36:50 +08:00
|
|
|
/// static-dimension-list ::= (decimal-literal `x`)*
|
2019-01-24 06:39:45 +08:00
|
|
|
ParseResult
|
2019-02-08 00:36:50 +08:00
|
|
|
Parser::parseDimensionListRanked(SmallVectorImpl<int64_t> &dimensions,
|
|
|
|
bool allowDynamic = true) {
|
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)) {
|
2019-02-08 00:36:50 +08:00
|
|
|
if (!allowDynamic)
|
|
|
|
return emitError("expected static shape");
|
2018-06-23 06:52:02 +08:00
|
|
|
dimensions.push_back(-1);
|
|
|
|
} else {
|
2019-02-08 00:36:50 +08:00
|
|
|
// Hexadecimal integer literals (starting with `0x`) are not allowed in
|
|
|
|
// aggregate type declarations. Therefore, `0xf32` should be processed as
|
|
|
|
// a sequence of separate elements `0`, `x`, `f32`.
|
|
|
|
if (getTokenSpelling().size() > 1 && getTokenSpelling()[1] == 'x') {
|
|
|
|
// We can get here only if the token is an integer literal. Hexadecimal
|
|
|
|
// integer literals can only start with `0x` (`1x` wouldn't lex as a
|
|
|
|
// literal, just `1` would, at which point we don't get into this
|
|
|
|
// branch).
|
|
|
|
assert(getTokenSpelling()[0] == '0' && "invalid integer literal");
|
|
|
|
dimensions.push_back(0);
|
|
|
|
state.lex.resetPointer(getTokenSpelling().data() + 1);
|
|
|
|
consumeToken();
|
|
|
|
} else {
|
|
|
|
// Make sure this integer value is in bound and valid.
|
|
|
|
auto dimension = getToken().getUnsignedIntegerValue();
|
|
|
|
if (!dimension.hasValue())
|
|
|
|
return emitError("invalid dimension");
|
|
|
|
dimensions.push_back((int64_t)dimension.getValue());
|
|
|
|
consumeToken(Token::integer);
|
|
|
|
}
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure we have an 'x' or something like 'xbf32'.
|
2018-09-14 01:43:35 +08:00
|
|
|
if (parseXInDimensionList())
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
/// Parse the body of a pretty dialect symbol, which starts and ends with <>'s,
|
2019-04-06 10:36:42 +08:00
|
|
|
/// and may be recursive. Return with the 'prettyName' StringRef encompasing
|
|
|
|
/// the entire pretty name.
|
|
|
|
///
|
2019-05-16 00:10:52 +08:00
|
|
|
/// pretty-dialect-sym-body ::= '<' pretty-dialect-sym-contents+ '>'
|
|
|
|
/// pretty-dialect-sym-contents ::= pretty-dialect-sym-body
|
|
|
|
/// | '(' pretty-dialect-sym-contents+ ')'
|
|
|
|
/// | '[' pretty-dialect-sym-contents+ ']'
|
|
|
|
/// | '{' pretty-dialect-sym-contents+ '}'
|
2019-04-15 04:41:09 +08:00
|
|
|
/// | '[^[<({>\])}\0]+'
|
2019-04-06 10:36:42 +08:00
|
|
|
///
|
2019-05-16 00:10:52 +08:00
|
|
|
ParseResult Parser::parsePrettyDialectSymbolName(StringRef &prettyName) {
|
|
|
|
// Pretty symbol names are a relatively unstructured format that contains a
|
2019-04-15 04:41:09 +08:00
|
|
|
// series of properly nested punctuation, with anything else in the middle.
|
|
|
|
// Scan ahead to find it and consume it if successful, otherwise emit an
|
|
|
|
// error.
|
|
|
|
auto *curPtr = getTokenSpelling().data();
|
|
|
|
|
|
|
|
SmallVector<char, 8> nestedPunctuation;
|
|
|
|
|
|
|
|
// Scan over the nested punctuation, bailing out on error and consuming until
|
|
|
|
// we find the end. We know that we're currently looking at the '<', so we
|
|
|
|
// can go until we find the matching '>' character.
|
|
|
|
assert(*curPtr == '<');
|
|
|
|
do {
|
|
|
|
char c = *curPtr++;
|
|
|
|
switch (c) {
|
|
|
|
case '\0':
|
|
|
|
// This also handles the EOF case.
|
|
|
|
return emitError("unexpected nul or EOF in pretty dialect name");
|
|
|
|
case '<':
|
|
|
|
case '[':
|
|
|
|
case '(':
|
|
|
|
case '{':
|
|
|
|
nestedPunctuation.push_back(c);
|
|
|
|
continue;
|
2019-04-06 10:36:42 +08:00
|
|
|
|
2019-04-15 04:41:09 +08:00
|
|
|
case '>':
|
|
|
|
if (nestedPunctuation.pop_back_val() != '<')
|
|
|
|
return emitError("unbalanced '>' character in pretty dialect name");
|
|
|
|
break;
|
|
|
|
case ']':
|
|
|
|
if (nestedPunctuation.pop_back_val() != '[')
|
|
|
|
return emitError("unbalanced ']' character in pretty dialect name");
|
|
|
|
break;
|
|
|
|
case ')':
|
|
|
|
if (nestedPunctuation.pop_back_val() != '(')
|
|
|
|
return emitError("unbalanced ')' character in pretty dialect name");
|
|
|
|
break;
|
|
|
|
case '}':
|
|
|
|
if (nestedPunctuation.pop_back_val() != '{')
|
|
|
|
return emitError("unbalanced '}' character in pretty dialect name");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2019-04-06 10:36:42 +08:00
|
|
|
continue;
|
|
|
|
}
|
2019-04-15 04:41:09 +08:00
|
|
|
} while (!nestedPunctuation.empty());
|
2019-04-06 10:36:42 +08:00
|
|
|
|
2019-04-15 04:41:09 +08:00
|
|
|
// Ok, we succeeded, remember where we stopped, reset the lexer to know it is
|
|
|
|
// consuming all this stuff, and return.
|
|
|
|
state.lex.resetPointer(curPtr);
|
2019-04-06 10:36:42 +08:00
|
|
|
|
2019-04-15 04:41:09 +08:00
|
|
|
unsigned length = curPtr - prettyName.begin();
|
|
|
|
prettyName = StringRef(prettyName.begin(), length);
|
|
|
|
consumeToken();
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-04-06 10:36:42 +08:00
|
|
|
}
|
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
/// Parse an extended dialect symbol.
|
|
|
|
template <typename Symbol, typename SymbolAliasMap, typename CreateFn>
|
|
|
|
static Symbol parseExtendedSymbol(Parser &p, Token::Kind identifierTok,
|
|
|
|
SymbolAliasMap &aliases,
|
|
|
|
CreateFn &&createSymbol) {
|
2019-01-03 06:16:40 +08:00
|
|
|
// Parse the dialect namespace.
|
2019-05-16 00:10:52 +08:00
|
|
|
StringRef identifier = p.getTokenSpelling().drop_front();
|
|
|
|
auto loc = p.getToken().getLoc();
|
|
|
|
p.consumeToken(identifierTok);
|
2019-01-03 06:16:40 +08:00
|
|
|
|
2019-04-06 10:36:42 +08:00
|
|
|
// If there is no '<' token following this, and if the typename contains no
|
2019-05-16 00:10:52 +08:00
|
|
|
// dot, then we are parsing a symbol alias.
|
|
|
|
if (p.getToken().isNot(Token::less) && !identifier.contains('.')) {
|
2019-01-08 10:42:04 +08:00
|
|
|
// Check for an alias for this type.
|
2019-05-16 00:10:52 +08:00
|
|
|
auto aliasIt = aliases.find(identifier);
|
|
|
|
if (aliasIt == aliases.end())
|
|
|
|
return (p.emitError("undefined symbol alias id '" + identifier + "'"),
|
2019-01-08 10:42:04 +08:00
|
|
|
nullptr);
|
|
|
|
return aliasIt->second;
|
|
|
|
}
|
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
// Otherwise, we are parsing a dialect-specific symbol. If the name contains
|
|
|
|
// a dot, then this is the "pretty" form. If not, it is the verbose form that
|
2019-04-06 10:36:42 +08:00
|
|
|
// looks like <"...">.
|
2019-05-16 00:10:52 +08:00
|
|
|
std::string symbolData;
|
2019-04-06 10:36:42 +08:00
|
|
|
auto dialectName = identifier;
|
2019-01-03 06:16:40 +08:00
|
|
|
|
2019-04-06 10:36:42 +08:00
|
|
|
// Handle the verbose form, where "identifier" is a simple dialect name.
|
|
|
|
if (!identifier.contains('.')) {
|
|
|
|
// Consume the '<'.
|
2019-05-16 00:10:52 +08:00
|
|
|
if (p.parseToken(Token::less, "expected '<' in dialect type"))
|
2019-04-06 10:36:42 +08:00
|
|
|
return nullptr;
|
2019-01-03 06:16:40 +08:00
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
// Parse the symbol specific data.
|
|
|
|
if (p.getToken().isNot(Token::string))
|
|
|
|
return (p.emitError("expected string literal data in dialect symbol"),
|
2019-04-06 10:36:42 +08:00
|
|
|
nullptr);
|
2019-05-16 00:10:52 +08:00
|
|
|
symbolData = p.getToken().getStringValue();
|
|
|
|
loc = p.getToken().getLoc();
|
|
|
|
p.consumeToken(Token::string);
|
2019-01-08 01:58:34 +08:00
|
|
|
|
2019-04-06 10:36:42 +08:00
|
|
|
// Consume the '>'.
|
2019-05-16 00:10:52 +08:00
|
|
|
if (p.parseToken(Token::greater, "expected '>' in dialect symbol"))
|
2019-01-08 01:58:34 +08:00
|
|
|
return nullptr;
|
|
|
|
} else {
|
2019-04-06 10:36:42 +08:00
|
|
|
// Ok, the dialect name is the part of the identifier before the dot, the
|
2019-05-16 00:10:52 +08:00
|
|
|
// part after the dot is the dialect's symbol, or the start thereof.
|
2019-04-06 10:36:42 +08:00
|
|
|
auto dotHalves = identifier.split('.');
|
|
|
|
dialectName = dotHalves.first;
|
|
|
|
auto prettyName = dotHalves.second;
|
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
// If the dialect's symbol is followed immediately by a <, then lex the body
|
2019-04-06 10:36:42 +08:00
|
|
|
// of it into prettyName.
|
2019-05-16 00:10:52 +08:00
|
|
|
if (p.getToken().is(Token::less) &&
|
|
|
|
prettyName.bytes_end() == p.getTokenSpelling().bytes_begin()) {
|
|
|
|
if (p.parsePrettyDialectSymbolName(prettyName))
|
2019-04-06 10:36:42 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
symbolData = prettyName.str();
|
2019-01-08 01:58:34 +08:00
|
|
|
}
|
2019-01-03 06:16:40 +08:00
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
// Call into the provided symbol construction function.
|
|
|
|
auto encodedLoc = p.getEncodedSourceLocation(loc);
|
|
|
|
return createSymbol(dialectName, symbolData, encodedLoc);
|
|
|
|
}
|
2019-04-06 10:36:42 +08:00
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
/// Parse an extended type.
|
|
|
|
///
|
|
|
|
/// extended-type ::= (dialect-type | type-alias)
|
|
|
|
/// dialect-type ::= `!` dialect-namespace `<` `"` type-data `"` `>`
|
|
|
|
/// dialect-type ::= `!` alias-name pretty-dialect-attribute-body?
|
|
|
|
/// type-alias ::= `!` alias-name
|
|
|
|
///
|
|
|
|
Type Parser::parseExtendedType() {
|
|
|
|
return parseExtendedSymbol<Type>(
|
|
|
|
*this, Token::exclamation_identifier, state.typeAliasDefinitions,
|
|
|
|
[&](StringRef dialectName, StringRef symbolData, Location loc) -> Type {
|
|
|
|
// If we found a registered dialect, then ask it to parse the type.
|
|
|
|
if (auto *dialect = state.context->getRegisteredDialect(dialectName))
|
|
|
|
return dialect->parseType(symbolData, loc);
|
|
|
|
|
|
|
|
// Otherwise, form a new opaque type.
|
|
|
|
return OpaqueType::getChecked(
|
|
|
|
Identifier::get(dialectName, state.context), symbolData,
|
|
|
|
state.context, loc);
|
|
|
|
});
|
2019-01-03 06:16:40 +08:00
|
|
|
}
|
|
|
|
|
2018-06-23 06:52:02 +08:00
|
|
|
/// 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;
|
2019-01-24 06:39:45 +08:00
|
|
|
SmallVector<int64_t, 4> dimensions;
|
2018-06-23 06:52:02 +08:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-03-30 13:23:34 +08:00
|
|
|
/// Parse a complex type.
|
|
|
|
///
|
|
|
|
/// complex-type ::= `complex` `<` type `>`
|
|
|
|
///
|
|
|
|
Type Parser::parseComplexType() {
|
|
|
|
consumeToken(Token::kw_complex);
|
|
|
|
|
|
|
|
// Parse the '<'.
|
|
|
|
if (parseToken(Token::less, "expected '<' in complex type"))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto typeLocation = getEncodedSourceLocation(getToken().getLoc());
|
|
|
|
auto elementType = parseType();
|
|
|
|
if (!elementType ||
|
|
|
|
parseToken(Token::greater, "expected '>' in complex type"))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return ComplexType::getChecked(elementType, typeLocation);
|
|
|
|
}
|
|
|
|
|
2019-03-20 01:59:02 +08:00
|
|
|
/// Parse a tuple type.
|
|
|
|
///
|
|
|
|
/// tuple-type ::= `tuple` `<` (type (`,` type)*)? `>`
|
|
|
|
///
|
|
|
|
Type Parser::parseTupleType() {
|
|
|
|
consumeToken(Token::kw_tuple);
|
|
|
|
|
|
|
|
// Parse the '<'.
|
|
|
|
if (parseToken(Token::less, "expected '<' in tuple type"))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
// Check for an empty tuple by directly parsing '>'.
|
|
|
|
if (consumeIf(Token::greater))
|
|
|
|
return TupleType::get(getContext());
|
|
|
|
|
|
|
|
// Parse the element types and the '>'.
|
|
|
|
SmallVector<Type, 4> types;
|
|
|
|
if (parseTypeListNoParens(types) ||
|
|
|
|
parseToken(Token::greater, "expected '>' in tuple type"))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return TupleType::get(types, getContext());
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2019-01-24 06:39:45 +08:00
|
|
|
SmallVector<int64_t, 4> dimensions;
|
2018-06-23 06:52:02 +08:00
|
|
|
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");
|
2019-05-07 01:36:32 +08:00
|
|
|
auto affineMap = parseAttribute();
|
2018-10-10 07:39:24 +08:00
|
|
|
if (!affineMap)
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-05-07 01:36:32 +08:00
|
|
|
|
|
|
|
// Verify that the parsed attribute is an affine map.
|
|
|
|
if (auto affineMapAttr = affineMap.dyn_cast<AffineMapAttr>())
|
|
|
|
affineMapComposition.push_back(affineMapAttr.getValue());
|
|
|
|
else
|
|
|
|
return emitError("expected affine map in memref type");
|
2018-07-17 00:45:22 +08:00
|
|
|
}
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-17 00:45:22 +08:00
|
|
|
};
|
|
|
|
|
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;
|
2019-02-06 03:47:02 +08:00
|
|
|
if (parseTypeListParens(arguments) ||
|
2018-07-24 08:30:01 +08:00
|
|
|
parseToken(Token::arrow, "expected '->' in function type") ||
|
2019-02-06 03:47:02 +08:00
|
|
|
parseFunctionResultTypes(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);
|
2019-05-07 13:00:08 +08:00
|
|
|
return elt ? success() : failure();
|
2018-07-23 23:42:19 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
return parseCommaSeparatedList(parseElt);
|
|
|
|
}
|
|
|
|
|
2019-02-06 03:47:02 +08:00
|
|
|
/// Parse a parenthesized list of types.
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
|
|
|
/// type-list-parens ::= `(` `)`
|
2018-07-23 23:42:19 +08:00
|
|
|
/// | `(` type-list-no-parens `)`
|
2018-06-23 06:52:02 +08:00
|
|
|
///
|
2019-02-06 03:47:02 +08:00
|
|
|
ParseResult Parser::parseTypeListParens(SmallVectorImpl<Type> &elements) {
|
|
|
|
if (parseToken(Token::l_paren, "expected '('"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-06-23 13:03:48 +08:00
|
|
|
|
2019-02-06 03:47:02 +08:00
|
|
|
// Handle empty lists.
|
|
|
|
if (getToken().is(Token::r_paren))
|
2019-05-07 13:00:08 +08:00
|
|
|
return consumeToken(), success();
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2019-02-06 03:47:02 +08:00
|
|
|
if (parseTypeListNoParens(elements) ||
|
|
|
|
parseToken(Token::r_paren, "expected ')'"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
|
|
|
return success();
|
2019-02-06 03:47:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a function result type.
|
|
|
|
///
|
|
|
|
/// function-result-type ::= type-list-parens
|
|
|
|
/// | non-function-type
|
|
|
|
///
|
|
|
|
ParseResult Parser::parseFunctionResultTypes(SmallVectorImpl<Type> &elements) {
|
|
|
|
if (getToken().is(Token::l_paren))
|
|
|
|
return parseTypeListParens(elements);
|
2018-06-23 06:52:02 +08:00
|
|
|
|
2019-02-06 03:47:02 +08:00
|
|
|
Type t = parseNonFunctionType();
|
|
|
|
if (!t)
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-02-06 03:47:02 +08:00
|
|
|
elements.push_back(t);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-06-23 06:52:02 +08:00
|
|
|
}
|
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Attribute parsing.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-10-19 04:54:44 +08:00
|
|
|
namespace {
|
|
|
|
class TensorLiteralParser {
|
|
|
|
public:
|
2019-01-18 06:11:05 +08:00
|
|
|
TensorLiteralParser(Parser &p, Type eltTy) : p(p), eltTy(eltTy) {}
|
2018-10-19 04:54:44 +08:00
|
|
|
|
2019-01-12 03:23:15 +08:00
|
|
|
ParseResult parse() {
|
2019-04-02 01:01:47 +08:00
|
|
|
if (p.getToken().is(Token::l_square)) {
|
|
|
|
return parseList(shape);
|
|
|
|
}
|
|
|
|
return parseElement();
|
2019-01-12 03:23:15 +08:00
|
|
|
}
|
2018-10-19 04:54:44 +08:00
|
|
|
|
2019-01-18 06:11:05 +08:00
|
|
|
ArrayRef<Attribute> getValues() const { return storage; }
|
2018-10-19 04:54:44 +08:00
|
|
|
|
2019-01-24 06:39:45 +08:00
|
|
|
ArrayRef<int64_t> getShape() const { return shape; }
|
2018-10-19 04:54:44 +08:00
|
|
|
|
|
|
|
private:
|
2019-04-02 01:01:47 +08:00
|
|
|
/// Parse a single element, returning failure if it isn't a valid element
|
|
|
|
/// literal. For example:
|
|
|
|
/// parseElement(1) -> Success, 1
|
|
|
|
/// parseElement([1]) -> Failure
|
|
|
|
ParseResult parseElement();
|
2018-10-19 04:54:44 +08:00
|
|
|
|
|
|
|
/// 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
|
2019-01-24 06:39:45 +08:00
|
|
|
ParseResult parseList(llvm::SmallVectorImpl<int64_t> &dims);
|
2018-10-19 04:54:44 +08:00
|
|
|
|
|
|
|
Parser &p;
|
2018-10-31 05:59:22 +08:00
|
|
|
Type eltTy;
|
2019-01-24 06:39:45 +08:00
|
|
|
SmallVector<int64_t, 4> shape;
|
2019-01-18 06:11:05 +08:00
|
|
|
std::vector<Attribute> storage;
|
2018-10-19 04:54:44 +08:00
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
2019-04-02 01:01:47 +08:00
|
|
|
ParseResult TensorLiteralParser::parseElement() {
|
2018-10-19 04:54:44 +08:00
|
|
|
switch (p.getToken().getKind()) {
|
|
|
|
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)
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
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()) {
|
2019-01-04 06:29:52 +08:00
|
|
|
case StandardTypes::BF16:
|
|
|
|
case StandardTypes::F16:
|
|
|
|
case StandardTypes::F32:
|
|
|
|
case StandardTypes::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;
|
2019-01-18 06:11:05 +08:00
|
|
|
(void)apInt;
|
2018-10-19 04:54:44 +08:00
|
|
|
break;
|
|
|
|
}
|
2019-01-04 06:29:52 +08:00
|
|
|
case StandardTypes::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();
|
2019-01-18 06:11:05 +08:00
|
|
|
if (value.getMinSignedBits() > eltTy.getIntOrFloatBitWidth())
|
2018-10-19 04:54:44 +08:00
|
|
|
return p.emitError("tensor literal element has more bits than that "
|
|
|
|
"specified in the type");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return p.emitError("expected integer or float tensor element");
|
|
|
|
}
|
2019-01-18 06:11:05 +08:00
|
|
|
storage.push_back(result);
|
2018-10-19 04:54:44 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2019-04-02 01:01:47 +08:00
|
|
|
return p.emitError("expected element literal of primitive type");
|
2018-10-19 04:54:44 +08:00
|
|
|
}
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-10-19 04:54:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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
|
2019-01-24 06:39:45 +08:00
|
|
|
ParseResult
|
|
|
|
TensorLiteralParser::parseList(llvm::SmallVectorImpl<int64_t> &dims) {
|
2018-10-19 04:54:44 +08:00
|
|
|
p.consumeToken(Token::l_square);
|
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
auto checkDims =
|
|
|
|
[&](const llvm::SmallVectorImpl<int64_t> &prevDims,
|
|
|
|
const llvm::SmallVectorImpl<int64_t> &newDims) -> ParseResult {
|
2018-10-19 04:54:44 +08:00
|
|
|
if (prevDims == newDims)
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-10-19 04:54:44 +08:00
|
|
|
return p.emitError("tensor literal is invalid; ranks are not consistent "
|
|
|
|
"between elements");
|
|
|
|
};
|
|
|
|
|
|
|
|
bool first = true;
|
2019-01-24 06:39:45 +08:00
|
|
|
llvm::SmallVector<int64_t, 4> newDims;
|
2018-10-19 04:54:44 +08:00
|
|
|
unsigned size = 0;
|
2019-05-07 13:00:08 +08:00
|
|
|
auto parseCommaSeparatedList = [&]() -> ParseResult {
|
2019-01-24 06:39:45 +08:00
|
|
|
llvm::SmallVector<int64_t, 4> thisDims;
|
2019-04-02 01:01:47 +08:00
|
|
|
if (p.getToken().getKind() == Token::l_square) {
|
|
|
|
if (parseList(thisDims))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-04-02 01:01:47 +08:00
|
|
|
} else if (parseElement()) {
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-04-02 01:01:47 +08:00
|
|
|
}
|
2018-10-19 04:54:44 +08:00
|
|
|
++size;
|
|
|
|
if (!first)
|
|
|
|
return checkDims(newDims, thisDims);
|
|
|
|
newDims = thisDims;
|
|
|
|
first = false;
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-10-19 04:54:44 +08:00
|
|
|
};
|
|
|
|
if (p.parseCommaSeparatedListUntil(Token::r_square, parseCommaSeparatedList))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-10-19 04:54:44 +08:00
|
|
|
|
|
|
|
// Return the sublists' dimensions with 'size' prepended.
|
|
|
|
dims.clear();
|
|
|
|
dims.push_back(size);
|
2019-01-08 07:06:32 +08:00
|
|
|
dims.append(newDims.begin(), newDims.end());
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-10-19 04:54:44 +08:00
|
|
|
}
|
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
/// Parse an extended attribute.
|
|
|
|
///
|
|
|
|
/// extended-attribute ::= (dialect-attribute | attribute-alias)
|
|
|
|
/// dialect-attribute ::= `#` dialect-namespace `<` `"` attr-data `"` `>`
|
|
|
|
/// dialect-attribute ::= `#` alias-name pretty-dialect-sym-body?
|
|
|
|
/// attribute-alias ::= `#` alias-name
|
|
|
|
///
|
|
|
|
Attribute Parser::parseExtendedAttribute(Type type) {
|
|
|
|
Attribute attr = parseExtendedSymbol<Attribute>(
|
|
|
|
*this, Token::hash_identifier, state.attributeAliasDefinitions,
|
|
|
|
[&](StringRef dialectName, StringRef symbolData,
|
|
|
|
Location loc) -> Attribute {
|
|
|
|
// If we found a registered dialect, then ask it to parse the attribute.
|
|
|
|
if (auto *dialect = state.context->getRegisteredDialect(dialectName))
|
|
|
|
return dialect->parseAttribute(symbolData, loc);
|
|
|
|
|
|
|
|
// Otherwise, form a new opaque attribute.
|
|
|
|
return OpaqueAttr::getChecked(
|
|
|
|
Identifier::get(dialectName, state.context), symbolData,
|
|
|
|
state.context, loc);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Ensure that the attribute has the same type as requested.
|
|
|
|
if (type && attr.getType() != type) {
|
|
|
|
emitError("attribute type different than expected: expected ")
|
|
|
|
<< type << ", but got " << attr.getType();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
/// Attribute parsing.
|
|
|
|
///
|
2019-04-26 00:56:09 +08:00
|
|
|
/// attribute-value ::= `unit`
|
|
|
|
/// | bool-literal
|
2019-02-13 04:55:40 +08:00
|
|
|
/// | integer-literal (`:` (index-type | 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
|
2019-02-13 04:55:40 +08:00
|
|
|
/// | (`splat` | `dense`) `<` (tensor-type | vector-type) `,`
|
|
|
|
/// attribute-value `>`
|
|
|
|
/// | `sparse` `<` (tensor-type | vector-type)`,`
|
|
|
|
/// attribute-value `,` attribute-value `>`
|
|
|
|
/// | `opaque` `<` dialect-namespace `,`
|
|
|
|
/// (tensor-type | vector-type) `,` hex-string-literal `>`
|
2019-05-16 00:10:52 +08:00
|
|
|
/// | extended-attribute
|
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()) {
|
2019-05-16 00:10:52 +08:00
|
|
|
case Token::hash_identifier:
|
|
|
|
return parseExtendedAttribute(type);
|
2019-04-26 00:56:09 +08:00
|
|
|
case Token::kw_unit:
|
|
|
|
consumeToken(Token::kw_unit);
|
|
|
|
return builder.getUnitAttr();
|
|
|
|
|
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);
|
2019-01-14 21:37:14 +08:00
|
|
|
auto valTok = getToken().getLoc();
|
2018-08-01 08:15:15 +08:00
|
|
|
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);
|
2019-01-14 21:37:14 +08:00
|
|
|
return FloatAttr::getChecked(type, val.getValue(),
|
|
|
|
getEncodedSourceLocation(valTok));
|
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);
|
2019-01-14 21:37:14 +08:00
|
|
|
auto valTok = getToken().getLoc();
|
2018-08-01 08:15:15 +08:00
|
|
|
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-28 08:51:09 +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 type"), nullptr);
|
2019-01-14 21:37:14 +08:00
|
|
|
return FloatAttr::getChecked(type, -val.getValue(),
|
|
|
|
getEncodedSourceLocation(valTok));
|
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());
|
2019-05-07 13:00:08 +08:00
|
|
|
return elements.back() ? success() : failure();
|
2018-07-05 11:45:39 +08:00
|
|
|
};
|
|
|
|
|
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::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;
|
2019-01-27 02:41:17 +08:00
|
|
|
if (parseAffineMapOrIntegerSetReference(map, set))
|
2019-01-29 13:23:53 +08:00
|
|
|
return nullptr;
|
2018-10-26 13:13:03 +08:00
|
|
|
if (map)
|
|
|
|
return builder.getAffineMapAttr(map);
|
2019-01-27 02:41:17 +08:00
|
|
|
assert(set);
|
|
|
|
return builder.getIntegerSetAttr(set);
|
2018-07-05 11:45:39 +08:00
|
|
|
}
|
2018-08-20 12:17:22 +08:00
|
|
|
|
|
|
|
case Token::at_identifier: {
|
2018-08-22 08:55:22 +08:00
|
|
|
auto nameStr = getTokenSpelling();
|
2018-08-20 12:17:22 +08:00
|
|
|
consumeToken(Token::at_identifier);
|
2019-05-23 04:41:23 +08:00
|
|
|
return builder.getFunctionAttr(nameStr.drop_front());
|
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;
|
2019-02-12 14:51:34 +08:00
|
|
|
|
|
|
|
if (getToken().getKind() != Token::string)
|
|
|
|
return (emitError("expected dialect namespace"), nullptr);
|
|
|
|
auto name = getToken().getStringValue();
|
|
|
|
auto *dialect = builder.getContext()->getRegisteredDialect(name);
|
|
|
|
// TODO(shpeisman): Allow for having an unknown dialect on an opaque
|
|
|
|
// attribute. Otherwise, it can't be roundtripped without having the dialect
|
|
|
|
// registered.
|
|
|
|
if (!dialect)
|
|
|
|
return (emitError("no registered dialect with namespace '" + name + "'"),
|
|
|
|
nullptr);
|
|
|
|
|
|
|
|
consumeToken(Token::string);
|
|
|
|
if (parseToken(Token::comma, "expected ','"))
|
|
|
|
return nullptr;
|
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
auto type = parseElementsLiteralType();
|
2018-10-24 04:44:04 +08:00
|
|
|
if (!type)
|
|
|
|
return nullptr;
|
2019-02-12 14:51:34 +08:00
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
if (parseToken(Token::comma, "expected ',' after elements literal type"))
|
|
|
|
return nullptr;
|
|
|
|
|
2019-01-12 01:03:34 +08:00
|
|
|
if (getToken().getKind() != Token::string)
|
|
|
|
return (emitError("opaque string should start with '0x'"), nullptr);
|
2018-10-24 04:44:04 +08:00
|
|
|
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;
|
2019-02-12 14:51:34 +08:00
|
|
|
return builder.getOpaqueElementsAttr(dialect, type, llvm::fromHex(val));
|
2018-10-24 04:44:04 +08:00
|
|
|
}
|
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;
|
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
auto type = parseElementsLiteralType();
|
2018-10-19 04:54:44 +08:00
|
|
|
if (!type)
|
2018-10-10 23:57:51 +08:00
|
|
|
return nullptr;
|
2019-05-30 04:46:41 +08:00
|
|
|
if (parseToken(Token::comma, "expected ',' after elements literal type"))
|
|
|
|
return nullptr;
|
2018-10-10 23:57:51 +08:00
|
|
|
switch (getToken().getKind()) {
|
|
|
|
case Token::floatliteral:
|
|
|
|
case Token::integer:
|
2019-03-19 04:42:55 +08:00
|
|
|
case Token::kw_false:
|
|
|
|
case Token::kw_true:
|
2018-10-10 23:57:51 +08:00
|
|
|
case Token::minus: {
|
2018-11-16 09:53:51 +08:00
|
|
|
auto scalar = parseAttribute(type.getElementType());
|
2019-01-05 03:27:58 +08:00
|
|
|
if (!scalar)
|
|
|
|
return nullptr;
|
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
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
auto type = parseElementsLiteralType();
|
2018-10-19 04:54:44 +08:00
|
|
|
if (!type)
|
|
|
|
return nullptr;
|
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
if (parseToken(Token::comma, "expected ',' after elements literal type"))
|
|
|
|
return nullptr;
|
|
|
|
|
2019-04-02 01:01:47 +08:00
|
|
|
auto attr = parseDenseElementsAttr(type);
|
|
|
|
if (!attr)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (parseToken(Token::greater, "expected '>'"))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return attr;
|
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
|
|
|
case Token::kw_sparse: {
|
|
|
|
consumeToken(Token::kw_sparse);
|
|
|
|
if (parseToken(Token::less, "Expected '<' after 'sparse'"))
|
|
|
|
return nullptr;
|
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
auto type = parseElementsLiteralType();
|
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;
|
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
if (parseToken(Token::comma, "expected ',' after elements literal type"))
|
|
|
|
return 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
|
|
|
switch (getToken().getKind()) {
|
|
|
|
case Token::l_square: {
|
|
|
|
/// Parse indices
|
2019-01-20 12:54:09 +08:00
|
|
|
auto indicesEltType = builder.getIntegerType(64);
|
2019-02-08 02:13:50 +08:00
|
|
|
auto indices = parseDenseElementsAttrAsTensor(indicesEltType);
|
2019-01-15 04:23:38 +08:00
|
|
|
if (!indices)
|
|
|
|
return 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
|
|
|
|
|
|
|
if (parseToken(Token::comma, "expected ','"))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
/// Parse values.
|
2018-10-31 05:59:22 +08:00
|
|
|
auto valuesEltType = type.getElementType();
|
2019-02-08 02:13:50 +08:00
|
|
|
auto values = parseDenseElementsAttrAsTensor(valuesEltType);
|
2019-01-12 02:30:40 +08:00
|
|
|
if (!values)
|
|
|
|
return 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
|
|
|
|
|
|
|
/// Sanity check.
|
2018-10-31 05:59:22 +08:00
|
|
|
auto valuesType = values.getType();
|
2019-04-02 01:01:47 +08:00
|
|
|
if (valuesType.getRank() != 1) {
|
|
|
|
return (emitError("expected 1-d tensor for values"), nullptr);
|
|
|
|
}
|
|
|
|
auto indicesType = indices.getType();
|
2018-10-31 05:59:22 +08:00
|
|
|
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) {
|
2019-05-09 01:29:50 +08:00
|
|
|
emitError() << "expected shape ([" << type.getShape()
|
|
|
|
<< "]); inferred shape of indices literal (["
|
|
|
|
<< indicesType.getShape()
|
|
|
|
<< "]); inferred shape of values literal (["
|
|
|
|
<< valuesType.getShape() << "])";
|
|
|
|
return 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
|
|
|
}
|
|
|
|
|
|
|
|
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)*)? `]`
|
|
|
|
///
|
2019-02-08 02:13:50 +08:00
|
|
|
/// This method returns a constructed dense elements attribute of tensor type
|
|
|
|
/// with the shape from the parsing result.
|
|
|
|
DenseElementsAttr Parser::parseDenseElementsAttrAsTensor(Type eltType) {
|
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;
|
|
|
|
|
2019-02-08 02:13:50 +08:00
|
|
|
auto 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.
|
2019-05-16 15:12:45 +08:00
|
|
|
DenseElementsAttr Parser::parseDenseElementsAttr(ShapedType type) {
|
2018-10-31 05:59:22 +08:00
|
|
|
auto eltTy = type.getElementType();
|
2018-10-19 04:54:44 +08:00
|
|
|
TensorLiteralParser literalParser(*this, eltTy);
|
|
|
|
if (literalParser.parse())
|
|
|
|
return nullptr;
|
2019-04-02 01:01:47 +08:00
|
|
|
|
2018-10-31 05:59:22 +08:00
|
|
|
if (literalParser.getShape() != type.getShape()) {
|
2019-05-09 01:29:50 +08:00
|
|
|
emitError() << "inferred shape of elements literal (["
|
|
|
|
<< literalParser.getShape() << "]) does not match type (["
|
|
|
|
<< type.getShape() << "])";
|
|
|
|
return nullptr;
|
2018-10-19 04:54:44 +08:00
|
|
|
}
|
2019-04-02 01:01:47 +08:00
|
|
|
|
2018-10-26 06:46:10 +08:00
|
|
|
return builder.getDenseElementsAttr(type, literalParser.getValues())
|
|
|
|
.cast<DenseElementsAttr>();
|
2018-10-19 04:54:44 +08:00
|
|
|
}
|
|
|
|
|
2019-05-16 15:12:45 +08:00
|
|
|
/// Shaped type for elements attribute.
|
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
|
|
|
///
|
2019-05-30 04:46:41 +08:00
|
|
|
/// elements-literal-type ::= vector-type | ranked-tensor-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
|
|
|
///
|
2019-05-25 08:46:58 +08:00
|
|
|
/// This method also checks the type has static shape.
|
2019-05-30 04:46:41 +08:00
|
|
|
ShapedType Parser::parseElementsLiteralType() {
|
|
|
|
auto type = parseType();
|
|
|
|
if (!type)
|
2018-11-10 13:24:37 +08:00
|
|
|
return nullptr;
|
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
if (!type.isa<RankedTensorType>() && !type.isa<VectorType>()) {
|
|
|
|
return (
|
|
|
|
emitError("elements literal must be a ranked tensor or vector type"),
|
|
|
|
nullptr);
|
2018-10-19 04:54:44 +08:00
|
|
|
}
|
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
auto sType = type.cast<ShapedType>();
|
|
|
|
if (!sType.hasStaticShape())
|
|
|
|
return (emitError("elements literal type must have static shape"), nullptr);
|
2018-10-19 04:54:44 +08:00
|
|
|
|
2019-05-30 04:46:41 +08:00
|
|
|
return sType;
|
2018-10-19 04:54:44 +08:00
|
|
|
}
|
|
|
|
|
2019-01-24 05:11:23 +08:00
|
|
|
/// Debug Location.
|
|
|
|
///
|
|
|
|
/// location ::= `loc` inline-location
|
|
|
|
/// inline-location ::= '(' location-inst ')'
|
|
|
|
///
|
|
|
|
ParseResult Parser::parseLocation(llvm::Optional<Location> *loc) {
|
|
|
|
assert(loc && "loc is expected to be non-null");
|
|
|
|
|
|
|
|
// Check for 'loc' identifier.
|
|
|
|
if (getToken().isNot(Token::kw_loc))
|
|
|
|
return emitError("expected location keyword");
|
|
|
|
consumeToken(Token::kw_loc);
|
|
|
|
|
|
|
|
// Parse the inline-location.
|
|
|
|
if (parseToken(Token::l_paren, "expected '(' in inline location") ||
|
|
|
|
parseLocationInstance(loc) ||
|
|
|
|
parseToken(Token::r_paren, "expected ')' in inline location"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
|
|
|
return success();
|
2019-01-24 05:11:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Specific location instances.
|
|
|
|
///
|
|
|
|
/// location-inst ::= filelinecol-location |
|
|
|
|
/// name-location |
|
|
|
|
/// callsite-location |
|
|
|
|
/// fused-location |
|
|
|
|
/// unknown-location
|
|
|
|
/// filelinecol-location ::= string-literal ':' integer-literal
|
|
|
|
/// ':' integer-literal
|
|
|
|
/// name-location ::= string-literal
|
|
|
|
/// callsite-location ::= 'callsite' '(' location-inst 'at' location-inst ')'
|
|
|
|
/// fused-location ::= fused ('<' attribute-value '>')?
|
|
|
|
/// '[' location-inst (location-inst ',')* ']'
|
|
|
|
/// unknown-location ::= 'unknown'
|
|
|
|
///
|
|
|
|
ParseResult Parser::parseLocationInstance(llvm::Optional<Location> *loc) {
|
|
|
|
auto *ctx = getContext();
|
|
|
|
|
|
|
|
// Handle either name or filelinecol locations.
|
|
|
|
if (getToken().is(Token::string)) {
|
|
|
|
auto str = getToken().getStringValue();
|
|
|
|
consumeToken(Token::string);
|
|
|
|
|
|
|
|
// If the next token is ':' this is a filelinecol location.
|
|
|
|
if (consumeIf(Token::colon)) {
|
|
|
|
// Parse the line number.
|
|
|
|
if (getToken().isNot(Token::integer))
|
|
|
|
return emitError("expected integer line number in FileLineColLoc");
|
|
|
|
auto line = getToken().getUnsignedIntegerValue();
|
|
|
|
if (!line.hasValue())
|
|
|
|
return emitError("expected integer line number in FileLineColLoc");
|
|
|
|
consumeToken(Token::integer);
|
|
|
|
|
|
|
|
// Parse the ':'.
|
|
|
|
if (parseToken(Token::colon, "expected ':' in FileLineColLoc"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
|
|
|
// Parse the column number.
|
|
|
|
if (getToken().isNot(Token::integer))
|
|
|
|
return emitError("expected integer column number in FileLineColLoc");
|
|
|
|
auto column = getToken().getUnsignedIntegerValue();
|
|
|
|
if (!column.hasValue())
|
|
|
|
return emitError("expected integer column number in FileLineColLoc");
|
|
|
|
consumeToken(Token::integer);
|
|
|
|
|
|
|
|
auto file = UniquedFilename::get(str, ctx);
|
|
|
|
*loc = FileLineColLoc::get(file, line.getValue(), column.getValue(), ctx);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-24 05:11:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, this is a NameLoc.
|
2019-05-14 05:45:48 +08:00
|
|
|
|
|
|
|
// Check for a child location.
|
|
|
|
if (consumeIf(Token::l_paren)) {
|
|
|
|
auto childSourceLoc = getToken().getLoc();
|
|
|
|
|
|
|
|
// Parse the child location.
|
|
|
|
llvm::Optional<Location> childLoc;
|
|
|
|
if (parseLocationInstance(&childLoc))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// The child must not be another NameLoc.
|
|
|
|
if (childLoc->isa<NameLoc>())
|
|
|
|
return emitError(childSourceLoc,
|
|
|
|
"child of NameLoc cannot be another NameLoc");
|
|
|
|
*loc = NameLoc::get(Identifier::get(str, ctx), *childLoc, ctx);
|
|
|
|
|
|
|
|
// Parse the closing ')'.
|
|
|
|
if (parseToken(Token::r_paren,
|
|
|
|
"expected ')' after child location of NameLoc"))
|
|
|
|
return failure();
|
|
|
|
} else {
|
|
|
|
*loc = NameLoc::get(Identifier::get(str, ctx), ctx);
|
|
|
|
}
|
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-24 05:11:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check for a 'unknown' for an unknown location.
|
|
|
|
if (getToken().is(Token::bare_identifier) &&
|
|
|
|
getToken().getSpelling() == "unknown") {
|
|
|
|
consumeToken(Token::bare_identifier);
|
|
|
|
*loc = UnknownLoc::get(ctx);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-24 05:11:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the token is 'fused', then this is a fused location.
|
|
|
|
if (getToken().is(Token::bare_identifier) &&
|
|
|
|
getToken().getSpelling() == "fused") {
|
|
|
|
consumeToken(Token::bare_identifier);
|
|
|
|
|
|
|
|
// Try to parse the optional metadata.
|
|
|
|
Attribute metadata;
|
|
|
|
if (consumeIf(Token::less)) {
|
|
|
|
metadata = parseAttribute();
|
|
|
|
if (!metadata)
|
|
|
|
return emitError("expected valid attribute metadata");
|
|
|
|
// Parse the '>' token.
|
|
|
|
if (parseToken(Token::greater,
|
|
|
|
"expected '>' after fused location metadata"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the '['.
|
|
|
|
if (parseToken(Token::l_square, "expected '[' in fused location"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
|
|
|
// Parse the internal locations.
|
|
|
|
llvm::SmallVector<Location, 4> locations;
|
|
|
|
do {
|
|
|
|
llvm::Optional<Location> newLoc;
|
|
|
|
if (parseLocationInstance(&newLoc))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
locations.push_back(*newLoc);
|
|
|
|
|
|
|
|
// Parse the ','.
|
|
|
|
} while (consumeIf(Token::comma));
|
|
|
|
|
|
|
|
// Parse the ']'.
|
|
|
|
if (parseToken(Token::r_square, "expected ']' in fused location"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
|
|
|
// Return the fused location.
|
|
|
|
if (metadata)
|
|
|
|
*loc = FusedLoc::get(locations, metadata, getContext());
|
|
|
|
else
|
|
|
|
*loc = FusedLoc::get(locations, ctx);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-24 05:11:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check for the 'callsite' signifying a callsite location.
|
|
|
|
if (getToken().is(Token::bare_identifier) &&
|
|
|
|
getToken().getSpelling() == "callsite") {
|
|
|
|
consumeToken(Token::bare_identifier);
|
|
|
|
|
|
|
|
// Parse the '('.
|
|
|
|
if (parseToken(Token::l_paren, "expected '(' in callsite location"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
|
|
|
// Parse the callee location.
|
|
|
|
llvm::Optional<Location> calleeLoc;
|
|
|
|
if (parseLocationInstance(&calleeLoc))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
|
|
|
// Parse the 'at'.
|
|
|
|
if (getToken().isNot(Token::bare_identifier) ||
|
|
|
|
getToken().getSpelling() != "at")
|
|
|
|
return emitError("expected 'at' in callsite location");
|
|
|
|
consumeToken(Token::bare_identifier);
|
|
|
|
|
|
|
|
// Parse the caller location.
|
|
|
|
llvm::Optional<Location> callerLoc;
|
|
|
|
if (parseLocationInstance(&callerLoc))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
|
|
|
// Parse the ')'.
|
|
|
|
if (parseToken(Token::r_paren, "expected ')' in callsite location"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
|
|
|
// Return the callsite location.
|
|
|
|
*loc = CallSiteLoc::get(*calleeLoc, *callerLoc, ctx);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-24 05:11:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return emitError("expected location instance");
|
|
|
|
}
|
|
|
|
|
2018-07-05 11:45:39 +08:00
|
|
|
/// Attribute dictionary.
|
|
|
|
///
|
2018-07-10 00:00:25 +08:00
|
|
|
/// attribute-dict ::= `{` `}`
|
|
|
|
/// | `{` attribute-entry (`,` attribute-entry)* `}`
|
2019-03-03 14:34:18 +08:00
|
|
|
/// 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))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
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");
|
2019-03-03 14:34:18 +08:00
|
|
|
Identifier nameId = builder.getIdentifier(getTokenSpelling());
|
2018-07-05 11:45:39 +08:00
|
|
|
consumeToken();
|
|
|
|
|
2019-04-26 00:56:09 +08:00
|
|
|
// Try to parse the ':' for the attribute value.
|
|
|
|
if (!consumeIf(Token::colon)) {
|
|
|
|
// If there is no ':', we treat this as a unit attribute.
|
|
|
|
attributes.push_back({nameId, builder.getUnitAttr()});
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-04-26 00:56:09 +08:00
|
|
|
}
|
2018-07-05 11:45:39 +08:00
|
|
|
|
|
|
|
auto attr = parseAttribute();
|
2018-07-24 07:56:32 +08:00
|
|
|
if (!attr)
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-05 11:45:39 +08:00
|
|
|
|
|
|
|
attributes.push_back({nameId, attr});
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-05 11:45:39 +08:00
|
|
|
};
|
|
|
|
|
2018-07-22 05:32:09 +08:00
|
|
|
if (parseCommaSeparatedListUntil(Token::r_brace, parseElt))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-05 11:45:39 +08:00
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-05 11:45:39 +08:00
|
|
|
}
|
|
|
|
|
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
|
|
|
AffineMap parseAffineMapRange(unsigned numDims, unsigned numSymbols);
|
2019-01-27 02:41:17 +08:00
|
|
|
ParseResult parseAffineMapOrIntegerSetInline(AffineMap &map, IntegerSet &set);
|
2018-10-26 09:33:42 +08:00
|
|
|
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);
|
2019-01-27 02:41:17 +08:00
|
|
|
ParseResult parseDimAndOptionalSymbolIdList(unsigned &numDims,
|
|
|
|
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();
|
|
|
|
|
2019-01-07 23:51:23 +08:00
|
|
|
AffineExpr getAffineBinaryOpExpr(AffineHighPrecOp op, AffineExpr lhs,
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr rhs, SMLoc opLoc);
|
2019-01-07 23:51:23 +08:00
|
|
|
AffineExpr getAffineBinaryOpExpr(AffineLowPrecOp op, AffineExpr lhs,
|
2018-10-09 04:47:18 +08:00
|
|
|
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.
|
2019-01-07 23:51:23 +08:00
|
|
|
AffineExpr AffineParser::getAffineBinaryOpExpr(AffineHighPrecOp op,
|
2018-10-09 04:47:18 +08:00
|
|
|
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;
|
|
|
|
}
|
2019-05-11 05:06:10 +08:00
|
|
|
llvm_unreachable("Unknown AffineHighPrecOp");
|
2018-07-04 11:16:08 +08:00
|
|
|
}
|
|
|
|
|
2018-07-10 00:00:25 +08:00
|
|
|
/// Create an affine binary low precedence op expression (add, sub).
|
2019-01-07 23:51:23 +08:00
|
|
|
AffineExpr AffineParser::getAffineBinaryOpExpr(AffineLowPrecOp op,
|
2018-10-09 04:47:18 +08:00
|
|
|
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;
|
|
|
|
}
|
2019-05-11 05:06:10 +08:00
|
|
|
llvm_unreachable("Unknown AffineLowPrecOp");
|
2018-07-04 11:16:08 +08:00
|
|
|
}
|
|
|
|
|
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) {
|
2019-01-07 23:51:23 +08:00
|
|
|
AffineExpr expr = getAffineBinaryOpExpr(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)
|
2019-01-07 23:51:23 +08:00
|
|
|
return getAffineBinaryOpExpr(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) {
|
2019-01-07 23:51:23 +08:00
|
|
|
AffineExpr sum = getAffineBinaryOpExpr(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 =
|
2019-01-07 23:51:23 +08:00
|
|
|
llhs ? getAffineBinaryOpExpr(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)
|
2019-01-07 23:51:23 +08:00
|
|
|
return getAffineBinaryOpExpr(llhsOp, llhs, lhs);
|
2018-07-10 00:00:25 +08:00
|
|
|
// 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)
|
2019-05-09 01:29:50 +08:00
|
|
|
return emitError("redefinition of identifier '" + name + "'");
|
2018-07-26 03:55:50 +08:00
|
|
|
}
|
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});
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
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,
|
2019-01-27 02:41:17 +08:00
|
|
|
"expected '(' at start of dimensional identifiers list")) {
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-27 02:41:17 +08:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2019-01-27 02:41:17 +08:00
|
|
|
/// Parse the list of symbolic identifiers to an affine map.
|
|
|
|
ParseResult AffineParser::parseSymbolIdList(unsigned &numSymbols) {
|
|
|
|
consumeToken(Token::l_square);
|
|
|
|
auto parseElt = [&]() -> ParseResult {
|
|
|
|
auto symbol = getAffineSymbolExpr(numSymbols++, getContext());
|
|
|
|
return parseIdentifierDefinition(symbol);
|
|
|
|
};
|
|
|
|
return parseCommaSeparatedListUntil(Token::r_square, parseElt);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse the list of symbolic identifiers to an affine map.
|
|
|
|
ParseResult
|
|
|
|
AffineParser::parseDimAndOptionalSymbolIdList(unsigned &numDims,
|
|
|
|
unsigned &numSymbols) {
|
|
|
|
if (parseDimIdList(numDims)) {
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-27 02:41:17 +08:00
|
|
|
}
|
|
|
|
if (!getToken().is(Token::l_square)) {
|
|
|
|
numSymbols = 0;
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-27 02:41:17 +08:00
|
|
|
}
|
|
|
|
return parseSymbolIdList(numSymbols);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses an ambiguous affine map or integer set definition inline.
|
|
|
|
ParseResult AffineParser::parseAffineMapOrIntegerSetInline(AffineMap &map,
|
|
|
|
IntegerSet &set) {
|
|
|
|
unsigned numDims = 0, numSymbols = 0;
|
|
|
|
|
|
|
|
// List of dimensional and optional symbol identifiers.
|
|
|
|
if (parseDimAndOptionalSymbolIdList(numDims, numSymbols)) {
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
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.
|
2019-01-27 02:41:17 +08:00
|
|
|
bool isArrow = getToken().is(Token::arrow);
|
|
|
|
bool isColon = getToken().is(Token::colon);
|
|
|
|
if (!isArrow && !isColon) {
|
2019-02-02 16:07:30 +08:00
|
|
|
return emitError("expected '->' or ':'");
|
2019-01-27 02:41:17 +08:00
|
|
|
} else if (isArrow) {
|
|
|
|
parseToken(Token::arrow, "expected '->' or '['");
|
|
|
|
map = parseAffineMapRange(numDims, numSymbols);
|
2019-05-07 13:00:08 +08:00
|
|
|
return map ? success() : failure();
|
2019-01-27 02:41:17 +08:00
|
|
|
} else if (parseToken(Token::colon, "expected ':' or '['")) {
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-10-26 09:33:42 +08:00
|
|
|
}
|
2019-01-27 02:41:17 +08:00
|
|
|
|
|
|
|
if ((set = parseIntegerSetConstraints(numDims, numSymbols)))
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-27 02:41:17 +08:00
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-10-26 09:33:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse the range and sizes affine map definition inline.
|
|
|
|
///
|
2019-05-07 01:36:32 +08:00
|
|
|
/// affine-map ::= dim-and-symbol-id-lists `->` multi-dim-affine-expr
|
2018-10-26 09:33:42 +08:00
|
|
|
///
|
|
|
|
/// 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();
|
2019-05-07 13:00:08 +08:00
|
|
|
ParseResult res = elt ? success() : failure();
|
2018-06-30 09:09:29 +08:00
|
|
|
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))
|
2019-01-27 02:41:17 +08:00
|
|
|
return AffineMap();
|
2018-06-30 09:09:29 +08:00
|
|
|
|
2018-07-04 11:16:08 +08:00
|
|
|
// Parsed a valid affine map.
|
2019-05-30 05:56:41 +08:00
|
|
|
return builder.getAffineMap(numDims, numSymbols, exprs);
|
2018-06-28 02:03:08 +08:00
|
|
|
}
|
|
|
|
|
2019-01-27 02:41:17 +08:00
|
|
|
/// Parse an ambiguous reference to either and affine map or an integer set.
|
|
|
|
ParseResult Parser::parseAffineMapOrIntegerSetReference(AffineMap &map,
|
|
|
|
IntegerSet &set) {
|
2019-05-07 01:36:32 +08:00
|
|
|
return AffineParser(state).parseAffineMapOrIntegerSetInline(map, 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-12-29 10:41:31 +08:00
|
|
|
/// This builder intentionally shadows the builder in the base class, with a
|
|
|
|
/// more specific builder type.
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wshadow-field"
|
|
|
|
FuncBuilder builder;
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
|
|
|
|
FunctionParser(ParserState &state, Function *function)
|
|
|
|
: Parser(state), builder(function), function(function) {}
|
|
|
|
|
|
|
|
~FunctionParser();
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
ParseResult parseFunctionBody(bool hadNamedArguments);
|
2018-12-29 10:41:31 +08:00
|
|
|
|
|
|
|
/// Parse a single operation successor and it's operand list.
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseSuccessorAndUseList(Block *&dest,
|
|
|
|
SmallVectorImpl<Value *> &operands);
|
2018-12-29 10:41:31 +08:00
|
|
|
|
2019-01-10 04:28:30 +08:00
|
|
|
/// Parse a comma-separated list of operation successors in brackets.
|
|
|
|
ParseResult
|
|
|
|
parseSuccessors(SmallVectorImpl<Block *> &destinations,
|
|
|
|
SmallVectorImpl<SmallVector<Value *, 4>> &operands);
|
|
|
|
|
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-12-29 10:41:31 +08:00
|
|
|
ParseResult finalizeFunction(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-12-28 06:35:10 +08:00
|
|
|
Value *resolveSSAUse(SSAUseInfo useInfo, Type type);
|
2018-07-19 23:35:28 +08:00
|
|
|
|
|
|
|
/// Register a definition of a value with the symbol table.
|
2018-12-28 06:35:10 +08:00
|
|
|
ParseResult addDefinition(SSAUseInfo useInfo, Value *value);
|
2018-07-19 23:35:28 +08:00
|
|
|
|
|
|
|
// 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
|
|
|
|
2018-12-28 06:35:10 +08:00
|
|
|
Value *parseSSAUseAndType() {
|
|
|
|
return parseSSADefOrUseAndType<Value *>(
|
|
|
|
[&](SSAUseInfo useInfo, Type type) -> Value * {
|
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
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
// Block references.
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2019-02-02 08:42:18 +08:00
|
|
|
ParseResult
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
parseOperationRegion(Region ®ion,
|
2019-03-15 01:38:44 +08:00
|
|
|
ArrayRef<std::pair<SSAUseInfo, Type>> entryArguments);
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
ParseResult parseRegionBody(Region ®ion);
|
2019-02-02 08:42:18 +08:00
|
|
|
ParseResult parseBlock(Block *&block);
|
|
|
|
ParseResult parseBlockBody(Block *block);
|
|
|
|
|
|
|
|
ParseResult
|
|
|
|
parseOptionalBlockArgList(SmallVectorImpl<BlockArgument *> &results,
|
|
|
|
Block *owner);
|
|
|
|
|
|
|
|
/// Cleans up the memory for allocated blocks when a parser error occurs.
|
|
|
|
void cleanupInvalidBlocks(ArrayRef<Block *> invalidBlocks) {
|
|
|
|
// Add the referenced blocks to the function so that they can be properly
|
|
|
|
// cleaned up when the function is destroyed.
|
|
|
|
for (auto *block : invalidBlocks)
|
|
|
|
function->push_back(block);
|
|
|
|
}
|
|
|
|
|
2018-12-29 13:24:30 +08:00
|
|
|
/// Get the block with the specified name, creating it if it doesn't
|
2018-12-29 10:41:31 +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.
|
|
|
|
Block *getBlockNamed(StringRef name, SMLoc loc);
|
2018-11-16 01:56:06 +08:00
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
// Define the block with the specified name. Returns the Block* or
|
2018-12-29 10:41:31 +08:00
|
|
|
// nullptr in the case of redefinition.
|
2018-12-30 05:36:59 +08:00
|
|
|
Block *defineBlockNamed(StringRef name, SMLoc loc, Block *existing);
|
2018-07-27 09:09:20 +08:00
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
// Operations
|
|
|
|
ParseResult parseOperation();
|
2019-03-28 23:24:38 +08:00
|
|
|
Operation *parseGenericOperation();
|
|
|
|
Operation *parseCustomOperation();
|
2018-12-29 10:41:31 +08:00
|
|
|
|
2019-03-28 23:24:38 +08:00
|
|
|
ParseResult parseOperations(Block *block);
|
2018-10-14 22:55:29 +08:00
|
|
|
|
2019-04-27 05:46:13 +08:00
|
|
|
/// Return the location of the value identified by its name and number if it
|
|
|
|
/// has been already defined. Placeholder values are considered undefined.
|
|
|
|
llvm::Optional<SMLoc> getDefinitionLoc(StringRef name, unsigned number) {
|
|
|
|
if (!values.count(name) || number >= values[name].size())
|
|
|
|
return {};
|
|
|
|
Value *value = values[name][number].first;
|
|
|
|
if (value && !isForwardReferencePlaceholder(value))
|
|
|
|
return values[name][number].second;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
private:
|
2018-12-29 10:41:31 +08:00
|
|
|
Function *function;
|
|
|
|
|
|
|
|
// This keeps track of the block names as well as the location of the first
|
|
|
|
// reference, used to diagnose invalid block references and memoize them.
|
|
|
|
llvm::StringMap<std::pair<Block *, SMLoc>> blocksByName;
|
|
|
|
DenseMap<Block *, SMLoc> forwardRef;
|
|
|
|
|
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.
|
2018-12-28 06:35:10 +08:00
|
|
|
llvm::StringMap<SmallVector<std::pair<Value *, SMLoc>, 1>> values;
|
2018-07-21 09:41:34 +08:00
|
|
|
|
|
|
|
/// 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.
|
2018-12-28 06:35:10 +08:00
|
|
|
DenseMap<Value *, SMLoc> forwardReferencePlaceholders;
|
2018-07-21 09:41:34 +08:00
|
|
|
|
2018-12-28 06:35:10 +08:00
|
|
|
Value *createForwardReferencePlaceholder(SMLoc loc, Type type);
|
2018-07-21 09:41:34 +08:00
|
|
|
|
|
|
|
/// Return true if this is a forward reference.
|
2018-12-28 06:35:10 +08:00
|
|
|
bool isForwardReferencePlaceholder(Value *value) {
|
2018-07-21 09:41:34 +08:00
|
|
|
return forwardReferencePlaceholders.count(value);
|
|
|
|
}
|
2018-07-19 23:35:28 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
ParseResult FunctionParser::parseFunctionBody(bool hadNamedArguments) {
|
2018-12-29 10:41:31 +08:00
|
|
|
auto braceLoc = getToken().getLoc();
|
|
|
|
if (parseToken(Token::l_brace, "expected '{' in function"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-29 10:41:31 +08:00
|
|
|
|
|
|
|
// Make sure we have at least one block.
|
|
|
|
if (getToken().is(Token::r_brace))
|
|
|
|
return emitError("function must have a body");
|
|
|
|
|
2018-12-30 05:36:59 +08:00
|
|
|
// If we had named arguments, then we don't allow a block name.
|
2018-12-30 01:01:59 +08:00
|
|
|
if (hadNamedArguments) {
|
2018-12-30 05:36:59 +08:00
|
|
|
if (getToken().is(Token::caret_identifier))
|
|
|
|
return emitError("invalid block name in function with named arguments");
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
|
|
|
|
2018-12-30 05:36:59 +08:00
|
|
|
// The first block is already created and should be filled in.
|
|
|
|
auto firstBlock = &function->front();
|
|
|
|
|
2019-01-26 04:48:25 +08:00
|
|
|
// Parse the first block.
|
|
|
|
if (parseBlock(firstBlock))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-30 01:01:59 +08:00
|
|
|
|
2019-01-26 04:48:25 +08:00
|
|
|
// Parse the remaining list of blocks.
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
if (parseRegionBody(function->getBody()))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-30 05:36:59 +08:00
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
// Verify that all referenced blocks were defined.
|
|
|
|
if (!forwardRef.empty()) {
|
|
|
|
SmallVector<std::pair<const char *, Block *>, 4> errors;
|
|
|
|
// Iteration over the map isn't deterministic, so sort by source location.
|
2019-01-26 04:48:25 +08:00
|
|
|
for (auto entry : forwardRef) {
|
2018-12-29 10:41:31 +08:00
|
|
|
errors.push_back({entry.second.getPointer(), entry.first});
|
2019-01-26 04:48:25 +08:00
|
|
|
cleanupInvalidBlocks(entry.first);
|
|
|
|
}
|
2018-12-29 10:41:31 +08:00
|
|
|
llvm::array_pod_sort(errors.begin(), errors.end());
|
|
|
|
|
|
|
|
for (auto entry : errors) {
|
|
|
|
auto loc = SMLoc::getFromPointer(entry.first);
|
2018-12-30 01:01:59 +08:00
|
|
|
emitError(loc, "reference to an undefined block");
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return finalizeFunction(braceLoc);
|
|
|
|
}
|
|
|
|
|
2019-01-26 04:48:25 +08:00
|
|
|
/// Block list.
|
|
|
|
///
|
|
|
|
/// block-list ::= '{' block-list-body
|
|
|
|
///
|
2019-03-15 01:38:44 +08:00
|
|
|
ParseResult FunctionParser::parseOperationRegion(
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
Region ®ion,
|
2019-02-02 08:42:18 +08:00
|
|
|
ArrayRef<std::pair<FunctionParser::SSAUseInfo, Type>> entryArguments) {
|
2019-01-26 04:48:25 +08:00
|
|
|
// Parse the '{'.
|
2019-03-15 01:38:44 +08:00
|
|
|
if (parseToken(Token::l_brace, "expected '{' to begin a region"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-02-02 08:42:18 +08:00
|
|
|
|
2019-03-15 01:38:44 +08:00
|
|
|
// Check for an empty region.
|
2019-02-02 08:42:18 +08:00
|
|
|
if (entryArguments.empty() && consumeIf(Token::r_brace))
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-26 04:48:25 +08:00
|
|
|
Block *currentBlock = builder.getInsertionBlock();
|
|
|
|
|
|
|
|
// Parse the first block directly to allow for it to be unnamed.
|
|
|
|
Block *block = new Block();
|
2019-02-02 08:42:18 +08:00
|
|
|
|
|
|
|
// Add arguments to the entry block.
|
|
|
|
for (auto &placeholderArgPair : entryArguments)
|
|
|
|
if (addDefinition(placeholderArgPair.first,
|
|
|
|
block->addArgument(placeholderArgPair.second))) {
|
|
|
|
delete block;
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-02-02 08:42:18 +08:00
|
|
|
}
|
|
|
|
|
2019-01-26 04:48:25 +08:00
|
|
|
if (parseBlock(block)) {
|
2019-02-02 08:42:18 +08:00
|
|
|
delete block;
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-26 04:48:25 +08:00
|
|
|
}
|
2019-02-02 08:42:18 +08:00
|
|
|
|
|
|
|
// Verify that no other arguments were parsed.
|
|
|
|
if (!entryArguments.empty() &&
|
|
|
|
block->getNumArguments() > entryArguments.size()) {
|
|
|
|
delete block;
|
|
|
|
return emitError("entry block arguments were already defined");
|
|
|
|
}
|
2019-01-26 04:48:25 +08:00
|
|
|
|
2019-03-15 01:38:44 +08:00
|
|
|
// Parse the rest of the region.
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
region.push_back(block);
|
|
|
|
if (parseRegionBody(region))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-26 04:48:25 +08:00
|
|
|
|
|
|
|
// Reset insertion point to the current block.
|
|
|
|
builder.setInsertionPointToEnd(currentBlock);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-26 04:48:25 +08:00
|
|
|
}
|
|
|
|
|
2019-03-15 01:38:44 +08:00
|
|
|
/// Region.
|
2019-01-26 04:48:25 +08:00
|
|
|
///
|
2019-03-15 01:38:44 +08:00
|
|
|
/// region-body ::= block* '}'
|
2019-01-26 04:48:25 +08:00
|
|
|
///
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
ParseResult FunctionParser::parseRegionBody(Region ®ion) {
|
2019-03-15 01:38:44 +08:00
|
|
|
// Parse the list of blocks.
|
2019-01-26 04:48:25 +08:00
|
|
|
while (!consumeIf(Token::r_brace)) {
|
|
|
|
Block *newBlock = nullptr;
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
if (parseBlock(newBlock))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
region.push_back(newBlock);
|
2019-01-26 04:48:25 +08:00
|
|
|
}
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-26 04:48:25 +08:00
|
|
|
}
|
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
/// Block declaration.
|
|
|
|
///
|
2019-03-28 23:24:38 +08:00
|
|
|
/// block ::= block-label? operation* terminator-op
|
2018-12-29 10:41:31 +08:00
|
|
|
/// block-label ::= block-id block-arg-list? `:`
|
2018-12-30 03:32:37 +08:00
|
|
|
/// block-id ::= caret-id
|
2018-12-29 10:41:31 +08:00
|
|
|
/// block-arg-list ::= `(` ssa-id-and-type-list? `)`
|
|
|
|
///
|
2019-01-26 04:48:25 +08:00
|
|
|
ParseResult FunctionParser::parseBlock(Block *&block) {
|
2018-12-30 05:36:59 +08:00
|
|
|
// The first block for a function is already created.
|
|
|
|
if (block) {
|
|
|
|
// The name for a first block is optional.
|
|
|
|
if (getToken().isNot(Token::caret_identifier))
|
|
|
|
return parseBlockBody(block);
|
|
|
|
}
|
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
SMLoc nameLoc = getToken().getLoc();
|
|
|
|
auto name = getTokenSpelling();
|
2018-12-30 03:32:37 +08:00
|
|
|
if (parseToken(Token::caret_identifier, "expected block name"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-29 10:41:31 +08:00
|
|
|
|
2018-12-30 05:36:59 +08:00
|
|
|
block = defineBlockNamed(name, nameLoc, block);
|
2018-12-29 10:41:31 +08:00
|
|
|
|
|
|
|
// Fail if redefinition.
|
|
|
|
if (!block)
|
|
|
|
return emitError(nameLoc, "redefinition of block '" + name.str() + "'");
|
|
|
|
|
|
|
|
// If an argument list is present, parse it.
|
|
|
|
if (consumeIf(Token::l_paren)) {
|
|
|
|
SmallVector<BlockArgument *, 8> bbArgs;
|
|
|
|
if (parseOptionalBlockArgList(bbArgs, block) ||
|
|
|
|
parseToken(Token::r_paren, "expected ')' to end argument list"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
if (parseToken(Token::colon, "expected ':' after block name"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-29 10:41:31 +08:00
|
|
|
|
|
|
|
return parseBlockBody(block);
|
|
|
|
}
|
|
|
|
|
|
|
|
ParseResult FunctionParser::parseBlockBody(Block *block) {
|
|
|
|
|
|
|
|
// Set the insertion point to the block we want to insert new operations
|
|
|
|
// into.
|
|
|
|
builder.setInsertionPointToEnd(block);
|
|
|
|
|
|
|
|
// Parse the list of operations that make up the body of the block.
|
2019-05-27 04:01:39 +08:00
|
|
|
while (getToken().isNot(Token::caret_identifier, Token::r_brace))
|
|
|
|
if (parseOperation())
|
|
|
|
return failure();
|
2018-12-29 10:41:31 +08:00
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
|
|
|
|
2018-07-21 09:41:34 +08:00
|
|
|
/// Create and remember a new placeholder for a forward reference.
|
2018-12-28 06:35:10 +08:00
|
|
|
Value *FunctionParser::createForwardReferencePlaceholder(SMLoc loc, Type type) {
|
2019-03-28 23:24:38 +08:00
|
|
|
// Forward references are always created as operations, even in ML
|
2018-07-21 09:41:34 +08:00
|
|
|
// 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());
|
2019-03-28 23:24:38 +08:00
|
|
|
auto *op = Operation::create(
|
2019-01-26 04:48:25 +08:00
|
|
|
getEncodedSourceLocation(loc), name, /*operands=*/{}, type,
|
2019-03-15 04:56:45 +08:00
|
|
|
/*attributes=*/llvm::None, /*successors=*/{}, /*numRegions=*/0,
|
2019-02-03 14:46:43 +08:00
|
|
|
/*resizableOperandList=*/false, getContext());
|
2019-03-28 23:24:38 +08:00
|
|
|
forwardReferencePlaceholders[op->getResult(0)] = loc;
|
|
|
|
return op->getResult(0);
|
2018-07-21 09:41:34 +08:00
|
|
|
}
|
|
|
|
|
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-12-28 06:35:10 +08:00
|
|
|
Value *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;
|
|
|
|
|
2019-05-14 02:43:17 +08:00
|
|
|
emitError(useInfo.loc, "use of value '")
|
|
|
|
.append(useInfo.name, "' expects different type than prior uses")
|
|
|
|
.attachNote(getEncodedSourceLocation(entries[useInfo.number].second))
|
|
|
|
.append("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);
|
|
|
|
|
2019-01-02 23:00:07 +08:00
|
|
|
// Otherwise, this is a forward reference. Create a placeholder and remember
|
2018-08-01 14:14:16 +08:00
|
|
|
// that we did so.
|
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
|
|
|
}
|
|
|
|
|
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-12-29 10:41:31 +08:00
|
|
|
ParseResult FunctionParser::finalizeFunction(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()) {
|
2018-12-28 06:35:10 +08:00
|
|
|
SmallVector<std::pair<const char *, Value *>, 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");
|
|
|
|
}
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-21 09:41:34 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
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
|
2019-03-28 23:24:38 +08:00
|
|
|
// defining operation.
|
2019-01-08 00:16:49 +08:00
|
|
|
fwd.first->dropAllUses();
|
2019-03-27 08:05:09 +08:00
|
|
|
fwd.first->getDefiningOp()->destroy();
|
2018-10-14 22:55:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
/// Register a definition of a value with the symbol table.
|
|
|
|
ParseResult FunctionParser::addDefinition(SSAUseInfo useInfo, Value *value) {
|
|
|
|
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)) {
|
2019-05-14 02:43:17 +08:00
|
|
|
emitError(useInfo.loc)
|
|
|
|
.append("redefinition of SSA value '", useInfo.name, "'")
|
|
|
|
.attachNote(getEncodedSourceLocation(entries[useInfo.number].second))
|
|
|
|
.append("previously defined here");
|
|
|
|
return failure();
|
2018-12-29 10:41:31 +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.
|
|
|
|
existing->replaceAllUsesWith(value);
|
2019-03-27 08:05:09 +08:00
|
|
|
existing->getDefiningOp()->destroy();
|
2018-12-29 10:41:31 +08:00
|
|
|
forwardReferencePlaceholders.erase(existing);
|
|
|
|
}
|
|
|
|
|
|
|
|
entries[useInfo.number].first = value;
|
|
|
|
entries[useInfo.number].second = useInfo.loc;
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
|
|
|
|
2019-03-28 23:24:38 +08:00
|
|
|
/// Parse a SSA operand for an operation.
|
2018-07-08 06:48:26 +08:00
|
|
|
///
|
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"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-21 09:41:34 +08:00
|
|
|
|
2019-05-16 00:10:52 +08:00
|
|
|
// If we have an attribute ID, it is a result number.
|
2018-07-21 09:41:34 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
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))
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-22 05:32:09 +08:00
|
|
|
return parseCommaSeparatedList([&]() -> ParseResult {
|
2018-07-19 23:35:28 +08:00
|
|
|
SSAUseInfo result;
|
|
|
|
if (parseSSAUse(result))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-19 23:35:28 +08:00
|
|
|
results.push_back(result);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-19 23:35:28 +08:00
|
|
|
});
|
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))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-24 02:56:17 +08:00
|
|
|
|
|
|
|
// If there were no operands, then there is no colon or type lists.
|
|
|
|
if (valueIDs.empty())
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-24 02:56:17 +08:00
|
|
|
|
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))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-24 02:56:17 +08:00
|
|
|
|
|
|
|
if (valueIDs.size() != types.size())
|
2019-05-09 01:29:50 +08:00
|
|
|
return emitError("expected ")
|
|
|
|
<< valueIDs.size() << " types to match operand list";
|
2018-07-24 02:56:17 +08:00
|
|
|
|
|
|
|
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
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-24 02:56:17 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-08 06:48:26 +08:00
|
|
|
}
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
/// Get the block with the specified name, creating it if it doesn't already
|
|
|
|
/// exist. The location specified is the point of use, which allows
|
2018-12-29 10:41:31 +08:00
|
|
|
/// us to diagnose references to blocks that are not defined precisely.
|
|
|
|
Block *FunctionParser::getBlockNamed(StringRef name, SMLoc loc) {
|
|
|
|
auto &blockAndLoc = blocksByName[name];
|
|
|
|
if (!blockAndLoc.first) {
|
|
|
|
blockAndLoc.first = new Block();
|
|
|
|
forwardRef[blockAndLoc.first] = loc;
|
|
|
|
blockAndLoc.second = loc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return blockAndLoc.first;
|
|
|
|
}
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
/// Define the block with the specified name. Returns the Block* or nullptr in
|
|
|
|
/// the case of redefinition.
|
2018-12-30 05:36:59 +08:00
|
|
|
Block *FunctionParser::defineBlockNamed(StringRef name, SMLoc loc,
|
|
|
|
Block *existing) {
|
2018-12-29 10:41:31 +08:00
|
|
|
auto &blockAndLoc = blocksByName[name];
|
|
|
|
if (!blockAndLoc.first) {
|
2018-12-30 05:36:59 +08:00
|
|
|
// If the caller provided a block, use it. Otherwise create a new one.
|
|
|
|
if (!existing)
|
2019-01-26 04:48:25 +08:00
|
|
|
existing = new Block();
|
2018-12-30 05:36:59 +08:00
|
|
|
blockAndLoc.first = existing;
|
2018-12-29 10:41:31 +08:00
|
|
|
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;
|
|
|
|
return blockAndLoc.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a single operation successor and it's operand list.
|
|
|
|
///
|
|
|
|
/// successor ::= block-id branch-use-list?
|
|
|
|
/// branch-use-list ::= `(` ssa-use-list ':' type-list-no-parens `)`
|
|
|
|
///
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult
|
|
|
|
FunctionParser::parseSuccessorAndUseList(Block *&dest,
|
|
|
|
SmallVectorImpl<Value *> &operands) {
|
2018-12-29 10:41:31 +08:00
|
|
|
// Verify branch is identifier and get the matching block.
|
2018-12-30 03:32:37 +08:00
|
|
|
if (!getToken().is(Token::caret_identifier))
|
2019-05-07 13:01:31 +08:00
|
|
|
return emitError("expected block name");
|
2018-12-29 10:41:31 +08:00
|
|
|
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"))) {
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
|
|
|
|
2019-01-10 04:28:30 +08:00
|
|
|
/// Parse a comma-separated list of operation successors in brackets.
|
|
|
|
///
|
|
|
|
/// successor-list ::= `[` successor (`,` successor )* `]`
|
|
|
|
///
|
|
|
|
ParseResult FunctionParser::parseSuccessors(
|
|
|
|
SmallVectorImpl<Block *> &destinations,
|
|
|
|
SmallVectorImpl<SmallVector<Value *, 4>> &operands) {
|
|
|
|
if (parseToken(Token::l_square, "expected '['"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-10 04:28:30 +08:00
|
|
|
|
|
|
|
auto parseElt = [this, &destinations, &operands]() {
|
|
|
|
Block *dest;
|
|
|
|
SmallVector<Value *, 4> destOperands;
|
2019-05-07 13:01:31 +08:00
|
|
|
auto res = parseSuccessorAndUseList(dest, destOperands);
|
2019-01-10 04:28:30 +08:00
|
|
|
destinations.push_back(dest);
|
|
|
|
operands.push_back(destOperands);
|
2019-05-07 13:01:31 +08:00
|
|
|
return res;
|
2019-01-10 04:28:30 +08:00
|
|
|
};
|
|
|
|
return parseCommaSeparatedListUntil(Token::r_square, parseElt,
|
|
|
|
/*allowEmptyList=*/false);
|
|
|
|
}
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
/// Parse a (possibly empty) list of SSA operands with types as block arguments.
|
2018-12-29 10:41:31 +08:00
|
|
|
///
|
|
|
|
/// ssa-id-and-type-list ::= ssa-id-and-type (`,` ssa-id-and-type)*
|
|
|
|
///
|
|
|
|
ParseResult FunctionParser::parseOptionalBlockArgList(
|
|
|
|
SmallVectorImpl<BlockArgument *> &results, Block *owner) {
|
|
|
|
if (getToken().is(Token::r_brace))
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-12-29 10:41:31 +08:00
|
|
|
|
2018-12-30 05:36:59 +08:00
|
|
|
// If the block already has arguments, then we're handling the entry block.
|
|
|
|
// Parse and register the names for the arguments, but do not add them.
|
|
|
|
bool definingExistingArgs = owner->getNumArguments() != 0;
|
|
|
|
unsigned nextArgument = 0;
|
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
return parseCommaSeparatedList([&]() -> ParseResult {
|
|
|
|
auto type = parseSSADefOrUseAndType<Type>(
|
|
|
|
[&](SSAUseInfo useInfo, Type type) -> Type {
|
2018-12-30 05:36:59 +08:00
|
|
|
BlockArgument *arg;
|
|
|
|
if (!definingExistingArgs) {
|
|
|
|
arg = owner->addArgument(type);
|
|
|
|
} else if (nextArgument >= owner->getNumArguments()) {
|
|
|
|
emitError("too many arguments specified in argument list");
|
|
|
|
return {};
|
|
|
|
} else {
|
|
|
|
arg = owner->getArgument(nextArgument++);
|
|
|
|
if (arg->getType() != type) {
|
|
|
|
emitError("argument and block argument type mismatch");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
if (addDefinition(useInfo, arg))
|
|
|
|
return {};
|
|
|
|
return type;
|
|
|
|
});
|
2019-05-07 13:00:08 +08:00
|
|
|
return type ? success() : failure();
|
2018-12-29 10:41:31 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse an operation.
|
2018-07-17 02:47:09 +08:00
|
|
|
///
|
|
|
|
/// operation ::=
|
2019-03-29 05:58:52 +08:00
|
|
|
/// operation-result? string '(' ssa-use-list? ')' attribute-dict?
|
2019-01-24 05:11:23 +08:00
|
|
|
/// `:` function-type trailing-location?
|
2019-03-29 05:58:52 +08:00
|
|
|
/// operation-result ::= ssa-id ((`:` integer-literal) | (`,` ssa-id)*) `=`
|
2018-07-17 02:47:09 +08:00
|
|
|
///
|
2018-12-29 10:41:31 +08:00
|
|
|
ParseResult FunctionParser::parseOperation() {
|
2018-07-17 02:47:09 +08:00
|
|
|
auto loc = getToken().getLoc();
|
2019-03-29 05:58:52 +08:00
|
|
|
SmallVector<std::pair<StringRef, SMLoc>, 1> resultIDs;
|
|
|
|
size_t numExpectedResults;
|
2018-07-17 02:47:09 +08:00
|
|
|
if (getToken().is(Token::percent_identifier)) {
|
2019-03-29 05:58:52 +08:00
|
|
|
// Parse the first result id.
|
|
|
|
resultIDs.emplace_back(getTokenSpelling(), loc);
|
2018-07-17 02:47:09 +08:00
|
|
|
consumeToken(Token::percent_identifier);
|
2019-03-29 05:58:52 +08:00
|
|
|
|
|
|
|
// If the next token is a ':', we parse the expected result count.
|
|
|
|
if (consumeIf(Token::colon)) {
|
|
|
|
// Check that the next token is an integer.
|
|
|
|
if (!getToken().is(Token::integer))
|
|
|
|
return emitError("expected integer number of results");
|
|
|
|
|
|
|
|
// Check that number of results is > 0.
|
|
|
|
auto val = getToken().getUInt64IntegerValue();
|
|
|
|
if (!val.hasValue() || val.getValue() < 1)
|
|
|
|
return emitError("expected named operation to have atleast 1 result");
|
|
|
|
consumeToken(Token::integer);
|
|
|
|
numExpectedResults = *val;
|
|
|
|
} else {
|
|
|
|
// Otherwise, this is a comma separated list of result ids.
|
|
|
|
if (consumeIf(Token::comma)) {
|
2019-05-07 13:00:08 +08:00
|
|
|
auto parseNextResult = [&]() -> ParseResult {
|
2019-03-29 05:58:52 +08:00
|
|
|
// Parse the next result id.
|
|
|
|
if (!getToken().is(Token::percent_identifier))
|
|
|
|
return emitError("expected valid ssa identifier");
|
|
|
|
|
|
|
|
resultIDs.emplace_back(getTokenSpelling(), getToken().getLoc());
|
|
|
|
consumeToken(Token::percent_identifier);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-03-29 05:58:52 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (parseCommaSeparatedList(parseNextResult))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-03-29 05:58:52 +08:00
|
|
|
}
|
|
|
|
numExpectedResults = resultIDs.size();
|
|
|
|
}
|
|
|
|
|
2018-07-24 08:30:01 +08:00
|
|
|
if (parseToken(Token::equal, "expected '=' after SSA name"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-17 02:47:09 +08:00
|
|
|
}
|
|
|
|
|
2019-03-28 23:24:38 +08:00
|
|
|
Operation *op;
|
2018-07-26 02:15:20 +08:00
|
|
|
if (getToken().is(Token::bare_identifier) || getToken().isKeyword())
|
2018-12-29 10:41:31 +08:00
|
|
|
op = parseCustomOperation();
|
2018-07-26 02:15:20 +08:00
|
|
|
else if (getToken().is(Token::string))
|
2019-01-24 03:26:56 +08:00
|
|
|
op = parseGenericOperation();
|
2018-07-26 02:15:20 +08:00
|
|
|
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)
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
|
2019-03-28 23:24:38 +08:00
|
|
|
// If the operation had a name, register it.
|
2019-03-29 05:58:52 +08:00
|
|
|
if (!resultIDs.empty()) {
|
2018-07-27 09:09:20 +08:00
|
|
|
if (op->getNumResults() == 0)
|
|
|
|
return emitError(loc, "cannot name an operation with no results");
|
2019-03-29 05:58:52 +08:00
|
|
|
if (numExpectedResults != op->getNumResults())
|
2019-05-09 01:29:50 +08:00
|
|
|
return emitError(loc, "operation defines ")
|
|
|
|
<< op->getNumResults() << " results but was provided "
|
|
|
|
<< numExpectedResults << " to bind";
|
2019-03-29 05:58:52 +08:00
|
|
|
|
|
|
|
// If the number of result names matches the number of operation results, we
|
2019-03-29 07:18:52 +08:00
|
|
|
// can directly use the provided names.
|
2019-03-29 05:58:52 +08:00
|
|
|
if (resultIDs.size() == op->getNumResults()) {
|
|
|
|
for (unsigned i = 0, e = op->getNumResults(); i != e; ++i)
|
|
|
|
if (addDefinition({resultIDs[i].first, 0, resultIDs[i].second},
|
|
|
|
op->getResult(i)))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-03-29 05:58:52 +08:00
|
|
|
} else {
|
2019-03-29 07:18:52 +08:00
|
|
|
// Otherwise, we use the same name for all results.
|
2019-03-29 05:58:52 +08:00
|
|
|
StringRef name = resultIDs.front().first;
|
|
|
|
for (unsigned i = 0, e = op->getNumResults(); i != e; ++i)
|
|
|
|
if (addDefinition({name, i, loc}, op->getResult(i)))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-03-29 05:58:52 +08:00
|
|
|
}
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-01-24 05:11:23 +08:00
|
|
|
// Try to parse the optional trailing location.
|
|
|
|
if (parseOptionalTrailingLocation(op))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
namespace {
|
|
|
|
// RAII-style guard for cleaning up the regions in the operation state before
|
|
|
|
// deleting them. Within the parser, regions may get deleted if parsing failed,
|
|
|
|
// and other errors may be present, in praticular undominated uses. This makes
|
|
|
|
// sure such uses are deleted.
|
|
|
|
struct CleanupOpStateRegions {
|
|
|
|
~CleanupOpStateRegions() {
|
|
|
|
SmallVector<Region *, 4> regionsToClean;
|
|
|
|
regionsToClean.reserve(state.regions.size());
|
|
|
|
for (auto ®ion : state.regions)
|
|
|
|
if (region)
|
|
|
|
for (auto &block : *region)
|
|
|
|
block.dropAllDefinedValueUses();
|
|
|
|
}
|
|
|
|
OperationState &state;
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
2019-03-28 23:24:38 +08:00
|
|
|
Operation *FunctionParser::parseGenericOperation() {
|
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
|
|
|
|
2019-02-01 13:25:17 +08:00
|
|
|
// Generic operations have a resizable operation list.
|
|
|
|
result.setOperandListToResizable();
|
|
|
|
|
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
|
|
|
|
2019-01-10 04:28:30 +08:00
|
|
|
// Parse the successor list but don't add successors to the result yet to
|
|
|
|
// avoid messing up with the argument order.
|
|
|
|
SmallVector<Block *, 2> successors;
|
|
|
|
SmallVector<SmallVector<Value *, 4>, 2> successorOperands;
|
|
|
|
if (getToken().is(Token::l_square)) {
|
|
|
|
// Check if the operation is a known terminator.
|
|
|
|
const AbstractOperation *abstractOp = result.name.getAbstractOperation();
|
2019-02-09 01:52:26 +08:00
|
|
|
if (abstractOp && !abstractOp->hasProperty(OperationProperty::Terminator))
|
2019-01-10 04:28:30 +08:00
|
|
|
return emitError("successors in non-terminator"), nullptr;
|
|
|
|
if (parseSuccessors(successors, successorOperands))
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-05-06 16:40:13 +08:00
|
|
|
// Parse the region list.
|
|
|
|
CleanupOpStateRegions guard{result};
|
|
|
|
if (consumeIf(Token::l_paren)) {
|
|
|
|
do {
|
|
|
|
// Create temporary regions with function as parent.
|
|
|
|
result.regions.emplace_back(new Region(function));
|
|
|
|
if (parseOperationRegion(*result.regions.back(),
|
|
|
|
/*entryArguments*/ {}))
|
|
|
|
return nullptr;
|
|
|
|
} while (consumeIf(Token::comma));
|
|
|
|
if (parseToken(Token::r_paren, "expected ')' to end region list"))
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-03-28 23:24:38 +08:00
|
|
|
if (parseToken(Token::colon, "expected ':' followed by operation 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];
|
2019-05-09 01:29:50 +08:00
|
|
|
return (emitError(typeLoc, "expected ")
|
|
|
|
<< operandInfos.size() << " operand type" << plural
|
|
|
|
<< " but had " << operandTypes.size(),
|
2018-07-26 02:15:20 +08:00
|
|
|
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
|
|
|
|
2019-01-10 04:28:30 +08:00
|
|
|
// Add the sucessors, and their operands after the proper operands.
|
|
|
|
for (const auto &succ : llvm::zip(successors, successorOperands)) {
|
|
|
|
Block *successor = std::get<0>(succ);
|
|
|
|
const SmallVector<Value *, 4> &operands = std::get<1>(succ);
|
|
|
|
result.addSuccessor(successor, operands);
|
|
|
|
}
|
|
|
|
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
return builder.createOperation(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) {}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseOperation(const AbstractOperation *opDefinition,
|
|
|
|
OperationState *opState) {
|
2019-02-02 08:42:18 +08:00
|
|
|
if (opDefinition->parseAssembly(this, opState))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2019-02-02 08:42:18 +08:00
|
|
|
|
2019-02-28 09:58:09 +08:00
|
|
|
// Check that none of the operands of the current operation reference an
|
2019-03-15 01:38:44 +08:00
|
|
|
// entry block argument for any of the region.
|
|
|
|
for (auto *entryArg : parsedRegionEntryArgumentPlaceholders)
|
2019-02-28 09:58:09 +08:00
|
|
|
if (llvm::is_contained(opState->operands, entryArg))
|
|
|
|
return emitError(nameLoc, "operand use before it's defined");
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2019-02-02 08:42:18 +08:00
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// High level parsing methods.
|
|
|
|
//===--------------------------------------------------------------------===//
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult getCurrentLocation(llvm::SMLoc *loc) override {
|
2018-08-22 08:55:22 +08:00
|
|
|
*loc = parser.getToken().getLoc();
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-08-22 08:55:22 +08:00
|
|
|
}
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseComma() override {
|
|
|
|
return parser.parseToken(Token::comma, "expected ','");
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseColon() override {
|
|
|
|
return parser.parseToken(Token::colon, "expected ':'");
|
2019-04-03 06:33:54 +08:00
|
|
|
}
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseEqual() override {
|
|
|
|
return parser.parseToken(Token::equal, "expected '='");
|
2019-02-02 08:42:18 +08:00
|
|
|
}
|
2018-07-26 02:15:20 +08:00
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseType(Type &result) override {
|
|
|
|
return failure(!(result = parser.parseType()));
|
2018-11-16 09:53:51 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseColonType(Type &result) override {
|
|
|
|
return failure(parser.parseToken(Token::colon, "expected ':'") ||
|
|
|
|
!(result = parser.parseType()));
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseColonTypeList(SmallVectorImpl<Type> &result) override {
|
2018-08-24 05:58:27 +08:00
|
|
|
if (parser.parseToken(Token::colon, "expected ':'"))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
|
|
|
|
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
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
|
|
|
|
} while (parser.consumeIf(Token::comma));
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseTrailingOperandList(SmallVectorImpl<OperandType> &result,
|
|
|
|
int requiredOperandCount,
|
|
|
|
Delimiter delimiter) override {
|
2018-12-06 07:30:25 +08:00
|
|
|
if (parser.getToken().is(Token::comma)) {
|
|
|
|
parseComma();
|
|
|
|
return parseOperandList(result, requiredOperandCount, delimiter);
|
|
|
|
}
|
|
|
|
if (requiredOperandCount != -1)
|
2019-05-09 01:29:50 +08:00
|
|
|
return emitError(parser.getToken().getLoc(), "expected ")
|
|
|
|
<< requiredOperandCount << " operands";
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-12-06 07:30:25 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseOptionalComma() override {
|
|
|
|
return success(parser.consumeIf(Token::comma));
|
|
|
|
}
|
2019-05-06 17:30:50 +08:00
|
|
|
|
2019-01-29 13:23:53 +08:00
|
|
|
/// Parse an optional keyword.
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseOptionalKeyword(const char *keyword) override {
|
2019-01-29 13:23:53 +08:00
|
|
|
// Check that the current token is a bare identifier or keyword.
|
|
|
|
if (parser.getToken().isNot(Token::bare_identifier) &&
|
|
|
|
!parser.getToken().isKeyword())
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2019-01-29 13:23:53 +08:00
|
|
|
|
|
|
|
if (parser.getTokenSpelling() == keyword) {
|
|
|
|
parser.consumeToken();
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2019-01-29 13:23:53 +08:00
|
|
|
}
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-09-14 00:16:32 +08:00
|
|
|
}
|
|
|
|
|
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.
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseAttribute(Attribute &result, Type type, StringRef attrName,
|
|
|
|
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)
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-08-03 07:54:36 +08:00
|
|
|
|
2019-03-01 08:45:30 +08:00
|
|
|
attrs.push_back(parser.builder.getNamedAttr(attrName, result));
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-08-03 07:54:36 +08:00
|
|
|
}
|
|
|
|
|
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.
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseAttribute(Attribute &result, StringRef attrName,
|
|
|
|
SmallVectorImpl<NamedAttribute> &attrs) override {
|
2018-11-16 09:53:51 +08:00
|
|
|
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.
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult
|
2018-08-24 05:58:27 +08:00
|
|
|
parseOptionalAttributeDict(SmallVectorImpl<NamedAttribute> &result) override {
|
2018-08-03 07:54:36 +08:00
|
|
|
if (parser.getToken().isNot(Token::l_brace))
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
|
|
|
return parser.parseAttributeDict(result);
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseOperand(OperandType &result) override {
|
2018-07-26 02:15:20 +08:00
|
|
|
FunctionParser::SSAUseInfo useInfo;
|
|
|
|
if (parser.parseSSAUse(useInfo))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
|
|
|
|
result = {useInfo.loc, useInfo.name, useInfo.number};
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult
|
|
|
|
parseSuccessorAndUseList(Block *&dest,
|
|
|
|
SmallVectorImpl<Value *> &operands) override {
|
2018-11-16 01:56:06 +08:00
|
|
|
// Defer successor parsing to the function parsers.
|
|
|
|
return parser.parseSuccessorAndUseList(dest, operands);
|
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseLParen() override {
|
|
|
|
return parser.parseToken(Token::l_paren, "expected '('");
|
2019-04-03 06:33:54 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseRParen() override {
|
|
|
|
return parser.parseToken(Token::r_paren, "expected ')'");
|
2019-04-03 06:33:54 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseOperandList(SmallVectorImpl<OperandType> &result,
|
|
|
|
int requiredOperandCount = -1,
|
|
|
|
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))
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-07-29 00:36:25 +08:00
|
|
|
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"))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
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))
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-07-29 00:36:25 +08:00
|
|
|
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"))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for zero operands.
|
|
|
|
if (parser.getToken().is(Token::percent_identifier)) {
|
|
|
|
do {
|
|
|
|
OperandType operand;
|
|
|
|
if (parseOperand(operand))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
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"))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
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"))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
break;
|
2018-07-19 23:35:28 +08:00
|
|
|
}
|
2018-07-26 02:15:20 +08:00
|
|
|
|
2019-05-14 09:10:48 +08:00
|
|
|
if (requiredOperandCount != -1 &&
|
|
|
|
result.size() != static_cast<size_t>(requiredOperandCount))
|
2019-05-09 01:29:50 +08:00
|
|
|
return emitError(startLoc, "expected ")
|
|
|
|
<< requiredOperandCount << " operands";
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-07-19 23:35:28 +08:00
|
|
|
}
|
|
|
|
|
2019-04-27 05:46:13 +08:00
|
|
|
/// Parse a region that takes `arguments` of `argTypes` types. This
|
|
|
|
/// effectively defines the SSA values of `arguments` and assignes their type.
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseRegion(Region ®ion, ArrayRef<OperandType> arguments,
|
|
|
|
ArrayRef<Type> argTypes) override {
|
2019-04-27 05:46:13 +08:00
|
|
|
assert(arguments.size() == argTypes.size() &&
|
|
|
|
"mismatching number of arguments and types");
|
|
|
|
|
|
|
|
SmallVector<std::pair<FunctionParser::SSAUseInfo, Type>, 2> regionArguments;
|
|
|
|
for (const auto &pair : llvm::zip(arguments, argTypes)) {
|
|
|
|
const OperandType &operand = std::get<0>(pair);
|
|
|
|
Type type = std::get<1>(pair);
|
|
|
|
FunctionParser::SSAUseInfo operandInfo = {operand.name, operand.number,
|
|
|
|
operand.location};
|
|
|
|
regionArguments.emplace_back(operandInfo, type);
|
|
|
|
|
|
|
|
// Create a placeholder for this argument so that we can detect invalid
|
|
|
|
// references to region arguments.
|
|
|
|
Value *value = parser.resolveSSAUse(operandInfo, type);
|
|
|
|
if (!value)
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2019-04-27 05:46:13 +08:00
|
|
|
parsedRegionEntryArgumentPlaceholders.emplace_back(value);
|
|
|
|
}
|
2019-02-02 08:42:18 +08:00
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
return parser.parseOperationRegion(region, regionArguments);
|
2019-01-29 13:23:53 +08:00
|
|
|
}
|
|
|
|
|
2019-04-27 05:46:13 +08:00
|
|
|
/// Parse a region argument. Region arguments define new values, so this also
|
|
|
|
/// checks if the values with the same name has not been defined yet. The
|
|
|
|
/// type of the argument will be resolved later by a call to `parseRegion`.
|
2019-05-10 13:45:38 +08:00
|
|
|
ParseResult parseRegionArgument(OperandType &argument) override {
|
2019-04-27 05:46:13 +08:00
|
|
|
// Use parseOperand to fill in the OperandType structure.
|
|
|
|
if (parseOperand(argument))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2019-04-27 05:46:13 +08:00
|
|
|
if (auto defLoc = parser.getDefinitionLoc(argument.name, argument.number)) {
|
|
|
|
parser.emitError(argument.location,
|
2019-05-09 01:29:50 +08:00
|
|
|
"redefinition of SSA value '" + argument.name + "'")
|
|
|
|
.attachNote(parser.getEncodedSourceLocation(*defLoc))
|
|
|
|
<< "previously defined here";
|
|
|
|
return failure();
|
2019-02-02 08:42:18 +08:00
|
|
|
}
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2019-02-02 08:42:18 +08:00
|
|
|
}
|
|
|
|
|
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; }
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult resolveOperand(const OperandType &operand, Type type,
|
|
|
|
SmallVectorImpl<Value *> &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);
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2018-08-09 02:02:58 +08:00
|
|
|
}
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:01:31 +08:00
|
|
|
/// Emit a diagnostic at the specified location and return failure.
|
2019-05-09 01:29:50 +08:00
|
|
|
InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) override {
|
2018-07-26 02:15:20 +08:00
|
|
|
emittedError = true;
|
2019-05-09 01:29:50 +08:00
|
|
|
return parser.emitError(loc, "custom op '" + opName + "' " + message);
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool didEmitError() const { return emittedError; }
|
|
|
|
|
|
|
|
private:
|
2019-03-15 01:38:44 +08:00
|
|
|
SmallVector<Value *, 2> parsedRegionEntryArgumentPlaceholders;
|
2018-07-26 02:15:20 +08:00
|
|
|
SMLoc nameLoc;
|
|
|
|
StringRef opName;
|
|
|
|
FunctionParser &parser;
|
|
|
|
bool emittedError = false;
|
|
|
|
};
|
|
|
|
} // end anonymous namespace.
|
|
|
|
|
2019-03-28 23:24:38 +08:00
|
|
|
Operation *FunctionParser::parseCustomOperation() {
|
2018-07-26 02:15:20 +08:00
|
|
|
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());
|
2019-03-03 10:03:03 +08:00
|
|
|
if (!opDefinition && !opName.contains('.')) {
|
|
|
|
// If the operation name has no namespace prefix we treat it as a standard
|
|
|
|
// operation and prefix it with "std".
|
|
|
|
// TODO: Would it be better to just build a mapping of the registered
|
|
|
|
// operations in the standard dialect?
|
|
|
|
opDefinition =
|
|
|
|
AbstractOperation::lookup(Twine("std." + opName).str(), 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.
|
2019-03-03 10:03:03 +08:00
|
|
|
OperationState opState(builder.getContext(), srcLocation, opDefinition->name);
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
CleanupOpStateRegions guard{opState};
|
2019-02-02 08:42:18 +08:00
|
|
|
if (opAsmParser.parseOperation(opDefinition, &opState))
|
2018-08-08 00:12:35 +08:00
|
|
|
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.
|
Allow creating standalone Regions
Currently, regions can only be constructed by passing in a `Function` or an
`Instruction` pointer referencing the parent object, unlike `Function`s or
`Instruction`s themselves that can be created without a parent. It leads to a
rather complex flow in operation construction where one has to create the
operation first before being able to work with its regions. It may be
necessary to work with the regions before the operation is created. In
particular, in `build` and `parse` functions that are executed _before_ the
operation is created in cases where boilerplate region manipulation is required
(for example, inserting the hypothetical default terminator in affine regions).
Allow creating standalone regions. Such regions are meant to own a list of
blocks and transfer them to other regions on demand.
Each instruction stores a fixed number of regions as trailing objects and has
ownership of them. This decreases the size of the Instruction object for the
common case of instructions without regions. Keep this behavior intact. To
allow some flexibility in construction, make OperationState store an owning
vector of regions. When the Builder creates an Instruction from
OperationState, the bodies of the regions are transferred into the
instruction-owned regions to minimize copying. Thus, it becomes possible to
fill standalone regions with blocks and move them to an operation when it is
constructed, or move blocks from a region to an operation region, e.g., for
inlining.
PiperOrigin-RevId: 240368183
2019-03-27 00:55:06 +08:00
|
|
|
return builder.createOperation(opState);
|
2018-06-29 08:02:32 +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 `:`
|
2019-02-03 13:01:11 +08:00
|
|
|
/// '(' affine-constraint-conjunction? ')'
|
|
|
|
/// affine-constraint-conjunction ::= 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) {
|
2019-01-15 01:45:09 +08:00
|
|
|
if (parseToken(Token::l_paren,
|
|
|
|
"expected '(' at start of integer set constraint list"))
|
2019-01-27 02:41:17 +08:00
|
|
|
return IntegerSet();
|
2018-08-08 05:24:38 +08:00
|
|
|
|
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);
|
2019-05-07 13:00:08 +08:00
|
|
|
ParseResult res = elt ? success() : failure();
|
2018-08-08 05:24:38 +08:00
|
|
|
if (elt) {
|
|
|
|
constraints.push_back(elt);
|
|
|
|
isEqs.push_back(isEq);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
};
|
|
|
|
|
2019-02-03 13:01:11 +08:00
|
|
|
// Parse a list of affine constraints (comma-separated).
|
2018-08-08 05:24:38 +08:00
|
|
|
if (parseCommaSeparatedListUntil(Token::r_paren, parseElt, true))
|
2019-01-27 02:41:17 +08:00
|
|
|
return IntegerSet();
|
2019-01-15 05:56:59 +08:00
|
|
|
|
2019-02-03 13:01:11 +08:00
|
|
|
// If no constraints were parsed, then treat this as a degenerate 'true' case.
|
2019-01-15 05:56:59 +08:00
|
|
|
if (constraints.empty()) {
|
2019-02-03 13:01:11 +08:00
|
|
|
/* 0 == 0 */
|
|
|
|
auto zero = getAffineConstantExpr(0, getContext());
|
|
|
|
return builder.getIntegerSet(numDims, numSymbols, zero, true);
|
2019-01-15 05:56:59 +08:00
|
|
|
}
|
2018-08-08 05:24:38 +08:00
|
|
|
|
|
|
|
// Parsed a valid integer set.
|
|
|
|
return builder.getIntegerSet(numDims, numSymbols, constraints, isEqs);
|
|
|
|
}
|
|
|
|
|
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:
|
2019-05-28 23:57:38 +08:00
|
|
|
/// Parse an attribute alias declaration.
|
2019-05-07 01:36:32 +08:00
|
|
|
ParseResult parseAttributeAliasDef();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2019-05-28 23:57:38 +08:00
|
|
|
/// Parse an attribute alias declaration.
|
2019-01-08 10:42:04 +08:00
|
|
|
ParseResult parseTypeAliasDef();
|
|
|
|
|
2018-07-11 01:08:27 +08:00
|
|
|
// Functions.
|
2019-03-01 01:30:52 +08:00
|
|
|
ParseResult
|
|
|
|
parseArgumentList(SmallVectorImpl<Type> &argTypes,
|
|
|
|
SmallVectorImpl<StringRef> &argNames,
|
|
|
|
SmallVectorImpl<SmallVector<NamedAttribute, 2>> &argAttrs);
|
|
|
|
ParseResult parseFunctionSignature(
|
|
|
|
StringRef &name, FunctionType &type, SmallVectorImpl<StringRef> &argNames,
|
|
|
|
SmallVectorImpl<SmallVector<NamedAttribute, 2>> &argAttrs);
|
2019-01-03 02:20:00 +08:00
|
|
|
ParseResult parseFunc();
|
2018-07-11 01:08:27 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2019-05-07 01:36:32 +08:00
|
|
|
/// Parses an attribute alias declaration.
|
2018-07-11 01:08:27 +08:00
|
|
|
///
|
2019-05-07 01:36:32 +08:00
|
|
|
/// attribute-alias-def ::= '#' alias-name `=` attribute-value
|
2018-07-11 01:08:27 +08:00
|
|
|
///
|
2019-05-07 01:36:32 +08:00
|
|
|
ParseResult ModuleParser::parseAttributeAliasDef() {
|
2018-07-11 01:08:27 +08:00
|
|
|
assert(getToken().is(Token::hash_identifier));
|
2019-05-28 23:57:38 +08:00
|
|
|
StringRef aliasName = getTokenSpelling().drop_front();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
// Check for redefinitions.
|
2019-05-28 23:57:38 +08:00
|
|
|
if (getState().attributeAliasDefinitions.count(aliasName) > 0)
|
|
|
|
return emitError("redefinition of attribute alias id '" + aliasName + "'");
|
|
|
|
|
|
|
|
// Make sure this isn't invading the dialect attribute namespace.
|
|
|
|
if (aliasName.contains('.'))
|
|
|
|
return emitError("attribute names with a '.' are reserved for "
|
|
|
|
"dialect-defined names");
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
consumeToken(Token::hash_identifier);
|
|
|
|
|
2019-05-28 23:57:38 +08:00
|
|
|
// Parse the '='.
|
2019-05-07 01:36:32 +08:00
|
|
|
if (parseToken(Token::equal, "expected '=' in attribute alias definition"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2019-05-07 01:36:32 +08:00
|
|
|
// Parse the attribute value.
|
|
|
|
Attribute attr = parseAttribute();
|
|
|
|
if (!attr)
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-08-08 05:24:38 +08:00
|
|
|
|
2019-05-28 23:57:38 +08:00
|
|
|
getState().attributeAliasDefinitions[aliasName] = attr;
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-11 01:08:27 +08:00
|
|
|
}
|
|
|
|
|
2019-01-08 10:42:04 +08:00
|
|
|
/// Parse a type alias declaration.
|
|
|
|
///
|
|
|
|
/// type-alias-def ::= '!' alias-name `=` 'type' type
|
|
|
|
///
|
|
|
|
ParseResult ModuleParser::parseTypeAliasDef() {
|
|
|
|
assert(getToken().is(Token::exclamation_identifier));
|
|
|
|
StringRef aliasName = getTokenSpelling().drop_front();
|
|
|
|
|
|
|
|
// Check for redefinitions.
|
|
|
|
if (getState().typeAliasDefinitions.count(aliasName) > 0)
|
|
|
|
return emitError("redefinition of type alias id '" + aliasName + "'");
|
|
|
|
|
2019-04-06 10:36:42 +08:00
|
|
|
// Make sure this isn't invading the dialect type namespace.
|
|
|
|
if (aliasName.contains('.'))
|
|
|
|
return emitError("type names with a '.' are reserved for "
|
|
|
|
"dialect-defined names");
|
|
|
|
|
2019-01-08 10:42:04 +08:00
|
|
|
consumeToken(Token::exclamation_identifier);
|
|
|
|
|
|
|
|
// Parse the '=' and 'type'.
|
|
|
|
if (parseToken(Token::equal, "expected '=' in type alias definition") ||
|
|
|
|
parseToken(Token::kw_type, "expected 'type' in type alias definition"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-08 10:42:04 +08:00
|
|
|
|
|
|
|
// Parse the type.
|
|
|
|
Type aliasedType = parseType();
|
|
|
|
if (!aliasedType)
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-08 10:42:04 +08:00
|
|
|
|
|
|
|
// Register this alias with the parser state.
|
|
|
|
getState().typeAliasDefinitions.try_emplace(aliasName, aliasedType);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-08 10:42:04 +08:00
|
|
|
}
|
|
|
|
|
2018-12-29 00:48:09 +08:00
|
|
|
/// Parse a (possibly empty) list of Function arguments with types.
|
2018-07-20 00:52:39 +08:00
|
|
|
///
|
2019-03-01 01:30:52 +08:00
|
|
|
/// named-argument ::= ssa-id `:` type attribute-dict?
|
2018-12-30 01:01:59 +08:00
|
|
|
/// argument-list ::= named-argument (`,` named-argument)* | /*empty*/
|
2019-03-01 01:30:52 +08:00
|
|
|
/// argument-list ::= type attribute-dict? (`,` type attribute-dict?)*
|
|
|
|
/// | /*empty*/
|
2018-07-20 00:52:39 +08:00
|
|
|
///
|
2019-03-01 01:30:52 +08:00
|
|
|
ParseResult ModuleParser::parseArgumentList(
|
|
|
|
SmallVectorImpl<Type> &argTypes, SmallVectorImpl<StringRef> &argNames,
|
|
|
|
SmallVectorImpl<SmallVector<NamedAttribute, 2>> &argAttrs) {
|
2018-07-24 08:30:01 +08:00
|
|
|
consumeToken(Token::l_paren);
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
// The argument list either has to consistently have ssa-id's followed by
|
|
|
|
// types, or just be a type list. It isn't ok to sometimes have SSA ID's and
|
|
|
|
// sometimes not.
|
2018-07-20 00:52:39 +08:00
|
|
|
auto parseElt = [&]() -> ParseResult {
|
2018-12-30 01:01:59 +08:00
|
|
|
// Parse argument name if present.
|
|
|
|
auto loc = getToken().getLoc();
|
2018-08-07 02:54:39 +08:00
|
|
|
StringRef name = getTokenSpelling();
|
2018-12-30 01:01:59 +08:00
|
|
|
if (consumeIf(Token::percent_identifier)) {
|
|
|
|
// Reject this if the preceding argument was missing a name.
|
|
|
|
if (argNames.empty() && !argTypes.empty())
|
|
|
|
return emitError(loc, "expected type instead of SSA identifier");
|
2018-07-20 00:52:39 +08:00
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
argNames.push_back(name);
|
|
|
|
|
|
|
|
if (parseToken(Token::colon, "expected ':'"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-30 01:01:59 +08:00
|
|
|
} else {
|
|
|
|
// Reject this if the preceding argument had a name.
|
|
|
|
if (!argNames.empty())
|
|
|
|
return emitError("expected SSA identifier");
|
|
|
|
}
|
2018-07-20 00:52:39 +08:00
|
|
|
|
|
|
|
// Parse argument type
|
|
|
|
auto elt = parseType();
|
|
|
|
if (!elt)
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-20 00:52:39 +08:00
|
|
|
argTypes.push_back(elt);
|
2019-03-01 01:30:52 +08:00
|
|
|
|
|
|
|
// Parse the attribute dict.
|
|
|
|
SmallVector<NamedAttribute, 2> attrs;
|
2019-05-28 23:57:38 +08:00
|
|
|
if (getToken().is(Token::l_brace))
|
2019-03-01 01:30:52 +08:00
|
|
|
if (parseAttributeDict(attrs))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-05-28 23:57:38 +08:00
|
|
|
|
2019-03-01 01:30:52 +08:00
|
|
|
argAttrs.push_back(attrs);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-20 00:52:39 +08:00
|
|
|
};
|
|
|
|
|
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-12-30 01:01:59 +08:00
|
|
|
/// function-signature ::=
|
|
|
|
/// function-id `(` argument-list `)` (`->` type-list)?
|
2018-07-11 01:08:27 +08:00
|
|
|
///
|
2019-03-01 01:30:52 +08:00
|
|
|
ParseResult ModuleParser::parseFunctionSignature(
|
|
|
|
StringRef &name, FunctionType &type, SmallVectorImpl<StringRef> &argNames,
|
|
|
|
SmallVectorImpl<SmallVector<NamedAttribute, 2>> &argAttrs) {
|
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;
|
2019-03-01 01:30:52 +08:00
|
|
|
if (parseArgumentList(argTypes, argNames, argAttrs))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
// 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)) {
|
2019-02-06 03:47:02 +08:00
|
|
|
if (parseFunctionResultTypes(results))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-11 01:08:27 +08:00
|
|
|
}
|
2018-07-20 00:52:39 +08:00
|
|
|
type = builder.getFunctionType(argTypes, results);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-11 01:08:27 +08:00
|
|
|
}
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
/// Function declarations.
|
2018-09-19 07:36:26 +08:00
|
|
|
///
|
2019-01-24 05:11:23 +08:00
|
|
|
/// function ::= `func` function-signature function-attributes?
|
|
|
|
/// trailing-location? function-body?
|
2019-01-03 04:32:30 +08:00
|
|
|
/// function-body ::= `{` block+ `}`
|
2018-12-30 01:01:59 +08:00
|
|
|
/// function-attributes ::= `attributes` attribute-dict
|
2018-07-11 01:08:27 +08:00
|
|
|
///
|
2019-01-03 02:20:00 +08:00
|
|
|
ParseResult ModuleParser::parseFunc() {
|
2018-12-30 01:01:59 +08:00
|
|
|
consumeToken();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
StringRef name;
|
2018-10-31 05:59:22 +08:00
|
|
|
FunctionType type;
|
2018-12-30 01:01:59 +08:00
|
|
|
SmallVector<StringRef, 4> argNames;
|
2019-03-01 01:30:52 +08:00
|
|
|
SmallVector<SmallVector<NamedAttribute, 2>, 4> argAttrs;
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2018-08-18 07:49:42 +08:00
|
|
|
auto loc = getToken().getLoc();
|
2019-03-01 01:30:52 +08:00
|
|
|
if (parseFunctionSignature(name, type, argNames, argAttrs))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
// If function attributes are present, parse them.
|
2018-09-19 07:36:26 +08:00
|
|
|
SmallVector<NamedAttribute, 8> attrs;
|
2018-12-30 01:01:59 +08:00
|
|
|
if (consumeIf(Token::kw_attributes)) {
|
|
|
|
if (parseAttributeDict(attrs))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-09-19 07:36:26 +08:00
|
|
|
}
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
// Okay, the function signature was parsed correctly, create the function now.
|
2018-09-19 07:36:26 +08:00
|
|
|
auto *function =
|
2019-01-03 02:20:00 +08:00
|
|
|
new Function(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)
|
2019-05-09 01:29:50 +08:00
|
|
|
return emitError(loc, "redefinition of function named '") << name << "'";
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2019-01-24 05:11:23 +08:00
|
|
|
// Parse an optional trailing location.
|
|
|
|
if (parseOptionalTrailingLocation(function))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-24 05:11:23 +08:00
|
|
|
|
2019-03-01 01:30:52 +08:00
|
|
|
// Add the attributes to the function arguments.
|
|
|
|
for (unsigned i = 0, e = function->getNumArguments(); i != e; ++i)
|
|
|
|
function->setArgAttrs(i, argAttrs[i]);
|
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
// External functions have no body.
|
2019-01-03 02:20:00 +08:00
|
|
|
if (getToken().isNot(Token::l_brace))
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
// Create the parser.
|
|
|
|
auto parser = FunctionParser(getState(), function);
|
2018-07-20 00:52:39 +08:00
|
|
|
|
2018-12-30 05:36:59 +08:00
|
|
|
bool hadNamedArguments = !argNames.empty();
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2019-01-03 02:20:00 +08:00
|
|
|
// Add the entry block and argument list.
|
|
|
|
function->addEntryBlock();
|
2018-09-19 07:36:26 +08:00
|
|
|
|
2018-08-07 02:54:39 +08:00
|
|
|
// Add definitions of the function arguments.
|
2018-12-30 01:01:59 +08:00
|
|
|
if (hadNamedArguments) {
|
|
|
|
for (unsigned i = 0, e = function->getNumArguments(); i != e; ++i) {
|
|
|
|
if (parser.addDefinition({argNames[i], 0, loc}, function->getArgument(i)))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-30 01:01:59 +08:00
|
|
|
}
|
2018-08-07 02:54:39 +08:00
|
|
|
}
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2018-12-30 01:01:59 +08:00
|
|
|
return parser.parseFunctionBody(hadNamedArguments);
|
2018-07-11 01:08:27 +08:00
|
|
|
}
|
|
|
|
|
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");
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2019-05-28 23:57:38 +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:
|
2019-05-23 04:41:23 +08:00
|
|
|
return success();
|
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:
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-06-23 01:39:19 +08:00
|
|
|
|
2019-05-28 23:57:38 +08:00
|
|
|
// Parse an attribute alias.
|
2018-07-11 01:08:27 +08:00
|
|
|
case Token::hash_identifier:
|
2019-05-07 01:36:32 +08:00
|
|
|
if (parseAttributeAliasDef())
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-08-08 05:24:38 +08:00
|
|
|
break;
|
|
|
|
|
2019-05-28 23:57:38 +08:00
|
|
|
// Parse a type alias.
|
2019-01-08 10:42:04 +08:00
|
|
|
case Token::exclamation_identifier:
|
|
|
|
if (parseTypeAliasDef())
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-08 10:42:04 +08:00
|
|
|
break;
|
|
|
|
|
2019-01-03 02:20:00 +08:00
|
|
|
case Token::kw_func:
|
|
|
|
if (parseFunc())
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
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.
|
2019-04-03 01:24:11 +08:00
|
|
|
if (failed(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
|
|
|
|
2019-03-23 13:17:10 +08:00
|
|
|
/// This parses the file specified by the indicated filename and returns an
|
|
|
|
/// MLIR module if it was valid. If not, the error message is emitted through
|
|
|
|
/// the error handler registered in the context, and a null pointer is returned.
|
2019-03-23 13:26:01 +08:00
|
|
|
Module *mlir::parseSourceFile(StringRef filename, MLIRContext *context) {
|
2019-03-23 13:17:10 +08:00
|
|
|
auto file_or_err = llvm::MemoryBuffer::getFile(filename);
|
|
|
|
if (std::error_code error = file_or_err.getError()) {
|
|
|
|
context->emitError(mlir::UnknownLoc::get(context),
|
|
|
|
"Could not open input file " + filename);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load the MLIR module.
|
|
|
|
llvm::SourceMgr source_mgr;
|
|
|
|
source_mgr.AddNewSourceBuffer(std::move(*file_or_err), llvm::SMLoc());
|
|
|
|
return parseSourceFile(source_mgr, context);
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|