This attributes is intended cover the current set of use cases that abuse DenseElementsAttr, e.g. when the data is large. Using resources for large data is one of the major reasons why they were added; e.g. they can be deallocated mid-compilation, they support a wide variety of data origins (e.g, heap allocated, mmap'd, etc.), they can support mutation, etc. I considered at length not having a builtin variant of this, and instead having multiple versions of this attribute for dialects that are interested, but they all boiled down to the exact same attribute definition. Given the generality of this attribute, it feels more aligned to keep it next to DenseArrayAttr (given that DenseArrayAttr covers the "small" case, and DenseResourcesElementsAttr covers the "large" case). The underlying infra used to build this attribute is general, and having a builtin attribute doesn't preclude users from defining their own when it makes sense (they can even share a blob manager with the builtin dialect to avoid data duplication). Differential Revision: https://reviews.llvm.org/D130022
230 lines
8.1 KiB
C++
230 lines
8.1 KiB
C++
//===- BuiltinDialect.cpp - MLIR Builtin Dialect --------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains the Builtin dialect that contains all of the attributes,
|
|
// operations, and types that are necessary for the validity of the IR.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/IR/BuiltinDialect.h"
|
|
#include "mlir/IR/BlockAndValueMapping.h"
|
|
#include "mlir/IR/Builders.h"
|
|
#include "mlir/IR/BuiltinOps.h"
|
|
#include "mlir/IR/BuiltinTypes.h"
|
|
#include "mlir/IR/DialectResourceBlobManager.h"
|
|
#include "mlir/IR/OpImplementation.h"
|
|
#include "mlir/IR/PatternMatch.h"
|
|
#include "mlir/IR/TypeRange.h"
|
|
|
|
using namespace mlir;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// TableGen'erated dialect
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/IR/BuiltinDialect.cpp.inc"
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// BuiltinBlobManagerInterface
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
using BuiltinBlobManagerInterface =
|
|
ResourceBlobManagerDialectInterfaceBase<DenseResourceElementsHandle>;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// BuiltinOpAsmDialectInterface
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
struct BuiltinOpAsmDialectInterface : public OpAsmDialectInterface {
|
|
BuiltinOpAsmDialectInterface(Dialect *dialect,
|
|
BuiltinBlobManagerInterface &mgr)
|
|
: OpAsmDialectInterface(dialect), blobManager(mgr) {}
|
|
|
|
AliasResult getAlias(Attribute attr, raw_ostream &os) const override {
|
|
if (attr.isa<AffineMapAttr>()) {
|
|
os << "map";
|
|
return AliasResult::OverridableAlias;
|
|
}
|
|
if (attr.isa<IntegerSetAttr>()) {
|
|
os << "set";
|
|
return AliasResult::OverridableAlias;
|
|
}
|
|
if (attr.isa<LocationAttr>()) {
|
|
os << "loc";
|
|
return AliasResult::OverridableAlias;
|
|
}
|
|
return AliasResult::NoAlias;
|
|
}
|
|
|
|
AliasResult getAlias(Type type, raw_ostream &os) const final {
|
|
if (auto tupleType = type.dyn_cast<TupleType>()) {
|
|
if (tupleType.size() > 16) {
|
|
os << "tuple";
|
|
return AliasResult::OverridableAlias;
|
|
}
|
|
}
|
|
return AliasResult::NoAlias;
|
|
}
|
|
|
|
//===------------------------------------------------------------------===//
|
|
// Resources
|
|
//===------------------------------------------------------------------===//
|
|
|
|
std::string
|
|
getResourceKey(const AsmDialectResourceHandle &handle) const override {
|
|
return cast<DenseResourceElementsHandle>(handle).getKey().str();
|
|
}
|
|
FailureOr<AsmDialectResourceHandle>
|
|
declareResource(StringRef key) const final {
|
|
return blobManager.insert(key);
|
|
}
|
|
LogicalResult parseResource(AsmParsedResourceEntry &entry) const final {
|
|
FailureOr<AsmResourceBlob> blob = entry.parseAsBlob();
|
|
if (failed(blob))
|
|
return failure();
|
|
|
|
// Update the blob for this entry.
|
|
blobManager.update(entry.getKey(), std::move(*blob));
|
|
return success();
|
|
}
|
|
void
|
|
buildResources(Operation *op,
|
|
const SetVector<AsmDialectResourceHandle> &referencedResources,
|
|
AsmResourceBuilder &provider) const final {
|
|
blobManager.buildResources(provider, referencedResources.getArrayRef());
|
|
}
|
|
|
|
private:
|
|
/// The blob manager for the dialect.
|
|
BuiltinBlobManagerInterface &blobManager;
|
|
};
|
|
} // namespace
|
|
|
|
void BuiltinDialect::initialize() {
|
|
registerTypes();
|
|
registerAttributes();
|
|
registerLocationAttributes();
|
|
addOperations<
|
|
#define GET_OP_LIST
|
|
#include "mlir/IR/BuiltinOps.cpp.inc"
|
|
>();
|
|
|
|
auto &blobInterface = addInterface<BuiltinBlobManagerInterface>();
|
|
addInterface<BuiltinOpAsmDialectInterface>(blobInterface);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ModuleOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void ModuleOp::build(OpBuilder &builder, OperationState &state,
|
|
Optional<StringRef> name) {
|
|
state.addRegion()->emplaceBlock();
|
|
if (name) {
|
|
state.attributes.push_back(builder.getNamedAttr(
|
|
mlir::SymbolTable::getSymbolAttrName(), builder.getStringAttr(*name)));
|
|
}
|
|
}
|
|
|
|
/// Construct a module from the given context.
|
|
ModuleOp ModuleOp::create(Location loc, Optional<StringRef> name) {
|
|
OpBuilder builder(loc->getContext());
|
|
return builder.create<ModuleOp>(loc, name);
|
|
}
|
|
|
|
DataLayoutSpecInterface ModuleOp::getDataLayoutSpec() {
|
|
// Take the first and only (if present) attribute that implements the
|
|
// interface. This needs a linear search, but is called only once per data
|
|
// layout object construction that is used for repeated queries.
|
|
for (NamedAttribute attr : getOperation()->getAttrs())
|
|
if (auto spec = attr.getValue().dyn_cast<DataLayoutSpecInterface>())
|
|
return spec;
|
|
return {};
|
|
}
|
|
|
|
LogicalResult ModuleOp::verify() {
|
|
// Check that none of the attributes are non-dialect attributes, except for
|
|
// the symbol related attributes.
|
|
for (auto attr : (*this)->getAttrs()) {
|
|
if (!attr.getName().strref().contains('.') &&
|
|
!llvm::is_contained(
|
|
ArrayRef<StringRef>{mlir::SymbolTable::getSymbolAttrName(),
|
|
mlir::SymbolTable::getVisibilityAttrName()},
|
|
attr.getName().strref()))
|
|
return emitOpError() << "can only contain attributes with "
|
|
"dialect-prefixed names, found: '"
|
|
<< attr.getName().getValue() << "'";
|
|
}
|
|
|
|
// Check that there is at most one data layout spec attribute.
|
|
StringRef layoutSpecAttrName;
|
|
DataLayoutSpecInterface layoutSpec;
|
|
for (const NamedAttribute &na : (*this)->getAttrs()) {
|
|
if (auto spec = na.getValue().dyn_cast<DataLayoutSpecInterface>()) {
|
|
if (layoutSpec) {
|
|
InFlightDiagnostic diag =
|
|
emitOpError() << "expects at most one data layout attribute";
|
|
diag.attachNote() << "'" << layoutSpecAttrName
|
|
<< "' is a data layout attribute";
|
|
diag.attachNote() << "'" << na.getName().getValue()
|
|
<< "' is a data layout attribute";
|
|
}
|
|
layoutSpecAttrName = na.getName().strref();
|
|
layoutSpec = spec;
|
|
}
|
|
}
|
|
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// UnrealizedConversionCastOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult
|
|
UnrealizedConversionCastOp::fold(ArrayRef<Attribute> attrOperands,
|
|
SmallVectorImpl<OpFoldResult> &foldResults) {
|
|
OperandRange operands = getInputs();
|
|
ResultRange results = getOutputs();
|
|
|
|
if (operands.getType() == results.getType()) {
|
|
foldResults.append(operands.begin(), operands.end());
|
|
return success();
|
|
}
|
|
|
|
if (operands.empty())
|
|
return failure();
|
|
|
|
// Check that the input is a cast with results that all feed into this
|
|
// operation, and operand types that directly match the result types of this
|
|
// operation.
|
|
Value firstInput = operands.front();
|
|
auto inputOp = firstInput.getDefiningOp<UnrealizedConversionCastOp>();
|
|
if (!inputOp || inputOp.getResults() != operands ||
|
|
inputOp.getOperandTypes() != results.getTypes())
|
|
return failure();
|
|
|
|
// If everything matches up, we can fold the passthrough.
|
|
foldResults.append(inputOp->operand_begin(), inputOp->operand_end());
|
|
return success();
|
|
}
|
|
|
|
bool UnrealizedConversionCastOp::areCastCompatible(TypeRange inputs,
|
|
TypeRange outputs) {
|
|
// `UnrealizedConversionCastOp` is agnostic of the input/output types.
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// TableGen'd op method definitions
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define GET_OP_CLASSES
|
|
#include "mlir/IR/BuiltinOps.cpp.inc"
|