'irdl' Dialect
Basics ¶
The IRDL (Intermediate Representation Definition Language) dialect allows defining MLIR dialects as MLIR programs. Nested operations are used to represent dialect structure: dialects contain operations, types and attributes, themselves containing type parameters, operands, results, etc. Each of those concepts are mapped to MLIR operations in the IRDL dialect, as shown in the example dialect below:
irdl.dialect @cmath {
irdl.type @complex {
%0 = irdl.is f32
%1 = irdl.is f64
%2 = irdl.any_of(%0, %1)
irdl.parameters(%2)
}
irdl.operation @mul {
%0 = irdl.is f32
%1 = irdl.is f64
%2 = irdl.any_of(%0, %1)
%3 = irdl.parametric @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). For more information, see
constraints and combinators.
In order to simplify the dialect, IRDL variables are handles over
mlir::Attribute
. In order to support manipulating mlir::Type
,
IRDL wraps all types in an mlir::TypeAttr
attribute.
Principles ¶
The core principles of IRDL are the following, in no particular order:
- Portability. IRDL dialects should be self-contained, such that dialects can be easily distributed with minimal assumptions on which compiler infrastructure (or which commit of MLIR) is used.
- Introspection. The IRDL dialect definition mechanism should strive towards offering as much introspection abilities as possible. Dialects should be as easy to manipulate, generate, and analyze as possible.
- Runtime declaration support. The specification of IRDL dialects should offer the ability to have them be loaded at runtime, via dynamic registration or JIT compilation. Compatibility with dynamic workflows should not hinder the ability to compile IRDL dialects into ahead-of-time declarations.
- Reliability. Concepts in IRDL should be consistent and predictable, with as much focus on high-level simplicity as possible. Consequently, IRDL definitions that verify should work out of the box, and those that do not verify should provide clear and understandable errors in all circumstances.
While IRDL simplifies IR definition, it remains an IR itself and thus does not require to be comfortably user-writeable.
Constraints and combinators ¶
Attribute, type and operation verifiers are expressed in terms of constraint
variables. Constraint variables are defined as the results of constraint
operations (like irdl.is
or constraint combinators).
Constraint variables act as variables: as such, matching against the same constraint variable multiple times can only succeed if the matching type or attribute is the same as the one that previously matched. In the following example:
irdl.type @foo {
%ty = irdl.any_type
irdl.parameters(param1: %ty, param2: %ty)
}
only types with two equal parameters will successfully match (foo<i32, i32>
would match while foo<i32, i64>
would fail, even though both i32 and i64
individually satisfy the irdl.any_type
constraint). This constraint variable
mechanism allows to easily express a requirement on type or attribute equality.
To declare more complex verifiers, IRDL provides constraint-combinator
operations such as irdl.any_of
, irdl.all_of
or irdl.parametric
. These
combinators can be used to combine constraint variables into new constraint
variables. Like all uses of constraint variables, their constraint variable
operands enforce equality of matched types of attributes as explained in the
previous paragraph.
Motivating use cases ¶
To illustrate the rationale behind IRDL, the following list describes examples of intended use cases for IRDL, in no particular order:
- Fuzzer generation. With declarative verifier definitions, it is possible to compile IRDL dialects into compiler fuzzers that generate only programs passing verifiers.
- Portable dialects between compiler infrastructures. Some compiler infrastructures are independent from MLIR but are otherwise IR-compatible. Portable IRDL dialects allow to share the dialect definitions between MLIR and other compiler infrastructures without needing to maintain multiple potentially out-of-sync definitions.
- Dialect simplification. Because IRDL definitions can easily be mechanically modified, it is possible to simplify the definition of dialects based on which operations are actually used, leading to smaller compilers.
- SMT analysis. Because IRDL dialect definitions are declarative, their definition can be lowered to alternative representations like SMT, allowing analysis of the behavior of transforms taking verifiers into account.
Operations ¶
irdl.all_of
(irdl::AllOfOp) ¶
Constraints to the intersection of the provided constraints
Syntax:
operation ::= `irdl.all_of` `(` $args `)` ` ` attr-dict
irdl.all_of
defines a constraint that accepts any type or attribute that
satisfies all of its provided constraints.
Example:
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
has one parameter that must be 32-bit long and a float (in other
words, that must be f32
).
Traits: HasParent<TypeOp, AttributeOp, OperationOp>
, SameOperandsAndResultType
Interfaces: VerifyConstraintInterface
Operands: ¶
Operand | Description |
---|---|
args | variadic of IRDL handle to an mlir::Attribute |
Results: ¶
Result | Description |
---|---|
output | IRDL handle to an mlir::Attribute |
irdl.any_of
(irdl::AnyOfOp) ¶
Constraints to the union of the provided constraints
Syntax:
operation ::= `irdl.any_of` `(` $args `)` ` ` attr-dict
irdl.any_of
defines a constraint that accepts any type or attribute that
satisfies at least one of its provided type constraints.
Example:
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
has a single type parameter that can be either i32
, i64
, f32
or
f64
.
Traits: HasParent<TypeOp, AttributeOp, OperationOp>
, SameOperandsAndResultType
Interfaces: VerifyConstraintInterface
Operands: ¶
Operand | Description |
---|---|
args | variadic of IRDL handle to an mlir::Attribute |
Results: ¶
Result | Description |
---|---|
output | IRDL handle to an mlir::Attribute |
irdl.any
(irdl::AnyOp) ¶
Accept any type or attribute
Syntax:
operation ::= `irdl.any` attr-dict
irdl.any
defines a constraint that accepts any type or attribute.
Example:
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.
Traits: HasParent<TypeOp, AttributeOp, OperationOp>
Interfaces: InferTypeOpInterface
, VerifyConstraintInterface
Results: ¶
Result | Description |
---|---|
output | IRDL handle to an mlir::Attribute |
irdl.attribute
(irdl::AttributeOp) ¶
Define a new attribute
Syntax:
operation ::= `irdl.attribute` $sym_name attr-dict-with-keyword custom<SingleBlockRegion>($body)
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:
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"
.
Traits: AtMostOneChildOf<ParametersOp>
, HasParent<DialectOp>
, NoRegionArguments
, NoTerminator
Interfaces: Symbol
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
irdl.attributes
(irdl::AttributesOp) ¶
Define the attributes of an operation
Syntax:
operation ::= `irdl.attributes` custom<AttributesOp>($attributeValues, $attributeValueNames) attr-dict
irdl.attributes
defines the attributes of the irdl.operation
parent
operation definition.
In the following example, irdl.attributes
defines the attributes of the
attr_op
operation:
irdl.dialect @example {
irdl.operation @attr_op {
%0 = irdl.any
%1 = irdl.is i64
irdl.attibutes {
"attr1" = %0,
"attr2" = %1
}
}
}
The operation will expect an arbitrary attribute “attr1” and an
attribute “attr2” with value i64
.
Traits: HasParent<OperationOp>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
attributeValueNames | ::mlir::ArrayAttr | string array attribute |
Operands: ¶
Operand | Description |
---|---|
attributeValues | variadic of IRDL handle to an mlir::Attribute |
irdl.base
(irdl::BaseOp) ¶
Constraints an attribute/type base
Syntax:
operation ::= `irdl.base` ($base_ref^)? ($base_name^)? ` ` attr-dict
irdl.base
defines a constraint that only accepts a single type
or attribute base, e.g. an IntegerType
. The attribute base is defined
either by a symbolic reference to the corresponding IRDL definition,
or by the name of the base. Named bases are prefixed with !
or #
respectively for types and attributes.
Example:
irdl.dialect @cmath {
irdl.type @complex {
%0 = irdl.base "!builtin.integer"
irdl.parameters(%0)
}
irdl.type @complex_wrapper {
%0 = irdl.base @cmath::@complex
irdl.parameters(%0)
}
}
The above program defines a cmath.complex
type that expects a single
parameter, which is a type with base name builtin.integer
, which is the
name of an IntegerType
type.
It also defines a cmath.complex_wrapper
type that expects a single
parameter, which is a type of base type cmath.complex
.
Traits: HasParent<TypeOp, AttributeOp, OperationOp>
Interfaces: InferTypeOpInterface
, SymbolUserOpInterface
, VerifyConstraintInterface
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
base_ref | ::mlir::SymbolRefAttr | symbol reference attribute |
base_name | ::mlir::StringAttr | string attribute |
Results: ¶
Result | Description |
---|---|
output | IRDL handle to an mlir::Attribute |
irdl.c_pred
(irdl::CPredOp) ¶
Constraints an attribute using a C++ predicate
Syntax:
operation ::= `irdl.c_pred` $pred ` ` attr-dict
irdl.c_pred
defines a constraint that is written in C++.
Dialects using this operation cannot be registered at runtime, as it relies on C++ code.
Special placeholders can be used to refer to entities in the context where this predicate is used. They serve as “hooks” to the enclosing environment. The following special placeholders are supported in constraints for an op:
$_builder
will be replaced by a mlir::Builder instance.$_op
will be replaced by the current operation.$_self
will be replaced with the entity this predicate is attached to. Compared to ODS,$_self
is always of typemlir::Attribute
, and types are manipulated asTypeAttr
attributes.
Example:
irdl.type @op_with_attr {
%0 = irdl.c_pred "::llvm::isa<::mlir::IntegerAttr>($_self)"
irdl.parameters(%0)
}
In this example, @op_with_attr is defined as a type with a single
parameter, which is an IntegerAttr
, as constrained by the C++ predicate.
Interfaces: InferTypeOpInterface
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
pred | ::mlir::StringAttr | string attribute |
Results: ¶
Result | Description |
---|---|
output | IRDL handle to an mlir::Attribute |
irdl.dialect
(irdl::DialectOp) ¶
Define a new dialect
Syntax:
operation ::= `irdl.dialect` $sym_name attr-dict-with-keyword custom<SingleBlockRegion>($body)
The irdl.dialect
operation defines a dialect. All operations, attributes,
and types defined inside its region will be part of the dialect.
Example:
irdl.dialect @cmath {
...
}
The above program defines a cmath
dialect.
Traits: IsolatedFromAbove
, NoTerminator
, SymbolTable
Interfaces: Symbol
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
irdl.is
(irdl::IsOp) ¶
Constraints an attribute/type to be a specific attribute instance
Syntax:
operation ::= `irdl.is` $expected ` ` attr-dict
irdl.is
defines a constraint that only accepts a specific instance of a
type or attribute.
Example:
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.
Traits: AlwaysSpeculatableImplTrait
, HasParent<TypeOp, AttributeOp, OperationOp>
Interfaces: ConditionallySpeculatable
, InferTypeOpInterface
, NoMemoryEffect (MemoryEffectOpInterface)
, VerifyConstraintInterface
Effects: MemoryEffects::Effect{}
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
expected | ::mlir::Attribute | any attribute |
Results: ¶
Result | Description |
---|---|
output | IRDL handle to an mlir::Attribute |
irdl.operands
(irdl::OperandsOp) ¶
Define the operands of an operation
Syntax:
operation ::= `irdl.operands` `` custom<NamedValueListWithVariadicity>($args, $names, $variadicity) attr-dict
irdl.operands
define the operands of the irdl.operation
parent operation
definition. Each operand is named after an identifier.
In the following example, irdl.operands
defines the operands of the
mul
operation:
irdl.dialect @cmath {
irdl.type @complex { /* ... */ }
irdl.operation @mul {
%0 = irdl.any
%1 = irdl.parametric @cmath::@complex<%0>
irdl.results(res: %1)
irdl.operands(lhs: %1, rhs: %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.
The operands can also be marked as variadic or optional:
irdl.operands(foo: %0, bar: single %1, baz: optional %2, qux: variadic %3)
Here, foo and bar are required single operands, baz is an optional operand, and qux is a variadic operand.
When more than one operand is marked as optional or variadic, the operation will expect a ‘operandSegmentSizes’ attribute that defines the number of operands in each segment.
Traits: HasParent<OperationOp>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
names | ::mlir::ArrayAttr | string array attribute |
variadicity | ::mlir::irdl::VariadicityArrayAttr |
Operands: ¶
Operand | Description |
---|---|
args | variadic of IRDL handle to an mlir::Attribute |
irdl.operation
(irdl::OperationOp) ¶
Define a new operation
Syntax:
operation ::= `irdl.operation` $sym_name attr-dict-with-keyword custom<SingleBlockRegion>($body)
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:
irdl.dialect @cmath {
irdl.type @complex { /* ... */ }
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.
Traits: AtMostOneChildOf<OperandsOp, ResultsOp, AttributesOp, RegionsOp>
, HasParent<DialectOp>
, NoRegionArguments
, NoTerminator
Interfaces: Symbol
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
irdl.parameters
(irdl::ParametersOp) ¶
Define the constraints on parameters of a type/attribute definition
Syntax:
operation ::= `irdl.parameters` `` custom<NamedValueList>($args, $names) attr-dict
irdl.parameters
defines the constraints on parameters of a type or
attribute definition. Each parameter is named after an identifier.
Example:
irdl.dialect @cmath {
irdl.type @complex {
%0 = irdl.is i32
%1 = irdl.is i64
%2 = irdl.any_of(%0, %1)
irdl.parameters(elem: %2)
}
}
The above program defines a type complex
inside the dialect cmath
. The
type has a single parameter elem
that should be either i32
or i64
.
Traits: HasParent<AttributeOp, TypeOp>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
names | ::mlir::ArrayAttr | string array attribute |
Operands: ¶
Operand | Description |
---|---|
args | variadic of IRDL handle to an mlir::Attribute |
irdl.parametric
(irdl::ParametricOp) ¶
Constraints an attribute/type base and its parameters
Syntax:
operation ::= `irdl.parametric` $base_type `<` $args `>` ` ` attr-dict
irdl.parametric
defines a constraint that accepts only a single type
or attribute base. The attribute base is defined by a symbolic reference
to the corresponding definition. It will additionally constraint the
parameters of the type/attribute.
Example:
irdl.dialect @cmath {
irdl.type @complex { /* ... */ }
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
.
Traits: AlwaysSpeculatableImplTrait
, HasParent<TypeOp, AttributeOp, OperationOp>
Interfaces: ConditionallySpeculatable
, InferTypeOpInterface
, NoMemoryEffect (MemoryEffectOpInterface)
, SymbolUserOpInterface
, VerifyConstraintInterface
Effects: MemoryEffects::Effect{}
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
base_type | ::mlir::SymbolRefAttr | symbol reference attribute |
Operands: ¶
Operand | Description |
---|---|
args | variadic of IRDL handle to an mlir::Attribute |
Results: ¶
Result | Description |
---|---|
output | IRDL handle to an mlir::Attribute |
irdl.region
(irdl::RegionOp) ¶
Define a region of an operation
Syntax:
operation ::= `irdl.region` ``(`(` $entryBlockArgs $constrainedArguments^ `)`)?
``(` ` `with` `size` $numberOfBlocks^)? attr-dict
The irdl.region construct defines a set of characteristics that a region of an operation should satify. Each region is named after an identifier.
These characteristics include constraints for the entry block arguments of the region and the total number of blocks it contains. The number of blocks must be a non-zero and non-negative integer, and it is optional by default. The set of constraints for the entry block arguments may be optional or empty. If no parentheses are provided, the set is assumed to be optional, and the arguments are not constrained in any way. If parentheses are provided with no arguments, it means that the region must have no entry block arguments
Example:
irdl.dialect @example {
irdl.operation @op_with_regions {
%r0 = irdl.region
%r1 = irdl.region()
%v0 = irdl.is i32
%v1 = irdl.is i64
%r2 = irdl.region(%v0, %v1)
%r3 = irdl.region with size 3
irdl.regions(foo: %r0, bar: %r1, baz: %r2, qux: %r3)
}
}
The above snippet demonstrates an operation named @op_with_regions
,
which is constrained to have four regions.
- Region
foo
doesn’t have any constraints on the arguments or the number of blocks. - Region
bar
should have an empty set of arguments. - Region
baz
should have two arguments of typesi32
andi64
. - Region
qux
should contain exactly three blocks.
Traits: HasParent<OperationOp>
Interfaces: InferTypeOpInterface
, VerifyRegionInterface
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
numberOfBlocks | ::mlir::IntegerAttr | 32-bit signless integer attribute |
constrainedArguments | ::mlir::UnitAttr | unit attribute |
Operands: ¶
Operand | Description |
---|---|
entryBlockArgs | variadic of IRDL handle to an mlir::Attribute |
Results: ¶
Result | Description |
---|---|
output | IRDL handle to a region definition |
irdl.regions
(irdl::RegionsOp) ¶
Define the regions of an operation
Syntax:
operation ::= `irdl.regions` `` custom<NamedValueList>($args, $names) attr-dict
irdl.regions
defines the regions of an operation by accepting
values produced by irdl.region
operation as arguments. Each
region has an identifier as name.
Example:
irdl.dialect @example {
irdl.operation @op_with_regions {
%r1 = irdl.region with size 3
%0 = irdl.any
%r2 = irdl.region(%0)
irdl.regions(foo: %r1, bar: %r2)
}
}
In the snippet above the operation is constrained to have two regions.
The first region (foo
) should contain three blocks.
The second region (bar
) should have one region with one argument.
Traits: HasParent<OperationOp>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
names | ::mlir::ArrayAttr | string array attribute |
Operands: ¶
Operand | Description |
---|---|
args | variadic of IRDL handle to a region definition |
irdl.results
(irdl::ResultsOp) ¶
Define the results of an operation
Syntax:
operation ::= `irdl.results` `` custom<NamedValueListWithVariadicity>($args, $names, $variadicity) attr-dict
irdl.results
define the results of the irdl.operation
parent operation
definition. Each result is named after an identifier.
In the following example, irdl.results
defines the results of the
get_values
operation:
irdl.dialect @cmath {
irdl.type @complex { /* ... */ }
/// Returns the real and imaginary parts of a complex number.
irdl.operation @get_values {
%0 = irdl.any
%1 = irdl.parametric @cmath::@complex<%0>
irdl.results(re: %0, im: %0)
irdl.operands(complex: %1)
}
}
The operation will expect one operand of the cmath.complex
type, and two
results that have the underlying type of the cmath.complex
.
The results can also be marked as variadic or optional:
irdl.results(foo: %0, bar: single %1, baz: optional %2, qux: variadic %3)
Here, foo and bar are required single results, baz is an optional result, and qux is a variadic result.
When more than one result is marked as optional or variadic, the operation will expect a ‘resultSegmentSizes’ attribute that defines the number of results in each segment.
Traits: HasParent<OperationOp>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
names | ::mlir::ArrayAttr | string array attribute |
variadicity | ::mlir::irdl::VariadicityArrayAttr |
Operands: ¶
Operand | Description |
---|---|
args | variadic of IRDL handle to an mlir::Attribute |
irdl.type
(irdl::TypeOp) ¶
Define a new type
Syntax:
operation ::= `irdl.type` $sym_name attr-dict-with-keyword custom<SingleBlockRegion>($body)
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:
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
.
Traits: AtMostOneChildOf<ParametersOp>
, HasParent<DialectOp>
, NoRegionArguments
, NoTerminator
Interfaces: Symbol
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::StringAttr | string attribute |