2020-11-17 00:37:14 -08:00
|
|
|
//===- BuiltinDialect.cpp - MLIR Builtin Dialect --------------------------===//
|
2018-06-21 09:49:33 -07:00
|
|
|
//
|
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
|
2018-06-21 09:49:33 -07:00
|
|
|
//
|
2019-12-23 09:35:36 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2020-11-19 10:43:12 -08:00
|
|
|
//
|
|
|
|
// This file contains the Builtin dialect that contains all of the attributes,
|
|
|
|
// operations, and types that are necessary for the validity of the IR.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
2018-06-21 09:49:33 -07:00
|
|
|
|
2020-11-17 00:37:14 -08:00
|
|
|
#include "mlir/IR/BuiltinDialect.h"
|
2019-01-24 12:25:30 -08:00
|
|
|
#include "mlir/IR/BlockAndValueMapping.h"
|
2019-06-03 12:08:22 -07:00
|
|
|
#include "mlir/IR/Builders.h"
|
2020-11-19 10:43:12 -08:00
|
|
|
#include "mlir/IR/BuiltinOps.h"
|
2020-12-03 17:22:29 -08:00
|
|
|
#include "mlir/IR/BuiltinTypes.h"
|
2019-11-28 11:50:47 -08:00
|
|
|
#include "mlir/IR/FunctionImplementation.h"
|
2020-11-17 00:37:14 -08:00
|
|
|
#include "mlir/IR/OpImplementation.h"
|
2021-01-20 16:17:26 -08:00
|
|
|
#include "mlir/IR/PatternMatch.h"
|
2019-01-24 12:25:30 -08:00
|
|
|
#include "llvm/ADT/MapVector.h"
|
2019-05-13 09:00:22 -07:00
|
|
|
|
2018-06-21 09:49:33 -07:00
|
|
|
using namespace mlir;
|
2019-01-24 12:25:30 -08:00
|
|
|
|
2018-06-28 17:02:32 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2020-11-17 00:37:14 -08:00
|
|
|
// Builtin Dialect
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
struct BuiltinOpAsmDialectInterface : public OpAsmDialectInterface {
|
|
|
|
using OpAsmDialectInterface::OpAsmDialectInterface;
|
|
|
|
|
|
|
|
LogicalResult getAlias(Attribute attr, raw_ostream &os) const override {
|
|
|
|
if (attr.isa<AffineMapAttr>()) {
|
|
|
|
os << "map";
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
if (attr.isa<IntegerSetAttr>()) {
|
|
|
|
os << "set";
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
if (attr.isa<LocationAttr>()) {
|
|
|
|
os << "loc";
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
return failure();
|
|
|
|
}
|
2021-01-29 17:42:23 -08:00
|
|
|
|
|
|
|
LogicalResult getAlias(Type type, raw_ostream &os) const final {
|
|
|
|
if (auto tupleType = type.dyn_cast<TupleType>()) {
|
|
|
|
if (tupleType.size() > 16) {
|
|
|
|
os << "tuple";
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return failure();
|
|
|
|
}
|
2020-11-17 00:37:14 -08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace.
|
|
|
|
|
|
|
|
void BuiltinDialect::initialize() {
|
2021-03-11 11:24:43 -08:00
|
|
|
registerTypes();
|
|
|
|
registerAttributes();
|
|
|
|
registerLocationAttributes();
|
2020-11-17 00:37:14 -08:00
|
|
|
addOperations<
|
|
|
|
#define GET_OP_LIST
|
|
|
|
#include "mlir/IR/BuiltinOps.cpp.inc"
|
|
|
|
>();
|
|
|
|
addInterfaces<BuiltinOpAsmDialectInterface>();
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// FuncOp
|
2018-06-28 17:02:32 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-07-09 16:17:55 -07:00
|
|
|
FuncOp FuncOp::create(Location location, StringRef name, FunctionType type,
|
|
|
|
ArrayRef<NamedAttribute> attrs) {
|
2019-07-03 13:21:24 -07:00
|
|
|
OperationState state(location, "func");
|
2020-04-23 16:02:46 +02:00
|
|
|
OpBuilder builder(location->getContext());
|
|
|
|
FuncOp::build(builder, state, name, type, attrs);
|
2019-12-18 09:28:48 -08:00
|
|
|
return cast<FuncOp>(Operation::create(state));
|
2019-01-02 10:20:00 -08:00
|
|
|
}
|
2019-07-09 16:17:55 -07:00
|
|
|
FuncOp FuncOp::create(Location location, StringRef name, FunctionType type,
|
2021-02-25 22:20:45 +01:00
|
|
|
Operation::dialect_attr_range attrs) {
|
2019-07-03 13:21:24 -07:00
|
|
|
SmallVector<NamedAttribute, 8> attrRef(attrs);
|
|
|
|
return create(location, name, type, llvm::makeArrayRef(attrRef));
|
|
|
|
}
|
2019-07-09 16:17:55 -07:00
|
|
|
FuncOp FuncOp::create(Location location, StringRef name, FunctionType type,
|
|
|
|
ArrayRef<NamedAttribute> attrs,
|
2020-12-17 17:10:12 -08:00
|
|
|
ArrayRef<DictionaryAttr> argAttrs) {
|
2019-07-09 16:17:55 -07:00
|
|
|
FuncOp func = create(location, name, type, attrs);
|
2019-07-03 13:21:24 -07:00
|
|
|
func.setAllArgAttrs(argAttrs);
|
|
|
|
return func;
|
2018-08-13 11:30:36 -07:00
|
|
|
}
|
2019-06-03 12:08:22 -07:00
|
|
|
|
2020-11-17 00:37:14 -08:00
|
|
|
void FuncOp::build(OpBuilder &builder, OperationState &state, StringRef name,
|
2020-07-07 16:15:44 -07:00
|
|
|
FunctionType type, ArrayRef<NamedAttribute> attrs,
|
2020-12-17 17:10:12 -08:00
|
|
|
ArrayRef<DictionaryAttr> argAttrs) {
|
2020-11-17 00:37:14 -08:00
|
|
|
state.addAttribute(SymbolTable::getSymbolAttrName(),
|
|
|
|
builder.getStringAttr(name));
|
|
|
|
state.addAttribute(getTypeAttrName(), TypeAttr::get(type));
|
|
|
|
state.attributes.append(attrs.begin(), attrs.end());
|
|
|
|
state.addRegion();
|
2019-06-03 12:08:22 -07:00
|
|
|
|
2020-07-07 16:15:44 -07:00
|
|
|
if (argAttrs.empty())
|
|
|
|
return;
|
2019-07-03 13:21:24 -07:00
|
|
|
assert(type.getNumInputs() == argAttrs.size());
|
2021-05-07 19:30:25 -07:00
|
|
|
function_like_impl::addArgAndResultAttrs(builder, state, argAttrs,
|
|
|
|
/*resultAttrs=*/llvm::None);
|
2019-07-03 13:21:24 -07:00
|
|
|
}
|
|
|
|
|
2020-11-17 00:37:14 -08:00
|
|
|
static ParseResult parseFuncOp(OpAsmParser &parser, OperationState &result) {
|
2019-08-08 09:41:48 -07:00
|
|
|
auto buildFuncType = [](Builder &builder, ArrayRef<Type> argTypes,
|
2021-05-07 19:30:25 -07:00
|
|
|
ArrayRef<Type> results,
|
|
|
|
function_like_impl::VariadicFlag, std::string &) {
|
2019-08-08 09:41:48 -07:00
|
|
|
return builder.getFunctionType(argTypes, results);
|
|
|
|
};
|
|
|
|
|
2021-05-07 19:30:25 -07:00
|
|
|
return function_like_impl::parseFunctionLikeOp(
|
|
|
|
parser, result, /*allowVariadic=*/false, buildFuncType);
|
2019-06-03 12:08:22 -07:00
|
|
|
}
|
|
|
|
|
2020-11-17 00:37:14 -08:00
|
|
|
static void print(FuncOp op, OpAsmPrinter &p) {
|
|
|
|
FunctionType fnType = op.getType();
|
2021-05-07 19:30:25 -07:00
|
|
|
function_like_impl::printFunctionLikeOp(
|
|
|
|
p, op, fnType.getInputs(), /*isVariadic=*/false, fnType.getResults());
|
2019-06-03 12:08:22 -07:00
|
|
|
}
|
2019-06-04 11:01:32 -07:00
|
|
|
|
2020-11-17 00:37:14 -08:00
|
|
|
static LogicalResult verify(FuncOp op) {
|
2019-06-05 18:12:16 -07:00
|
|
|
// If this function is external there is nothing to do.
|
2020-11-17 00:37:14 -08:00
|
|
|
if (op.isExternal())
|
2019-06-05 18:12:16 -07:00
|
|
|
return success();
|
|
|
|
|
|
|
|
// Verify that the argument list of the function and the arg list of the entry
|
Introduce LLVMFuncOp
Originally, MLIR only supported functions of the built-in FunctionType. On the
conversion path to LLVM IR, we were creating MLIR functions that contained LLVM
dialect operations and used LLVM IR types for everything expect top-level
functions (e.g., a second-order function would have a FunctionType that consume
or produces a wrapped LLVM function pointer type). With MLIR functions
becoming operations, it is now possible to introduce non-built-in function
operations. This will let us use conversion patterns for function conversion,
simplify the MLIR-to-LLVM translation by removing the knowledge of the MLIR
built-in function types, and provide stronger correctness verifications (e.g.
LLVM functions only accept LLVM types).
Furthermore, we can currently construct a situation where the same function is
used with two different types: () -> () when its specified and called directly,
and !llvm<"void ()"> when it's passed somewhere on called indirectly. Having a
special function-op that is always of !llvm<"void ()"> type makes the function
model and the llvm dialect type system more consistent.
Introduce LLVMFuncOp to represent a function in the LLVM dialect. Unlike
standard FuncOp, this function has an LLVMType wrapping an LLVM IR function
type. Generalize the common behavior of function-defining operations
(functions live in a symbol table of a module, contain a single region, are
iterable as a list of blocks, and support argument attributes).
This only defines the operation. Custom syntax, conversion and translation
rules will be added in follow-ups.
The operation name mentions LLVM explicitly to avoid confusion with standard
FuncOp, especially in multiple files that use both `mlir` and `mlir::LLVM`
namespaces.
PiperOrigin-RevId: 259550940
2019-07-23 09:26:15 -07:00
|
|
|
// block line up. The trait already verified that the number of arguments is
|
|
|
|
// the same between the signature and the block.
|
2020-11-17 00:37:14 -08:00
|
|
|
auto fnInputTypes = op.getType().getInputs();
|
|
|
|
Block &entryBlock = op.front();
|
2019-06-05 18:12:16 -07:00
|
|
|
for (unsigned i = 0, e = entryBlock.getNumArguments(); i != e; ++i)
|
2020-01-11 08:54:04 -08:00
|
|
|
if (fnInputTypes[i] != entryBlock.getArgument(i).getType())
|
2020-11-17 00:37:14 -08:00
|
|
|
return op.emitOpError("type of entry block argument #")
|
2020-01-11 08:54:04 -08:00
|
|
|
<< i << '(' << entryBlock.getArgument(i).getType()
|
2019-06-05 18:12:16 -07:00
|
|
|
<< ") must match the type of the corresponding argument in "
|
|
|
|
<< "function signature(" << fnInputTypes[i] << ')';
|
|
|
|
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2019-07-03 13:21:24 -07:00
|
|
|
/// Clone the internal blocks from this function into dest and all attributes
|
|
|
|
/// from this function to dest.
|
|
|
|
void FuncOp::cloneInto(FuncOp dest, BlockAndValueMapping &mapper) {
|
|
|
|
// Add the attributes of this function to dest.
|
|
|
|
llvm::MapVector<Identifier, Attribute> newAttrs;
|
2021-02-25 19:35:53 +01:00
|
|
|
for (const auto &attr : dest->getAttrs())
|
2019-07-03 13:21:24 -07:00
|
|
|
newAttrs.insert(attr);
|
2021-02-25 19:35:53 +01:00
|
|
|
for (const auto &attr : (*this)->getAttrs())
|
2019-07-03 13:21:24 -07:00
|
|
|
newAttrs.insert(attr);
|
2021-02-08 09:44:03 +01:00
|
|
|
dest->setAttrs(DictionaryAttr::get(getContext(), newAttrs.takeVector()));
|
2019-07-03 13:21:24 -07:00
|
|
|
|
|
|
|
// Clone the body.
|
|
|
|
getBody().cloneInto(&dest.getBody(), mapper);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a deep copy of this function and all of its blocks, remapping
|
|
|
|
/// any operands that use values outside of the function using the map that is
|
|
|
|
/// provided (leaving them alone if no entry is present). Replaces references
|
|
|
|
/// to cloned sub-values with the corresponding value that is copied, and adds
|
|
|
|
/// those mappings to the mapper.
|
|
|
|
FuncOp FuncOp::clone(BlockAndValueMapping &mapper) {
|
2021-05-07 19:30:25 -07:00
|
|
|
// Create the new function.
|
|
|
|
FuncOp newFunc = cast<FuncOp>(getOperation()->cloneWithoutRegions());
|
2019-07-03 13:21:24 -07:00
|
|
|
|
|
|
|
// If the function has a body, then the user might be deleting arguments to
|
|
|
|
// the function by specifying them in the mapper. If so, we don't add the
|
|
|
|
// argument to the input type vector.
|
2021-05-07 19:30:25 -07:00
|
|
|
if (!isExternal()) {
|
|
|
|
FunctionType oldType = getType();
|
|
|
|
|
|
|
|
unsigned oldNumArgs = oldType.getNumInputs();
|
|
|
|
SmallVector<Type, 4> newInputs;
|
|
|
|
newInputs.reserve(oldNumArgs);
|
|
|
|
for (unsigned i = 0; i != oldNumArgs; ++i)
|
2019-07-03 13:21:24 -07:00
|
|
|
if (!mapper.contains(getArgument(i)))
|
2021-05-07 19:30:25 -07:00
|
|
|
newInputs.push_back(oldType.getInput(i));
|
|
|
|
|
|
|
|
/// If any of the arguments were dropped, update the type and drop any
|
|
|
|
/// necessary argument attributes.
|
|
|
|
if (newInputs.size() != oldNumArgs) {
|
|
|
|
newFunc.setType(FunctionType::get(oldType.getContext(), newInputs,
|
|
|
|
oldType.getResults()));
|
|
|
|
|
|
|
|
if (ArrayAttr argAttrs = getAllArgAttrs()) {
|
|
|
|
SmallVector<Attribute> newArgAttrs;
|
|
|
|
newArgAttrs.reserve(newInputs.size());
|
|
|
|
for (unsigned i = 0; i != oldNumArgs; ++i)
|
|
|
|
if (!mapper.contains(getArgument(i)))
|
|
|
|
newArgAttrs.push_back(argAttrs[i]);
|
|
|
|
newFunc.setAllArgAttrs(newArgAttrs);
|
|
|
|
}
|
|
|
|
}
|
2019-07-03 13:21:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Clone the current function into the new one and return it.
|
|
|
|
cloneInto(newFunc, mapper);
|
|
|
|
return newFunc;
|
|
|
|
}
|
|
|
|
FuncOp FuncOp::clone() {
|
|
|
|
BlockAndValueMapping mapper;
|
|
|
|
return clone(mapper);
|
|
|
|
}
|
2020-11-17 00:37:14 -08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ModuleOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
void ModuleOp::build(OpBuilder &builder, OperationState &state,
|
|
|
|
Optional<StringRef> name) {
|
2021-03-11 23:58:02 +00:00
|
|
|
state.addRegion()->emplaceBlock();
|
2020-11-17 00:37:14 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-03-22 14:57:39 +01:00
|
|
|
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 (Attribute attr : llvm::make_second_range(getOperation()->getAttrs())) {
|
|
|
|
if (auto spec = attr.dyn_cast<DataLayoutSpecInterface>())
|
|
|
|
return spec;
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-11-17 00:37:14 -08:00
|
|
|
static LogicalResult verify(ModuleOp op) {
|
|
|
|
// Check that none of the attributes are non-dialect attributes, except for
|
|
|
|
// the symbol related attributes.
|
2021-02-25 19:35:53 +01:00
|
|
|
for (auto attr : op->getAttrs()) {
|
2020-11-17 00:37:14 -08:00
|
|
|
if (!attr.first.strref().contains('.') &&
|
|
|
|
!llvm::is_contained(
|
|
|
|
ArrayRef<StringRef>{mlir::SymbolTable::getSymbolAttrName(),
|
|
|
|
mlir::SymbolTable::getVisibilityAttrName()},
|
|
|
|
attr.first.strref()))
|
2020-12-03 02:12:01 +05:30
|
|
|
return op.emitOpError() << "can only contain attributes with "
|
|
|
|
"dialect-prefixed names, found: '"
|
|
|
|
<< attr.first << "'";
|
2020-11-17 00:37:14 -08:00
|
|
|
}
|
|
|
|
|
2021-03-22 14:57:39 +01:00
|
|
|
// Check that there is at most one data layout spec attribute.
|
|
|
|
StringRef layoutSpecAttrName;
|
|
|
|
DataLayoutSpecInterface layoutSpec;
|
|
|
|
for (const NamedAttribute &na : op->getAttrs()) {
|
|
|
|
if (auto spec = na.second.dyn_cast<DataLayoutSpecInterface>()) {
|
|
|
|
if (layoutSpec) {
|
|
|
|
InFlightDiagnostic diag =
|
|
|
|
op.emitOpError() << "expects at most one data layout attribute";
|
|
|
|
diag.attachNote() << "'" << layoutSpecAttrName
|
|
|
|
<< "' is a data layout attribute";
|
|
|
|
diag.attachNote() << "'" << na.first << "' is a data layout attribute";
|
|
|
|
}
|
|
|
|
layoutSpecAttrName = na.first.strref();
|
|
|
|
layoutSpec = spec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-17 00:37:14 -08:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2021-01-20 16:17:26 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// UnrealizedConversionCastOp
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
UnrealizedConversionCastOp::fold(ArrayRef<Attribute> attrOperands,
|
|
|
|
SmallVectorImpl<OpFoldResult> &foldResults) {
|
|
|
|
OperandRange operands = inputs();
|
|
|
|
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.
|
|
|
|
ResultRange results = outputs();
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-11-17 00:37:14 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TableGen'd op method definitions
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#define GET_OP_CLASSES
|
|
|
|
#include "mlir/IR/BuiltinOps.cpp.inc"
|