Deduce Bool and BoolAttr value from optional keyword in assembly format

I have some types that have a BoolAttr as parameter

let parameters = (ins "mlir::Type":$element_type, "uint64_t":$size, DefaultValuedParameter<"BoolAttr", "BoolAttr::get($_ctxt, false)">:$is_const);

I would like this type to be conditionally printed as one of the following, depending on the value of is_const attribute.

!dialect.type_name<const element_type = !underlying, size = 50>
!dialect.type_name<element_type = !underlying, size = 50>

I have been able to use enums to print it as either const or nonConst but that is not as readable as it would be to just have the const keyword or nothing. Furthermore enums cannot be used in combinations with DefaultValuedParameter since it only support EnumsAttr, or Optional which are both things i would like to avoid if possible.

Ideally i would like to be able to write something in the assembly format such as

  let assemblyFormat = "`<`( ` when_true<"const">($is_const)^  )? struct($element_type, $size) `>` ";

but there does not seems to be any support for this pattern.

Is there a intended way to achieve this? If it is not present i can implement it myself and upstream it, either as suggested in the last snippet, or by supporting enums in DefaultValuedParameter.

Not sure if it works in the attribute/type assembly format yet, but for ops using UnitAttr instead would let you do (`const` $is_const^)?: Operation Definition Specification (ODS) - MLIR

2 Likes

the issue with that solution, which is the one i ended up using because i could not find an alternative, is that it prints const true instead of const alone.

Seems like a bug to me, it should be consistent with the op format I think. I expect a patch would be welcome.

This is because you’re using a BoolAttr instead of a UnitAttr.
(BoolAttr isn’t even a real attribute: it is really an IntegerAttr with 1 bit)

i managed to make UnitAttr work as i wanted it on operations, but i am still failing to make it work on types.

If i use a DefaultValuedParameter then it still prints unit.


def CustomDialect_PrimitiveType : CustomDialect_Type<"Primitive", "primitive"> {
  let parameters = (ins "PrimitiveKind":$kind, "uint64_t":$byte_size, DefaultValuedParameter<"UnitAttr", "nullptr">:$is_const);

  let assemblyFormat = "`<`( `const` $is_const^ )? struct($kind, $byte_size) `>` ";
}

  auto Primitive = PrimitiveType::get(&context, PrimitiveKind::Signed, 4,
                                      mlir::UnitAttr::get(&context));
  builder.create<UndefOp>(builder.getUnknownLoc(), Primitive);
  module.print(llvm::outs());
!int32_t = !dialect.primitive<const unit kind = Signed, byte_size = 4>

If I drop DefaultValuedParameter in favor of just "mlir::UnitAttr" then the parameter is not recognized as optional and thus it cannot be used a anchor of a optional group.

if i use UnitAttr without quotations then tablegen crashes due to a bad alloc in :DefGen::getBuilderParams(std::initializer_list<mlir::tblgen::MethodParameter>).

I guess there is no support to use UnitAttr within types?

As a workaround, can you not use DefaultValuedParameter and write a custom forwarding builder?

i not sure i understand how a custom builder would solve the issue. from what i understand the issue is the attribute and type assembly format has no support for unitattr the same way operations do.

I looked into the tblgen code and i found the following relevant pieces of code.

In the parser printer generator for operations there is a hardcoded rule that specifies that if a attribute is called UnitAttr then it is omitted from the parser.

While there is no such rule for attributes and types.

Would it be a acceptable implementation to replicate the same logic for attributes and types?

Yes I think so!
In general most of this is demand driven: FAQ - MLIR

@mogball may chime in as well.

I think this amounts to adding similar logic to UnitAttr for operation formats to attribute and type formats. It makes sense to add.