diff --git a/mlir/include/mlir/Dialect/CMakeLists.txt b/mlir/include/mlir/Dialect/CMakeLists.txt --- a/mlir/include/mlir/Dialect/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(Func) add_subdirectory(GPU) add_subdirectory(Index) +add_subdirectory(IRDL) add_subdirectory(LLVMIR) add_subdirectory(Linalg) add_subdirectory(MLProgram) diff --git a/mlir/include/mlir/Dialect/IRDL/AttributeWrapper.h b/mlir/include/mlir/Dialect/IRDL/AttributeWrapper.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/AttributeWrapper.h @@ -0,0 +1,153 @@ +//===- AttributeWrapper.h - IRDL type wrapper definition --------*- 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 declares wrappers around attribute and type definitions to +// manipulate them in a unified way, using their names and a list of parameters +// encoded as a list of attributes. These wrappers are necessary for IRDL, since +// attributes and types don't have names, nor a way to interact with them in a +// generic way. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_IRDL_ATTRIBUTEWRAPPER_H_ +#define MLIR_DIALECT_IRDL_ATTRIBUTEWRAPPER_H_ + +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/ExtensibleDialect.h" +#include "mlir/IR/OpDefinition.h" +#include "llvm/ADT/SmallString.h" + +namespace mlir { +namespace irdl { + +/// A wrapper around an attribute definition to manipulate its attribute +/// instances in a unified way, using a name and a list of parameters encoded as +/// attributes. +/// +/// If the attribute is defined in C++, CppAttributeWrapper should be preferred. +class AttributeWrapper { +public: + virtual ~AttributeWrapper(){}; + + /// Return the unique identifier of the attribute definition. + virtual TypeID getTypeID() = 0; + + /// Check if the given attribute is an instance of the one wrapped. + virtual bool isCorrectAttribute(mlir::Attribute attr) = 0; + + /// Get the parameters of an attribute. The attribute is expected to be an + /// instance of the wrapped attribute, which is checked with + /// `isCorrectAttribute`. + virtual llvm::SmallVector + getAttributeParameters(mlir::Attribute attr) = 0; + + /// Return the attribute definition name, including the dialect prefix. + virtual llvm::StringRef getName() = 0; + + /// Instantiate the attribute from parameters. + /// It is expected that the amount of parameters is correct, which is checked + /// with `getParameterAmount`. + virtual Attribute + instantiateAttribute(llvm::function_ref emitError, + llvm::ArrayRef parameters) = 0; + + /// Return the amount of parameters the attribute expects. + virtual size_t getParameterAmount() = 0; +}; + +/// A wrapper around a type definition to manipulate its type instances +/// in an unified way, using a name and a list of parameters encoded as +/// attributes. The wrappers also acts as an attribute wrapper, and expects +/// the type to be nested in a `TypeAttr`. +/// +/// If the type is defined as a C++ class, CppTypeWrapper should be preferred. +class TypeWrapper : public AttributeWrapper { +public: + virtual ~TypeWrapper(){}; + + /// Check if the given type is an instance of the one wrapped. + virtual bool isCorrectType(mlir::Type t) = 0; + + /// Get the parameters of a type. The type is expected to be an instance of + /// the wrapped type, which is checked with `isCorrectType`. + virtual llvm::SmallVector + getTypeParameters(mlir::Type t) = 0; + + /// Instantiate the type from parameters. + /// It is expected that the amount of parameters is correct, which is checked + /// with `getParameterAmount`. + virtual Type + instantiateType(llvm::function_ref emitError, + llvm::ArrayRef parameters) = 0; + + /// Check if the given attribute is a `TypeAttr`, and that it contains an + /// instance of the type wrapped. + bool isCorrectAttribute(mlir::Attribute attr) override; + + /// Get the parameters of a type. The type is expected to be nested in a + /// `TypeAttr`, and to be an instance of the wrapped type, which is checked + /// with `isCorrectType`. + llvm::SmallVector + getAttributeParameters(mlir::Attribute attr) override; + + /// Instantiate the type from parameters, and wrap it in a `TypeAttr`. + /// It is expected that the amount of parameters is correct, which is checked + /// with `getParameterAmount`. + Attribute + instantiateAttribute(llvm::function_ref emitError, + llvm::ArrayRef parameters) override; +}; + +using AttributeWrapperPtr = AttributeWrapper *; +using TypeWrapperPtr = TypeWrapper *; + +/// A wrapper around an attribute definition defined with a C++ class to +/// manipulate its attribute instances in a unified way, using a name and a list +/// of parameters encoded as attributes. +template +class CppAttributeWrapper : public AttributeWrapper { +public: + TypeID getTypeID() override { return TypeID::get(); } + + /// Get the parameters of an attribute. + virtual llvm::SmallVector getAttributeParameters(A attr) = 0; + + llvm::SmallVector + getAttributeParameters(mlir::Attribute attr) override { + return getAttributeParameters(attr.cast()); + }; + + bool isCorrectAttribute(mlir::Attribute attr) override { + return attr.isa(); + } +}; + +/// A wrapper around a type definition defined with a C++ class to +/// manipulate its type instances in a unified way, using a name and a list +/// of parameters encoded as attributes. +template +class CppTypeWrapper : public TypeWrapper { +public: + TypeID getTypeID() override { return TypeID::get(); } + + /// Get the parameters of a type. + virtual llvm::SmallVector getTypeParameters(T t) = 0; + + llvm::SmallVector + getTypeParameters(mlir::Type type) override { + return getTypeParameters(type.cast()); + }; + + bool isCorrectType(mlir::Type type) override { return type.isa(); } +}; + +} // namespace irdl +} // namespace mlir + +#endif // MLIR_DIALECT_IRDL_ATTRIBUTEWRAPPER_H_ diff --git a/mlir/include/mlir/Dialect/IRDL/CMakeLists.txt b/mlir/include/mlir/Dialect/IRDL/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(IR) diff --git a/mlir/include/mlir/Dialect/IRDL/IR/CMakeLists.txt b/mlir/include/mlir/Dialect/IRDL/IR/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/IR/CMakeLists.txt @@ -0,0 +1,22 @@ +add_mlir_dialect(IRDL irdl) + +# Add IRDL operations +set(LLVM_TARGET_DEFINITIONS IRDLOps.td) +mlir_tablegen(IRDLOps.h.inc -gen-op-decls) +mlir_tablegen(IRDLOps.cpp.inc -gen-op-defs) +add_public_tablegen_target(MLIRIRDLOpsIncGen) +add_dependencies(mlir-generic-headers MLIRIRDLOpsIncGen) + +# Add IRDL types +set(LLVM_TARGET_DEFINITIONS IRDLTypes.td) +mlir_tablegen(IRDLTypesGen.h.inc -gen-typedef-decls) +mlir_tablegen(IRDLTypesGen.cpp.inc -gen-typedef-defs) +add_public_tablegen_target(MLIRIRDLTypesIncGen) +add_dependencies(mlir-generic-headers MLIRIRDLTypesIncGen) + +# Add IRDL attributes +set(LLVM_TARGET_DEFINITIONS IRDLAttributes.td) +mlir_tablegen(IRDLAttributes.h.inc -gen-attrdef-decls) +mlir_tablegen(IRDLAttributes.cpp.inc -gen-attrdef-defs) +add_public_tablegen_target(MLIRIRDLAttributesIncGen) +add_dependencies(mlir-generic-headers MLIRIRDLAttributesIncGen) diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDL.h b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.h @@ -0,0 +1,54 @@ +//===- IRDL.h - IR Definition Language 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 declares the dialect for the IR Definition Language. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_IRDL_IR_IRDL_H_ +#define MLIR_DIALECT_IRDL_IR_IRDL_H_ + +#include "mlir/Dialect/IRDL/AttributeWrapper.h" +#include "mlir/Dialect/IRDL/IR/IRDLTraits.h" +#include "mlir/Dialect/IRDL/IRDLContext.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/ExtensibleDialect.h" +#include "mlir/IR/OpDefinition.h" +#include "mlir/IR/SymbolTable.h" +#include "mlir/Interfaces/InferTypeOpInterface.h" +#include "mlir/Interfaces/SideEffectInterfaces.h" +#include "llvm/ADT/TypeSwitch.h" +#include + +// Forward declaration. +namespace mlir { +namespace irdl { +class OpDef; +class OpDefAttr; +} // namespace irdl +} // namespace mlir + +//===----------------------------------------------------------------------===// +// IRDL Dialect +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/IRDL/IR/IRDLDialect.h.inc" + +#define GET_TYPEDEF_CLASSES +#include "mlir/Dialect/IRDL/IR/IRDLTypesGen.h.inc" + +#define GET_ATTRDEF_CLASSES +#include "mlir/Dialect/IRDL/IR/IRDLAttributes.h.inc" + +#define GET_OP_CLASSES +#include "mlir/Dialect/IRDL/IR/IRDLOps.h.inc" + +#endif // MLIR_DIALECT_IRDL_IR_IRDL_H_ diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td @@ -0,0 +1,81 @@ +//===- IRDL.td - IR Definition Language Dialect ------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares the IR Definition Language dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_IRDL_IR_IRDL +#define MLIR_DIALECT_IRDL_IR_IRDL + +include "mlir/IR/OpBase.td" + +//===----------------------------------------------------------------------===// +// IRDL Dialect +//===----------------------------------------------------------------------===// + +def IRDL_Dialect : Dialect { + let summary = "IR Definition Language Dialect"; + let description = [{ + IRDL is an SSA-based declarative representation of dynamic dialects. + It allows the definition of dialects, operations, attributes, and types, + with a declarative description of their verifiers. IRDL code is meant to + be generated and not written by hand. As such, the design focuses on ease + of generation/analysis instead of ease of writing/reading. + + Users can define a new dialect with `irdl.dialect`, operations with + `irdl.operation`, types with `irdl.type`, and attributes with + `irdl.attribute`. + + An example dialect is shown below: + + ```mlir + irdl.dialect cmath { + irdl.type complex { + %0 = irdl.is_type : f32 + %1 = irdl.is_type : f64 + %2 = irdl.any_of(%0, %1) + irdl.parameters(%2) + } + + irdl.operation mul { + %0 = irdl.is_type : f32 + %1 = irdl.is_type : f64 + %2 = irdl.any_of(%0, %1) + %3 = irdl.parametric_type : "cmath.complex"<%2> + irdl.operands(%3, %3) + irdl.results(%3) + } + } + ``` + + This program defines a `cmath` dialect that defines a `complex` type, and + a `mul` operation. Both express constraints over their parameters using + SSA constraint operations. Informally, one can see those SSA values as + constraint variables that evaluate to a single type at constraint + evaluation. For example, the result of the `irdl.any_of` stored in `%2` + in the `mul` operation will collapse into either `f32` or `f64` for the + entirety of this instance of `mul` constraint evaluation. As such, + both operands and the result of `mul` must be of equal type (and not just + satisfy the same constraint). + }]; + + let useDefaultTypePrinterParser = 1; + let useDefaultAttributePrinterParser = 1; + + let extraClassDeclaration = [{ + public: + /// Contains registered attribute and type wrappers. + ::mlir::irdl::IRDLContext irdlContext; + }]; + + let name = "irdl"; + let cppNamespace = "::mlir::irdl"; +} + +#endif // MLIR_DIALECT_IRDL_IR_IRDL diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLAttributes.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDLAttributes.td new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLAttributes.td @@ -0,0 +1,54 @@ +//===- IRDLAttributes.td - IR Definition Language Dialect --*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares the attributes used in IRDL. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_IRDL_IR_IRDLATTRIBUTES +#define MLIR_DIALECT_IRDL_IR_IRDLATTRIBUTES + +include "IRDL.td" +include "mlir/IR/AttrTypeBase.td" + +class IRDL_AttrDef traits = []> + : AttrDef { + let mnemonic = attrMnemonic; +} + +def TypeOrAttrDefRefAttr : IRDL_AttrDef<"TypeOrAttrDefRef", + "type_or_attr_def_ref"> { + let summary = "reference to a type or attribute definition"; + let description = [{ + An `irdl.type_or_attr_def_ref` references a type or attribute definition. + A type or attribute definition can either be a `TypeWrapper *` or + an `AttrWrapper *`, which refers to a C++-defined type or attribute, + or a `SymbolRefAttr` which refers to a type or attribute defined with IRDL. + }]; + let parameters = + (ins OptionalParameter<"mlir::irdl::TypeWrapperPtr">:$typeWrapper, + OptionalParameter<"mlir::irdl::AttributeWrapperPtr">:$attrWrapper, + OptionalParameter<"mlir::SymbolRefAttr">:$symRef); + + let builders = [ + AttrBuilder<(ins "mlir::irdl::TypeWrapper*":$typeWrapper), [{ + return $_get($_ctxt, typeWrapper, nullptr, mlir::SymbolRefAttr()); + }]>, + AttrBuilder<(ins "mlir::irdl::AttributeWrapper*":$attrWrapper), [{ + return $_get($_ctxt, nullptr, attrWrapper, mlir::SymbolRefAttr()); + }]>, + AttrBuilderWithInferredContext<(ins "mlir::SymbolRefAttr":$symRef), [{ + return $_get(symRef.getContext(), nullptr, nullptr, symRef); + }]> + ]; + + let genVerifyDecl = 1; + let hasCustomAssemblyFormat = 1; +} + +#endif // MLIR_DIALECT_IRDL_IR_IRDLATTRIBUTES diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td @@ -0,0 +1,433 @@ +//===- IRDLOps.td - IR Definition Language Dialect ---------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares the IRDL dialect ops. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_IRDL_IR_IRDLOPS +#define MLIR_DIALECT_IRDL_IR_IRDLOPS + +include "IRDL.td" +include "IRDLTypes.td" +include "IRDLAttributes.td" +include "mlir/Interfaces/SideEffectInterfaces.td" +include "mlir/Interfaces/InferTypeOpInterface.td" +include "mlir/IR/SymbolInterfaces.td" + +class IRDL_Op traits = []> + : Op { +} + +class AtMostOneChildOf : ParamNativeOpTrait<"AtMostOneChildOf", op>; + +//===----------------------------------------------------------------------===// +// Dialect definition +//===----------------------------------------------------------------------===// + +def IRDL_DialectOp : IRDL_Op<"dialect", + [IsolatedFromAbove, NoTerminator, Symbol, SymbolTable]> { + let summary = "Define a new dialect"; + let description = [{ + The `irdl.dialect` operation defines a dialect. All operations, attributes, + and types defined inside its region will be part of the dialect. + + Example: + + ```mlir + irdl.dialect cmath { + ... + } + ``` + + The above program defines a `cmath` dialect. + }]; + + let arguments = (ins SymbolNameAttr:$sym_name); + let regions = (region SizedRegion<1>:$body); + let assemblyFormat = + "$sym_name attr-dict-with-keyword custom($body)"; + let hasVerifier = 1; +} + +//===----------------------------------------------------------------------===// +// Type and Attribute definition +//===----------------------------------------------------------------------===// + +def IRDL_TypeOp : IRDL_Op<"type", + [HasParent<"DialectOp">, NoTerminator, NoRegionArguments, + AtMostOneChildOf<"ParametersOp">, Symbol]> { + let summary = "Define a new type"; + let description = [{ + `irdl.type` defines a new type belonging to the `irdl.dialect` parent. + + The type parameters can be defined with an `irdl.parameters` operation in + the optional region. + + Example: + + ```mlir + irdl.dialect @cmath { + irdl.type @complex { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.any_of(%0, %1) + irdl.parameters(%2) + } + } + ``` + + The above program defines a type `complex` inside the dialect `cmath`. The + type has a single parameter that should be either `i32` or `i64`. + }]; + + let arguments = (ins SymbolNameAttr:$sym_name); + let regions = (region SizedRegion<1>:$body); + let assemblyFormat = + "$sym_name attr-dict-with-keyword custom($body)"; + + let extraClassDeclaration = [{ + /// Get the parent dialect operation. + DialectOp getDialectOp(); + }]; +} + +def IRDL_AttributeOp : IRDL_Op<"attribute", + [HasParent<"DialectOp">, NoTerminator, NoRegionArguments, + AtMostOneChildOf<"ParametersOp">, Symbol]> { + let summary = "Define a new attribute"; + let description = [{ + `irdl.attribute` defines a new attribute belonging to the `irdl.dialect` + parent. + + The attribute parameters can be defined with an `irdl.parameters` operation + in the optional region. + + Example: + + ```mlir + irdl.dialect @testd { + irdl.attribute @enum_attr { + %0 = irdl.is "foo" + %1 = irdl.is "bar" + %2 = irdl.any_of(%0, %1) + irdl.parameters(%2) + } + } + ``` + + The above program defines an `enum_attr` attribute inside the `testd` + dialect. The attribute has one `StringAttr` parameter that should be + either a `"foo"` or a `"bar"`. + }]; + + let arguments = (ins SymbolNameAttr:$sym_name); + let regions = (region SizedRegion<1>:$body); + let assemblyFormat = + "$sym_name attr-dict-with-keyword custom($body)"; + + let extraClassDeclaration = [{ + /// Get the parent dialect operation. + DialectOp getDialectOp(); + }]; +} + +def IRDL_ParametersOp : IRDL_Op<"parameters", + [ParentOneOf<["AttributeOp", "TypeOp"]>]> { + let summary = + "Define the constraints on parameters of a type/attribute definition"; + let description = [{ + `irdl.parameters` defines the constraints on parameters of a type or + attribute definition. + + Example: + + ```mlir + irdl.dialect @cmath { + irdl.type @complex { + %0 = irdl.is i32 + %1 = irdl.is i64 + irdl.parameters(%2) + } + } + ``` + + The above program defines a type `complex` inside the dialect `cmath`. The + type has a single parameter that should be either `i32` or `i64`. + }]; + + let arguments = (ins Variadic:$args); + let assemblyFormat = [{ `(` $args `)` attr-dict }]; +} + +//===----------------------------------------------------------------------===// +// IRDL Operation definition +//===----------------------------------------------------------------------===// + +def IRDL_OperationOp : IRDL_Op<"operation", + [HasParent<"DialectOp">, NoTerminator, NoRegionArguments, + AtMostOneChildOf<"OperandsOp, ResultsOp">, Symbol]> { + let summary = "Define a new operation"; + let description = [{ + `irdl.operation` defines a new operation belonging to the `irdl.dialect` + parent. + + Operations can define constraints on their operands and results with the + `irdl.results` and `irdl.operands` operations. If these operations are not + present in the region, the results or operands are expected to be empty. + + Example: + + ```mlir + irdl.dialect @cmath { + irdl.operation @norm { + %0 = irdl.any + %1 = irdl.parametric !cmath.complex<%0> + irdl.results(%0) + irdl.operands(%1) + } + } + ``` + + The above program defines an operation `norm` inside the dialect `cmath`. + The operation expects a single operand of base type `cmath.complex`, and + returns a single result of the element type of the operand. + }]; + + let arguments = (ins SymbolNameAttr:$sym_name); + let regions = (region SizedRegion<1>:$body); + let assemblyFormat = + "$sym_name attr-dict-with-keyword custom($body)"; + + let extraClassDeclaration = [{ + /// Get the parent dialect operation. + DialectOp getDialectOp() { return cast(getOperation()->getParentOp()); }; + }]; +} + +def IRDL_OperandsOp : IRDL_Op<"operands", [HasParent<"OperationOp">]> { + let summary = "Define the operands of an operation"; + let description = [{ + `irdl.operands` define the operands of the `irdl.operation` parent operation + definition. + + In the following example, `irdl.operands` defines the operands of the + `norm` operation: + + ```mlir + irdl.dialect @cmath { + irdl.operation @mul { + %0 = irdl.any + %1 = irdl.parametric !cmath.complex<%0> + irdl.results(%1) + irdl.operands(%1, %1) + } + } + ``` + + The `mul` operation will expect two operands of type `cmath.complex`, that + have the same type, and return a result of the same type. + }]; + + let arguments = (ins Variadic:$args); + let assemblyFormat = [{ `(` $args `)` attr-dict }]; +} + +def IRDL_ResultsOp : IRDL_Op<"results", [HasParent<"OperationOp">]> { + let summary = "Define the results of an operation"; + let description = [{ + `irdl.results` define the results of the `irdl.operation` parent operation + definition. + + In the following example, `irdl.results` defines the results of the + `norm` operation: + + ```mlir + irdl.dialect @cmath { + irdl.operation @get_values { + %0 = irdl.any + %1 = irdl.parametric !cmath.complex<%0> + irdl.results(%0, %0) + irdl.operands(%1) + } + } + ``` + + The operation will expect one operand of the `cmath.complex` type, and two + results that have the underlying type of the `cmath.complex`. + }]; + + let arguments = (ins Variadic:$args); + let assemblyFormat = [{ `(` $args `)` attr-dict }]; +} + +//===----------------------------------------------------------------------===// +// IRDL Constraint operations +//===----------------------------------------------------------------------===// + +class IRDL_ConstraintOp traits = []> + : IRDL_Op { +} + +def IRDL_Is : IRDL_ConstraintOp<"is", + [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>, Pure]> { + let summary = "Constraints an attribute/type to be a specific attribute instance"; + let description = [{ + `irdl.is` defines a constraint that only accepts a specific instance of a + type or attribute. + + Example: + + ```mlir + irdl.dialect @cmath { + irdl.type @complex_i32 { + %0 = irdl.is i32 + irdl.parameters(%0) + } + } + ``` + + The above program defines a `complex_i32` type inside the dialect `cmath` + that can only have a `i32` as its parameter. + }]; + + let arguments = (ins AnyAttr:$expected); + let results = (outs IRDL_AttributeType:$output); + let assemblyFormat = [{ $expected ` ` attr-dict }]; +} + +def IRDL_Parametric : IRDL_ConstraintOp<"parametric", + [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>, Pure]> { + let summary = "Constraints an attribute/type base and its parameters"; + let description = [{ + `irdl.parametric` defines a constraint that accepts only a single type + or attribute base. The base is either a type/attribute defined in IRDL or a + type/attribute wrapper. It will additionally constraint the parameters of + the type/attribute. + + Example: + + ```mlir + irdl.dialect @cmath { + // ... + + irdl.operation @norm { + %0 = irdl.any + %1 = irdl.parametric "cmath.complex"<%0> + irdl.operands(%1) + irdl.results(%0) + } + } + ``` + + The above program defines an operation `norm` inside the dialect `cmath` that + for any `T` takes a `cmath.complex` with parameter `T` and returns a `T`. + }]; + + let arguments = (ins TypeOrAttrDefRefAttr:$base_type, + Variadic:$args); + let results = (outs IRDL_AttributeType:$output); + let assemblyFormat = [{ $base_type `<` $args `>` ` ` attr-dict }]; +} + +def IRDL_Any : IRDL_ConstraintOp<"any", + [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>]> { + let summary = "Accept any type or attribute"; + let description = [{ + `irdl.any` defines a constraint that accepts any type or attribute. + + Example: + + ```mlir + irdl.dialect @cmath { + irdl.type @complex_flexible { + %0 = irdl.any + irdl.parameters(%0) + } + } + ``` + + The above program defines a type `complex_flexible` inside the dialect + `cmath` that has a single parameter that can be any attribute. + }]; + + let results = (outs IRDL_AttributeType:$output); + let assemblyFormat = [{ ` ` attr-dict }]; +} + +def IRDL_AnyOf : IRDL_ConstraintOp<"any_of", + [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>, + SameOperandsAndResultType]> { + let summary = "Constraints to the union of the provided constraints"; + let description = [{ + `irdl.any_of` defines a constraint that accepts any type or attribute that + satisfies at least one of its provided type constraints. + + Example: + + ```mlir + irdl.dialect cmath { + irdl.type complex { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.is f32 + %3 = irdl.is f64 + %4 = irdl.any_of(%0, %1, %2, %3) + irdl.parameters(%4) + } + } + ``` + + The above program defines a type `complex` inside the dialect `cmath` that + can have a single type parameter that can be either `i32`, `i64`, `f32` or + `f32`. + }]; + + let arguments = (ins Variadic:$args); + let results = (outs IRDL_AttributeType:$output); + let assemblyFormat = [{ `(` $args `)` ` ` attr-dict }]; +} + +def IRDL_AllOf : IRDL_ConstraintOp<"all_of", + [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>, + SameOperandsAndResultType]> { + let summary = "Constraints to the intersection of the provided constraints"; + let description = [{ + `irdl.all_of` defines a constraint that accepts any type or attribute that + satisfies all of its provided constraints. + + Example: + + ```mlir + irdl.dialect cmath { + irdl.type complex_f32 { + %0 = irdl.is i32 + %1 = irdl.is f32 + %2 = irdl.any_of(%0, %1) // is 32-bit + + %3 = irdl.is f32 + %4 = irdl.is f64 + %5 = irdl.any_of(%3, %4) // is a float + + %6 = irdl.all_of(%2, %5) // is a 32-bit float + irdl.parameters(%6) + } + } + ``` + + The above program defines a type `complex` inside the dialect `cmath` that + can has one parameter that must be 32-bit long and a float (in other + words, that must be `f32`). + }]; + + let arguments = (ins Variadic:$args); + let results = (outs IRDL_AttributeType:$output); + let assemblyFormat = [{ `(` $args `)` ` ` attr-dict }]; +} + +#endif // MLIR_DIALECT_IRDL_IR_IRDLOPS diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLTraits.h b/mlir/include/mlir/Dialect/IRDL/IR/IRDLTraits.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLTraits.h @@ -0,0 +1,75 @@ +//===- IRDLTraits.h - IRDL traits definition ---------------------*- 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 declares the traits used by the IR Definition Language dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_IRDL_IR_IRDLTRAITS_H_ +#define MLIR_DIALECT_IRDL_IR_IRDLTRAITS_H_ + +#include "mlir/IR/OpDefinition.h" +#include "mlir/Support/LogicalResult.h" +#include "llvm/Support/Casting.h" + +namespace mlir { +namespace OpTrait { + +/// Characterize operations that have at most a single operation of certain +/// types in their region. +template +class AtMostOneChildOf { +public: + template + class Impl + : public ::mlir::OpTrait::TraitBase::Impl> { + private: + /// Return true if that there is at most one operation of type `ChildOp` + /// in the operation region. + template + static bool hasAtMostOneChildOf(Operation *op) { + auto ops = cast(op).template getOps(); + return ops.empty() || ++ops.begin() == ops.end(); + } + + public: + static LogicalResult verifyTrait(Operation *op) { + static_assert( + ConcreteType::template hasTrait<::mlir::OpTrait::OneRegion>(), + "expected operation to have a single region"); + + auto satisfiedOps = {hasAtMostOneChildOf(op)...}; + for (auto satisfiedOp : satisfiedOps) { + if (!satisfiedOp) { + op->emitError("failed to verify AtMostOneChildOf trait"); + return failure(); + } + } + return success(); + } + + /// Get the unique operation of a specific op that is in the operation + /// region. + template + std::enable_if_t...>::value, + llvm::Optional> + getOp() { + auto ops = + cast(this->getOperation()).template getOps(); + if (ops.empty()) + return {}; + return {*ops.begin()}; + } + }; +}; +} // namespace OpTrait +} // namespace mlir + +#endif // MLIR_DIALECT_IRDL_IR_IRDLTRAITS_H_ \ No newline at end of file diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLTypes.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDLTypes.td new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLTypes.td @@ -0,0 +1,48 @@ +//===- IRDLTypes.td - IRDL Types ---------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares the types IRDL uses. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_IRDL_IR_IRDLTYPES +#define MLIR_DIALECT_IRDL_IR_IRDLTYPES + +include "mlir/IR/AttrTypeBase.td" +include "IRDL.td" + +class IRDL_Type traits = []> + : TypeDef { + let mnemonic = typeMnemonic; +} + +def IRDL_AttributeType : IRDL_Type<"Attribute", "attribute"> { + let summary = "IRDL handle to an `mlir::Attribute`"; + let description = [{ + This type represents a handle to an instance of an `mlir::Attribute`, + so it can be used in an IRDL operation, type, or attribute definition. + + Example: + + ```mlir + irdl.dialect cmath { + irdl.type complex { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.any_of(%0, %1) + irdl.parameters(%2, %2) + } + } + ``` + + Here, `%0`, `%1` and `%2` are all of type `!irdl.attribute`. Since both + parameters refer to the same handle, they are expected to be equal. + }]; +} + +#endif // MLIR_DIALECT_IRDL_IR_IRDLTYPES diff --git a/mlir/include/mlir/Dialect/IRDL/IRDLContext.h b/mlir/include/mlir/Dialect/IRDL/IRDLContext.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/IRDLContext.h @@ -0,0 +1,74 @@ +//===- IRDLContext.h - IRDL context -----------------------------*- C++ -*-===// +// +// This file is licensed 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 +// +//===----------------------------------------------------------------------===// +// +// Manages the registration context of IRDL dialects. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_IRDL_IRDLCONTEXT_H_ +#define MLIR_DIALECT_IRDL_IRDLCONTEXT_H_ + +#include "mlir/Dialect/IRDL/AttributeWrapper.h" +#include "llvm/ADT/StringMap.h" + +namespace mlir { +namespace irdl { + +/// Context for the runtime registration of IRDL dialect definitions. +/// This class keeps track of all the attribute and types defined in C++ +/// that can be used in IRDL. +class IRDLContext { + llvm::StringMap> attributes; + llvm::StringMap> types; + llvm::DenseMap typeIDToAttributeWrapper; + llvm::DenseMap typeIDToTypeWrapper; + +public: + IRDLContext(); + + /// Add a concrete attribute wrapper to IRDL. + /// The attribute definition wrapped can then be used in IRDL with its name. + template + void addAttributeWrapper() { + addAttributeWrapper(std::make_unique()); + } + + /// Add an attribute wrapper to IRDL. + /// The attribute definition wrapped can then be used in IRDL with its name. + void addAttributeWrapper(std::unique_ptr wrapper); + + AttributeWrapper *getAttributeWrapper(StringRef typeName); + AttributeWrapper *getAttributeWrapper(TypeID typeID); + + /// Add a concrete type wrapper to IRDL. + /// The type definition wrapped can then be used in IRDL with its name. + template + void addTypeWrapper() { + addTypeWrapper(std::make_unique()); + } + + /// Add a type wrapper to IRDL. + /// The type definition wrapped can then be used in IRDL with its name. + void addTypeWrapper(std::unique_ptr wrapper); + + TypeWrapper *getTypeWrapper(StringRef typeName); + TypeWrapper *getTypeWrapper(TypeID typeID); + + llvm::StringMap> const &getAllAttributes() { + return attributes; + } + + llvm::StringMap> const &getAllTypes() { + return types; + } +}; + +} // namespace irdl +} // namespace mlir + +#endif // MLIR_DIALECT_IRDL_IRDLCONTEXT_H_ diff --git a/mlir/include/mlir/InitAllDialects.h b/mlir/include/mlir/InitAllDialects.h --- a/mlir/include/mlir/InitAllDialects.h +++ b/mlir/include/mlir/InitAllDialects.h @@ -33,6 +33,7 @@ #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/GPU/IR/GPUDialect.h" #include "mlir/Dialect/GPU/TransformOps/GPUTransformOps.h" +#include "mlir/Dialect/IRDL/IR/IRDL.h" #include "mlir/Dialect/Index/IR/IndexDialect.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/LLVMIR/NVVMDialect.h" @@ -93,6 +94,7 @@ func::FuncDialect, gpu::GPUDialect, index::IndexDialect, + irdl::IRDLDialect, LLVM::LLVMDialect, linalg::LinalgDialect, math::MathDialect, diff --git a/mlir/lib/Dialect/CMakeLists.txt b/mlir/lib/Dialect/CMakeLists.txt --- a/mlir/lib/Dialect/CMakeLists.txt +++ b/mlir/lib/Dialect/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(Func) add_subdirectory(GPU) add_subdirectory(Index) +add_subdirectory(IRDL) add_subdirectory(Linalg) add_subdirectory(LLVMIR) add_subdirectory(Math) diff --git a/mlir/lib/Dialect/IRDL/AttributeWrapper.cpp b/mlir/lib/Dialect/IRDL/AttributeWrapper.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/IRDL/AttributeWrapper.cpp @@ -0,0 +1,35 @@ +//===- AttributeWrapper.cpp - IRDL type wrapper definition ------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/IRDL/AttributeWrapper.h" +#include "mlir/Dialect/IRDL/IR/IRDL.h" + +namespace mlir { +namespace irdl { + +bool TypeWrapper::isCorrectAttribute(mlir::Attribute attr) { + return attr.isa() && + isCorrectType(attr.cast().getValue()); +} + +llvm::SmallVector +TypeWrapper::getAttributeParameters(mlir::Attribute attr) { + return getTypeParameters(attr.cast().getValue()); +} + +Attribute TypeWrapper::instantiateAttribute( + llvm::function_ref emitError, + llvm::ArrayRef parameters) { + Type type = instantiateType(emitError, parameters); + if (!type) + return {}; + return TypeAttr::get(type); +} + +} // namespace irdl +} // namespace mlir diff --git a/mlir/lib/Dialect/IRDL/CMakeLists.txt b/mlir/lib/Dialect/IRDL/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/IRDL/CMakeLists.txt @@ -0,0 +1,15 @@ +add_mlir_dialect_library(MLIRIRDL + IR/IRDL.cpp + IR/IRDLOps.cpp + IR/IRDLAttributes.cpp + IRDLContext.cpp + AttributeWrapper.cpp + + DEPENDS + MLIRIRDLIncGen + MLIRIRDLOpsIncGen + MLIRIRDLTypesIncGen + + LINK_LIBS PUBLIC + MLIRIR + ) diff --git a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp @@ -0,0 +1,90 @@ +//===- IRDL.cpp - IRDL dialect ----------------------------------*- C++ -*-===// +// +// This file is licensed 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/Dialect/IRDL/IR/IRDL.h" +#include "mlir/Dialect/IRDL/AttributeWrapper.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/ExtensibleDialect.h" +#include "mlir/IR/OpDefinition.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/Support/LogicalResult.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/TypeSwitch.h" +#include "llvm/IR/Metadata.h" +#include "llvm/Support/Casting.h" + +using namespace mlir; +using namespace mlir::irdl; +using mlir::irdl::TypeWrapper; + +//===----------------------------------------------------------------------===// +// IRDL dialect. +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/IRDL/IR/IRDL.cpp.inc" + +#include "mlir/Dialect/IRDL/IR/IRDLDialect.cpp.inc" + +void IRDLDialect::initialize() { + addOperations< +#define GET_OP_LIST +#include "mlir/Dialect/IRDL/IR/IRDLOps.cpp.inc" + >(); + addTypes< +#define GET_TYPEDEF_LIST +#include "mlir/Dialect/IRDL/IR/IRDLTypesGen.cpp.inc" + >(); + addAttributes< +#define GET_ATTRDEF_LIST +#include "mlir/Dialect/IRDL/IR/IRDLAttributes.cpp.inc" + >(); +} + +//===----------------------------------------------------------------------===// +// Parsing/Printing +//===----------------------------------------------------------------------===// + +/// Parse a region, and add a single block if the region is empty. +/// If no region is parsed, create a new region with a single empty block. +static ParseResult parseSingleBlockRegion(OpAsmParser &p, Region ®ion) { + auto regionParseRes = p.parseOptionalRegion(region); + if (regionParseRes.has_value()) { + if (failed(regionParseRes.value())) + return failure(); + } + // If the region is empty, add a single empty block. + if (region.getBlocks().size() == 0) { + region.push_back(new Block()); + } + + return success(); +} + +static void printSingleBlockRegion(OpAsmPrinter &p, Operation *op, + Region ®ion) { + if (!region.getBlocks().front().empty()) { + p.printRegion(region); + } +} + +LogicalResult DialectOp::verify() { + if (!Dialect::isValidNamespace(getName())) + return emitOpError("invalid dialect name"); + return success(); +} + +#define GET_TYPEDEF_CLASSES +#include "mlir/Dialect/IRDL/IR/IRDLTypesGen.cpp.inc" + +#define GET_ATTRDEF_CLASSES +#include "mlir/Dialect/IRDL/IR/IRDLAttributes.cpp.inc" + +#define GET_OP_CLASSES +#include "mlir/Dialect/IRDL/IR/IRDLOps.cpp.inc" diff --git a/mlir/lib/Dialect/IRDL/IR/IRDLAttributes.cpp b/mlir/lib/Dialect/IRDL/IR/IRDLAttributes.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/IRDL/IR/IRDLAttributes.cpp @@ -0,0 +1,93 @@ +//===- IRDLAttributes.cpp - IRDL dialect ------------------------*- C++ -*-===// +// +// This file is licensed 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/Dialect/IRDL/AttributeWrapper.h" +#include "mlir/Dialect/IRDL/IR/IRDL.h" + +using namespace mlir; +using namespace mlir::irdl; + +LogicalResult TypeOrAttrDefRefAttr::verify( + function_ref emitError, TypeWrapper *typeWrapper, + AttributeWrapper *attrWrapper, SymbolRefAttr symRef) { + if (!typeWrapper && !attrWrapper && !symRef) { + emitError() + << "expected a type wrapper, attribute wrapper, or symbol reference"; + return failure(); + } + if ((symRef && (attrWrapper || typeWrapper)) || + (attrWrapper && typeWrapper)) { + emitError() << "expected either a type wrapper, an attribute wrapper, or a " + "symbol reference, but not more than one"; + return failure(); + } + return success(); +} + +::mlir::Attribute TypeOrAttrDefRefAttr::parse(AsmParser &parser, Type type) { + auto loc = parser.getCurrentLocation(); + + std::string stringLit; + auto litStringRes = parser.parseOptionalString(&stringLit); + + // Wrapper cases. + if (litStringRes.succeeded()) { + auto ctx = parser.getBuilder().getContext(); + auto irdl = ctx->getOrLoadDialect(); + + if (stringLit.empty() || (stringLit[0] != '!' && stringLit[0] != '#')) { + parser.emitError(loc) << "attribute or type wrapper name should " + "be prefixed with either '!' or '#'"; + return {}; + } + + StringRef wrapperName = StringRef(stringLit).substr(1); + + // Type wrapper case + if (stringLit[0] == '!') { + auto *typeWrapper = irdl->irdlContext.getTypeWrapper(wrapperName); + if (typeWrapper) + return TypeOrAttrDefRefAttr::get(ctx, typeWrapper); + parser.emitError(loc) + << "type wrapper " << wrapperName << " is not registered"; + return {}; + } + + // Attribute wrapper case + auto *attrWrapper = irdl->irdlContext.getAttributeWrapper(wrapperName); + if (attrWrapper) + return TypeOrAttrDefRefAttr::get(ctx, attrWrapper); + + parser.emitError(loc) << "attribute wrapper " << wrapperName + << " is not registered"; + return {}; + } + + // Symref cases + Attribute attr; + auto res = parser.parseAttribute(attr); + + if (res.failed()) + return {}; + if (auto symRef = llvm::dyn_cast(attr)) + return TypeOrAttrDefRefAttr::get(symRef); + parser.emitError(loc) << "expected a string literal, or a symbol reference"; + return {}; +} + +void TypeOrAttrDefRefAttr::print(::mlir::AsmPrinter &odsPrinter) const { + if (auto *typeWrapper = getTypeWrapper()) + odsPrinter << "\"!" << typeWrapper->getName() << '"'; + else if (auto *attrWrapper = getAttrWrapper()) + odsPrinter << "\"#" << attrWrapper->getName() << '"'; + else if (auto symRef = getSymRef()) + odsPrinter << symRef; + else + llvm_unreachable("expected a type wrapper, attribute wrapper, or symbol " + "reference"); +} diff --git a/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp b/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp @@ -0,0 +1,20 @@ +//===- IRDLOps.cpp - IRDL dialect -------------------------------*- C++ -*-===// +// +// This file is licensed 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/Dialect/IRDL/IR/IRDL.h" + +using namespace mlir; +using namespace mlir::irdl; + +DialectOp AttributeOp::getDialectOp() { + return cast(getOperation()->getParentOp()); +} + +DialectOp TypeOp::getDialectOp() { + return cast(getOperation()->getParentOp()); +} diff --git a/mlir/lib/Dialect/IRDL/IRDLContext.cpp b/mlir/lib/Dialect/IRDL/IRDLContext.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/Dialect/IRDL/IRDLContext.cpp @@ -0,0 +1,69 @@ +//===- IRDLContext.cpp - IRDL context ---------------------------*- C++ -*-===// +// +// This file is licensed 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/Dialect/IRDL/IRDLContext.h" +#include "mlir/IR/BuiltinTypes.h" + +using namespace mlir; +using namespace irdl; + +/// A wrapper around the 'buitin.complex' type. +class ComplexTypeWrapper : public CppTypeWrapper { + StringRef getName() override { return "builtin.complex"; } + + SmallVector getTypeParameters(ComplexType type) override { + return {TypeAttr::get(type.getElementType())}; + } + + size_t getParameterAmount() override { return 1; } + + Type instantiateType(llvm::function_ref emitError, + ArrayRef parameters) override { + if (parameters.size() != this->getParameterAmount()) { + emitError().append("invalid number of type parameters ", + parameters.size(), " (expected ", + this->getParameterAmount(), ")"); + return Type(); + } + + return ComplexType::getChecked(emitError, + parameters[0].cast().getValue()); + } +}; + +IRDLContext::IRDLContext() { addTypeWrapper(); } + +void IRDLContext::addAttributeWrapper( + std::unique_ptr wrapper) { + auto emplaced = + typeIDToAttributeWrapper.insert({wrapper->getTypeID(), wrapper.get()}); + assert(emplaced.second && + "an attribute wrapper with the same name already exists"); + attributes.try_emplace(wrapper->getName(), std::move(wrapper)); +} + +AttributeWrapper *IRDLContext::getAttributeWrapper(StringRef attrName) { + auto it = attributes.find(attrName); + if (it == attributes.end()) + return nullptr; + return it->second.get(); +} + +void IRDLContext::addTypeWrapper(std::unique_ptr wrapper) { + auto emplaced = + typeIDToTypeWrapper.insert({wrapper->getTypeID(), wrapper.get()}); + assert(emplaced.second && "a type wrapper with the same name already exists"); + types.try_emplace(wrapper->getName(), std::move(wrapper)); +} + +TypeWrapper *IRDLContext::getTypeWrapper(StringRef typeName) { + auto it = types.find(typeName); + if (it == types.end()) + return nullptr; + return it->second.get(); +} diff --git a/mlir/test/Dialect/IRDL/cmath.irdl.mlir b/mlir/test/Dialect/IRDL/cmath.irdl.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/IRDL/cmath.irdl.mlir @@ -0,0 +1,51 @@ +// RUN: mlir-opt %s | mlir-opt | FileCheck %s + +module { + // CHECK-LABEL: irdl.dialect @cmath { + irdl.dialect @cmath { + + // CHECK: irdl.type @complex { + // CHECK: %[[v0:[^ ]*]] = irdl.is f32 + // CHECK: %[[v1:[^ ]*]] = irdl.is f64 + // CHECK: %[[v2:[^ ]*]] = irdl.any_of(%[[v0]], %[[v1]]) + // CHECK: irdl.parameters(%[[v2]]) + // CHECK: } + irdl.type @complex { + %0 = irdl.is f32 + %1 = irdl.is f64 + %2 = irdl.any_of(%0, %1) + irdl.parameters(%2) + } + + // CHECK: irdl.operation @norm { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: %[[v1:[^ ]*]] = irdl.parametric @complex<%[[v0]]> + // CHECK: irdl.operands(%[[v1]]) + // CHECK: irdl.results(%[[v0]]) + // CHECK: } + irdl.operation @norm { + %0 = irdl.any + %1 = irdl.parametric @complex<%0> + irdl.operands(%1) + irdl.results(%0) + } + + // CHECK: irdl.operation @mul { + // CHECK: %[[v0:[^ ]*]] = irdl.is f32 + // CHECK: %[[v1:[^ ]*]] = irdl.is f64 + // CHECK: %[[v2:[^ ]*]] = irdl.any_of(%[[v0]], %[[v1]]) + // CHECK: %[[v3:[^ ]*]] = irdl.parametric @complex<%[[v2]]> + // CHECK: irdl.operands(%[[v3]], %[[v3]]) + // CHECK: irdl.results(%[[v3]]) + // CHECK: } + irdl.operation @mul { + %0 = irdl.is f32 + %1 = irdl.is f64 + %2 = irdl.any_of(%0, %1) + %3 = irdl.parametric @complex<%2> + irdl.operands(%3, %3) + irdl.results(%3) + } + + } +} diff --git a/mlir/test/Dialect/IRDL/cyclic-types.irdl.mlir b/mlir/test/Dialect/IRDL/cyclic-types.irdl.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/IRDL/cyclic-types.irdl.mlir @@ -0,0 +1,50 @@ +// RUN: mlir-opt %s | mlir-opt | FileCheck %s + +// Types that have cyclic references. + +// CHECK: irdl.dialect @testd { +irdl.dialect @testd { + // CHECK: irdl.type @self_referencing { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: %[[v1:[^ ]*]] = irdl.parametric @self_referencing<%[[v0]]> + // CHECK: %[[v2:[^ ]*]] = irdl.is i32 + // CHECK: %[[v3:[^ ]*]] = irdl.any_of(%[[v1]], %[[v2]]) + // CHECK: irdl.parameters(%[[v3]]) + // CHECK: } + irdl.type @self_referencing { + %0 = irdl.any + %1 = irdl.parametric @self_referencing<%0> + %2 = irdl.is i32 + %3 = irdl.any_of(%1, %2) + irdl.parameters(%3) + } + + + // CHECK: irdl.type @type1 { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: %[[v1:[^ ]*]] = irdl.parametric @type2<%[[v0]]> + // CHECK: %[[v2:[^ ]*]] = irdl.is i32 + // CHECK: %[[v3:[^ ]*]] = irdl.any_of(%[[v1]], %[[v2]]) + // CHECK: irdl.parameters(%[[v3]]) + irdl.type @type1 { + %0 = irdl.any + %1 = irdl.parametric @type2<%0> + %2 = irdl.is i32 + %3 = irdl.any_of(%1, %2) + irdl.parameters(%3) + } + + // CHECK: irdl.type @type2 { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: %[[v1:[^ ]*]] = irdl.parametric @type1<%[[v0]]> + // CHECK: %[[v2:[^ ]*]] = irdl.is i32 + // CHECK: %[[v3:[^ ]*]] = irdl.any_of(%[[v1]], %[[v2]]) + // CHECK: irdl.parameters(%[[v3]]) + irdl.type @type2 { + %0 = irdl.any + %1 = irdl.parametric @type1<%0> + %2 = irdl.is i32 + %3 = irdl.any_of(%1, %2) + irdl.parameters(%3) + } +} \ No newline at end of file diff --git a/mlir/test/Dialect/IRDL/test-type.irdl.mlir b/mlir/test/Dialect/IRDL/test-type.irdl.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/IRDL/test-type.irdl.mlir @@ -0,0 +1,33 @@ +// RUN: mlir-opt %s | mlir-opt | FileCheck %s + +module { + // CHECK-LABEL: irdl.dialect @testd { + irdl.dialect @testd { + // CHECK: irdl.type @singleton + irdl.type @singleton + + // CHECK: irdl.type @parametrized { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: %[[v1:[^ ]*]] = irdl.is i32 + // CHECK: %[[v2:[^ ]*]] = irdl.is i64 + // CHECK: %[[v3:[^ ]*]] = irdl.any_of(%[[v1]], %[[v2]]) + // CHECK: irdl.parameters(%[[v0]], %[[v3]]) + // CHECK: } + irdl.type @parametrized { + %0 = irdl.any + %1 = irdl.is i32 + %2 = irdl.is i64 + %3 = irdl.any_of(%1, %2) + irdl.parameters(%0, %3) + } + + // CHECK: irdl.operation @any { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: irdl.results(%[[v0]]) + // CHECK: } + irdl.operation @any { + %0 = irdl.any + irdl.results(%0) + } + } +} diff --git a/mlir/test/Dialect/IRDL/testd.irdl.mlir b/mlir/test/Dialect/IRDL/testd.irdl.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/IRDL/testd.irdl.mlir @@ -0,0 +1,122 @@ +// RUN: mlir-opt %s | mlir-opt | FileCheck %s + +// CHECK: irdl.dialect @testd { +irdl.dialect @testd { + // CHECK: irdl.type @parametric { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: irdl.parameters(%[[v0]]) + // CHECK: } + irdl.type @parametric { + %0 = irdl.any + irdl.parameters(%0) + } + + // CHECK: irdl.type @attr_in_type_out { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: irdl.parameters(%[[v0]]) + // CHECK: } + irdl.type @attr_in_type_out { + %0 = irdl.any + irdl.parameters(%0) + } + + // CHECK: irdl.operation @eq { + // CHECK: %[[v0:[^ ]*]] = irdl.is i32 + // CHECK: irdl.results(%[[v0]]) + // CHECK: } + irdl.operation @eq { + %0 = irdl.is i32 + irdl.results(%0) + } + + // CHECK: irdl.operation @anyof { + // CHECK: %[[v0:[^ ]*]] = irdl.is i32 + // CHECK: %[[v1:[^ ]*]] = irdl.is i64 + // CHECK: %[[v2:[^ ]*]] = irdl.any_of(%[[v0]], %[[v1]]) + // CHECK: irdl.results(%[[v2]]) + // CHECK: } + irdl.operation @anyof { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.any_of(%0, %1) + irdl.results(%2) + } + + // CHECK: irdl.operation @all_of { + // CHECK: %[[v0:[^ ]*]] = irdl.is i32 + // CHECK: %[[v1:[^ ]*]] = irdl.is i64 + // CHECK: %[[v2:[^ ]*]] = irdl.any_of(%[[v0]], %[[v1]]) + // CHECK: %[[v3:[^ ]*]] = irdl.all_of(%[[v2]], %[[v1]]) + // CHECK: irdl.results(%[[v3]]) + // CHECK: } + irdl.operation @all_of { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.any_of(%0, %1) + %3 = irdl.all_of(%2, %1) + irdl.results(%3) + } + + // CHECK: irdl.operation @any { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: irdl.results(%[[v0]]) + // CHECK: } + irdl.operation @any { + %0 = irdl.any + irdl.results(%0) + } + + // CHECK: irdl.operation @dynbase { + // CHECK: %[[v0:[^ ]*]] = irdl.any + // CHECK: %[[v1:[^ ]*]] = irdl.parametric @parametric<%[[v0]]> + // CHECK: irdl.results(%[[v1]]) + // CHECK: } + irdl.operation @dynbase { + %0 = irdl.any + %1 = irdl.parametric @parametric<%0> + irdl.results(%1) + } + + // CHECK: irdl.operation @dynparams { + // CHECK: %[[v0:[^ ]*]] = irdl.is i32 + // CHECK: %[[v1:[^ ]*]] = irdl.is i64 + // CHECK: %[[v2:[^ ]*]] = irdl.any_of(%[[v0]], %[[v1]]) + // CHECK: %[[v3:[^ ]*]] = irdl.parametric @parametric<%[[v2]]> + // CHECK: irdl.results(%[[v3]]) + // CHECK: } + irdl.operation @dynparams { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.any_of(%0, %1) + %3 = irdl.parametric @parametric<%2> + irdl.results(%3) + } + + // CHECK: irdl.operation @params { + // CHECK: %[[v0:[^ ]*]] = irdl.is i32 + // CHECK: %[[v1:[^ ]*]] = irdl.is i64 + // CHECK: %[[v2:[^ ]*]] = irdl.any_of(%[[v0]], %[[v1]]) + // CHECK: %[[v3:[^ ]*]] = irdl.parametric "!builtin.complex"<%[[v2]]> + // CHECK: irdl.results(%[[v3]]) + // CHECK: } + irdl.operation @params { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.any_of(%0, %1) + %3 = irdl.parametric "!builtin.complex"<%2> + irdl.results(%3) + } + + // CHECK: irdl.operation @constraint_vars { + // CHECK: %[[v0:[^ ]*]] = irdl.is i32 + // CHECK: %[[v1:[^ ]*]] = irdl.is i64 + // CHECK: %[[v2:[^ ]*]] = irdl.any_of(%[[v0]], %[[v1]]) + // CHECK: irdl.results(%[[v2]], %[[v2]]) + // CHECK: } + irdl.operation @constraint_vars { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.any_of(%0, %1) + irdl.results(%2, %2) + } +} diff --git a/mlir/test/mlir-opt/commandline.mlir b/mlir/test/mlir-opt/commandline.mlir --- a/mlir/test/mlir-opt/commandline.mlir +++ b/mlir/test/mlir-opt/commandline.mlir @@ -17,6 +17,7 @@ // CHECK-NEXT: func // CHECK-NEXT: gpu // CHECK-NEXT: index +// CHECK-NEXT: irdl // CHECK-NEXT: linalg // CHECK-NEXT: llvm // CHECK-NEXT: math