diff --git a/mlir/include/mlir/IR/BuiltinDialect.h b/mlir/include/mlir/IR/BuiltinDialect.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/IR/BuiltinDialect.h @@ -0,0 +1,94 @@ +//===- 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 { +public: + using OwningOpRefBase::OwningOpRefBase; +}; +} // end namespace mlir + +namespace llvm { +// Functions hash just like pointers. +template <> +struct DenseMapInfo { + static mlir::FuncOp getEmptyKey() { + auto pointer = llvm::DenseMapInfo::getEmptyKey(); + return mlir::FuncOp::getFromOpaquePointer(pointer); + } + static mlir::FuncOp getTombstoneKey() { + auto pointer = llvm::DenseMapInfo::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 { +public: + static inline void *getAsVoidPointer(mlir::FuncOp I) { + return const_cast(I.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 { +public: + static inline void *getAsVoidPointer(mlir::ModuleOp I) { + return const_cast(I.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_ diff --git a/mlir/include/mlir/IR/BuiltinOps.td b/mlir/include/mlir/IR/BuiltinOps.td new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/IR/BuiltinOps.td @@ -0,0 +1,229 @@ +//===- 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 traits = []> : + Op; + +//===----------------------------------------------------------------------===// +// 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) -> 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:$sym_visibility); + let regions = (region AnyRegion:$body); + + let builders = [OpBuilderDAG<(ins + "StringRef":$name, "FunctionType":$type, + CArg<"ArrayRef", "{}">:$attrs, + CArg<"ArrayRef", "{}">:$argAttrs) + >]; + let extraClassDeclaration = [{ + static FuncOp create(Location location, StringRef name, FunctionType type, + ArrayRef attrs = {}); + static FuncOp create(Location location, StringRef name, FunctionType type, + iterator_range attrs); + static FuncOp create(Location location, StringRef name, FunctionType type, + ArrayRef attrs, + ArrayRef 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 getCallableResults() { return getType().getResults(); } + + private: + // This trait needs access to the hooks defined below. + friend class OpTrait::FunctionLike; + + /// 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()) + 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:$sym_name, + OptionalAttr:$sym_visibility); + let regions = (region SizedRegion<1>:$body); + + let assemblyFormat = "($sym_name^)? attr-dict-with-keyword $body"; + let builders = [OpBuilderDAG<(ins CArg<"Optional", "{}">:$name)>]; + let extraClassDeclaration = [{ + /// Construct a module from the given location with an optional name. + static ModuleOp create(Location loc, Optional name = llvm::None); + + /// Return the name of this module if present. + Optional 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 diff --git a/mlir/include/mlir/IR/CMakeLists.txt b/mlir/include/mlir/IR/CMakeLists.txt --- a/mlir/include/mlir/IR/CMakeLists.txt +++ b/mlir/include/mlir/IR/CMakeLists.txt @@ -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/) diff --git a/mlir/include/mlir/IR/Function.h b/mlir/include/mlir/IR/Function.h --- a/mlir/include/mlir/IR/Function.h +++ b/mlir/include/mlir/IR/Function.h @@ -13,131 +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 { -public: - using Op::Op; - using Op::print; - - static StringRef getOperationName() { return "func"; } - - static FuncOp create(Location location, StringRef name, FunctionType type, - ArrayRef attrs = {}); - static FuncOp create(Location location, StringRef name, FunctionType type, - iterator_range attrs); - static FuncOp create(Location location, StringRef name, FunctionType type, - ArrayRef attrs, - ArrayRef argAttrs); - - static void build(OpBuilder &builder, OperationState &result, StringRef name, - FunctionType type, ArrayRef attrs = {}, - ArrayRef 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 getCallableResults() { return getType().getResults(); } - -private: - // This trait needs access to the hooks defined below. - friend class OpTrait::FunctionLike; - - /// 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()) - return emitOpError("requires '" + getTypeAttrName() + - "' attribute of function type"); - return success(); - } -}; -} // end namespace mlir - -namespace llvm { - -// Functions hash just like pointers. -template <> struct DenseMapInfo { - static mlir::FuncOp getEmptyKey() { - auto pointer = llvm::DenseMapInfo::getEmptyKey(); - return mlir::FuncOp::getFromOpaquePointer(pointer); - } - static mlir::FuncOp getTombstoneKey() { - auto pointer = llvm::DenseMapInfo::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 { -public: - static inline void *getAsVoidPointer(mlir::FuncOp I) { - return const_cast(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 diff --git a/mlir/include/mlir/IR/Module.h b/mlir/include/mlir/IR/Module.h --- a/mlir/include/mlir/IR/Module.h +++ b/mlir/include/mlir/IR/Module.h @@ -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::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 name = llvm::None); - - /// Construct a module from the given location with an optional name. - static ModuleOp create(Location loc, Optional 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 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 iterator_range> getOps() { - return getBody()->getOps(); - } - - /// 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::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 { -public: - using OwningOpRefBase::OwningOpRefBase; -}; - -} // end namespace mlir - -namespace llvm { - -/// Allow stealing the low bits of ModuleOp. -template <> struct PointerLikeTypeTraits { -public: - static inline void *getAsVoidPointer(mlir::ModuleOp I) { - return const_cast(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 diff --git a/mlir/include/mlir/IR/OpDefinition.h b/mlir/include/mlir/IR/OpDefinition.h --- a/mlir/include/mlir/IR/OpDefinition.h +++ b/mlir/include/mlir/IR/OpDefinition.h @@ -786,6 +786,132 @@ : public detail::MultiSuccessorTraitBase { }; +//===----------------------------------------------------------------------===// +// SingleBlockImplicitTerminator + +/// This class provides APIs and verifiers for ops with regions having a single +/// block that must terminate with `TerminatorOpType`. +template +struct SingleBlockImplicitTerminator { + template + class Impl : public TraitBase { + 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 ®ion = 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(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 ®ion, Builder &builder, + Location loc) { + ::mlir::impl::ensureRegionTerminator(region, builder, loc, + buildTerminator); + } + static void ensureTerminator(Region ®ion, OpBuilder &builder, + Location loc) { + ::mlir::impl::ensureRegionTerminator(region, builder, loc, + buildTerminator); + } + + Block *getBody(unsigned idx = 0) { + Region ®ion = this->getOperation()->getRegion(idx); + assert(!region.empty() && "unexpected empty region"); + return ®ion.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 + using enable_if_single_region = + typename std::enable_if_t(), T>; + + template + enable_if_single_region begin() { + return getBody()->begin(); + } + template + enable_if_single_region end() { + return getBody()->end(); + } + template + enable_if_single_region front() { + return *begin(); + } + + /// Insert the operation into the back of the body, before the terminator. + template + enable_if_single_region 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 + enable_if_single_region insert(Operation *insertPt, Operation *op) { + insert(Block::iterator(insertPt), op); + } + template + enable_if_single_region 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 @@ } }; -/// This class provides APIs and verifiers for ops with regions having a single -/// block that must terminate with `TerminatorOpType`. -template struct SingleBlockImplicitTerminator { - template - class Impl : public TraitBase { - 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 ®ion = 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(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 ®ion, Builder &builder, - Location loc) { - ::mlir::impl::ensureRegionTerminator(region, builder, loc, - buildTerminator); - } - static void ensureTerminator(Region ®ion, OpBuilder &builder, - Location loc) { - ::mlir::impl::ensureRegionTerminator(region, builder, loc, - buildTerminator); - } - - Block *getBody(unsigned idx = 0) { - Region ®ion = this->getOperation()->getRegion(idx); - assert(!region.empty() && "unexpected empty region"); - return ®ion.front(); - } - }; -}; - /// This class provides a verifier for ops that are expecting their parent /// to be one of the given parent ops template diff --git a/mlir/lib/IR/Function.cpp b/mlir/lib/IR/BuiltinDialect.cpp rename from mlir/lib/IR/Function.cpp rename to mlir/lib/IR/BuiltinDialect.cpp --- a/mlir/lib/IR/Function.cpp +++ b/mlir/lib/IR/BuiltinDialect.cpp @@ -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()) { + os << "map"; + return success(); + } + if (attr.isa()) { + os << "set"; + return success(); + } + if (attr.isa()) { + 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(); + addAttributes(); + addAttributes(); + addOperations< +#define GET_OP_LIST +#include "mlir/IR/BuiltinOps.cpp.inc" + >(); + addInterfaces(); +} + +//===----------------------------------------------------------------------===// +// FuncOp //===----------------------------------------------------------------------===// FuncOp FuncOp::create(Location location, StringRef name, FunctionType type, @@ -41,14 +87,14 @@ return func; } -void FuncOp::build(OpBuilder &builder, OperationState &result, StringRef name, +void FuncOp::build(OpBuilder &builder, OperationState &state, StringRef name, FunctionType type, ArrayRef attrs, ArrayRef 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 @@ 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 argTypes, ArrayRef results, impl::VariadicFlag, std::string &) { @@ -72,25 +116,25 @@ 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 @@ BlockAndValueMapping mapper; return clone(mapper); } + +//===----------------------------------------------------------------------===// +// ModuleOp +//===----------------------------------------------------------------------===// + +void ModuleOp::build(OpBuilder &builder, OperationState &state, + Optional 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 name) { + OpBuilder builder(loc->getContext()); + return builder.create(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{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" diff --git a/mlir/lib/IR/CMakeLists.txt b/mlir/lib/IR/CMakeLists.txt --- a/mlir/lib/IR/CMakeLists.txt +++ b/mlir/lib/IR/CMakeLists.txt @@ -5,16 +5,15 @@ 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 @@ ${MLIR_MAIN_INCLUDE_DIR}/mlir/IR DEPENDS + MLIRBuiltinOpsIncGen MLIRCallInterfacesIncGen MLIROpAsmInterfaceIncGen - MLIRSymbolInterfacesIncGen MLIRRegionKindInterfaceIncGen + MLIRSymbolInterfacesIncGen LINK_LIBS PUBLIC MLIRSupport diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp --- a/mlir/lib/IR/MLIRContext.cpp +++ b/mlir/lib/IR/MLIRContext.cpp @@ -82,57 +82,6 @@ *clOptions; } -//===----------------------------------------------------------------------===// -// Builtin Dialect -//===----------------------------------------------------------------------===// - -namespace { -struct BuiltinOpAsmDialectInterface : public OpAsmDialectInterface { - using OpAsmDialectInterface::OpAsmDialectInterface; - - LogicalResult getAlias(Attribute attr, raw_ostream &os) const override { - if (attr.isa()) { - os << "map"; - return success(); - } - if (attr.isa()) { - os << "set"; - return success(); - } - if (attr.isa()) { - 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()) { - addTypes(); - addAttributes(); - addAttributes(); - addInterfaces(); - - // TODO: These operations should be moved to a different dialect when they - // have been fully decoupled from the core. - addOperations(); - } - static StringRef getDialectNamespace() { return ""; } -}; -} // end anonymous namespace. - //===----------------------------------------------------------------------===// // Locking Utilities //===----------------------------------------------------------------------===// diff --git a/mlir/lib/IR/Module.cpp b/mlir/lib/IR/Module.cpp deleted file mode 100644 --- a/mlir/lib/IR/Module.cpp +++ /dev/null @@ -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 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 name) { - OperationState state(loc, "module"); - OpBuilder builder(loc->getContext()); - ModuleOp::build(builder, state, name); - return cast(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 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{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 ModuleOp::getName() { - if (auto nameAttr = - getAttrOfType(mlir::SymbolTable::getSymbolAttrName())) - return nameAttr.getValue(); - return llvm::None; -} diff --git a/mlir/lib/TableGen/OpClass.cpp b/mlir/lib/TableGen/OpClass.cpp --- a/mlir/lib/TableGen/OpClass.cpp +++ b/mlir/lib/TableGen/OpClass.cpp @@ -303,9 +303,10 @@ 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) {