Using EnumAttr : missing or conflicting information

Hi Folks.

There seems to be conflicting or missing information on how EnumAttr should be used successfully. I read through whatever code and write-ups I could find - e.g. mlir/IR/, the generated .inc files, mlir reference etc etc, and tried several different ways, but each time I try to have the Enum attribute as an Op argument I run into myriad of problems.
Below is one attempt and error.

def MyEnum: I32EnumAttr<"MyEnum", "my enum tries", [
      I32EnumAttrCase<"RED", 0>,
      I32EnumAttrCase<"GREEN", 1>,
      I32EnumAttrCase<"BLUE", 2> ]> {
  let genSpecializedAttr = 0;
  let cppNamespace = "::mlir::MyDialect";
def MyEnumAttr : EnumAttr<MyDialect, MyEnum, "my_enum">;

def MyOp : MyDialect_Op<"myOp"> {
  let summary = "Trying to get enumAttr to work in an op";
  let description = [{ }];
  let arguments = (ins MyTensorType:$src,
  let results = (outs MyTensorType:$dst);
  let assemblyFormat = 
      "$src $method attr-dict `:` type($src) `->` type($dst)";


error: format ambiguity caused by `:` literal found after attribute `method` which does not have a buildable type
$src $method attr-dict `:` type($src) `->` type($dst)

Based on my previous attempts, I think the problem is in how I am using EnumAttr. If I use MyEnum directly into the MyOp (instead of MyEnumAttr) it compiles but gives new series of errors.

Please have a look at the above code . There MUST be a straightforward way of doing this as I am not doing anything fancy up here except trying to use Enum. I have lots of existing codes w/o Enum that works all fine.

Any thoughts anyone ?

If you look at the error message, it’s complaining about the operation assembly format.

Any attribute with a non-buildable type in an operation assembly format followed by the literal : is a format ambiguity, even with attr-dict in between because it is optional. This is because attributes optionally have types that are parsed separately from the attribute itself: when parsing attributes, the parser looks ahead to find and parse a colon-type.

Regular enums have special handling in the assembly format generator (which is deprecated and will be removed). I will check if a buildable type can be added to enum attributes, which would allow this syntax. For now, you will need to avoid having : after the enum attribute.

Alternatively, you can add a buildable type to the EnumAttr by specifying valueType, a Type that has builderCall, e.g.

def MyEnumAttr : EnumAttr<MyDialect, MyEnum, "my_enum"> {
  let valueType = I32;

I tried changing both above and it just gives new errors.

All I really want to know is how to implement in MLiR a simple Enum attribute e.g.
enum Color { RED, GREEN, BLUE };

What should the tablegen description of ir should be to fit in properly into an Op, if my Op has as argument something like
let arguments = (ins MyTensorType:$src, ColorAttr:$colour);

My definition of ColorAttr is (for which I just followed mlir/IR/

def ColorAttr: I32EnumAttr<"Color", "colour summary", [
      I32EnumAttrCase<"RED", 0>,
      I32EnumAttrCase<"GREEN", 1>,
      I32EnumAttrCase<"BLUE", 2> ]> {
  let genSpecializedAttr = 0;

The MLIR ref doc states , " To facilitate the interaction between *EnumAttrs and their C++ consumers, …TableGen backend can generate a few common utilities: … conversion functions from/to strings.". But the above code fails with error: error: ‚Äė::symbolizeColor‚Äô has not been declared

From above it looks like the default symbolize/stringify were not generated.


any thoughts ? I think an answer to this will not only help me but anyone else who comes along new to using EnumAttr in their implementation.
Thanks a lot.

From the documentation:

This is controlled via the `-gen-enum-decls` and `-gen-enum-defs` command-line options of `mlir-tblgen` .

Have you modified the CMakeLists.txt to invoke mlir-tblgen with those command line options? (You can look at llvm/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt for an example.)

If you are generating the enum sources, it may just be a matter of adding a #include for the generated enum header. As an example, in the LLVM dialect header (llvm/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.h) you will see the following line:

#include "mlir/Dialect/LLVMIR/"

Yes I had those -gen-enum-decls and -gen-enum-defs commands and Enum definitions are generated already. The problem is more intricate I am afraid and lies somewhere else actually.

The following is a similar Enum attribute that we have in the OpenMP dialect. Would something similar work for you?

def OMP_ScheduleModNone         : I32EnumAttrCase<"none", 0>;
def OMP_ScheduleModMonotonic    : I32EnumAttrCase<"monotonic", 1>;
def OMP_ScheduleModNonmonotonic : I32EnumAttrCase<"nonmonotonic", 2>;
def OMP_ScheduleModSIMD         : I32EnumAttrCase<"simd", 3>;

def ScheduleModifier
    : I32EnumAttr<"ScheduleModifier", "OpenMP Schedule Modifier",
                  [OMP_ScheduleModNone, OMP_ScheduleModMonotonic,
                   OMP_ScheduleModNonmonotonic, OMP_ScheduleModSIMD]> {
  let genSpecializedAttr = 0;
  let cppNamespace = "::mlir::omp";
def ScheduleModifierAttr : EnumAttr<OpenMP_Dialect, ScheduleModifier,

Kiran, actually your code highlights another concern which is not clear in MLIR. ‚ÄėScheduleModifier‚Äô is already an EnumAttr, so why did you need to wrap it into another ‚ÄėEnumAttr‚Äô (ScheduleModifierAttr) ?

In case it’s helpful, a few weeks ago I migrated the CIRCT codebase to use the new EnumAttr API, and in my GitHub Pull Request description, I wrote up a bit of information about the new APIs: Update LLVM to 1aa4f0bb by richardxia · Pull Request #2901 · llvm/circt · GitHub

It may not be immediately obvious, but there is indeed an additional layer of nested compared to the older APIs. The key thing to keep in mind is that the I32EnumAttr contains more of the pure Enum-like behavior (i.e. representing an enumeration of values), while the EnumAttr contains more of the Attr-like behavior (something that is attached to Ops).

1 Like