[Mlir-commits] [mlir] [MLIR][tblgen] generate EnumAttr C bindings (PR #186633)
Jack Greenbaum
llvmlistbot at llvm.org
Wed May 27 15:01:33 PDT 2026
=?utf-8?q?Michał_Górny?= <mgorny at gentoo.org>,Alexey Bataev
<a.bataev at outlook.com>,Mehdi Amini <joker.eph at gmail.com>,Matthew Nagy
<matthew.nagy at sony.com>,jgreenbaum <j.greenbaum at computer.org>,jgreenbaum
<j.greenbaum at computer.org>,jgreenbaum <j.greenbaum at computer.org>,jgreenbaum
<j.greenbaum at computer.org>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186633 at github.com>
https://github.com/jgreenbaum updated https://github.com/llvm/llvm-project/pull/186633
>From 970caa0eed2622586dab7b00c3ef7e14d697a263 Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Tue, 16 Dec 2025 19:41:23 -0800
Subject: [PATCH 01/19] [MLIR][TblGen] add AttrOrTypeCAPIGen
---
mlir/cmake/modules/AddMLIR.cmake | 3 +
mlir/include/mlir/IR/EnumAttr.td | 1 +
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 373 +++++++++++++++++++
mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp | 42 +--
mlir/tools/mlir-tblgen/AttrOrTypeFormatGen.h | 44 +++
mlir/tools/mlir-tblgen/CMakeLists.txt | 1 +
6 files changed, 425 insertions(+), 39 deletions(-)
create mode 100644 mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
diff --git a/mlir/cmake/modules/AddMLIR.cmake b/mlir/cmake/modules/AddMLIR.cmake
index 60b73876d53fe..fa28fa035d986 100644
--- a/mlir/cmake/modules/AddMLIR.cmake
+++ b/mlir/cmake/modules/AddMLIR.cmake
@@ -171,6 +171,9 @@ function(add_mlir_dialect dialect dialect_namespace)
mlir_tablegen(${dialect}.h.inc -gen-op-decls)
mlir_tablegen(${dialect}.cpp.inc -gen-op-defs)
mlir_tablegen(${dialect}Types.h.inc -gen-typedef-decls -typedefs-dialect=${dialect_namespace})
+ mlir_tablegen(${dialect}CAPIEnumAttrs.h.inc -gen-enum-capi-decls -attr-or-type-capi-dialect=${dialect_namespace})
+ mlir_tablegen(${dialect}CAPIAttrs.h.inc -gen-attrdef-capi-decls -attr-or-type-capi-dialect=${dialect_namespace})
+ mlir_tablegen(${dialect}CAPITypes.h.inc -gen-typedef-capi-decls -attr-or-type-capi-dialect=${dialect_namespace})
mlir_tablegen(${dialect}Types.cpp.inc -gen-typedef-defs -typedefs-dialect=${dialect_namespace})
mlir_tablegen(${dialect}Dialect.h.inc -gen-dialect-decls -dialect=${dialect_namespace})
mlir_tablegen(${dialect}Dialect.cpp.inc -gen-dialect-defs -dialect=${dialect_namespace})
diff --git a/mlir/include/mlir/IR/EnumAttr.td b/mlir/include/mlir/IR/EnumAttr.td
index 6eef5075fe18a..01302909efbb8 100644
--- a/mlir/include/mlir/IR/EnumAttr.td
+++ b/mlir/include/mlir/IR/EnumAttr.td
@@ -507,6 +507,7 @@ class EnumParameter<EnumInfo enumInfo>
!cast<EnumAttrInfo>(enumInfo).parameterParser, ?);
let printer = !if(!isa<EnumAttrInfo>(enumInfo),
!cast<EnumAttrInfo>(enumInfo).parameterPrinter, ?);
+ string underlyingEnumName = enumInfo.className;
}
// An attribute backed by a C++ enum. The attribute contains a single
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
new file mode 100644
index 0000000000000..5e5f4f29d44aa
--- /dev/null
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -0,0 +1,373 @@
+//===- AttrOrTypeCAPIGen.cpp - MLIR Attribute and Type CAPI generation ----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "AttrOrTypeFormatGen.h"
+#include "CppGenUtilities.h"
+#include "mlir/TableGen/AttrOrTypeDef.h"
+#include "mlir/TableGen/Class.h"
+#include "mlir/TableGen/EnumInfo.h"
+#include "mlir/TableGen/GenInfo.h"
+#include "mlir/TableGen/Interfaces.h"
+#include "mlir/TableGen/Pass.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
+
+#define DEBUG_TYPE "mlir-tblgen-attr-or-type-capi-gen"
+
+using namespace mlir;
+using namespace mlir::tblgen;
+using llvm::formatv;
+using llvm::Record;
+using llvm::RecordKeeper;
+
+static llvm::cl::OptionCategory attrOrTypeCAPIDefGenCat(
+ "Options for -gen-attr-capi-* and -gen-typedef-capi-*");
+static llvm::cl::opt<std::string>
+ capiDialect("attr-or-type-capi-dialect",
+ llvm::cl::desc("Generate C APIs for this dialect"),
+ llvm::cl::cat(attrOrTypeCAPIDefGenCat),
+ llvm::cl::CommaSeparated);
+static llvm::cl::opt<std::string> capiNamespacePrefix(
+ "attr-or-type-capi-namespace-prefix",
+ llvm::cl::desc("Generate C APIs with this namespace prefix"),
+ llvm::cl::cat(attrOrTypeCAPIDefGenCat));
+
+static std::string makeIdentifier(StringRef str) {
+ if (!str.empty() && llvm::isDigit(static_cast<unsigned char>(str.front()))) {
+ std::string newStr = std::string("_") + str.str();
+ return newStr;
+ }
+ return str.str();
+}
+
+static std::string withCapitalFirstLetter(std::string name) {
+ name[0] = static_cast<std::string::value_type>(
+ std::toupper(static_cast<unsigned char>(name[0])));
+ return name;
+}
+
+static std::string namespacePrefix() {
+ static const std::string prefix = [] {
+ if (!capiNamespacePrefix.empty())
+ return capiNamespacePrefix.getValue();
+ return withCapitalFirstLetter(capiDialect.getValue());
+ }();
+ return prefix;
+}
+
+static void emitAttrTypeHeader(StringRef name, raw_ostream &os) {
+ const char *const header = R"(
+//===----------------------------------------------------------------------===//
+// {0}
+//===----------------------------------------------------------------------===//
+
+)";
+ os << formatv(header, name);
+}
+
+namespace {
+struct CAPIDefGenerator : DefGenerator {
+ CAPIDefGenerator(ArrayRef<const llvm::Record *> defs, raw_ostream &os,
+ const StringRef &defType, const StringRef &valueType,
+ bool isAttrGenerator)
+ : DefGenerator(defs, os, defType, valueType, isAttrGenerator) {}
+
+ bool emitDecls(StringRef selectedDialect) override;
+};
+} // namespace
+
+static bool isUnsupportedParam(const AttrOrTypeParameter ¶m) {
+ if (auto *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
+ const Record *rec = defInit->getDef();
+ if (rec->isSubClassOf("ArrayRefParameter"))
+ return true;
+ if (rec->isSubClassOf("OptionalArrayRefParameter"))
+ return true;
+ }
+ return false;
+}
+
+static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
+ StringRef cppType = param.getCppType();
+ if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
+ const Record *rec = defInit->getDef();
+ if (rec->isSubClassOf("EnumParameter")) {
+ std::string type = "mlir";
+ type += namespacePrefix();
+ type += rec->getValueAsString("underlyingEnumName");
+ return type;
+ }
+ if (rec->isSubClassOf("StringRefParameter"))
+ return "MlirStringRef";
+ }
+ if (cppType == "Type")
+ return "MlirType";
+ if (cppType == "Attribute" || cppType == "::mlir::DictionaryAttr" ||
+ cppType == "::mlir::ArrayAttr" || cppType == "DictionaryAttr" ||
+ cppType == "ArrayAttr")
+ return "MlirAttribute";
+ return cppType.str();
+}
+
+static SmallVector<MethodParameter>
+getGettorParams(ArrayRef<AttrOrTypeParameter> params,
+ std::initializer_list<MethodParameter> prefix) {
+ SmallVector<MethodParameter> builderParams;
+ builderParams.append(prefix.begin(), prefix.end());
+ for (auto ¶m : params) {
+ builderParams.emplace_back(mapParamTypeToCAPI(param), param.getName());
+ }
+ return builderParams;
+}
+
+static void emitGettorDecl(StringRef name, ArrayRef<AttrOrTypeParameter> params,
+ raw_ostream &os, bool isAttrGenerator) {
+ os << "MLIR_CAPI_EXPORTED ";
+ if (isAttrGenerator)
+ os << "MlirAttribute ";
+ else
+ os << "MlirType ";
+ os << "mlir" + namespacePrefix() << name << "Get(";
+ SmallVector<MethodParameter> params_ =
+ getGettorParams(params, {{"MlirContext", "context"}});
+ for (auto [i, param] : llvm::enumerate(params_)) {
+ os << param.getType() << " " << param.getName()
+ << (i < params_.size() - 1 ? ", " : "");
+ }
+ os << ");\n";
+}
+
+static void emitAccessorDecls(StringRef name,
+ ArrayRef<AttrOrTypeParameter> params,
+ raw_ostream &os, bool isAttrGenerator) {
+ for (AttrOrTypeParameter param : params) {
+ if (isUnsupportedParam(param))
+ continue;
+ std::string paramName = param.getName().str();
+ os << "MLIR_CAPI_EXPORTED ";
+ os << mapParamTypeToCAPI(param) << " mlir" << namespacePrefix() << name
+ << "Get" << withCapitalFirstLetter(param.getName().str());
+ if (isAttrGenerator)
+ os << "(MlirAttribute attr);";
+ else
+ os << "(MlirType type);";
+ os << "\n";
+ }
+}
+
+static void emitTypeIDDecl(StringRef name, raw_ostream &os) {
+ os << "MLIR_CAPI_EXPORTED MlirTypeID mlir" << namespacePrefix() << name
+ << "GetTypeID();\n";
+}
+
+static void emitIsADecl(StringRef name, raw_ostream &os, bool isAttrGenerator) {
+ os << "MLIR_CAPI_EXPORTED bool mlir";
+ if (isAttrGenerator)
+ os << "Attribute";
+ else
+ os << "Type";
+ os << "IsA" << namespacePrefix() << name;
+ if (isAttrGenerator)
+ os << "(MlirAttribute attr);";
+ else
+ os << "(MlirType type);";
+ os << "\n";
+}
+
+static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
+ {
+ llvm::IfDefEmitter scope(os, "GET_ENUM_CAPI_DECLS");
+ for (const auto *rec : records) {
+ EnumInfo enumInfo(*rec);
+ os << "#define GET_" + enumInfo.getEnumClassName().upper() +
+ "_ENUM_CAPI_DECL\n";
+ }
+ }
+
+ for (const auto *rec : records) {
+ EnumInfo enumInfo(*rec);
+ llvm::IfDefEmitter scope(os, "GET_" + enumInfo.getEnumClassName().upper() +
+ "_ENUM_CAPI_DECL");
+ os << "// " << enumInfo.getSummary() << "\n";
+ os << "enum mlir" << namespacePrefix() << enumInfo.getEnumClassName();
+
+ if (!enumInfo.getUnderlyingType().empty())
+ os << " : " << enumInfo.getUnderlyingType();
+ os << " {\n";
+
+ for (const EnumCase &enumerant : enumInfo.getAllCases()) {
+ auto symbol = makeIdentifier(enumerant.getSymbol());
+ auto value = enumerant.getValue();
+ if (value >= 0)
+ os << formatv(" {0} = {1},\n", symbol, value);
+ else
+ os << formatv(" {0},\n", symbol);
+ }
+ os << "};\n";
+ }
+
+ os << "\n";
+
+ return false;
+}
+
+static bool emitEnumAttrDecls(ArrayRef<const Record *> records, raw_ostream &os,
+ StringRef selectedDialect) {
+ {
+ llvm::IfDefEmitter scope(os, "GET_ENUM_ATTR_CAPI_DECLS");
+ for (const auto *rec : records) {
+ AttrOrTypeDef attr(&*rec);
+ StringRef dialect = attr.getDialect().getName();
+ if (dialect != selectedDialect)
+ continue;
+ EnumInfo enumInfo(*attr.getDef()->getValueAsDef("enum"));
+ StringRef name = enumInfo.getEnumClassName();
+ os << "#define GET_" + namespacePrefix() + "_" + name.upper() +
+ "_ENUM_ATTR_CAPI_DECL\n";
+ }
+ }
+
+ for (const Record *rec : records) {
+ AttrOrTypeDef attr(&*rec);
+ StringRef dialect = attr.getDialect().getName();
+ if (dialect != selectedDialect)
+ continue;
+
+ EnumInfo enumInfo(*attr.getDef()->getValueAsDef("enum"));
+ llvm::IfDefEmitter scope(os, "GET_" + namespacePrefix() + "_" +
+ enumInfo.getEnumClassName().upper() +
+ "_ENUM_ATTR_CAPI_DECL");
+
+ os << "MLIR_CAPI_EXPORTED MlirAttribute mlir" << namespacePrefix()
+ << enumInfo.getEnumClassName() << "AttrGet(MlirContext context, mlir"
+ << namespacePrefix() << enumInfo.getEnumClassName() << " value);\n";
+
+ std::string name = enumInfo.getEnumClassName().str() + "Attr";
+ emitTypeIDDecl(name, os);
+ emitIsADecl(name, os, /*isAttrGenerator*/ true);
+
+ os << "MLIR_CAPI_EXPORTED mlir" << namespacePrefix()
+ << enumInfo.getEnumClassName() << " mlir";
+ os << namespacePrefix() << name << "GetValue(MlirAttribute attr);\n";
+ }
+
+ os << "\n";
+
+ return false;
+}
+
+bool CAPIDefGenerator::emitDecls(StringRef selectedDialect) {
+ emitSourceFileHeader((defType + "Def C API Def Declarations").str(), os);
+
+ SmallVector<AttrOrTypeDef, 16> defs;
+ collectAllDefs(selectedDialect, defRecords, defs);
+ if (defs.empty())
+ return false;
+
+ {
+ llvm::IfDefEmitter scope(os, "GET_" + defType.upper() + "_CAPI_DECLS");
+ for (const AttrOrTypeDef &def : defs) {
+ StringRef name = def.getCppClassName();
+ os << "#define GET_" + name.upper() + "_" + defType.upper() +
+ "_CAPI_DECL\n";
+ }
+ }
+
+ for (const AttrOrTypeDef &def : defs) {
+ StringRef name = def.getCppClassName();
+ llvm::IfDefEmitter scope(os, "GET_" + name.upper() + "_" + defType.upper() +
+ "_CAPI_DECL");
+
+ ArrayRef<AttrOrTypeParameter> params = def.getParameters();
+ emitAttrTypeHeader(name, os);
+ if (!llvm::any_of(params, isUnsupportedParam))
+ emitGettorDecl(name, params, os, isAttrGenerator);
+ emitTypeIDDecl(name, os);
+ emitIsADecl(name, os, isAttrGenerator);
+ if (def.genAccessors() && !params.empty())
+ emitAccessorDecls(name, params, os, isAttrGenerator);
+ }
+
+ os << "\n";
+
+ return false;
+}
+
+namespace {
+/// A specialized generator for AttrDefs.
+struct CAPIAttrDefGenerator : public CAPIDefGenerator {
+ CAPIAttrDefGenerator(ArrayRef<const llvm::Record *> defs, raw_ostream &os)
+ : CAPIDefGenerator(defs, os, "Attr", "Attribute",
+ /*isAttrGenerator=*/true) {}
+};
+/// A specialized generator for TypeDefs.
+struct CAPITypeDefGenerator : public CAPIDefGenerator {
+ CAPITypeDefGenerator(ArrayRef<const llvm::Record *> defs, raw_ostream &os)
+ : CAPIDefGenerator(defs, os, "Type", "Type",
+ /*isAttrGenerator=*/false) {}
+};
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// GEN: Registration hooks
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// AttrDef
+//===----------------------------------------------------------------------===//
+
+static mlir::GenRegistration genEnumDecls(
+ "gen-enum-capi-decls", "Generate Enum C API declarations",
+ [](const RecordKeeper &records, raw_ostream &os) {
+ emitSourceFileHeader("Enum C API Declarations", os);
+ emitEnumDecls(records.getAllDerivedDefinitionsIfDefined("EnumInfo"), os);
+ emitEnumAttrDecls(records.getAllDerivedDefinitionsIfDefined("EnumAttr"),
+ os, capiDialect);
+ return false;
+ });
+
+static mlir::GenRegistration genAttrDecls(
+ "gen-attrdef-capi-decls", "Generate AttrDef C API declarations",
+ [](const RecordKeeper &records, raw_ostream &os) {
+ CAPIAttrDefGenerator generator(
+ records.getAllDerivedDefinitionsIfDefined("AttrDef"), os);
+ return generator.emitDecls(capiDialect);
+ });
+
+// static mlir::GenRegistration
+// genAttrDefs("gen-attrdef-capi-defs", "Generate AttrDef C API
+// definitions",
+// [](const RecordKeeper &records, raw_ostream &os) {
+// CAPIAttrDefGenerator generator(records, os);
+// return generator.emitDefs(attrDialect);
+// });
+
+//===----------------------------------------------------------------------===//
+// TypeDef
+//===----------------------------------------------------------------------===//
+
+static mlir::GenRegistration genTypeDecls(
+ "gen-typedef-capi-decls", "Generate TypeDef C API declarations",
+ [](const RecordKeeper &records, raw_ostream &os) {
+ CAPITypeDefGenerator generator(
+ records.getAllDerivedDefinitionsIfDefined("TypeDef"), os);
+ return generator.emitDecls(capiDialect);
+ });
+
+// static mlir::GenRegistration
+// genTypeDefs("gen-typedef-capi-defs", "Generate TypeDef C API
+// definitions",
+// [](const RecordKeeper &records, raw_ostream &os) {
+// CAPITypeDefGenerator generator(records, os);
+// return generator.emitDefs(capiDialect);
+// });
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp
index 64f35e7fef6d3..5394e82520ae0 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp
@@ -33,9 +33,9 @@ using llvm::RecordKeeper;
/// Find all the AttrOrTypeDef for the specified dialect. If no dialect
/// specified and can only find one dialect's defs, use that.
-static void collectAllDefs(StringRef selectedDialect,
- ArrayRef<const Record *> records,
- SmallVectorImpl<AttrOrTypeDef> &resultDefs) {
+void mlir::tblgen::collectAllDefs(StringRef selectedDialect,
+ ArrayRef<const Record *> records,
+ SmallVectorImpl<AttrOrTypeDef> &resultDefs) {
// Nothing to do if no defs were found.
if (records.empty())
return;
@@ -829,42 +829,6 @@ void DefGen::emitStorageClass() {
//===----------------------------------------------------------------------===//
namespace {
-/// This struct is the base generator used when processing tablegen interfaces.
-class DefGenerator {
-public:
- bool emitDecls(StringRef selectedDialect);
- bool emitDefs(StringRef selectedDialect);
-
-protected:
- DefGenerator(ArrayRef<const Record *> defs, raw_ostream &os,
- StringRef defType, StringRef valueType, bool isAttrGenerator)
- : defRecords(defs), os(os), defType(defType), valueType(valueType),
- isAttrGenerator(isAttrGenerator) {
- // Sort by occurrence in file.
- llvm::sort(defRecords, [](const Record *lhs, const Record *rhs) {
- return lhs->getID() < rhs->getID();
- });
- }
-
- /// Emit the list of def type names.
- void emitTypeDefList(ArrayRef<AttrOrTypeDef> defs);
- /// Emit the code to dispatch between different defs during parsing/printing.
- void emitParsePrintDispatch(ArrayRef<AttrOrTypeDef> defs);
-
- /// The set of def records to emit.
- std::vector<const Record *> defRecords;
- /// The attribute or type class to emit.
- /// The stream to emit to.
- raw_ostream &os;
- /// The prefix of the tablegen def name, e.g. Attr or Type.
- StringRef defType;
- /// The C++ base value type of the def, e.g. Attribute or Type.
- StringRef valueType;
- /// Flag indicating if this generator is for Attributes. False if the
- /// generator is for types.
- bool isAttrGenerator;
-};
-
/// A specialized generator for AttrDefs.
struct AttrDefGenerator : public DefGenerator {
AttrDefGenerator(const RecordKeeper &records, raw_ostream &os)
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeFormatGen.h b/mlir/tools/mlir-tblgen/AttrOrTypeFormatGen.h
index d4711532a79bb..ca20fdba5ba96 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeFormatGen.h
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeFormatGen.h
@@ -20,6 +20,50 @@ class AttrOrTypeDef;
void generateAttrOrTypeFormat(const AttrOrTypeDef &def, MethodBody &parser,
MethodBody &printer);
+/// Find all the AttrOrTypeDef for the specified dialect. If no dialect
+/// specified and can only find one dialect's defs, use that.
+void collectAllDefs(StringRef selectedDialect,
+ ArrayRef<const llvm::Record *> records,
+ SmallVectorImpl<AttrOrTypeDef> &resultDefs);
+
+/// This struct is the base generator used when processing tablegen interfaces.
+class DefGenerator {
+public:
+ virtual ~DefGenerator() = default;
+ virtual bool emitDecls(StringRef selectedDialect);
+ virtual bool emitDefs(StringRef selectedDialect);
+
+protected:
+ DefGenerator(ArrayRef<const llvm::Record *> defs, raw_ostream &os,
+ StringRef defType, StringRef valueType, bool isAttrGenerator)
+ : defRecords(defs), os(os), defType(defType), valueType(valueType),
+ isAttrGenerator(isAttrGenerator) {
+ // Sort by occurrence in file.
+ llvm::sort(defRecords,
+ [](const llvm::Record *lhs, const llvm::Record *rhs) {
+ return lhs->getID() < rhs->getID();
+ });
+ }
+
+ /// Emit the list of def type names.
+ void emitTypeDefList(ArrayRef<AttrOrTypeDef> defs);
+ /// Emit the code to dispatch between different defs during parsing/printing.
+ void emitParsePrintDispatch(ArrayRef<AttrOrTypeDef> defs);
+
+ /// The set of def records to emit.
+ std::vector<const llvm::Record *> defRecords;
+ /// The attribute or type class to emit.
+ /// The stream to emit to.
+ raw_ostream &os;
+ /// The prefix of the tablegen def name, e.g. Attr or Type.
+ StringRef defType;
+ /// The C++ base value type of the def, e.g. Attribute or Type.
+ StringRef valueType;
+ /// Flag indicating if this generator is for Attributes. False if the
+ /// generator is for types.
+ bool isAttrGenerator;
+};
+
} // namespace tblgen
} // namespace mlir
diff --git a/mlir/tools/mlir-tblgen/CMakeLists.txt b/mlir/tools/mlir-tblgen/CMakeLists.txt
index d7087cba3c874..4256613ce4848 100644
--- a/mlir/tools/mlir-tblgen/CMakeLists.txt
+++ b/mlir/tools/mlir-tblgen/CMakeLists.txt
@@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS
add_tablegen(mlir-tblgen MLIR
DESTINATION "${MLIR_TOOLS_INSTALL_DIR}"
EXPORT MLIR
+ AttrOrTypeCAPIGen.cpp
AttrOrTypeDefGen.cpp
AttrOrTypeFormatGen.cpp
BytecodeDialectGen.cpp
>From 92ce292a38ccc7b0b28d718b6d68f74e2c19f05c Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Fri, 6 Mar 2026 16:06:26 -0800
Subject: [PATCH 02/19] Working with Index dialect
---
mlir/cmake/modules/AddMLIR.cmake | 1 +
mlir/include/mlir-c/Dialect/Index.h | 9 ++
mlir/lib/CAPI/Dialect/Index.cpp | 6 +
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 159 +++++++++++++++----
4 files changed, 147 insertions(+), 28 deletions(-)
diff --git a/mlir/cmake/modules/AddMLIR.cmake b/mlir/cmake/modules/AddMLIR.cmake
index fa28fa035d986..12b07801b38d5 100644
--- a/mlir/cmake/modules/AddMLIR.cmake
+++ b/mlir/cmake/modules/AddMLIR.cmake
@@ -173,6 +173,7 @@ function(add_mlir_dialect dialect dialect_namespace)
mlir_tablegen(${dialect}Types.h.inc -gen-typedef-decls -typedefs-dialect=${dialect_namespace})
mlir_tablegen(${dialect}CAPIEnumAttrs.h.inc -gen-enum-capi-decls -attr-or-type-capi-dialect=${dialect_namespace})
mlir_tablegen(${dialect}CAPIAttrs.h.inc -gen-attrdef-capi-decls -attr-or-type-capi-dialect=${dialect_namespace})
+ mlir_tablegen(${dialect}CAPIAttrs.cpp.inc -gen-attrdef-capi-defs -attr-or-type-capi-dialect=${dialect_namespace})
mlir_tablegen(${dialect}CAPITypes.h.inc -gen-typedef-capi-decls -attr-or-type-capi-dialect=${dialect_namespace})
mlir_tablegen(${dialect}Types.cpp.inc -gen-typedef-defs -typedefs-dialect=${dialect_namespace})
mlir_tablegen(${dialect}Dialect.h.inc -gen-dialect-decls -dialect=${dialect_namespace})
diff --git a/mlir/include/mlir-c/Dialect/Index.h b/mlir/include/mlir-c/Dialect/Index.h
index 3f05694acf7a7..df377240f6cf9 100644
--- a/mlir/include/mlir-c/Dialect/Index.h
+++ b/mlir/include/mlir-c/Dialect/Index.h
@@ -17,6 +17,15 @@ extern "C" {
MLIR_DECLARE_CAPI_DIALECT_REGISTRATION(Index, index);
+// Currently these are opt-in
+#define GET_ENUM_CAPI_DECLS
+#define GET_ENUM_ATTR_CAPI_DECLS
+#include "mlir/Dialect/Index/IR/IndexOpsCAPIEnumAttrs.h.inc"
+#define GET_TYPE_CAPI_DECLS
+#include "mlir/Dialect/Index/IR/IndexOpsCAPITypes.h.inc"
+#define GET_ATTR_CAPI_DECLS
+#include "mlir/Dialect/Index/IR/IndexOpsCAPIAttrs.h.inc"
+
#ifdef __cplusplus
}
#endif
diff --git a/mlir/lib/CAPI/Dialect/Index.cpp b/mlir/lib/CAPI/Dialect/Index.cpp
index 84579143605f9..70868b173f66c 100644
--- a/mlir/lib/CAPI/Dialect/Index.cpp
+++ b/mlir/lib/CAPI/Dialect/Index.cpp
@@ -6,8 +6,14 @@
//
//===----------------------------------------------------------------------===//
+#define GET_OP_CLASSES
+#define GET_ATTRDEF_CLASSES
#include "mlir-c/Dialect/Index.h"
#include "mlir/CAPI/Registration.h"
#include "mlir/Dialect/Index/IR/IndexDialect.h"
+#include "mlir/Dialect/Index/IR/IndexOps.h"
MLIR_DEFINE_CAPI_DIALECT_REGISTRATION(Index, index, mlir::index::IndexDialect)
+
+using mlir::index::IndexCmpPredicateAttr;
+#include "mlir/Dialect/Index/IR/IndexOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index 5e5f4f29d44aa..f0579064a0ee3 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -83,6 +83,7 @@ struct CAPIDefGenerator : DefGenerator {
: DefGenerator(defs, os, defType, valueType, isAttrGenerator) {}
bool emitDecls(StringRef selectedDialect) override;
+ bool emitDefs(StringRef selectedDialect) override;
};
} // namespace
@@ -97,13 +98,60 @@ static bool isUnsupportedParam(const AttrOrTypeParameter ¶m) {
return false;
}
+static std::string toLower(std::string s) {
+ std::transform(s.begin(), s.end(), s.begin(), ::tolower);
+ return s;
+}
+
+static void printInitType(const llvm::Init *I, llvm::raw_ostream &OS) {
+ if (!I) {
+ OS << "null Init\n";
+ return;
+ }
+
+ if (isa<llvm::UnsetInit>(I)) OS << "UnsetInit";
+ else if (isa<llvm::BitInit>(I)) OS << "BitInit";
+ else if (isa<llvm::BitsInit>(I)) OS << "BitsInit";
+ else if (isa<llvm::IntInit>(I)) OS << "IntInit";
+ else if (isa<llvm::StringInit>(I)) OS << "StringInit";
+ else if (isa<llvm::ListInit>(I)) OS << "ListInit";
+ else if (isa<llvm::DefInit>(I)) OS << "DefInit";
+ else if (isa<llvm::VarInit>(I)) OS << "VarInit";
+ else if (isa<llvm::VarBitInit>(I)) OS << "VarBitInit";
+ else if (isa<llvm::FieldInit>(I)) OS << "FieldInit";
+ else if (isa<llvm::DagInit>(I)) OS << "DagInit";
+ else if (isa<llvm::UnOpInit>(I)) OS << "UnOpInit";
+ else if (isa<llvm::BinOpInit>(I)) OS << "BinOpInit";
+ else if (isa<llvm::TernOpInit>(I)) OS << "TernOpInit";
+ else if (isa<llvm::FoldOpInit>(I)) OS << "FoldOpInit";
+ else if (isa<llvm::IsAOpInit>(I)) OS << "IsAOpInit";
+ else if (isa<llvm::ExistsOpInit>(I)) OS << "ExistsOpInit";
+ else if (isa<llvm::CondOpInit>(I)) OS << "CondOpInit";
+ else OS << "Unknown Init";
+
+ OS << " : ";
+ I->print(OS);
+ OS << "\n";
+}
+
static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
StringRef cppType = param.getCppType();
+ // if (cppType.contains("IncludeStyle")) {
+ // llvm::errs() << "Found " << cppType << "\n";
+ // printInitType(param.getDef(), llvm::errs());
+ // }
if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
+ // if (cppType.contains("IncludeStyle")) {
+ // llvm::errs() << "\tDefInit as epected \n";
+ // }
const Record *rec = defInit->getDef();
if (rec->isSubClassOf("EnumParameter")) {
- std::string type = "mlir";
- type += namespacePrefix();
+ // if (cppType.contains("IncludeStyle")) {
+ // llvm::errs() << "\tEnumParameter as epected, underlying is \n";
+ // }
+
+ std::string type = "";
+ type += toLower(namespacePrefix());
type += rec->getValueAsString("underlyingEnumName");
return type;
}
@@ -112,9 +160,7 @@ static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
}
if (cppType == "Type")
return "MlirType";
- if (cppType == "Attribute" || cppType == "::mlir::DictionaryAttr" ||
- cppType == "::mlir::ArrayAttr" || cppType == "DictionaryAttr" ||
- cppType == "ArrayAttr")
+ if (cppType == "Attribute" || cppType.ends_with("Attr"))
return "MlirAttribute";
return cppType.str();
}
@@ -130,21 +176,58 @@ getGettorParams(ArrayRef<AttrOrTypeParameter> params,
return builderParams;
}
-static void emitGettorDecl(StringRef name, ArrayRef<AttrOrTypeParameter> params,
- raw_ostream &os, bool isAttrGenerator) {
+static bool isEnumParam(const AttrOrTypeParameter ¶m) {
+ // Do I need a case for StringInits? I've seen cases where some types are
+ // encoded as strings
+ if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
+ const Record *rec = defInit->getDef();
+ return rec->isSubClassOf("EnumParameter");
+ } else {
+ return false;
+ }
+}
+
+static void emitGettorDeclOrDef(StringRef name, ArrayRef<AttrOrTypeParameter> params,
+ raw_ostream &os, bool isAttrGenerator, bool isDeclGenerator) {
os << "MLIR_CAPI_EXPORTED ";
if (isAttrGenerator)
os << "MlirAttribute ";
else
os << "MlirType ";
- os << "mlir" + namespacePrefix() << name << "Get(";
+ os << llvm::StringRef(namespacePrefix()).lower() << name << "Get(";
SmallVector<MethodParameter> params_ =
getGettorParams(params, {{"MlirContext", "context"}});
for (auto [i, param] : llvm::enumerate(params_)) {
os << param.getType() << " " << param.getName()
- << (i < params_.size() - 1 ? ", " : "");
+ << (i < (params_.size() - 1) ? ", " : "");
+ }
+ if (isDeclGenerator) {
+ os << ");\n";
+ } else {
+ os << ") {\n";
+ os << "\treturn wrap(";
+ os << name << "::get(unwrap(context)";
+ if (params.size() > 0) {
+ os << ", ";
+ }
+ for (auto [i, param] : llvm::enumerate(params)) {
+ // If this is an enum, just use C++ static cast
+ if (isEnumParam(param)) {
+ os << "static_cast<" << param.getCppType() << ">(" << param.getName() << ")";
+ } else {
+ os << "llvm::cast<" << param.getCppType() << ">";
+ // Is this a wrapped type? If so unwrap it, otherwised don't
+ if (llvm::StringRef(mapParamTypeToCAPI(param)).starts_with("Mlir")) {
+ os << "(unwrap(" << param.getName() << "))";
+ } else {
+ os << "(" << param.getName() << ")";
+ }
+ }
+ os << (i < params.size() - 1 ? ", " : "");
+ }
+ os << "));\n";
+ os << "}\n";
}
- os << ");\n";
}
static void emitAccessorDecls(StringRef name,
@@ -155,7 +238,7 @@ static void emitAccessorDecls(StringRef name,
continue;
std::string paramName = param.getName().str();
os << "MLIR_CAPI_EXPORTED ";
- os << mapParamTypeToCAPI(param) << " mlir" << namespacePrefix() << name
+ os << mapParamTypeToCAPI(param) << " " << toLower(namespacePrefix()) << name
<< "Get" << withCapitalFirstLetter(param.getName().str());
if (isAttrGenerator)
os << "(MlirAttribute attr);";
@@ -166,12 +249,12 @@ static void emitAccessorDecls(StringRef name,
}
static void emitTypeIDDecl(StringRef name, raw_ostream &os) {
- os << "MLIR_CAPI_EXPORTED MlirTypeID mlir" << namespacePrefix() << name
+ os << "MLIR_CAPI_EXPORTED MlirTypeID " << toLower(namespacePrefix()) << name
<< "GetTypeID();\n";
}
static void emitIsADecl(StringRef name, raw_ostream &os, bool isAttrGenerator) {
- os << "MLIR_CAPI_EXPORTED bool mlir";
+ os << "MLIR_CAPI_EXPORTED bool mlir"; // JEG: Perhaps this one is correct?
if (isAttrGenerator)
os << "Attribute";
else
@@ -199,7 +282,7 @@ static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
llvm::IfDefEmitter scope(os, "GET_" + enumInfo.getEnumClassName().upper() +
"_ENUM_CAPI_DECL");
os << "// " << enumInfo.getSummary() << "\n";
- os << "enum mlir" << namespacePrefix() << enumInfo.getEnumClassName();
+ os << "enum " << toLower(namespacePrefix()) << enumInfo.getEnumClassName();
if (!enumInfo.getUnderlyingType().empty())
os << " : " << enumInfo.getUnderlyingType();
@@ -214,6 +297,8 @@ static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
os << formatv(" {0},\n", symbol);
}
os << "};\n";
+ // Add convenience typedef
+ os << formatv("typedef enum {0}{1} {0}{1}; \n", toLower(namespacePrefix()), enumInfo.getEnumClassName());
}
os << "\n";
@@ -248,17 +333,17 @@ static bool emitEnumAttrDecls(ArrayRef<const Record *> records, raw_ostream &os,
enumInfo.getEnumClassName().upper() +
"_ENUM_ATTR_CAPI_DECL");
- os << "MLIR_CAPI_EXPORTED MlirAttribute mlir" << namespacePrefix()
- << enumInfo.getEnumClassName() << "AttrGet(MlirContext context, mlir"
- << namespacePrefix() << enumInfo.getEnumClassName() << " value);\n";
+ os << "MLIR_CAPI_EXPORTED MlirAttribute " << namespacePrefix()
+ << enumInfo.getEnumClassName() << "AttrGet(MlirContext context, "
+ << toLower(namespacePrefix()) << enumInfo.getEnumClassName() << " value);\n";
std::string name = enumInfo.getEnumClassName().str() + "Attr";
emitTypeIDDecl(name, os);
emitIsADecl(name, os, /*isAttrGenerator*/ true);
- os << "MLIR_CAPI_EXPORTED mlir" << namespacePrefix()
- << enumInfo.getEnumClassName() << " mlir";
- os << namespacePrefix() << name << "GetValue(MlirAttribute attr);\n";
+ os << "MLIR_CAPI_EXPORTED " << toLower(namespacePrefix())
+ << enumInfo.getEnumClassName() << " ";
+ os << toLower(namespacePrefix()) << name << "GetValue(MlirAttribute attr);\n";
}
os << "\n";
@@ -291,7 +376,7 @@ bool CAPIDefGenerator::emitDecls(StringRef selectedDialect) {
ArrayRef<AttrOrTypeParameter> params = def.getParameters();
emitAttrTypeHeader(name, os);
if (!llvm::any_of(params, isUnsupportedParam))
- emitGettorDecl(name, params, os, isAttrGenerator);
+ emitGettorDeclOrDef(name, params, os, isAttrGenerator, true);
emitTypeIDDecl(name, os);
emitIsADecl(name, os, isAttrGenerator);
if (def.genAccessors() && !params.empty())
@@ -303,6 +388,25 @@ bool CAPIDefGenerator::emitDecls(StringRef selectedDialect) {
return false;
}
+bool CAPIDefGenerator::emitDefs(StringRef selectedDialect) {
+ emitSourceFileHeader((defType + "Def C API Defs").str(), os);
+
+ SmallVector<AttrOrTypeDef, 16> defs;
+ collectAllDefs(selectedDialect, defRecords, defs);
+ if (defs.empty())
+ return false;
+
+ for (const AttrOrTypeDef &def : defs) {
+ StringRef name = def.getCppClassName();
+ ArrayRef<AttrOrTypeParameter> params = def.getParameters();
+ if (!llvm::any_of(params, isUnsupportedParam)) {
+ emitGettorDeclOrDef(name, params, os, isAttrGenerator, false);
+ }
+ }
+
+ return false;
+}
+
namespace {
/// A specialized generator for AttrDefs.
struct CAPIAttrDefGenerator : public CAPIDefGenerator {
@@ -344,13 +448,12 @@ static mlir::GenRegistration genAttrDecls(
return generator.emitDecls(capiDialect);
});
-// static mlir::GenRegistration
-// genAttrDefs("gen-attrdef-capi-defs", "Generate AttrDef C API
-// definitions",
-// [](const RecordKeeper &records, raw_ostream &os) {
-// CAPIAttrDefGenerator generator(records, os);
-// return generator.emitDefs(attrDialect);
-// });
+static mlir::GenRegistration genAttrDefs(
+ "gen-attrdef-capi-defs", "Generate AttrDef C API definitions",
+ [](const RecordKeeper &records, raw_ostream &os) {
+ CAPIAttrDefGenerator generator(records.getAllDerivedDefinitionsIfDefined("AttrDef"), os);
+ return generator.emitDefs(capiDialect);
+});
//===----------------------------------------------------------------------===//
// TypeDef
>From 59836671f299a065f86bb616a42bb32b6bde3c24 Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Fri, 6 Mar 2026 16:18:34 -0800
Subject: [PATCH 03/19] mlir-tblgen C API example
---
.../mlir-c-enums-and-attrs/.gitignore | 2 +
.../mlir-c-enums-and-attrs/CMakeLists.txt | 37 ++++++
.../examples/mlir-c-enums-and-attrs/README.md | 30 +++++
mlir/examples/mlir-c-enums-and-attrs/index.c | 121 ++++++++++++++++++
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 42 ------
5 files changed, 190 insertions(+), 42 deletions(-)
create mode 100644 mlir/examples/mlir-c-enums-and-attrs/.gitignore
create mode 100644 mlir/examples/mlir-c-enums-and-attrs/CMakeLists.txt
create mode 100644 mlir/examples/mlir-c-enums-and-attrs/README.md
create mode 100644 mlir/examples/mlir-c-enums-and-attrs/index.c
diff --git a/mlir/examples/mlir-c-enums-and-attrs/.gitignore b/mlir/examples/mlir-c-enums-and-attrs/.gitignore
new file mode 100644
index 0000000000000..af96791fc63ed
--- /dev/null
+++ b/mlir/examples/mlir-c-enums-and-attrs/.gitignore
@@ -0,0 +1,2 @@
+.vscode
+build
diff --git a/mlir/examples/mlir-c-enums-and-attrs/CMakeLists.txt b/mlir/examples/mlir-c-enums-and-attrs/CMakeLists.txt
new file mode 100644
index 0000000000000..b6423e38b1e3e
--- /dev/null
+++ b/mlir/examples/mlir-c-enums-and-attrs/CMakeLists.txt
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 3.13)
+project(MlirCPlayground LANGUAGES C CXX)
+
+# Pass -DMLIR_INSTALL_DIR=<path to where MLIR is installed>
+list(APPEND CMAKE_PREFIX_PATH "${MLIR_INSTALL_DIR}/lib/cmake")
+
+# Find MLIR and LLVM
+find_package(MLIR REQUIRED CONFIG)
+find_package(LLVM REQUIRED CONFIG)
+
+# Set C++ standard
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# Include MLIR and LLVM headers
+include_directories(${MLIR_INCLUDE_DIRS})
+include_directories(${LLVM_INCLUDE_DIRS})
+include_directories(${MLIR_C_INCLUDE_DIRS})
+
+# Link MLIR and LLVM libraries
+link_directories(${MLIR_LIBRARY_DIRS})
+link_directories(${LLVM_LIBRARY_DIRS})
+# link_directories(${CIRCT_LIBRARY_DIRS})
+
+add_executable(index
+ index.c
+)
+# target_include_directories(index
+# PRIVATE
+# )
+target_link_libraries(index
+ PRIVATE
+
+ MLIRCAPIIR
+ MLIRCAPIFunc
+ MLIRCAPIIndex
+)
diff --git a/mlir/examples/mlir-c-enums-and-attrs/README.md b/mlir/examples/mlir-c-enums-and-attrs/README.md
new file mode 100644
index 0000000000000..d1de36a25395a
--- /dev/null
+++ b/mlir/examples/mlir-c-enums-and-attrs/README.md
@@ -0,0 +1,30 @@
+# Using the generated MLIR C API
+
+This example uses MLIR C API enumerations and attribute types generated by mlir-tblgen. In
+particular it uses the (`index` Dialect `IndexCmpPredicateAttr`)[https://mlir.llvm.org/docs/Dialects/IndexOps/#indexcmppredicateattr] and associated (`IndexCmpPredicate`)[https://mlir.llvm.org/docs/Dialects/IndexOps/#indexcmppredicate] enumeration.
+
+# Building
+
+```
+mkdir build
+cd build
+cmake -G Ninja -DMLIR_INSTALL_DIR=<where you installed your build> ..
+ninja
+```
+
+# Running
+
+```
+build/index
+```
+
+# Output
+
+Here is the output of the program:
+
+```
+func.func @cmp_op(%arg0: index, %arg1: index) {
+ %0 = index.cmp eq(%arg0, %arg1)
+ return
+}
+```
diff --git a/mlir/examples/mlir-c-enums-and-attrs/index.c b/mlir/examples/mlir-c-enums-and-attrs/index.c
new file mode 100644
index 0000000000000..903c0d6e3bb34
--- /dev/null
+++ b/mlir/examples/mlir-c-enums-and-attrs/index.c
@@ -0,0 +1,121 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <mlir-c/IR.h>
+#include <mlir-c/Support.h>
+#include <mlir-c/BuiltinTypes.h>
+#include <mlir-c/BuiltinAttributes.h>
+#include <mlir-c/Dialect/Func.h>
+#include <mlir-c/Dialect/Index.h>
+
+
+// HACK
+typedef enum indexIndexCmpPredicate indexIndexCmpPredicate;
+
+/* forward decls */
+
+// Simple callback for MLIR *Print routines. It just prints to the stdout.
+void print_callback(MlirStringRef r, void *data);
+// Load dialect and error check
+MlirDialect load_dialect(MlirContext ctx, MlirDialectHandle handle);
+
+#define HERE(ctx) (mlirLocationFileLineColGet(ctx, mlirStringRefCreateFromCString(__FILE__),\
+ __LINE__, 0))
+
+int main(int argc, char **argv)
+{
+ // Initialize the MLIR context
+ MlirContext ctx = mlirContextCreate();
+
+ // Load the HW and SV Dialects
+ MlirDialect func = load_dialect(ctx, mlirGetDialectHandle__func__());
+ MlirDialect index = load_dialect(ctx, mlirGetDialectHandle__index__());
+
+ MlirBlock top_block = mlirBlockCreate(0, NULL, NULL);
+
+ MlirType index_type = mlirIndexTypeGet(ctx);
+ MlirBlock func_body = mlirBlockCreate(0, NULL, NULL);
+ MlirRegion func_region = mlirRegionCreate();
+ mlirRegionAppendOwnedBlock(func_region, func_body);
+
+ // Add two arguments, for the a and b parameters
+ MlirValue arg_a = mlirBlockAddArgument(func_body, index_type, HERE(ctx));
+ MlirValue arg_b = mlirBlockAddArgument(func_body, index_type, HERE(ctx));
+ MlirValue cmp_args[] = {arg_a,arg_b};
+
+ MlirAttribute cmp_attr = indexIndexCmpPredicateAttrGet(ctx, EQ);
+
+ MlirNamedAttribute cmp_pred
+ = mlirNamedAttributeGet(mlirIdentifierGet(ctx,mlirStringRefCreateFromCString("pred")),
+ cmp_attr);
+ MlirNamedAttribute cmp_attrs[] = {cmp_pred};
+
+ MlirType cmp_result_type = mlirIntegerTypeGet(ctx,1);
+
+ MlirOperationState cmp_op_state
+ = mlirOperationStateGet(mlirStringRefCreateFromCString("index.cmp"), HERE(ctx));
+ mlirOperationStateAddOperands(&cmp_op_state, 2, cmp_args);
+ mlirOperationStateAddResults(&cmp_op_state, 1, &cmp_result_type);
+ mlirOperationStateAddAttributes(&cmp_op_state, 1, cmp_attrs);
+ MlirOperation cmp_op = mlirOperationCreate(&cmp_op_state);
+ if (mlirOperationIsNull(cmp_op)) {
+ printf("cmp_op is null");
+ exit(-1);
+ }
+ mlirBlockAppendOwnedOperation(func_body, cmp_op);
+
+ MlirOperationState return_op_state
+ = mlirOperationStateGet(mlirStringRefCreateFromCString("func.return"), HERE(ctx));
+ MlirOperation return_op = mlirOperationCreate(&return_op_state);
+ mlirBlockAppendOwnedOperation(func_body, return_op);
+
+ MlirOperationState func_state
+ = mlirOperationStateGet(mlirStringRefCreateFromCString("func.func"), HERE(ctx));
+ // mlirOperationStateAddOperands(&func_state, 2, cmp_args);
+ MlirAttribute func_sym_attr = mlirStringAttrGet(ctx,mlirStringRefCreateFromCString("cmp_op"));
+ MlirNamedAttribute func_named_sym_attr = mlirNamedAttributeGet(
+ mlirIdentifierGet(ctx,mlirStringRefCreateFromCString("sym_name")),
+ func_sym_attr);
+
+
+ MlirType func_input_types[] = {index_type, index_type};
+ MlirType func_type = mlirFunctionTypeGet(ctx, 2, func_input_types, 0, NULL);
+ MlirAttribute func_type_attr = mlirTypeAttrGet(func_type);
+
+ MlirNamedAttribute func_named_type_attr = mlirNamedAttributeGet(
+ mlirIdentifierGet(ctx,mlirStringRefCreateFromCString("function_type")),
+ func_type_attr);
+ MlirNamedAttribute func_attrs[] = {func_named_sym_attr, func_named_type_attr};
+ mlirOperationStateAddAttributes(&func_state, 2, func_attrs);
+ MlirRegion func_regions[] = {func_region};
+ mlirOperationStateAddOwnedRegions(&func_state, 1, func_regions);
+
+
+
+ MlirOperation func_op = mlirOperationCreate(&func_state);
+ if (mlirOperationIsNull(func_op)) {
+ printf("func_op is null");
+ exit(-1);
+ }
+
+ MlirModule top = mlirModuleFromOperation(func_op);
+
+ if (!mlirOperationVerify(func_op)) {
+ printf("func_op failed verification");
+ exit(-1);
+ }
+ mlirOperationDump(func_op);
+ exit(0);
+}
+
+MlirDialect load_dialect(MlirContext ctx, MlirDialectHandle handle)
+{
+ return mlirDialectHandleLoadDialect(handle, ctx);
+}
+
+// Simple callback for MLIR *Print routines. It just prints to the stdout.
+void print_callback(MlirStringRef r, void *data)
+{
+ write(0, r.data, r.length);
+}
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index f0579064a0ee3..45a6e5b523143 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -103,53 +103,11 @@ static std::string toLower(std::string s) {
return s;
}
-static void printInitType(const llvm::Init *I, llvm::raw_ostream &OS) {
- if (!I) {
- OS << "null Init\n";
- return;
- }
-
- if (isa<llvm::UnsetInit>(I)) OS << "UnsetInit";
- else if (isa<llvm::BitInit>(I)) OS << "BitInit";
- else if (isa<llvm::BitsInit>(I)) OS << "BitsInit";
- else if (isa<llvm::IntInit>(I)) OS << "IntInit";
- else if (isa<llvm::StringInit>(I)) OS << "StringInit";
- else if (isa<llvm::ListInit>(I)) OS << "ListInit";
- else if (isa<llvm::DefInit>(I)) OS << "DefInit";
- else if (isa<llvm::VarInit>(I)) OS << "VarInit";
- else if (isa<llvm::VarBitInit>(I)) OS << "VarBitInit";
- else if (isa<llvm::FieldInit>(I)) OS << "FieldInit";
- else if (isa<llvm::DagInit>(I)) OS << "DagInit";
- else if (isa<llvm::UnOpInit>(I)) OS << "UnOpInit";
- else if (isa<llvm::BinOpInit>(I)) OS << "BinOpInit";
- else if (isa<llvm::TernOpInit>(I)) OS << "TernOpInit";
- else if (isa<llvm::FoldOpInit>(I)) OS << "FoldOpInit";
- else if (isa<llvm::IsAOpInit>(I)) OS << "IsAOpInit";
- else if (isa<llvm::ExistsOpInit>(I)) OS << "ExistsOpInit";
- else if (isa<llvm::CondOpInit>(I)) OS << "CondOpInit";
- else OS << "Unknown Init";
-
- OS << " : ";
- I->print(OS);
- OS << "\n";
-}
-
static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
StringRef cppType = param.getCppType();
- // if (cppType.contains("IncludeStyle")) {
- // llvm::errs() << "Found " << cppType << "\n";
- // printInitType(param.getDef(), llvm::errs());
- // }
if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
- // if (cppType.contains("IncludeStyle")) {
- // llvm::errs() << "\tDefInit as epected \n";
- // }
const Record *rec = defInit->getDef();
if (rec->isSubClassOf("EnumParameter")) {
- // if (cppType.contains("IncludeStyle")) {
- // llvm::errs() << "\tEnumParameter as epected, underlying is \n";
- // }
-
std::string type = "";
type += toLower(namespacePrefix());
type += rec->getValueAsString("underlyingEnumName");
>From 12e6818d1d5ee60a46fc39cafbeaa3516e8f3cc3 Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Fri, 13 Mar 2026 17:46:20 -0700
Subject: [PATCH 04/19] Removing example for ease of review
---
.../mlir-c-enums-and-attrs/.gitignore | 2 -
.../mlir-c-enums-and-attrs/CMakeLists.txt | 37 ------
.../examples/mlir-c-enums-and-attrs/README.md | 30 -----
mlir/examples/mlir-c-enums-and-attrs/index.c | 121 ------------------
4 files changed, 190 deletions(-)
delete mode 100644 mlir/examples/mlir-c-enums-and-attrs/.gitignore
delete mode 100644 mlir/examples/mlir-c-enums-and-attrs/CMakeLists.txt
delete mode 100644 mlir/examples/mlir-c-enums-and-attrs/README.md
delete mode 100644 mlir/examples/mlir-c-enums-and-attrs/index.c
diff --git a/mlir/examples/mlir-c-enums-and-attrs/.gitignore b/mlir/examples/mlir-c-enums-and-attrs/.gitignore
deleted file mode 100644
index af96791fc63ed..0000000000000
--- a/mlir/examples/mlir-c-enums-and-attrs/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.vscode
-build
diff --git a/mlir/examples/mlir-c-enums-and-attrs/CMakeLists.txt b/mlir/examples/mlir-c-enums-and-attrs/CMakeLists.txt
deleted file mode 100644
index b6423e38b1e3e..0000000000000
--- a/mlir/examples/mlir-c-enums-and-attrs/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-cmake_minimum_required(VERSION 3.13)
-project(MlirCPlayground LANGUAGES C CXX)
-
-# Pass -DMLIR_INSTALL_DIR=<path to where MLIR is installed>
-list(APPEND CMAKE_PREFIX_PATH "${MLIR_INSTALL_DIR}/lib/cmake")
-
-# Find MLIR and LLVM
-find_package(MLIR REQUIRED CONFIG)
-find_package(LLVM REQUIRED CONFIG)
-
-# Set C++ standard
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
-# Include MLIR and LLVM headers
-include_directories(${MLIR_INCLUDE_DIRS})
-include_directories(${LLVM_INCLUDE_DIRS})
-include_directories(${MLIR_C_INCLUDE_DIRS})
-
-# Link MLIR and LLVM libraries
-link_directories(${MLIR_LIBRARY_DIRS})
-link_directories(${LLVM_LIBRARY_DIRS})
-# link_directories(${CIRCT_LIBRARY_DIRS})
-
-add_executable(index
- index.c
-)
-# target_include_directories(index
-# PRIVATE
-# )
-target_link_libraries(index
- PRIVATE
-
- MLIRCAPIIR
- MLIRCAPIFunc
- MLIRCAPIIndex
-)
diff --git a/mlir/examples/mlir-c-enums-and-attrs/README.md b/mlir/examples/mlir-c-enums-and-attrs/README.md
deleted file mode 100644
index d1de36a25395a..0000000000000
--- a/mlir/examples/mlir-c-enums-and-attrs/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Using the generated MLIR C API
-
-This example uses MLIR C API enumerations and attribute types generated by mlir-tblgen. In
-particular it uses the (`index` Dialect `IndexCmpPredicateAttr`)[https://mlir.llvm.org/docs/Dialects/IndexOps/#indexcmppredicateattr] and associated (`IndexCmpPredicate`)[https://mlir.llvm.org/docs/Dialects/IndexOps/#indexcmppredicate] enumeration.
-
-# Building
-
-```
-mkdir build
-cd build
-cmake -G Ninja -DMLIR_INSTALL_DIR=<where you installed your build> ..
-ninja
-```
-
-# Running
-
-```
-build/index
-```
-
-# Output
-
-Here is the output of the program:
-
-```
-func.func @cmp_op(%arg0: index, %arg1: index) {
- %0 = index.cmp eq(%arg0, %arg1)
- return
-}
-```
diff --git a/mlir/examples/mlir-c-enums-and-attrs/index.c b/mlir/examples/mlir-c-enums-and-attrs/index.c
deleted file mode 100644
index 903c0d6e3bb34..0000000000000
--- a/mlir/examples/mlir-c-enums-and-attrs/index.c
+++ /dev/null
@@ -1,121 +0,0 @@
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <mlir-c/IR.h>
-#include <mlir-c/Support.h>
-#include <mlir-c/BuiltinTypes.h>
-#include <mlir-c/BuiltinAttributes.h>
-#include <mlir-c/Dialect/Func.h>
-#include <mlir-c/Dialect/Index.h>
-
-
-// HACK
-typedef enum indexIndexCmpPredicate indexIndexCmpPredicate;
-
-/* forward decls */
-
-// Simple callback for MLIR *Print routines. It just prints to the stdout.
-void print_callback(MlirStringRef r, void *data);
-// Load dialect and error check
-MlirDialect load_dialect(MlirContext ctx, MlirDialectHandle handle);
-
-#define HERE(ctx) (mlirLocationFileLineColGet(ctx, mlirStringRefCreateFromCString(__FILE__),\
- __LINE__, 0))
-
-int main(int argc, char **argv)
-{
- // Initialize the MLIR context
- MlirContext ctx = mlirContextCreate();
-
- // Load the HW and SV Dialects
- MlirDialect func = load_dialect(ctx, mlirGetDialectHandle__func__());
- MlirDialect index = load_dialect(ctx, mlirGetDialectHandle__index__());
-
- MlirBlock top_block = mlirBlockCreate(0, NULL, NULL);
-
- MlirType index_type = mlirIndexTypeGet(ctx);
- MlirBlock func_body = mlirBlockCreate(0, NULL, NULL);
- MlirRegion func_region = mlirRegionCreate();
- mlirRegionAppendOwnedBlock(func_region, func_body);
-
- // Add two arguments, for the a and b parameters
- MlirValue arg_a = mlirBlockAddArgument(func_body, index_type, HERE(ctx));
- MlirValue arg_b = mlirBlockAddArgument(func_body, index_type, HERE(ctx));
- MlirValue cmp_args[] = {arg_a,arg_b};
-
- MlirAttribute cmp_attr = indexIndexCmpPredicateAttrGet(ctx, EQ);
-
- MlirNamedAttribute cmp_pred
- = mlirNamedAttributeGet(mlirIdentifierGet(ctx,mlirStringRefCreateFromCString("pred")),
- cmp_attr);
- MlirNamedAttribute cmp_attrs[] = {cmp_pred};
-
- MlirType cmp_result_type = mlirIntegerTypeGet(ctx,1);
-
- MlirOperationState cmp_op_state
- = mlirOperationStateGet(mlirStringRefCreateFromCString("index.cmp"), HERE(ctx));
- mlirOperationStateAddOperands(&cmp_op_state, 2, cmp_args);
- mlirOperationStateAddResults(&cmp_op_state, 1, &cmp_result_type);
- mlirOperationStateAddAttributes(&cmp_op_state, 1, cmp_attrs);
- MlirOperation cmp_op = mlirOperationCreate(&cmp_op_state);
- if (mlirOperationIsNull(cmp_op)) {
- printf("cmp_op is null");
- exit(-1);
- }
- mlirBlockAppendOwnedOperation(func_body, cmp_op);
-
- MlirOperationState return_op_state
- = mlirOperationStateGet(mlirStringRefCreateFromCString("func.return"), HERE(ctx));
- MlirOperation return_op = mlirOperationCreate(&return_op_state);
- mlirBlockAppendOwnedOperation(func_body, return_op);
-
- MlirOperationState func_state
- = mlirOperationStateGet(mlirStringRefCreateFromCString("func.func"), HERE(ctx));
- // mlirOperationStateAddOperands(&func_state, 2, cmp_args);
- MlirAttribute func_sym_attr = mlirStringAttrGet(ctx,mlirStringRefCreateFromCString("cmp_op"));
- MlirNamedAttribute func_named_sym_attr = mlirNamedAttributeGet(
- mlirIdentifierGet(ctx,mlirStringRefCreateFromCString("sym_name")),
- func_sym_attr);
-
-
- MlirType func_input_types[] = {index_type, index_type};
- MlirType func_type = mlirFunctionTypeGet(ctx, 2, func_input_types, 0, NULL);
- MlirAttribute func_type_attr = mlirTypeAttrGet(func_type);
-
- MlirNamedAttribute func_named_type_attr = mlirNamedAttributeGet(
- mlirIdentifierGet(ctx,mlirStringRefCreateFromCString("function_type")),
- func_type_attr);
- MlirNamedAttribute func_attrs[] = {func_named_sym_attr, func_named_type_attr};
- mlirOperationStateAddAttributes(&func_state, 2, func_attrs);
- MlirRegion func_regions[] = {func_region};
- mlirOperationStateAddOwnedRegions(&func_state, 1, func_regions);
-
-
-
- MlirOperation func_op = mlirOperationCreate(&func_state);
- if (mlirOperationIsNull(func_op)) {
- printf("func_op is null");
- exit(-1);
- }
-
- MlirModule top = mlirModuleFromOperation(func_op);
-
- if (!mlirOperationVerify(func_op)) {
- printf("func_op failed verification");
- exit(-1);
- }
- mlirOperationDump(func_op);
- exit(0);
-}
-
-MlirDialect load_dialect(MlirContext ctx, MlirDialectHandle handle)
-{
- return mlirDialectHandleLoadDialect(handle, ctx);
-}
-
-// Simple callback for MLIR *Print routines. It just prints to the stdout.
-void print_callback(MlirStringRef r, void *data)
-{
- write(0, r.data, r.length);
-}
>From d317883d389644e89842c8e891c0dca2fa8b3d63 Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Thu, 26 Mar 2026 14:24:41 -0700
Subject: [PATCH 05/19] Use fully qualified names, and unique enum tags
Arith now uses the gen'd code.
---
mlir/include/mlir-c/Dialect/Arith.h | 11 ++
mlir/lib/CAPI/Dialect/Arith.cpp | 3 +
mlir/lib/CAPI/Dialect/Index.cpp | 2 +-
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 117 +++++++++++++++----
4 files changed, 109 insertions(+), 24 deletions(-)
diff --git a/mlir/include/mlir-c/Dialect/Arith.h b/mlir/include/mlir-c/Dialect/Arith.h
index af2e7c2e69287..d19e97bc8262e 100644
--- a/mlir/include/mlir-c/Dialect/Arith.h
+++ b/mlir/include/mlir-c/Dialect/Arith.h
@@ -30,6 +30,17 @@ MLIR_DECLARE_CAPI_DIALECT_REGISTRATION(Arith, arith);
}
#endif
+// Currently these are opt-in
+#define GET_ENUM_CAPI_DECLS
+#define GET_ENUM_ATTR_CAPI_DECLS
+#include "mlir/Dialect/Arith/IR/ArithOpsCAPIEnumAttrs.h.inc"
+#define GET_TYPE_CAPI_DECLS
+#include "mlir/Dialect/Arith/IR/ArithOpsCAPITypes.h.inc"
+#define GET_ATTR_CAPI_DECLS
+#include "mlir/Dialect/Arith/IR/ArithOpsCAPIAttrs.h.inc"
+
+#include "mlir/Dialect/Arith/IR/ArithOpsCAPIEnumAttrs.h.inc"
+
#include "mlir/Dialect/Arith/Transforms/Passes.capi.h.inc"
#endif // MLIR_C_DIALECT_ARITH_H
diff --git a/mlir/lib/CAPI/Dialect/Arith.cpp b/mlir/lib/CAPI/Dialect/Arith.cpp
index 993f77e555ad9..ca0790489933e 100644
--- a/mlir/lib/CAPI/Dialect/Arith.cpp
+++ b/mlir/lib/CAPI/Dialect/Arith.cpp
@@ -9,5 +9,8 @@
#include "mlir-c/Dialect/Arith.h"
#include "mlir/CAPI/Registration.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "llvm/Support/Casting.h"
MLIR_DEFINE_CAPI_DIALECT_REGISTRATION(Arith, arith, mlir::arith::ArithDialect)
+
+#include "mlir/Dialect/Arith/IR/ArithOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
diff --git a/mlir/lib/CAPI/Dialect/Index.cpp b/mlir/lib/CAPI/Dialect/Index.cpp
index 70868b173f66c..74b5c7defffe5 100644
--- a/mlir/lib/CAPI/Dialect/Index.cpp
+++ b/mlir/lib/CAPI/Dialect/Index.cpp
@@ -12,8 +12,8 @@
#include "mlir/CAPI/Registration.h"
#include "mlir/Dialect/Index/IR/IndexDialect.h"
#include "mlir/Dialect/Index/IR/IndexOps.h"
+#include "llvm/Support/Casting.h"
MLIR_DEFINE_CAPI_DIALECT_REGISTRATION(Index, index, mlir::index::IndexDialect)
-using mlir::index::IndexCmpPredicateAttr;
#include "mlir/Dialect/Index/IR/IndexOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index 45a6e5b523143..2a6eaca56f640 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -76,6 +76,9 @@ static void emitAttrTypeHeader(StringRef name, raw_ostream &os) {
}
namespace {
+
+static const bool EMIT_DECLS = true;
+static const bool EMIT_DEFS = false;
struct CAPIDefGenerator : DefGenerator {
CAPIDefGenerator(ArrayRef<const llvm::Record *> defs, raw_ostream &os,
const StringRef &defType, const StringRef &valueType,
@@ -145,14 +148,29 @@ static bool isEnumParam(const AttrOrTypeParameter ¶m) {
}
}
-static void emitGettorDeclOrDef(StringRef name, ArrayRef<AttrOrTypeParameter> params,
+static llvm::StringRef getDefCppType(const AttrOrTypeDef &def) {
+ const llvm::Record *rec = def.getDef();
+ const llvm::RecordVal *name_val = rec->getValue("cppType");
+ const llvm::Init *name_init = name_val->getValue();
+ return llvm::cast<llvm::StringInit>(name_init)->getValue();
+}
+
+static llvm::StringRef getDefCppType(const EnumInfo &def) {
+ const llvm::Record rec = def.getDef();
+ const llvm::RecordVal *name_val = rec.getValue("cppType");
+ const llvm::Init *name_init = name_val->getValue();
+ return llvm::cast<llvm::StringInit>(name_init)->getValue();
+}
+
+
+static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypeParameter> params,
raw_ostream &os, bool isAttrGenerator, bool isDeclGenerator) {
os << "MLIR_CAPI_EXPORTED ";
if (isAttrGenerator)
os << "MlirAttribute ";
else
os << "MlirType ";
- os << llvm::StringRef(namespacePrefix()).lower() << name << "Get(";
+ os << llvm::StringRef(namespacePrefix()).lower() << def.getCppClassName() << "Get(";
SmallVector<MethodParameter> params_ =
getGettorParams(params, {{"MlirContext", "context"}});
for (auto [i, param] : llvm::enumerate(params_)) {
@@ -164,7 +182,7 @@ static void emitGettorDeclOrDef(StringRef name, ArrayRef<AttrOrTypeParameter> pa
} else {
os << ") {\n";
os << "\treturn wrap(";
- os << name << "::get(unwrap(context)";
+ os << getDefCppType(def) << "::get(unwrap(context)";
if (params.size() > 0) {
os << ", ";
}
@@ -188,7 +206,7 @@ static void emitGettorDeclOrDef(StringRef name, ArrayRef<AttrOrTypeParameter> pa
}
}
-static void emitAccessorDecls(StringRef name,
+static void emitAccessorDecls(const AttrOrTypeDef &def,
ArrayRef<AttrOrTypeParameter> params,
raw_ostream &os, bool isAttrGenerator) {
for (AttrOrTypeParameter param : params) {
@@ -196,7 +214,7 @@ static void emitAccessorDecls(StringRef name,
continue;
std::string paramName = param.getName().str();
os << "MLIR_CAPI_EXPORTED ";
- os << mapParamTypeToCAPI(param) << " " << toLower(namespacePrefix()) << name
+ os << mapParamTypeToCAPI(param) << " " << toLower(namespacePrefix()) << def.getCppClassName()
<< "Get" << withCapitalFirstLetter(param.getName().str());
if (isAttrGenerator)
os << "(MlirAttribute attr);";
@@ -206,23 +224,74 @@ static void emitAccessorDecls(StringRef name,
}
}
-static void emitTypeIDDecl(StringRef name, raw_ostream &os) {
- os << "MLIR_CAPI_EXPORTED MlirTypeID " << toLower(namespacePrefix()) << name
- << "GetTypeID();\n";
+static void emitTypeIDDeclOrDef(const AttrOrTypeDef &def, raw_ostream &os, bool isDeclGenerator) {
+ os << "MLIR_CAPI_EXPORTED MlirTypeID " << toLower(namespacePrefix()) << def.getCppClassName()
+ << "GetTypeID(";
+ if (isDeclGenerator) {
+ os << ");\n";
+ } else {
+ os << ") {\n";
+ os << "\treturn wrap(" << getDefCppType(def) << "::getTypeID());\n";
+ os << "}\n";
+ }
+}
+
+static void emitTypeIDDeclOrDef(const EnumInfo &enumInfo, raw_ostream &os, bool isDeclGenerator) {
+ os << "MLIR_CAPI_EXPORTED MlirTypeID " << toLower(namespacePrefix()) << enumInfo.getEnumClassName()
+ << "GetTypeID(";
+ if (isDeclGenerator) {
+ os << ");\n";
+ } else {
+ os << ") {\n";
+ os << "\treturn wrap(" << getDefCppType(enumInfo) << "::getTypeID());\n";
+ os << "}\n";
+ }
}
-static void emitIsADecl(StringRef name, raw_ostream &os, bool isAttrGenerator) {
+static void emitTypeIDDecl(const AttrOrTypeDef &def, raw_ostream &os) {
+ emitTypeIDDeclOrDef(def, os, EMIT_DECLS);
+}
+
+static void emitIsADeclOrDef(const AttrOrTypeDef &def, raw_ostream &os,
+ bool isAttrGenerator, bool isDeclGenerator) {
os << "MLIR_CAPI_EXPORTED bool mlir"; // JEG: Perhaps this one is correct?
if (isAttrGenerator)
os << "Attribute";
else
os << "Type";
- os << "IsA" << namespacePrefix() << name;
+ os << "IsA" << namespacePrefix() << def.getCppClassName();
if (isAttrGenerator)
- os << "(MlirAttribute attr);";
+ os << "(MlirAttribute attr";
else
- os << "(MlirType type);";
- os << "\n";
+ os << "(MlirType type";
+
+ if (isDeclGenerator) {
+ os << ");\n";
+ } else {
+
+ os << ") {\n";
+ os << "\treturn llvm::isa<" << getDefCppType(def)
+ << ">(unwrap(" << (isAttrGenerator? "attr" : "type") << "));\n";
+ os << "}\n";
+ }
+}
+
+static void emitIsADeclOrDef(const EnumInfo &enumInfo, raw_ostream &os,
+ bool isDeclGenerator) {
+ std::string name = enumInfo.getEnumClassName().str() + "Attr";
+ os << "MLIR_CAPI_EXPORTED bool mlirAttributeIsA" << namespacePrefix() << name;
+ os << "(MlirAttribute attr";
+ if (isDeclGenerator) {
+ os << ");\n";
+ } else {
+ os << ") {\n";
+ os << "\treturn llvm::isa<" << getDefCppType(enumInfo) << ">(unwrap(attr));\n";
+ os << "}\n";
+ }
+}
+
+static void emitIsADecl(const AttrOrTypeDef &def, raw_ostream &os, bool isAttrGenerator) {
+ emitIsADeclOrDef(def, os, isAttrGenerator, EMIT_DECLS);
}
static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
@@ -246,13 +315,14 @@ static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
os << " : " << enumInfo.getUnderlyingType();
os << " {\n";
+ auto prefix = formatv("{0}{1}_", namespacePrefix(), enumInfo.getEnumClassName());
for (const EnumCase &enumerant : enumInfo.getAllCases()) {
auto symbol = makeIdentifier(enumerant.getSymbol());
auto value = enumerant.getValue();
if (value >= 0)
- os << formatv(" {0} = {1},\n", symbol, value);
+ os << formatv(" {0}{1} = {2},\n", prefix, symbol, value);
else
- os << formatv(" {0},\n", symbol);
+ os << formatv(" {0}{1},\n", prefix, symbol);
}
os << "};\n";
// Add convenience typedef
@@ -296,8 +366,8 @@ static bool emitEnumAttrDecls(ArrayRef<const Record *> records, raw_ostream &os,
<< toLower(namespacePrefix()) << enumInfo.getEnumClassName() << " value);\n";
std::string name = enumInfo.getEnumClassName().str() + "Attr";
- emitTypeIDDecl(name, os);
- emitIsADecl(name, os, /*isAttrGenerator*/ true);
+ emitTypeIDDeclOrDef(enumInfo, os, EMIT_DECLS);
+ emitIsADeclOrDef(enumInfo, os, EMIT_DECLS);
os << "MLIR_CAPI_EXPORTED " << toLower(namespacePrefix())
<< enumInfo.getEnumClassName() << " ";
@@ -334,11 +404,11 @@ bool CAPIDefGenerator::emitDecls(StringRef selectedDialect) {
ArrayRef<AttrOrTypeParameter> params = def.getParameters();
emitAttrTypeHeader(name, os);
if (!llvm::any_of(params, isUnsupportedParam))
- emitGettorDeclOrDef(name, params, os, isAttrGenerator, true);
- emitTypeIDDecl(name, os);
- emitIsADecl(name, os, isAttrGenerator);
+ emitGettorDeclOrDef(def, params, os, isAttrGenerator, EMIT_DECLS);
+ emitTypeIDDecl(def, os);
+ emitIsADecl(def, os, isAttrGenerator);
if (def.genAccessors() && !params.empty())
- emitAccessorDecls(name, params, os, isAttrGenerator);
+ emitAccessorDecls(def, params, os, isAttrGenerator);
}
os << "\n";
@@ -355,11 +425,12 @@ bool CAPIDefGenerator::emitDefs(StringRef selectedDialect) {
return false;
for (const AttrOrTypeDef &def : defs) {
- StringRef name = def.getCppClassName();
ArrayRef<AttrOrTypeParameter> params = def.getParameters();
if (!llvm::any_of(params, isUnsupportedParam)) {
- emitGettorDeclOrDef(name, params, os, isAttrGenerator, false);
+ emitGettorDeclOrDef(def, params, os, isAttrGenerator, EMIT_DEFS);
}
+ emitTypeIDDeclOrDef(def, os, EMIT_DEFS);
+ emitIsADeclOrDef(def, os, isAttrGenerator, EMIT_DEFS);
}
return false;
>From cb0d25dbbff8c045b5c2f344924023ddc200ff49 Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Wed, 8 Apr 2026 10:11:15 -0700
Subject: [PATCH 06/19] Affine dialect
---
mlir/include/mlir-c/Dialect/Affine.h | 9 +++++++++
mlir/lib/CAPI/Dialect/Affine.cpp | 2 ++
2 files changed, 11 insertions(+)
diff --git a/mlir/include/mlir-c/Dialect/Affine.h b/mlir/include/mlir-c/Dialect/Affine.h
index a5359c7ffc157..d386f5d0f6bb8 100644
--- a/mlir/include/mlir-c/Dialect/Affine.h
+++ b/mlir/include/mlir-c/Dialect/Affine.h
@@ -22,6 +22,15 @@ MLIR_DECLARE_CAPI_DIALECT_REGISTRATION(Affine, affine);
}
#endif
+// Currently these are opt-in
+#define GET_ENUM_CAPI_DECLS
+#define GET_ENUM_ATTR_CAPI_DECLS
+#include "mlir/Dialect/Affine/IR/AffineOpsCAPIEnumAttrs.h.inc"
+#define GET_TYPE_CAPI_DECLS
+#include "mlir/Dialect/Affine/IR/AffineOpsCAPITypes.h.inc"
+#define GET_ATTR_CAPI_DECLS
+#include "mlir/Dialect/Affine/IR/AffineOpsCAPIAttrs.h.inc"
+
#include "mlir/Dialect/Affine/Transforms/Passes.capi.h.inc"
#endif // MLIR_C_DIALECT_AFFINE_H
diff --git a/mlir/lib/CAPI/Dialect/Affine.cpp b/mlir/lib/CAPI/Dialect/Affine.cpp
index b796523390a29..a91bb8cfdbb89 100644
--- a/mlir/lib/CAPI/Dialect/Affine.cpp
+++ b/mlir/lib/CAPI/Dialect/Affine.cpp
@@ -12,3 +12,5 @@
MLIR_DEFINE_CAPI_DIALECT_REGISTRATION(Affine, affine,
mlir::affine::AffineDialect)
+
+#include "mlir/Dialect/Affine/IR/AffineOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
>From c78d8d0ce9964af9373f0d9f537308aae8eac003 Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Wed, 8 Apr 2026 16:06:03 -0700
Subject: [PATCH 07/19] Uniformly use cppNamespace as the prefix
The MLIR CAPI document says all functions/types are prefaced with
"mlir". But to avoid having problems with external dialects that
have the same dialect names but different cpp namespaces, e.g.
dialects like the CIRCT dialects, preface everything with the
dialect namespace, which for CIRCT dialect SV means circt::sv
which gets mangled to circtSv. MLIR's built in dialects don't
change.
---
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 56 +++++++++++++-------
1 file changed, 36 insertions(+), 20 deletions(-)
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index 2a6eaca56f640..b0a1803189b64 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -56,14 +56,14 @@ static std::string withCapitalFirstLetter(std::string name) {
return name;
}
-static std::string namespacePrefix() {
+/* static std::string namespacePrefix() {
static const std::string prefix = [] {
if (!capiNamespacePrefix.empty())
return capiNamespacePrefix.getValue();
return withCapitalFirstLetter(capiDialect.getValue());
}();
return prefix;
-}
+} */
static void emitAttrTypeHeader(StringRef name, raw_ostream &os) {
const char *const header = R"(
@@ -101,9 +101,24 @@ static bool isUnsupportedParam(const AttrOrTypeParameter ¶m) {
return false;
}
-static std::string toLower(std::string s) {
+/* static std::string toLower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
return s;
+} */
+
+// Transforms a C++ namespace string (e.g. "::ns1::ns2") into a camel-case
+// identifier prefix (e.g. "ns1Ns2") suitable for use in C API names.
+static std::string cppNamespaceToPrefix(StringRef ns) {
+ std::string result;
+ SmallVector<StringRef, 4> parts;
+ ns.split(parts, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+ for (auto [i, part] : llvm::enumerate(parts)) {
+ std::string s = part.str();
+ if (i > 0 && !s.empty())
+ s[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(s[0])));
+ result += s;
+ }
+ return result;
}
static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
@@ -112,8 +127,9 @@ static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
const Record *rec = defInit->getDef();
if (rec->isSubClassOf("EnumParameter")) {
std::string type = "";
- type += toLower(namespacePrefix());
- type += rec->getValueAsString("underlyingEnumName");
+ // type += toLower(namespacePrefix());
+ type += cppNamespaceToPrefix(param.getCppType());
+ // type += rec->getValueAsString("underlyingEnumName");
return type;
}
if (rec->isSubClassOf("StringRefParameter"))
@@ -170,7 +186,7 @@ static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypePar
os << "MlirAttribute ";
else
os << "MlirType ";
- os << llvm::StringRef(namespacePrefix()).lower() << def.getCppClassName() << "Get(";
+ os << llvm::StringRef(cppNamespaceToPrefix(def.getDialect().getCppNamespace())) << def.getCppClassName() << "Get(";
SmallVector<MethodParameter> params_ =
getGettorParams(params, {{"MlirContext", "context"}});
for (auto [i, param] : llvm::enumerate(params_)) {
@@ -214,7 +230,7 @@ static void emitAccessorDecls(const AttrOrTypeDef &def,
continue;
std::string paramName = param.getName().str();
os << "MLIR_CAPI_EXPORTED ";
- os << mapParamTypeToCAPI(param) << " " << toLower(namespacePrefix()) << def.getCppClassName()
+ os << mapParamTypeToCAPI(param) << " " << cppNamespaceToPrefix(def.getDialect().getCppNamespace()) << def.getCppClassName()
<< "Get" << withCapitalFirstLetter(param.getName().str());
if (isAttrGenerator)
os << "(MlirAttribute attr);";
@@ -225,7 +241,7 @@ static void emitAccessorDecls(const AttrOrTypeDef &def,
}
static void emitTypeIDDeclOrDef(const AttrOrTypeDef &def, raw_ostream &os, bool isDeclGenerator) {
- os << "MLIR_CAPI_EXPORTED MlirTypeID " << toLower(namespacePrefix()) << def.getCppClassName()
+ os << "MLIR_CAPI_EXPORTED MlirTypeID " << cppNamespaceToPrefix(def.getDialect().getCppNamespace()) << def.getCppClassName()
<< "GetTypeID(";
if (isDeclGenerator) {
os << ");\n";
@@ -237,7 +253,7 @@ static void emitTypeIDDeclOrDef(const AttrOrTypeDef &def, raw_ostream &os, bool
}
static void emitTypeIDDeclOrDef(const EnumInfo &enumInfo, raw_ostream &os, bool isDeclGenerator) {
- os << "MLIR_CAPI_EXPORTED MlirTypeID " << toLower(namespacePrefix()) << enumInfo.getEnumClassName()
+ os << "MLIR_CAPI_EXPORTED MlirTypeID " << cppNamespaceToPrefix(enumInfo.getCppNamespace()) << enumInfo.getEnumClassName()
<< "GetTypeID(";
if (isDeclGenerator) {
os << ");\n";
@@ -259,7 +275,7 @@ static void emitIsADeclOrDef(const AttrOrTypeDef &def, raw_ostream &os,
os << "Attribute";
else
os << "Type";
- os << "IsA" << namespacePrefix() << def.getCppClassName();
+ os << "IsA" << cppNamespaceToPrefix(def.getDialect().getCppNamespace()) << def.getCppClassName();
if (isAttrGenerator)
os << "(MlirAttribute attr";
else
@@ -279,7 +295,7 @@ static void emitIsADeclOrDef(const AttrOrTypeDef &def, raw_ostream &os,
static void emitIsADeclOrDef(const EnumInfo &enumInfo, raw_ostream &os,
bool isDeclGenerator) {
std::string name = enumInfo.getEnumClassName().str() + "Attr";
- os << "MLIR_CAPI_EXPORTED bool mlirAttributeIsA" << namespacePrefix() << name;
+ os << "MLIR_CAPI_EXPORTED bool mlirAttributeIsA" << cppNamespaceToPrefix(enumInfo.getCppNamespace()) << name;
os << "(MlirAttribute attr";
if (isDeclGenerator) {
os << ");\n";
@@ -309,13 +325,13 @@ static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
llvm::IfDefEmitter scope(os, "GET_" + enumInfo.getEnumClassName().upper() +
"_ENUM_CAPI_DECL");
os << "// " << enumInfo.getSummary() << "\n";
- os << "enum " << toLower(namespacePrefix()) << enumInfo.getEnumClassName();
+ os << "enum " << cppNamespaceToPrefix(enumInfo.getCppNamespace()) << enumInfo.getEnumClassName();
if (!enumInfo.getUnderlyingType().empty())
os << " : " << enumInfo.getUnderlyingType();
os << " {\n";
- auto prefix = formatv("{0}{1}_", namespacePrefix(), enumInfo.getEnumClassName());
+ auto prefix = formatv("{0}{1}_", cppNamespaceToPrefix(enumInfo.getCppNamespace()), enumInfo.getEnumClassName());
for (const EnumCase &enumerant : enumInfo.getAllCases()) {
auto symbol = makeIdentifier(enumerant.getSymbol());
auto value = enumerant.getValue();
@@ -326,7 +342,7 @@ static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
}
os << "};\n";
// Add convenience typedef
- os << formatv("typedef enum {0}{1} {0}{1}; \n", toLower(namespacePrefix()), enumInfo.getEnumClassName());
+ os << formatv("typedef enum {0}{1} {0}{1}; \n", cppNamespaceToPrefix(enumInfo.getCppNamespace()), enumInfo.getEnumClassName());
}
os << "\n";
@@ -345,7 +361,7 @@ static bool emitEnumAttrDecls(ArrayRef<const Record *> records, raw_ostream &os,
continue;
EnumInfo enumInfo(*attr.getDef()->getValueAsDef("enum"));
StringRef name = enumInfo.getEnumClassName();
- os << "#define GET_" + namespacePrefix() + "_" + name.upper() +
+ os << "#define GET_" + cppNamespaceToPrefix(enumInfo.getCppNamespace()) + "_" + name.upper() +
"_ENUM_ATTR_CAPI_DECL\n";
}
}
@@ -357,21 +373,21 @@ static bool emitEnumAttrDecls(ArrayRef<const Record *> records, raw_ostream &os,
continue;
EnumInfo enumInfo(*attr.getDef()->getValueAsDef("enum"));
- llvm::IfDefEmitter scope(os, "GET_" + namespacePrefix() + "_" +
+ llvm::IfDefEmitter scope(os, "GET_" + cppNamespaceToPrefix(enumInfo.getCppNamespace()) + "_" +
enumInfo.getEnumClassName().upper() +
"_ENUM_ATTR_CAPI_DECL");
- os << "MLIR_CAPI_EXPORTED MlirAttribute " << namespacePrefix()
+ os << "MLIR_CAPI_EXPORTED MlirAttribute " << cppNamespaceToPrefix(enumInfo.getCppNamespace())
<< enumInfo.getEnumClassName() << "AttrGet(MlirContext context, "
- << toLower(namespacePrefix()) << enumInfo.getEnumClassName() << " value);\n";
+ << cppNamespaceToPrefix(enumInfo.getCppNamespace()) << enumInfo.getEnumClassName() << " value);\n";
std::string name = enumInfo.getEnumClassName().str() + "Attr";
emitTypeIDDeclOrDef(enumInfo, os, EMIT_DECLS);
emitIsADeclOrDef(enumInfo, os, EMIT_DECLS);
- os << "MLIR_CAPI_EXPORTED " << toLower(namespacePrefix())
+ os << "MLIR_CAPI_EXPORTED " << cppNamespaceToPrefix(enumInfo.getCppNamespace())
<< enumInfo.getEnumClassName() << " ";
- os << toLower(namespacePrefix()) << name << "GetValue(MlirAttribute attr);\n";
+ os << cppNamespaceToPrefix(enumInfo.getCppNamespace()) << name << "GetValue(MlirAttribute attr);\n";
}
os << "\n";
>From 628ea36c3f7ba87c393dbf9b2ad73ad78e8e1f90 Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Sun, 12 Apr 2026 11:28:34 -0700
Subject: [PATCH 08/19] Opt out instead of opt in, accessor defs.
---
mlir/include/mlir-c/Dialect/Arith.h | 7 --
mlir/include/mlir-c/Dialect/Index.h | 5 --
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 91 +++++++++++---------
3 files changed, 50 insertions(+), 53 deletions(-)
diff --git a/mlir/include/mlir-c/Dialect/Arith.h b/mlir/include/mlir-c/Dialect/Arith.h
index d19e97bc8262e..da34bc7c4101d 100644
--- a/mlir/include/mlir-c/Dialect/Arith.h
+++ b/mlir/include/mlir-c/Dialect/Arith.h
@@ -30,17 +30,10 @@ MLIR_DECLARE_CAPI_DIALECT_REGISTRATION(Arith, arith);
}
#endif
-// Currently these are opt-in
-#define GET_ENUM_CAPI_DECLS
-#define GET_ENUM_ATTR_CAPI_DECLS
#include "mlir/Dialect/Arith/IR/ArithOpsCAPIEnumAttrs.h.inc"
-#define GET_TYPE_CAPI_DECLS
#include "mlir/Dialect/Arith/IR/ArithOpsCAPITypes.h.inc"
-#define GET_ATTR_CAPI_DECLS
#include "mlir/Dialect/Arith/IR/ArithOpsCAPIAttrs.h.inc"
-#include "mlir/Dialect/Arith/IR/ArithOpsCAPIEnumAttrs.h.inc"
-
#include "mlir/Dialect/Arith/Transforms/Passes.capi.h.inc"
#endif // MLIR_C_DIALECT_ARITH_H
diff --git a/mlir/include/mlir-c/Dialect/Index.h b/mlir/include/mlir-c/Dialect/Index.h
index df377240f6cf9..89dbab2072bb9 100644
--- a/mlir/include/mlir-c/Dialect/Index.h
+++ b/mlir/include/mlir-c/Dialect/Index.h
@@ -17,13 +17,8 @@ extern "C" {
MLIR_DECLARE_CAPI_DIALECT_REGISTRATION(Index, index);
-// Currently these are opt-in
-#define GET_ENUM_CAPI_DECLS
-#define GET_ENUM_ATTR_CAPI_DECLS
#include "mlir/Dialect/Index/IR/IndexOpsCAPIEnumAttrs.h.inc"
-#define GET_TYPE_CAPI_DECLS
#include "mlir/Dialect/Index/IR/IndexOpsCAPITypes.h.inc"
-#define GET_ATTR_CAPI_DECLS
#include "mlir/Dialect/Index/IR/IndexOpsCAPIAttrs.h.inc"
#ifdef __cplusplus
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index b0a1803189b64..fc2e955451b4a 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include <string>
#include "AttrOrTypeFormatGen.h"
#include "CppGenUtilities.h"
#include "mlir/TableGen/AttrOrTypeDef.h"
@@ -121,6 +122,12 @@ static std::string cppNamespaceToPrefix(StringRef ns) {
return result;
}
+static std::string cppNamespaceToUpper(StringRef ns) {
+ std::string prefix = cppNamespaceToPrefix(ns);
+ std::transform(prefix.begin(), prefix.end(), prefix.begin(), ::toupper);
+ return prefix;
+}
+
static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
StringRef cppType = param.getCppType();
if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
@@ -142,6 +149,16 @@ static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
return cppType.str();
}
+static bool paramIsEnum(const AttrOrTypeParameter ¶m) {
+ if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
+ const Record *rec = defInit->getDef();
+ if (rec->isSubClassOf("EnumParameter")) {
+ return true;
+ }
+ }
+ return false;
+}
+
static SmallVector<MethodParameter>
getGettorParams(ArrayRef<AttrOrTypeParameter> params,
std::initializer_list<MethodParameter> prefix) {
@@ -222,9 +239,10 @@ static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypePar
}
}
-static void emitAccessorDecls(const AttrOrTypeDef &def,
+static void emitAccessorDeclsOrDefs(const AttrOrTypeDef &def,
ArrayRef<AttrOrTypeParameter> params,
- raw_ostream &os, bool isAttrGenerator) {
+ raw_ostream &os, bool isAttrGenerator, bool isDeclGenerator) {
+
for (AttrOrTypeParameter param : params) {
if (isUnsupportedParam(param))
continue;
@@ -233,10 +251,28 @@ static void emitAccessorDecls(const AttrOrTypeDef &def,
os << mapParamTypeToCAPI(param) << " " << cppNamespaceToPrefix(def.getDialect().getCppNamespace()) << def.getCppClassName()
<< "Get" << withCapitalFirstLetter(param.getName().str());
if (isAttrGenerator)
- os << "(MlirAttribute attr);";
+ os << "(MlirAttribute attr";
else
- os << "(MlirType type);";
- os << "\n";
+ os << "(MlirType type";
+ if (isDeclGenerator) {
+ os << ");\n";
+ } else {
+ bool isEnumGenerator = paramIsEnum(param);
+ os << ") {\n";
+ if (!isEnumGenerator) {
+ os << "\treturn wrap(";
+ } else {
+ // Enums are enums not objects
+ os << "\treturn (" << mapParamTypeToCAPI(param) << ")";
+ }
+ os << "llvm::cast<" << getDefCppType(def) << ">(unwrap(attr)).get" << withCapitalFirstLetter(param.getName().str()) << "()";
+ if (!isEnumGenerator) {
+ // close the wrap
+ os << ")";
+ }
+ os << ";\n";
+ os << "}\n";
+ }
}
}
@@ -311,18 +347,10 @@ static void emitIsADecl(const AttrOrTypeDef &def, raw_ostream &os, bool isAttrGe
}
static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
- {
- llvm::IfDefEmitter scope(os, "GET_ENUM_CAPI_DECLS");
- for (const auto *rec : records) {
- EnumInfo enumInfo(*rec);
- os << "#define GET_" + enumInfo.getEnumClassName().upper() +
- "_ENUM_CAPI_DECL\n";
- }
- }
for (const auto *rec : records) {
EnumInfo enumInfo(*rec);
- llvm::IfDefEmitter scope(os, "GET_" + enumInfo.getEnumClassName().upper() +
+ llvm::IfNDefGuardEmitter scope(os, "NO_" + cppNamespaceToUpper(enumInfo.getEnumClassName()) +
"_ENUM_CAPI_DECL");
os << "// " << enumInfo.getSummary() << "\n";
os << "enum " << cppNamespaceToPrefix(enumInfo.getCppNamespace()) << enumInfo.getEnumClassName();
@@ -352,20 +380,6 @@ static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
static bool emitEnumAttrDecls(ArrayRef<const Record *> records, raw_ostream &os,
StringRef selectedDialect) {
- {
- llvm::IfDefEmitter scope(os, "GET_ENUM_ATTR_CAPI_DECLS");
- for (const auto *rec : records) {
- AttrOrTypeDef attr(&*rec);
- StringRef dialect = attr.getDialect().getName();
- if (dialect != selectedDialect)
- continue;
- EnumInfo enumInfo(*attr.getDef()->getValueAsDef("enum"));
- StringRef name = enumInfo.getEnumClassName();
- os << "#define GET_" + cppNamespaceToPrefix(enumInfo.getCppNamespace()) + "_" + name.upper() +
- "_ENUM_ATTR_CAPI_DECL\n";
- }
- }
-
for (const Record *rec : records) {
AttrOrTypeDef attr(&*rec);
StringRef dialect = attr.getDialect().getName();
@@ -373,7 +387,7 @@ static bool emitEnumAttrDecls(ArrayRef<const Record *> records, raw_ostream &os,
continue;
EnumInfo enumInfo(*attr.getDef()->getValueAsDef("enum"));
- llvm::IfDefEmitter scope(os, "GET_" + cppNamespaceToPrefix(enumInfo.getCppNamespace()) + "_" +
+ llvm::IfNDefGuardEmitter scope(os, "NO_" + cppNamespaceToUpper(enumInfo.getCppNamespace()) + "_" +
enumInfo.getEnumClassName().upper() +
"_ENUM_ATTR_CAPI_DECL");
@@ -403,19 +417,10 @@ bool CAPIDefGenerator::emitDecls(StringRef selectedDialect) {
if (defs.empty())
return false;
- {
- llvm::IfDefEmitter scope(os, "GET_" + defType.upper() + "_CAPI_DECLS");
- for (const AttrOrTypeDef &def : defs) {
- StringRef name = def.getCppClassName();
- os << "#define GET_" + name.upper() + "_" + defType.upper() +
- "_CAPI_DECL\n";
- }
- }
-
for (const AttrOrTypeDef &def : defs) {
+ // StringRef name = cppNamespaceToPrefix(def.getCppClassName());
StringRef name = def.getCppClassName();
- llvm::IfDefEmitter scope(os, "GET_" + name.upper() + "_" + defType.upper() +
- "_CAPI_DECL");
+ llvm::IfNDefGuardEmitter scope(os, "NO_" + name.upper() + "_CAPI_DECL");
ArrayRef<AttrOrTypeParameter> params = def.getParameters();
emitAttrTypeHeader(name, os);
@@ -424,7 +429,7 @@ bool CAPIDefGenerator::emitDecls(StringRef selectedDialect) {
emitTypeIDDecl(def, os);
emitIsADecl(def, os, isAttrGenerator);
if (def.genAccessors() && !params.empty())
- emitAccessorDecls(def, params, os, isAttrGenerator);
+ emitAccessorDeclsOrDefs(def, params, os, isAttrGenerator, EMIT_DECLS);
}
os << "\n";
@@ -441,12 +446,16 @@ bool CAPIDefGenerator::emitDefs(StringRef selectedDialect) {
return false;
for (const AttrOrTypeDef &def : defs) {
+ // StringRef name = cppNamespaceToPrefix(def.getCppClassName());
+ StringRef name = def.getCppClassName();
+ llvm::IfNDefGuardEmitter scope(os, "NO_" + name.upper() + "_CAPI_DECL");
ArrayRef<AttrOrTypeParameter> params = def.getParameters();
if (!llvm::any_of(params, isUnsupportedParam)) {
emitGettorDeclOrDef(def, params, os, isAttrGenerator, EMIT_DEFS);
}
emitTypeIDDeclOrDef(def, os, EMIT_DEFS);
emitIsADeclOrDef(def, os, isAttrGenerator, EMIT_DEFS);
+ emitAccessorDeclsOrDefs(def, params, os, isAttrGenerator, EMIT_DEFS);
}
return false;
>From c9007a94f525186929f7bc535d037c10c13e1d8b Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Sun, 12 Apr 2026 16:54:15 -0700
Subject: [PATCH 09/19] WIP before trying CIRCT
---
mlir/include/mlir/TableGen/Builder.h | 2 +
mlir/include/mlir/TableGen/Class.h | 13 +-
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 129 ++++++++++++++-----
3 files changed, 106 insertions(+), 38 deletions(-)
diff --git a/mlir/include/mlir/TableGen/Builder.h b/mlir/include/mlir/TableGen/Builder.h
index a733bc569fb31..13decac4c64e9 100644
--- a/mlir/include/mlir/TableGen/Builder.h
+++ b/mlir/include/mlir/TableGen/Builder.h
@@ -46,6 +46,8 @@ class Builder {
/// parameter.
std::optional<StringRef> getDefaultValue() const;
+ const llvm::Init *getDef() const { return def; };
+
private:
Parameter(std::optional<StringRef> name, const llvm::Init *def)
: name(name), def(def) {}
diff --git a/mlir/include/mlir/TableGen/Class.h b/mlir/include/mlir/TableGen/Class.h
index e6bedc7cc896d..27aa7c33f8d86 100644
--- a/mlir/include/mlir/TableGen/Class.h
+++ b/mlir/include/mlir/TableGen/Class.h
@@ -47,18 +47,19 @@ class MethodParameter {
/// the generated code.
template <typename TypeT, typename NameT, typename DefaultT>
MethodParameter(TypeT &&type, NameT &&name, DefaultT &&defaultValue,
- bool optional = false)
+ bool optional = false, bool is_enum_param = false)
: type(stringify(std::forward<TypeT>(type))),
name(stringify(std::forward<NameT>(name))),
defaultValue(stringify(std::forward<DefaultT>(defaultValue))),
- optional(optional) {}
+ optional(optional),
+ is_enum_param(is_enum_param) {}
/// Create a method parameter with a C++ type, parameter name, and no default
/// value.
template <typename TypeT, typename NameT>
- MethodParameter(TypeT &&type, NameT &&name, bool optional = false)
+ MethodParameter(TypeT &&type, NameT &&name, bool optional = false, bool is_enum_param = false)
: MethodParameter(std::forward<TypeT>(type), std::forward<NameT>(name),
- /*defaultValue=*/"", optional) {}
+ /*defaultValue=*/"", optional, is_enum_param) {}
/// Write the parameter as part of a method declaration.
void writeDeclTo(raw_indented_ostream &os) const;
@@ -75,6 +76,8 @@ class MethodParameter {
StringRef getDefaultValue() const { return defaultValue; }
/// Returns true if the parameter is optional.
bool isOptional() const { return optional; }
+ /// Returns true if the parameter is an Enum type vs an object
+ bool isEnumParam() const { return is_enum_param; }
private:
/// The C++ type.
@@ -86,6 +89,8 @@ class MethodParameter {
std::string defaultValue;
/// Whether the parameter should be indicated as "optional".
bool optional;
+ /// Whether the parameter is an Enum type vs an object
+ bool is_enum_param;
};
/// This class contains a list of method parameters for constructor, class
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index fc2e955451b4a..4459a9e382642 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -86,8 +86,15 @@ struct CAPIDefGenerator : DefGenerator {
bool isAttrGenerator)
: DefGenerator(defs, os, defType, valueType, isAttrGenerator) {}
- bool emitDecls(StringRef selectedDialect) override;
- bool emitDefs(StringRef selectedDialect) override;
+ bool emitDecls(StringRef selectedDialect) override {
+ return emitDeclsOrDefs(selectedDialect, EMIT_DECLS);
+ }
+ bool emitDefs(StringRef selectedDialect) override {
+ return emitDeclsOrDefs(selectedDialect, EMIT_DEFS);
+ }
+
+ bool emitDeclsOrDefs(StringRef selectedDialect, bool isDeclGenerator);
+
};
} // namespace
@@ -128,14 +135,36 @@ static std::string cppNamespaceToUpper(StringRef ns) {
return prefix;
}
-static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
- StringRef cppType = param.getCppType();
- if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
+// Inverse of cppNamespaceToPrefix: converts a lower-camel-case identifier like
+// "classDialectSomeMoreWords" into "class::dialect::SomeMoreWords", treating
+// the first `numNsParts` camel-case words as (lowercased) namespace segments.
+static std::string camelCaseToCppNamespace(StringRef name,
+ unsigned numNsParts = 2) {
+ std::string result;
+ unsigned partsSeen = 0;
+ size_t wordStart = 0;
+
+ for (size_t i = 1; i <= name.size() && partsSeen < numNsParts; ++i) {
+ if (i == name.size() ||
+ llvm::isUpper(static_cast<unsigned char>(name[i]))) {
+ result += name.slice(wordStart, i).lower() + "::";
+ wordStart = i;
+ ++partsSeen;
+ }
+ }
+
+ result += name.drop_front(wordStart).str();
+ return result;
+}
+
+static std::string mapParamTypeStringToCAPI(StringRef cppType,
+ const llvm::Init *def) {
+ if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(def)) {
const Record *rec = defInit->getDef();
if (rec->isSubClassOf("EnumParameter")) {
std::string type = "";
// type += toLower(namespacePrefix());
- type += cppNamespaceToPrefix(param.getCppType());
+ type += cppNamespaceToPrefix(cppType);
// type += rec->getValueAsString("underlyingEnumName");
return type;
}
@@ -146,9 +175,22 @@ static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
return "MlirType";
if (cppType == "Attribute" || cppType.ends_with("Attr"))
return "MlirAttribute";
+ if (cppType == "::llvm::StringRef") {
+ return "StringRef";
+ }
return cppType.str();
}
+// TODO: Is there a common supertype for these two?
+
+static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
+ return mapParamTypeStringToCAPI(param.getCppType(), param.getDef());
+}
+
+static std::string mapParamTypeToCAPI(const Builder::Parameter ¶m) {
+ return mapParamTypeStringToCAPI(param.getCppType(), param.getDef());
+}
+
static bool paramIsEnum(const AttrOrTypeParameter ¶m) {
if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
const Record *rec = defInit->getDef();
@@ -159,17 +201,6 @@ static bool paramIsEnum(const AttrOrTypeParameter ¶m) {
return false;
}
-static SmallVector<MethodParameter>
-getGettorParams(ArrayRef<AttrOrTypeParameter> params,
- std::initializer_list<MethodParameter> prefix) {
- SmallVector<MethodParameter> builderParams;
- builderParams.append(prefix.begin(), prefix.end());
- for (auto ¶m : params) {
- builderParams.emplace_back(mapParamTypeToCAPI(param), param.getName());
- }
- return builderParams;
-}
-
static bool isEnumParam(const AttrOrTypeParameter ¶m) {
// Do I need a case for StringInits? I've seen cases where some types are
// encoded as strings
@@ -181,6 +212,25 @@ static bool isEnumParam(const AttrOrTypeParameter ¶m) {
}
}
+
+static SmallVector<MethodParameter>
+getGettorParams(ArrayRef<AttrOrTypeParameter> params) {
+ SmallVector<MethodParameter> builderParams;
+ for (auto ¶m : params) {
+ builderParams.emplace_back(mapParamTypeToCAPI(param), param.getName(), false, isEnumParam(param));
+ }
+ return builderParams;
+}
+
+static SmallVector<MethodParameter>
+getGettorParams(ArrayRef<mlir::tblgen::Builder::Parameter> params) {
+ SmallVector<MethodParameter> builderParams;
+ for (auto ¶m : params) {
+ builderParams.emplace_back(mapParamTypeToCAPI(param), param.getName());
+ }
+ return builderParams;
+}
+
static llvm::StringRef getDefCppType(const AttrOrTypeDef &def) {
const llvm::Record *rec = def.getDef();
const llvm::RecordVal *name_val = rec->getValue("cppType");
@@ -196,16 +246,24 @@ static llvm::StringRef getDefCppType(const EnumInfo &def) {
}
-static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypeParameter> params,
- raw_ostream &os, bool isAttrGenerator, bool isDeclGenerator) {
+
+static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<MethodParameter> params,
+ raw_ostream &os, bool isAttrGenerator, bool isDeclGenerator, unsigned altIndex) {
os << "MLIR_CAPI_EXPORTED ";
if (isAttrGenerator)
os << "MlirAttribute ";
else
os << "MlirType ";
- os << llvm::StringRef(cppNamespaceToPrefix(def.getDialect().getCppNamespace())) << def.getCppClassName() << "Get(";
- SmallVector<MethodParameter> params_ =
- getGettorParams(params, {{"MlirContext", "context"}});
+ os << llvm::StringRef(cppNamespaceToPrefix(def.getDialect().getCppNamespace())) << def.getCppClassName() << "Get";
+ if (altIndex == 0) {
+ os << "(";
+ } else {
+ os << "Alt" << altIndex << "(";
+ }
+ SmallVector<MethodParameter> prefix = {{"MlirContext", "context"}};
+ SmallVector<MethodParameter> params_;
+ params_.insert(params_.begin(), prefix.begin(), prefix.end());
+ params_.insert(params_.end(), params.begin(), params.end());
for (auto [i, param] : llvm::enumerate(params_)) {
os << param.getType() << " " << param.getName()
<< (i < (params_.size() - 1) ? ", " : "");
@@ -221,12 +279,12 @@ static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypePar
}
for (auto [i, param] : llvm::enumerate(params)) {
// If this is an enum, just use C++ static cast
- if (isEnumParam(param)) {
- os << "static_cast<" << param.getCppType() << ">(" << param.getName() << ")";
+ if (param.isEnumParam()) {
+ os << "(" << camelCaseToCppNamespace(param.getType()) << ")" << param.getName();
} else {
- os << "llvm::cast<" << param.getCppType() << ">";
+ os << "llvm::cast<" << param.getType() << ">";
// Is this a wrapped type? If so unwrap it, otherwised don't
- if (llvm::StringRef(mapParamTypeToCAPI(param)).starts_with("Mlir")) {
+ if (param.getType().starts_with("Mlir")) {
os << "(unwrap(" << param.getName() << "))";
} else {
os << "(" << param.getName() << ")";
@@ -405,11 +463,10 @@ static bool emitEnumAttrDecls(ArrayRef<const Record *> records, raw_ostream &os,
}
os << "\n";
-
return false;
}
-bool CAPIDefGenerator::emitDecls(StringRef selectedDialect) {
+bool CAPIDefGenerator::emitDeclsOrDefs(StringRef selectedDialect, bool isDeclGenerator) {
emitSourceFileHeader((defType + "Def C API Def Declarations").str(), os);
SmallVector<AttrOrTypeDef, 16> defs;
@@ -424,20 +481,24 @@ bool CAPIDefGenerator::emitDecls(StringRef selectedDialect) {
ArrayRef<AttrOrTypeParameter> params = def.getParameters();
emitAttrTypeHeader(name, os);
- if (!llvm::any_of(params, isUnsupportedParam))
- emitGettorDeclOrDef(def, params, os, isAttrGenerator, EMIT_DECLS);
+ if (!def.skipDefaultBuilders() && !llvm::any_of(params, isUnsupportedParam))
+ emitGettorDeclOrDef(def, getGettorParams(params), os, isAttrGenerator, isDeclGenerator, 0);
+ unsigned altNum = 1;
+ for (const AttrOrTypeBuilder &builder : def.getBuilders()) {
+ emitGettorDeclOrDef(def, getGettorParams(builder.getParameters()), os, isAttrGenerator, isDeclGenerator, altNum);
+ altNum++;
+ }
emitTypeIDDecl(def, os);
emitIsADecl(def, os, isAttrGenerator);
if (def.genAccessors() && !params.empty())
- emitAccessorDeclsOrDefs(def, params, os, isAttrGenerator, EMIT_DECLS);
+ emitAccessorDeclsOrDefs(def, params, os, isAttrGenerator, isDeclGenerator);
}
-
os << "\n";
return false;
}
-bool CAPIDefGenerator::emitDefs(StringRef selectedDialect) {
+/* bool CAPIDefGenerator::emitDefs(StringRef selectedDialect) {
emitSourceFileHeader((defType + "Def C API Defs").str(), os);
SmallVector<AttrOrTypeDef, 16> defs;
@@ -459,7 +520,7 @@ bool CAPIDefGenerator::emitDefs(StringRef selectedDialect) {
}
return false;
-}
+}*/
namespace {
/// A specialized generator for AttrDefs.
>From 1e44016c4d56dc758bfac5148b5be5f02f1509bb Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Fri, 17 Apr 2026 17:08:28 -0700
Subject: [PATCH 10/19] Arith and Index dialects, enums working
---
mlir/include/mlir-c/Dialect/Arith.h | 6 +-
mlir/lib/CAPI/Dialect/Arith.cpp | 2 +-
mlir/lib/CAPI/Dialect/Index.cpp | 2 +-
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 172 ++++++++++++++-----
4 files changed, 130 insertions(+), 52 deletions(-)
diff --git a/mlir/include/mlir-c/Dialect/Arith.h b/mlir/include/mlir-c/Dialect/Arith.h
index da34bc7c4101d..120d16f9ad590 100644
--- a/mlir/include/mlir-c/Dialect/Arith.h
+++ b/mlir/include/mlir-c/Dialect/Arith.h
@@ -30,9 +30,9 @@ MLIR_DECLARE_CAPI_DIALECT_REGISTRATION(Arith, arith);
}
#endif
-#include "mlir/Dialect/Arith/IR/ArithOpsCAPIEnumAttrs.h.inc"
-#include "mlir/Dialect/Arith/IR/ArithOpsCAPITypes.h.inc"
-#include "mlir/Dialect/Arith/IR/ArithOpsCAPIAttrs.h.inc"
+// #include "mlir/Dialect/Arith/IR/ArithOpsCAPIEnumAttrs.h.inc"
+// #include "mlir/Dialect/Arith/IR/ArithOpsCAPITypes.h.inc"
+// #include "mlir/Dialect/Arith/IR/ArithOpsCAPIAttrs.h.inc"
#include "mlir/Dialect/Arith/Transforms/Passes.capi.h.inc"
diff --git a/mlir/lib/CAPI/Dialect/Arith.cpp b/mlir/lib/CAPI/Dialect/Arith.cpp
index ca0790489933e..8bd903592f285 100644
--- a/mlir/lib/CAPI/Dialect/Arith.cpp
+++ b/mlir/lib/CAPI/Dialect/Arith.cpp
@@ -13,4 +13,4 @@
MLIR_DEFINE_CAPI_DIALECT_REGISTRATION(Arith, arith, mlir::arith::ArithDialect)
-#include "mlir/Dialect/Arith/IR/ArithOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
+// #include "mlir/Dialect/Arith/IR/ArithOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
diff --git a/mlir/lib/CAPI/Dialect/Index.cpp b/mlir/lib/CAPI/Dialect/Index.cpp
index 74b5c7defffe5..b736d328aa20e 100644
--- a/mlir/lib/CAPI/Dialect/Index.cpp
+++ b/mlir/lib/CAPI/Dialect/Index.cpp
@@ -16,4 +16,4 @@
MLIR_DEFINE_CAPI_DIALECT_REGISTRATION(Index, index, mlir::index::IndexDialect)
-#include "mlir/Dialect/Index/IR/IndexOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
+// #include "mlir/Dialect/Index/IR/IndexOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index 4459a9e382642..b8a9bd10fee21 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -138,7 +138,7 @@ static std::string cppNamespaceToUpper(StringRef ns) {
// Inverse of cppNamespaceToPrefix: converts a lower-camel-case identifier like
// "classDialectSomeMoreWords" into "class::dialect::SomeMoreWords", treating
// the first `numNsParts` camel-case words as (lowercased) namespace segments.
-static std::string camelCaseToCppNamespace(StringRef name,
+/* static std::string camelCaseToCppNamespace(StringRef name,
unsigned numNsParts = 2) {
std::string result;
unsigned partsSeen = 0;
@@ -155,40 +155,106 @@ static std::string camelCaseToCppNamespace(StringRef name,
result += name.drop_front(wordStart).str();
return result;
-}
+} */
+
+struct AttrOrTypeOrBuilderParam
+{
+ // Only one of these must be non-null at a type
+ private:
+ StringRef name;
+ bool is_builder_param;
+ const void *ptr;
+
+ public:
+ AttrOrTypeOrBuilderParam(const AttrOrTypeParameter ¶m)
+ : name(param.getName()), is_builder_param(false), ptr(¶m) {}
+ AttrOrTypeOrBuilderParam(const AttrOrTypeParameter ¶m, StringRef name)
+ : name(name), is_builder_param(false), ptr(¶m) {}
+ AttrOrTypeOrBuilderParam(const Builder::Parameter ¶m, StringRef name)
+ : name(name), is_builder_param(true), ptr(¶m) {}
+
+ StringRef getName() const {
+ if (is_builder_param) {
+ return ((const Builder::Parameter *)ptr)->getName().value();
+ } else {
+ return ((const AttrOrTypeParameter*)ptr)->getName();
+ }
+ }
+
+
+
+ std::string getCAPIType() const;
-static std::string mapParamTypeStringToCAPI(StringRef cppType,
- const llvm::Init *def) {
- if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(def)) {
+ StringRef getCppType() const {
+ if (is_builder_param) {
+ return ((const Builder::Parameter *)ptr)->getCppType();
+ } else {
+ return ((const AttrOrTypeParameter*)ptr)->getCppType();
+ }
+ }
+
+ const llvm::Init * getDef() const {
+ if (is_builder_param) {
+ return ((const Builder::Parameter *)ptr)->getDef();
+ } else {
+ return ((const AttrOrTypeParameter*)ptr)->getDef();
+ }
+ }
+
+
+ bool isEnumParam() const {
+ const llvm::Init *def = getDef();
+ if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(def)) {
+ const Record *rec = defInit->getDef();
+ return rec->isSubClassOf("EnumParameter");
+ } else {
+ return false;
+ }
+ }
+
+};
+
+/**
+ * @brief Map an AttrOrTypeOrBuilderParam to its CAPI type
+ *
+ * @param param
+ * @param return_type True if this param represents a return value, for example as the return value of an attribute getter.
+ * @return std::string
+ */
+static std::string mapParamTypeToCAPI(const AttrOrTypeOrBuilderParam *param, bool return_type) {
+ if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param->getDef())) {
const Record *rec = defInit->getDef();
if (rec->isSubClassOf("EnumParameter")) {
std::string type = "";
- // type += toLower(namespacePrefix());
- type += cppNamespaceToPrefix(cppType);
+ type += cppNamespaceToPrefix(param->getCppType());
// type += rec->getValueAsString("underlyingEnumName");
return type;
}
if (rec->isSubClassOf("StringRefParameter"))
return "MlirStringRef";
}
- if (cppType == "Type")
+ auto cppType = param->getCppType();
+ if (cppType == "Type" || cppType == "::mlir::Type")
return "MlirType";
- if (cppType == "Attribute" || cppType.ends_with("Attr"))
- return "MlirAttribute";
+ if (cppType == "Attribute" || cppType.ends_with("Attr")) {
+ if (return_type && cppType == "::mlir::StringAttr") {
+ // For some reason these map to MlirIdentifier when wrapped instead of MlirAttribute
+ return "MlirIdentifier";
+ } else {
+ return "MlirAttribute";
+ }
+ }
if (cppType == "::llvm::StringRef") {
- return "StringRef";
+ return "MlirStringRef";
+ }
+ if (cppType.starts_with("::")) {
+ return cppNamespaceToPrefix(cppType);
}
return cppType.str();
}
-// TODO: Is there a common supertype for these two?
-
-static std::string mapParamTypeToCAPI(const AttrOrTypeParameter ¶m) {
- return mapParamTypeStringToCAPI(param.getCppType(), param.getDef());
-}
-
-static std::string mapParamTypeToCAPI(const Builder::Parameter ¶m) {
- return mapParamTypeStringToCAPI(param.getCppType(), param.getDef());
+std::string AttrOrTypeOrBuilderParam::getCAPIType() const {
+ return mapParamTypeToCAPI(this, false);
}
static bool paramIsEnum(const AttrOrTypeParameter ¶m) {
@@ -201,32 +267,31 @@ static bool paramIsEnum(const AttrOrTypeParameter ¶m) {
return false;
}
-static bool isEnumParam(const AttrOrTypeParameter ¶m) {
+/* static bool isEnumParam(const AttrOrTypeParameter ¶m) {
// Do I need a case for StringInits? I've seen cases where some types are
// encoded as strings
if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
const Record *rec = defInit->getDef();
return rec->isSubClassOf("EnumParameter");
} else {
- return false;
+ return paramIsEnum(param);
}
-}
-
+} */
-static SmallVector<MethodParameter>
-getGettorParams(ArrayRef<AttrOrTypeParameter> params) {
- SmallVector<MethodParameter> builderParams;
+static SmallVector<AttrOrTypeOrBuilderParam>
+getGettorParams(ArrayRef<AttrOrTypeParameter> const params) {
+ SmallVector<AttrOrTypeOrBuilderParam> builderParams;
for (auto ¶m : params) {
- builderParams.emplace_back(mapParamTypeToCAPI(param), param.getName(), false, isEnumParam(param));
+ builderParams.emplace_back(param, param.getName());
}
return builderParams;
}
-static SmallVector<MethodParameter>
+static SmallVector<AttrOrTypeOrBuilderParam>
getGettorParams(ArrayRef<mlir::tblgen::Builder::Parameter> params) {
- SmallVector<MethodParameter> builderParams;
+ SmallVector<AttrOrTypeOrBuilderParam> builderParams;
for (auto ¶m : params) {
- builderParams.emplace_back(mapParamTypeToCAPI(param), param.getName());
+ builderParams.emplace_back(param, param.getName().value());
}
return builderParams;
}
@@ -247,7 +312,7 @@ static llvm::StringRef getDefCppType(const EnumInfo &def) {
-static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<MethodParameter> params,
+static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypeOrBuilderParam> params,
raw_ostream &os, bool isAttrGenerator, bool isDeclGenerator, unsigned altIndex) {
os << "MLIR_CAPI_EXPORTED ";
if (isAttrGenerator)
@@ -260,13 +325,17 @@ static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<MethodParamet
} else {
os << "Alt" << altIndex << "(";
}
- SmallVector<MethodParameter> prefix = {{"MlirContext", "context"}};
+/* SmallVector<MethodParameter> prefix = {{"MlirContext", "context"}};
SmallVector<MethodParameter> params_;
params_.insert(params_.begin(), prefix.begin(), prefix.end());
- params_.insert(params_.end(), params.begin(), params.end());
- for (auto [i, param] : llvm::enumerate(params_)) {
- os << param.getType() << " " << param.getName()
- << (i < (params_.size() - 1) ? ", " : "");
+ params_.insert(params_.end(), params.begin(), params.end()); */
+ os << "MlirContext context";
+ if (!params.empty() > 0) {
+ os << ",";
+ }
+ for (auto [i, param] : llvm::enumerate(params)) {
+ os << param.getCAPIType() << " " << param.getName()
+ << (i < (params.size() - 1) ? ", " : "");
}
if (isDeclGenerator) {
os << ");\n";
@@ -280,11 +349,11 @@ static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<MethodParamet
for (auto [i, param] : llvm::enumerate(params)) {
// If this is an enum, just use C++ static cast
if (param.isEnumParam()) {
- os << "(" << camelCaseToCppNamespace(param.getType()) << ")" << param.getName();
+ os << "(" << param.getCppType() << ")" << param.getName();
} else {
- os << "llvm::cast<" << param.getType() << ">";
+ os << "llvm::cast<" << param.getCppType() << ">";
// Is this a wrapped type? If so unwrap it, otherwised don't
- if (param.getType().starts_with("Mlir")) {
+ if (StringRef(param.getCAPIType()).starts_with("Mlir")) {
os << "(unwrap(" << param.getName() << "))";
} else {
os << "(" << param.getName() << ")";
@@ -306,7 +375,8 @@ static void emitAccessorDeclsOrDefs(const AttrOrTypeDef &def,
continue;
std::string paramName = param.getName().str();
os << "MLIR_CAPI_EXPORTED ";
- os << mapParamTypeToCAPI(param) << " " << cppNamespaceToPrefix(def.getDialect().getCppNamespace()) << def.getCppClassName()
+ const AttrOrTypeOrBuilderParam aotob_param = AttrOrTypeOrBuilderParam(param);
+ os << mapParamTypeToCAPI(&aotob_param,true) << " " << cppNamespaceToPrefix(def.getDialect().getCppNamespace()) << def.getCppClassName()
<< "Get" << withCapitalFirstLetter(param.getName().str());
if (isAttrGenerator)
os << "(MlirAttribute attr";
@@ -321,7 +391,7 @@ static void emitAccessorDeclsOrDefs(const AttrOrTypeDef &def,
os << "\treturn wrap(";
} else {
// Enums are enums not objects
- os << "\treturn (" << mapParamTypeToCAPI(param) << ")";
+ os << "\treturn (" << mapParamTypeToCAPI(&aotob_param,false) << ")";
}
os << "llvm::cast<" << getDefCppType(def) << ">(unwrap(attr)).get" << withCapitalFirstLetter(param.getName().str()) << "()";
if (!isEnumGenerator) {
@@ -408,16 +478,24 @@ static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
for (const auto *rec : records) {
EnumInfo enumInfo(*rec);
- llvm::IfNDefGuardEmitter scope(os, "NO_" + cppNamespaceToUpper(enumInfo.getEnumClassName()) +
+
+ // JEG: I've seen both className being fully qualified and cppNamespace being blank, and
+ // cppNamespace not being empty and className not being qualified. This should cover both.
+ std::string enum_class_name = "";
+ enum_class_name += enumInfo.getCppNamespace();
+ enum_class_name += enumInfo.getEnumClassName();
+
+ llvm::IfNDefGuardEmitter scope(os, "NO_" + cppNamespaceToUpper(enum_class_name) +
"_ENUM_CAPI_DECL");
os << "// " << enumInfo.getSummary() << "\n";
- os << "enum " << cppNamespaceToPrefix(enumInfo.getCppNamespace()) << enumInfo.getEnumClassName();
+ // Move the enum name creation into mapParamTypeToCAPI, or dup there
+ os << "enum " << cppNamespaceToPrefix(enum_class_name);
if (!enumInfo.getUnderlyingType().empty())
os << " : " << enumInfo.getUnderlyingType();
os << " {\n";
- auto prefix = formatv("{0}{1}_", cppNamespaceToPrefix(enumInfo.getCppNamespace()), enumInfo.getEnumClassName());
+ auto prefix = formatv("{0}_", cppNamespaceToPrefix(enum_class_name));
for (const EnumCase &enumerant : enumInfo.getAllCases()) {
auto symbol = makeIdentifier(enumerant.getSymbol());
auto value = enumerant.getValue();
@@ -428,7 +506,7 @@ static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
}
os << "};\n";
// Add convenience typedef
- os << formatv("typedef enum {0}{1} {0}{1}; \n", cppNamespaceToPrefix(enumInfo.getCppNamespace()), enumInfo.getEnumClassName());
+ os << formatv("typedef enum {0} {0}; \n", cppNamespaceToPrefix(enum_class_name));
}
os << "\n";
@@ -483,11 +561,11 @@ bool CAPIDefGenerator::emitDeclsOrDefs(StringRef selectedDialect, bool isDeclGen
emitAttrTypeHeader(name, os);
if (!def.skipDefaultBuilders() && !llvm::any_of(params, isUnsupportedParam))
emitGettorDeclOrDef(def, getGettorParams(params), os, isAttrGenerator, isDeclGenerator, 0);
- unsigned altNum = 1;
+ /* unsigned altNum = 1;
for (const AttrOrTypeBuilder &builder : def.getBuilders()) {
emitGettorDeclOrDef(def, getGettorParams(builder.getParameters()), os, isAttrGenerator, isDeclGenerator, altNum);
altNum++;
- }
+ } */
emitTypeIDDecl(def, os);
emitIsADecl(def, os, isAttrGenerator);
if (def.genAccessors() && !params.empty())
>From 9e54cc6b092fd3fb591e8f75aa50f4c16913adae Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Fri, 17 Apr 2026 17:30:42 -0700
Subject: [PATCH 11/19] Require cppName on EnumInfos, enable alt builders
---
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index b8a9bd10fee21..c7742989fca0b 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -479,10 +479,14 @@ static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
for (const auto *rec : records) {
EnumInfo enumInfo(*rec);
- // JEG: I've seen both className being fully qualified and cppNamespace being blank, and
- // cppNamespace not being empty and className not being qualified. This should cover both.
+ // cppNamespace cannot being empty when generating the CAPI disambiguated enums, so
+ // error out if it is missing
std::string enum_class_name = "";
enum_class_name += enumInfo.getCppNamespace();
+ if (enum_class_name == "") {
+ PrintFatalNote(formatv("enumInfo for {0} is missing a cppNamespace value",
+ enumInfo.getEnumClassName()));
+ }
enum_class_name += enumInfo.getEnumClassName();
llvm::IfNDefGuardEmitter scope(os, "NO_" + cppNamespaceToUpper(enum_class_name) +
@@ -561,11 +565,11 @@ bool CAPIDefGenerator::emitDeclsOrDefs(StringRef selectedDialect, bool isDeclGen
emitAttrTypeHeader(name, os);
if (!def.skipDefaultBuilders() && !llvm::any_of(params, isUnsupportedParam))
emitGettorDeclOrDef(def, getGettorParams(params), os, isAttrGenerator, isDeclGenerator, 0);
- /* unsigned altNum = 1;
+ unsigned altNum = 1;
for (const AttrOrTypeBuilder &builder : def.getBuilders()) {
emitGettorDeclOrDef(def, getGettorParams(builder.getParameters()), os, isAttrGenerator, isDeclGenerator, altNum);
altNum++;
- } */
+ }
emitTypeIDDecl(def, os);
emitIsADecl(def, os, isAttrGenerator);
if (def.genAccessors() && !params.empty())
>From 6407da82b1da1f9fb2a23af34f60746d2a2d6033 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny at gentoo.org>
Date: Sat, 21 Mar 2026 15:40:16 +0100
Subject: [PATCH 12/19] [lldb] Fix LLVMSupportHTTP linkage against libLLVM
(#187848)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Regression introduced in 39d6bb21804d21abe2fa0ec019919d72104827ac.
Signed-off-by: Michał Górny <mgorny at gentoo.org>
>From a58eb435118d914b5886146e82806cec34bdd04f Mon Sep 17 00:00:00 2001
From: Alexey Bataev <a.bataev at outlook.com>
Date: Thu, 26 Mar 2026 07:11:40 -0400
Subject: [PATCH 13/19] [SLP] Fix incorrect operand info for select in
getCmpSelInstrCost (#188506)
The operand info passed to getCmpSelInstrCost for Select instructions
was using operands 0 and 1 (condition and true value), but the API
expects info about the data operands (true and false values). For
selects, the data operands are at indices 1 and 2, not 0 and 1.
This led to the cost model receiving the condition's operand info
instead of the false arm's, potentially producing inaccurate cost
estimates.
>From 41ec15b2df1f66fa747977f863065b458e10c772 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Fri, 27 Mar 2026 17:32:56 +0100
Subject: [PATCH 14/19] [MLIR][XeVM] Wrap in-place op modifications in
modifyOpInPlace in LLVMLoadStoreToOCLPattern (#188952)
LLVMLoadStoreToOCLPattern::matchAndRewrite was calling op->removeAttr()
and op->setOperand() directly without going through the rewriter API.
This caused MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS to report "expected
pattern to replace the root operation or modify it in place".
Fix: wrap the direct mutations in rewriter.modifyOpInPlace().
Assisted-by: Claude Code
Fix a failure present with MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS=ON.
>From 58d6b46399a2160a6001f9d87ad6d5144e634460 Mon Sep 17 00:00:00 2001
From: Matthew Nagy <matthew.nagy at sony.com>
Date: Mon, 13 Apr 2026 23:38:42 +0100
Subject: [PATCH 15/19] =?UTF-8?q?Revert=20"[TySan][Sanitizer=20Common]=20E?=
=?UTF-8?q?nable=20TySan=20testing=20in=20the=20sanitizer=20commo=E2=80=A6?=
=?UTF-8?q?"=20(#191902)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Reverts llvm/llvm-project#191385
Some tests seem to be failing, but not under all environments, possibly
due to non-tysan related reasons. Clearly I need to look more into this
before enabling this
>From 9a770dabbd8af6f534b26ffbfeb373f6bac74bea Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Sun, 19 Apr 2026 12:13:03 -0700
Subject: [PATCH 16/19] Use new CAPI with Index dialect
---
mlir/lib/CAPI/Dialect/Index.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/lib/CAPI/Dialect/Index.cpp b/mlir/lib/CAPI/Dialect/Index.cpp
index b736d328aa20e..74b5c7defffe5 100644
--- a/mlir/lib/CAPI/Dialect/Index.cpp
+++ b/mlir/lib/CAPI/Dialect/Index.cpp
@@ -16,4 +16,4 @@
MLIR_DEFINE_CAPI_DIALECT_REGISTRATION(Index, index, mlir::index::IndexDialect)
-// #include "mlir/Dialect/Index/IR/IndexOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
+#include "mlir/Dialect/Index/IR/IndexOpsCAPIAttrs.cpp.inc"
\ No newline at end of file
>From 9bb6aa834de05215936d9e3c2cb99ccd2354e26b Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Sun, 19 Apr 2026 12:13:32 -0700
Subject: [PATCH 17/19] Deal with enum parameters in alt builders
The parameters for alternate builders are StringInits, so there
is no DefInit to test for being an Enum class. This change
records the seen enum Definits by cppTypeName so when the same
cppTypeName is seen in an alt builder we can tell it is an enum.
---
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 69 ++++++++++++--------
1 file changed, 41 insertions(+), 28 deletions(-)
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index c7742989fca0b..babf399ee2ead 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -16,6 +16,7 @@
#include "mlir/TableGen/Interfaces.h"
#include "mlir/TableGen/Pass.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FormatVariadic.h"
@@ -157,6 +158,9 @@ static std::string cppNamespaceToUpper(StringRef ns) {
return result;
} */
+struct AttrOrTypeOrBuilderParam;
+
+static bool isEnumParam(const AttrOrTypeOrBuilderParam ¶m);
struct AttrOrTypeOrBuilderParam
{
// Only one of these must be non-null at a type
@@ -203,17 +207,30 @@ struct AttrOrTypeOrBuilderParam
bool isEnumParam() const {
- const llvm::Init *def = getDef();
- if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(def)) {
- const Record *rec = defInit->getDef();
- return rec->isSubClassOf("EnumParameter");
- } else {
- return false;
- }
+ return ::isEnumParam(*this);
}
};
+// BuilderParams are just strings, so no way to tell if they are an enum or not. So when they
+// are seen elsewhere we register them here so when used in alt builders the types are here.
+static llvm::StringSet<> enum_types = llvm::StringSet();
+
+static bool isEnumParam(const AttrOrTypeOrBuilderParam ¶m) {
+ if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
+ const Record *rec = defInit->getDef();
+ if (rec->isSubClassOf("EnumParameter")) {
+ enum_types.insert(param.getCppType());
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return enum_types.contains(param.getCppType());
+ }
+}
+
+
/**
* @brief Map an AttrOrTypeOrBuilderParam to its CAPI type
*
@@ -224,7 +241,7 @@ struct AttrOrTypeOrBuilderParam
static std::string mapParamTypeToCAPI(const AttrOrTypeOrBuilderParam *param, bool return_type) {
if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param->getDef())) {
const Record *rec = defInit->getDef();
- if (rec->isSubClassOf("EnumParameter")) {
+ if (rec->isSubClassOf("EnumParameter")) { // || rec->isSubClassOf("EnumInfo")) {
std::string type = "";
type += cppNamespaceToPrefix(param->getCppType());
// type += rec->getValueAsString("underlyingEnumName");
@@ -257,7 +274,7 @@ std::string AttrOrTypeOrBuilderParam::getCAPIType() const {
return mapParamTypeToCAPI(this, false);
}
-static bool paramIsEnum(const AttrOrTypeParameter ¶m) {
+/* static bool paramIsEnum(const AttrOrTypeParameter ¶m) {
if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
const Record *rec = defInit->getDef();
if (rec->isSubClassOf("EnumParameter")) {
@@ -265,17 +282,6 @@ static bool paramIsEnum(const AttrOrTypeParameter ¶m) {
}
}
return false;
-}
-
-/* static bool isEnumParam(const AttrOrTypeParameter ¶m) {
- // Do I need a case for StringInits? I've seen cases where some types are
- // encoded as strings
- if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
- const Record *rec = defInit->getDef();
- return rec->isSubClassOf("EnumParameter");
- } else {
- return paramIsEnum(param);
- }
} */
static SmallVector<AttrOrTypeOrBuilderParam>
@@ -311,9 +317,9 @@ static llvm::StringRef getDefCppType(const EnumInfo &def) {
}
-
static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypeOrBuilderParam> params,
raw_ostream &os, bool isAttrGenerator, bool isDeclGenerator, unsigned altIndex) {
+ // Output *Get function signature
os << "MLIR_CAPI_EXPORTED ";
if (isAttrGenerator)
os << "MlirAttribute ";
@@ -325,10 +331,7 @@ static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypeOrB
} else {
os << "Alt" << altIndex << "(";
}
-/* SmallVector<MethodParameter> prefix = {{"MlirContext", "context"}};
- SmallVector<MethodParameter> params_;
- params_.insert(params_.begin(), prefix.begin(), prefix.end());
- params_.insert(params_.end(), params.begin(), params.end()); */
+
os << "MlirContext context";
if (!params.empty() > 0) {
os << ",";
@@ -340,6 +343,7 @@ static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypeOrB
if (isDeclGenerator) {
os << ");\n";
} else {
+ // Output *get function definition
os << ") {\n";
os << "\treturn wrap(";
os << getDefCppType(def) << "::get(unwrap(context)";
@@ -349,9 +353,18 @@ static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypeOrB
for (auto [i, param] : llvm::enumerate(params)) {
// If this is an enum, just use C++ static cast
if (param.isEnumParam()) {
- os << "(" << param.getCppType() << ")" << param.getName();
+ os << "(" << formatv("/* Line {0} */", __LINE__) << param.getCppType() << ")" << param.getName();
} else {
- os << "llvm::cast<" << param.getCppType() << ">";
+ if (param.getCppType().contains(':')) {
+ // std::string debug_str;
+ // if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
+ // defInit;
+ // debug_str = formatv("/* def = {0} */", (void *)param.getDef()).str();
+ // } else {
+ // debug_str = formatv("/* not a def {0} */", (void *)param.getDef()).str();
+ // }
+ os << /* debug_str << */ "llvm::cast<" << param.getCppType() << ">";
+ }
// Is this a wrapped type? If so unwrap it, otherwised don't
if (StringRef(param.getCAPIType()).starts_with("Mlir")) {
os << "(unwrap(" << param.getName() << "))";
@@ -385,7 +398,7 @@ static void emitAccessorDeclsOrDefs(const AttrOrTypeDef &def,
if (isDeclGenerator) {
os << ");\n";
} else {
- bool isEnumGenerator = paramIsEnum(param);
+ bool isEnumGenerator = isEnumParam(param);
os << ") {\n";
if (!isEnumGenerator) {
os << "\treturn wrap(";
>From 776ef37e4991279677b3a1daf4cf8160b7b7d30e Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Sun, 19 Apr 2026 12:27:37 -0700
Subject: [PATCH 18/19] Silence CTAD warning
Claude assisted
---
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index babf399ee2ead..5363db2778d78 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -214,7 +214,7 @@ struct AttrOrTypeOrBuilderParam
// BuilderParams are just strings, so no way to tell if they are an enum or not. So when they
// are seen elsewhere we register them here so when used in alt builders the types are here.
-static llvm::StringSet<> enum_types = llvm::StringSet();
+static llvm::StringSet<llvm::MallocAllocator> enum_types;
static bool isEnumParam(const AttrOrTypeOrBuilderParam ¶m) {
if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
>From b4bb0de494535dba86141040b9293698743bfcc1 Mon Sep 17 00:00:00 2001
From: jgreenbaum <j.greenbaum at computer.org>
Date: Wed, 27 May 2026 14:59:16 -0700
Subject: [PATCH 19/19] Various fixes from circt dialect work
---
mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp | 129 ++++++++++++-------
1 file changed, 85 insertions(+), 44 deletions(-)
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
index 5363db2778d78..0a9d1d3b193a1 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeCAPIGen.cpp
@@ -11,6 +11,7 @@
#include "CppGenUtilities.h"
#include "mlir/TableGen/AttrOrTypeDef.h"
#include "mlir/TableGen/Class.h"
+#include "mlir/TableGen/Format.h"
#include "mlir/TableGen/EnumInfo.h"
#include "mlir/TableGen/GenInfo.h"
#include "mlir/TableGen/Interfaces.h"
@@ -251,9 +252,9 @@ static std::string mapParamTypeToCAPI(const AttrOrTypeOrBuilderParam *param, boo
return "MlirStringRef";
}
auto cppType = param->getCppType();
- if (cppType == "Type" || cppType == "::mlir::Type")
+ if (cppType == "Type" || cppType == "::mlir::Type" || cppType == "mlir::Type")
return "MlirType";
- if (cppType == "Attribute" || cppType.ends_with("Attr")) {
+ if (cppType == "Attribute" || cppType == "::mlir::Attribute" || cppType.ends_with("Attr")) {
if (return_type && cppType == "::mlir::StringAttr") {
// For some reason these map to MlirIdentifier when wrapped instead of MlirAttribute
return "MlirIdentifier";
@@ -261,10 +262,14 @@ static std::string mapParamTypeToCAPI(const AttrOrTypeOrBuilderParam *param, boo
return "MlirAttribute";
}
}
- if (cppType == "::llvm::StringRef") {
+ if (cppType == "::llvm::StringRef" || cppType == "::mlir::StringRef") {
return "MlirStringRef";
}
if (cppType.starts_with("::")) {
+ auto debug = llvm::StringRef(cppNamespaceToPrefix(cppType));
+ if (debug.starts_with("mlirStringRef")) {
+ llvm::PrintFatalNote(formatv(" from {0}", cppType));
+ }
return cppNamespaceToPrefix(cppType);
}
return cppType.str();
@@ -317,7 +322,8 @@ static llvm::StringRef getDefCppType(const EnumInfo &def) {
}
-static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypeOrBuilderParam> params,
+static void emitGettorDeclOrDef(const AttrOrTypeDef &def,
+ ArrayRef<AttrOrTypeOrBuilderParam> params, bool hasInferredContextParameter,
raw_ostream &os, bool isAttrGenerator, bool isDeclGenerator, unsigned altIndex) {
// Output *Get function signature
os << "MLIR_CAPI_EXPORTED ";
@@ -345,36 +351,54 @@ static void emitGettorDeclOrDef(const AttrOrTypeDef &def, ArrayRef<AttrOrTypeOrB
} else {
// Output *get function definition
os << ") {\n";
- os << "\treturn wrap(";
- os << getDefCppType(def) << "::get(unwrap(context)";
- if (params.size() > 0) {
- os << ", ";
- }
- for (auto [i, param] : llvm::enumerate(params)) {
- // If this is an enum, just use C++ static cast
- if (param.isEnumParam()) {
- os << "(" << formatv("/* Line {0} */", __LINE__) << param.getCppType() << ")" << param.getName();
- } else {
- if (param.getCppType().contains(':')) {
- // std::string debug_str;
- // if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
- // defInit;
- // debug_str = formatv("/* def = {0} */", (void *)param.getDef()).str();
- // } else {
- // debug_str = formatv("/* not a def {0} */", (void *)param.getDef()).str();
- // }
- os << /* debug_str << */ "llvm::cast<" << param.getCppType() << ">";
+ /* if (body.has_value()) {
+ os << body.value() << "\n";
+ } else { */
+ os << "\treturn ";
+ // Don't wrap enums, they are scalar types
+ // if (!def.getDef()->isSubClassOf("EnumAttr")) {
+ os << "wrap(";
+ // }
+ os << getDefCppType(def) << "::get(";
+ if (!hasInferredContextParameter) {
+ os << "unwrap(context)";
+ if (params.size() > 0) {
+ os << ", ";
}
- // Is this a wrapped type? If so unwrap it, otherwised don't
- if (StringRef(param.getCAPIType()).starts_with("Mlir")) {
- os << "(unwrap(" << param.getName() << "))";
+ }
+ for (auto [i, param] : llvm::enumerate(params)) {
+ // If this is an enum, just use C++ static cast
+ if (param.isEnumParam()) {
+ os << "(" << formatv("/* Line {0} */", __LINE__) << param.getCppType() << ")" << param.getName();
} else {
- os << "(" << param.getName() << ")";
+ if (param.getCppType().contains(':')) {
+ // std::string debug_str;
+ // if (const llvm::DefInit *defInit = dyn_cast<llvm::DefInit>(param.getDef())) {
+ // defInit;
+ // debug_str = formatv("/* def = {0} */", (void *)param.getDef()).str();
+ // } else {
+ // debug_str = formatv("/* not a def {0} */", (void *)param.getDef()).str();
+ // }
+ os << /* debug_str << */ "llvm::cast<" << param.getCppType() << ">(";
+ }
+ // Is this a wrapped type? If so unwrap it, otherwised don't
+ if (StringRef(param.getCAPIType()).starts_with("Mlir")) {
+ os << "(unwrap(" << param.getName() << "))";
+ } else {
+ os << "(" << param.getName() << ")";
+ }
+ if (param.getCppType().contains(':')) {
+ os << ")"; // close the cast
+ }
}
+ os << (i < params.size() - 1 ? ", " : "");
}
- os << (i < params.size() - 1 ? ", " : "");
- }
- os << "));\n";
+ // if (!def.getDef()->isSubClassOf("EnumAttr")) {
+ // Close the wrap
+ os << ")";
+ // }
+ os << ");\n";
+ // }
os << "}\n";
}
}
@@ -384,8 +408,10 @@ static void emitAccessorDeclsOrDefs(const AttrOrTypeDef &def,
raw_ostream &os, bool isAttrGenerator, bool isDeclGenerator) {
for (AttrOrTypeParameter param : params) {
- if (isUnsupportedParam(param))
+ if (isUnsupportedParam(param)) {
+ os << "// Skipping accessor for unsupported parameter " << param.getName() << "\n";
continue;
+ }
std::string paramName = param.getName().str();
os << "MLIR_CAPI_EXPORTED ";
const AttrOrTypeOrBuilderParam aotob_param = AttrOrTypeOrBuilderParam(param);
@@ -441,10 +467,6 @@ static void emitTypeIDDeclOrDef(const EnumInfo &enumInfo, raw_ostream &os, bool
}
}
-static void emitTypeIDDecl(const AttrOrTypeDef &def, raw_ostream &os) {
- emitTypeIDDeclOrDef(def, os, EMIT_DECLS);
-}
-
static void emitIsADeclOrDef(const AttrOrTypeDef &def, raw_ostream &os,
bool isAttrGenerator, bool isDeclGenerator) {
os << "MLIR_CAPI_EXPORTED bool mlir"; // JEG: Perhaps this one is correct?
@@ -483,10 +505,6 @@ static void emitIsADeclOrDef(const EnumInfo &enumInfo, raw_ostream &os,
}
}
-static void emitIsADecl(const AttrOrTypeDef &def, raw_ostream &os, bool isAttrGenerator) {
- emitIsADeclOrDef(def, os, isAttrGenerator, EMIT_DECLS);
-}
-
static bool emitEnumDecls(ArrayRef<const Record *> records, raw_ostream &os) {
for (const auto *rec : records) {
@@ -576,15 +594,38 @@ bool CAPIDefGenerator::emitDeclsOrDefs(StringRef selectedDialect, bool isDeclGen
ArrayRef<AttrOrTypeParameter> params = def.getParameters();
emitAttrTypeHeader(name, os);
- if (!def.skipDefaultBuilders() && !llvm::any_of(params, isUnsupportedParam))
- emitGettorDeclOrDef(def, getGettorParams(params), os, isAttrGenerator, isDeclGenerator, 0);
+ bool has_unsupported_params = llvm::any_of(params, isUnsupportedParam);
+ if (!def.skipDefaultBuilders() && !has_unsupported_params) {
+ emitGettorDeclOrDef(def, getGettorParams(params), false, os, isAttrGenerator, isDeclGenerator, 0);
+ } else if (has_unsupported_params) {
+ os << formatv("// Skipping gettor {0} for {1}, it has an unsupported parameter type \n",
+ 0, name);
+ }
+
unsigned altNum = 1;
for (const AttrOrTypeBuilder &builder : def.getBuilders()) {
- emitGettorDeclOrDef(def, getGettorParams(builder.getParameters()), os, isAttrGenerator, isDeclGenerator, altNum);
+ // If the builder doesn't have a body
+ // if (!builder.getBody().has_value()) {
+ os << "// " << getDefCppType(def).str() << " Alt " << altNum << "\n";
+ emitGettorDeclOrDef(def, getGettorParams(builder.getParameters()), builder.hasInferredContextParameter(),
+ os, isAttrGenerator, isDeclGenerator, altNum);
+ /* } else {
+ os << "// Has Body " << getDefCppType(def).str() << " Alt " << altNum << "\n";
+ // Do substitutions into body
+ FmtContext fmtctx;
+ fmtctx.addSubst("_get", getDefCppType(def).str() + "::get");
+ if (!builder.hasInferredContextParameter()) {
+ fmtctx.addSubst("_ctxt", "unwrap(context)");
+ }
+ std::string body_str = tgfmt(builder.getBody().value(), &fmtctx);
+ std::optional<StringRef> body_str_ref = body_str;
+ emitGettorDeclOrDef(def, getGettorParams(builder.getParameters()), body_str_ref,
+ os, isAttrGenerator, isDeclGenerator, altNum);
+ } */
altNum++;
}
- emitTypeIDDecl(def, os);
- emitIsADecl(def, os, isAttrGenerator);
+ emitTypeIDDeclOrDef(def, os, isDeclGenerator);
+ emitIsADeclOrDef(def, os, isAttrGenerator, isDeclGenerator);
if (def.genAccessors() && !params.empty())
emitAccessorDeclsOrDefs(def, params, os, isAttrGenerator, isDeclGenerator);
}
More information about the Mlir-commits
mailing list