AnyAttr and AnyAttrOf in a type ("assertion _M_is_engaged failed")

Greetings everyone. I am trying to make a description for an operation to create a constant with a literal value and a corresponding type for it - as to this moment only Integer and Float values are to be supported, so I decided to use AnyAttrOf-parameter for the type and have no arguments for the operation (the decision is based on the example of MLIR LLVM-dialect.

def Const : DFCIR_DialectType<"DFCIRConst", "const", [ConstantLike]> {
        let parameters = (ins AnyAttrOf<[Builtin_IntegerAttr, Builtin_FloatAttr]>:$value);
        let assemblyFormat = "`<` $value `>`";
}

def ConstOp : DFCIR_Op<"const_op"> {
        let assemblyFormat = " type($res)";
        let results = (outs Const:$res);
}

The problem appears when I am trying to use mlir-rblgen - something causes an error and a stack dump to appear:


The problem persists if I am using AnyAttr as well. Could anyone please give me a hint of how to deal with it? The error says to post an issue on GitHub, but I am almost sure I just did something wrong and it’s not MLIR’s fault. Thank you very much in advance.

Looks like a bug in TableGen, but without a debug build with symbols it hard to say where it comes from. Can you try providing a reproducer in the test dialect?

ConstantLike is an Operation trait, you added it to a type here?

Sorry, I was experimenting before and forgot to remove it - the problem existed even before I added it.

I am sorry, but I am afraid I am not sure what kind of error reproducer we are speaking about.

I need to be able to reproduce myself locally on my computer the problem. The most convenient is to produce the same op and same type in the TestDialect in MLIR and post a patch showing the issue.

Turns out even without the ConstOp the error persists, but here is the reproducer, if I understood everything right:

mlir-tblgen --gen-typedef-decls check.td -I <<your LLVM-project-DIR>>/mlir/include

check.td file:

include "mlir/IR/OpBase.td"
include "mlir/IR/DialectBase.td"
include "mlir/IR/AttrTypeBase.td"
include "mlir/IR/BuiltinAttributes.td"
include "mlir/Interfaces/InferTypeOpInterface.td"


def Test_Dialect : Dialect {
  let name = "test";
}

class Test_Op<string name, list<Trait> traits = []> : Op<Test_Dialect, name, traits>;


class Test_DialectType<string typeName, string typeMnemonic, list<Trait> traits = []> 
	: TypeDef<Test_Dialect, typeName, traits> {
	let mnemonic = typeMnemonic;
}

def Const : Test_DialectType<"DFCIRConst", "const"> {
        let parameters = (ins AnyAttrOf<[Builtin_IntegerAttr, Builtin_FloatAttr]>:$value);
        let assemblyFormat = "`<` $value `>`";
}

def ConstOp : Test_Op<"const_op"> {
        let assemblyFormat = " type($res)";
        let results = (outs Const:$res);
}

The similar stack trace message appears:

I don’t reproduce at HEAD, but you haven’t mentioned which revision you tried?

$ bin/mlir-tblgen --gen-typedef-decls /tmp/check.td -I ../mlir/include
Included from /tmp/check.td:1:
Included from ../mlir/include/mlir/IR/OpBase.td:18:
Included from ../mlir/include/mlir/IR/Interfaces.td:16:
Included from ../mlir/include/mlir/IR/AttrTypeBase.td:17:
../mlir/include/mlir/IR/CommonAttrConstraints.td:171:7: error: Missing `cppType` field in Attribute/Type parameter: anonymous_387
class AnyAttrOf<list<Attr> allowedAttrs, string summary = "",
      ^

Basically it is complaining that you’re using a constraint as parameter of a Type here (instead of an operation argument)

1 Like

I am afraid I wasn’t using a particular release, but a main branch build at this commit. If the problem was about using a constraint as a type parameter, is there a way to parametrize a type with an arbitrary attribute? If we are speaking about a constant-defining operation, we need to be able to somehow create a constant of an arbitrary type and without the mentioned above functionality we would have to make a parameterless type, but the operation will have the parameters of the type.

It looks like you’re trying to store the constant value in the Type, but typically this would be stored as an attribute of the ConstantLike op instead, e.g.: https://github.com/llvm/llvm-project/blob/45eb6026d979e306a12ea9e223852b28c3c9edbf/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td#L1565-L1566

It’s unusual to encode the constant value in the type system (in general we “type erase” this by returning the same “scalar type” for constant and variable).
That said it opens also some interesting properties, wonder what kind of tradeoff we’re getting into here.

Initially I thought it would be more precise to emphasise the constant-like nature of literal values by storing them in type itself.

Right now I changed the specifications a bit:

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);
}

Does it mean that by ensuring this “type erasure”-like pattern we achieve simpler type specification, yet forcing ourselves to refer to the operation where the value of said type was returned in case we need to verify its semantics at some point? If that’s the case, then I believe I understand how to work with something like that better.

By analogy with C/C++:

  const int a = foo();
  const int a_const = 42;

They both have the same type here, we’re translating it in SSA the same way, if you looked at the arith dialect for example you’ll see:

%a = call @foo() : () -> i32
%a_const = arith.constant 42 : i32

The type in both cases is just i32. The users of the SSA value don’t need to know that it was produced from a constant or from the result of other operations.

1 Like

Got it. Thank you very much.