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:
1. 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.
2. EnumPythonBindingGen.cpp: there are two weedy aspects here that took investigation:
1. 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).
2. 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 `assemblyFormat`s (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:
1. 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).
2. `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).
3. I turned on `llvm` dialect generation here in order to test `nvvm.WGMMAScaleIn`, which is an enum with [[ d7e26b5620/mlir/include/mlir/IR/EnumAttr.td (L22-L25) | 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 `assemblyFormat`s and `replace=True`).
Reviewed By: stellaraccident
Differential Revision: https://reviews.llvm.org/D157934
82 lines
3.0 KiB
C++
82 lines
3.0 KiB
C++
//===- OpGenHelpers.cpp - MLIR operation generator helpers ----------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines helpers used in the op generators.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "OpGenHelpers.h"
|
|
#include "llvm/ADT/StringSet.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
#include "llvm/Support/Regex.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
|
|
using namespace llvm;
|
|
using namespace mlir;
|
|
using namespace mlir::tblgen;
|
|
|
|
cl::OptionCategory opDefGenCat("Options for op definition generators");
|
|
|
|
static cl::opt<std::string> opIncFilter(
|
|
"op-include-regex",
|
|
cl::desc("Regex of name of op's to include (no filter if empty)"),
|
|
cl::cat(opDefGenCat));
|
|
static cl::opt<std::string> opExcFilter(
|
|
"op-exclude-regex",
|
|
cl::desc("Regex of name of op's to exclude (no filter if empty)"),
|
|
cl::cat(opDefGenCat));
|
|
|
|
static std::string getOperationName(const Record &def) {
|
|
auto prefix = def.getValueAsDef("opDialect")->getValueAsString("name");
|
|
auto opName = def.getValueAsString("opName");
|
|
if (prefix.empty())
|
|
return std::string(opName);
|
|
return std::string(llvm::formatv("{0}.{1}", prefix, opName));
|
|
}
|
|
|
|
std::vector<Record *>
|
|
mlir::tblgen::getRequestedOpDefinitions(const RecordKeeper &recordKeeper) {
|
|
Record *classDef = recordKeeper.getClass("Op");
|
|
if (!classDef)
|
|
PrintFatalError("ERROR: Couldn't find the 'Op' class!\n");
|
|
|
|
llvm::Regex includeRegex(opIncFilter), excludeRegex(opExcFilter);
|
|
std::vector<Record *> defs;
|
|
for (const auto &def : recordKeeper.getDefs()) {
|
|
if (!def.second->isSubClassOf(classDef))
|
|
continue;
|
|
// Include if no include filter or include filter matches.
|
|
if (!opIncFilter.empty() &&
|
|
!includeRegex.match(getOperationName(*def.second)))
|
|
continue;
|
|
// Unless there is an exclude filter and it matches.
|
|
if (!opExcFilter.empty() &&
|
|
excludeRegex.match(getOperationName(*def.second)))
|
|
continue;
|
|
defs.push_back(def.second.get());
|
|
}
|
|
|
|
return defs;
|
|
}
|
|
|
|
bool mlir::tblgen::isPythonReserved(StringRef str) {
|
|
static llvm::StringSet<> reserved({
|
|
"False", "None", "True", "and", "as", "assert", "async",
|
|
"await", "break", "class", "continue", "def", "del", "elif",
|
|
"else", "except", "finally", "for", "from", "global", "if",
|
|
"import", "in", "is", "lambda", "nonlocal", "not", "or",
|
|
"pass", "raise", "return", "try", "while", "with", "yield",
|
|
});
|
|
// These aren't Python keywords but builtin functions that shouldn't/can't be
|
|
// shadowed.
|
|
reserved.insert("callable");
|
|
reserved.insert("issubclass");
|
|
reserved.insert("type");
|
|
return reserved.contains(str);
|
|
} |