[mlir][IR] Use tablegen for the BuiltinDialect and operations

This has been a long standing TODO, and cleans up a bit of IR/. This will also make it easier to move FuncOp out of IR/ at some point in the future. For now, Module.h and Function.h just forward BuiltinDialect.h. These files will be removed in a followup.

Differential Revision: https://reviews.llvm.org/D91571
This commit is contained in:
River Riddle 2020-11-17 00:37:14 -08:00
parent 9faedb2d71
commit c51e4c4f01
12 changed files with 587 additions and 525 deletions

View File

@ -0,0 +1,93 @@
//===- BuiltinDialect.h - MLIR Builtin Dialect ------------------*- C++ -*-===//
//
// 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 and its operations.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_IR_BUILTINDIALECT_H_
#define MLIR_IR_BUILTINDIALECT_H_
#include "mlir/IR/Dialect.h"
#include "mlir/IR/FunctionSupport.h"
#include "mlir/IR/OwningOpRefBase.h"
#include "mlir/IR/SymbolTable.h"
#include "mlir/Interfaces/CallInterfaces.h"
#include "llvm/Support/PointerLikeTypeTraits.h"
//===----------------------------------------------------------------------===//
// Dialect
//===----------------------------------------------------------------------===//
#include "mlir/IR/BuiltinDialect.h.inc"
//===----------------------------------------------------------------------===//
// Dialect Operations
//===----------------------------------------------------------------------===//
#define GET_OP_CLASSES
#include "mlir/IR/BuiltinOps.h.inc"
//===----------------------------------------------------------------------===//
// Dialect Utilities
//===----------------------------------------------------------------------===//
namespace mlir {
/// This class acts as an owning reference to a module, and will automatically
/// destroy the held module on destruction if the held module is valid.
class OwningModuleRef : public OwningOpRefBase<ModuleOp> {
public:
using OwningOpRefBase<ModuleOp>::OwningOpRefBase;
};
} // end namespace mlir
namespace llvm {
// Functions hash just like pointers.
template <>
struct DenseMapInfo<mlir::FuncOp> {
static mlir::FuncOp getEmptyKey() {
auto *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
return mlir::FuncOp::getFromOpaquePointer(pointer);
}
static mlir::FuncOp getTombstoneKey() {
auto *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
return mlir::FuncOp::getFromOpaquePointer(pointer);
}
static unsigned getHashValue(mlir::FuncOp val) {
return hash_value(val.getAsOpaquePointer());
}
static bool isEqual(mlir::FuncOp lhs, mlir::FuncOp rhs) { return lhs == rhs; }
};
/// Allow stealing the low bits of FuncOp.
template <>
struct PointerLikeTypeTraits<mlir::FuncOp> {
static inline void *getAsVoidPointer(mlir::FuncOp val) {
return const_cast<void *>(val.getAsOpaquePointer());
}
static inline mlir::FuncOp getFromVoidPointer(void *p) {
return mlir::FuncOp::getFromOpaquePointer(p);
}
static constexpr int NumLowBitsAvailable = 3;
};
/// Allow stealing the low bits of ModuleOp.
template <>
struct PointerLikeTypeTraits<mlir::ModuleOp> {
public:
static inline void *getAsVoidPointer(mlir::ModuleOp val) {
return const_cast<void *>(val.getAsOpaquePointer());
}
static inline mlir::ModuleOp getFromVoidPointer(void *p) {
return mlir::ModuleOp::getFromOpaquePointer(p);
}
static constexpr int NumLowBitsAvailable = 3;
};
} // end namespace llvm
#endif // MLIR_IR_BUILTINDIALECT_H_

View File

@ -0,0 +1,235 @@
//===- BuiltinOps.td - Builtin operation definitions -------*- tablegen -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Defines the set of builtin MLIR operations.
//
//===----------------------------------------------------------------------===//
#ifndef BUILTIN_OPS
#define BUILTIN_OPS
include "mlir/IR/SymbolInterfaces.td"
include "mlir/Interfaces/CallInterfaces.td"
def Builtin_Dialect : Dialect {
let summary =
"A dialect containing the builtin Attributes, Operations, and Types";
let name = "";
let cppNamespace = "::mlir";
}
// Base class for Builtin dialect ops.
class Builtin_Op<string mnemonic, list<OpTrait> traits = []> :
Op<Builtin_Dialect, mnemonic, traits>;
//===----------------------------------------------------------------------===//
// FuncOp
//===----------------------------------------------------------------------===//
def FuncOp : Builtin_Op<"func", [
AffineScope, AutomaticAllocationScope, CallableOpInterface, FunctionLike,
IsolatedFromAbove, Symbol
]> {
let summary = "An operation with a name containing a single `SSACFG` region";
let description = [{
Operations within the function cannot implicitly capture values defined
outside of the function, i.e. Functions are `IsolatedFromAbove`. All
external references must use function arguments or attributes that establish
a symbolic connection (e.g. symbols referenced by name via a string
attribute like SymbolRefAttr). An external function declaration (used when
referring to a function declared in some other module) has no body. While
the MLIR textual form provides a nice inline syntax for function arguments,
they are internally represented as block arguments to the first block in
the region.
Only dialect attribute names may be specified in the attribute dictionaries
for function arguments, results, or the function itself.
Example:
```mlir
// External function definitions.
func @abort()
func @scribble(i32, i64, memref<? x 128 x f32, #layout_map0>) -> f64
// A function that returns its argument twice:
func @count(%x: i64) -> (i64, i64)
attributes {fruit: "banana"} {
return %x, %x: i64, i64
}
// A function with an argument attribute
func @example_fn_arg(%x: i32 {swift.self = unit})
// A function with a result attribute
func @example_fn_result() -> (f64 {dialectName.attrName = 0 : i64})
// A function with an attribute
func @example_fn_attr() attributes {dialectName.attrName = false}
```
}];
let arguments = (ins SymbolNameAttr:$sym_name,
TypeAttr:$type,
OptionalAttr<StrAttr>:$sym_visibility);
let regions = (region AnyRegion:$body);
let builders = [OpBuilderDAG<(ins
"StringRef":$name, "FunctionType":$type,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs,
CArg<"ArrayRef<MutableDictionaryAttr>", "{}">:$argAttrs)
>];
let extraClassDeclaration = [{
static FuncOp create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs = {});
static FuncOp create(Location location, StringRef name, FunctionType type,
iterator_range<dialect_attr_iterator> attrs);
static FuncOp create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs,
ArrayRef<MutableDictionaryAttr> argAttrs);
/// 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). If the mapper
/// contains entries for function arguments, these arguments are not
/// included in the new function. Replaces references to cloned sub-values
/// with the corresponding value that is copied, and adds those mappings to
/// the mapper.
FuncOp clone(BlockAndValueMapping &mapper);
FuncOp clone();
/// Clone the internal blocks and attributes from this function into dest.
/// Any cloned blocks are appended to the back of dest. This function
/// asserts that the attributes of the current function and dest are
/// compatible.
void cloneInto(FuncOp dest, BlockAndValueMapping &mapper);
//===------------------------------------------------------------------===//
// CallableOpInterface
//===------------------------------------------------------------------===//
/// Returns the region on the current operation that is callable. This may
/// return null in the case of an external callable object, e.g. an external
/// function.
Region *getCallableRegion() { return isExternal() ? nullptr : &getBody(); }
/// Returns the results types that the callable region produces when
/// executed.
ArrayRef<Type> getCallableResults() { return getType().getResults(); }
//===------------------------------------------------------------------===//
// SymbolOpInterface Methods
//===------------------------------------------------------------------===//
bool isDeclaration() { return isExternal(); }
private:
// This trait needs access to the hooks defined below.
friend class OpTrait::FunctionLike<FuncOp>;
/// Returns the number of arguments. This is a hook for
/// OpTrait::FunctionLike.
unsigned getNumFuncArguments() { return getType().getInputs().size(); }
/// Returns the number of results. This is a hook for OpTrait::FunctionLike.
unsigned getNumFuncResults() { return getType().getResults().size(); }
/// Hook for OpTrait::FunctionLike, called after verifying that the 'type'
/// attribute is present and checks if it holds a function type. Ensures
/// getType, getNumFuncArguments, and getNumFuncResults can be called
/// safely.
LogicalResult verifyType() {
auto type = getTypeAttr().getValue();
if (!type.isa<FunctionType>())
return emitOpError("requires '" + getTypeAttrName() +
"' attribute of function type");
return success();
}
}];
let parser = [{ return ::parseFuncOp(parser, result); }];
let printer = [{ return ::print(*this, p); }];
let verifier = [{ return ::verify(*this); }];
}
//===----------------------------------------------------------------------===//
// ModuleOp
//===----------------------------------------------------------------------===//
def ModuleOp : Builtin_Op<"module", [
AffineScope, IsolatedFromAbove, NoRegionArguments, SymbolTable, Symbol,
SingleBlockImplicitTerminator<"ModuleTerminatorOp">
]> {
let summary = "A top level container operation";
let description = [{
A `module` represents a top-level container operation. It contains a single
SSACFG region containing a single block which can contain any
operations. Operations within this region cannot implicitly capture values
defined outside the module, i.e. Modules are `IsolatedFromAbove`. Modules
have an optional symbol name which can be used to refer to them in
operations.
Example:
```mlir
module {
func @foo()
}
```
}];
let arguments = (ins OptionalAttr<SymbolNameAttr>:$sym_name,
OptionalAttr<StrAttr>:$sym_visibility);
let regions = (region SizedRegion<1>:$body);
let assemblyFormat = "($sym_name^)? attr-dict-with-keyword $body";
let builders = [OpBuilderDAG<(ins CArg<"Optional<StringRef>", "{}">:$name)>];
let extraClassDeclaration = [{
/// Construct a module from the given location with an optional name.
static ModuleOp create(Location loc, Optional<StringRef> name = llvm::None);
/// Return the name of this module if present.
Optional<StringRef> getName() { return sym_name(); }
/// Print the this module in the custom top-level form.
void print(raw_ostream &os, OpPrintingFlags flags = llvm::None);
void print(raw_ostream &os, AsmState &state,
OpPrintingFlags flags = llvm::None);
void dump();
//===------------------------------------------------------------------===//
// SymbolOpInterface Methods
//===------------------------------------------------------------------===//
/// A ModuleOp may optionally define a symbol.
bool isOptionalSymbol() { return true; }
}];
let verifier = [{ return ::verify(*this); }];
// We need to ensure the block inside the region is properly terminated;
// the auto-generated builders do not guarantee that.
let skipDefaultBuilders = 1;
}
//===----------------------------------------------------------------------===//
// ModuleTerminatorOp
//===----------------------------------------------------------------------===//
def ModuleTerminatorOp : Builtin_Op<"module_terminator", [
Terminator, HasParent<"ModuleOp">
]> {
let summary = "A pseudo op that marks the end of a module";
let description = [{
`module_terminator` is a special terminator operation for the body of a
`module`, it has no semantic meaning beyond keeping the body of a `module`
well-formed.
}];
let assemblyFormat = "attr-dict";
}
#endif // BUILTIN_OPS

View File

@ -1,3 +1,11 @@
add_mlir_interface(OpAsmInterface)
add_mlir_interface(SymbolInterfaces)
add_mlir_interface(RegionKindInterface)
set(LLVM_TARGET_DEFINITIONS BuiltinOps.td)
mlir_tablegen(BuiltinOps.h.inc -gen-op-decls)
mlir_tablegen(BuiltinOps.cpp.inc -gen-op-defs)
mlir_tablegen(BuiltinDialect.h.inc -gen-dialect-decls)
add_public_tablegen_target(MLIRBuiltinOpsIncGen)
add_mlir_doc(BuiltinOps -gen-op-doc Builtin Dialects/)

View File

@ -13,137 +13,7 @@
#ifndef MLIR_IR_FUNCTION_H
#define MLIR_IR_FUNCTION_H
#include "mlir/IR/Block.h"
#include "mlir/IR/FunctionSupport.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/SymbolTable.h"
#include "mlir/Interfaces/CallInterfaces.h"
#include "llvm/Support/PointerLikeTypeTraits.h"
namespace mlir {
//===--------------------------------------------------------------------===//
// Function Operation.
//===--------------------------------------------------------------------===//
/// FuncOp represents a function, or an operation containing one region that
/// forms a CFG(Control Flow Graph). The region of a function is not allowed to
/// implicitly capture global values, and all external references must use
/// Function arguments or attributes that establish a symbolic connection(e.g.
/// symbols referenced by name via a string attribute).
class FuncOp
: public Op<FuncOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
OpTrait::OneRegion, OpTrait::IsIsolatedFromAbove,
OpTrait::FunctionLike, OpTrait::AutomaticAllocationScope,
OpTrait::AffineScope, CallableOpInterface::Trait,
SymbolOpInterface::Trait> {
public:
using Op::Op;
using Op::print;
static StringRef getOperationName() { return "func"; }
static FuncOp create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs = {});
static FuncOp create(Location location, StringRef name, FunctionType type,
iterator_range<dialect_attr_iterator> attrs);
static FuncOp create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs,
ArrayRef<MutableDictionaryAttr> argAttrs);
static void build(OpBuilder &builder, OperationState &result, StringRef name,
FunctionType type, ArrayRef<NamedAttribute> attrs = {},
ArrayRef<MutableDictionaryAttr> argAttrs = {});
/// Operation hooks.
static ParseResult parse(OpAsmParser &parser, OperationState &result);
void print(OpAsmPrinter &p);
LogicalResult verify();
/// 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). If the mapper
/// contains entries for function arguments, these arguments are not included
/// in the new function. Replaces references to cloned sub-values with the
/// corresponding value that is copied, and adds those mappings to the mapper.
FuncOp clone(BlockAndValueMapping &mapper);
FuncOp clone();
/// Clone the internal blocks and attributes from this function into dest. Any
/// cloned blocks are appended to the back of dest. This function asserts that
/// the attributes of the current function and dest are compatible.
void cloneInto(FuncOp dest, BlockAndValueMapping &mapper);
//===--------------------------------------------------------------------===//
// CallableOpInterface
//===--------------------------------------------------------------------===//
/// Returns the region on the current operation that is callable. This may
/// return null in the case of an external callable object, e.g. an external
/// function.
Region *getCallableRegion() { return isExternal() ? nullptr : &getBody(); }
/// Returns the results types that the callable region produces when executed.
ArrayRef<Type> getCallableResults() { return getType().getResults(); }
//===--------------------------------------------------------------------===//
// SymbolOpInterface Methods
//===--------------------------------------------------------------------===//
bool isDeclaration() { return isExternal(); }
private:
// This trait needs access to the hooks defined below.
friend class OpTrait::FunctionLike<FuncOp>;
/// Returns the number of arguments. This is a hook for OpTrait::FunctionLike.
unsigned getNumFuncArguments() { return getType().getInputs().size(); }
/// Returns the number of results. This is a hook for OpTrait::FunctionLike.
unsigned getNumFuncResults() { return getType().getResults().size(); }
/// Hook for OpTrait::FunctionLike, called after verifying that the 'type'
/// attribute is present and checks if it holds a function type. Ensures
/// getType, getNumFuncArguments, and getNumFuncResults can be called safely.
LogicalResult verifyType() {
auto type = getTypeAttr().getValue();
if (!type.isa<FunctionType>())
return emitOpError("requires '" + getTypeAttrName() +
"' attribute of function type");
return success();
}
};
} // end namespace mlir
namespace llvm {
// Functions hash just like pointers.
template <> struct DenseMapInfo<mlir::FuncOp> {
static mlir::FuncOp getEmptyKey() {
auto pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
return mlir::FuncOp::getFromOpaquePointer(pointer);
}
static mlir::FuncOp getTombstoneKey() {
auto pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
return mlir::FuncOp::getFromOpaquePointer(pointer);
}
static unsigned getHashValue(mlir::FuncOp val) {
return hash_value(val.getAsOpaquePointer());
}
static bool isEqual(mlir::FuncOp LHS, mlir::FuncOp RHS) { return LHS == RHS; }
};
/// Allow stealing the low bits of FuncOp.
template <> struct PointerLikeTypeTraits<mlir::FuncOp> {
public:
static inline void *getAsVoidPointer(mlir::FuncOp I) {
return const_cast<void *>(I.getAsOpaquePointer());
}
static inline mlir::FuncOp getFromVoidPointer(void *P) {
return mlir::FuncOp::getFromOpaquePointer(P);
}
static constexpr int NumLowBitsAvailable = 3;
};
} // namespace llvm
// TODO: This is a temporary forward until Function.h is removed.
#include "mlir/IR/BuiltinDialect.h"
#endif // MLIR_IR_FUNCTION_H

View File

@ -13,138 +13,7 @@
#ifndef MLIR_IR_MODULE_H
#define MLIR_IR_MODULE_H
#include "mlir/IR/OwningOpRefBase.h"
#include "mlir/IR/SymbolTable.h"
#include "llvm/Support/PointerLikeTypeTraits.h"
namespace mlir {
class ModuleTerminatorOp;
//===----------------------------------------------------------------------===//
// Module Operation.
//===----------------------------------------------------------------------===//
/// ModuleOp represents a module, or an operation containing one region with a
/// single block containing opaque operations. The region of a module is not
/// allowed to implicitly capture global values, and all external references
/// must use symbolic references via attributes(e.g. via a string name).
class ModuleOp
: public Op<
ModuleOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
OpTrait::IsIsolatedFromAbove, OpTrait::AffineScope,
OpTrait::SymbolTable,
OpTrait::SingleBlockImplicitTerminator<ModuleTerminatorOp>::Impl,
SymbolOpInterface::Trait, OpTrait::NoRegionArguments> {
public:
using Op::Op;
using Op::print;
static StringRef getOperationName() { return "module"; }
static void build(OpBuilder &builder, OperationState &result,
Optional<StringRef> name = llvm::None);
/// Construct a module from the given location with an optional name.
static ModuleOp create(Location loc, Optional<StringRef> name = llvm::None);
/// Operation hooks.
static ParseResult parse(OpAsmParser &parser, OperationState &result);
void print(OpAsmPrinter &p);
LogicalResult verify();
/// Return body of this module.
Region &getBodyRegion();
Block *getBody();
/// Return the name of this module if present.
Optional<StringRef> getName();
/// Print the this module in the custom top-level form.
void print(raw_ostream &os, OpPrintingFlags flags = llvm::None);
void print(raw_ostream &os, AsmState &state,
OpPrintingFlags flags = llvm::None);
void dump();
//===--------------------------------------------------------------------===//
// Body Management.
//===--------------------------------------------------------------------===//
/// Iteration over the operations in the module.
using iterator = Block::iterator;
iterator begin() { return getBody()->begin(); }
iterator end() { return getBody()->end(); }
Operation &front() { return *begin(); }
/// This returns a range of operations of the given type 'T' held within the
/// module.
template <typename T> iterator_range<Block::op_iterator<T>> getOps() {
return getBody()->getOps<T>();
}
/// Insert the operation into the back of the body, before the terminator.
void push_back(Operation *op) {
insert(Block::iterator(getBody()->getTerminator()), op);
}
/// Insert the operation at the given insertion point. Note: The operation is
/// never inserted after the terminator, even if the insertion point is end().
void insert(Operation *insertPt, Operation *op) {
insert(Block::iterator(insertPt), op);
}
void insert(Block::iterator insertPt, Operation *op) {
auto *body = getBody();
if (insertPt == body->end())
insertPt = Block::iterator(body->getTerminator());
body->getOperations().insert(insertPt, op);
}
//===--------------------------------------------------------------------===//
// SymbolOpInterface Methods
//===--------------------------------------------------------------------===//
/// A ModuleOp may optionally define a symbol.
bool isOptionalSymbol() { return true; }
};
/// The ModuleTerminatorOp is a special terminator operation for the body of a
/// ModuleOp, it has no semantic meaning beyond keeping the body of a ModuleOp
/// well-formed.
///
/// This operation does _not_ have a custom syntax. However, ModuleOp will omit
/// the terminator in their custom syntax for brevity.
class ModuleTerminatorOp
: public Op<ModuleTerminatorOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
OpTrait::HasParent<ModuleOp>::Impl, OpTrait::IsTerminator> {
public:
using Op::Op;
static StringRef getOperationName() { return "module_terminator"; }
static void build(OpBuilder &, OperationState &) {}
};
/// This class acts as an owning reference to a module, and will automatically
/// destroy the held module on destruction if the held module is valid.
class OwningModuleRef : public OwningOpRefBase<ModuleOp> {
public:
using OwningOpRefBase<ModuleOp>::OwningOpRefBase;
};
} // end namespace mlir
namespace llvm {
/// Allow stealing the low bits of ModuleOp.
template <> struct PointerLikeTypeTraits<mlir::ModuleOp> {
public:
static inline void *getAsVoidPointer(mlir::ModuleOp I) {
return const_cast<void *>(I.getAsOpaquePointer());
}
static inline mlir::ModuleOp getFromVoidPointer(void *P) {
return mlir::ModuleOp::getFromOpaquePointer(P);
}
static constexpr int NumLowBitsAvailable = 3;
};
} // end namespace llvm
// TODO: This is a temporary forward until Module.h is removed.
#include "mlir/IR/BuiltinDialect.h"
#endif // MLIR_IR_MODULE_H

View File

@ -786,6 +786,132 @@ class VariadicSuccessors
: public detail::MultiSuccessorTraitBase<ConcreteType, VariadicSuccessors> {
};
//===----------------------------------------------------------------------===//
// SingleBlockImplicitTerminator
/// This class provides APIs and verifiers for ops with regions having a single
/// block that must terminate with `TerminatorOpType`.
template <typename TerminatorOpType>
struct SingleBlockImplicitTerminator {
template <typename ConcreteType>
class Impl : public TraitBase<ConcreteType, Impl> {
private:
/// Builds a terminator operation without relying on OpBuilder APIs to avoid
/// cyclic header inclusion.
static Operation *buildTerminator(OpBuilder &builder, Location loc) {
OperationState state(loc, TerminatorOpType::getOperationName());
TerminatorOpType::build(builder, state);
return Operation::create(state);
}
public:
static LogicalResult verifyTrait(Operation *op) {
for (unsigned i = 0, e = op->getNumRegions(); i < e; ++i) {
Region &region = op->getRegion(i);
// Empty regions are fine.
if (region.empty())
continue;
// Non-empty regions must contain a single basic block.
if (std::next(region.begin()) != region.end())
return op->emitOpError("expects region #")
<< i << " to have 0 or 1 blocks";
Block &block = region.front();
if (block.empty())
return op->emitOpError() << "expects a non-empty block";
Operation &terminator = block.back();
if (isa<TerminatorOpType>(terminator))
continue;
return op->emitOpError("expects regions to end with '" +
TerminatorOpType::getOperationName() +
"', found '" +
terminator.getName().getStringRef() + "'")
.attachNote()
<< "in custom textual format, the absence of terminator implies "
"'"
<< TerminatorOpType::getOperationName() << '\'';
}
return success();
}
/// Ensure that the given region has the terminator required by this trait.
/// If OpBuilder is provided, use it to build the terminator and notify the
/// OpBuilder litsteners accordingly. If only a Builder is provided, locally
/// construct an OpBuilder with no listeners; this should only be used if no
/// OpBuilder is available at the call site, e.g., in the parser.
static void ensureTerminator(Region &region, Builder &builder,
Location loc) {
::mlir::impl::ensureRegionTerminator(region, builder, loc,
buildTerminator);
}
static void ensureTerminator(Region &region, OpBuilder &builder,
Location loc) {
::mlir::impl::ensureRegionTerminator(region, builder, loc,
buildTerminator);
}
Block *getBody(unsigned idx = 0) {
Region &region = this->getOperation()->getRegion(idx);
assert(!region.empty() && "unexpected empty region");
return &region.front();
}
Region &getBodyRegion(unsigned idx = 0) {
return this->getOperation()->getRegion(idx);
}
//===------------------------------------------------------------------===//
// Single Region Utilities
//===------------------------------------------------------------------===//
/// The following are a set of methods only enabled when the parent
/// operation has a single region. Each of these methods take an additional
/// template parameter that represents the concrete operation so that we
/// can use SFINAE to disable the methods for non-single region operations.
template <typename OpT, typename T = void>
using enable_if_single_region =
typename std::enable_if_t<OpT::template hasTrait<OneRegion>(), T>;
template <typename OpT = ConcreteType>
enable_if_single_region<OpT, Block::iterator> begin() {
return getBody()->begin();
}
template <typename OpT = ConcreteType>
enable_if_single_region<OpT, Block::iterator> end() {
return getBody()->end();
}
template <typename OpT = ConcreteType>
enable_if_single_region<OpT, Operation &> front() {
return *begin();
}
/// Insert the operation into the back of the body, before the terminator.
template <typename OpT = ConcreteType>
enable_if_single_region<OpT> push_back(Operation *op) {
insert(Block::iterator(getBody()->getTerminator()), op);
}
/// Insert the operation at the given insertion point. Note: The operation
/// is never inserted after the terminator, even if the insertion point is
/// end().
template <typename OpT = ConcreteType>
enable_if_single_region<OpT> insert(Operation *insertPt, Operation *op) {
insert(Block::iterator(insertPt), op);
}
template <typename OpT = ConcreteType>
enable_if_single_region<OpT> insert(Block::iterator insertPt,
Operation *op) {
auto *body = getBody();
if (insertPt == body->end())
insertPt = Block::iterator(body->getTerminator());
body->getOperations().insert(insertPt, op);
}
};
};
//===----------------------------------------------------------------------===//
// Misc Traits
@ -1036,78 +1162,6 @@ public:
}
};
/// This class provides APIs and verifiers for ops with regions having a single
/// block that must terminate with `TerminatorOpType`.
template <typename TerminatorOpType> struct SingleBlockImplicitTerminator {
template <typename ConcreteType>
class Impl : public TraitBase<ConcreteType, Impl> {
private:
/// Builds a terminator operation without relying on OpBuilder APIs to avoid
/// cyclic header inclusion.
static Operation *buildTerminator(OpBuilder &builder, Location loc) {
OperationState state(loc, TerminatorOpType::getOperationName());
TerminatorOpType::build(builder, state);
return Operation::create(state);
}
public:
static LogicalResult verifyTrait(Operation *op) {
for (unsigned i = 0, e = op->getNumRegions(); i < e; ++i) {
Region &region = op->getRegion(i);
// Empty regions are fine.
if (region.empty())
continue;
// Non-empty regions must contain a single basic block.
if (std::next(region.begin()) != region.end())
return op->emitOpError("expects region #")
<< i << " to have 0 or 1 blocks";
Block &block = region.front();
if (block.empty())
return op->emitOpError() << "expects a non-empty block";
Operation &terminator = block.back();
if (isa<TerminatorOpType>(terminator))
continue;
return op->emitOpError("expects regions to end with '" +
TerminatorOpType::getOperationName() +
"', found '" +
terminator.getName().getStringRef() + "'")
.attachNote()
<< "in custom textual format, the absence of terminator implies "
"'"
<< TerminatorOpType::getOperationName() << '\'';
}
return success();
}
/// Ensure that the given region has the terminator required by this trait.
/// If OpBuilder is provided, use it to build the terminator and notify the
/// OpBuilder litsteners accordingly. If only a Builder is provided, locally
/// construct an OpBuilder with no listeners; this should only be used if no
/// OpBuilder is available at the call site, e.g., in the parser.
static void ensureTerminator(Region &region, Builder &builder,
Location loc) {
::mlir::impl::ensureRegionTerminator(region, builder, loc,
buildTerminator);
}
static void ensureTerminator(Region &region, OpBuilder &builder,
Location loc) {
::mlir::impl::ensureRegionTerminator(region, builder, loc,
buildTerminator);
}
Block *getBody(unsigned idx = 0) {
Region &region = this->getOperation()->getRegion(idx);
assert(!region.empty() && "unexpected empty region");
return &region.front();
}
};
};
/// This class provides a verifier for ops that are expecting their parent
/// to be one of the given parent ops
template <typename... ParentOpTypes>

View File

@ -147,9 +147,9 @@ public:
void replaceUsesOfWith(Value from, Value to);
/// Replace all uses of results of this operation with the provided 'values'.
template <typename ValuesT,
typename = decltype(std::declval<ValuesT>().begin())>
void replaceAllUsesWith(ValuesT &&values) {
template <typename ValuesT>
std::enable_if_t<!std::is_convertible<ValuesT, Operation *>::value>
replaceAllUsesWith(ValuesT &&values) {
assert(std::distance(values.begin(), values.end()) == getNumResults() &&
"expected 'values' to correspond 1-1 with the number of results");

View File

@ -1,4 +1,4 @@
//===- Function.cpp - MLIR Function Classes -------------------------------===//
//===- 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.
@ -6,19 +6,65 @@
//
//===----------------------------------------------------------------------===//
#include "mlir/IR/Function.h"
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/FunctionImplementation.h"
#include "llvm/ADT/BitVector.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/StandardTypes.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
using namespace mlir;
//===----------------------------------------------------------------------===//
// Function Operation.
// 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();
}
};
} // end anonymous namespace.
/// A builtin dialect to define types/etc that are necessary for the validity of
/// the IR.
void BuiltinDialect::initialize() {
addTypes<ComplexType, BFloat16Type, Float16Type, Float32Type, Float64Type,
FunctionType, IndexType, IntegerType, MemRefType, UnrankedMemRefType,
NoneType, OpaqueType, RankedTensorType, TupleType,
UnrankedTensorType, VectorType>();
addAttributes<AffineMapAttr, ArrayAttr, DenseIntOrFPElementsAttr,
DenseStringElementsAttr, DictionaryAttr, FloatAttr,
SymbolRefAttr, IntegerAttr, IntegerSetAttr, OpaqueAttr,
OpaqueElementsAttr, SparseElementsAttr, StringAttr, TypeAttr,
UnitAttr>();
addAttributes<CallSiteLoc, FileLineColLoc, FusedLoc, NameLoc, OpaqueLoc,
UnknownLoc>();
addOperations<
#define GET_OP_LIST
#include "mlir/IR/BuiltinOps.cpp.inc"
>();
addInterfaces<BuiltinOpAsmDialectInterface>();
}
//===----------------------------------------------------------------------===//
// FuncOp
//===----------------------------------------------------------------------===//
FuncOp FuncOp::create(Location location, StringRef name, FunctionType type,
@ -41,14 +87,14 @@ FuncOp FuncOp::create(Location location, StringRef name, FunctionType type,
return func;
}
void FuncOp::build(OpBuilder &builder, OperationState &result, StringRef name,
void FuncOp::build(OpBuilder &builder, OperationState &state, StringRef name,
FunctionType type, ArrayRef<NamedAttribute> attrs,
ArrayRef<MutableDictionaryAttr> argAttrs) {
result.addAttribute(SymbolTable::getSymbolAttrName(),
builder.getStringAttr(name));
result.addAttribute(getTypeAttrName(), TypeAttr::get(type));
result.attributes.append(attrs.begin(), attrs.end());
result.addRegion();
state.addAttribute(SymbolTable::getSymbolAttrName(),
builder.getStringAttr(name));
state.addAttribute(getTypeAttrName(), TypeAttr::get(type));
state.attributes.append(attrs.begin(), attrs.end());
state.addRegion();
if (argAttrs.empty())
return;
@ -56,12 +102,10 @@ void FuncOp::build(OpBuilder &builder, OperationState &result, StringRef name,
SmallString<8> argAttrName;
for (unsigned i = 0, e = type.getNumInputs(); i != e; ++i)
if (auto argDict = argAttrs[i].getDictionary(builder.getContext()))
result.addAttribute(getArgAttrName(i, argAttrName), argDict);
state.addAttribute(getArgAttrName(i, argAttrName), argDict);
}
/// Parsing/Printing methods.
ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
static ParseResult parseFuncOp(OpAsmParser &parser, OperationState &result) {
auto buildFuncType = [](Builder &builder, ArrayRef<Type> argTypes,
ArrayRef<Type> results, impl::VariadicFlag,
std::string &) {
@ -72,25 +116,25 @@ ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
buildFuncType);
}
void FuncOp::print(OpAsmPrinter &p) {
FunctionType fnType = getType();
impl::printFunctionLikeOp(p, *this, fnType.getInputs(), /*isVariadic=*/false,
static void print(FuncOp op, OpAsmPrinter &p) {
FunctionType fnType = op.getType();
impl::printFunctionLikeOp(p, op, fnType.getInputs(), /*isVariadic=*/false,
fnType.getResults());
}
LogicalResult FuncOp::verify() {
static LogicalResult verify(FuncOp op) {
// If this function is external there is nothing to do.
if (isExternal())
if (op.isExternal())
return success();
// Verify that the argument list of the function and the arg list of the entry
// block line up. The trait already verified that the number of arguments is
// the same between the signature and the block.
auto fnInputTypes = getType().getInputs();
Block &entryBlock = front();
auto fnInputTypes = op.getType().getInputs();
Block &entryBlock = op.front();
for (unsigned i = 0, e = entryBlock.getNumArguments(); i != e; ++i)
if (fnInputTypes[i] != entryBlock.getArgument(i).getType())
return emitOpError("type of entry block argument #")
return op.emitOpError("type of entry block argument #")
<< i << '(' << entryBlock.getArgument(i).getType()
<< ") must match the type of the corresponding argument in "
<< "function signature(" << fnInputTypes[i] << ')';
@ -152,3 +196,46 @@ FuncOp FuncOp::clone() {
BlockAndValueMapping mapper;
return clone(mapper);
}
//===----------------------------------------------------------------------===//
// ModuleOp
//===----------------------------------------------------------------------===//
void ModuleOp::build(OpBuilder &builder, OperationState &state,
Optional<StringRef> name) {
ensureTerminator(*state.addRegion(), builder, state.location);
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);
}
static LogicalResult verify(ModuleOp op) {
// Check that none of the attributes are non-dialect attributes, except for
// the symbol related attributes.
for (auto attr : op.getAttrs()) {
if (!attr.first.strref().contains('.') &&
!llvm::is_contained(
ArrayRef<StringRef>{mlir::SymbolTable::getSymbolAttrName(),
mlir::SymbolTable::getVisibilityAttrName()},
attr.first.strref()))
return op.emitOpError()
<< "can only contain dialect-specific attributes, found: '"
<< attr.first << "'";
}
return success();
}
//===----------------------------------------------------------------------===//
// TableGen'd op method definitions
//===----------------------------------------------------------------------===//
#define GET_OP_CLASSES
#include "mlir/IR/BuiltinOps.cpp.inc"

View File

@ -5,16 +5,15 @@ add_mlir_library(MLIRIR
Attributes.cpp
Block.cpp
Builders.cpp
BuiltinDialect.cpp
Diagnostics.cpp
Dialect.cpp
Dominance.cpp
Function.cpp
FunctionImplementation.cpp
FunctionSupport.cpp
IntegerSet.cpp
Location.cpp
MLIRContext.cpp
Module.cpp
Operation.cpp
OperationSupport.cpp
PatternMatch.cpp
@ -33,10 +32,11 @@ add_mlir_library(MLIRIR
${MLIR_MAIN_INCLUDE_DIR}/mlir/IR
DEPENDS
MLIRBuiltinOpsIncGen
MLIRCallInterfacesIncGen
MLIROpAsmInterfaceIncGen
MLIRSymbolInterfacesIncGen
MLIRRegionKindInterfaceIncGen
MLIRSymbolInterfacesIncGen
LINK_LIBS PUBLIC
MLIRSupport

View File

@ -82,57 +82,6 @@ void mlir::registerMLIRContextCLOptions() {
*clOptions;
}
//===----------------------------------------------------------------------===//
// 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();
}
};
/// A builtin dialect to define types/etc that are necessary for the validity of
/// the IR.
struct BuiltinDialect : public Dialect {
BuiltinDialect(MLIRContext *context)
: Dialect(/*name=*/"", context, TypeID::get<BuiltinDialect>()) {
addTypes<ComplexType, BFloat16Type, Float16Type, Float32Type, Float64Type,
FunctionType, IndexType, IntegerType, MemRefType,
UnrankedMemRefType, NoneType, OpaqueType, RankedTensorType,
TupleType, UnrankedTensorType, VectorType>();
addAttributes<AffineMapAttr, ArrayAttr, DenseIntOrFPElementsAttr,
DenseStringElementsAttr, DictionaryAttr, FloatAttr,
SymbolRefAttr, IntegerAttr, IntegerSetAttr, OpaqueAttr,
OpaqueElementsAttr, SparseElementsAttr, StringAttr, TypeAttr,
UnitAttr>();
addAttributes<CallSiteLoc, FileLineColLoc, FusedLoc, NameLoc, OpaqueLoc,
UnknownLoc>();
addInterfaces<BuiltinOpAsmDialectInterface>();
// TODO: These operations should be moved to a different dialect when they
// have been fully decoupled from the core.
addOperations<FuncOp, ModuleOp, ModuleTerminatorOp>();
}
static StringRef getDialectNamespace() { return ""; }
};
} // end anonymous namespace.
//===----------------------------------------------------------------------===//
// Locking Utilities
//===----------------------------------------------------------------------===//

View File

@ -1,104 +0,0 @@
//===- Module.cpp - MLIR Module Operation ---------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "mlir/IR/Module.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/OpImplementation.h"
using namespace mlir;
//===----------------------------------------------------------------------===//
// Module Operation.
//===----------------------------------------------------------------------===//
void ModuleOp::build(OpBuilder &builder, OperationState &result,
Optional<StringRef> name) {
ensureTerminator(*result.addRegion(), builder, result.location);
if (name)
result.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) {
OperationState state(loc, "module");
OpBuilder builder(loc->getContext());
ModuleOp::build(builder, state, name);
return cast<ModuleOp>(Operation::create(state));
}
ParseResult ModuleOp::parse(OpAsmParser &parser, OperationState &result) {
// If the name is present, parse it.
StringAttr nameAttr;
(void)parser.parseOptionalSymbolName(
nameAttr, mlir::SymbolTable::getSymbolAttrName(), result.attributes);
// If module attributes are present, parse them.
if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
return failure();
// Parse the module body.
auto *body = result.addRegion();
if (parser.parseRegion(*body, llvm::None, llvm::None))
return failure();
// Ensure that this module has a valid terminator.
ensureTerminator(*body, parser.getBuilder(), result.location);
return success();
}
void ModuleOp::print(OpAsmPrinter &p) {
p << "module";
if (Optional<StringRef> name = getName()) {
p << ' ';
p.printSymbolName(*name);
}
// Print the module attributes.
p.printOptionalAttrDictWithKeyword(getAttrs(),
{mlir::SymbolTable::getSymbolAttrName()});
// Print the region.
p.printRegion(getOperation()->getRegion(0), /*printEntryBlockArgs=*/false,
/*printBlockTerminators=*/false);
}
LogicalResult ModuleOp::verify() {
auto &bodyRegion = getOperation()->getRegion(0);
// The body must contain a single basic block.
if (!llvm::hasSingleElement(bodyRegion))
return emitOpError("expected body region to have a single block");
// Check that none of the attributes are non-dialect attributes, except for
// the symbol related attributes.
for (auto attr : getOperation()->getMutableAttrDict().getAttrs()) {
if (!attr.first.strref().contains('.') &&
!llvm::is_contained(
ArrayRef<StringRef>{mlir::SymbolTable::getSymbolAttrName(),
mlir::SymbolTable::getVisibilityAttrName()},
attr.first.strref()))
return emitOpError(
"can only contain dialect-specific attributes, found: '")
<< attr.first << "'";
}
return success();
}
/// Return body of this module.
Region &ModuleOp::getBodyRegion() { return getOperation()->getRegion(0); }
Block *ModuleOp::getBody() { return &getBodyRegion().front(); }
Optional<StringRef> ModuleOp::getName() {
if (auto nameAttr =
getAttrOfType<StringAttr>(mlir::SymbolTable::getSymbolAttrName()))
return nameAttr.getValue();
return llvm::None;
}

View File

@ -303,9 +303,10 @@ void OpClass::writeDeclTo(raw_ostream &os) const {
os << "class " << className << " : public ::mlir::Op<" << className;
for (const auto &trait : traitsVec)
os << ", " << trait;
os << "> {\npublic:\n";
os << " using Op::Op;\n";
os << " using Adaptor = " << className << "Adaptor;\n";
os << "> {\npublic:\n"
<< " using Op::Op;\n"
<< " using Op::print;\n"
<< " using Adaptor = " << className << "Adaptor;\n";
bool hasPrivateMethod = false;
forAllMethods([&](const OpMethod &method) {