This PR implements python enum bindings for *all* the enums - this includes I*Attrs (including positional/bit) and Dialect/EnumAttr.
There are a few parts to this:
- CMake: a small addition to declare_mlir_dialect_python_bindings and declare_mlir_dialect_extension_python_bindings to generate the enum, a boolean arg GEN_ENUM_BINDINGS to make it opt-in (even though it works for basically all of the dialects), and an optional GEN_ENUM_BINDINGS_TD_FILE for handling corner cases.
- EnumPythonBindingGen.cpp: there are two weedy aspects here that took investigation:
- If an enum attribute is not a Dialect/EnumAttr then the EnumAttrInfo record is canonical, as far as both the cases of the enum and the AttrDefName. On the otherhand, if an enum is a Dialect/EnumAttr then the EnumAttr record has the correct AttrDefName ("load bearing", i.e., populates ods.ir.AttributeBuilder('<NAME>')) but its enum field contains the cases, which is an instance of EnumAttrInfo. The solution is to generate an one enum class for both Dialect/EnumAttr and "independent" EnumAttrInfo but to make that class interopable with two builder registrations that both do the right thing (see next sub-bullet).
- Because we don't have a good connection to cpp EnumAttr, i.e., only the enum class getters are exposed (like DimensionAttr::get(Dimension value)), we have to resort to parsing e.g., Attribute.parse(f'#gpu<dim {x}>'). This means that the set of supported assemblyFormats (for the enum) is fixed at compile of MLIR (currently 2, the only 2 I saw). There might be some things that could be done here but they would require quite a bit more C API work to support generically (e.g., casting ints to enum cases and binding all the getters or going generically through the symbolize* methods, like symbolizeDimension(uint32_t) or symbolizeDimension(StringRef)).
A few small changes:
- In addition, since this patch registers default builders for attributes where people might've had their own builders already written, I added a replace param to AttributeBuilder.insert (False by default).
- makePythonEnumCaseName can't handle all the different ways in which people write their enum cases, e.g., llvm.CConv.Intel_OCL_BI, which gets turned into INTEL_O_C_L_B_I (because llvm::convertToSnakeFromCamelCase doesn't look for runs of caps). So I dropped it. On the otherhand regularization does need to done because some enums have None as a case (and others might have other python keywords).
- I turned on llvm dialect generation here in order to test nvvm.WGMMAScaleIn, which is an enum with no explicit discriminator for the neg case.
Note, dialects that didn't get a GEN_ENUM_BINDINGS don't have any enums to generate.
Let me know if I should add more tests (the three trivial ones I added exercise both the supported assemblyFormats and replace=True).
Can we drop the "relative to ROOT_DIR" part, and point directly at the "real" td file? (the python bindings having their own wrapper td files should be removed, see TODO below)