2019-05-23 15:11:19 -07:00
|
|
|
//===- TestDialect.cpp - MLIR Dialect for Testing -------------------------===//
|
|
|
|
//
|
2020-01-26 03:58:30 +00:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
2019-12-23 09:35:36 -08:00
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2019-05-23 15:11:19 -07:00
|
|
|
//
|
2019-12-23 09:35:36 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-05-23 15:11:19 -07:00
|
|
|
|
|
|
|
#include "TestDialect.h"
|
2021-03-03 16:37:32 -08:00
|
|
|
#include "TestAttributes.h"
|
2021-06-02 07:00:19 +08:00
|
|
|
#include "TestInterfaces.h"
|
2020-06-30 15:42:52 -07:00
|
|
|
#include "TestTypes.h"
|
2023-02-14 08:45:08 -08:00
|
|
|
#include "mlir/Bytecode/BytecodeImplementation.h"
|
2022-09-29 11:14:47 -04:00
|
|
|
#include "mlir/Dialect/Arith/IR/Arith.h"
|
2022-02-26 14:49:54 -08:00
|
|
|
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
2021-06-16 22:12:16 -07:00
|
|
|
#include "mlir/Dialect/Tensor/IR/Tensor.h"
|
[mlir] Allow for attaching external resources to .mlir files
This commit enables support for providing and processing external
resources within MLIR assembly formats. This is a mechanism with which
dialects, and external clients, may attach additional information when
printing IR without that information being encoded in the IR itself.
External resources are not uniqued within the MLIR context, are not
attached directly to any operation, and are solely intended to live and be
processed outside of the immediate IR. There are many potential uses of this
functionality, for example MLIR's pass crash reproducer could utilize this to
attach the pass resource executing when a crash occurs. Other types of
uses may be embedding large amounts of binary data, such as weights in ML
applications, that shouldn't be copied directly into the MLIR context, but
need to be kept adjacent to the IR.
External resources are encoded using a key-value pair nested within a
dictionary anchored by name either on a dialect, or an externally registered
entity. The key is an identifier used to disambiguate the data. The value
may be stored in various limited forms, but general encodings use a string
(human readable) or blob format (binary). Within the textual format, an
example may be of the form:
```mlir
{-#
// The `dialect_resources` section within the file-level metadata
// dictionary is used to contain any dialect resource entries.
dialect_resources: {
// Here is a dictionary anchored on "foo_dialect", which is a dialect
// namespace.
foo_dialect: {
// `some_dialect_resource` is a key to be interpreted by the dialect,
// and used to initialize/configure/etc.
some_dialect_resource: "Some important resource value"
}
},
// The `external_resources` section within the file-level metadata
// dictionary is used to contain any non-dialect resource entries.
external_resources: {
// Here is a dictionary anchored on "mlir_reproducer", which is an
// external entity representing MLIR's crash reproducer functionality.
mlir_reproducer: {
// `pipeline` is an entry that holds a crash reproducer pipeline
// resource.
pipeline: "func.func(canonicalize,cse)"
}
}
```
Differential Revision: https://reviews.llvm.org/D126446
2022-06-28 13:25:24 -07:00
|
|
|
#include "mlir/IR/AsmState.h"
|
2022-06-02 21:45:52 +00:00
|
|
|
#include "mlir/IR/BuiltinAttributes.h"
|
2020-11-19 10:43:12 -08:00
|
|
|
#include "mlir/IR/BuiltinOps.h"
|
2022-06-02 21:45:52 +00:00
|
|
|
#include "mlir/IR/Diagnostics.h"
|
2022-04-26 19:48:13 -07:00
|
|
|
#include "mlir/IR/ExtensibleDialect.h"
|
2023-03-22 08:38:55 +01:00
|
|
|
#include "mlir/IR/FunctionImplementation.h"
|
2022-06-02 21:45:52 +00:00
|
|
|
#include "mlir/IR/MLIRContext.h"
|
2023-02-26 10:46:01 -05:00
|
|
|
#include "mlir/IR/ODSSupport.h"
|
2022-06-02 21:45:52 +00:00
|
|
|
#include "mlir/IR/OperationSupport.h"
|
2019-05-23 15:11:19 -07:00
|
|
|
#include "mlir/IR/PatternMatch.h"
|
2019-07-20 03:03:45 -07:00
|
|
|
#include "mlir/IR/TypeUtilities.h"
|
2022-04-15 04:33:40 +00:00
|
|
|
#include "mlir/IR/Verifier.h"
|
2022-06-02 21:45:52 +00:00
|
|
|
#include "mlir/Interfaces/InferIntRangeInterface.h"
|
2021-06-02 07:00:19 +08:00
|
|
|
#include "mlir/Reducer/ReductionPatternInterface.h"
|
2023-05-25 21:04:35 -07:00
|
|
|
#include "mlir/Support/LogicalResult.h"
|
2019-09-01 20:06:42 -07:00
|
|
|
#include "mlir/Transforms/FoldUtils.h"
|
2019-09-05 12:23:45 -07:00
|
|
|
#include "mlir/Transforms/InliningUtils.h"
|
2022-06-02 21:45:52 +00:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
2022-04-25 18:51:09 +00:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2019-11-25 11:29:21 -08:00
|
|
|
#include "llvm/ADT/StringSwitch.h"
|
2023-05-07 19:28:46 -04:00
|
|
|
#include "llvm/Support/Base64.h"
|
2019-05-23 15:11:19 -07:00
|
|
|
|
2023-05-25 21:04:35 -07:00
|
|
|
#include <cstdint>
|
2022-12-25 19:29:31 +01:00
|
|
|
#include <numeric>
|
2023-02-14 08:45:08 -08:00
|
|
|
#include <optional>
|
2022-12-25 19:29:31 +01:00
|
|
|
|
2020-09-24 11:54:46 -07:00
|
|
|
// Include this before the using namespace lines below to
|
|
|
|
// test that we don't have namespace dependencies.
|
2021-06-28 22:54:11 +00:00
|
|
|
#include "TestOpsDialect.cpp.inc"
|
|
|
|
|
2020-09-24 11:54:46 -07:00
|
|
|
using namespace mlir;
|
|
|
|
using namespace test;
|
|
|
|
|
2023-02-26 10:46:01 -05:00
|
|
|
Attribute MyPropStruct::asAttribute(MLIRContext *ctx) const {
|
|
|
|
return StringAttr::get(ctx, content);
|
|
|
|
}
|
|
|
|
LogicalResult MyPropStruct::setFromAttr(MyPropStruct &prop, Attribute attr,
|
|
|
|
InFlightDiagnostic *diag) {
|
2023-05-08 16:33:54 +02:00
|
|
|
StringAttr strAttr = dyn_cast<StringAttr>(attr);
|
2023-02-26 10:46:01 -05:00
|
|
|
if (!strAttr) {
|
|
|
|
if (diag)
|
|
|
|
*diag << "Expect StringAttr but got " << attr;
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
prop.content = strAttr.getValue();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
llvm::hash_code MyPropStruct::hash() const {
|
|
|
|
return hash_value(StringRef(content));
|
|
|
|
}
|
|
|
|
|
2023-05-25 21:04:35 -07:00
|
|
|
static LogicalResult readFromMlirBytecode(DialectBytecodeReader &reader,
|
|
|
|
MyPropStruct &prop) {
|
|
|
|
StringRef str;
|
|
|
|
if (failed(reader.readString(str)))
|
|
|
|
return failure();
|
|
|
|
prop.content = str.str();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void writeToMlirBytecode(::mlir::DialectBytecodeWriter &writer,
|
|
|
|
MyPropStruct &prop) {
|
|
|
|
writer.writeOwnedString(prop.content);
|
|
|
|
}
|
|
|
|
|
|
|
|
static LogicalResult readFromMlirBytecode(DialectBytecodeReader &reader,
|
|
|
|
MutableArrayRef<int64_t> prop) {
|
|
|
|
uint64_t size;
|
|
|
|
if (failed(reader.readVarInt(size)))
|
|
|
|
return failure();
|
|
|
|
if (size != prop.size())
|
|
|
|
return reader.emitError("array size mismach when reading properties: ")
|
|
|
|
<< size << " vs expected " << prop.size();
|
|
|
|
for (auto &elt : prop) {
|
|
|
|
uint64_t value;
|
|
|
|
if (failed(reader.readVarInt(value)))
|
|
|
|
return failure();
|
|
|
|
elt = value;
|
|
|
|
}
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void writeToMlirBytecode(::mlir::DialectBytecodeWriter &writer,
|
|
|
|
ArrayRef<int64_t> prop) {
|
|
|
|
writer.writeVarInt(prop.size());
|
|
|
|
for (auto elt : prop)
|
|
|
|
writer.writeVarInt(elt);
|
|
|
|
}
|
|
|
|
|
2023-02-26 10:46:01 -05:00
|
|
|
static LogicalResult setPropertiesFromAttribute(PropertiesWithCustomPrint &prop,
|
|
|
|
Attribute attr,
|
|
|
|
InFlightDiagnostic *diagnostic);
|
|
|
|
static DictionaryAttr
|
|
|
|
getPropertiesAsAttribute(MLIRContext *ctx,
|
|
|
|
const PropertiesWithCustomPrint &prop);
|
|
|
|
static llvm::hash_code computeHash(const PropertiesWithCustomPrint &prop);
|
|
|
|
static void customPrintProperties(OpAsmPrinter &p,
|
|
|
|
const PropertiesWithCustomPrint &prop);
|
|
|
|
static ParseResult customParseProperties(OpAsmParser &parser,
|
|
|
|
PropertiesWithCustomPrint &prop);
|
|
|
|
|
2020-09-24 11:54:46 -07:00
|
|
|
void test::registerTestDialect(DialectRegistry ®istry) {
|
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-18 20:01:19 +00:00
|
|
|
registry.insert<TestDialect>();
|
|
|
|
}
|
|
|
|
|
2023-02-14 08:45:08 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestDialect version utilities
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
struct TestDialectVersion : public DialectVersion {
|
|
|
|
uint32_t major = 2;
|
|
|
|
uint32_t minor = 0;
|
|
|
|
};
|
|
|
|
|
2019-09-01 20:06:42 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestDialect Interfaces
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
2019-11-20 10:19:01 -08:00
|
|
|
|
2021-03-11 23:58:02 +00:00
|
|
|
/// Testing the correctness of some traits.
|
|
|
|
static_assert(
|
|
|
|
llvm::is_detected<OpTrait::has_implicit_terminator_t,
|
|
|
|
SingleBlockImplicitTerminatorOp>::value,
|
|
|
|
"has_implicit_terminator_t does not match SingleBlockImplicitTerminatorOp");
|
|
|
|
static_assert(OpTrait::hasSingleBlockImplicitTerminator<
|
|
|
|
SingleBlockImplicitTerminatorOp>::value,
|
|
|
|
"hasSingleBlockImplicitTerminator does not match "
|
|
|
|
"SingleBlockImplicitTerminatorOp");
|
|
|
|
|
2022-07-25 18:25:26 -07:00
|
|
|
struct TestResourceBlobManagerInterface
|
|
|
|
: public ResourceBlobManagerDialectInterfaceBase<
|
|
|
|
TestDialectResourceBlobHandle> {
|
|
|
|
using ResourceBlobManagerDialectInterfaceBase<
|
|
|
|
TestDialectResourceBlobHandle>::ResourceBlobManagerDialectInterfaceBase;
|
|
|
|
};
|
|
|
|
|
2023-02-14 08:45:08 -08:00
|
|
|
namespace {
|
|
|
|
enum test_encoding { k_attr_params = 0 };
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test support for interacting with the Bytecode reader/writer.
|
|
|
|
struct TestBytecodeDialectInterface : public BytecodeDialectInterface {
|
|
|
|
using BytecodeDialectInterface::BytecodeDialectInterface;
|
|
|
|
TestBytecodeDialectInterface(Dialect *dialect)
|
|
|
|
: BytecodeDialectInterface(dialect) {}
|
|
|
|
|
|
|
|
LogicalResult writeAttribute(Attribute attr,
|
|
|
|
DialectBytecodeWriter &writer) const final {
|
|
|
|
if (auto concreteAttr = llvm::dyn_cast<TestAttrParamsAttr>(attr)) {
|
|
|
|
writer.writeVarInt(test_encoding::k_attr_params);
|
|
|
|
writer.writeVarInt(concreteAttr.getV0());
|
|
|
|
writer.writeVarInt(concreteAttr.getV1());
|
|
|
|
return success();
|
|
|
|
}
|
2023-05-11 05:19:06 -07:00
|
|
|
return failure();
|
2023-02-14 08:45:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
Attribute readAttribute(DialectBytecodeReader &reader,
|
|
|
|
const DialectVersion &version_) const final {
|
|
|
|
const auto &version = static_cast<const TestDialectVersion &>(version_);
|
|
|
|
if (version.major < 2)
|
|
|
|
return readAttrOldEncoding(reader);
|
|
|
|
if (version.major == 2 && version.minor == 0)
|
|
|
|
return readAttrNewEncoding(reader);
|
|
|
|
// Forbid reading future versions by returning nullptr.
|
|
|
|
return Attribute();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emit a specific version of the dialect.
|
|
|
|
void writeVersion(DialectBytecodeWriter &writer) const final {
|
|
|
|
auto version = TestDialectVersion();
|
|
|
|
writer.writeVarInt(version.major); // major
|
|
|
|
writer.writeVarInt(version.minor); // minor
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<DialectVersion>
|
|
|
|
readVersion(DialectBytecodeReader &reader) const final {
|
|
|
|
uint64_t major, minor;
|
|
|
|
if (failed(reader.readVarInt(major)) || failed(reader.readVarInt(minor)))
|
|
|
|
return nullptr;
|
|
|
|
auto version = std::make_unique<TestDialectVersion>();
|
|
|
|
version->major = major;
|
|
|
|
version->minor = minor;
|
|
|
|
return version;
|
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult upgradeFromVersion(Operation *topLevelOp,
|
|
|
|
const DialectVersion &version_) const final {
|
|
|
|
const auto &version = static_cast<const TestDialectVersion &>(version_);
|
|
|
|
if ((version.major == 2) && (version.minor == 0))
|
|
|
|
return success();
|
|
|
|
if (version.major > 2 || (version.major == 2 && version.minor > 0)) {
|
|
|
|
return topLevelOp->emitError()
|
|
|
|
<< "current test dialect version is 2.0, can't parse version: "
|
|
|
|
<< version.major << "." << version.minor;
|
|
|
|
}
|
|
|
|
// Prior version 2.0, the old op supported only a single attribute called
|
|
|
|
// "dimensions". We can perform the upgrade.
|
|
|
|
topLevelOp->walk([](TestVersionedOpA op) {
|
|
|
|
if (auto dims = op->getAttr("dimensions")) {
|
|
|
|
op->removeAttr("dimensions");
|
|
|
|
op->setAttr("dims", dims);
|
|
|
|
}
|
|
|
|
op->setAttr("modifier", BoolAttr::get(op->getContext(), false));
|
|
|
|
});
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Attribute readAttrNewEncoding(DialectBytecodeReader &reader) const {
|
|
|
|
uint64_t encoding;
|
|
|
|
if (failed(reader.readVarInt(encoding)) ||
|
|
|
|
encoding != test_encoding::k_attr_params)
|
|
|
|
return Attribute();
|
|
|
|
// The new encoding has v0 first, v1 second.
|
|
|
|
uint64_t v0, v1;
|
|
|
|
if (failed(reader.readVarInt(v0)) || failed(reader.readVarInt(v1)))
|
|
|
|
return Attribute();
|
|
|
|
return TestAttrParamsAttr::get(getContext(), static_cast<int>(v0),
|
|
|
|
static_cast<int>(v1));
|
|
|
|
}
|
|
|
|
|
|
|
|
Attribute readAttrOldEncoding(DialectBytecodeReader &reader) const {
|
|
|
|
uint64_t encoding;
|
|
|
|
if (failed(reader.readVarInt(encoding)) ||
|
|
|
|
encoding != test_encoding::k_attr_params)
|
|
|
|
return Attribute();
|
|
|
|
// The old encoding has v1 first, v0 second.
|
|
|
|
uint64_t v0, v1;
|
|
|
|
if (failed(reader.readVarInt(v1)) || failed(reader.readVarInt(v0)))
|
|
|
|
return Attribute();
|
|
|
|
return TestAttrParamsAttr::get(getContext(), static_cast<int>(v0),
|
|
|
|
static_cast<int>(v1));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-11-20 10:19:01 -08:00
|
|
|
// Test support for interacting with the AsmPrinter.
|
|
|
|
struct TestOpAsmInterface : public OpAsmDialectInterface {
|
|
|
|
using OpAsmDialectInterface::OpAsmDialectInterface;
|
2022-07-25 18:25:26 -07:00
|
|
|
TestOpAsmInterface(Dialect *dialect, TestResourceBlobManagerInterface &mgr)
|
|
|
|
: OpAsmDialectInterface(dialect), blobManager(mgr) {}
|
2019-11-20 10:19:01 -08:00
|
|
|
|
[mlir] Allow for attaching external resources to .mlir files
This commit enables support for providing and processing external
resources within MLIR assembly formats. This is a mechanism with which
dialects, and external clients, may attach additional information when
printing IR without that information being encoded in the IR itself.
External resources are not uniqued within the MLIR context, are not
attached directly to any operation, and are solely intended to live and be
processed outside of the immediate IR. There are many potential uses of this
functionality, for example MLIR's pass crash reproducer could utilize this to
attach the pass resource executing when a crash occurs. Other types of
uses may be embedding large amounts of binary data, such as weights in ML
applications, that shouldn't be copied directly into the MLIR context, but
need to be kept adjacent to the IR.
External resources are encoded using a key-value pair nested within a
dictionary anchored by name either on a dialect, or an externally registered
entity. The key is an identifier used to disambiguate the data. The value
may be stored in various limited forms, but general encodings use a string
(human readable) or blob format (binary). Within the textual format, an
example may be of the form:
```mlir
{-#
// The `dialect_resources` section within the file-level metadata
// dictionary is used to contain any dialect resource entries.
dialect_resources: {
// Here is a dictionary anchored on "foo_dialect", which is a dialect
// namespace.
foo_dialect: {
// `some_dialect_resource` is a key to be interpreted by the dialect,
// and used to initialize/configure/etc.
some_dialect_resource: "Some important resource value"
}
},
// The `external_resources` section within the file-level metadata
// dictionary is used to contain any non-dialect resource entries.
external_resources: {
// Here is a dictionary anchored on "mlir_reproducer", which is an
// external entity representing MLIR's crash reproducer functionality.
mlir_reproducer: {
// `pipeline` is an entry that holds a crash reproducer pipeline
// resource.
pipeline: "func.func(canonicalize,cse)"
}
}
```
Differential Revision: https://reviews.llvm.org/D126446
2022-06-28 13:25:24 -07:00
|
|
|
//===------------------------------------------------------------------===//
|
|
|
|
// Aliases
|
|
|
|
//===------------------------------------------------------------------===//
|
|
|
|
|
2021-08-03 17:23:31 +03:00
|
|
|
AliasResult getAlias(Attribute attr, raw_ostream &os) const final {
|
2023-05-08 16:33:54 +02:00
|
|
|
StringAttr strAttr = dyn_cast<StringAttr>(attr);
|
2020-10-30 00:30:59 -07:00
|
|
|
if (!strAttr)
|
2021-08-03 17:23:31 +03:00
|
|
|
return AliasResult::NoAlias;
|
2020-10-30 00:30:59 -07:00
|
|
|
|
|
|
|
// Check the contents of the string attribute to see what the test alias
|
|
|
|
// should be named.
|
2022-12-14 11:39:19 +01:00
|
|
|
std::optional<StringRef> aliasName =
|
|
|
|
StringSwitch<std::optional<StringRef>>(strAttr.getValue())
|
2020-10-30 00:30:59 -07:00
|
|
|
.Case("alias_test:dot_in_name", StringRef("test.alias"))
|
|
|
|
.Case("alias_test:trailing_digit", StringRef("test_alias0"))
|
|
|
|
.Case("alias_test:prefixed_digit", StringRef("0_test_alias"))
|
|
|
|
.Case("alias_test:sanitize_conflict_a",
|
|
|
|
StringRef("test_alias_conflict0"))
|
|
|
|
.Case("alias_test:sanitize_conflict_b",
|
|
|
|
StringRef("test_alias_conflict0_"))
|
2021-07-07 18:07:51 +03:00
|
|
|
.Case("alias_test:tensor_encoding", StringRef("test_encoding"))
|
2022-12-03 18:50:27 -08:00
|
|
|
.Default(std::nullopt);
|
2020-10-30 00:30:59 -07:00
|
|
|
if (!aliasName)
|
2021-08-03 17:23:31 +03:00
|
|
|
return AliasResult::NoAlias;
|
2020-10-30 00:30:59 -07:00
|
|
|
|
|
|
|
os << *aliasName;
|
2021-08-03 17:23:31 +03:00
|
|
|
return AliasResult::FinalAlias;
|
|
|
|
}
|
|
|
|
|
|
|
|
AliasResult getAlias(Type type, raw_ostream &os) const final {
|
2023-05-08 16:33:54 +02:00
|
|
|
if (auto tupleType = dyn_cast<TupleType>(type)) {
|
2021-08-03 17:23:31 +03:00
|
|
|
if (tupleType.size() > 0 &&
|
|
|
|
llvm::all_of(tupleType.getTypes(), [](Type elemType) {
|
2023-05-08 16:33:54 +02:00
|
|
|
return isa<SimpleAType>(elemType);
|
2021-08-03 17:23:31 +03:00
|
|
|
})) {
|
|
|
|
os << "test_tuple";
|
|
|
|
return AliasResult::FinalAlias;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 16:33:54 +02:00
|
|
|
if (auto intType = dyn_cast<TestIntegerType>(type)) {
|
2021-10-11 14:52:06 +03:00
|
|
|
if (intType.getSignedness() ==
|
|
|
|
TestIntegerType::SignednessSemantics::Unsigned &&
|
|
|
|
intType.getWidth() == 8) {
|
|
|
|
os << "test_ui8";
|
|
|
|
return AliasResult::FinalAlias;
|
|
|
|
}
|
|
|
|
}
|
2023-05-08 16:33:54 +02:00
|
|
|
if (auto recType = dyn_cast<TestRecursiveType>(type)) {
|
2022-05-20 21:52:49 -07:00
|
|
|
if (recType.getName() == "type_to_alias") {
|
|
|
|
// We only make alias for a specific recursive type.
|
|
|
|
os << "testrec";
|
|
|
|
return AliasResult::FinalAlias;
|
|
|
|
}
|
|
|
|
}
|
2021-08-03 17:23:31 +03:00
|
|
|
return AliasResult::NoAlias;
|
2020-10-30 00:30:59 -07:00
|
|
|
}
|
[mlir] Allow for attaching external resources to .mlir files
This commit enables support for providing and processing external
resources within MLIR assembly formats. This is a mechanism with which
dialects, and external clients, may attach additional information when
printing IR without that information being encoded in the IR itself.
External resources are not uniqued within the MLIR context, are not
attached directly to any operation, and are solely intended to live and be
processed outside of the immediate IR. There are many potential uses of this
functionality, for example MLIR's pass crash reproducer could utilize this to
attach the pass resource executing when a crash occurs. Other types of
uses may be embedding large amounts of binary data, such as weights in ML
applications, that shouldn't be copied directly into the MLIR context, but
need to be kept adjacent to the IR.
External resources are encoded using a key-value pair nested within a
dictionary anchored by name either on a dialect, or an externally registered
entity. The key is an identifier used to disambiguate the data. The value
may be stored in various limited forms, but general encodings use a string
(human readable) or blob format (binary). Within the textual format, an
example may be of the form:
```mlir
{-#
// The `dialect_resources` section within the file-level metadata
// dictionary is used to contain any dialect resource entries.
dialect_resources: {
// Here is a dictionary anchored on "foo_dialect", which is a dialect
// namespace.
foo_dialect: {
// `some_dialect_resource` is a key to be interpreted by the dialect,
// and used to initialize/configure/etc.
some_dialect_resource: "Some important resource value"
}
},
// The `external_resources` section within the file-level metadata
// dictionary is used to contain any non-dialect resource entries.
external_resources: {
// Here is a dictionary anchored on "mlir_reproducer", which is an
// external entity representing MLIR's crash reproducer functionality.
mlir_reproducer: {
// `pipeline` is an entry that holds a crash reproducer pipeline
// resource.
pipeline: "func.func(canonicalize,cse)"
}
}
```
Differential Revision: https://reviews.llvm.org/D126446
2022-06-28 13:25:24 -07:00
|
|
|
|
|
|
|
//===------------------------------------------------------------------===//
|
|
|
|
// Resources
|
|
|
|
//===------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
std::string
|
|
|
|
getResourceKey(const AsmDialectResourceHandle &handle) const override {
|
2022-07-25 18:25:26 -07:00
|
|
|
return cast<TestDialectResourceBlobHandle>(handle).getKey().str();
|
[mlir] Allow for attaching external resources to .mlir files
This commit enables support for providing and processing external
resources within MLIR assembly formats. This is a mechanism with which
dialects, and external clients, may attach additional information when
printing IR without that information being encoded in the IR itself.
External resources are not uniqued within the MLIR context, are not
attached directly to any operation, and are solely intended to live and be
processed outside of the immediate IR. There are many potential uses of this
functionality, for example MLIR's pass crash reproducer could utilize this to
attach the pass resource executing when a crash occurs. Other types of
uses may be embedding large amounts of binary data, such as weights in ML
applications, that shouldn't be copied directly into the MLIR context, but
need to be kept adjacent to the IR.
External resources are encoded using a key-value pair nested within a
dictionary anchored by name either on a dialect, or an externally registered
entity. The key is an identifier used to disambiguate the data. The value
may be stored in various limited forms, but general encodings use a string
(human readable) or blob format (binary). Within the textual format, an
example may be of the form:
```mlir
{-#
// The `dialect_resources` section within the file-level metadata
// dictionary is used to contain any dialect resource entries.
dialect_resources: {
// Here is a dictionary anchored on "foo_dialect", which is a dialect
// namespace.
foo_dialect: {
// `some_dialect_resource` is a key to be interpreted by the dialect,
// and used to initialize/configure/etc.
some_dialect_resource: "Some important resource value"
}
},
// The `external_resources` section within the file-level metadata
// dictionary is used to contain any non-dialect resource entries.
external_resources: {
// Here is a dictionary anchored on "mlir_reproducer", which is an
// external entity representing MLIR's crash reproducer functionality.
mlir_reproducer: {
// `pipeline` is an entry that holds a crash reproducer pipeline
// resource.
pipeline: "func.func(canonicalize,cse)"
}
}
```
Differential Revision: https://reviews.llvm.org/D126446
2022-06-28 13:25:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
FailureOr<AsmDialectResourceHandle>
|
|
|
|
declareResource(StringRef key) const final {
|
2022-07-25 18:25:26 -07:00
|
|
|
return blobManager.insert(key);
|
[mlir] Allow for attaching external resources to .mlir files
This commit enables support for providing and processing external
resources within MLIR assembly formats. This is a mechanism with which
dialects, and external clients, may attach additional information when
printing IR without that information being encoded in the IR itself.
External resources are not uniqued within the MLIR context, are not
attached directly to any operation, and are solely intended to live and be
processed outside of the immediate IR. There are many potential uses of this
functionality, for example MLIR's pass crash reproducer could utilize this to
attach the pass resource executing when a crash occurs. Other types of
uses may be embedding large amounts of binary data, such as weights in ML
applications, that shouldn't be copied directly into the MLIR context, but
need to be kept adjacent to the IR.
External resources are encoded using a key-value pair nested within a
dictionary anchored by name either on a dialect, or an externally registered
entity. The key is an identifier used to disambiguate the data. The value
may be stored in various limited forms, but general encodings use a string
(human readable) or blob format (binary). Within the textual format, an
example may be of the form:
```mlir
{-#
// The `dialect_resources` section within the file-level metadata
// dictionary is used to contain any dialect resource entries.
dialect_resources: {
// Here is a dictionary anchored on "foo_dialect", which is a dialect
// namespace.
foo_dialect: {
// `some_dialect_resource` is a key to be interpreted by the dialect,
// and used to initialize/configure/etc.
some_dialect_resource: "Some important resource value"
}
},
// The `external_resources` section within the file-level metadata
// dictionary is used to contain any non-dialect resource entries.
external_resources: {
// Here is a dictionary anchored on "mlir_reproducer", which is an
// external entity representing MLIR's crash reproducer functionality.
mlir_reproducer: {
// `pipeline` is an entry that holds a crash reproducer pipeline
// resource.
pipeline: "func.func(canonicalize,cse)"
}
}
```
Differential Revision: https://reviews.llvm.org/D126446
2022-06-28 13:25:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult parseResource(AsmParsedResourceEntry &entry) const final {
|
2022-07-25 18:25:26 -07:00
|
|
|
FailureOr<AsmResourceBlob> blob = entry.parseAsBlob();
|
[mlir] Allow for attaching external resources to .mlir files
This commit enables support for providing and processing external
resources within MLIR assembly formats. This is a mechanism with which
dialects, and external clients, may attach additional information when
printing IR without that information being encoded in the IR itself.
External resources are not uniqued within the MLIR context, are not
attached directly to any operation, and are solely intended to live and be
processed outside of the immediate IR. There are many potential uses of this
functionality, for example MLIR's pass crash reproducer could utilize this to
attach the pass resource executing when a crash occurs. Other types of
uses may be embedding large amounts of binary data, such as weights in ML
applications, that shouldn't be copied directly into the MLIR context, but
need to be kept adjacent to the IR.
External resources are encoded using a key-value pair nested within a
dictionary anchored by name either on a dialect, or an externally registered
entity. The key is an identifier used to disambiguate the data. The value
may be stored in various limited forms, but general encodings use a string
(human readable) or blob format (binary). Within the textual format, an
example may be of the form:
```mlir
{-#
// The `dialect_resources` section within the file-level metadata
// dictionary is used to contain any dialect resource entries.
dialect_resources: {
// Here is a dictionary anchored on "foo_dialect", which is a dialect
// namespace.
foo_dialect: {
// `some_dialect_resource` is a key to be interpreted by the dialect,
// and used to initialize/configure/etc.
some_dialect_resource: "Some important resource value"
}
},
// The `external_resources` section within the file-level metadata
// dictionary is used to contain any non-dialect resource entries.
external_resources: {
// Here is a dictionary anchored on "mlir_reproducer", which is an
// external entity representing MLIR's crash reproducer functionality.
mlir_reproducer: {
// `pipeline` is an entry that holds a crash reproducer pipeline
// resource.
pipeline: "func.func(canonicalize,cse)"
}
}
```
Differential Revision: https://reviews.llvm.org/D126446
2022-06-28 13:25:24 -07:00
|
|
|
if (failed(blob))
|
|
|
|
return failure();
|
|
|
|
|
2022-07-25 18:25:26 -07:00
|
|
|
// Update the blob for this entry.
|
|
|
|
blobManager.update(entry.getKey(), std::move(*blob));
|
[mlir] Allow for attaching external resources to .mlir files
This commit enables support for providing and processing external
resources within MLIR assembly formats. This is a mechanism with which
dialects, and external clients, may attach additional information when
printing IR without that information being encoded in the IR itself.
External resources are not uniqued within the MLIR context, are not
attached directly to any operation, and are solely intended to live and be
processed outside of the immediate IR. There are many potential uses of this
functionality, for example MLIR's pass crash reproducer could utilize this to
attach the pass resource executing when a crash occurs. Other types of
uses may be embedding large amounts of binary data, such as weights in ML
applications, that shouldn't be copied directly into the MLIR context, but
need to be kept adjacent to the IR.
External resources are encoded using a key-value pair nested within a
dictionary anchored by name either on a dialect, or an externally registered
entity. The key is an identifier used to disambiguate the data. The value
may be stored in various limited forms, but general encodings use a string
(human readable) or blob format (binary). Within the textual format, an
example may be of the form:
```mlir
{-#
// The `dialect_resources` section within the file-level metadata
// dictionary is used to contain any dialect resource entries.
dialect_resources: {
// Here is a dictionary anchored on "foo_dialect", which is a dialect
// namespace.
foo_dialect: {
// `some_dialect_resource` is a key to be interpreted by the dialect,
// and used to initialize/configure/etc.
some_dialect_resource: "Some important resource value"
}
},
// The `external_resources` section within the file-level metadata
// dictionary is used to contain any non-dialect resource entries.
external_resources: {
// Here is a dictionary anchored on "mlir_reproducer", which is an
// external entity representing MLIR's crash reproducer functionality.
mlir_reproducer: {
// `pipeline` is an entry that holds a crash reproducer pipeline
// resource.
pipeline: "func.func(canonicalize,cse)"
}
}
```
Differential Revision: https://reviews.llvm.org/D126446
2022-06-28 13:25:24 -07:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
buildResources(Operation *op,
|
|
|
|
const SetVector<AsmDialectResourceHandle> &referencedResources,
|
|
|
|
AsmResourceBuilder &provider) const final {
|
2022-07-25 18:25:26 -07:00
|
|
|
blobManager.buildResources(provider, referencedResources.getArrayRef());
|
[mlir] Allow for attaching external resources to .mlir files
This commit enables support for providing and processing external
resources within MLIR assembly formats. This is a mechanism with which
dialects, and external clients, may attach additional information when
printing IR without that information being encoded in the IR itself.
External resources are not uniqued within the MLIR context, are not
attached directly to any operation, and are solely intended to live and be
processed outside of the immediate IR. There are many potential uses of this
functionality, for example MLIR's pass crash reproducer could utilize this to
attach the pass resource executing when a crash occurs. Other types of
uses may be embedding large amounts of binary data, such as weights in ML
applications, that shouldn't be copied directly into the MLIR context, but
need to be kept adjacent to the IR.
External resources are encoded using a key-value pair nested within a
dictionary anchored by name either on a dialect, or an externally registered
entity. The key is an identifier used to disambiguate the data. The value
may be stored in various limited forms, but general encodings use a string
(human readable) or blob format (binary). Within the textual format, an
example may be of the form:
```mlir
{-#
// The `dialect_resources` section within the file-level metadata
// dictionary is used to contain any dialect resource entries.
dialect_resources: {
// Here is a dictionary anchored on "foo_dialect", which is a dialect
// namespace.
foo_dialect: {
// `some_dialect_resource` is a key to be interpreted by the dialect,
// and used to initialize/configure/etc.
some_dialect_resource: "Some important resource value"
}
},
// The `external_resources` section within the file-level metadata
// dictionary is used to contain any non-dialect resource entries.
external_resources: {
// Here is a dictionary anchored on "mlir_reproducer", which is an
// external entity representing MLIR's crash reproducer functionality.
mlir_reproducer: {
// `pipeline` is an entry that holds a crash reproducer pipeline
// resource.
pipeline: "func.func(canonicalize,cse)"
}
}
```
Differential Revision: https://reviews.llvm.org/D126446
2022-06-28 13:25:24 -07:00
|
|
|
}
|
2022-07-25 18:25:26 -07:00
|
|
|
|
|
|
|
private:
|
|
|
|
/// The blob manager for the dialect.
|
|
|
|
TestResourceBlobManagerInterface &blobManager;
|
2019-11-20 10:19:01 -08:00
|
|
|
};
|
|
|
|
|
2020-08-12 22:45:16 +00:00
|
|
|
struct TestDialectFoldInterface : public DialectFoldInterface {
|
|
|
|
using DialectFoldInterface::DialectFoldInterface;
|
2019-09-01 20:06:42 -07:00
|
|
|
|
|
|
|
/// Registered hook to check if the given region, which is attached to an
|
|
|
|
/// operation that is *not* isolated from above, should be used when
|
|
|
|
/// materializing constants.
|
2019-09-07 18:56:39 -07:00
|
|
|
bool shouldMaterializeInto(Region *region) const final {
|
2019-09-01 20:06:42 -07:00
|
|
|
// If this is a one region operation, then insert into it.
|
|
|
|
return isa<OneRegionOp>(region->getParentOp());
|
|
|
|
}
|
|
|
|
};
|
2019-09-05 12:23:45 -07:00
|
|
|
|
|
|
|
/// This class defines the interface for handling inlining with standard
|
|
|
|
/// operations.
|
|
|
|
struct TestInlinerInterface : public DialectInlinerInterface {
|
|
|
|
using DialectInlinerInterface::DialectInlinerInterface;
|
|
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Analysis Hooks
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
2020-10-28 21:48:48 -07:00
|
|
|
bool isLegalToInline(Operation *call, Operation *callable,
|
|
|
|
bool wouldBeCloned) const final {
|
2020-10-28 21:48:38 -07:00
|
|
|
// Don't allow inlining calls that are marked `noinline`.
|
|
|
|
return !call->hasAttr("noinline");
|
|
|
|
}
|
2023-01-08 14:15:07 -08:00
|
|
|
bool isLegalToInline(Region *, Region *, bool, IRMapping &) const final {
|
2019-10-03 23:04:56 -07:00
|
|
|
// Inlining into test dialect regions is legal.
|
|
|
|
return true;
|
|
|
|
}
|
2023-01-08 14:15:07 -08:00
|
|
|
bool isLegalToInline(Operation *, Region *, bool, IRMapping &) const final {
|
2019-09-05 12:23:45 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-10-03 23:10:25 -07:00
|
|
|
bool shouldAnalyzeRecursively(Operation *op) const final {
|
2019-09-05 12:23:45 -07:00
|
|
|
// Analyze recursively if this is not a functional region operation, it
|
|
|
|
// froms a separate functional scope.
|
|
|
|
return !isa<FunctionalRegionOp>(op);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Transformation Hooks
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Handle the given inlined terminator by replacing it with a new operation
|
|
|
|
/// as necessary.
|
|
|
|
void handleTerminator(Operation *op,
|
2019-12-23 14:45:01 -08:00
|
|
|
ArrayRef<Value> valuesToRepl) const final {
|
2019-09-05 12:23:45 -07:00
|
|
|
// Only handle "test.return" here.
|
|
|
|
auto returnOp = dyn_cast<TestReturnOp>(op);
|
|
|
|
if (!returnOp)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Replace the values directly with the return operands.
|
|
|
|
assert(returnOp.getNumOperands() == valuesToRepl.size());
|
|
|
|
for (const auto &it : llvm::enumerate(returnOp.getOperands()))
|
2020-01-11 08:54:04 -08:00
|
|
|
valuesToRepl[it.index()].replaceAllUsesWith(it.value());
|
2019-09-05 12:23:45 -07:00
|
|
|
}
|
2019-10-03 23:10:25 -07:00
|
|
|
|
|
|
|
/// Attempt to materialize a conversion for a type mismatch between a call
|
|
|
|
/// from this dialect, and a callable region. This method should generate an
|
|
|
|
/// operation that takes 'input' as the only operand, and produces a single
|
|
|
|
/// result of 'resultType'. If a conversion can not be generated, nullptr
|
|
|
|
/// should be returned.
|
2019-12-23 14:45:01 -08:00
|
|
|
Operation *materializeCallConversion(OpBuilder &builder, Value input,
|
2019-10-03 23:10:25 -07:00
|
|
|
Type resultType,
|
|
|
|
Location conversionLoc) const final {
|
|
|
|
// Only allow conversion for i16/i32 types.
|
2020-01-10 14:48:24 -05:00
|
|
|
if (!(resultType.isSignlessInteger(16) ||
|
|
|
|
resultType.isSignlessInteger(32)) ||
|
|
|
|
!(input.getType().isSignlessInteger(16) ||
|
|
|
|
input.getType().isSignlessInteger(32)))
|
2019-10-03 23:10:25 -07:00
|
|
|
return nullptr;
|
|
|
|
return builder.create<TestCastOp>(conversionLoc, resultType, input);
|
|
|
|
}
|
2021-06-16 12:53:21 -07:00
|
|
|
|
2023-03-22 08:38:55 +01:00
|
|
|
Value handleArgument(OpBuilder &builder, Operation *call, Operation *callable,
|
2023-04-11 06:16:12 +00:00
|
|
|
Value argument,
|
2023-03-22 08:38:55 +01:00
|
|
|
DictionaryAttr argumentAttrs) const final {
|
|
|
|
if (!argumentAttrs.contains("test.handle_argument"))
|
|
|
|
return argument;
|
2023-04-11 06:16:12 +00:00
|
|
|
return builder.create<TestTypeChangerOp>(call->getLoc(), argument.getType(),
|
2023-03-22 08:38:55 +01:00
|
|
|
argument);
|
|
|
|
}
|
|
|
|
|
|
|
|
Value handleResult(OpBuilder &builder, Operation *call, Operation *callable,
|
2023-04-11 06:16:12 +00:00
|
|
|
Value result, DictionaryAttr resultAttrs) const final {
|
2023-03-22 08:38:55 +01:00
|
|
|
if (!resultAttrs.contains("test.handle_result"))
|
|
|
|
return result;
|
2023-04-11 06:16:12 +00:00
|
|
|
return builder.create<TestTypeChangerOp>(call->getLoc(), result.getType(),
|
2023-03-22 08:38:55 +01:00
|
|
|
result);
|
|
|
|
}
|
|
|
|
|
2021-06-16 12:53:21 -07:00
|
|
|
void processInlinedCallBlocks(
|
|
|
|
Operation *call,
|
|
|
|
iterator_range<Region::iterator> inlinedBlocks) const final {
|
|
|
|
if (!isa<ConversionCallOp>(call))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Set attributed on all ops in the inlined blocks.
|
|
|
|
for (Block &block : inlinedBlocks) {
|
|
|
|
block.walk([&](Operation *op) {
|
|
|
|
op->setAttr("inlined_conversion", UnitAttr::get(call->getContext()));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-09-05 12:23:45 -07:00
|
|
|
};
|
2021-06-02 07:00:19 +08:00
|
|
|
|
|
|
|
struct TestReductionPatternInterface : public DialectReductionPatternInterface {
|
|
|
|
public:
|
|
|
|
TestReductionPatternInterface(Dialect *dialect)
|
|
|
|
: DialectReductionPatternInterface(dialect) {}
|
|
|
|
|
2021-09-26 22:01:19 +00:00
|
|
|
void populateReductionPatterns(RewritePatternSet &patterns) const final {
|
2021-06-02 07:00:19 +08:00
|
|
|
populateTestReductionPatterns(patterns);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-12-07 18:27:58 +00:00
|
|
|
} // namespace
|
2019-09-01 20:06:42 -07:00
|
|
|
|
2022-04-26 19:48:13 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Dynamic operations
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
std::unique_ptr<DynamicOpDefinition> getDynamicGenericOp(TestDialect *dialect) {
|
|
|
|
return DynamicOpDefinition::get(
|
|
|
|
"dynamic_generic", dialect, [](Operation *op) { return success(); },
|
|
|
|
[](Operation *op) { return success(); });
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<DynamicOpDefinition>
|
|
|
|
getDynamicOneOperandTwoResultsOp(TestDialect *dialect) {
|
|
|
|
return DynamicOpDefinition::get(
|
|
|
|
"dynamic_one_operand_two_results", dialect,
|
|
|
|
[](Operation *op) {
|
|
|
|
if (op->getNumOperands() != 1) {
|
|
|
|
op->emitOpError()
|
|
|
|
<< "expected 1 operand, but had " << op->getNumOperands();
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
if (op->getNumResults() != 2) {
|
|
|
|
op->emitOpError()
|
|
|
|
<< "expected 2 results, but had " << op->getNumResults();
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
return success();
|
|
|
|
},
|
|
|
|
[](Operation *op) { return success(); });
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<DynamicOpDefinition>
|
|
|
|
getDynamicCustomParserPrinterOp(TestDialect *dialect) {
|
|
|
|
auto verifier = [](Operation *op) {
|
|
|
|
if (op->getNumOperands() == 0 && op->getNumResults() == 0)
|
|
|
|
return success();
|
|
|
|
op->emitError() << "operation should have no operands and no results";
|
|
|
|
return failure();
|
|
|
|
};
|
|
|
|
auto regionVerifier = [](Operation *op) { return success(); };
|
|
|
|
|
|
|
|
auto parser = [](OpAsmParser &parser, OperationState &state) {
|
|
|
|
return parser.parseKeyword("custom_keyword");
|
|
|
|
};
|
|
|
|
|
|
|
|
auto printer = [](Operation *op, OpAsmPrinter &printer, llvm::StringRef) {
|
|
|
|
printer << op->getName() << " custom_keyword";
|
|
|
|
};
|
|
|
|
|
|
|
|
return DynamicOpDefinition::get("dynamic_custom_parser_printer", dialect,
|
|
|
|
verifier, regionVerifier, parser, printer);
|
|
|
|
}
|
|
|
|
|
2019-05-23 15:11:19 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestDialect
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2021-03-24 08:25:25 +00:00
|
|
|
static void testSideEffectOpGetEffect(
|
|
|
|
Operation *op,
|
|
|
|
SmallVectorImpl<SideEffects::EffectInstance<TestEffects::Effect>> &effects);
|
|
|
|
|
|
|
|
// This is the implementation of a dialect fallback for `TestEffectOpInterface`.
|
|
|
|
struct TestOpEffectInterfaceFallback
|
|
|
|
: public TestEffectOpInterface::FallbackModel<
|
|
|
|
TestOpEffectInterfaceFallback> {
|
|
|
|
static bool classof(Operation *op) {
|
|
|
|
bool isSupportedOp =
|
|
|
|
op->getName().getStringRef() == "test.unregistered_side_effect_op";
|
|
|
|
assert(isSupportedOp && "Unexpected dispatch");
|
|
|
|
return isSupportedOp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
getEffects(Operation *op,
|
|
|
|
SmallVectorImpl<SideEffects::EffectInstance<TestEffects::Effect>>
|
|
|
|
&effects) const {
|
|
|
|
testSideEffectOpGetEffect(op, effects);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-08-07 02:41:44 +00:00
|
|
|
void TestDialect::initialize() {
|
2021-03-11 11:24:43 -08:00
|
|
|
registerAttributes();
|
|
|
|
registerTypes();
|
2019-05-23 15:11:19 -07:00
|
|
|
addOperations<
|
|
|
|
#define GET_OP_LIST
|
|
|
|
#include "TestOps.cpp.inc"
|
|
|
|
>();
|
2023-02-23 00:06:21 +01:00
|
|
|
addOperations<ManualCppOpWithFold>();
|
2022-04-26 19:48:13 -07:00
|
|
|
registerDynamicOp(getDynamicGenericOp(this));
|
|
|
|
registerDynamicOp(getDynamicOneOperandTwoResultsOp(this));
|
|
|
|
registerDynamicOp(getDynamicCustomParserPrinterOp(this));
|
|
|
|
|
2022-07-25 18:25:26 -07:00
|
|
|
auto &blobInterface = addInterface<TestResourceBlobManagerInterface>();
|
|
|
|
addInterface<TestOpAsmInterface>(blobInterface);
|
|
|
|
|
|
|
|
addInterfaces<TestDialectFoldInterface, TestInlinerInterface,
|
2023-02-14 08:45:08 -08:00
|
|
|
TestReductionPatternInterface, TestBytecodeDialectInterface>();
|
2019-06-19 13:58:31 -07:00
|
|
|
allowUnknownOperations();
|
2021-03-24 08:25:25 +00:00
|
|
|
|
|
|
|
// Instantiate our fallback op interface that we'll use on specific
|
|
|
|
// unregistered op.
|
|
|
|
fallbackEffectOpInterfaces = new TestOpEffectInterfaceFallback;
|
|
|
|
}
|
|
|
|
TestDialect::~TestDialect() {
|
|
|
|
delete static_cast<TestOpEffectInterfaceFallback *>(
|
|
|
|
fallbackEffectOpInterfaces);
|
2019-05-23 15:11:19 -07:00
|
|
|
}
|
|
|
|
|
2020-12-10 14:13:37 -08:00
|
|
|
Operation *TestDialect::materializeConstant(OpBuilder &builder, Attribute value,
|
|
|
|
Type type, Location loc) {
|
|
|
|
return builder.create<TestOpConstant>(loc, type, value);
|
|
|
|
}
|
|
|
|
|
2022-01-04 08:28:59 -08:00
|
|
|
::mlir::LogicalResult FormatInferType2Op::inferReturnTypes(
|
2022-12-14 11:39:19 +01:00
|
|
|
::mlir::MLIRContext *context, ::std::optional<::mlir::Location> location,
|
2022-01-04 08:28:59 -08:00
|
|
|
::mlir::ValueRange operands, ::mlir::DictionaryAttr attributes,
|
2023-02-26 10:46:01 -05:00
|
|
|
OpaqueProperties properties, ::mlir::RegionRange regions,
|
2022-01-04 08:28:59 -08:00
|
|
|
::llvm::SmallVectorImpl<::mlir::Type> &inferredReturnTypes) {
|
|
|
|
inferredReturnTypes.assign({::mlir::IntegerType::get(context, 16)});
|
|
|
|
return ::mlir::success();
|
|
|
|
}
|
|
|
|
|
2021-03-24 08:25:25 +00:00
|
|
|
void *TestDialect::getRegisteredInterfaceForOp(TypeID typeID,
|
|
|
|
OperationName opName) {
|
|
|
|
if (opName.getIdentifier() == "test.unregistered_side_effect_op" &&
|
|
|
|
typeID == TypeID::get<TestEffectOpInterface>())
|
|
|
|
return fallbackEffectOpInterfaces;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-11-12 11:57:47 -08:00
|
|
|
LogicalResult TestDialect::verifyOperationAttribute(Operation *op,
|
|
|
|
NamedAttribute namedAttr) {
|
2021-11-18 05:23:32 +00:00
|
|
|
if (namedAttr.getName() == "test.invalid_attr")
|
2019-11-12 11:57:47 -08:00
|
|
|
return op->emitError() << "invalid to use 'test.invalid_attr'";
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2019-10-18 16:02:56 -07:00
|
|
|
LogicalResult TestDialect::verifyRegionArgAttribute(Operation *op,
|
|
|
|
unsigned regionIndex,
|
|
|
|
unsigned argIndex,
|
|
|
|
NamedAttribute namedAttr) {
|
2021-11-18 05:23:32 +00:00
|
|
|
if (namedAttr.getName() == "test.invalid_attr")
|
2019-10-18 16:02:56 -07:00
|
|
|
return op->emitError() << "invalid to use 'test.invalid_attr'";
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
TestDialect::verifyRegionResultAttribute(Operation *op, unsigned regionIndex,
|
|
|
|
unsigned resultIndex,
|
|
|
|
NamedAttribute namedAttr) {
|
2021-11-18 05:23:32 +00:00
|
|
|
if (namedAttr.getName() == "test.invalid_attr")
|
2019-10-18 16:02:56 -07:00
|
|
|
return op->emitError() << "invalid to use 'test.invalid_attr'";
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-12-14 11:39:19 +01:00
|
|
|
std::optional<Dialect::ParseOpHook>
|
2021-03-23 00:33:03 +00:00
|
|
|
TestDialect::getParseOperationHook(StringRef opName) const {
|
|
|
|
if (opName == "test.dialect_custom_printer") {
|
|
|
|
return ParseOpHook{[](OpAsmParser &parser, OperationState &state) {
|
|
|
|
return parser.parseKeyword("custom_format");
|
|
|
|
}};
|
|
|
|
}
|
2021-12-10 00:47:48 +00:00
|
|
|
if (opName == "test.dialect_custom_format_fallback") {
|
|
|
|
return ParseOpHook{[](OpAsmParser &parser, OperationState &state) {
|
|
|
|
return parser.parseKeyword("custom_format_fallback");
|
|
|
|
}};
|
|
|
|
}
|
2022-05-19 16:13:51 +02:00
|
|
|
if (opName == "test.dialect_custom_printer.with.dot") {
|
|
|
|
return ParseOpHook{[](OpAsmParser &parser, OperationState &state) {
|
|
|
|
return ParseResult::success();
|
|
|
|
}};
|
|
|
|
}
|
2022-12-03 18:50:27 -08:00
|
|
|
return std::nullopt;
|
2021-03-23 00:33:03 +00:00
|
|
|
}
|
|
|
|
|
2021-08-28 03:02:55 +00:00
|
|
|
llvm::unique_function<void(Operation *, OpAsmPrinter &)>
|
|
|
|
TestDialect::getOperationPrinter(Operation *op) const {
|
2021-03-23 00:33:03 +00:00
|
|
|
StringRef opName = op->getName().getStringRef();
|
|
|
|
if (opName == "test.dialect_custom_printer") {
|
2021-08-28 03:02:55 +00:00
|
|
|
return [](Operation *op, OpAsmPrinter &printer) {
|
2021-08-28 03:03:15 +00:00
|
|
|
printer.getStream() << " custom_format";
|
2021-08-28 03:02:55 +00:00
|
|
|
};
|
2021-03-23 00:33:03 +00:00
|
|
|
}
|
2021-12-10 00:47:48 +00:00
|
|
|
if (opName == "test.dialect_custom_format_fallback") {
|
|
|
|
return [](Operation *op, OpAsmPrinter &printer) {
|
|
|
|
printer.getStream() << " custom_format_fallback";
|
|
|
|
};
|
|
|
|
}
|
2021-08-28 03:02:55 +00:00
|
|
|
return {};
|
2021-03-23 00:33:03 +00:00
|
|
|
}
|
|
|
|
|
2022-08-30 12:13:15 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TypedAttrOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Parse an attribute with a given type.
|
|
|
|
static ParseResult parseAttrElideType(AsmParser &parser, TypeAttr type,
|
|
|
|
Attribute &attr) {
|
|
|
|
return parser.parseAttribute(attr, type.getValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Print an attribute without its type.
|
|
|
|
static void printAttrElideType(AsmPrinter &printer, Operation *op,
|
|
|
|
TypeAttr type, Attribute attr) {
|
|
|
|
printer.printAttributeWithoutType(attr);
|
|
|
|
}
|
|
|
|
|
2020-03-05 12:40:23 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestBranchOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-04-08 08:17:36 +02:00
|
|
|
SuccessorOperands TestBranchOp::getSuccessorOperands(unsigned index) {
|
2020-03-05 12:40:23 -08:00
|
|
|
assert(index == 0 && "invalid successor index");
|
2022-04-08 08:17:36 +02:00
|
|
|
return SuccessorOperands(getTargetOperandsMutable());
|
2020-03-05 12:40:23 -08:00
|
|
|
}
|
|
|
|
|
2022-03-21 13:26:00 +01:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestProducingBranchOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-04-08 08:17:36 +02:00
|
|
|
SuccessorOperands TestProducingBranchOp::getSuccessorOperands(unsigned index) {
|
2022-03-21 13:26:00 +01:00
|
|
|
assert(index <= 1 && "invalid successor index");
|
2022-03-28 16:54:34 +02:00
|
|
|
if (index == 1)
|
2022-04-08 08:17:36 +02:00
|
|
|
return SuccessorOperands(getFirstOperandsMutable());
|
|
|
|
return SuccessorOperands(getSecondOperandsMutable());
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestProducingBranchOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
SuccessorOperands TestInternalBranchOp::getSuccessorOperands(unsigned index) {
|
|
|
|
assert(index <= 1 && "invalid successor index");
|
|
|
|
if (index == 0)
|
|
|
|
return SuccessorOperands(0, getSuccessOperandsMutable());
|
|
|
|
return SuccessorOperands(1, getErrorOperandsMutable());
|
2022-03-21 13:26:00 +01:00
|
|
|
}
|
|
|
|
|
2021-05-27 17:26:45 +09:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestDialectCanonicalizerOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
static LogicalResult
|
|
|
|
dialectCanonicalizationPattern(TestDialectCanonicalizerOp op,
|
|
|
|
PatternRewriter &rewriter) {
|
2021-10-12 23:14:57 +00:00
|
|
|
rewriter.replaceOpWithNewOp<arith::ConstantOp>(
|
|
|
|
op, rewriter.getI32IntegerAttr(42));
|
2021-05-27 17:26:45 +09:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestDialect::getCanonicalizationPatterns(
|
|
|
|
RewritePatternSet &results) const {
|
|
|
|
results.add(&dialectCanonicalizationPattern);
|
|
|
|
}
|
|
|
|
|
2022-03-16 11:45:14 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestCallOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
LogicalResult TestCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
|
|
|
|
// Check that the callee attribute was specified.
|
|
|
|
auto fnAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("callee");
|
|
|
|
if (!fnAttr)
|
|
|
|
return emitOpError("requires a 'callee' symbol reference attribute");
|
|
|
|
if (!symbolTable.lookupNearestSymbolFrom<FunctionOpInterface>(*this, fnAttr))
|
|
|
|
return emitOpError() << "'" << fnAttr.getValue()
|
|
|
|
<< "' does not reference a valid function";
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2023-03-22 08:38:55 +01:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ConversionFuncOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
ParseResult ConversionFuncOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &result) {
|
|
|
|
auto buildFuncType =
|
|
|
|
[](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
|
|
|
|
function_interface_impl::VariadicFlag,
|
|
|
|
std::string &) { return builder.getFunctionType(argTypes, results); };
|
|
|
|
|
|
|
|
return function_interface_impl::parseFunctionOp(
|
|
|
|
parser, result, /*allowVariadic=*/false,
|
|
|
|
getFunctionTypeAttrName(result.name), buildFuncType,
|
|
|
|
getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConversionFuncOp::print(OpAsmPrinter &p) {
|
|
|
|
function_interface_impl::printFunctionOp(
|
|
|
|
p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
|
|
|
|
getArgAttrsAttrName(), getResAttrsAttrName());
|
|
|
|
}
|
|
|
|
|
2020-06-17 13:13:48 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestFoldToCallOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
struct FoldToCallOpPattern : public OpRewritePattern<FoldToCallOp> {
|
|
|
|
using OpRewritePattern<FoldToCallOp>::OpRewritePattern;
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(FoldToCallOp op,
|
|
|
|
PatternRewriter &rewriter) const override {
|
2022-02-26 14:49:54 -08:00
|
|
|
rewriter.replaceOpWithNewOp<func::CallOp>(op, TypeRange(),
|
|
|
|
op.getCalleeAttr(), ValueRange());
|
2020-06-17 13:13:48 -07:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
};
|
2021-12-07 18:27:58 +00:00
|
|
|
} // namespace
|
2020-06-17 13:13:48 -07:00
|
|
|
|
2021-03-22 16:58:34 -07:00
|
|
|
void FoldToCallOp::getCanonicalizationPatterns(RewritePatternSet &results,
|
|
|
|
MLIRContext *context) {
|
|
|
|
results.add<FoldToCallOpPattern>(context);
|
2020-06-17 13:13:48 -07:00
|
|
|
}
|
|
|
|
|
2020-08-31 12:33:36 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Test Format* operations
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Parsing
|
|
|
|
|
2022-03-21 21:42:13 +01:00
|
|
|
static ParseResult parseCustomOptionalOperand(
|
2022-12-14 11:39:19 +01:00
|
|
|
OpAsmParser &parser,
|
|
|
|
std::optional<OpAsmParser::UnresolvedOperand> &optOperand) {
|
2022-02-17 10:24:10 +05:30
|
|
|
if (succeeded(parser.parseOptionalLParen())) {
|
|
|
|
optOperand.emplace();
|
|
|
|
if (parser.parseOperand(*optOperand) || parser.parseRParen())
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2020-08-31 12:33:36 -07:00
|
|
|
static ParseResult parseCustomDirectiveOperands(
|
2022-03-21 21:42:13 +01:00
|
|
|
OpAsmParser &parser, OpAsmParser::UnresolvedOperand &operand,
|
2022-12-14 11:39:19 +01:00
|
|
|
std::optional<OpAsmParser::UnresolvedOperand> &optOperand,
|
2022-03-21 21:42:13 +01:00
|
|
|
SmallVectorImpl<OpAsmParser::UnresolvedOperand> &varOperands) {
|
2020-08-31 12:33:36 -07:00
|
|
|
if (parser.parseOperand(operand))
|
|
|
|
return failure();
|
|
|
|
if (succeeded(parser.parseOptionalComma())) {
|
|
|
|
optOperand.emplace();
|
|
|
|
if (parser.parseOperand(*optOperand))
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
if (parser.parseArrow() || parser.parseLParen() ||
|
|
|
|
parser.parseOperandList(varOperands) || parser.parseRParen())
|
|
|
|
return failure();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
static ParseResult
|
|
|
|
parseCustomDirectiveResults(OpAsmParser &parser, Type &operandType,
|
|
|
|
Type &optOperandType,
|
|
|
|
SmallVectorImpl<Type> &varOperandTypes) {
|
|
|
|
if (parser.parseColon())
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
if (parser.parseType(operandType))
|
|
|
|
return failure();
|
|
|
|
if (succeeded(parser.parseOptionalComma())) {
|
|
|
|
if (parser.parseType(optOperandType))
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
if (parser.parseArrow() || parser.parseLParen() ||
|
|
|
|
parser.parseTypeList(varOperandTypes) || parser.parseRParen())
|
|
|
|
return failure();
|
|
|
|
return success();
|
|
|
|
}
|
2020-09-18 06:13:25 -04:00
|
|
|
static ParseResult
|
|
|
|
parseCustomDirectiveWithTypeRefs(OpAsmParser &parser, Type operandType,
|
|
|
|
Type optOperandType,
|
|
|
|
const SmallVectorImpl<Type> &varOperandTypes) {
|
|
|
|
if (parser.parseKeyword("type_refs_capture"))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
Type operandType2, optOperandType2;
|
|
|
|
SmallVector<Type, 1> varOperandTypes2;
|
|
|
|
if (parseCustomDirectiveResults(parser, operandType2, optOperandType2,
|
|
|
|
varOperandTypes2))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
if (operandType != operandType2 || optOperandType != optOperandType2 ||
|
|
|
|
varOperandTypes != varOperandTypes2)
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
2020-08-31 12:33:36 -07:00
|
|
|
static ParseResult parseCustomDirectiveOperandsAndTypes(
|
2022-03-21 21:42:13 +01:00
|
|
|
OpAsmParser &parser, OpAsmParser::UnresolvedOperand &operand,
|
2022-12-14 11:39:19 +01:00
|
|
|
std::optional<OpAsmParser::UnresolvedOperand> &optOperand,
|
2022-03-21 21:42:13 +01:00
|
|
|
SmallVectorImpl<OpAsmParser::UnresolvedOperand> &varOperands,
|
|
|
|
Type &operandType, Type &optOperandType,
|
|
|
|
SmallVectorImpl<Type> &varOperandTypes) {
|
2020-08-31 12:33:36 -07:00
|
|
|
if (parseCustomDirectiveOperands(parser, operand, optOperand, varOperands) ||
|
|
|
|
parseCustomDirectiveResults(parser, operandType, optOperandType,
|
|
|
|
varOperandTypes))
|
|
|
|
return failure();
|
|
|
|
return success();
|
|
|
|
}
|
2020-08-31 12:33:55 -07:00
|
|
|
static ParseResult parseCustomDirectiveRegions(
|
|
|
|
OpAsmParser &parser, Region ®ion,
|
|
|
|
SmallVectorImpl<std::unique_ptr<Region>> &varRegions) {
|
|
|
|
if (parser.parseRegion(region))
|
|
|
|
return failure();
|
|
|
|
if (failed(parser.parseOptionalComma()))
|
|
|
|
return success();
|
|
|
|
std::unique_ptr<Region> varRegion = std::make_unique<Region>();
|
|
|
|
if (parser.parseRegion(*varRegion))
|
|
|
|
return failure();
|
|
|
|
varRegions.emplace_back(std::move(varRegion));
|
|
|
|
return success();
|
|
|
|
}
|
2020-08-31 12:33:36 -07:00
|
|
|
static ParseResult
|
|
|
|
parseCustomDirectiveSuccessors(OpAsmParser &parser, Block *&successor,
|
|
|
|
SmallVectorImpl<Block *> &varSuccessors) {
|
|
|
|
if (parser.parseSuccessor(successor))
|
|
|
|
return failure();
|
|
|
|
if (failed(parser.parseOptionalComma()))
|
|
|
|
return success();
|
|
|
|
Block *varSuccessor;
|
|
|
|
if (parser.parseSuccessor(varSuccessor))
|
|
|
|
return failure();
|
|
|
|
varSuccessors.append(2, varSuccessor);
|
|
|
|
return success();
|
|
|
|
}
|
2020-09-23 18:01:39 +00:00
|
|
|
static ParseResult parseCustomDirectiveAttributes(OpAsmParser &parser,
|
|
|
|
IntegerAttr &attr,
|
|
|
|
IntegerAttr &optAttr) {
|
|
|
|
if (parser.parseAttribute(attr))
|
|
|
|
return failure();
|
|
|
|
if (succeeded(parser.parseOptionalComma())) {
|
|
|
|
if (parser.parseAttribute(optAttr))
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
return success();
|
|
|
|
}
|
2022-12-06 11:56:53 -08:00
|
|
|
static ParseResult parseCustomDirectiveSpacing(OpAsmParser &parser,
|
|
|
|
mlir::StringAttr &attr) {
|
|
|
|
return parser.parseAttribute(attr);
|
|
|
|
}
|
2020-10-28 01:01:44 +00:00
|
|
|
static ParseResult parseCustomDirectiveAttrDict(OpAsmParser &parser,
|
|
|
|
NamedAttrList &attrs) {
|
|
|
|
return parser.parseOptionalAttrDict(attrs);
|
|
|
|
}
|
2021-02-09 14:32:15 -08:00
|
|
|
static ParseResult parseCustomDirectiveOptionalOperandRef(
|
2022-12-14 11:39:19 +01:00
|
|
|
OpAsmParser &parser,
|
|
|
|
std::optional<OpAsmParser::UnresolvedOperand> &optOperand) {
|
2021-02-09 14:32:15 -08:00
|
|
|
int64_t operandCount = 0;
|
|
|
|
if (parser.parseInteger(operandCount))
|
|
|
|
return failure();
|
|
|
|
bool expectedOptionalOperand = operandCount == 0;
|
2022-06-20 20:05:16 -07:00
|
|
|
return success(expectedOptionalOperand != optOperand.has_value());
|
2021-02-09 14:32:15 -08:00
|
|
|
}
|
2020-10-28 01:01:44 +00:00
|
|
|
|
2020-08-31 12:33:36 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Printing
|
|
|
|
|
2022-02-17 10:24:10 +05:30
|
|
|
static void printCustomOptionalOperand(OpAsmPrinter &printer, Operation *,
|
|
|
|
Value optOperand) {
|
|
|
|
if (optOperand)
|
|
|
|
printer << "(" << optOperand << ") ";
|
|
|
|
}
|
|
|
|
|
2020-10-28 01:01:44 +00:00
|
|
|
static void printCustomDirectiveOperands(OpAsmPrinter &printer, Operation *,
|
|
|
|
Value operand, Value optOperand,
|
2020-08-31 12:33:36 -07:00
|
|
|
OperandRange varOperands) {
|
|
|
|
printer << operand;
|
|
|
|
if (optOperand)
|
|
|
|
printer << ", " << optOperand;
|
|
|
|
printer << " -> (" << varOperands << ")";
|
|
|
|
}
|
2020-10-28 01:01:44 +00:00
|
|
|
static void printCustomDirectiveResults(OpAsmPrinter &printer, Operation *,
|
|
|
|
Type operandType, Type optOperandType,
|
2020-08-31 12:33:36 -07:00
|
|
|
TypeRange varOperandTypes) {
|
|
|
|
printer << " : " << operandType;
|
|
|
|
if (optOperandType)
|
|
|
|
printer << ", " << optOperandType;
|
|
|
|
printer << " -> (" << varOperandTypes << ")";
|
|
|
|
}
|
2020-09-18 06:13:25 -04:00
|
|
|
static void printCustomDirectiveWithTypeRefs(OpAsmPrinter &printer,
|
2020-10-28 01:01:44 +00:00
|
|
|
Operation *op, Type operandType,
|
2020-09-18 06:13:25 -04:00
|
|
|
Type optOperandType,
|
|
|
|
TypeRange varOperandTypes) {
|
|
|
|
printer << " type_refs_capture ";
|
2020-10-28 01:01:44 +00:00
|
|
|
printCustomDirectiveResults(printer, op, operandType, optOperandType,
|
2020-09-18 06:13:25 -04:00
|
|
|
varOperandTypes);
|
|
|
|
}
|
2020-10-28 01:01:44 +00:00
|
|
|
static void printCustomDirectiveOperandsAndTypes(
|
|
|
|
OpAsmPrinter &printer, Operation *op, Value operand, Value optOperand,
|
|
|
|
OperandRange varOperands, Type operandType, Type optOperandType,
|
|
|
|
TypeRange varOperandTypes) {
|
|
|
|
printCustomDirectiveOperands(printer, op, operand, optOperand, varOperands);
|
|
|
|
printCustomDirectiveResults(printer, op, operandType, optOperandType,
|
2020-08-31 12:33:36 -07:00
|
|
|
varOperandTypes);
|
|
|
|
}
|
2020-10-28 01:01:44 +00:00
|
|
|
static void printCustomDirectiveRegions(OpAsmPrinter &printer, Operation *,
|
|
|
|
Region ®ion,
|
2020-08-31 12:33:55 -07:00
|
|
|
MutableArrayRef<Region> varRegions) {
|
|
|
|
printer.printRegion(region);
|
|
|
|
if (!varRegions.empty()) {
|
|
|
|
printer << ", ";
|
|
|
|
for (Region ®ion : varRegions)
|
|
|
|
printer.printRegion(region);
|
|
|
|
}
|
|
|
|
}
|
2020-10-28 01:01:44 +00:00
|
|
|
static void printCustomDirectiveSuccessors(OpAsmPrinter &printer, Operation *,
|
2020-08-31 12:33:36 -07:00
|
|
|
Block *successor,
|
|
|
|
SuccessorRange varSuccessors) {
|
|
|
|
printer << successor;
|
|
|
|
if (!varSuccessors.empty())
|
|
|
|
printer << ", " << varSuccessors.front();
|
|
|
|
}
|
2020-10-28 01:01:44 +00:00
|
|
|
static void printCustomDirectiveAttributes(OpAsmPrinter &printer, Operation *,
|
2020-09-23 18:01:39 +00:00
|
|
|
Attribute attribute,
|
|
|
|
Attribute optAttribute) {
|
|
|
|
printer << attribute;
|
|
|
|
if (optAttribute)
|
|
|
|
printer << ", " << optAttribute;
|
|
|
|
}
|
2022-12-06 11:56:53 -08:00
|
|
|
static void printCustomDirectiveSpacing(OpAsmPrinter &printer, Operation *op,
|
|
|
|
Attribute attribute) {
|
|
|
|
printer << attribute;
|
|
|
|
}
|
2020-10-28 01:01:44 +00:00
|
|
|
static void printCustomDirectiveAttrDict(OpAsmPrinter &printer, Operation *op,
|
2020-12-17 17:10:12 -08:00
|
|
|
DictionaryAttr attrs) {
|
|
|
|
printer.printOptionalAttrDict(attrs.getValue());
|
2020-10-28 01:01:44 +00:00
|
|
|
}
|
2021-02-09 14:32:15 -08:00
|
|
|
|
|
|
|
static void printCustomDirectiveOptionalOperandRef(OpAsmPrinter &printer,
|
|
|
|
Operation *op,
|
|
|
|
Value optOperand) {
|
|
|
|
printer << (optOperand ? "1" : "0");
|
|
|
|
}
|
|
|
|
|
2019-08-19 15:26:43 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Test IsolatedRegionOp - parse passthrough region arguments.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult IsolatedRegionOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &result) {
|
2019-08-19 15:26:43 -07:00
|
|
|
// Parse the input operand.
|
2022-04-28 17:26:43 -07:00
|
|
|
OpAsmParser::Argument argInfo;
|
|
|
|
argInfo.type = parser.getBuilder().getIndexType();
|
|
|
|
if (parser.parseOperand(argInfo.ssaName) ||
|
|
|
|
parser.resolveOperand(argInfo.ssaName, argInfo.type, result.operands))
|
2019-08-19 15:26:43 -07:00
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Parse the body region, and reuse the operand info as the argument info.
|
2019-09-20 19:47:05 -07:00
|
|
|
Region *body = result.addRegion();
|
2022-04-28 17:26:43 -07:00
|
|
|
return parser.parseRegion(*body, argInfo, /*enableNameShadowing=*/true);
|
2019-08-19 15:26:43 -07:00
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
void IsolatedRegionOp::print(OpAsmPrinter &p) {
|
2023-05-07 19:28:46 -04:00
|
|
|
p << ' ';
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printOperand(getOperand());
|
|
|
|
p.shadowRegionArgs(getRegion(), getOperand());
|
2022-01-18 07:47:25 +00:00
|
|
|
p << ' ';
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
|
2019-08-23 10:35:24 -07:00
|
|
|
}
|
|
|
|
|
[MLIR] Add RegionKindInterface
Some dialects have semantics which is not well represented by common
SSA structures with dominance constraints. This patch allows
operations to declare the 'kind' of their contained regions.
Currently, two kinds are allowed: "SSACFG" and "Graph". The only
difference between them at the moment is that SSACFG regions are
required to have dominance, while Graph regions are not required to
have dominance. The intention is that this Interface would be
generated by ODS for existing operations, although this has not yet
been implemented. Presumably, if someone were interested in code
generation, we might also have a "CFG" dialect, which defines control
flow, but does not require SSA.
The new behavior is mostly identical to the previous behavior, since
registered operations without a RegionKindInterface are assumed to
contain SSACFG regions. However, the behavior has changed for
unregistered operations. Previously, these were checked for
dominance, however the new behavior allows dominance violations, in
order to allow the processing of unregistered dialects with Graph
regions. One implication of this is that regions in unregistered
operations with more than one op are no longer CSE'd (since it
requires dominance info).
I've also reorganized the LangRef documentation to remove assertions
about "sequential execution", "SSA Values", and "Dominance". Instead,
the core IR is simply "ordered" (i.e. totally ordered) and consists of
"Values". I've also clarified some things about how control flow
passes between blocks in an SSACFG region. Control Flow must enter a
region at the entry block and follow terminator operation successors
or be returned to the containing op. Graph regions do not define a
notion of control flow.
see discussion here:
https://llvm.discourse.group/t/rfc-allowing-dialects-to-relax-the-ssa-dominance-condition/833/53
Differential Revision: https://reviews.llvm.org/D80358
2020-05-15 10:33:13 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Test SSACFGRegionOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
RegionKind SSACFGRegionOp::getRegionKind(unsigned index) {
|
|
|
|
return RegionKind::SSACFG;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Test GraphRegionOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
RegionKind GraphRegionOp::getRegionKind(unsigned index) {
|
|
|
|
return RegionKind::Graph;
|
|
|
|
}
|
|
|
|
|
2020-04-29 05:38:23 +05:30
|
|
|
//===----------------------------------------------------------------------===//
|
2020-05-06 12:32:06 +05:30
|
|
|
// Test AffineScopeOp
|
2020-04-29 05:38:23 +05:30
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult AffineScopeOp::parse(OpAsmParser &parser, OperationState &result) {
|
2020-04-29 05:38:23 +05:30
|
|
|
// Parse the body region, and reuse the operand info as the argument info.
|
|
|
|
Region *body = result.addRegion();
|
|
|
|
return parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{});
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
void AffineScopeOp::print(OpAsmPrinter &p) {
|
2023-05-07 19:28:46 -04:00
|
|
|
p << " ";
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
|
2020-04-29 05:38:23 +05:30
|
|
|
}
|
|
|
|
|
2019-09-08 23:39:34 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-09-17 17:54:54 -07:00
|
|
|
// Test parser.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult ParseIntegerLiteralOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &result) {
|
2020-12-14 11:53:43 -08:00
|
|
|
if (parser.parseOptionalColon())
|
|
|
|
return success();
|
|
|
|
uint64_t numResults;
|
|
|
|
if (parser.parseInteger(numResults))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
IndexType type = parser.getBuilder().getIndexType();
|
|
|
|
for (unsigned i = 0; i < numResults; ++i)
|
|
|
|
result.addTypes(type);
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
void ParseIntegerLiteralOp::print(OpAsmPrinter &p) {
|
|
|
|
if (unsigned numResults = getNumResults())
|
2020-12-14 11:53:43 -08:00
|
|
|
p << " : " << numResults;
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult ParseWrappedKeywordOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &result) {
|
2019-09-17 17:54:54 -07:00
|
|
|
StringRef keyword;
|
2019-09-20 11:36:49 -07:00
|
|
|
if (parser.parseKeyword(&keyword))
|
2019-09-17 17:54:54 -07:00
|
|
|
return failure();
|
2019-09-20 19:47:05 -07:00
|
|
|
result.addAttribute("keyword", parser.getBuilder().getStringAttr(keyword));
|
2019-09-17 17:54:54 -07:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
void ParseWrappedKeywordOp::print(OpAsmPrinter &p) { p << " " << getKeyword(); }
|
2019-09-17 17:54:54 -07:00
|
|
|
|
2022-11-15 21:23:18 -08:00
|
|
|
ParseResult ParseB64BytesOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &result) {
|
|
|
|
std::vector<char> bytes;
|
|
|
|
if (parser.parseBase64Bytes(&bytes))
|
|
|
|
return failure();
|
|
|
|
result.addAttribute("b64", parser.getBuilder().getStringAttr(
|
|
|
|
StringRef(&bytes.front(), bytes.size())));
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ParseB64BytesOp::print(OpAsmPrinter &p) {
|
2023-05-07 19:28:46 -04:00
|
|
|
p << " \"" << llvm::encodeBase64(getB64()) << "\"";
|
2022-11-15 21:23:18 -08:00
|
|
|
}
|
|
|
|
|
2019-09-08 23:39:34 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-09-17 17:54:54 -07:00
|
|
|
// Test WrapRegionOp - wrapping op exercising `parseGenericOperation()`.
|
2019-09-08 23:39:34 -07:00
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult WrappingRegionOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &result) {
|
2019-09-20 11:36:49 -07:00
|
|
|
if (parser.parseKeyword("wraps"))
|
2019-09-08 23:39:34 -07:00
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Parse the wrapped op in a region
|
2019-09-20 19:47:05 -07:00
|
|
|
Region &body = *result.addRegion();
|
2019-09-08 23:39:34 -07:00
|
|
|
body.push_back(new Block);
|
|
|
|
Block &block = body.back();
|
2021-12-20 19:45:05 +00:00
|
|
|
Operation *wrappedOp = parser.parseGenericOperation(&block, block.begin());
|
|
|
|
if (!wrappedOp)
|
2019-09-08 23:39:34 -07:00
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Create a return terminator in the inner region, pass as operand to the
|
|
|
|
// terminator the returned values from the wrapped operation.
|
2021-12-20 19:45:05 +00:00
|
|
|
SmallVector<Value, 8> returnOperands(wrappedOp->getResults());
|
2021-09-29 17:47:08 -07:00
|
|
|
OpBuilder builder(parser.getContext());
|
2019-09-08 23:39:34 -07:00
|
|
|
builder.setInsertionPointToEnd(&block);
|
2021-12-20 19:45:05 +00:00
|
|
|
builder.create<TestReturnOp>(wrappedOp->getLoc(), returnOperands);
|
2019-09-08 23:39:34 -07:00
|
|
|
|
|
|
|
// Get the results type for the wrapping op from the terminator operands.
|
2021-12-20 19:45:05 +00:00
|
|
|
Operation &returnOp = body.back().back();
|
|
|
|
result.types.append(returnOp.operand_type_begin(),
|
|
|
|
returnOp.operand_type_end());
|
2019-10-28 15:11:00 -07:00
|
|
|
|
|
|
|
// Use the location of the wrapped op for the "test.wrapping_region" op.
|
2021-12-20 19:45:05 +00:00
|
|
|
result.location = wrappedOp->getLoc();
|
2019-10-28 15:11:00 -07:00
|
|
|
|
2019-09-08 23:39:34 -07:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
void WrappingRegionOp::print(OpAsmPrinter &p) {
|
2021-08-28 03:03:15 +00:00
|
|
|
p << " wraps ";
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printGenericOp(&getRegion().front().front());
|
2019-09-08 23:39:34 -07:00
|
|
|
}
|
|
|
|
|
2021-11-23 06:05:41 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Test PrettyPrintedRegionOp - exercising the following parser APIs
|
|
|
|
// parseGenericOperationAfterOpName
|
|
|
|
// parseCustomOperationName
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult PrettyPrintedRegionOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &result) {
|
2021-11-23 06:05:41 +00:00
|
|
|
|
2022-01-26 15:49:53 -08:00
|
|
|
SMLoc loc = parser.getCurrentLocation();
|
2021-11-23 06:05:41 +00:00
|
|
|
Location currLocation = parser.getEncodedSourceLoc(loc);
|
|
|
|
|
|
|
|
// Parse the operands.
|
2022-03-21 21:42:13 +01:00
|
|
|
SmallVector<OpAsmParser::UnresolvedOperand, 2> operands;
|
2021-11-23 06:05:41 +00:00
|
|
|
if (parser.parseOperandList(operands))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Check if we are parsing the pretty-printed version
|
|
|
|
// test.pretty_printed_region start <inner-op> end : <functional-type>
|
|
|
|
// Else fallback to parsing the "non pretty-printed" version.
|
|
|
|
if (!succeeded(parser.parseOptionalKeyword("start")))
|
2023-01-09 18:11:07 +01:00
|
|
|
return parser.parseGenericOperationAfterOpName(result,
|
|
|
|
llvm::ArrayRef(operands));
|
2021-11-23 06:05:41 +00:00
|
|
|
|
|
|
|
FailureOr<OperationName> parseOpNameInfo = parser.parseCustomOperationName();
|
|
|
|
if (failed(parseOpNameInfo))
|
|
|
|
return failure();
|
|
|
|
|
2022-03-23 21:37:26 +00:00
|
|
|
StringAttr innerOpName = parseOpNameInfo->getIdentifier();
|
2021-11-23 06:05:41 +00:00
|
|
|
|
|
|
|
FunctionType opFntype;
|
2023-01-14 01:25:58 -08:00
|
|
|
std::optional<Location> explicitLoc;
|
2021-11-23 06:05:41 +00:00
|
|
|
if (parser.parseKeyword("end") || parser.parseColon() ||
|
|
|
|
parser.parseType(opFntype) ||
|
|
|
|
parser.parseOptionalLocationSpecifier(explicitLoc))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// If location of the op is explicitly provided, then use it; Else use
|
|
|
|
// the parser's current location.
|
2022-06-19 10:34:41 -07:00
|
|
|
Location opLoc = explicitLoc.value_or(currLocation);
|
2021-11-23 06:05:41 +00:00
|
|
|
|
|
|
|
// Derive the SSA-values for op's operands.
|
|
|
|
if (parser.resolveOperands(operands, opFntype.getInputs(), loc,
|
|
|
|
result.operands))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Add a region for op.
|
|
|
|
Region ®ion = *result.addRegion();
|
|
|
|
|
|
|
|
// Create a basic-block inside op's region.
|
|
|
|
Block &block = region.emplaceBlock();
|
|
|
|
|
|
|
|
// Create and insert an "inner-op" operation in the block.
|
|
|
|
// Just for testing purposes, we can assume that inner op is a binary op with
|
|
|
|
// result and operand types all same as the test-op's first operand.
|
|
|
|
Type innerOpType = opFntype.getInput(0);
|
|
|
|
Value lhs = block.addArgument(innerOpType, opLoc);
|
|
|
|
Value rhs = block.addArgument(innerOpType, opLoc);
|
|
|
|
|
|
|
|
OpBuilder builder(parser.getBuilder().getContext());
|
|
|
|
builder.setInsertionPointToStart(&block);
|
|
|
|
|
2022-03-23 21:37:26 +00:00
|
|
|
Operation *innerOp =
|
|
|
|
builder.create(opLoc, innerOpName, /*operands=*/{lhs, rhs}, innerOpType);
|
2021-11-23 06:05:41 +00:00
|
|
|
|
|
|
|
// Insert a return statement in the block returning the inner-op's result.
|
|
|
|
builder.create<TestReturnOp>(innerOp->getLoc(), innerOp->getResults());
|
|
|
|
|
|
|
|
// Populate the op operation-state with result-type and location.
|
|
|
|
result.addTypes(opFntype.getResults());
|
|
|
|
result.location = innerOp->getLoc();
|
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
void PrettyPrintedRegionOp::print(OpAsmPrinter &p) {
|
2021-11-23 06:05:41 +00:00
|
|
|
p << ' ';
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printOperands(getOperands());
|
2021-11-23 06:05:41 +00:00
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
Operation &innerOp = getRegion().front().front();
|
2021-11-23 06:05:41 +00:00
|
|
|
// Assuming that region has a single non-terminator inner-op, if the inner-op
|
|
|
|
// meets some criteria (which in this case is a simple one based on the name
|
|
|
|
// of inner-op), then we can print the entire region in a succinct way.
|
2023-07-10 19:15:29 +02:00
|
|
|
// Here we assume that the prototype of "test.special.op" can be trivially
|
|
|
|
// derived while parsing it back.
|
2023-03-13 16:37:21 +01:00
|
|
|
if (innerOp.getName().getStringRef().equals("test.special.op")) {
|
|
|
|
p << " start test.special.op end";
|
2021-11-23 06:05:41 +00:00
|
|
|
} else {
|
|
|
|
p << " (";
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printRegion(getRegion());
|
2021-11-23 06:05:41 +00:00
|
|
|
p << ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
p << " : ";
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printFunctionalType(*this);
|
2021-11-23 06:05:41 +00:00
|
|
|
}
|
|
|
|
|
2019-07-22 17:41:38 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Test PolyForOp - parse list of region arguments.
|
|
|
|
//===----------------------------------------------------------------------===//
|
2019-11-20 10:19:01 -08:00
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult PolyForOp::parse(OpAsmParser &parser, OperationState &result) {
|
2022-04-28 17:26:43 -07:00
|
|
|
SmallVector<OpAsmParser::Argument, 4> ivsInfo;
|
2019-07-22 17:41:38 -07:00
|
|
|
// Parse list of region arguments without a delimiter.
|
2022-04-28 17:26:43 -07:00
|
|
|
if (parser.parseArgumentList(ivsInfo, OpAsmParser::Delimiter::None))
|
2019-07-22 17:41:38 -07:00
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Parse the body region.
|
2019-09-20 19:47:05 -07:00
|
|
|
Region *body = result.addRegion();
|
2022-04-28 17:26:43 -07:00
|
|
|
for (auto &iv : ivsInfo)
|
|
|
|
iv.type = parser.getBuilder().getIndexType();
|
|
|
|
return parser.parseRegion(*body, ivsInfo);
|
2019-07-22 17:41:38 -07:00
|
|
|
}
|
|
|
|
|
2023-05-07 19:28:46 -04:00
|
|
|
void PolyForOp::print(OpAsmPrinter &p) {
|
|
|
|
p << " ";
|
|
|
|
llvm::interleaveComma(getRegion().getArguments(), p, [&](auto arg) {
|
|
|
|
p.printRegionArgument(arg, /*argAttrs =*/{}, /*omitType=*/true);
|
|
|
|
});
|
|
|
|
p << " ";
|
|
|
|
p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
|
|
|
|
}
|
2022-02-04 20:47:01 -08:00
|
|
|
|
2021-12-20 07:17:26 +00:00
|
|
|
void PolyForOp::getAsmBlockArgumentNames(Region ®ion,
|
|
|
|
OpAsmSetValueNameFn setNameFn) {
|
|
|
|
auto arrayAttr = getOperation()->getAttrOfType<ArrayAttr>("arg_names");
|
|
|
|
if (!arrayAttr)
|
|
|
|
return;
|
|
|
|
auto args = getRegion().front().getArguments();
|
|
|
|
auto e = std::min(arrayAttr.size(), args.size());
|
|
|
|
for (unsigned i = 0; i < e; ++i) {
|
2023-05-08 16:33:54 +02:00
|
|
|
if (auto strAttr = dyn_cast<StringAttr>(arrayAttr[i]))
|
2021-12-20 07:17:26 +00:00
|
|
|
setNameFn(args[i], strAttr.getValue());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-29 14:43:08 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestAttrWithLoc - parse/printOptionalLocationSpecifier
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
static ParseResult parseOptionalLoc(OpAsmParser &p, Attribute &loc) {
|
2023-01-14 01:25:58 -08:00
|
|
|
std::optional<Location> result;
|
2022-09-29 14:43:08 -07:00
|
|
|
SMLoc sourceLoc = p.getCurrentLocation();
|
|
|
|
if (p.parseOptionalLocationSpecifier(result))
|
|
|
|
return failure();
|
|
|
|
if (result)
|
|
|
|
loc = *result;
|
|
|
|
else
|
|
|
|
loc = p.getEncodedSourceLoc(sourceLoc);
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void printOptionalLoc(OpAsmPrinter &p, Operation *op, Attribute loc) {
|
2023-05-08 16:33:54 +02:00
|
|
|
p.printOptionalLocationSpecifier(cast<LocationAttr>(loc));
|
2022-09-29 14:43:08 -07:00
|
|
|
}
|
|
|
|
|
2019-08-06 11:08:22 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Test removing op with inner ops.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
2019-08-26 09:44:09 -07:00
|
|
|
struct TestRemoveOpWithInnerOps
|
|
|
|
: public OpRewritePattern<TestOpWithRegionPattern> {
|
|
|
|
using OpRewritePattern<TestOpWithRegionPattern>::OpRewritePattern;
|
2019-08-06 11:08:22 -07:00
|
|
|
|
2021-06-02 11:43:01 -07:00
|
|
|
void initialize() { setDebugName("TestRemoveOpWithInnerOps"); }
|
|
|
|
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult matchAndRewrite(TestOpWithRegionPattern op,
|
|
|
|
PatternRewriter &rewriter) const override {
|
2019-10-16 09:50:28 -07:00
|
|
|
rewriter.eraseOp(op);
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-08-06 11:08:22 -07:00
|
|
|
}
|
|
|
|
};
|
2021-12-07 18:27:58 +00:00
|
|
|
} // namespace
|
2019-08-06 11:08:22 -07:00
|
|
|
|
2019-08-26 09:44:09 -07:00
|
|
|
void TestOpWithRegionPattern::getCanonicalizationPatterns(
|
2021-03-22 16:58:34 -07:00
|
|
|
RewritePatternSet &results, MLIRContext *context) {
|
|
|
|
results.add<TestRemoveOpWithInnerOps>(context);
|
2019-08-06 11:08:22 -07:00
|
|
|
}
|
|
|
|
|
2023-01-10 21:27:18 +01:00
|
|
|
OpFoldResult TestOpWithRegionFold::fold(FoldAdaptor adaptor) {
|
2021-10-20 07:08:36 -07:00
|
|
|
return getOperand();
|
2019-08-26 09:44:09 -07:00
|
|
|
}
|
|
|
|
|
2023-02-14 08:45:08 -08:00
|
|
|
OpFoldResult TestOpConstant::fold(FoldAdaptor adaptor) { return getValue(); }
|
2020-10-09 13:32:01 -07:00
|
|
|
|
2019-10-09 20:42:32 -07:00
|
|
|
LogicalResult TestOpWithVariadicResultsAndFolder::fold(
|
2023-01-10 21:27:18 +01:00
|
|
|
FoldAdaptor adaptor, SmallVectorImpl<OpFoldResult> &results) {
|
2021-10-18 10:00:37 -07:00
|
|
|
for (Value input : this->getOperands()) {
|
2019-10-09 20:42:32 -07:00
|
|
|
results.push_back(input);
|
|
|
|
}
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2023-01-10 21:27:18 +01:00
|
|
|
OpFoldResult TestOpInPlaceFold::fold(FoldAdaptor adaptor) {
|
2023-02-26 10:46:01 -05:00
|
|
|
if (adaptor.getOp() && !(*this)->getAttr("attr")) {
|
2023-02-23 08:55:29 +01:00
|
|
|
// The folder adds "attr" if not present.
|
2023-01-10 21:27:18 +01:00
|
|
|
(*this)->setAttr("attr", adaptor.getOp());
|
2020-05-06 17:39:23 +02:00
|
|
|
return getResult();
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-01-10 21:27:18 +01:00
|
|
|
OpFoldResult TestPassthroughFold::fold(FoldAdaptor adaptor) {
|
2021-02-04 01:53:59 +00:00
|
|
|
return getOperand();
|
|
|
|
}
|
|
|
|
|
2022-12-25 19:29:31 +01:00
|
|
|
OpFoldResult TestOpFoldWithFoldAdaptor::fold(FoldAdaptor adaptor) {
|
|
|
|
int64_t sum = 0;
|
|
|
|
if (auto value = dyn_cast_or_null<IntegerAttr>(adaptor.getOp()))
|
|
|
|
sum += value.getValue().getSExtValue();
|
|
|
|
|
|
|
|
for (Attribute attr : adaptor.getVariadic())
|
|
|
|
if (auto value = dyn_cast_or_null<IntegerAttr>(attr))
|
|
|
|
sum += 2 * value.getValue().getSExtValue();
|
|
|
|
|
|
|
|
for (ArrayRef<Attribute> attrs : adaptor.getVarOfVar())
|
|
|
|
for (Attribute attr : attrs)
|
|
|
|
if (auto value = dyn_cast_or_null<IntegerAttr>(attr))
|
|
|
|
sum += 3 * value.getValue().getSExtValue();
|
|
|
|
|
|
|
|
sum += 4 * std::distance(adaptor.getBody().begin(), adaptor.getBody().end());
|
|
|
|
|
|
|
|
return IntegerAttr::get(getType(), sum);
|
|
|
|
}
|
|
|
|
|
[MLIR] Add RegionKindInterface
Some dialects have semantics which is not well represented by common
SSA structures with dominance constraints. This patch allows
operations to declare the 'kind' of their contained regions.
Currently, two kinds are allowed: "SSACFG" and "Graph". The only
difference between them at the moment is that SSACFG regions are
required to have dominance, while Graph regions are not required to
have dominance. The intention is that this Interface would be
generated by ODS for existing operations, although this has not yet
been implemented. Presumably, if someone were interested in code
generation, we might also have a "CFG" dialect, which defines control
flow, but does not require SSA.
The new behavior is mostly identical to the previous behavior, since
registered operations without a RegionKindInterface are assumed to
contain SSACFG regions. However, the behavior has changed for
unregistered operations. Previously, these were checked for
dominance, however the new behavior allows dominance violations, in
order to allow the processing of unregistered dialects with Graph
regions. One implication of this is that regions in unregistered
operations with more than one op are no longer CSE'd (since it
requires dominance info).
I've also reorganized the LangRef documentation to remove assertions
about "sequential execution", "SSA Values", and "Dominance". Instead,
the core IR is simply "ordered" (i.e. totally ordered) and consists of
"Values". I've also clarified some things about how control flow
passes between blocks in an SSACFG region. Control Flow must enter a
region at the entry block and follow terminator operation successors
or be returned to the containing op. Graph regions do not define a
notion of control flow.
see discussion here:
https://llvm.discourse.group/t/rfc-allowing-dialects-to-relax-the-ssa-dominance-condition/833/53
Differential Revision: https://reviews.llvm.org/D80358
2020-05-15 10:33:13 -07:00
|
|
|
LogicalResult OpWithInferTypeInterfaceOp::inferReturnTypes(
|
2022-12-14 11:39:19 +01:00
|
|
|
MLIRContext *, std::optional<Location> location, ValueRange operands,
|
2023-02-26 10:46:01 -05:00
|
|
|
DictionaryAttr attributes, OpaqueProperties properties, RegionRange regions,
|
2020-02-28 10:59:34 -08:00
|
|
|
SmallVectorImpl<Type> &inferredReturnTypes) {
|
2020-01-11 08:54:04 -08:00
|
|
|
if (operands[0].getType() != operands[1].getType()) {
|
2019-12-06 14:42:16 -08:00
|
|
|
return emitOptionalError(location, "operand type mismatch ",
|
2020-01-11 08:54:04 -08:00
|
|
|
operands[0].getType(), " vs ",
|
|
|
|
operands[1].getType());
|
2019-11-07 07:51:12 -08:00
|
|
|
}
|
2020-02-28 10:59:34 -08:00
|
|
|
inferredReturnTypes.assign({operands[0].getType()});
|
2019-12-06 14:42:16 -08:00
|
|
|
return success();
|
2019-09-29 17:28:29 -07:00
|
|
|
}
|
|
|
|
|
2022-07-18 22:18:52 -07:00
|
|
|
// TODO: We should be able to only define either inferReturnType or
|
|
|
|
// refineReturnType, currently only refineReturnType can be omitted.
|
|
|
|
LogicalResult OpWithRefineTypeInterfaceOp::inferReturnTypes(
|
2022-12-14 11:39:19 +01:00
|
|
|
MLIRContext *context, std::optional<Location> location, ValueRange operands,
|
2023-02-26 10:46:01 -05:00
|
|
|
DictionaryAttr attributes, OpaqueProperties properties, RegionRange regions,
|
2022-07-18 22:18:52 -07:00
|
|
|
SmallVectorImpl<Type> &returnTypes) {
|
|
|
|
returnTypes.clear();
|
|
|
|
return OpWithRefineTypeInterfaceOp::refineReturnTypes(
|
2023-02-26 10:46:01 -05:00
|
|
|
context, location, operands, attributes, properties, regions,
|
|
|
|
returnTypes);
|
2022-07-18 22:18:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult OpWithRefineTypeInterfaceOp::refineReturnTypes(
|
2022-12-14 11:39:19 +01:00
|
|
|
MLIRContext *, std::optional<Location> location, ValueRange operands,
|
2023-02-26 10:46:01 -05:00
|
|
|
DictionaryAttr attributes, OpaqueProperties properties, RegionRange regions,
|
2022-07-18 22:18:52 -07:00
|
|
|
SmallVectorImpl<Type> &returnTypes) {
|
|
|
|
if (operands[0].getType() != operands[1].getType()) {
|
|
|
|
return emitOptionalError(location, "operand type mismatch ",
|
|
|
|
operands[0].getType(), " vs ",
|
|
|
|
operands[1].getType());
|
|
|
|
}
|
|
|
|
// TODO: Add helper to make this more concise to write.
|
|
|
|
if (returnTypes.empty())
|
|
|
|
returnTypes.resize(1, nullptr);
|
|
|
|
if (returnTypes[0] && returnTypes[0] != operands[0].getType())
|
|
|
|
return emitOptionalError(location,
|
|
|
|
"required first operand and result to match");
|
|
|
|
returnTypes[0] = operands[0].getType();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2020-01-08 18:48:38 -08:00
|
|
|
LogicalResult OpWithShapedTypeInferTypeInterfaceOp::inferReturnTypeComponents(
|
2022-12-14 11:39:19 +01:00
|
|
|
MLIRContext *context, std::optional<Location> location,
|
2023-02-26 10:46:01 -05:00
|
|
|
ValueShapeRange operands, DictionaryAttr attributes,
|
|
|
|
OpaqueProperties properties, RegionRange regions,
|
2020-02-28 10:59:34 -08:00
|
|
|
SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
|
2020-02-28 08:37:09 -08:00
|
|
|
// Create return type consisting of the last element of the first operand.
|
2021-07-26 17:08:31 -07:00
|
|
|
auto operandType = operands.front().getType();
|
2023-05-08 16:33:54 +02:00
|
|
|
auto sval = dyn_cast<ShapedType>(operandType);
|
2020-02-28 08:37:09 -08:00
|
|
|
if (!sval) {
|
|
|
|
return emitOptionalError(location, "only shaped type operands allowed");
|
2020-01-08 18:48:38 -08:00
|
|
|
}
|
2022-12-14 11:39:19 +01:00
|
|
|
int64_t dim = sval.hasRank() ? sval.getShape().front() : ShapedType::kDynamic;
|
2020-12-17 12:24:45 -08:00
|
|
|
auto type = IntegerType::get(context, 17);
|
2022-12-07 17:20:56 -08:00
|
|
|
|
|
|
|
Attribute encoding;
|
2023-05-08 16:33:54 +02:00
|
|
|
if (auto rankedTy = dyn_cast<RankedTensorType>(sval))
|
2022-12-10 14:16:17 +00:00
|
|
|
encoding = rankedTy.getEncoding();
|
2022-12-07 17:20:56 -08:00
|
|
|
inferredReturnShapes.push_back(ShapedTypeComponents({dim}, type, encoding));
|
2020-02-28 08:37:09 -08:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult OpWithShapedTypeInferTypeInterfaceOp::reifyReturnTypeShapes(
|
2021-05-19 02:11:33 +00:00
|
|
|
OpBuilder &builder, ValueRange operands,
|
|
|
|
llvm::SmallVectorImpl<Value> &shapes) {
|
2020-02-28 08:37:09 -08:00
|
|
|
shapes = SmallVector<Value, 1>{
|
2021-07-01 09:58:48 +09:00
|
|
|
builder.createOrFold<tensor::DimOp>(getLoc(), operands.front(), 0)};
|
2020-01-08 18:48:38 -08:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2021-06-16 22:12:16 -07:00
|
|
|
LogicalResult OpWithResultShapeInterfaceOp::reifyReturnTypeShapes(
|
|
|
|
OpBuilder &builder, ValueRange operands,
|
|
|
|
llvm::SmallVectorImpl<Value> &shapes) {
|
|
|
|
Location loc = getLoc();
|
|
|
|
shapes.reserve(operands.size());
|
|
|
|
for (Value operand : llvm::reverse(operands)) {
|
2023-05-08 16:33:54 +02:00
|
|
|
auto rank = cast<RankedTensorType>(operand.getType()).getRank();
|
2021-12-16 14:42:27 +01:00
|
|
|
auto currShape = llvm::to_vector<4>(
|
|
|
|
llvm::map_range(llvm::seq<int64_t>(0, rank), [&](int64_t dim) -> Value {
|
2021-07-01 09:58:48 +09:00
|
|
|
return builder.createOrFold<tensor::DimOp>(loc, operand, dim);
|
2021-06-16 22:12:16 -07:00
|
|
|
}));
|
|
|
|
shapes.push_back(builder.create<tensor::FromElementsOp>(
|
2021-12-16 14:42:27 +01:00
|
|
|
getLoc(), RankedTensorType::get({rank}, builder.getIndexType()),
|
|
|
|
currShape));
|
2021-06-16 22:12:16 -07:00
|
|
|
}
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2021-07-19 14:35:20 -07:00
|
|
|
LogicalResult OpWithResultShapePerDimInterfaceOp::reifyResultShapes(
|
|
|
|
OpBuilder &builder, ReifiedRankedShapedTypeDims &shapes) {
|
2021-06-16 22:12:16 -07:00
|
|
|
Location loc = getLoc();
|
|
|
|
shapes.reserve(getNumOperands());
|
|
|
|
for (Value operand : llvm::reverse(getOperands())) {
|
2023-05-08 16:33:54 +02:00
|
|
|
auto tensorType = cast<RankedTensorType>(operand.getType());
|
2021-06-16 22:12:16 -07:00
|
|
|
auto currShape = llvm::to_vector<4>(llvm::map_range(
|
2023-03-10 11:25:15 +01:00
|
|
|
llvm::seq<int64_t>(0, tensorType.getRank()),
|
2023-03-03 17:56:39 +01:00
|
|
|
[&](int64_t dim) -> OpFoldResult {
|
2023-03-10 11:25:15 +01:00
|
|
|
return tensorType.isDynamicDim(dim)
|
|
|
|
? static_cast<OpFoldResult>(
|
|
|
|
builder.createOrFold<tensor::DimOp>(loc, operand,
|
|
|
|
dim))
|
|
|
|
: static_cast<OpFoldResult>(
|
|
|
|
builder.getIndexAttr(tensorType.getDimSize(dim)));
|
2021-06-16 22:12:16 -07:00
|
|
|
}));
|
|
|
|
shapes.emplace_back(std::move(currShape));
|
2021-03-29 10:57:23 -07:00
|
|
|
}
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
[mlir][SideEffects] Define a set of interfaces and traits for defining side effects
This revision introduces the infrastructure for defining side-effects and attaching them to operations. This infrastructure allows for defining different types of side effects, that don't interact with each other, but use the same internal mechanisms. At the base of this is an interface that allows operations to specify the different effect instances that are exhibited by a specific operation instance. An effect instance is comprised of the following:
* Effect: The specific effect being applied.
For memory related effects this may be reading from memory, storing to memory, etc.
* Value: A specific value, either operand/result/region argument, the effect pertains to.
* Resource: This is a global entity that represents the domain within which the effect is being applied.
MLIR serves many different abstractions, which cover many different domains. Simple effects are may have very different context, for example writing to an in-memory buffer vs a database. This revision defines uses this infrastructure to define a set of initial MemoryEffects. The are effects that generally correspond to memory of some kind; Allocate, Free, Read, Write.
This set of memory effects will be used in follow revisions to generalize various parts of the compiler, and make others more powerful(e.g. DCE).
This infrastructure was originally proposed here:
https://groups.google.com/a/tensorflow.org/g/mlir/c/v2mNl4vFCUM
Differential Revision: https://reviews.llvm.org/D74439
2020-03-06 13:53:16 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Test SideEffect interfaces
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// A test resource for side effects.
|
|
|
|
struct TestResource : public SideEffects::Resource::Base<TestResource> {
|
2022-03-30 17:00:37 -07:00
|
|
|
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestResource)
|
|
|
|
|
[mlir][SideEffects] Define a set of interfaces and traits for defining side effects
This revision introduces the infrastructure for defining side-effects and attaching them to operations. This infrastructure allows for defining different types of side effects, that don't interact with each other, but use the same internal mechanisms. At the base of this is an interface that allows operations to specify the different effect instances that are exhibited by a specific operation instance. An effect instance is comprised of the following:
* Effect: The specific effect being applied.
For memory related effects this may be reading from memory, storing to memory, etc.
* Value: A specific value, either operand/result/region argument, the effect pertains to.
* Resource: This is a global entity that represents the domain within which the effect is being applied.
MLIR serves many different abstractions, which cover many different domains. Simple effects are may have very different context, for example writing to an in-memory buffer vs a database. This revision defines uses this infrastructure to define a set of initial MemoryEffects. The are effects that generally correspond to memory of some kind; Allocate, Free, Read, Write.
This set of memory effects will be used in follow revisions to generalize various parts of the compiler, and make others more powerful(e.g. DCE).
This infrastructure was originally proposed here:
https://groups.google.com/a/tensorflow.org/g/mlir/c/v2mNl4vFCUM
Differential Revision: https://reviews.llvm.org/D74439
2020-03-06 13:53:16 -08:00
|
|
|
StringRef getName() final { return "<Test>"; }
|
|
|
|
};
|
2021-12-07 18:27:58 +00:00
|
|
|
} // namespace
|
[mlir][SideEffects] Define a set of interfaces and traits for defining side effects
This revision introduces the infrastructure for defining side-effects and attaching them to operations. This infrastructure allows for defining different types of side effects, that don't interact with each other, but use the same internal mechanisms. At the base of this is an interface that allows operations to specify the different effect instances that are exhibited by a specific operation instance. An effect instance is comprised of the following:
* Effect: The specific effect being applied.
For memory related effects this may be reading from memory, storing to memory, etc.
* Value: A specific value, either operand/result/region argument, the effect pertains to.
* Resource: This is a global entity that represents the domain within which the effect is being applied.
MLIR serves many different abstractions, which cover many different domains. Simple effects are may have very different context, for example writing to an in-memory buffer vs a database. This revision defines uses this infrastructure to define a set of initial MemoryEffects. The are effects that generally correspond to memory of some kind; Allocate, Free, Read, Write.
This set of memory effects will be used in follow revisions to generalize various parts of the compiler, and make others more powerful(e.g. DCE).
This infrastructure was originally proposed here:
https://groups.google.com/a/tensorflow.org/g/mlir/c/v2mNl4vFCUM
Differential Revision: https://reviews.llvm.org/D74439
2020-03-06 13:53:16 -08:00
|
|
|
|
2021-03-24 08:25:25 +00:00
|
|
|
static void testSideEffectOpGetEffect(
|
|
|
|
Operation *op,
|
|
|
|
SmallVectorImpl<SideEffects::EffectInstance<TestEffects::Effect>>
|
|
|
|
&effects) {
|
|
|
|
auto effectsAttr = op->getAttrOfType<AffineMapAttr>("effect_parameter");
|
|
|
|
if (!effectsAttr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
effects.emplace_back(TestEffects::Concrete::get(), effectsAttr);
|
|
|
|
}
|
|
|
|
|
[mlir][SideEffects] Define a set of interfaces and traits for defining side effects
This revision introduces the infrastructure for defining side-effects and attaching them to operations. This infrastructure allows for defining different types of side effects, that don't interact with each other, but use the same internal mechanisms. At the base of this is an interface that allows operations to specify the different effect instances that are exhibited by a specific operation instance. An effect instance is comprised of the following:
* Effect: The specific effect being applied.
For memory related effects this may be reading from memory, storing to memory, etc.
* Value: A specific value, either operand/result/region argument, the effect pertains to.
* Resource: This is a global entity that represents the domain within which the effect is being applied.
MLIR serves many different abstractions, which cover many different domains. Simple effects are may have very different context, for example writing to an in-memory buffer vs a database. This revision defines uses this infrastructure to define a set of initial MemoryEffects. The are effects that generally correspond to memory of some kind; Allocate, Free, Read, Write.
This set of memory effects will be used in follow revisions to generalize various parts of the compiler, and make others more powerful(e.g. DCE).
This infrastructure was originally proposed here:
https://groups.google.com/a/tensorflow.org/g/mlir/c/v2mNl4vFCUM
Differential Revision: https://reviews.llvm.org/D74439
2020-03-06 13:53:16 -08:00
|
|
|
void SideEffectOp::getEffects(
|
|
|
|
SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
|
|
|
|
// Check for an effects attribute on the op instance.
|
2020-12-09 11:50:18 +01:00
|
|
|
ArrayAttr effectsAttr = (*this)->getAttrOfType<ArrayAttr>("effects");
|
[mlir][SideEffects] Define a set of interfaces and traits for defining side effects
This revision introduces the infrastructure for defining side-effects and attaching them to operations. This infrastructure allows for defining different types of side effects, that don't interact with each other, but use the same internal mechanisms. At the base of this is an interface that allows operations to specify the different effect instances that are exhibited by a specific operation instance. An effect instance is comprised of the following:
* Effect: The specific effect being applied.
For memory related effects this may be reading from memory, storing to memory, etc.
* Value: A specific value, either operand/result/region argument, the effect pertains to.
* Resource: This is a global entity that represents the domain within which the effect is being applied.
MLIR serves many different abstractions, which cover many different domains. Simple effects are may have very different context, for example writing to an in-memory buffer vs a database. This revision defines uses this infrastructure to define a set of initial MemoryEffects. The are effects that generally correspond to memory of some kind; Allocate, Free, Read, Write.
This set of memory effects will be used in follow revisions to generalize various parts of the compiler, and make others more powerful(e.g. DCE).
This infrastructure was originally proposed here:
https://groups.google.com/a/tensorflow.org/g/mlir/c/v2mNl4vFCUM
Differential Revision: https://reviews.llvm.org/D74439
2020-03-06 13:53:16 -08:00
|
|
|
if (!effectsAttr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If there is one, it is an array of dictionary attributes that hold
|
|
|
|
// information on the effects of this operation.
|
|
|
|
for (Attribute element : effectsAttr) {
|
2023-05-08 16:33:54 +02:00
|
|
|
DictionaryAttr effectElement = cast<DictionaryAttr>(element);
|
[mlir][SideEffects] Define a set of interfaces and traits for defining side effects
This revision introduces the infrastructure for defining side-effects and attaching them to operations. This infrastructure allows for defining different types of side effects, that don't interact with each other, but use the same internal mechanisms. At the base of this is an interface that allows operations to specify the different effect instances that are exhibited by a specific operation instance. An effect instance is comprised of the following:
* Effect: The specific effect being applied.
For memory related effects this may be reading from memory, storing to memory, etc.
* Value: A specific value, either operand/result/region argument, the effect pertains to.
* Resource: This is a global entity that represents the domain within which the effect is being applied.
MLIR serves many different abstractions, which cover many different domains. Simple effects are may have very different context, for example writing to an in-memory buffer vs a database. This revision defines uses this infrastructure to define a set of initial MemoryEffects. The are effects that generally correspond to memory of some kind; Allocate, Free, Read, Write.
This set of memory effects will be used in follow revisions to generalize various parts of the compiler, and make others more powerful(e.g. DCE).
This infrastructure was originally proposed here:
https://groups.google.com/a/tensorflow.org/g/mlir/c/v2mNl4vFCUM
Differential Revision: https://reviews.llvm.org/D74439
2020-03-06 13:53:16 -08:00
|
|
|
|
|
|
|
// Get the specific memory effect.
|
|
|
|
MemoryEffects::Effect *effect =
|
2020-10-07 16:17:35 +02:00
|
|
|
StringSwitch<MemoryEffects::Effect *>(
|
2023-05-08 16:33:54 +02:00
|
|
|
cast<StringAttr>(effectElement.get("effect")).getValue())
|
[mlir][SideEffects] Define a set of interfaces and traits for defining side effects
This revision introduces the infrastructure for defining side-effects and attaching them to operations. This infrastructure allows for defining different types of side effects, that don't interact with each other, but use the same internal mechanisms. At the base of this is an interface that allows operations to specify the different effect instances that are exhibited by a specific operation instance. An effect instance is comprised of the following:
* Effect: The specific effect being applied.
For memory related effects this may be reading from memory, storing to memory, etc.
* Value: A specific value, either operand/result/region argument, the effect pertains to.
* Resource: This is a global entity that represents the domain within which the effect is being applied.
MLIR serves many different abstractions, which cover many different domains. Simple effects are may have very different context, for example writing to an in-memory buffer vs a database. This revision defines uses this infrastructure to define a set of initial MemoryEffects. The are effects that generally correspond to memory of some kind; Allocate, Free, Read, Write.
This set of memory effects will be used in follow revisions to generalize various parts of the compiler, and make others more powerful(e.g. DCE).
This infrastructure was originally proposed here:
https://groups.google.com/a/tensorflow.org/g/mlir/c/v2mNl4vFCUM
Differential Revision: https://reviews.llvm.org/D74439
2020-03-06 13:53:16 -08:00
|
|
|
.Case("allocate", MemoryEffects::Allocate::get())
|
|
|
|
.Case("free", MemoryEffects::Free::get())
|
|
|
|
.Case("read", MemoryEffects::Read::get())
|
|
|
|
.Case("write", MemoryEffects::Write::get());
|
|
|
|
|
|
|
|
// Check for a non-default resource to use.
|
|
|
|
SideEffects::Resource *resource = SideEffects::DefaultResource::get();
|
|
|
|
if (effectElement.get("test_resource"))
|
|
|
|
resource = TestResource::get();
|
|
|
|
|
2020-11-18 18:31:40 -08:00
|
|
|
// Check for a result to affect.
|
|
|
|
if (effectElement.get("on_result"))
|
|
|
|
effects.emplace_back(effect, getResult(), resource);
|
|
|
|
else if (Attribute ref = effectElement.get("on_reference"))
|
2023-05-08 16:33:54 +02:00
|
|
|
effects.emplace_back(effect, cast<SymbolRefAttr>(ref), resource);
|
2020-11-18 18:31:40 -08:00
|
|
|
else
|
|
|
|
effects.emplace_back(effect, resource);
|
[mlir][SideEffects] Define a set of interfaces and traits for defining side effects
This revision introduces the infrastructure for defining side-effects and attaching them to operations. This infrastructure allows for defining different types of side effects, that don't interact with each other, but use the same internal mechanisms. At the base of this is an interface that allows operations to specify the different effect instances that are exhibited by a specific operation instance. An effect instance is comprised of the following:
* Effect: The specific effect being applied.
For memory related effects this may be reading from memory, storing to memory, etc.
* Value: A specific value, either operand/result/region argument, the effect pertains to.
* Resource: This is a global entity that represents the domain within which the effect is being applied.
MLIR serves many different abstractions, which cover many different domains. Simple effects are may have very different context, for example writing to an in-memory buffer vs a database. This revision defines uses this infrastructure to define a set of initial MemoryEffects. The are effects that generally correspond to memory of some kind; Allocate, Free, Read, Write.
This set of memory effects will be used in follow revisions to generalize various parts of the compiler, and make others more powerful(e.g. DCE).
This infrastructure was originally proposed here:
https://groups.google.com/a/tensorflow.org/g/mlir/c/v2mNl4vFCUM
Differential Revision: https://reviews.llvm.org/D74439
2020-03-06 13:53:16 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-15 15:25:01 +01:00
|
|
|
void SideEffectOp::getEffects(
|
|
|
|
SmallVectorImpl<TestEffects::EffectInstance> &effects) {
|
2021-03-24 08:25:25 +00:00
|
|
|
testSideEffectOpGetEffect(getOperation(), effects);
|
2020-11-15 15:25:01 +01: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-15 17:13:59 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// StringAttrPrettyNameOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
// This op has fancy handling of its SSA result name.
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult StringAttrPrettyNameOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &result) {
|
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-15 17:13:59 -07:00
|
|
|
// Add the result types.
|
|
|
|
for (size_t i = 0, e = parser.getNumResults(); i != e; ++i)
|
|
|
|
result.addTypes(parser.getBuilder().getIntegerType(32));
|
|
|
|
|
|
|
|
if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// If the attribute dictionary contains no 'names' attribute, infer it from
|
|
|
|
// the SSA name (if specified).
|
|
|
|
bool hadNames = llvm::any_of(result.attributes, [](NamedAttribute attr) {
|
2021-11-18 05:23:32 +00:00
|
|
|
return attr.getName() == "names";
|
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-15 17:13:59 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
// If there was no name specified, check to see if there was a useful name
|
|
|
|
// specified in the asm file.
|
|
|
|
if (hadNames || parser.getNumResults() == 0)
|
|
|
|
return success();
|
|
|
|
|
|
|
|
SmallVector<StringRef, 4> names;
|
|
|
|
auto *context = result.getContext();
|
|
|
|
|
|
|
|
for (size_t i = 0, e = parser.getNumResults(); i != e; ++i) {
|
|
|
|
auto resultName = parser.getResultName(i);
|
|
|
|
StringRef nameStr;
|
|
|
|
if (!resultName.first.empty() && !isdigit(resultName.first[0]))
|
|
|
|
nameStr = resultName.first;
|
|
|
|
|
|
|
|
names.push_back(nameStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto namesAttr = parser.getBuilder().getStrArrayAttr(names);
|
2021-11-16 17:21:15 +00:00
|
|
|
result.attributes.push_back({StringAttr::get(context, "names"), namesAttr});
|
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-15 17:13:59 -07:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
void StringAttrPrettyNameOp::print(OpAsmPrinter &p) {
|
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-15 17:13:59 -07:00
|
|
|
// Note that we only need to print the "name" attribute if the asmprinter
|
|
|
|
// result name disagrees with it. This can happen in strange cases, e.g.
|
|
|
|
// when there are conflicts.
|
2022-02-04 20:47:01 -08:00
|
|
|
bool namesDisagree = getNames().size() != getNumResults();
|
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-15 17:13:59 -07:00
|
|
|
|
|
|
|
SmallString<32> resultNameStr;
|
2022-02-04 20:47:01 -08:00
|
|
|
for (size_t i = 0, e = getNumResults(); i != e && !namesDisagree; ++i) {
|
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-15 17:13:59 -07:00
|
|
|
resultNameStr.clear();
|
|
|
|
llvm::raw_svector_ostream tmpStream(resultNameStr);
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printOperand(getResult(i), tmpStream);
|
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-15 17:13:59 -07:00
|
|
|
|
2023-05-08 16:33:54 +02:00
|
|
|
auto expectedName = dyn_cast<StringAttr>(getNames()[i]);
|
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-15 17:13:59 -07:00
|
|
|
if (!expectedName ||
|
|
|
|
tmpStream.str().drop_front() != expectedName.getValue()) {
|
|
|
|
namesDisagree = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (namesDisagree)
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printOptionalAttrDictWithKeyword((*this)->getAttrs());
|
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-15 17:13:59 -07:00
|
|
|
else
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printOptionalAttrDictWithKeyword((*this)->getAttrs(), {"names"});
|
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-15 17:13:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// We set the SSA name in the asm syntax to the contents of the name
|
|
|
|
// attribute.
|
|
|
|
void StringAttrPrettyNameOp::getAsmResultNames(
|
|
|
|
function_ref<void(Value, StringRef)> setNameFn) {
|
|
|
|
|
2021-10-20 07:08:36 -07:00
|
|
|
auto value = getNames();
|
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-15 17:13:59 -07:00
|
|
|
for (size_t i = 0, e = value.size(); i != e; ++i)
|
2023-05-08 16:33:54 +02:00
|
|
|
if (auto str = dyn_cast<StringAttr>(value[i]))
|
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-15 17:13:59 -07:00
|
|
|
if (!str.getValue().empty())
|
|
|
|
setNameFn(getResult(i), str.getValue());
|
|
|
|
}
|
|
|
|
|
2022-04-22 18:52:54 +00:00
|
|
|
void CustomResultsNameOp::getAsmResultNames(
|
|
|
|
function_ref<void(Value, StringRef)> setNameFn) {
|
|
|
|
ArrayAttr value = getNames();
|
|
|
|
for (size_t i = 0, e = value.size(); i != e; ++i)
|
2023-05-08 16:33:54 +02:00
|
|
|
if (auto str = dyn_cast<StringAttr>(value[i]))
|
2022-04-22 18:52:54 +00:00
|
|
|
if (!str.getValue().empty())
|
|
|
|
setNameFn(getResult(i), str.getValue());
|
|
|
|
}
|
|
|
|
|
2022-02-01 15:01:30 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ResultTypeWithTraitOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
LogicalResult ResultTypeWithTraitOp::verify() {
|
|
|
|
if ((*this)->getResultTypes()[0].hasTrait<TypeTrait::TestTypeTrait>())
|
|
|
|
return success();
|
|
|
|
return emitError("result type should have trait 'TestTypeTrait'");
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AttrWithTraitOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
LogicalResult AttrWithTraitOp::verify() {
|
|
|
|
if (getAttr().hasTrait<AttributeTrait::TestAttrTrait>())
|
|
|
|
return success();
|
|
|
|
return emitError("'attr' attribute should have trait 'TestAttrTrait'");
|
|
|
|
}
|
|
|
|
|
2020-06-30 11:58:45 +02:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// RegionIfOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
void RegionIfOp::print(OpAsmPrinter &p) {
|
2021-08-28 03:03:15 +00:00
|
|
|
p << " ";
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printOperands(getOperands());
|
|
|
|
p << ": " << getOperandTypes();
|
|
|
|
p.printArrowTypeList(getResultTypes());
|
2022-01-24 23:00:39 +00:00
|
|
|
p << " then ";
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printRegion(getThenRegion(),
|
2020-06-30 11:58:45 +02:00
|
|
|
/*printEntryBlockArgs=*/true,
|
|
|
|
/*printBlockTerminators=*/true);
|
2022-01-24 23:00:39 +00:00
|
|
|
p << " else ";
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printRegion(getElseRegion(),
|
2020-06-30 11:58:45 +02:00
|
|
|
/*printEntryBlockArgs=*/true,
|
|
|
|
/*printBlockTerminators=*/true);
|
2022-01-24 23:00:39 +00:00
|
|
|
p << " join ";
|
2022-02-04 20:47:01 -08:00
|
|
|
p.printRegion(getJoinRegion(),
|
2020-06-30 11:58:45 +02:00
|
|
|
/*printEntryBlockArgs=*/true,
|
|
|
|
/*printBlockTerminators=*/true);
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult RegionIfOp::parse(OpAsmParser &parser, OperationState &result) {
|
2022-03-21 21:42:13 +01:00
|
|
|
SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
|
2020-06-30 11:58:45 +02:00
|
|
|
SmallVector<Type, 2> operandTypes;
|
|
|
|
|
|
|
|
result.regions.reserve(3);
|
|
|
|
Region *thenRegion = result.addRegion();
|
|
|
|
Region *elseRegion = result.addRegion();
|
|
|
|
Region *joinRegion = result.addRegion();
|
|
|
|
|
|
|
|
// Parse operand, type and arrow type lists.
|
|
|
|
if (parser.parseOperandList(operandInfos) ||
|
|
|
|
parser.parseColonTypeList(operandTypes) ||
|
|
|
|
parser.parseArrowTypeList(result.types))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Parse all attached regions.
|
|
|
|
if (parser.parseKeyword("then") || parser.parseRegion(*thenRegion, {}, {}) ||
|
|
|
|
parser.parseKeyword("else") || parser.parseRegion(*elseRegion, {}, {}) ||
|
|
|
|
parser.parseKeyword("join") || parser.parseRegion(*joinRegion, {}, {}))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
return parser.resolveOperands(operandInfos, operandTypes,
|
|
|
|
parser.getCurrentLocation(), result.operands);
|
|
|
|
}
|
|
|
|
|
2022-12-14 11:39:19 +01:00
|
|
|
OperandRange
|
|
|
|
RegionIfOp::getSuccessorEntryOperands(std::optional<unsigned> index) {
|
2022-06-13 22:02:02 +00:00
|
|
|
assert(index && *index < 2 && "invalid region index");
|
2020-06-30 11:58:45 +02:00
|
|
|
return getOperands();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RegionIfOp::getSuccessorRegions(
|
2022-12-14 11:39:19 +01:00
|
|
|
std::optional<unsigned> index, ArrayRef<Attribute> operands,
|
2020-06-30 11:58:45 +02:00
|
|
|
SmallVectorImpl<RegionSuccessor> ®ions) {
|
|
|
|
// We always branch to the join region.
|
2022-07-13 00:57:02 -07:00
|
|
|
if (index.has_value()) {
|
2022-07-14 00:19:59 -07:00
|
|
|
if (index.value() < 2)
|
2021-10-20 07:08:36 -07:00
|
|
|
regions.push_back(RegionSuccessor(&getJoinRegion(), getJoinArgs()));
|
2020-06-30 11:58:45 +02:00
|
|
|
else
|
|
|
|
regions.push_back(RegionSuccessor(getResults()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The then and else regions are the entry regions of this op.
|
2021-10-20 07:08:36 -07:00
|
|
|
regions.push_back(RegionSuccessor(&getThenRegion(), getThenArgs()));
|
|
|
|
regions.push_back(RegionSuccessor(&getElseRegion(), getElseArgs()));
|
2020-06-30 11:58:45 +02:00
|
|
|
}
|
|
|
|
|
2022-01-24 23:00:39 +00:00
|
|
|
void RegionIfOp::getRegionInvocationBounds(
|
|
|
|
ArrayRef<Attribute> operands,
|
|
|
|
SmallVectorImpl<InvocationBounds> &invocationBounds) {
|
|
|
|
// Each region is invoked at most once.
|
|
|
|
invocationBounds.assign(/*NumElts=*/3, /*Elt=*/{0, 1});
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AnyCondOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-12-14 11:39:19 +01:00
|
|
|
void AnyCondOp::getSuccessorRegions(std::optional<unsigned> index,
|
2022-01-24 23:00:39 +00:00
|
|
|
ArrayRef<Attribute> operands,
|
|
|
|
SmallVectorImpl<RegionSuccessor> ®ions) {
|
|
|
|
// The parent op branches into the only region, and the region branches back
|
|
|
|
// to the parent op.
|
2022-06-13 22:02:02 +00:00
|
|
|
if (!index)
|
2022-01-24 23:00:39 +00:00
|
|
|
regions.emplace_back(&getRegion());
|
|
|
|
else
|
|
|
|
regions.emplace_back(getResults());
|
|
|
|
}
|
|
|
|
|
|
|
|
void AnyCondOp::getRegionInvocationBounds(
|
|
|
|
ArrayRef<Attribute> operands,
|
|
|
|
SmallVectorImpl<InvocationBounds> &invocationBounds) {
|
|
|
|
invocationBounds.emplace_back(1, 1);
|
|
|
|
}
|
|
|
|
|
2021-05-28 07:21:41 -04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// SingleNoTerminatorCustomAsmOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
ParseResult SingleNoTerminatorCustomAsmOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &state) {
|
2021-05-28 07:21:41 -04:00
|
|
|
Region *body = state.addRegion();
|
|
|
|
if (parser.parseRegion(*body, /*arguments=*/{}, /*argTypes=*/{}))
|
|
|
|
return failure();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-02-04 20:47:01 -08:00
|
|
|
void SingleNoTerminatorCustomAsmOp::print(OpAsmPrinter &printer) {
|
2021-05-28 07:21:41 -04:00
|
|
|
printer.printRegion(
|
2022-02-04 20:47:01 -08:00
|
|
|
getRegion(), /*printEntryBlockArgs=*/false,
|
2021-05-28 07:21:41 -04:00
|
|
|
// This op has a single block without terminators. But explicitly mark
|
|
|
|
// as not printing block terminators for testing.
|
|
|
|
/*printBlockTerminators=*/false);
|
|
|
|
}
|
|
|
|
|
2022-04-15 04:33:40 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TestVerifiersOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
LogicalResult TestVerifiersOp::verify() {
|
|
|
|
if (!getRegion().hasOneBlock())
|
|
|
|
return emitOpError("`hasOneBlock` trait hasn't been verified");
|
|
|
|
|
|
|
|
Operation *definingOp = getInput().getDefiningOp();
|
|
|
|
if (definingOp && failed(mlir::verify(definingOp)))
|
|
|
|
return emitOpError("operand hasn't been verified");
|
|
|
|
|
2023-07-10 19:15:29 +02:00
|
|
|
// Avoid using `emitRemark(msg)` since that will trigger an infinite verifier
|
|
|
|
// loop.
|
|
|
|
mlir::emitRemark(getLoc(), "success run of verifier");
|
2022-04-15 04:33:40 +00:00
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult TestVerifiersOp::verifyRegions() {
|
|
|
|
if (!getRegion().hasOneBlock())
|
|
|
|
return emitOpError("`hasOneBlock` trait hasn't been verified");
|
|
|
|
|
|
|
|
for (Block &block : getRegion())
|
|
|
|
for (Operation &op : block)
|
|
|
|
if (failed(mlir::verify(&op)))
|
|
|
|
return emitOpError("nested op hasn't been verified");
|
|
|
|
|
2023-07-10 19:15:29 +02:00
|
|
|
// Avoid using `emitRemark(msg)` since that will trigger an infinite verifier
|
|
|
|
// loop.
|
|
|
|
mlir::emitRemark(getLoc(), "success run of region verifier");
|
2022-04-15 04:33:40 +00:00
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-06-02 21:45:52 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Test InferIntRangeInterface
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
void TestWithBoundsOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
|
|
|
|
SetIntRangeFn setResultRanges) {
|
|
|
|
setResultRanges(getResult(), {getUmin(), getUmax(), getSmin(), getSmax()});
|
|
|
|
}
|
|
|
|
|
|
|
|
ParseResult TestWithBoundsRegionOp::parse(OpAsmParser &parser,
|
|
|
|
OperationState &result) {
|
|
|
|
if (parser.parseOptionalAttrDict(result.attributes))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Parse the input argument
|
|
|
|
OpAsmParser::Argument argInfo;
|
|
|
|
argInfo.type = parser.getBuilder().getIndexType();
|
|
|
|
if (failed(parser.parseArgument(argInfo)))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
// Parse the body region, and reuse the operand info as the argument info.
|
|
|
|
Region *body = result.addRegion();
|
|
|
|
return parser.parseRegion(*body, argInfo, /*enableNameShadowing=*/false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestWithBoundsRegionOp::print(OpAsmPrinter &p) {
|
|
|
|
p.printOptionalAttrDict((*this)->getAttrs());
|
|
|
|
p << ' ';
|
|
|
|
p.printRegionArgument(getRegion().getArgument(0), /*argAttrs=*/{},
|
|
|
|
/*omitType=*/true);
|
|
|
|
p << ' ';
|
|
|
|
p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestWithBoundsRegionOp::inferResultRanges(
|
|
|
|
ArrayRef<ConstantIntRanges> argRanges, SetIntRangeFn setResultRanges) {
|
|
|
|
Value arg = getRegion().getArgument(0);
|
|
|
|
setResultRanges(arg, {getUmin(), getUmax(), getSmin(), getSmax()});
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestIncrementOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
|
|
|
|
SetIntRangeFn setResultRanges) {
|
|
|
|
const ConstantIntRanges &range = argRanges[0];
|
|
|
|
APInt one(range.umin().getBitWidth(), 1);
|
|
|
|
setResultRanges(getResult(),
|
|
|
|
{range.umin().uadd_sat(one), range.umax().uadd_sat(one),
|
|
|
|
range.smin().sadd_sat(one), range.smax().sadd_sat(one)});
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestReflectBoundsOp::inferResultRanges(
|
|
|
|
ArrayRef<ConstantIntRanges> argRanges, SetIntRangeFn setResultRanges) {
|
|
|
|
const ConstantIntRanges &range = argRanges[0];
|
|
|
|
MLIRContext *ctx = getContext();
|
|
|
|
Builder b(ctx);
|
|
|
|
setUminAttr(b.getIndexAttr(range.umin().getZExtValue()));
|
|
|
|
setUmaxAttr(b.getIndexAttr(range.umax().getZExtValue()));
|
|
|
|
setSminAttr(b.getIndexAttr(range.smin().getSExtValue()));
|
|
|
|
setSmaxAttr(b.getIndexAttr(range.smax().getSExtValue()));
|
|
|
|
setResultRanges(getResult(), range);
|
|
|
|
}
|
|
|
|
|
2023-02-23 00:06:21 +01:00
|
|
|
OpFoldResult ManualCppOpWithFold::fold(ArrayRef<Attribute> attributes) {
|
|
|
|
// Just a simple fold for testing purposes that reads an operands constant
|
|
|
|
// value and returns it.
|
|
|
|
if (!attributes.empty())
|
|
|
|
return attributes.front();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-02-26 10:46:01 -05:00
|
|
|
static LogicalResult
|
|
|
|
setPropertiesFromAttribute(PropertiesWithCustomPrint &prop, Attribute attr,
|
|
|
|
InFlightDiagnostic *diagnostic) {
|
|
|
|
DictionaryAttr dict = dyn_cast<DictionaryAttr>(attr);
|
|
|
|
if (!dict) {
|
|
|
|
if (diagnostic)
|
|
|
|
*diagnostic << "expected DictionaryAttr to set TestProperties";
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
auto label = dict.getAs<mlir::StringAttr>("label");
|
|
|
|
if (!label) {
|
|
|
|
if (diagnostic)
|
|
|
|
*diagnostic << "expected StringAttr for key `label`";
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
auto valueAttr = dict.getAs<IntegerAttr>("value");
|
|
|
|
if (!valueAttr) {
|
|
|
|
if (diagnostic)
|
|
|
|
*diagnostic << "expected IntegerAttr for key `value`";
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
|
|
|
|
prop.label = std::make_shared<std::string>(label.getValue());
|
|
|
|
prop.value = valueAttr.getValue().getSExtValue();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
static DictionaryAttr
|
|
|
|
getPropertiesAsAttribute(MLIRContext *ctx,
|
|
|
|
const PropertiesWithCustomPrint &prop) {
|
|
|
|
SmallVector<NamedAttribute> attrs;
|
|
|
|
Builder b{ctx};
|
|
|
|
attrs.push_back(b.getNamedAttr("label", b.getStringAttr(*prop.label)));
|
|
|
|
attrs.push_back(b.getNamedAttr("value", b.getI32IntegerAttr(prop.value)));
|
|
|
|
return b.getDictionaryAttr(attrs);
|
|
|
|
}
|
|
|
|
static llvm::hash_code computeHash(const PropertiesWithCustomPrint &prop) {
|
|
|
|
return llvm::hash_combine(prop.value, StringRef(*prop.label));
|
|
|
|
}
|
|
|
|
static void customPrintProperties(OpAsmPrinter &p,
|
|
|
|
const PropertiesWithCustomPrint &prop) {
|
|
|
|
p.printKeywordOrString(*prop.label);
|
|
|
|
p << " is " << prop.value;
|
|
|
|
}
|
|
|
|
static ParseResult customParseProperties(OpAsmParser &parser,
|
|
|
|
PropertiesWithCustomPrint &prop) {
|
|
|
|
std::string label;
|
|
|
|
if (parser.parseKeywordOrString(&label) || parser.parseKeyword("is") ||
|
|
|
|
parser.parseInteger(prop.value))
|
|
|
|
return failure();
|
|
|
|
prop.label = std::make_shared<std::string>(std::move(label));
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2023-07-12 13:30:08 +02:00
|
|
|
static bool parseUsingPropertyInCustom(OpAsmParser &parser, int64_t value[3]) {
|
|
|
|
return parser.parseLSquare() || parser.parseInteger(value[0]) ||
|
|
|
|
parser.parseComma() || parser.parseInteger(value[1]) ||
|
|
|
|
parser.parseComma() || parser.parseInteger(value[2]) ||
|
|
|
|
parser.parseRSquare();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void printUsingPropertyInCustom(OpAsmPrinter &printer, Operation *op,
|
|
|
|
ArrayRef<int64_t> value) {
|
|
|
|
printer << '[' << value << ']';
|
|
|
|
}
|
|
|
|
|
2019-11-25 11:29:21 -08:00
|
|
|
#include "TestOpEnums.cpp.inc"
|
2020-11-15 15:25:01 +01:00
|
|
|
#include "TestOpInterfaces.cpp.inc"
|
2020-06-30 15:42:52 -07:00
|
|
|
#include "TestTypeInterfaces.cpp.inc"
|
2019-11-25 11:29:21 -08:00
|
|
|
|
2019-05-23 15:11:19 -07:00
|
|
|
#define GET_OP_CLASSES
|
|
|
|
#include "TestOps.cpp.inc"
|