2018-06-23 01:39:19 +08:00
|
|
|
//===- Parser.cpp - MLIR Parser Implementation ----------------------------===//
|
|
|
|
//
|
2020-01-26 11:58:30 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
2019-12-24 01:35:36 +08:00
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-06-23 01:39:19 +08:00
|
|
|
//
|
2019-12-24 01:35:36 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-06-23 01:39:19 +08:00
|
|
|
//
|
|
|
|
// This file implements the parser for the MLIR textual form.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
#include "Parser.h"
|
|
|
|
#include "mlir/IR/AffineMap.h"
|
2020-11-20 02:43:12 +08:00
|
|
|
#include "mlir/IR/BuiltinOps.h"
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
#include "mlir/IR/Dialect.h"
|
2020-06-11 07:58:55 +08:00
|
|
|
#include "mlir/IR/Verifier.h"
|
|
|
|
#include "mlir/Parser.h"
|
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/StringSet.h"
|
|
|
|
#include "llvm/ADT/bit.h"
|
|
|
|
#include "llvm/Support/PrettyStackTrace.h"
|
|
|
|
#include "llvm/Support/SourceMgr.h"
|
|
|
|
#include <algorithm>
|
2019-01-24 05:11:23 +08:00
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
using namespace mlir;
|
|
|
|
using namespace mlir::detail;
|
|
|
|
using llvm::MemoryBuffer;
|
|
|
|
using llvm::SMLoc;
|
|
|
|
using llvm::SourceMgr;
|
2019-01-24 05:11:23 +08:00
|
|
|
|
2018-06-28 02:03:08 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2020-06-11 07:58:55 +08:00
|
|
|
// Parser
|
2018-06-28 02:03:08 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
/// Parse a comma separated list of elements that must have at least one entry
|
|
|
|
/// in it.
|
2020-11-04 04:31:24 +08:00
|
|
|
ParseResult
|
|
|
|
Parser::parseCommaSeparatedList(function_ref<ParseResult()> parseElement) {
|
2020-06-11 07:58:55 +08:00
|
|
|
// Non-empty case starts with an element.
|
|
|
|
if (parseElement())
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-01-27 02:41:17 +08:00
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
// Otherwise we have a list of comma separated elements.
|
|
|
|
while (consumeIf(Token::comma)) {
|
|
|
|
if (parseElement())
|
[MLIR][Affine] Add affine.parallel op
Summary:
As discussed in https://llvm.discourse.group/t/rfc-add-affine-parallel/350, this is the first in a series of patches to bring in support for the `affine.parallel` operation.
This first patch adds the IR representation along with custom printer/parser implementations.
Reviewers: bondhugula, herhut, mehdi_amini, nicolasvasilache, rriddle, earhart, jbruestle
Reviewed By: bondhugula, nicolasvasilache, rriddle, earhart, jbruestle
Subscribers: jpienaar, burmako, shauheen, antiagainst, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74288
2020-02-13 09:59:57 +08:00
|
|
|
return failure();
|
|
|
|
}
|
2019-06-24 22:31:52 +08:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
/// Parse a comma-separated list of elements, terminated with an arbitrary
|
|
|
|
/// token. This allows empty lists if allowEmptyList is true.
|
2019-06-01 04:48:43 +08:00
|
|
|
///
|
2020-06-11 07:58:55 +08:00
|
|
|
/// abstract-list ::= rightToken // if allowEmptyList == true
|
|
|
|
/// abstract-list ::= element (',' element)* rightToken
|
2019-06-01 04:48:43 +08:00
|
|
|
///
|
2020-11-04 04:31:24 +08:00
|
|
|
ParseResult
|
|
|
|
Parser::parseCommaSeparatedListUntil(Token::Kind rightToken,
|
|
|
|
function_ref<ParseResult()> parseElement,
|
|
|
|
bool allowEmptyList) {
|
2020-06-11 07:58:55 +08:00
|
|
|
// Handle the empty case.
|
|
|
|
if (getToken().is(rightToken)) {
|
|
|
|
if (!allowEmptyList)
|
|
|
|
return emitError("expected list element");
|
|
|
|
consumeToken(rightToken);
|
|
|
|
return success();
|
2019-06-01 04:48:43 +08:00
|
|
|
}
|
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
if (parseCommaSeparatedList(parseElement) ||
|
|
|
|
parseToken(rightToken, "expected ',' or '" +
|
|
|
|
Token::getTokenSpelling(rightToken) + "'"))
|
|
|
|
return failure();
|
2019-06-01 04:48:43 +08:00
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
return success();
|
2019-06-01 04:48:43 +08:00
|
|
|
}
|
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
InFlightDiagnostic Parser::emitError(SMLoc loc, const Twine &message) {
|
|
|
|
auto diag = mlir::emitError(getEncodedSourceLocation(loc), message);
|
2019-06-01 04:48:43 +08:00
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
// If we hit a parse error in response to a lexer error, then the lexer
|
|
|
|
// already reported the error.
|
|
|
|
if (getToken().is(Token::error))
|
|
|
|
diag.abandon();
|
|
|
|
return diag;
|
2020-01-14 05:12:37 +08:00
|
|
|
}
|
2018-07-17 00:45:22 +08:00
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
/// Consume the specified token if present and return success. On failure,
|
|
|
|
/// output a diagnostic and return failure.
|
|
|
|
ParseResult Parser::parseToken(Token::Kind expectedToken,
|
|
|
|
const Twine &message) {
|
|
|
|
if (consumeIf(expectedToken))
|
|
|
|
return success();
|
|
|
|
return emitError(message);
|
2019-06-24 22:31:52 +08:00
|
|
|
}
|
|
|
|
|
2020-12-15 03:53:43 +08:00
|
|
|
/// Parse an optional integer value from the stream.
|
|
|
|
OptionalParseResult Parser::parseOptionalInteger(uint64_t &result) {
|
|
|
|
Token curToken = getToken();
|
|
|
|
if (curToken.isNot(Token::integer, Token::minus))
|
|
|
|
return llvm::None;
|
|
|
|
|
|
|
|
bool negative = consumeIf(Token::minus);
|
|
|
|
Token curTok = getToken();
|
|
|
|
if (parseToken(Token::integer, "expected integer value"))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
auto val = curTok.getUInt64IntegerValue();
|
|
|
|
if (!val)
|
|
|
|
return emitError(curTok.getLoc(), "integer value too large");
|
|
|
|
result = negative ? -*val : *val;
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2018-06-23 01:39:19 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-06-06 08:04:37 +08:00
|
|
|
// OperationParser
|
2018-06-23 01:39:19 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
namespace {
|
2019-06-06 08:04:37 +08:00
|
|
|
/// This class provides support for parsing operations and regions of
|
|
|
|
/// operations.
|
|
|
|
class OperationParser : public Parser {
|
2018-07-19 23:35:28 +08:00
|
|
|
public:
|
2020-12-04 07:46:41 +08:00
|
|
|
OperationParser(ParserState &state, Operation *topLevelOp)
|
|
|
|
: Parser(state), opBuilder(topLevelOp->getRegion(0)),
|
|
|
|
topLevelOp(topLevelOp) {
|
|
|
|
// The top level operation starts a new name scope.
|
|
|
|
pushSSANameScope(/*isIsolated=*/true);
|
2019-07-04 04:21:24 +08:00
|
|
|
}
|
2018-12-29 10:41:31 +08:00
|
|
|
|
2019-06-06 08:04:37 +08:00
|
|
|
~OperationParser();
|
2018-12-29 10:41:31 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// After parsing is finished, this function must be called to see if there
|
|
|
|
/// are any remaining issues.
|
2019-07-04 04:21:24 +08:00
|
|
|
ParseResult finalize();
|
2019-01-10 04:28:30 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// SSA Value Handling
|
|
|
|
//===--------------------------------------------------------------------===//
|
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
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Push a new SSA name scope to the parser.
|
2019-08-20 06:26:43 +08:00
|
|
|
void pushSSANameScope(bool isIsolated);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
/// Pop the last SSA name scope from the parser.
|
|
|
|
ParseResult popSSANameScope();
|
2018-07-19 23:35:28 +08:00
|
|
|
|
|
|
|
/// Register a definition of a value with the symbol table.
|
2019-12-24 06:45:01 +08:00
|
|
|
ParseResult addDefinition(SSAUseInfo useInfo, Value value);
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Parse an optional list of SSA uses into 'results'.
|
2018-07-22 05:32:09 +08:00
|
|
|
ParseResult parseOptionalSSAUseList(SmallVectorImpl<SSAUseInfo> &results);
|
2018-07-23 06:45:24 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Parse a single SSA use into 'result'.
|
|
|
|
ParseResult parseSSAUse(SSAUseInfo &result);
|
2018-07-23 06:45:24 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Given a reference to an SSA value and its type, return a reference. This
|
|
|
|
/// returns null on failure.
|
2019-12-24 06:45:01 +08:00
|
|
|
Value resolveSSAUse(SSAUseInfo useInfo, Type type);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
2020-11-04 04:31:24 +08:00
|
|
|
ParseResult
|
|
|
|
parseSSADefOrUseAndType(function_ref<ParseResult(SSAUseInfo, Type)> action);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
2019-12-24 06:45:01 +08:00
|
|
|
ParseResult parseOptionalSSAUseAndTypeList(SmallVectorImpl<Value> &results);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
/// Return the location of the value identified by its name and number if it
|
2019-08-20 06:26:43 +08:00
|
|
|
/// has been already reference.
|
2019-12-19 01:28:48 +08:00
|
|
|
Optional<SMLoc> getReferenceLoc(StringRef name, unsigned number) {
|
2019-08-20 06:26:43 +08:00
|
|
|
auto &values = isolatedNameScopes.back().values;
|
2019-06-06 02:57:37 +08:00
|
|
|
if (!values.count(name) || number >= values[name].size())
|
|
|
|
return {};
|
2019-08-20 06:26:43 +08:00
|
|
|
if (values[name][number].first)
|
2019-06-06 02:57:37 +08:00
|
|
|
return values[name][number].second;
|
|
|
|
return {};
|
2018-07-23 06:45:24 +08:00
|
|
|
}
|
2018-07-22 05:32:09 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Operation Parsing
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Parse an operation instance.
|
|
|
|
ParseResult parseOperation();
|
|
|
|
|
2020-03-06 04:48:28 +08:00
|
|
|
/// Parse a single operation successor.
|
|
|
|
ParseResult parseSuccessor(Block *&dest);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
/// Parse a comma-separated list of operation successors in brackets.
|
2020-03-06 04:48:28 +08:00
|
|
|
ParseResult parseSuccessors(SmallVectorImpl<Block *> &destinations);
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Parse an operation instance that is in the generic form.
|
|
|
|
Operation *parseGenericOperation();
|
2019-06-04 00:43:22 +08:00
|
|
|
|
2019-09-09 14:39:34 +08:00
|
|
|
/// Parse an operation instance that is in the generic form and insert it at
|
|
|
|
/// the provided insertion point.
|
|
|
|
Operation *parseGenericOperation(Block *insertBlock,
|
|
|
|
Block::iterator insertPt);
|
|
|
|
|
2020-11-13 15:33:43 +08:00
|
|
|
/// Parse an optional trailing location for the given operation.
|
|
|
|
///
|
|
|
|
/// trailing-location ::= (`loc` (`(` location `)` | attribute-alias))?
|
|
|
|
///
|
|
|
|
ParseResult parseTrailingOperationLocation(Operation *op);
|
|
|
|
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
/// This is the structure of a result specifier in the assembly syntax,
|
|
|
|
/// including the name, number of results, and location.
|
2020-11-13 15:33:43 +08:00
|
|
|
using ResultRecord = std::tuple<StringRef, unsigned, SMLoc>;
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Parse an operation instance that is in the op-defined custom form.
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
/// resultInfo specifies information about the "%name =" specifiers.
|
2020-06-11 07:58:55 +08:00
|
|
|
Operation *parseCustomOperation(ArrayRef<ResultRecord> resultIDs);
|
2019-06-04 00:43:22 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Region Parsing
|
|
|
|
//===--------------------------------------------------------------------===//
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Parse a region into 'region' with the provided entry block arguments.
|
2019-08-20 06:26:43 +08:00
|
|
|
/// 'isIsolatedNameScope' indicates if the naming scope of this region is
|
|
|
|
/// isolated from those above.
|
2019-06-04 00:43:22 +08:00
|
|
|
ParseResult parseRegion(Region ®ion,
|
2019-08-20 06:26:43 +08:00
|
|
|
ArrayRef<std::pair<SSAUseInfo, Type>> entryArguments,
|
|
|
|
bool isIsolatedNameScope = false);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
/// Parse a region body into '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
|
|
|
ParseResult parseRegionBody(Region ®ion);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Block Parsing
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Parse a new block into 'block'.
|
2019-02-02 08:42:18 +08:00
|
|
|
ParseResult parseBlock(Block *&block);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
/// Parse a list of operations into 'block'.
|
2019-02-02 08:42:18 +08:00
|
|
|
ParseResult parseBlockBody(Block *block);
|
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Parse a (possibly empty) list of block arguments.
|
2019-12-24 06:45:01 +08:00
|
|
|
ParseResult parseOptionalBlockArgList(SmallVectorImpl<BlockArgument> &results,
|
|
|
|
Block *owner);
|
2019-02-02 08:42:18 +08:00
|
|
|
|
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
|
|
|
|
2019-06-06 02:57:37 +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 *defineBlockNamed(StringRef name, SMLoc loc, Block *existing);
|
2018-07-27 09:09:20 +08:00
|
|
|
|
2018-07-19 23:35:28 +08:00
|
|
|
private:
|
2019-06-04 00:43:22 +08:00
|
|
|
/// Returns the info for a block at the current scope for the given name.
|
|
|
|
std::pair<Block *, SMLoc> &getBlockInfoByName(StringRef name) {
|
|
|
|
return blocksByName.back()[name];
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert a new forward reference to the given block.
|
|
|
|
void insertForwardRef(Block *block, SMLoc loc) {
|
|
|
|
forwardRef.back().try_emplace(block, loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Erase any forward reference to the given block.
|
|
|
|
bool eraseForwardRef(Block *block) { return forwardRef.back().erase(block); }
|
|
|
|
|
|
|
|
/// Record that a definition was added at the current scope.
|
2019-08-20 06:26:43 +08:00
|
|
|
void recordDefinition(StringRef def);
|
|
|
|
|
|
|
|
/// Get the value entry for the given SSA name.
|
2019-12-24 06:45:01 +08:00
|
|
|
SmallVectorImpl<std::pair<Value, SMLoc>> &getSSAValueEntry(StringRef name);
|
2019-06-04 00:43:22 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Create a forward reference placeholder value with the given location and
|
|
|
|
/// result type.
|
2019-12-24 06:45:01 +08:00
|
|
|
Value createForwardRefPlaceholder(SMLoc loc, Type type);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
/// Return true if this is a forward reference.
|
2019-12-24 06:45:01 +08:00
|
|
|
bool isForwardRefPlaceholder(Value value) {
|
2019-06-06 02:57:37 +08:00
|
|
|
return forwardRefPlaceholders.count(value);
|
|
|
|
}
|
|
|
|
|
2019-08-20 06:26:43 +08:00
|
|
|
/// This struct represents an isolated SSA name scope. This scope may contain
|
|
|
|
/// other nested non-isolated scopes. These scopes are used for operations
|
|
|
|
/// that are known to be isolated to allow for reusing names within their
|
|
|
|
/// regions, even if those names are used above.
|
|
|
|
struct IsolatedSSANameScope {
|
|
|
|
/// Record that a definition was added at the current scope.
|
|
|
|
void recordDefinition(StringRef def) {
|
|
|
|
definitionsPerScope.back().insert(def);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Push a nested name scope.
|
|
|
|
void pushSSANameScope() { definitionsPerScope.push_back({}); }
|
|
|
|
|
|
|
|
/// Pop a nested name scope.
|
|
|
|
void popSSANameScope() {
|
|
|
|
for (auto &def : definitionsPerScope.pop_back_val())
|
|
|
|
values.erase(def.getKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This keeps track of all of the SSA values we are tracking for each name
|
|
|
|
/// scope, indexed by their name. This has one entry per result number.
|
2019-12-24 06:45:01 +08:00
|
|
|
llvm::StringMap<SmallVector<std::pair<Value, SMLoc>, 1>> values;
|
2019-08-20 06:26:43 +08:00
|
|
|
|
|
|
|
/// This keeps track of all of the values defined by a specific name scope.
|
|
|
|
SmallVector<llvm::StringSet<>, 2> definitionsPerScope;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A list of isolated name scopes.
|
|
|
|
SmallVector<IsolatedSSANameScope, 2> isolatedNameScopes;
|
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// This keeps track of the block names as well as the location of the first
|
|
|
|
/// reference for each nested name scope. This is used to diagnose invalid
|
2019-10-20 15:11:03 +08:00
|
|
|
/// block references and memorize them.
|
2019-06-04 00:43:22 +08:00
|
|
|
SmallVector<DenseMap<StringRef, std::pair<Block *, SMLoc>>, 2> blocksByName;
|
|
|
|
SmallVector<DenseMap<Block *, SMLoc>, 2> forwardRef;
|
2018-12-29 10:41:31 +08:00
|
|
|
|
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.
|
2019-12-24 06:45:01 +08:00
|
|
|
DenseMap<Value, SMLoc> forwardRefPlaceholders;
|
2019-06-06 04:59:28 +08:00
|
|
|
|
2020-11-13 15:33:43 +08:00
|
|
|
/// A set of operations whose locations reference aliases that have yet to
|
|
|
|
/// be resolved.
|
|
|
|
SmallVector<std::pair<Operation *, Token>, 8> opsWithDeferredLocs;
|
|
|
|
|
2019-06-06 04:59:28 +08:00
|
|
|
/// The builder used when creating parsed operation instances.
|
|
|
|
OpBuilder opBuilder;
|
2019-07-04 04:21:24 +08:00
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
/// The top level operation that holds all of the parsed operations.
|
|
|
|
Operation *topLevelOp;
|
2019-06-06 02:57:37 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2019-06-06 08:04:37 +08:00
|
|
|
OperationParser::~OperationParser() {
|
2019-06-06 02:57:37 +08:00
|
|
|
for (auto &fwd : forwardRefPlaceholders) {
|
|
|
|
// Drop all uses of undefined forward declared reference and destroy
|
|
|
|
// defining operation.
|
2020-01-12 00:54:04 +08:00
|
|
|
fwd.first.dropAllUses();
|
|
|
|
fwd.first.getDefiningOp()->destroy();
|
2019-06-06 02:57:37 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-21 09:41:34 +08:00
|
|
|
|
2019-06-06 08:04:37 +08:00
|
|
|
/// After parsing is finished, this function must be called to see if there are
|
|
|
|
/// any remaining issues.
|
2019-07-04 04:21:24 +08:00
|
|
|
ParseResult OperationParser::finalize() {
|
2019-06-06 02:57:37 +08:00
|
|
|
// Check for any forward references that are left. If we find any, error
|
|
|
|
// out.
|
|
|
|
if (!forwardRefPlaceholders.empty()) {
|
2020-05-25 05:08:27 +08:00
|
|
|
SmallVector<const char *, 4> errors;
|
2019-06-06 02:57:37 +08:00
|
|
|
// Iteration over the map isn't deterministic, so sort by source location.
|
|
|
|
for (auto entry : forwardRefPlaceholders)
|
2020-05-25 05:08:27 +08:00
|
|
|
errors.push_back(entry.second.getPointer());
|
2019-06-06 02:57:37 +08:00
|
|
|
llvm::array_pod_sort(errors.begin(), errors.end());
|
2018-07-21 09:41:34 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
for (auto entry : errors) {
|
2020-05-25 05:08:27 +08:00
|
|
|
auto loc = SMLoc::getFromPointer(entry);
|
2019-06-06 02:57:37 +08:00
|
|
|
emitError(loc, "use of undeclared SSA value name");
|
|
|
|
}
|
|
|
|
return failure();
|
2018-07-21 09:41:34 +08:00
|
|
|
}
|
2019-06-06 02:57:37 +08:00
|
|
|
|
2020-11-13 15:33:43 +08:00
|
|
|
// Resolve the locations of any deferred operations.
|
|
|
|
auto &attributeAliases = getState().symbols.attributeAliasDefinitions;
|
|
|
|
for (std::pair<Operation *, Token> &it : opsWithDeferredLocs) {
|
|
|
|
llvm::SMLoc tokLoc = it.second.getLoc();
|
|
|
|
StringRef identifier = it.second.getSpelling().drop_front();
|
|
|
|
Attribute attr = attributeAliases.lookup(identifier);
|
|
|
|
if (!attr)
|
|
|
|
return emitError(tokLoc) << "operation location alias was never defined";
|
|
|
|
|
|
|
|
LocationAttr locAttr = attr.dyn_cast<LocationAttr>();
|
|
|
|
if (!locAttr)
|
|
|
|
return emitError(tokLoc)
|
|
|
|
<< "expected location, but found '" << attr << "'";
|
|
|
|
it.first->setLoc(locAttr);
|
|
|
|
}
|
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
// Pop the top level name scope.
|
|
|
|
return popSSANameScope();
|
2019-06-06 02:57:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// SSA Value Handling
|
|
|
|
//===----------------------------------------------------------------------===//
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2019-08-20 06:26:43 +08:00
|
|
|
void OperationParser::pushSSANameScope(bool isIsolated) {
|
2019-06-04 00:43:22 +08:00
|
|
|
blocksByName.push_back(DenseMap<StringRef, std::pair<Block *, SMLoc>>());
|
|
|
|
forwardRef.push_back(DenseMap<Block *, SMLoc>());
|
2019-08-20 06:26:43 +08:00
|
|
|
|
|
|
|
// Push back a new name definition scope.
|
|
|
|
if (isIsolated)
|
|
|
|
isolatedNameScopes.push_back({});
|
|
|
|
isolatedNameScopes.back().pushSSANameScope();
|
2019-06-04 00:43:22 +08:00
|
|
|
}
|
2018-12-30 01:01:59 +08:00
|
|
|
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::popSSANameScope() {
|
2019-06-04 00:43:22 +08:00
|
|
|
auto forwardRefInCurrentScope = forwardRef.pop_back_val();
|
2018-12-30 05:36:59 +08:00
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
// Verify that all referenced blocks were defined.
|
2019-06-04 00:43:22 +08:00
|
|
|
if (!forwardRefInCurrentScope.empty()) {
|
2018-12-29 10:41:31 +08:00
|
|
|
SmallVector<std::pair<const char *, Block *>, 4> errors;
|
|
|
|
// Iteration over the map isn't deterministic, so sort by source location.
|
2019-06-04 00:43:22 +08:00
|
|
|
for (auto entry : forwardRefInCurrentScope) {
|
2018-12-29 10:41:31 +08:00
|
|
|
errors.push_back({entry.second.getPointer(), entry.first});
|
2019-06-06 02:57:37 +08:00
|
|
|
// Add this block to the top-level region to allow for automatic cleanup.
|
2020-12-04 07:46:41 +08:00
|
|
|
topLevelOp->getRegion(0).push_back(entry.first);
|
2019-01-26 04:48:25 +08:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2019-08-20 06:26:43 +08:00
|
|
|
// Pop the next nested namescope. If there is only one internal namescope,
|
|
|
|
// just pop the isolated scope.
|
|
|
|
auto ¤tNameScope = isolatedNameScopes.back();
|
|
|
|
if (currentNameScope.definitionsPerScope.size() == 1)
|
|
|
|
isolatedNameScopes.pop_back();
|
|
|
|
else
|
|
|
|
currentNameScope.popSSANameScope();
|
2019-06-04 00:43:22 +08:00
|
|
|
|
2019-08-20 06:26:43 +08:00
|
|
|
blocksByName.pop_back();
|
2019-06-04 00:43:22 +08:00
|
|
|
return success();
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Register a definition of a value with the symbol table.
|
2019-12-24 06:45:01 +08:00
|
|
|
ParseResult OperationParser::addDefinition(SSAUseInfo useInfo, Value value) {
|
2019-08-20 06:26:43 +08:00
|
|
|
auto &entries = getSSAValueEntry(useInfo.name);
|
2019-06-04 00:43:22 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
// Make sure there is a slot for this value.
|
|
|
|
if (entries.size() <= useInfo.number)
|
|
|
|
entries.resize(useInfo.number + 1);
|
2019-02-02 08:42:18 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
// If we already have an entry for this, check to see if it was a definition
|
|
|
|
// or a forward reference.
|
2019-12-23 13:59:55 +08:00
|
|
|
if (auto existing = entries[useInfo.number].first) {
|
2019-06-06 02:57:37 +08:00
|
|
|
if (!isForwardRefPlaceholder(existing)) {
|
2019-06-23 05:39:16 +08:00
|
|
|
return emitError(useInfo.loc)
|
2019-06-06 02:57:37 +08:00
|
|
|
.append("redefinition of SSA value '", useInfo.name, "'")
|
|
|
|
.attachNote(getEncodedSourceLocation(entries[useInfo.number].second))
|
|
|
|
.append("previously defined here");
|
|
|
|
}
|
2019-02-02 08:42:18 +08:00
|
|
|
|
2020-05-05 17:03:26 +08:00
|
|
|
if (existing.getType() != value.getType()) {
|
|
|
|
return emitError(useInfo.loc)
|
|
|
|
.append("definition of SSA value '", useInfo.name, "#",
|
|
|
|
useInfo.number, "' has type ", value.getType())
|
|
|
|
.attachNote(getEncodedSourceLocation(entries[useInfo.number].second))
|
|
|
|
.append("previously used here with type ", existing.getType());
|
|
|
|
}
|
|
|
|
|
2019-06-06 02:57:37 +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.
|
2020-01-12 00:54:04 +08:00
|
|
|
existing.replaceAllUsesWith(value);
|
|
|
|
existing.getDefiningOp()->destroy();
|
2019-06-06 02:57:37 +08:00
|
|
|
forwardRefPlaceholders.erase(existing);
|
2019-02-02 08:42:18 +08:00
|
|
|
}
|
2019-01-26 04:48:25 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Record this definition for the current scope.
|
|
|
|
entries[useInfo.number] = {value, useInfo.loc};
|
|
|
|
recordDefinition(useInfo.name);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-26 04:48:25 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Parse a (possibly empty) list of SSA operands.
|
2019-01-26 04:48:25 +08:00
|
|
|
///
|
2019-06-06 02:57:37 +08:00
|
|
|
/// ssa-use-list ::= ssa-use (`,` ssa-use)*
|
|
|
|
/// ssa-use-list-opt ::= ssa-use-list?
|
2019-01-26 04:48:25 +08:00
|
|
|
///
|
2019-06-06 02:57:37 +08:00
|
|
|
ParseResult
|
2019-06-06 08:04:37 +08:00
|
|
|
OperationParser::parseOptionalSSAUseList(SmallVectorImpl<SSAUseInfo> &results) {
|
2019-06-06 02:57:37 +08:00
|
|
|
if (getToken().isNot(Token::percent_identifier))
|
|
|
|
return success();
|
|
|
|
return parseCommaSeparatedList([&]() -> ParseResult {
|
|
|
|
SSAUseInfo result;
|
|
|
|
if (parseSSAUse(result))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-06-06 02:57:37 +08:00
|
|
|
results.push_back(result);
|
|
|
|
return success();
|
|
|
|
});
|
2019-01-26 04:48:25 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Parse a SSA operand for an operation.
|
2018-12-29 10:41:31 +08:00
|
|
|
///
|
2019-06-06 02:57:37 +08:00
|
|
|
/// ssa-use ::= ssa-id
|
2018-12-29 10:41:31 +08:00
|
|
|
///
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::parseSSAUse(SSAUseInfo &result) {
|
2019-06-06 02:57:37 +08:00
|
|
|
result.name = getTokenSpelling();
|
|
|
|
result.number = 0;
|
|
|
|
result.loc = getToken().getLoc();
|
|
|
|
if (parseToken(Token::percent_identifier, "expected SSA operand"))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2018-12-29 10:41:31 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
// If we have an attribute ID, it is a result number.
|
|
|
|
if (getToken().is(Token::hash_identifier)) {
|
|
|
|
if (auto value = getToken().getHashIdentifierNumber())
|
|
|
|
result.number = value.getValue();
|
|
|
|
else
|
|
|
|
return emitError("invalid SSA value result number");
|
|
|
|
consumeToken(Token::hash_identifier);
|
2018-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-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.
|
2019-12-24 06:45:01 +08:00
|
|
|
Value OperationParser::resolveSSAUse(SSAUseInfo useInfo, Type type) {
|
2019-08-20 06:26:43 +08:00
|
|
|
auto &entries = getSSAValueEntry(useInfo.name);
|
2018-07-21 09:41:34 +08:00
|
|
|
|
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) {
|
2019-12-23 13:59:55 +08:00
|
|
|
auto result = entries[useInfo.number].first;
|
2018-07-19 23:35:28 +08:00
|
|
|
// Check that the type matches the other uses.
|
2020-01-12 00:54:04 +08:00
|
|
|
if (result.getType() == type)
|
2018-07-19 23:35:28 +08:00
|
|
|
return result;
|
|
|
|
|
2019-05-14 02:43:17 +08:00
|
|
|
emitError(useInfo.loc, "use of value '")
|
2019-06-25 08:51:44 +08:00
|
|
|
.append(useInfo.name,
|
|
|
|
"' expects different type than prior uses: ", type, " vs ",
|
2020-01-12 00:54:04 +08:00
|
|
|
result.getType())
|
2019-05-14 02:43:17 +08:00
|
|
|
.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.
|
2019-06-06 02:57:37 +08:00
|
|
|
if (entries[0].first && !isForwardRefPlaceholder(entries[0].first))
|
2018-07-21 09:41:34 +08:00
|
|
|
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.
|
2019-12-23 13:59:55 +08:00
|
|
|
auto result = createForwardRefPlaceholder(useInfo.loc, type);
|
2018-07-21 09:41:34 +08:00
|
|
|
entries[useInfo.number].first = result;
|
|
|
|
entries[useInfo.number].second = useInfo.loc;
|
|
|
|
return result;
|
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
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::parseSSADefOrUseAndType(
|
2020-11-04 04:31:24 +08:00
|
|
|
function_ref<ParseResult(SSAUseInfo, Type)> action) {
|
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"))
|
2019-06-06 02:57:37 +08:00
|
|
|
return failure();
|
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)
|
2019-06-06 02:57:37 +08:00
|
|
|
return failure();
|
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
|
|
|
///
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::parseOptionalSSAUseAndTypeList(
|
2019-12-24 06:45:01 +08:00
|
|
|
SmallVectorImpl<Value> &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
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
if (valueIDs.size() != types.size())
|
|
|
|
return emitError("expected ")
|
|
|
|
<< valueIDs.size() << " types to match operand list";
|
2018-12-29 10:41:31 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
results.reserve(valueIDs.size());
|
|
|
|
for (unsigned i = 0, e = valueIDs.size(); i != e; ++i) {
|
2019-12-23 13:59:55 +08:00
|
|
|
if (auto value = resolveSSAUse(valueIDs[i], types[i]))
|
2019-06-06 02:57:37 +08:00
|
|
|
results.push_back(value);
|
|
|
|
else
|
|
|
|
return failure();
|
|
|
|
}
|
2018-12-30 05:36:59 +08:00
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2019-08-20 06:26:43 +08:00
|
|
|
/// Record that a definition was added at the current scope.
|
|
|
|
void OperationParser::recordDefinition(StringRef def) {
|
|
|
|
isolatedNameScopes.back().recordDefinition(def);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the value entry for the given SSA name.
|
2019-12-24 06:45:01 +08:00
|
|
|
SmallVectorImpl<std::pair<Value, SMLoc>> &
|
2019-08-20 06:26:43 +08:00
|
|
|
OperationParser::getSSAValueEntry(StringRef name) {
|
|
|
|
return isolatedNameScopes.back().values[name];
|
|
|
|
}
|
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
/// Create and remember a new placeholder for a forward reference.
|
2019-12-24 06:45:01 +08:00
|
|
|
Value OperationParser::createForwardRefPlaceholder(SMLoc loc, Type type) {
|
2019-06-06 02:57:37 +08:00
|
|
|
// Forward references are always created as operations, because we just need
|
|
|
|
// something with a def/use chain.
|
|
|
|
//
|
|
|
|
// We create these placeholders as having an empty name, which we know
|
|
|
|
// cannot be created through normal user input, allowing us to distinguish
|
|
|
|
// them.
|
|
|
|
auto name = OperationName("placeholder", getContext());
|
|
|
|
auto *op = Operation::create(
|
2019-09-29 00:35:23 +08:00
|
|
|
getEncodedSourceLocation(loc), name, type, /*operands=*/{},
|
2020-04-27 12:28:22 +08:00
|
|
|
/*attributes=*/llvm::None, /*successors=*/{}, /*numRegions=*/0);
|
2019-06-06 02:57:37 +08:00
|
|
|
forwardRefPlaceholders[op->getResult(0)] = loc;
|
|
|
|
return op->getResult(0);
|
2018-12-29 10:41:31 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Operation Parsing
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-12-29 10:41:31 +08:00
|
|
|
/// Parse an operation.
|
2018-07-17 02:47:09 +08:00
|
|
|
///
|
2019-12-05 04:05:52 +08:00
|
|
|
/// operation ::= op-result-list?
|
|
|
|
/// (generic-operation | custom-operation)
|
|
|
|
/// trailing-location?
|
2020-02-06 00:31:49 +08:00
|
|
|
/// generic-operation ::= string-literal `(` ssa-use-list? `)`
|
|
|
|
/// successor-list? (`(` region-list `)`)?
|
|
|
|
/// attribute-dict? `:` function-type
|
2019-12-05 04:05:52 +08:00
|
|
|
/// custom-operation ::= bare-id custom-operation-format
|
|
|
|
/// op-result-list ::= op-result (`,` op-result)* `=`
|
|
|
|
/// op-result ::= ssa-id (`:` integer-literal)
|
2018-07-17 02:47:09 +08:00
|
|
|
///
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::parseOperation() {
|
2018-07-17 02:47:09 +08:00
|
|
|
auto loc = getToken().getLoc();
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
SmallVector<ResultRecord, 1> resultIDs;
|
2019-10-26 00:33:32 +08:00
|
|
|
size_t numExpectedResults = 0;
|
2018-07-17 02:47:09 +08:00
|
|
|
if (getToken().is(Token::percent_identifier)) {
|
2019-10-26 00:33:32 +08:00
|
|
|
// Parse the group of result ids.
|
|
|
|
auto parseNextResult = [&]() -> ParseResult {
|
|
|
|
// Parse the next result id.
|
|
|
|
if (!getToken().is(Token::percent_identifier))
|
|
|
|
return emitError("expected valid ssa identifier");
|
|
|
|
|
|
|
|
Token nameTok = getToken();
|
|
|
|
consumeToken(Token::percent_identifier);
|
|
|
|
|
|
|
|
// If the next token is a ':', we parse the expected result count.
|
|
|
|
size_t expectedSubResults = 1;
|
|
|
|
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);
|
|
|
|
expectedSubResults = *val;
|
2019-03-29 05:58:52 +08:00
|
|
|
}
|
2019-10-26 00:33:32 +08:00
|
|
|
|
|
|
|
resultIDs.emplace_back(nameTok.getSpelling(), expectedSubResults,
|
|
|
|
nameTok.getLoc());
|
|
|
|
numExpectedResults += expectedSubResults;
|
|
|
|
return success();
|
|
|
|
};
|
|
|
|
if (parseCommaSeparatedList(parseNextResult))
|
|
|
|
return failure();
|
2019-03-29 05:58:52 +08:00
|
|
|
|
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())
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
op = parseCustomOperation(resultIDs);
|
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
|
|
|
|
2019-10-26 00:33:32 +08:00
|
|
|
// Add definitions for each of the result groups.
|
|
|
|
unsigned opResI = 0;
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
for (ResultRecord &resIt : resultIDs) {
|
2019-10-26 00:33:32 +08:00
|
|
|
for (unsigned subRes : llvm::seq<unsigned>(0, std::get<1>(resIt))) {
|
|
|
|
if (addDefinition({std::get<0>(resIt), subRes, std::get<2>(resIt)},
|
|
|
|
op->getResult(opResI++)))
|
2019-05-07 13:00:08 +08:00
|
|
|
return failure();
|
2019-10-26 00:33:32 +08:00
|
|
|
}
|
2019-03-29 05:58:52 +08:00
|
|
|
}
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2020-03-06 04:48:28 +08:00
|
|
|
/// Parse a single operation successor.
|
2019-06-06 02:57:37 +08:00
|
|
|
///
|
2020-03-06 04:48:28 +08:00
|
|
|
/// successor ::= block-id
|
2019-06-06 02:57:37 +08:00
|
|
|
///
|
2020-03-06 04:48:28 +08:00
|
|
|
ParseResult OperationParser::parseSuccessor(Block *&dest) {
|
2019-06-06 02:57:37 +08:00
|
|
|
// Verify branch is identifier and get the matching block.
|
|
|
|
if (!getToken().is(Token::caret_identifier))
|
|
|
|
return emitError("expected block name");
|
|
|
|
dest = getBlockNamed(getTokenSpelling(), getToken().getLoc());
|
|
|
|
consumeToken();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a comma-separated list of operation successors in brackets.
|
|
|
|
///
|
|
|
|
/// successor-list ::= `[` successor (`,` successor )* `]`
|
|
|
|
///
|
2020-03-06 04:48:28 +08:00
|
|
|
ParseResult
|
|
|
|
OperationParser::parseSuccessors(SmallVectorImpl<Block *> &destinations) {
|
2019-06-06 02:57:37 +08:00
|
|
|
if (parseToken(Token::l_square, "expected '['"))
|
|
|
|
return failure();
|
|
|
|
|
2020-03-06 04:48:28 +08:00
|
|
|
auto parseElt = [this, &destinations] {
|
2019-06-06 02:57:37 +08:00
|
|
|
Block *dest;
|
2020-03-06 04:48:28 +08:00
|
|
|
ParseResult res = parseSuccessor(dest);
|
2019-06-06 02:57:37 +08:00
|
|
|
destinations.push_back(dest);
|
|
|
|
return res;
|
|
|
|
};
|
|
|
|
return parseCommaSeparatedListUntil(Token::r_square, parseElt,
|
|
|
|
/*allowEmptyList=*/false);
|
|
|
|
}
|
|
|
|
|
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,
|
2019-10-20 15:11:03 +08:00
|
|
|
// and other errors may be present, in particular undominated uses. This makes
|
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
|
|
|
// 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-06-06 08:04:37 +08:00
|
|
|
Operation *OperationParser::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
|
|
|
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
std::string name = getToken().getStringValue();
|
2018-07-17 02:47:09 +08:00
|
|
|
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);
|
|
|
|
|
2019-06-23 02:08:52 +08:00
|
|
|
OperationState result(srcLocation, name);
|
2018-08-08 03:02:37 +08:00
|
|
|
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
// Lazy load dialects in the context as needed.
|
|
|
|
if (!result.name.getAbstractOperation()) {
|
|
|
|
StringRef dialectName = StringRef(name).split('.').first;
|
|
|
|
if (!getContext()->getLoadedDialect(dialectName) &&
|
|
|
|
getContext()->getOrLoadDialect(dialectName)) {
|
|
|
|
result.name = OperationName(name, getContext());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-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
|
|
|
|
2020-03-06 04:48:28 +08:00
|
|
|
// Parse the successor list.
|
2019-01-10 04:28:30 +08:00
|
|
|
if (getToken().is(Token::l_square)) {
|
|
|
|
// Check if the operation is a known terminator.
|
|
|
|
const AbstractOperation *abstractOp = result.name.getAbstractOperation();
|
2021-02-10 03:41:10 +08:00
|
|
|
if (abstractOp && !abstractOp->hasTrait<OpTrait::IsTerminator>())
|
2019-01-10 04:28:30 +08:00
|
|
|
return emitError("successors in non-terminator"), nullptr;
|
2020-03-06 04:48:28 +08:00
|
|
|
|
|
|
|
SmallVector<Block *, 2> successors;
|
|
|
|
if (parseSuccessors(successors))
|
2019-01-10 04:28:30 +08:00
|
|
|
return nullptr;
|
2020-03-06 04:48:28 +08:00
|
|
|
result.addSuccessors(successors);
|
2019-01-10 04:28:30 +08:00
|
|
|
}
|
|
|
|
|
2019-05-06 16:40:13 +08:00
|
|
|
// Parse the region list.
|
|
|
|
CleanupOpStateRegions guard{result};
|
|
|
|
if (consumeIf(Token::l_paren)) {
|
|
|
|
do {
|
2019-06-06 08:04:37 +08:00
|
|
|
// Create temporary regions with the top level region as parent.
|
2020-12-04 07:46:41 +08:00
|
|
|
result.regions.emplace_back(new Region(topLevelOp));
|
2019-07-04 04:21:24 +08:00
|
|
|
if (parseRegion(*result.regions.back(), /*entryArguments=*/{}))
|
2019-05-06 16:40:13 +08:00
|
|
|
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
|
|
|
|
2020-11-13 15:33:43 +08:00
|
|
|
// Create the operation and try to parse a location for it.
|
|
|
|
Operation *op = opBuilder.createOperation(result);
|
|
|
|
if (parseTrailingOperationLocation(op))
|
2019-10-29 06:11:00 +08:00
|
|
|
return nullptr;
|
2020-11-13 15:33:43 +08:00
|
|
|
return op;
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
2018-07-17 02:47:09 +08:00
|
|
|
|
2019-09-09 14:39:34 +08:00
|
|
|
Operation *OperationParser::parseGenericOperation(Block *insertBlock,
|
|
|
|
Block::iterator insertPt) {
|
|
|
|
OpBuilder::InsertionGuard restoreInsertionPoint(opBuilder);
|
|
|
|
opBuilder.setInsertionPoint(insertBlock, insertPt);
|
|
|
|
return parseGenericOperation();
|
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
namespace {
|
|
|
|
class CustomOpAsmParser : public OpAsmParser {
|
|
|
|
public:
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
CustomOpAsmParser(SMLoc nameLoc,
|
|
|
|
ArrayRef<OperationParser::ResultRecord> resultIDs,
|
|
|
|
const AbstractOperation *opDefinition,
|
2019-08-20 06:26:43 +08:00
|
|
|
OperationParser &parser)
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
: nameLoc(nameLoc), resultIDs(resultIDs), opDefinition(opDefinition),
|
|
|
|
parser(parser) {}
|
2018-07-26 02:15:20 +08:00
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse an instance of the operation described by 'opDefinition' into the
|
|
|
|
/// provided operation state.
|
2019-09-21 10:47:05 +08:00
|
|
|
ParseResult parseOperation(OperationState &opState) {
|
2019-09-21 02:36:49 +08:00
|
|
|
if (opDefinition->parseAssembly(*this, opState))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2020-11-04 06:31:23 +08:00
|
|
|
// Verify that the parsed attributes does not have duplicate attributes.
|
|
|
|
// This can happen if an attribute set during parsing is also specified in
|
|
|
|
// the attribute dictionary in the assembly, or the attribute is set
|
|
|
|
// multiple during parsing.
|
|
|
|
Optional<NamedAttribute> duplicate = opState.attributes.findDuplicate();
|
|
|
|
if (duplicate)
|
|
|
|
return emitError(getNameLoc(), "attribute '")
|
|
|
|
<< duplicate->first
|
|
|
|
<< "' occurs more than once in the attribute list";
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2019-02-02 08:42:18 +08:00
|
|
|
}
|
|
|
|
|
2019-09-09 14:39:34 +08:00
|
|
|
Operation *parseGenericOperation(Block *insertBlock,
|
|
|
|
Block::iterator insertPt) final {
|
|
|
|
return parser.parseGenericOperation(insertBlock, insertPt);
|
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
2019-06-06 05:49:52 +08:00
|
|
|
// Utilities
|
2018-07-26 02:15:20 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
2018-07-19 23:35:28 +08:00
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Return if any errors were emitted during parsing.
|
|
|
|
bool didEmitError() const { return emittedError; }
|
|
|
|
|
|
|
|
/// Emit a diagnostic at the specified location and return failure.
|
|
|
|
InFlightDiagnostic emitError(llvm::SMLoc loc, const Twine &message) override {
|
|
|
|
emittedError = true;
|
2020-09-03 03:10:36 +08:00
|
|
|
return parser.emitError(loc, "custom op '" + opDefinition->name.strref() +
|
|
|
|
"' " + message);
|
2019-06-06 05:49:52 +08:00
|
|
|
}
|
|
|
|
|
2019-06-05 14:33:18 +08:00
|
|
|
llvm::SMLoc getCurrentLocation() override {
|
|
|
|
return parser.getToken().getLoc();
|
2018-08-22 08:55:22 +08:00
|
|
|
}
|
2019-06-05 02:35:21 +08:00
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
Builder &getBuilder() const override { return parser.builder; }
|
2019-06-05 02:35:21 +08:00
|
|
|
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
/// Return the name of the specified result in the specified syntax, as well
|
|
|
|
/// as the subelement in the name. For example, in this operation:
|
|
|
|
///
|
|
|
|
/// %x, %y:2, %z = foo.op
|
|
|
|
///
|
|
|
|
/// getResultName(0) == {"x", 0 }
|
|
|
|
/// getResultName(1) == {"y", 0 }
|
|
|
|
/// getResultName(2) == {"y", 1 }
|
|
|
|
/// getResultName(3) == {"z", 0 }
|
|
|
|
std::pair<StringRef, unsigned>
|
|
|
|
getResultName(unsigned resultNo) const override {
|
|
|
|
// Scan for the resultID that contains this result number.
|
|
|
|
for (unsigned nameID = 0, e = resultIDs.size(); nameID != e; ++nameID) {
|
|
|
|
const auto &entry = resultIDs[nameID];
|
|
|
|
if (resultNo < std::get<1>(entry)) {
|
|
|
|
// Don't pass on the leading %.
|
|
|
|
StringRef name = std::get<0>(entry).drop_front();
|
|
|
|
return {name, resultNo};
|
|
|
|
}
|
|
|
|
resultNo -= std::get<1>(entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invalid result number.
|
|
|
|
return {"", ~0U};
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the number of declared SSA results. This returns 4 for the foo.op
|
|
|
|
/// example in the comment for getResultName.
|
|
|
|
size_t getNumResults() const override {
|
|
|
|
size_t count = 0;
|
|
|
|
for (auto &entry : resultIDs)
|
|
|
|
count += std::get<1>(entry);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
llvm::SMLoc getNameLoc() const override { return nameLoc; }
|
2019-06-05 02:35:21 +08:00
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Token Parsing
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
2019-07-03 01:13:34 +08:00
|
|
|
/// Parse a `->` token.
|
|
|
|
ParseResult parseArrow() override {
|
|
|
|
return parser.parseToken(Token::arrow, "expected '->'");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses a `->` if present.
|
|
|
|
ParseResult parseOptionalArrow() override {
|
|
|
|
return success(parser.consumeIf(Token::arrow));
|
|
|
|
}
|
|
|
|
|
[mlir] Add a new "Pattern Descriptor Language" (PDL) dialect.
PDL presents a high level abstraction for the rewrite pattern infrastructure available in MLIR. This abstraction allows for representing patterns transforming MLIR, as MLIR. This allows for applying all of the benefits that the general MLIR infrastructure provides, to the infrastructure itself. This means that pattern matching can be more easily verified for correctness, targeted by frontends, and optimized.
PDL abstracts over various different aspects of patterns and core MLIR data structures. Patterns are specified via a `pdl.pattern` operation. These operations contain a region body for the "matcher" code, and terminate with a `pdl.rewrite` that either dispatches to an external rewriter or contains a region for the rewrite specified via `pdl`. The types of values in `pdl` are handle types to MLIR C++ types, with `!pdl.attribute`, `!pdl.operation`, and `!pdl.type` directly mapping to `mlir::Attribute`, `mlir::Operation*`, and `mlir::Value` respectively.
An example pattern is shown below:
```mlir
// pdl.pattern contains metadata similarly to a `RewritePattern`.
pdl.pattern : benefit(1) {
// External input operand values are specified via `pdl.input` operations.
// Result types are constrainted via `pdl.type` operations.
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite(%root) {
pdl.replace %root with (%inputOperand)
}
}
```
This is a culmination of the work originally discussed here: https://groups.google.com/a/tensorflow.org/g/mlir/c/j_bn74ByxlQ
Differential Revision: https://reviews.llvm.org/D84578
2020-08-20 03:57:45 +08:00
|
|
|
/// Parse a '{' token.
|
|
|
|
ParseResult parseLBrace() override {
|
|
|
|
return parser.parseToken(Token::l_brace, "expected '{'");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a '{' token if present
|
|
|
|
ParseResult parseOptionalLBrace() override {
|
|
|
|
return success(parser.consumeIf(Token::l_brace));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a `}` token.
|
|
|
|
ParseResult parseRBrace() override {
|
|
|
|
return parser.parseToken(Token::r_brace, "expected '}'");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a `}` token if present
|
|
|
|
ParseResult parseOptionalRBrace() override {
|
|
|
|
return success(parser.consumeIf(Token::r_brace));
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a `:` token.
|
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-06-05 02:35:21 +08:00
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a `:` token if present.
|
2019-06-05 02:35:21 +08:00
|
|
|
ParseResult parseOptionalColon() override {
|
|
|
|
return success(parser.consumeIf(Token::colon));
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a `,` token.
|
|
|
|
ParseResult parseComma() override {
|
|
|
|
return parser.parseToken(Token::comma, "expected ','");
|
2019-06-04 03:08:22 +08:00
|
|
|
}
|
2018-07-26 02:15:20 +08:00
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a `,` token if present.
|
|
|
|
ParseResult parseOptionalComma() override {
|
|
|
|
return success(parser.consumeIf(Token::comma));
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-08-09 03:11:27 +08:00
|
|
|
/// Parses a `...` if present.
|
|
|
|
ParseResult parseOptionalEllipsis() override {
|
|
|
|
return success(parser.consumeIf(Token::ellipsis));
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a `=` token.
|
|
|
|
ParseResult parseEqual() override {
|
|
|
|
return parser.parseToken(Token::equal, "expected '='");
|
2018-12-06 07:30:25 +08:00
|
|
|
}
|
|
|
|
|
[mlir] Add a new "Pattern Descriptor Language" (PDL) dialect.
PDL presents a high level abstraction for the rewrite pattern infrastructure available in MLIR. This abstraction allows for representing patterns transforming MLIR, as MLIR. This allows for applying all of the benefits that the general MLIR infrastructure provides, to the infrastructure itself. This means that pattern matching can be more easily verified for correctness, targeted by frontends, and optimized.
PDL abstracts over various different aspects of patterns and core MLIR data structures. Patterns are specified via a `pdl.pattern` operation. These operations contain a region body for the "matcher" code, and terminate with a `pdl.rewrite` that either dispatches to an external rewriter or contains a region for the rewrite specified via `pdl`. The types of values in `pdl` are handle types to MLIR C++ types, with `!pdl.attribute`, `!pdl.operation`, and `!pdl.type` directly mapping to `mlir::Attribute`, `mlir::Operation*`, and `mlir::Value` respectively.
An example pattern is shown below:
```mlir
// pdl.pattern contains metadata similarly to a `RewritePattern`.
pdl.pattern : benefit(1) {
// External input operand values are specified via `pdl.input` operations.
// Result types are constrainted via `pdl.type` operations.
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite(%root) {
pdl.replace %root with (%inputOperand)
}
}
```
This is a culmination of the work originally discussed here: https://groups.google.com/a/tensorflow.org/g/mlir/c/j_bn74ByxlQ
Differential Revision: https://reviews.llvm.org/D84578
2020-08-20 03:57:45 +08:00
|
|
|
/// Parse a `=` token if present.
|
|
|
|
ParseResult parseOptionalEqual() override {
|
|
|
|
return success(parser.consumeIf(Token::equal));
|
|
|
|
}
|
|
|
|
|
2019-12-19 01:59:37 +08:00
|
|
|
/// Parse a '<' token.
|
|
|
|
ParseResult parseLess() override {
|
|
|
|
return parser.parseToken(Token::less, "expected '<'");
|
|
|
|
}
|
|
|
|
|
2020-11-12 01:01:39 +08:00
|
|
|
/// Parse a '<' token if present.
|
|
|
|
ParseResult parseOptionalLess() override {
|
|
|
|
return success(parser.consumeIf(Token::less));
|
|
|
|
}
|
|
|
|
|
2019-12-19 01:59:37 +08:00
|
|
|
/// Parse a '>' token.
|
|
|
|
ParseResult parseGreater() override {
|
|
|
|
return parser.parseToken(Token::greater, "expected '>'");
|
|
|
|
}
|
|
|
|
|
2020-11-12 01:01:39 +08:00
|
|
|
/// Parse a '>' token if present.
|
|
|
|
ParseResult parseOptionalGreater() override {
|
|
|
|
return success(parser.consumeIf(Token::greater));
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a `(` token.
|
|
|
|
ParseResult parseLParen() override {
|
|
|
|
return parser.parseToken(Token::l_paren, "expected '('");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses a '(' if present.
|
|
|
|
ParseResult parseOptionalLParen() override {
|
|
|
|
return success(parser.consumeIf(Token::l_paren));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a `)` token.
|
|
|
|
ParseResult parseRParen() override {
|
|
|
|
return parser.parseToken(Token::r_paren, "expected ')'");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses a ')' if present.
|
|
|
|
ParseResult parseOptionalRParen() override {
|
|
|
|
return success(parser.consumeIf(Token::r_paren));
|
|
|
|
}
|
|
|
|
|
2019-06-24 22:31:52 +08:00
|
|
|
/// Parse a `[` token.
|
|
|
|
ParseResult parseLSquare() override {
|
|
|
|
return parser.parseToken(Token::l_square, "expected '['");
|
|
|
|
}
|
|
|
|
|
2019-06-25 01:59:05 +08:00
|
|
|
/// Parses a '[' if present.
|
|
|
|
ParseResult parseOptionalLSquare() override {
|
|
|
|
return success(parser.consumeIf(Token::l_square));
|
|
|
|
}
|
|
|
|
|
2019-06-24 22:31:52 +08:00
|
|
|
/// Parse a `]` token.
|
|
|
|
ParseResult parseRSquare() override {
|
|
|
|
return parser.parseToken(Token::r_square, "expected ']'");
|
|
|
|
}
|
|
|
|
|
2019-06-25 01:59:05 +08:00
|
|
|
/// Parses a ']' if present.
|
|
|
|
ParseResult parseOptionalRSquare() override {
|
|
|
|
return success(parser.consumeIf(Token::r_square));
|
|
|
|
}
|
|
|
|
|
2020-11-12 01:01:39 +08:00
|
|
|
/// Parses a '?' token.
|
|
|
|
ParseResult parseQuestion() override {
|
|
|
|
return parser.parseToken(Token::question, "expected '?'");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses a '?' token if present.
|
|
|
|
ParseResult parseOptionalQuestion() override {
|
|
|
|
return success(parser.consumeIf(Token::question));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses a '+' token.
|
|
|
|
ParseResult parsePlus() override {
|
|
|
|
return parser.parseToken(Token::plus, "expected '+'");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses a '+' token if present.
|
|
|
|
ParseResult parseOptionalPlus() override {
|
|
|
|
return success(parser.consumeIf(Token::plus));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses a '*' token.
|
|
|
|
ParseResult parseStar() override {
|
|
|
|
return parser.parseToken(Token::star, "expected '*'");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses a '*' token if present.
|
|
|
|
ParseResult parseOptionalStar() override {
|
|
|
|
return success(parser.consumeIf(Token::star));
|
|
|
|
}
|
|
|
|
|
2020-12-15 03:53:43 +08:00
|
|
|
/// Parse an optional integer value from the stream.
|
|
|
|
OptionalParseResult parseOptionalInteger(uint64_t &result) override {
|
|
|
|
return parser.parseOptionalInteger(result);
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Attribute Parsing
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
[mlir] Add a new "Pattern Descriptor Language" (PDL) dialect.
PDL presents a high level abstraction for the rewrite pattern infrastructure available in MLIR. This abstraction allows for representing patterns transforming MLIR, as MLIR. This allows for applying all of the benefits that the general MLIR infrastructure provides, to the infrastructure itself. This means that pattern matching can be more easily verified for correctness, targeted by frontends, and optimized.
PDL abstracts over various different aspects of patterns and core MLIR data structures. Patterns are specified via a `pdl.pattern` operation. These operations contain a region body for the "matcher" code, and terminate with a `pdl.rewrite` that either dispatches to an external rewriter or contains a region for the rewrite specified via `pdl`. The types of values in `pdl` are handle types to MLIR C++ types, with `!pdl.attribute`, `!pdl.operation`, and `!pdl.type` directly mapping to `mlir::Attribute`, `mlir::Operation*`, and `mlir::Value` respectively.
An example pattern is shown below:
```mlir
// pdl.pattern contains metadata similarly to a `RewritePattern`.
pdl.pattern : benefit(1) {
// External input operand values are specified via `pdl.input` operations.
// Result types are constrainted via `pdl.type` operations.
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite(%root) {
pdl.replace %root with (%inputOperand)
}
}
```
This is a culmination of the work originally discussed here: https://groups.google.com/a/tensorflow.org/g/mlir/c/j_bn74ByxlQ
Differential Revision: https://reviews.llvm.org/D84578
2020-08-20 03:57:45 +08:00
|
|
|
/// Parse an arbitrary attribute of a given type and return it in result.
|
|
|
|
ParseResult parseAttribute(Attribute &result, Type type) override {
|
2018-11-16 09:53:51 +08:00
|
|
|
result = parser.parseAttribute(type);
|
[mlir] Add a new "Pattern Descriptor Language" (PDL) dialect.
PDL presents a high level abstraction for the rewrite pattern infrastructure available in MLIR. This abstraction allows for representing patterns transforming MLIR, as MLIR. This allows for applying all of the benefits that the general MLIR infrastructure provides, to the infrastructure itself. This means that pattern matching can be more easily verified for correctness, targeted by frontends, and optimized.
PDL abstracts over various different aspects of patterns and core MLIR data structures. Patterns are specified via a `pdl.pattern` operation. These operations contain a region body for the "matcher" code, and terminate with a `pdl.rewrite` that either dispatches to an external rewriter or contains a region for the rewrite specified via `pdl`. The types of values in `pdl` are handle types to MLIR C++ types, with `!pdl.attribute`, `!pdl.operation`, and `!pdl.type` directly mapping to `mlir::Attribute`, `mlir::Operation*`, and `mlir::Value` respectively.
An example pattern is shown below:
```mlir
// pdl.pattern contains metadata similarly to a `RewritePattern`.
pdl.pattern : benefit(1) {
// External input operand values are specified via `pdl.input` operations.
// Result types are constrainted via `pdl.type` operations.
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite(%root) {
pdl.replace %root with (%inputOperand)
}
}
```
This is a culmination of the work originally discussed here: https://groups.google.com/a/tensorflow.org/g/mlir/c/j_bn74ByxlQ
Differential Revision: https://reviews.llvm.org/D84578
2020-08-20 03:57:45 +08:00
|
|
|
return success(static_cast<bool>(result));
|
2018-08-03 07:54:36 +08:00
|
|
|
}
|
|
|
|
|
2020-07-15 04:14:14 +08:00
|
|
|
/// Parse an optional attribute.
|
[mlir][PDL] Add a PDL Interpreter Dialect
The PDL Interpreter dialect provides a lower level abstraction compared to the PDL dialect, and is targeted towards low level optimization and interpreter code generation. The dialect operations encapsulates low-level pattern match and rewrite "primitives", such as navigating the IR (Operation::getOperand), creating new operations (OpBuilder::create), etc. Many of the operations within this dialect also fuse branching control flow with some form of a predicate comparison operation. This type of fusion reduces the amount of work that an interpreter must do when executing.
An example of this representation is shown below:
```mlir
// The following high level PDL pattern:
pdl.pattern : benefit(1) {
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite %root {
pdl.replace %root with (%inputOperand)
}
}
// May be represented in the interpreter dialect as follows:
module {
func @matcher(%arg0: !pdl.operation) {
pdl_interp.check_operation_name of %arg0 is "foo.op" -> ^bb2, ^bb1
^bb1:
pdl_interp.return
^bb2:
pdl_interp.check_operand_count of %arg0 is 1 -> ^bb3, ^bb1
^bb3:
pdl_interp.check_result_count of %arg0 is 1 -> ^bb4, ^bb1
^bb4:
%0 = pdl_interp.get_operand 0 of %arg0
pdl_interp.is_not_null %0 : !pdl.value -> ^bb5, ^bb1
^bb5:
%1 = pdl_interp.get_result 0 of %arg0
pdl_interp.is_not_null %1 : !pdl.value -> ^bb6, ^bb1
^bb6:
pdl_interp.record_match @rewriters::@rewriter(%0, %arg0 : !pdl.value, !pdl.operation) : benefit(1), loc([%arg0]), root("foo.op") -> ^bb1
}
module @rewriters {
func @rewriter(%arg0: !pdl.value, %arg1: !pdl.operation) {
pdl_interp.replace %arg1 with(%arg0)
pdl_interp.return
}
}
}
```
Differential Revision: https://reviews.llvm.org/D84579
2020-08-26 20:12:07 +08:00
|
|
|
template <typename AttrT>
|
|
|
|
OptionalParseResult
|
|
|
|
parseOptionalAttributeAndAddToList(AttrT &result, Type type,
|
|
|
|
StringRef attrName, NamedAttrList &attrs) {
|
2020-07-15 04:14:14 +08:00
|
|
|
OptionalParseResult parseResult =
|
|
|
|
parser.parseOptionalAttribute(result, type);
|
|
|
|
if (parseResult.hasValue() && succeeded(*parseResult))
|
|
|
|
attrs.push_back(parser.builder.getNamedAttr(attrName, result));
|
|
|
|
return parseResult;
|
|
|
|
}
|
[mlir][PDL] Add a PDL Interpreter Dialect
The PDL Interpreter dialect provides a lower level abstraction compared to the PDL dialect, and is targeted towards low level optimization and interpreter code generation. The dialect operations encapsulates low-level pattern match and rewrite "primitives", such as navigating the IR (Operation::getOperand), creating new operations (OpBuilder::create), etc. Many of the operations within this dialect also fuse branching control flow with some form of a predicate comparison operation. This type of fusion reduces the amount of work that an interpreter must do when executing.
An example of this representation is shown below:
```mlir
// The following high level PDL pattern:
pdl.pattern : benefit(1) {
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite %root {
pdl.replace %root with (%inputOperand)
}
}
// May be represented in the interpreter dialect as follows:
module {
func @matcher(%arg0: !pdl.operation) {
pdl_interp.check_operation_name of %arg0 is "foo.op" -> ^bb2, ^bb1
^bb1:
pdl_interp.return
^bb2:
pdl_interp.check_operand_count of %arg0 is 1 -> ^bb3, ^bb1
^bb3:
pdl_interp.check_result_count of %arg0 is 1 -> ^bb4, ^bb1
^bb4:
%0 = pdl_interp.get_operand 0 of %arg0
pdl_interp.is_not_null %0 : !pdl.value -> ^bb5, ^bb1
^bb5:
%1 = pdl_interp.get_result 0 of %arg0
pdl_interp.is_not_null %1 : !pdl.value -> ^bb6, ^bb1
^bb6:
pdl_interp.record_match @rewriters::@rewriter(%0, %arg0 : !pdl.value, !pdl.operation) : benefit(1), loc([%arg0]), root("foo.op") -> ^bb1
}
module @rewriters {
func @rewriter(%arg0: !pdl.value, %arg1: !pdl.operation) {
pdl_interp.replace %arg1 with(%arg0)
pdl_interp.return
}
}
}
```
Differential Revision: https://reviews.llvm.org/D84579
2020-08-26 20:12:07 +08:00
|
|
|
OptionalParseResult parseOptionalAttribute(Attribute &result, Type type,
|
|
|
|
StringRef attrName,
|
|
|
|
NamedAttrList &attrs) override {
|
|
|
|
return parseOptionalAttributeAndAddToList(result, type, attrName, attrs);
|
|
|
|
}
|
2020-09-01 03:33:55 +08:00
|
|
|
OptionalParseResult parseOptionalAttribute(ArrayAttr &result, Type type,
|
[mlir][PDL] Add a PDL Interpreter Dialect
The PDL Interpreter dialect provides a lower level abstraction compared to the PDL dialect, and is targeted towards low level optimization and interpreter code generation. The dialect operations encapsulates low-level pattern match and rewrite "primitives", such as navigating the IR (Operation::getOperand), creating new operations (OpBuilder::create), etc. Many of the operations within this dialect also fuse branching control flow with some form of a predicate comparison operation. This type of fusion reduces the amount of work that an interpreter must do when executing.
An example of this representation is shown below:
```mlir
// The following high level PDL pattern:
pdl.pattern : benefit(1) {
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite %root {
pdl.replace %root with (%inputOperand)
}
}
// May be represented in the interpreter dialect as follows:
module {
func @matcher(%arg0: !pdl.operation) {
pdl_interp.check_operation_name of %arg0 is "foo.op" -> ^bb2, ^bb1
^bb1:
pdl_interp.return
^bb2:
pdl_interp.check_operand_count of %arg0 is 1 -> ^bb3, ^bb1
^bb3:
pdl_interp.check_result_count of %arg0 is 1 -> ^bb4, ^bb1
^bb4:
%0 = pdl_interp.get_operand 0 of %arg0
pdl_interp.is_not_null %0 : !pdl.value -> ^bb5, ^bb1
^bb5:
%1 = pdl_interp.get_result 0 of %arg0
pdl_interp.is_not_null %1 : !pdl.value -> ^bb6, ^bb1
^bb6:
pdl_interp.record_match @rewriters::@rewriter(%0, %arg0 : !pdl.value, !pdl.operation) : benefit(1), loc([%arg0]), root("foo.op") -> ^bb1
}
module @rewriters {
func @rewriter(%arg0: !pdl.value, %arg1: !pdl.operation) {
pdl_interp.replace %arg1 with(%arg0)
pdl_interp.return
}
}
}
```
Differential Revision: https://reviews.llvm.org/D84579
2020-08-26 20:12:07 +08:00
|
|
|
StringRef attrName,
|
|
|
|
NamedAttrList &attrs) override {
|
2020-11-03 03:21:29 +08:00
|
|
|
return parseOptionalAttributeAndAddToList(result, type, attrName, attrs);
|
|
|
|
}
|
|
|
|
OptionalParseResult parseOptionalAttribute(StringAttr &result, Type type,
|
|
|
|
StringRef attrName,
|
|
|
|
NamedAttrList &attrs) override {
|
2020-09-01 03:33:55 +08:00
|
|
|
return parseOptionalAttributeAndAddToList(result, type, attrName, attrs);
|
[mlir][PDL] Add a PDL Interpreter Dialect
The PDL Interpreter dialect provides a lower level abstraction compared to the PDL dialect, and is targeted towards low level optimization and interpreter code generation. The dialect operations encapsulates low-level pattern match and rewrite "primitives", such as navigating the IR (Operation::getOperand), creating new operations (OpBuilder::create), etc. Many of the operations within this dialect also fuse branching control flow with some form of a predicate comparison operation. This type of fusion reduces the amount of work that an interpreter must do when executing.
An example of this representation is shown below:
```mlir
// The following high level PDL pattern:
pdl.pattern : benefit(1) {
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite %root {
pdl.replace %root with (%inputOperand)
}
}
// May be represented in the interpreter dialect as follows:
module {
func @matcher(%arg0: !pdl.operation) {
pdl_interp.check_operation_name of %arg0 is "foo.op" -> ^bb2, ^bb1
^bb1:
pdl_interp.return
^bb2:
pdl_interp.check_operand_count of %arg0 is 1 -> ^bb3, ^bb1
^bb3:
pdl_interp.check_result_count of %arg0 is 1 -> ^bb4, ^bb1
^bb4:
%0 = pdl_interp.get_operand 0 of %arg0
pdl_interp.is_not_null %0 : !pdl.value -> ^bb5, ^bb1
^bb5:
%1 = pdl_interp.get_result 0 of %arg0
pdl_interp.is_not_null %1 : !pdl.value -> ^bb6, ^bb1
^bb6:
pdl_interp.record_match @rewriters::@rewriter(%0, %arg0 : !pdl.value, !pdl.operation) : benefit(1), loc([%arg0]), root("foo.op") -> ^bb1
}
module @rewriters {
func @rewriter(%arg0: !pdl.value, %arg1: !pdl.operation) {
pdl_interp.replace %arg1 with(%arg0)
pdl_interp.return
}
}
}
```
Differential Revision: https://reviews.llvm.org/D84579
2020-08-26 20:12:07 +08:00
|
|
|
}
|
2020-07-15 04:14:14 +08:00
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a named dictionary into 'result' if it is present.
|
2020-05-07 04:48:36 +08:00
|
|
|
ParseResult parseOptionalAttrDict(NamedAttrList &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-11-06 09:58:16 +08:00
|
|
|
/// Parse a named dictionary into 'result' if the `attributes` keyword is
|
|
|
|
/// present.
|
2020-05-07 04:48:36 +08:00
|
|
|
ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result) override {
|
2019-11-06 09:58:16 +08:00
|
|
|
if (failed(parseOptionalKeyword("attributes")))
|
|
|
|
return success();
|
|
|
|
return parser.parseAttributeDict(result);
|
|
|
|
}
|
|
|
|
|
2020-01-14 05:12:37 +08:00
|
|
|
/// Parse an affine map instance into 'map'.
|
|
|
|
ParseResult parseAffineMap(AffineMap &map) override {
|
|
|
|
return parser.parseAffineMapReference(map);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse an integer set instance into 'set'.
|
|
|
|
ParseResult printIntegerSet(IntegerSet &set) override {
|
|
|
|
return parser.parseIntegerSetReference(set);
|
|
|
|
}
|
|
|
|
|
2019-08-09 20:01:23 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Identifier Parsing
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
2020-08-30 15:59:53 +08:00
|
|
|
/// Returns true if the current token corresponds to a keyword.
|
2019-09-18 08:54:54 +08:00
|
|
|
bool isCurrentTokenAKeyword() const {
|
|
|
|
return parser.getToken().is(Token::bare_identifier) ||
|
|
|
|
parser.getToken().isKeyword();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse the given keyword if present.
|
|
|
|
ParseResult parseOptionalKeyword(StringRef keyword) override {
|
|
|
|
// Check that the current token has the same spelling.
|
|
|
|
if (!isCurrentTokenAKeyword() || parser.getTokenSpelling() != keyword)
|
|
|
|
return failure();
|
|
|
|
parser.consumeToken();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a keyword, if present, into 'keyword'.
|
|
|
|
ParseResult parseOptionalKeyword(StringRef *keyword) override {
|
|
|
|
// Check that the current token is a keyword.
|
|
|
|
if (!isCurrentTokenAKeyword())
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
*keyword = parser.getTokenSpelling();
|
|
|
|
parser.consumeToken();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2020-11-10 00:23:55 +08:00
|
|
|
/// Parse a keyword if it is one of the 'allowedKeywords'.
|
|
|
|
ParseResult
|
|
|
|
parseOptionalKeyword(StringRef *keyword,
|
|
|
|
ArrayRef<StringRef> allowedKeywords) override {
|
|
|
|
// Check that the current token is a keyword.
|
|
|
|
if (!isCurrentTokenAKeyword())
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
StringRef currentKeyword = parser.getTokenSpelling();
|
|
|
|
if (llvm::is_contained(allowedKeywords, currentKeyword)) {
|
|
|
|
*keyword = currentKeyword;
|
|
|
|
parser.consumeToken();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
|
2019-11-14 01:31:45 +08:00
|
|
|
/// Parse an optional @-identifier and store it (without the '@' symbol) in a
|
|
|
|
/// string attribute named 'attrName'.
|
2020-05-07 04:48:36 +08:00
|
|
|
ParseResult parseOptionalSymbolName(StringAttr &result, StringRef attrName,
|
|
|
|
NamedAttrList &attrs) override {
|
2019-10-09 08:44:39 +08:00
|
|
|
Token atToken = parser.getToken();
|
|
|
|
if (atToken.isNot(Token::at_identifier))
|
2019-08-09 20:01:23 +08:00
|
|
|
return failure();
|
2019-10-09 08:44:39 +08:00
|
|
|
|
2020-06-11 07:58:55 +08:00
|
|
|
result = getBuilder().getStringAttr(atToken.getSymbolReference());
|
2019-08-09 20:01:23 +08:00
|
|
|
attrs.push_back(getBuilder().getNamedAttr(attrName, result));
|
|
|
|
parser.consumeToken();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Operand Parsing
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Parse a single operand.
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseOperand(OperandType &result) override {
|
2019-06-06 08:04:37 +08:00
|
|
|
OperationParser::SSAUseInfo useInfo;
|
2018-07-26 02:15:20 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-03-25 23:25:52 +08:00
|
|
|
/// Parse a single operand if present.
|
|
|
|
OptionalParseResult parseOptionalOperand(OperandType &result) override {
|
|
|
|
if (parser.getToken().is(Token::percent_identifier))
|
|
|
|
return parseOperand(result);
|
|
|
|
return llvm::None;
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse zero or more SSA comma-separated operand references with a specified
|
|
|
|
/// surrounding delimiter, and an optional required operand count.
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseOperandList(SmallVectorImpl<OperandType> &result,
|
|
|
|
int requiredOperandCount = -1,
|
|
|
|
Delimiter delimiter = Delimiter::None) override {
|
2019-07-23 08:41:38 +08:00
|
|
|
return parseOperandOrRegionArgList(result, /*isOperandList=*/true,
|
|
|
|
requiredOperandCount, delimiter);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse zero or more SSA comma-separated operand or region arguments with
|
|
|
|
/// optional surrounding delimiter and required operand count.
|
|
|
|
ParseResult
|
|
|
|
parseOperandOrRegionArgList(SmallVectorImpl<OperandType> &result,
|
|
|
|
bool isOperandList, int requiredOperandCount = -1,
|
|
|
|
Delimiter delimiter = Delimiter::None) {
|
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 {
|
2019-07-23 08:41:38 +08:00
|
|
|
OperandType operandOrArg;
|
|
|
|
if (isOperandList ? parseOperand(operandOrArg)
|
|
|
|
: parseRegionArgument(operandOrArg))
|
2019-05-07 13:01:31 +08:00
|
|
|
return failure();
|
2019-07-23 08:41:38 +08:00
|
|
|
result.push_back(operandOrArg);
|
2018-07-26 02:15:20 +08:00
|
|
|
} 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-06-06 05:49:52 +08:00
|
|
|
/// Parse zero or more trailing SSA comma-separated trailing operand
|
|
|
|
/// references with a specified surrounding delimiter, and an optional
|
|
|
|
/// required operand count. A leading comma is expected before the operands.
|
|
|
|
ParseResult parseTrailingOperandList(SmallVectorImpl<OperandType> &result,
|
|
|
|
int requiredOperandCount,
|
|
|
|
Delimiter delimiter) override {
|
|
|
|
if (parser.getToken().is(Token::comma)) {
|
|
|
|
parseComma();
|
|
|
|
return parseOperandList(result, requiredOperandCount, delimiter);
|
|
|
|
}
|
|
|
|
if (requiredOperandCount != -1)
|
|
|
|
return emitError(parser.getToken().getLoc(), "expected ")
|
|
|
|
<< requiredOperandCount << " operands";
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Resolve an operand to an SSA value, emitting an error on failure.
|
|
|
|
ParseResult resolveOperand(const OperandType &operand, Type type,
|
2019-12-24 06:45:01 +08:00
|
|
|
SmallVectorImpl<Value> &result) override {
|
2019-06-06 08:04:37 +08:00
|
|
|
OperationParser::SSAUseInfo operandInfo = {operand.name, operand.number,
|
|
|
|
operand.location};
|
2019-12-23 13:59:55 +08:00
|
|
|
if (auto value = parser.resolveSSAUse(operandInfo, type)) {
|
2019-06-06 05:49:52 +08:00
|
|
|
result.push_back(value);
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
|
2019-06-24 22:31:52 +08:00
|
|
|
/// Parse an AffineMap of SSA ids.
|
[MLIR][Affine] Add affine.parallel op
Summary:
As discussed in https://llvm.discourse.group/t/rfc-add-affine-parallel/350, this is the first in a series of patches to bring in support for the `affine.parallel` operation.
This first patch adds the IR representation along with custom printer/parser implementations.
Reviewers: bondhugula, herhut, mehdi_amini, nicolasvasilache, rriddle, earhart, jbruestle
Reviewed By: bondhugula, nicolasvasilache, rriddle, earhart, jbruestle
Subscribers: jpienaar, burmako, shauheen, antiagainst, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74288
2020-02-13 09:59:57 +08:00
|
|
|
ParseResult parseAffineMapOfSSAIds(SmallVectorImpl<OperandType> &operands,
|
|
|
|
Attribute &mapAttr, StringRef attrName,
|
2020-05-07 04:48:36 +08:00
|
|
|
NamedAttrList &attrs,
|
[MLIR][Affine] Add affine.parallel op
Summary:
As discussed in https://llvm.discourse.group/t/rfc-add-affine-parallel/350, this is the first in a series of patches to bring in support for the `affine.parallel` operation.
This first patch adds the IR representation along with custom printer/parser implementations.
Reviewers: bondhugula, herhut, mehdi_amini, nicolasvasilache, rriddle, earhart, jbruestle
Reviewed By: bondhugula, nicolasvasilache, rriddle, earhart, jbruestle
Subscribers: jpienaar, burmako, shauheen, antiagainst, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74288
2020-02-13 09:59:57 +08:00
|
|
|
Delimiter delimiter) override {
|
2019-06-24 22:31:52 +08:00
|
|
|
SmallVector<OperandType, 2> dimOperands;
|
|
|
|
SmallVector<OperandType, 1> symOperands;
|
|
|
|
|
|
|
|
auto parseElement = [&](bool isSymbol) -> ParseResult {
|
|
|
|
OperandType operand;
|
|
|
|
if (parseOperand(operand))
|
|
|
|
return failure();
|
|
|
|
if (isSymbol)
|
|
|
|
symOperands.push_back(operand);
|
|
|
|
else
|
|
|
|
dimOperands.push_back(operand);
|
|
|
|
return success();
|
|
|
|
};
|
|
|
|
|
|
|
|
AffineMap map;
|
[MLIR][Affine] Add affine.parallel op
Summary:
As discussed in https://llvm.discourse.group/t/rfc-add-affine-parallel/350, this is the first in a series of patches to bring in support for the `affine.parallel` operation.
This first patch adds the IR representation along with custom printer/parser implementations.
Reviewers: bondhugula, herhut, mehdi_amini, nicolasvasilache, rriddle, earhart, jbruestle
Reviewed By: bondhugula, nicolasvasilache, rriddle, earhart, jbruestle
Subscribers: jpienaar, burmako, shauheen, antiagainst, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74288
2020-02-13 09:59:57 +08:00
|
|
|
if (parser.parseAffineMapOfSSAIds(map, parseElement, delimiter))
|
2019-06-24 22:31:52 +08:00
|
|
|
return failure();
|
|
|
|
// Add AffineMap attribute.
|
2019-07-04 01:35:03 +08:00
|
|
|
if (map) {
|
2019-10-18 11:08:01 +08:00
|
|
|
mapAttr = AffineMapAttr::get(map);
|
2019-07-04 01:35:03 +08:00
|
|
|
attrs.push_back(parser.builder.getNamedAttr(attrName, mapAttr));
|
|
|
|
}
|
2019-06-24 22:31:52 +08:00
|
|
|
|
|
|
|
// Add dim operands before symbol operands in 'operands'.
|
|
|
|
operands.assign(dimOperands.begin(), dimOperands.end());
|
|
|
|
operands.append(symOperands.begin(), symOperands.end());
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Region Parsing
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
2019-04-27 05:46:13 +08:00
|
|
|
/// Parse a region that takes `arguments` of `argTypes` types. This
|
2019-10-20 15:11:03 +08:00
|
|
|
/// effectively defines the SSA values of `arguments` and assigns their type.
|
2019-05-07 13:01:31 +08:00
|
|
|
ParseResult parseRegion(Region ®ion, ArrayRef<OperandType> arguments,
|
2019-08-20 06:26:43 +08:00
|
|
|
ArrayRef<Type> argTypes,
|
|
|
|
bool enableNameShadowing) override {
|
2019-04-27 05:46:13 +08:00
|
|
|
assert(arguments.size() == argTypes.size() &&
|
|
|
|
"mismatching number of arguments and types");
|
|
|
|
|
2019-06-06 08:04:37 +08:00
|
|
|
SmallVector<std::pair<OperationParser::SSAUseInfo, Type>, 2>
|
|
|
|
regionArguments;
|
2020-01-02 07:55:14 +08:00
|
|
|
for (auto pair : llvm::zip(arguments, argTypes)) {
|
2019-04-27 05:46:13 +08:00
|
|
|
const OperandType &operand = std::get<0>(pair);
|
|
|
|
Type type = std::get<1>(pair);
|
2019-06-06 08:04:37 +08:00
|
|
|
OperationParser::SSAUseInfo operandInfo = {operand.name, operand.number,
|
|
|
|
operand.location};
|
2019-04-27 05:46:13 +08:00
|
|
|
regionArguments.emplace_back(operandInfo, type);
|
|
|
|
}
|
2019-02-02 08:42:18 +08:00
|
|
|
|
2019-08-20 06:26:43 +08:00
|
|
|
// Try to parse the region.
|
|
|
|
assert((!enableNameShadowing ||
|
2021-02-10 03:41:10 +08:00
|
|
|
opDefinition->hasTrait<OpTrait::IsIsolatedFromAbove>()) &&
|
2019-08-20 06:26:43 +08:00
|
|
|
"name shadowing is only allowed on isolated regions");
|
|
|
|
if (parser.parseRegion(region, regionArguments, enableNameShadowing))
|
|
|
|
return failure();
|
|
|
|
return success();
|
2019-01-29 13:23:53 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parses a region if present.
|
2020-12-03 08:49:47 +08:00
|
|
|
OptionalParseResult parseOptionalRegion(Region ®ion,
|
|
|
|
ArrayRef<OperandType> arguments,
|
|
|
|
ArrayRef<Type> argTypes,
|
|
|
|
bool enableNameShadowing) override {
|
2019-07-04 04:21:24 +08:00
|
|
|
if (parser.getToken().isNot(Token::l_brace))
|
2020-12-03 08:49:47 +08:00
|
|
|
return llvm::None;
|
2019-08-20 06:26:43 +08:00
|
|
|
return parseRegion(region, arguments, argTypes, enableNameShadowing);
|
2019-06-04 03:08:22 +08:00
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// Parses a region if present. If the region is present, a new region is
|
|
|
|
/// allocated and placed in `region`. If no region is present, `region`
|
|
|
|
/// remains untouched.
|
|
|
|
OptionalParseResult
|
|
|
|
parseOptionalRegion(std::unique_ptr<Region> ®ion,
|
|
|
|
ArrayRef<OperandType> arguments, ArrayRef<Type> argTypes,
|
|
|
|
bool enableNameShadowing = false) override {
|
|
|
|
if (parser.getToken().isNot(Token::l_brace))
|
|
|
|
return llvm::None;
|
|
|
|
std::unique_ptr<Region> newRegion = std::make_unique<Region>();
|
|
|
|
if (parseRegion(*newRegion, arguments, argTypes, enableNameShadowing))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
region = std::move(newRegion);
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2019-08-20 06:26:43 +08:00
|
|
|
/// Parse a region argument. 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-08-20 06:26:43 +08:00
|
|
|
return parseOperand(argument);
|
2019-02-02 08:42:18 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a region argument if present.
|
2019-06-04 03:08:22 +08:00
|
|
|
ParseResult parseOptionalRegionArgument(OperandType &argument) override {
|
|
|
|
if (parser.getToken().isNot(Token::percent_identifier))
|
|
|
|
return success();
|
|
|
|
return parseRegionArgument(argument);
|
|
|
|
}
|
|
|
|
|
2019-07-23 08:41:38 +08:00
|
|
|
ParseResult
|
|
|
|
parseRegionArgumentList(SmallVectorImpl<OperandType> &result,
|
|
|
|
int requiredOperandCount = -1,
|
|
|
|
Delimiter delimiter = Delimiter::None) override {
|
|
|
|
return parseOperandOrRegionArgList(result, /*isOperandList=*/false,
|
|
|
|
requiredOperandCount, delimiter);
|
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
2019-06-06 05:49:52 +08:00
|
|
|
// Successor Parsing
|
2018-07-26 02:15:20 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
2020-03-06 04:48:28 +08:00
|
|
|
/// Parse a single operation successor.
|
|
|
|
ParseResult parseSuccessor(Block *&dest) override {
|
|
|
|
return parser.parseSuccessor(dest);
|
2019-06-06 05:49:52 +08:00
|
|
|
}
|
2018-07-26 02:15:20 +08:00
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
/// Parse an optional operation successor and its operand list.
|
2020-03-06 04:48:28 +08:00
|
|
|
OptionalParseResult parseOptionalSuccessor(Block *&dest) override {
|
2020-02-22 05:20:06 +08:00
|
|
|
if (parser.getToken().isNot(Token::caret_identifier))
|
|
|
|
return llvm::None;
|
2020-03-06 04:48:28 +08:00
|
|
|
return parseSuccessor(dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a single operation successor and its operand list.
|
|
|
|
ParseResult
|
|
|
|
parseSuccessorAndUseList(Block *&dest,
|
|
|
|
SmallVectorImpl<Value> &operands) override {
|
|
|
|
if (parseSuccessor(dest))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Handle optional arguments.
|
|
|
|
if (succeeded(parseOptionalLParen()) &&
|
|
|
|
(parser.parseOptionalSSAUseAndTypeList(operands) || parseRParen())) {
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
return success();
|
2020-02-22 05:20:06 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Type Parsing
|
|
|
|
//===--------------------------------------------------------------------===//
|
2018-07-26 02:15:20 +08:00
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a type.
|
|
|
|
ParseResult parseType(Type &result) override {
|
|
|
|
return failure(!(result = parser.parseType()));
|
|
|
|
}
|
|
|
|
|
2020-04-11 05:11:45 +08:00
|
|
|
/// Parse an optional type.
|
|
|
|
OptionalParseResult parseOptionalType(Type &result) override {
|
|
|
|
return parser.parseOptionalType(result);
|
|
|
|
}
|
|
|
|
|
2020-02-22 01:58:00 +08:00
|
|
|
/// Parse an arrow followed by a type list.
|
|
|
|
ParseResult parseArrowTypeList(SmallVectorImpl<Type> &result) override {
|
|
|
|
if (parseArrow() || parser.parseFunctionResultTypes(result))
|
|
|
|
return failure();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse an optional arrow followed by a type list.
|
2019-06-21 15:44:11 +08:00
|
|
|
ParseResult
|
|
|
|
parseOptionalArrowTypeList(SmallVectorImpl<Type> &result) override {
|
2019-06-06 05:49:52 +08:00
|
|
|
if (!parser.consumeIf(Token::arrow))
|
2019-05-07 13:01:31 +08:00
|
|
|
return success();
|
2019-06-06 05:49:52 +08:00
|
|
|
return parser.parseFunctionResultTypes(result);
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a colon followed by a type.
|
|
|
|
ParseResult parseColonType(Type &result) override {
|
|
|
|
return failure(parser.parseToken(Token::colon, "expected ':'") ||
|
|
|
|
!(result = parser.parseType()));
|
2018-07-26 02:15:20 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 05:49:52 +08:00
|
|
|
/// Parse a colon followed by a type list, which must have at least one type.
|
|
|
|
ParseResult parseColonTypeList(SmallVectorImpl<Type> &result) override {
|
|
|
|
if (parser.parseToken(Token::colon, "expected ':'"))
|
|
|
|
return failure();
|
|
|
|
return parser.parseTypeListNoParens(result);
|
|
|
|
}
|
2018-07-26 02:15:20 +08:00
|
|
|
|
2019-06-07 06:58:47 +08:00
|
|
|
/// Parse an optional colon followed by a type list, which if present must
|
|
|
|
/// have at least one type.
|
|
|
|
ParseResult
|
|
|
|
parseOptionalColonTypeList(SmallVectorImpl<Type> &result) override {
|
|
|
|
if (!parser.consumeIf(Token::colon))
|
|
|
|
return success();
|
|
|
|
return parser.parseTypeListNoParens(result);
|
|
|
|
}
|
|
|
|
|
2020-02-22 01:58:00 +08:00
|
|
|
/// Parse a list of assignments of the form
|
2020-11-04 16:41:55 +08:00
|
|
|
/// (%x1 = %y1, %x2 = %y2, ...).
|
|
|
|
OptionalParseResult
|
|
|
|
parseOptionalAssignmentList(SmallVectorImpl<OperandType> &lhs,
|
|
|
|
SmallVectorImpl<OperandType> &rhs) override {
|
|
|
|
if (failed(parseOptionalLParen()))
|
|
|
|
return llvm::None;
|
|
|
|
|
2020-02-22 01:58:00 +08:00
|
|
|
auto parseElt = [&]() -> ParseResult {
|
|
|
|
OperandType regionArg, operand;
|
|
|
|
if (parseRegionArgument(regionArg) || parseEqual() ||
|
|
|
|
parseOperand(operand))
|
|
|
|
return failure();
|
|
|
|
lhs.push_back(regionArg);
|
|
|
|
rhs.push_back(operand);
|
|
|
|
return success();
|
|
|
|
};
|
|
|
|
return parser.parseCommaSeparatedListUntil(Token::r_paren, parseElt);
|
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
private:
|
2019-06-06 05:49:52 +08:00
|
|
|
/// The source location of the operation name.
|
2018-07-26 02:15:20 +08:00
|
|
|
SMLoc nameLoc;
|
2019-06-06 05:49:52 +08:00
|
|
|
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
/// Information about the result name specifiers.
|
|
|
|
ArrayRef<OperationParser::ResultRecord> resultIDs;
|
|
|
|
|
2019-08-20 06:26:43 +08:00
|
|
|
/// The abstract information of the operation.
|
|
|
|
const AbstractOperation *opDefinition;
|
2019-06-06 05:49:52 +08:00
|
|
|
|
|
|
|
/// The main operation parser.
|
2019-06-06 08:04:37 +08:00
|
|
|
OperationParser &parser;
|
2019-06-06 05:49:52 +08:00
|
|
|
|
|
|
|
/// A flag that indicates if any errors were emitted during parsing.
|
2018-07-26 02:15:20 +08:00
|
|
|
bool emittedError = false;
|
|
|
|
};
|
|
|
|
} // end anonymous namespace.
|
|
|
|
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
Operation *
|
|
|
|
OperationParser::parseCustomOperation(ArrayRef<ResultRecord> resultIDs) {
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
llvm::SMLoc opLoc = getToken().getLoc();
|
|
|
|
StringRef opName = getTokenSpelling();
|
2018-07-26 02:15:20 +08:00
|
|
|
|
2018-10-22 10:49:31 +08:00
|
|
|
auto *opDefinition = AbstractOperation::lookup(opName, getContext());
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
if (!opDefinition) {
|
|
|
|
if (opName.contains('.')) {
|
|
|
|
// This op has a dialect, we try to check if we can register it in the
|
|
|
|
// context on the fly.
|
|
|
|
StringRef dialectName = opName.split('.').first;
|
|
|
|
if (!getContext()->getLoadedDialect(dialectName) &&
|
|
|
|
getContext()->getOrLoadDialect(dialectName)) {
|
|
|
|
opDefinition = AbstractOperation::lookup(opName, getContext());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// 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?
|
|
|
|
if (getContext()->getOrLoadDialect("std"))
|
|
|
|
opDefinition = AbstractOperation::lookup(Twine("std." + opName).str(),
|
|
|
|
getContext());
|
|
|
|
}
|
2019-03-03 10:03:03 +08:00
|
|
|
}
|
|
|
|
|
2018-07-26 02:15:20 +08:00
|
|
|
if (!opDefinition) {
|
2019-08-20 06:26:43 +08:00
|
|
|
emitError(opLoc) << "custom op '" << opName << "' is unknown";
|
2018-07-26 02:15:20 +08:00
|
|
|
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-06-23 02:08:52 +08:00
|
|
|
OperationState opState(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};
|
Add support for custom op parser/printer hooks to know about result names.
Summary:
This allows the custom parser/printer hooks to do interesting things with
the SSA names. This patch:
- Adds a new 'getResultName' method to OpAsmParser that allows a parser
implementation to get information about its result names, along with
a getNumResults() method that allows op parser impls to know how many
results are expected.
- Adds a OpAsmPrinter::printOperand overload that takes an explicit stream.
- Adds a test.string_attr_pretty_name operation that uses these hooks to
do fancy things with the result name.
Reviewers: rriddle!
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76205
2020-03-16 08:13:59 +08:00
|
|
|
CustomOpAsmParser opAsmParser(opLoc, resultIDs, opDefinition, *this);
|
2019-09-21 10:47:05 +08:00
|
|
|
if (opAsmParser.parseOperation(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;
|
|
|
|
|
2020-11-13 15:33:43 +08:00
|
|
|
// Otherwise, create the operation and try to parse a location for it.
|
|
|
|
Operation *op = opBuilder.createOperation(opState);
|
|
|
|
if (parseTrailingOperationLocation(op))
|
2019-10-29 06:11:00 +08:00
|
|
|
return nullptr;
|
2020-11-13 15:33:43 +08:00
|
|
|
return op;
|
|
|
|
}
|
|
|
|
|
|
|
|
ParseResult OperationParser::parseTrailingOperationLocation(Operation *op) {
|
|
|
|
// If there is a 'loc' we parse a trailing location.
|
|
|
|
if (!consumeIf(Token::kw_loc))
|
|
|
|
return success();
|
|
|
|
if (parseToken(Token::l_paren, "expected '(' in location"))
|
|
|
|
return failure();
|
|
|
|
Token tok = getToken();
|
|
|
|
|
|
|
|
// Check to see if we are parsing a location alias.
|
|
|
|
LocationAttr directLoc;
|
|
|
|
if (tok.is(Token::hash_identifier)) {
|
|
|
|
consumeToken();
|
|
|
|
|
|
|
|
StringRef identifier = tok.getSpelling().drop_front();
|
|
|
|
if (identifier.contains('.')) {
|
|
|
|
return emitError(tok.getLoc())
|
|
|
|
<< "expected location, but found dialect attribute: '#"
|
|
|
|
<< identifier << "'";
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this alias can be resolved, do it now.
|
|
|
|
Attribute attr =
|
|
|
|
getState().symbols.attributeAliasDefinitions.lookup(identifier);
|
|
|
|
if (attr) {
|
|
|
|
if (!(directLoc = attr.dyn_cast<LocationAttr>()))
|
|
|
|
return emitError(tok.getLoc())
|
|
|
|
<< "expected location, but found '" << attr << "'";
|
|
|
|
} else {
|
|
|
|
// Otherwise, remember this operation and resolve its location later.
|
|
|
|
opsWithDeferredLocs.emplace_back(op, tok);
|
|
|
|
}
|
2019-10-29 06:11:00 +08:00
|
|
|
|
2020-11-13 15:33:43 +08:00
|
|
|
// Otherwise, we parse the location directly.
|
|
|
|
} else if (parseLocationInstance(directLoc)) {
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parseToken(Token::r_paren, "expected ')' in location"))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
if (directLoc)
|
|
|
|
op->setLoc(directLoc);
|
|
|
|
return success();
|
2018-06-29 08:02:32 +08:00
|
|
|
}
|
|
|
|
|
2019-06-06 02:57:37 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Region Parsing
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Region.
|
|
|
|
///
|
|
|
|
/// region ::= '{' region-body
|
|
|
|
///
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::parseRegion(
|
2019-06-06 02:57:37 +08:00
|
|
|
Region ®ion,
|
2019-08-20 06:26:43 +08:00
|
|
|
ArrayRef<std::pair<OperationParser::SSAUseInfo, Type>> entryArguments,
|
|
|
|
bool isIsolatedNameScope) {
|
2019-06-06 02:57:37 +08:00
|
|
|
// Parse the '{'.
|
|
|
|
if (parseToken(Token::l_brace, "expected '{' to begin a region"))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Check for an empty region.
|
|
|
|
if (entryArguments.empty() && consumeIf(Token::r_brace))
|
|
|
|
return success();
|
2019-06-06 04:59:28 +08:00
|
|
|
auto currentPt = opBuilder.saveInsertionPoint();
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
// Push a new named value scope.
|
2019-08-20 06:26:43 +08:00
|
|
|
pushSSANameScope(isIsolatedNameScope);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
// Parse the first block directly to allow for it to be unnamed.
|
2020-07-12 04:05:37 +08:00
|
|
|
auto owning_block = std::make_unique<Block>();
|
|
|
|
Block *block = owning_block.get();
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
// Add arguments to the entry block.
|
|
|
|
if (!entryArguments.empty()) {
|
2019-08-20 06:26:43 +08:00
|
|
|
for (auto &placeholderArgPair : entryArguments) {
|
|
|
|
auto &argInfo = placeholderArgPair.first;
|
|
|
|
// Ensure that the argument was not already defined.
|
|
|
|
if (auto defLoc = getReferenceLoc(argInfo.name, argInfo.number)) {
|
|
|
|
return emitError(argInfo.loc, "region entry argument '" + argInfo.name +
|
|
|
|
"' is already in use")
|
|
|
|
.attachNote(getEncodedSourceLocation(*defLoc))
|
|
|
|
<< "previously referenced here";
|
|
|
|
}
|
2019-06-06 02:57:37 +08:00
|
|
|
if (addDefinition(placeholderArgPair.first,
|
|
|
|
block->addArgument(placeholderArgPair.second))) {
|
|
|
|
return failure();
|
|
|
|
}
|
2019-08-20 06:26:43 +08:00
|
|
|
}
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
// If we had named arguments, then don't allow a block name.
|
|
|
|
if (getToken().is(Token::caret_identifier))
|
|
|
|
return emitError("invalid block name in region with named arguments");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parseBlock(block)) {
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that no other arguments were parsed.
|
|
|
|
if (!entryArguments.empty() &&
|
|
|
|
block->getNumArguments() > entryArguments.size()) {
|
|
|
|
return emitError("entry block arguments were already defined");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the rest of the region.
|
2020-07-12 04:05:37 +08:00
|
|
|
region.push_back(owning_block.release());
|
2019-06-06 02:57:37 +08:00
|
|
|
if (parseRegionBody(region))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Pop the SSA value scope for this region.
|
|
|
|
if (popSSANameScope())
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Reset the original insertion point.
|
2019-06-06 04:59:28 +08:00
|
|
|
opBuilder.restoreInsertionPoint(currentPt);
|
2019-06-06 02:57:37 +08:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Region.
|
|
|
|
///
|
|
|
|
/// region-body ::= block* '}'
|
|
|
|
///
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::parseRegionBody(Region ®ion) {
|
2019-06-06 02:57:37 +08:00
|
|
|
// Parse the list of blocks.
|
|
|
|
while (!consumeIf(Token::r_brace)) {
|
|
|
|
Block *newBlock = nullptr;
|
|
|
|
if (parseBlock(newBlock))
|
|
|
|
return failure();
|
|
|
|
region.push_back(newBlock);
|
|
|
|
}
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Block Parsing
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Block declaration.
|
|
|
|
///
|
|
|
|
/// block ::= block-label? operation*
|
|
|
|
/// block-label ::= block-id block-arg-list? `:`
|
|
|
|
/// block-id ::= caret-id
|
|
|
|
/// block-arg-list ::= `(` ssa-id-and-type-list? `)`
|
|
|
|
///
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::parseBlock(Block *&block) {
|
2019-06-06 02:57:37 +08:00
|
|
|
// The first block of a region may already exist, if it does the caret
|
|
|
|
// identifier is optional.
|
|
|
|
if (block && getToken().isNot(Token::caret_identifier))
|
|
|
|
return parseBlockBody(block);
|
|
|
|
|
|
|
|
SMLoc nameLoc = getToken().getLoc();
|
|
|
|
auto name = getTokenSpelling();
|
|
|
|
if (parseToken(Token::caret_identifier, "expected block name"))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
block = defineBlockNamed(name, nameLoc, block);
|
|
|
|
|
|
|
|
// Fail if the block was already defined.
|
|
|
|
if (!block)
|
|
|
|
return emitError(nameLoc, "redefinition of block '") << name << "'";
|
|
|
|
|
|
|
|
// If an argument list is present, parse it.
|
|
|
|
if (consumeIf(Token::l_paren)) {
|
2019-12-24 06:45:01 +08:00
|
|
|
SmallVector<BlockArgument, 8> bbArgs;
|
2019-06-06 02:57:37 +08:00
|
|
|
if (parseOptionalBlockArgList(bbArgs, block) ||
|
|
|
|
parseToken(Token::r_paren, "expected ')' to end argument list"))
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parseToken(Token::colon, "expected ':' after block name"))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
return parseBlockBody(block);
|
|
|
|
}
|
|
|
|
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::parseBlockBody(Block *block) {
|
2019-06-06 02:57:37 +08:00
|
|
|
// Set the insertion point to the end of the block to parse.
|
2019-06-06 04:59:28 +08:00
|
|
|
opBuilder.setInsertionPointToEnd(block);
|
2019-06-06 02:57:37 +08:00
|
|
|
|
|
|
|
// Parse the list of operations that make up the body of the block.
|
|
|
|
while (getToken().isNot(Token::caret_identifier, Token::r_brace))
|
|
|
|
if (parseOperation())
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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
|
|
|
|
/// us to diagnose references to blocks that are not defined precisely.
|
2019-06-06 08:04:37 +08:00
|
|
|
Block *OperationParser::getBlockNamed(StringRef name, SMLoc loc) {
|
2019-06-06 02:57:37 +08:00
|
|
|
auto &blockAndLoc = getBlockInfoByName(name);
|
|
|
|
if (!blockAndLoc.first) {
|
|
|
|
blockAndLoc = {new Block(), loc};
|
|
|
|
insertForwardRef(blockAndLoc.first, loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
return blockAndLoc.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Define the block with the specified name. Returns the Block* or nullptr in
|
|
|
|
/// the case of redefinition.
|
2019-06-06 08:04:37 +08:00
|
|
|
Block *OperationParser::defineBlockNamed(StringRef name, SMLoc loc,
|
|
|
|
Block *existing) {
|
2019-06-06 02:57:37 +08:00
|
|
|
auto &blockAndLoc = getBlockInfoByName(name);
|
|
|
|
if (!blockAndLoc.first) {
|
|
|
|
// If the caller provided a block, use it. Otherwise create a new one.
|
|
|
|
if (!existing)
|
|
|
|
existing = new Block();
|
|
|
|
blockAndLoc.first = existing;
|
|
|
|
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 (!eraseForwardRef(blockAndLoc.first))
|
|
|
|
return nullptr;
|
|
|
|
return blockAndLoc.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a (possibly empty) list of SSA operands with types as block arguments.
|
|
|
|
///
|
|
|
|
/// ssa-id-and-type-list ::= ssa-id-and-type (`,` ssa-id-and-type)*
|
|
|
|
///
|
2019-06-06 08:04:37 +08:00
|
|
|
ParseResult OperationParser::parseOptionalBlockArgList(
|
2019-12-24 06:45:01 +08:00
|
|
|
SmallVectorImpl<BlockArgument> &results, Block *owner) {
|
2019-06-06 02:57:37 +08:00
|
|
|
if (getToken().is(Token::r_brace))
|
|
|
|
return success();
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
return parseCommaSeparatedList([&]() -> ParseResult {
|
|
|
|
return parseSSADefOrUseAndType(
|
|
|
|
[&](SSAUseInfo useInfo, Type type) -> ParseResult {
|
|
|
|
// If this block did not have existing arguments, define a new one.
|
|
|
|
if (!definingExistingArgs)
|
|
|
|
return addDefinition(useInfo, owner->addArgument(type));
|
|
|
|
|
|
|
|
// Otherwise, ensure that this argument has already been created.
|
|
|
|
if (nextArgument >= owner->getNumArguments())
|
|
|
|
return emitError("too many arguments specified in argument list");
|
|
|
|
|
|
|
|
// Finally, make sure the existing argument has the correct type.
|
2019-12-23 13:59:55 +08:00
|
|
|
auto arg = owner->getArgument(nextArgument++);
|
2020-01-12 00:54:04 +08:00
|
|
|
if (arg.getType() != type)
|
2019-06-06 02:57:37 +08:00
|
|
|
return emitError("argument and block argument type mismatch");
|
|
|
|
return addDefinition(useInfo, arg);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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.
|
2020-12-04 07:46:41 +08:00
|
|
|
class TopLevelOperationParser : public Parser {
|
2018-07-11 01:08:27 +08:00
|
|
|
public:
|
2020-12-04 07:46:41 +08:00
|
|
|
explicit TopLevelOperationParser(ParserState &state) : Parser(state) {}
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
/// Parse a set of operations into the end of the given Block.
|
|
|
|
ParseResult parse(Block *topLevelBlock, Location parserLoc);
|
2018-07-11 01:08:27 +08:00
|
|
|
|
|
|
|
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
|
|
|
};
|
|
|
|
} // 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
|
|
|
///
|
2020-12-04 07:46:41 +08:00
|
|
|
ParseResult TopLevelOperationParser::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-11-02 06:39:30 +08:00
|
|
|
if (getState().symbols.attributeAliasDefinitions.count(aliasName) > 0)
|
2019-05-28 23:57:38 +08:00
|
|
|
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-11-02 06:39:30 +08:00
|
|
|
getState().symbols.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
|
|
|
|
///
|
2020-12-04 07:46:41 +08:00
|
|
|
ParseResult TopLevelOperationParser::parseTypeAliasDef() {
|
2019-01-08 10:42:04 +08:00
|
|
|
assert(getToken().is(Token::exclamation_identifier));
|
|
|
|
StringRef aliasName = getTokenSpelling().drop_front();
|
|
|
|
|
|
|
|
// Check for redefinitions.
|
2019-11-02 06:39:30 +08:00
|
|
|
if (getState().symbols.typeAliasDefinitions.count(aliasName) > 0)
|
2019-01-08 10:42:04 +08:00
|
|
|
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.
|
2019-11-02 06:39:30 +08:00
|
|
|
getState().symbols.typeAliasDefinitions.try_emplace(aliasName, aliasedType);
|
2019-05-07 13:00:08 +08:00
|
|
|
return success();
|
2019-01-08 10:42:04 +08:00
|
|
|
}
|
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
ParseResult TopLevelOperationParser::parse(Block *topLevelBlock,
|
|
|
|
Location parserLoc) {
|
|
|
|
// Create a top-level operation to contain the parsed state.
|
|
|
|
OwningOpRef<Operation *> topLevelOp(ModuleOp::create(parserLoc));
|
|
|
|
OperationParser opParser(getState(), topLevelOp.get());
|
2019-09-23 17:33:51 +08:00
|
|
|
while (true) {
|
2018-07-10 10:05:38 +08:00
|
|
|
switch (getToken().getKind()) {
|
2018-06-23 01:39:19 +08:00
|
|
|
default:
|
2019-07-04 04:21:24 +08:00
|
|
|
// Parse a top-level operation.
|
|
|
|
if (opParser.parseOperation())
|
|
|
|
return failure();
|
|
|
|
break;
|
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.
|
2019-07-11 06:16:52 +08:00
|
|
|
case Token::eof: {
|
|
|
|
if (opParser.finalize())
|
|
|
|
return failure();
|
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
// Verify that the parsed operations are valid.
|
|
|
|
if (failed(verify(topLevelOp.get())))
|
|
|
|
return failure();
|
2019-07-11 06:16:52 +08:00
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
// Splice the blocks of the parsed operation over to the provided
|
|
|
|
// top-level block.
|
|
|
|
auto &parsedOps = (*topLevelOp)->getRegion(0).front().getOperations();
|
|
|
|
auto &destOps = topLevelBlock->getOperations();
|
|
|
|
destOps.splice(destOps.empty() ? destOps.end() : std::prev(destOps.end()),
|
|
|
|
parsedOps, parsedOps.begin(), std::prev(parsedOps.end()));
|
|
|
|
return success();
|
2019-07-11 06:16:52 +08:00
|
|
|
}
|
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;
|
2018-06-23 01:39:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
LogicalResult mlir::parseSourceFile(const llvm::SourceMgr &sourceMgr,
|
|
|
|
Block *block, MLIRContext *context,
|
|
|
|
LocationAttr *sourceFileLoc) {
|
|
|
|
const auto *sourceBuf = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID());
|
2018-08-21 23:42:19 +08:00
|
|
|
|
2021-03-09 06:25:38 +08:00
|
|
|
Location parserLoc = FileLineColLoc::get(
|
|
|
|
context, sourceBuf->getBufferIdentifier(), /*line=*/0, /*column=*/0);
|
2020-12-04 07:46:41 +08:00
|
|
|
if (sourceFileLoc)
|
|
|
|
*sourceFileLoc = parserLoc;
|
2018-07-11 01:08:27 +08:00
|
|
|
|
2019-11-02 06:39:30 +08:00
|
|
|
SymbolState aliasState;
|
2019-11-02 05:47:42 +08:00
|
|
|
ParserState state(sourceMgr, context, aliasState);
|
2020-12-04 07:46:41 +08:00
|
|
|
return TopLevelOperationParser(state).parse(block, parserLoc);
|
2018-06-23 01:39:19 +08:00
|
|
|
}
|
2018-09-03 22:38:31 +08:00
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
LogicalResult mlir::parseSourceFile(llvm::StringRef filename, Block *block,
|
|
|
|
MLIRContext *context,
|
|
|
|
LocationAttr *sourceFileLoc) {
|
2019-06-27 02:12:40 +08:00
|
|
|
llvm::SourceMgr sourceMgr;
|
2020-12-04 07:46:41 +08:00
|
|
|
return parseSourceFile(filename, sourceMgr, block, context, sourceFileLoc);
|
2019-06-27 02:12:40 +08:00
|
|
|
}
|
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
LogicalResult mlir::parseSourceFile(llvm::StringRef filename,
|
|
|
|
llvm::SourceMgr &sourceMgr, Block *block,
|
|
|
|
MLIRContext *context,
|
|
|
|
LocationAttr *sourceFileLoc) {
|
2019-06-27 02:12:40 +08:00
|
|
|
if (sourceMgr.getNumBuffers() != 0) {
|
2020-07-07 16:35:23 +08:00
|
|
|
// TODO: Extend to support multiple buffers.
|
2020-12-04 07:46:41 +08:00
|
|
|
return emitError(mlir::UnknownLoc::get(context),
|
|
|
|
"only main buffer parsed at the moment");
|
2019-06-27 02:12:40 +08:00
|
|
|
}
|
2019-06-27 10:56:42 +08:00
|
|
|
auto file_or_err = llvm::MemoryBuffer::getFileOrSTDIN(filename);
|
2020-12-04 07:46:41 +08:00
|
|
|
if (std::error_code error = file_or_err.getError())
|
|
|
|
return emitError(mlir::UnknownLoc::get(context),
|
|
|
|
"could not open input file " + filename);
|
2019-03-23 13:17:10 +08:00
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
// Load the MLIR source file.
|
2019-06-27 02:12:40 +08:00
|
|
|
sourceMgr.AddNewSourceBuffer(std::move(*file_or_err), llvm::SMLoc());
|
2020-12-04 07:46:41 +08:00
|
|
|
return parseSourceFile(sourceMgr, block, context, sourceFileLoc);
|
2019-03-23 13:17:10 +08:00
|
|
|
}
|
|
|
|
|
2020-12-04 07:46:41 +08:00
|
|
|
LogicalResult mlir::parseSourceString(llvm::StringRef sourceStr, Block *block,
|
|
|
|
MLIRContext *context,
|
|
|
|
LocationAttr *sourceFileLoc) {
|
|
|
|
auto memBuffer = MemoryBuffer::getMemBuffer(sourceStr);
|
2018-09-03 22:38:31 +08:00
|
|
|
if (!memBuffer)
|
2020-12-04 07:46:41 +08:00
|
|
|
return failure();
|
2018-09-03 22:38:31 +08:00
|
|
|
|
|
|
|
SourceMgr sourceMgr;
|
|
|
|
sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
|
2020-12-04 07:46:41 +08:00
|
|
|
return parseSourceFile(sourceMgr, block, context, sourceFileLoc);
|
2018-09-03 22:38:31 +08:00
|
|
|
}
|