I am talking about the textual assembly format. Here is one suitable example:
let’s define a constant-creating operation const_op
, which returns a value of a type, satisfying a constraint const
(either with a type constI
or constF
).
def ConstInt : DFCIR_DialectType<"DFCIRConstInt", "constI"> {
let parameters = (ins Builtin_IntegerAttr:$value);
let assemblyFormat = "`<` $value `>`";
}
def ConstFloat : DFCIR_DialectType<"DFCIRConstFloat", "constF"> {
let parameters = (ins Builtin_FloatAttr:$value);
let assemblyFormat = "`<` $value `>`";
}
def Const : TypeConstraint<Or<[ConstInt.predicate, ConstFloat.predicate]>, "const">;
def ConstOp : DFCIR_Op<"const_op"> {
let assemblyFormat = " type($res) attr-dict";
let results = (outs Const:$res);
}
So in .mlir
-file it will look something like this:
%zeroConst = dfcir.const_op dfcir.constI<0>
%oneConst = dfcir.const_op dfcir.constI<1>
%twoConst = dfcir.const_op dfcir.constI<2>
%threeConst = dfcir.const_op dfcir.constI<3>
Then, let’s say we have a logical operation less
, which can have operands of types, satisfying the constraint variable
, where not only const
types apply, but stream
and scalar
types as well:
def Stream : DFCIR_DialectType<"DFCIRStream", "stream"> {
let parameters = (ins "Type":$streamType);
let assemblyFormat = "`<` $streamType `>`";
}
def Scalar : DFCIR_DialectType<"DFCIRScalar", "scalar"> {
let parameters = (ins "Type":$scalarType);
let assemblyFormat = "`<` $scalarType `>`";
}
def Variable : TypeConstraint<Or<[Stream.predicate, Scalar.predicate, Const.predicate]>, "variable">;
def LessOp : DFCIR_Op<"less"> {
let arguments = (ins
Variable:$first,
Variable:$second);
let assemblyFormat = "`(` $first `:` type($first) `,` $second `:` type($second)`)` attr-dict `:` type($res)";
let results = (outs Variable:$res);
}
Because all the operands can have different types (we cannot use traits like SameOperandsAndResultType
) as well as the operation’s result, the assembly format to compare two literals - %intValue
and %floatValue
and return a literal of boolean type will look like this:
%intValue = dfcir.const_op dfcir.constI<0>
%floatValue= dfcir.const_op dfcir.constF<2.5>
%result = dfcir.less(%intValue : dfcir.constI<0>, %floatValue : dfcir.constF<2.5>) : dfcir.constI<1>
I understand that in this case it is correct to somehow specify the type of resulting value, but in case with the operands - they all have “parent operations”, where their types were already specified, so it seems strange to me to specify the same types again, when the same type specifications can be extracted from the parent operation.
Once again, I am sorry if questions like this might seem weird to you, but I honestly have little experience with MLIR and have little understanding of its intrinsics.