[Mlir-commits] [mlir] [MLIR][IRDL] Instantiate "." in IRDL-defined names as nested namespaces (PR #207035)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Mon Sep 14 08:55:31 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-irdl
Author: Ivan Ho (hhkit)
<details>
<summary>Changes</summary>
In PR #<!-- -->187911, it was revealed that the constraints on IRDL op names placed by irdl-to-cpp were too restrictive.
This PR will address that by allowing "." in operation names to denote nested namespaces, but not "$".
To discuss:
- ops like `wmma.f16.16x16x64.bf8_bf` cannot be registered as C++ identifiers cannot begin with a number. I'm opening this up as a draft PR while I work on the actual generation so we can discuss how to handle this case.
Codex was used to generate the lit tests.
Assisted-by: OpenAI Codex (GPT-5)
---
Full diff: https://github.com/llvm/llvm-project/pull/207035.diff
3 Files Affected:
- (modified) mlir/lib/Dialect/IRDL/IR/IRDL.cpp (+18-13)
- (modified) mlir/lib/Target/IRDLToCpp/IRDLToCpp.cpp (+89-23)
- (modified) mlir/test/lib/Dialect/TestIRDLToCpp/test_irdl_to_cpp.irdl.mlir (+110-12)
``````````diff
diff --git a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp
index 278113fc6e966..10319687cd134 100644
--- a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp
+++ b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp
@@ -77,25 +77,30 @@ static llvm::LogicalResult isValidName(llvm::StringRef in, mlir::Operation *loc,
if (in.empty())
return loc->emitError("name of ") << label << " is empty";
- bool allowUnderscore = false;
- for (auto &elem : in) {
+ char prev = '_'; // treat the initial as _ to eliminate leading underscores
+ for (const auto &elem : in) {
if (elem == '_') {
- if (!allowUnderscore)
+ if (prev == '_')
return loc->emitError("name of ")
<< label << " should not contain leading or double underscores";
} else {
- if (!isalnum(elem))
- return loc->emitError("name of ")
- << label
- << " must contain only lowercase letters, digits and "
- "underscores";
-
- if (llvm::isUpper(elem))
- return loc->emitError("name of ")
- << label << " should not contain uppercase letters";
+ if (elem == '.') {
+ if (prev == '.')
+ return loc->emitError("empty namespace not allowed");
+ } else {
+ if (!isalnum(elem))
+ return loc->emitError("name of ")
+ << label
+ << " must contain only lowercase letters, digits and "
+ "underscores";
+
+ if (llvm::isUpper(elem))
+ return loc->emitError("name of ")
+ << label << " should not contain uppercase letters";
+ }
}
- allowUnderscore = elem != '_';
+ prev = elem;
}
return success();
diff --git a/mlir/lib/Target/IRDLToCpp/IRDLToCpp.cpp b/mlir/lib/Target/IRDLToCpp/IRDLToCpp.cpp
index 2bbd9de0ec51b..53d19fb5b8e80 100644
--- a/mlir/lib/Target/IRDLToCpp/IRDLToCpp.cpp
+++ b/mlir/lib/Target/IRDLToCpp/IRDLToCpp.cpp
@@ -13,6 +13,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
@@ -52,6 +53,8 @@ struct TypeStrings {
struct OpStrings {
StringRef opName;
std::string opCppName;
+ std::string opScopedCppName;
+ SmallVector<std::string> opNameSpaces;
SmallVector<std::string> opResultNames;
SmallVector<std::string> opOperandNames;
SmallVector<std::string> opRegionNames;
@@ -65,16 +68,42 @@ static std::string joinNameList(llvm::ArrayRef<std::string> names) {
return nameArray;
}
+/// Prefix identifiers that start with a digit to make them valid C++ names.
+static std::string legalizeCppName(StringRef name) {
+ if (!name.empty() && llvm::isDigit(name.front()))
+ return ("_" + name).str();
+ return name.str();
+}
+
/// Generates the C++ type name for a TypeOp
static std::string typeToCppName(irdl::TypeOp type) {
- return llvm::formatv("{0}Type",
- convertToCamelFromSnakeCase(type.getSymName(), true));
+ return llvm::formatv("{0}Type", legalizeCppName(convertToCamelFromSnakeCase(
+ type.getSymName(), true)));
}
/// Generates the C++ class name for an OperationOp
static std::string opToCppName(irdl::OperationOp op) {
- return llvm::formatv("{0}Op",
- convertToCamelFromSnakeCase(op.getSymName(), true));
+ const auto opName = op.getSymName();
+ const auto periodIndex = opName.find_last_of(".");
+ const auto nameSubstr = periodIndex == std::string::npos
+ ? opName
+ : opName.substr(periodIndex + 1);
+ return llvm::formatv(
+ "{0}Op", legalizeCppName(convertToCamelFromSnakeCase(nameSubstr, true)));
+}
+
+/// Generates the C++ namespace components for an OperationOp.
+static SmallVector<std::string> opToCppNamespaces(irdl::OperationOp op) {
+ auto parts = SmallVector<StringRef>(llvm::split(op.getSymName(), "."));
+ parts.pop_back();
+ return llvm::map_to_vector(parts, legalizeCppName);
+}
+
+// Generates the C++ class name for an OperationOp, scoped to the namespace
+static std::string opToScopedCppName(irdl::OperationOp op) {
+ auto names = opToCppNamespaces(op);
+ names.push_back(opToCppName(op));
+ return llvm::join(names, "::");
}
/// Generates TypeStrings from a TypeOp
@@ -93,7 +122,9 @@ static OpStrings getStrings(irdl::OperationOp op) {
OpStrings strings;
strings.opName = op.getSymName();
+ strings.opNameSpaces = opToCppNamespaces(op);
strings.opCppName = opToCppName(op);
+ strings.opScopedCppName = opToScopedCppName(op);
if (operandOp) {
strings.opOperandNames = SmallVector<std::string>(
@@ -134,6 +165,7 @@ static void fillDict(irdl::detail::dictionary &dict, const OpStrings &strings) {
dict["OP_NAME"] = strings.opName;
dict["OP_CPP_NAME"] = strings.opCppName;
+ dict["OP_SCOPED_CPP_NAME"] = strings.opScopedCppName;
dict["OP_OPERAND_COUNT"] = std::to_string(strings.opOperandNames.size());
dict["OP_RESULT_COUNT"] = std::to_string(strings.opResultNames.size());
dict["OP_OPERAND_INITIALIZER_LIST"] =
@@ -141,6 +173,32 @@ static void fillDict(irdl::detail::dictionary &dict, const OpStrings &strings) {
dict["OP_RESULT_INITIALIZER_LIST"] =
resultCount ? joinNameList(strings.opResultNames) : "{\"\"}";
dict["OP_REGION_COUNT"] = std::to_string(regionCount);
+ dict["NAMESPACE_OPEN"] =
+ (dict["NAMESPACE_OPEN"] +
+ llvm::join(llvm::map_range(strings.opNameSpaces,
+ [](llvm::StringRef ref) -> std::string {
+ return llvm::formatv("namespace {0} {{",
+ ref);
+ }),
+ "\n"))
+ .str();
+ dict["NAMESPACE_PATH"] =
+ (dict["NAMESPACE_PATH"] +
+ llvm::join(llvm::map_range(strings.opNameSpaces,
+ [](llvm::StringRef ref) -> std::string {
+ return llvm::formatv("::{0}", ref);
+ }),
+ ""))
+ .str();
+ dict["NAMESPACE_CLOSE"] =
+ (llvm::join(llvm::map_range(llvm::reverse(strings.opNameSpaces),
+ [](llvm::StringRef ref) -> std::string {
+ return llvm::formatv("} // namespace {0}\n",
+ ref);
+ }),
+ "") +
+ dict["NAMESPACE_CLOSE"])
+ .str();
}
/// Fills a dictionary with values from DialectStrings
@@ -166,7 +224,7 @@ static LogicalResult generateTypedefList(irdl::DialectOp &dialect,
static LogicalResult generateOpList(irdl::DialectOp &dialect,
SmallVector<std::string> &opNames) {
auto operationOps = dialect.getOps<irdl::OperationOp>();
- auto range = llvm::map_range(operationOps, opToCppName);
+ auto range = llvm::map_range(operationOps, opToScopedCppName);
opNames = SmallVector<std::string>(range);
return success();
}
@@ -281,26 +339,27 @@ static SmallVector<std::string> generateTraits(irdl::OperationOp op,
return cppTraitNames;
}
-static LogicalResult generateOperationInclude(irdl::OperationOp op,
- raw_ostream &output,
- irdl::detail::dictionary &dict) {
+static LogicalResult
+generateOperationInclude(irdl::OperationOp op, raw_ostream &output,
+ const irdl::detail::dictionary &dict) {
static const auto perOpDeclTemplate = irdl::detail::Template(
#include "Templates/PerOperationDecl.txt"
);
const auto opStrings = getStrings(op);
- fillDict(dict, opStrings);
+ auto opDict = dict;
+ fillDict(opDict, opStrings);
SmallVector<std::string> traitNames = generateTraits(op, opStrings);
if (traitNames.empty())
- dict["OP_TEMPLATE_ARGS"] = opStrings.opCppName;
+ opDict["OP_TEMPLATE_ARGS"] = opStrings.opCppName;
else
- dict["OP_TEMPLATE_ARGS"] = llvm::formatv("{0}, {1}", opStrings.opCppName,
- llvm::join(traitNames, ", "));
+ opDict["OP_TEMPLATE_ARGS"] = llvm::formatv("{0}, {1}", opStrings.opCppName,
+ llvm::join(traitNames, ", "));
- generateOpGetterDeclarations(dict, opStrings);
- generateOpBuilderDeclarations(dict, opStrings);
+ generateOpGetterDeclarations(opDict, opStrings);
+ generateOpBuilderDeclarations(opDict, opStrings);
- perOpDeclTemplate.render(output, dict);
+ perOpDeclTemplate.render(output, opDict);
return success();
}
@@ -333,10 +392,16 @@ static LogicalResult generateInclude(irdl::DialectOp dialect,
return failure();
auto classDeclarations =
- llvm::join(llvm::map_range(opNames,
- [](llvm::StringRef name) -> std::string {
- return llvm::formatv("class {0};", name);
- }),
+ llvm::join(llvm::map_range(
+ opNames,
+ [](llvm::StringRef name) -> std::string {
+ if (name.contains("::")) {
+ auto [scope, className] = name.rsplit("::");
+ return llvm::formatv("namespace {0} {{\nclass {1};\n}",
+ scope, className);
+ }
+ return llvm::formatv("class {0};", name);
+ }),
"\n");
const auto forwardDeclarations = llvm::formatv(
"{1}\n{0}\n{2}", std::move(classDeclarations),
@@ -462,7 +527,8 @@ static std::string generateOpDefinition(irdl::detail::dictionary &dict,
};
auto opStrings = getStrings(op);
- fillDict(dict, opStrings);
+ auto opDict = dict;
+ fillDict(opDict, opStrings);
auto resultTypes = llvm::join(
llvm::map_range(opStrings.opResultNames,
@@ -516,13 +582,13 @@ void {0}::build(::mlir::OpBuilder &opBuilder, ::mlir::OperationState &opState, {
llvm::join(opStrings.opOperandNames, ",") +
(!opStrings.opOperandNames.empty() ? "," : ""));
- dict["OP_BUILD_DEFS"] = buildDefinition;
+ opDict["OP_BUILD_DEFS"] = buildDefinition;
- generateVerifiers(dict, op, opStrings);
+ generateVerifiers(opDict, op, opStrings);
std::string str;
llvm::raw_string_ostream stream{str};
- perOpDefTemplate.render(stream, dict);
+ perOpDefTemplate.render(stream, opDict);
return str;
}
diff --git a/mlir/test/lib/Dialect/TestIRDLToCpp/test_irdl_to_cpp.irdl.mlir b/mlir/test/lib/Dialect/TestIRDLToCpp/test_irdl_to_cpp.irdl.mlir
index 770cd36cdee33..35a973656d999 100644
--- a/mlir/test/lib/Dialect/TestIRDLToCpp/test_irdl_to_cpp.irdl.mlir
+++ b/mlir/test/lib/Dialect/TestIRDLToCpp/test_irdl_to_cpp.irdl.mlir
@@ -11,6 +11,26 @@ irdl.dialect @test_irdl_to_cpp {
// CHECK: class FooType
irdl.type @foo
+ // CHECK: class _8Type
+ irdl.type @"8"
+
+ // CHECK: class BarOp;
+ // CHECK-NEXT: namespace nested {
+ // CHECK-NEXT: class NamespacedOp;
+ // CHECK-NEXT: }
+ // CHECK-NEXT: namespace nested::namespaced {
+ // CHECK-NEXT: class MoreOp;
+ // CHECK-NEXT: }
+ // CHECK-NEXT: class BeefOp;
+
+ // CHECK: namespace wmma::f16::_16x16x64 {
+ // CHECK-NEXT: class Bf8BfOp;
+ // CHECK-NEXT: }
+ // CHECK-NEXT: namespace _7::_8 {
+ // CHECK-NEXT: class _9Op;
+ // CHECK-NEXT: }
+ // CHECK-NEXT: class _10Op;
+
// CHECK: class BarOp
// CHECK: ::mlir::Value getRes()
irdl.operation @bar {
@@ -18,6 +38,32 @@ irdl.dialect @test_irdl_to_cpp {
irdl.results(res: %0)
}
+ // CHECK: // ::mlir::test_irdl_to_cpp::nested::NamespacedOp declarations
+ // CHECK: namespace nested {
+ // CHECK: class NamespacedOp : public ::mlir::Op<NamespacedOp
+ // CHECK: static constexpr ::llvm::StringLiteral getOperationName()
+ // CHECK-NEXT: return ::llvm::StringLiteral("test_irdl_to_cpp.nested.namespaced");
+ // CHECK: } // namespace nested
+ // CHECK: MLIR_DECLARE_EXPLICIT_TYPE_ID(::mlir::test_irdl_to_cpp::nested::NamespacedOp)
+ irdl.operation @nested.namespaced {
+ %0 = irdl.any
+ irdl.results(res: %0)
+ }
+
+ // CHECK: // ::mlir::test_irdl_to_cpp::nested::namespaced::MoreOp declarations
+ // CHECK: namespace nested {
+ // CHECK: namespace namespaced {
+ // CHECK: class MoreOp : public ::mlir::Op<MoreOp
+ // CHECK: static constexpr ::llvm::StringLiteral getOperationName()
+ // CHECK-NEXT: return ::llvm::StringLiteral("test_irdl_to_cpp.nested.namespaced.more");
+ // CHECK: } // namespace namespaced
+ // CHECK: } // namespace nested
+ // CHECK: MLIR_DECLARE_EXPLICIT_TYPE_ID(::mlir::test_irdl_to_cpp::nested::namespaced::MoreOp)
+ irdl.operation @nested.namespaced.more {
+ %0 = irdl.any
+ irdl.results(res: %0)
+ }
+
// CHECK: class BeefOp
// CHECK: ::mlir::Value getLhs()
// CHECK: ::mlir::Value getRhs()
@@ -49,6 +95,56 @@ irdl.dialect @test_irdl_to_cpp {
// CHECK: ::mlir::Region &getThen() { return (*this)->getRegion(1); }
// CHECK: ::mlir::Region &getElse() { return (*this)->getRegion(2); }
+ irdl.operation @conditional {
+ %r0 = irdl.region // Unconstrained region
+ %r1 = irdl.region() // Region with no entry block arguments
+
+ // TODO(#161018): support irdl.is in irdl-to-cpp
+ // %v0 = irdl.is i1 // Type constraint: i1 (boolean)
+ %v0 = irdl.any
+ %r2 = irdl.region(%v0) // Region with one i1 entry block argument
+ irdl.regions(cond: %r2, then: %r0, else: %r1)
+
+ %0 = irdl.any
+ irdl.operands(input: %0)
+ }
+
+ // CHECK: // ::mlir::test_irdl_to_cpp::wmma::f16::_16x16x64::Bf8BfOp declarations
+ // CHECK: namespace wmma {
+ // CHECK-NEXT: namespace f16 {
+ // CHECK-NEXT: namespace _16x16x64 {
+ // CHECK: class Bf8BfOp : public ::mlir::Op<Bf8BfOp
+ // CHECK: return ::llvm::StringLiteral("test_irdl_to_cpp.wmma.f16.16x16x64.bf8_bf");
+ // CHECK: } // namespace _16x16x64
+ // CHECK: MLIR_DECLARE_EXPLICIT_TYPE_ID(::mlir::test_irdl_to_cpp::wmma::f16::_16x16x64::Bf8BfOp)
+ irdl.operation @wmma.f16.16x16x64.bf8_bf
+
+ // CHECK: // ::mlir::test_irdl_to_cpp::_7::_8::_9Op declarations
+ // CHECK: namespace _7 {
+ // CHECK-NEXT: namespace _8 {
+ // CHECK: class _9Op : public ::mlir::Op<_9Op
+ // CHECK: return ::llvm::StringLiteral("test_irdl_to_cpp.7.8.9");
+ // CHECK: MLIR_DECLARE_EXPLICIT_TYPE_ID(::mlir::test_irdl_to_cpp::_7::_8::_9Op)
+ irdl.operation @"7.8.9"
+
+ // CHECK: class _10Op : public ::mlir::Op<_10Op
+ // CHECK: return ::llvm::StringLiteral("test_irdl_to_cpp.10");
+ irdl.operation @"10"
+
+ // CHECK: // ::mlir::test_irdl_to_cpp::nested::NamespacedOp definitions
+ // CHECK: namespace nested {
+ // CHECK: NamespacedOp::build
+ // CHECK: } // namespace nested
+ // CHECK: MLIR_DEFINE_EXPLICIT_TYPE_ID(::mlir::test_irdl_to_cpp::nested::NamespacedOp)
+
+ // CHECK: // ::mlir::test_irdl_to_cpp::nested::namespaced::MoreOp definitions
+ // CHECK: namespace nested {
+ // CHECK: namespace namespaced {
+ // CHECK: MoreOp::build
+ // CHECK: } // namespace namespaced
+ // CHECK: } // namespace nested
+ // CHECK: MLIR_DEFINE_EXPLICIT_TYPE_ID(::mlir::test_irdl_to_cpp::nested::namespaced::MoreOp)
+
// CHECK: ConditionalOp definitions
// CHECK: __mlir_irdl_local_region_constraint_ConditionalOp_cond
// CHECK: if (!(region.getNumArguments() == 1)) {
@@ -73,17 +169,19 @@ irdl.dialect @test_irdl_to_cpp {
// CHECK: __mlir_irdl_local_region_constraint_ConditionalOp_else
// CHECK: failure
// CHECK: success
- irdl.operation @conditional {
- %r0 = irdl.region // Unconstrained region
- %r1 = irdl.region() // Region with no entry block arguments
-
- // TODO(#161018): support irdl.is in irdl-to-cpp
- // %v0 = irdl.is i1 // Type constraint: i1 (boolean)
- %v0 = irdl.any
- %r2 = irdl.region(%v0) // Region with one i1 entry block argument
- irdl.regions(cond: %r2, then: %r0, else: %r1)
- %0 = irdl.any
- irdl.operands(input: %0)
- }
+ // CHECK: // ::mlir::test_irdl_to_cpp::wmma::f16::_16x16x64::Bf8BfOp definitions
+ // CHECK: namespace _16x16x64 {
+ // CHECK: Bf8BfOp::build
+ // CHECK: } // namespace _16x16x64
+ // CHECK: MLIR_DEFINE_EXPLICIT_TYPE_ID(::mlir::test_irdl_to_cpp::wmma::f16::_16x16x64::Bf8BfOp)
+ // CHECK: _9Op::build
+ // CHECK: MLIR_DEFINE_EXPLICIT_TYPE_ID(::mlir::test_irdl_to_cpp::_7::_8::_9Op)
+ // CHECK: _10Op::build
+ // CHECK: void TestIrdlToCppDialect::initialize()
+ // CHECK: ::mlir::test_irdl_to_cpp::nested::NamespacedOp
+ // CHECK: ::mlir::test_irdl_to_cpp::nested::namespaced::MoreOp
+ // CHECK: ::mlir::test_irdl_to_cpp::wmma::f16::_16x16x64::Bf8BfOp,
+ // CHECK-NEXT: ::mlir::test_irdl_to_cpp::_7::_8::_9Op,
+ // CHECK-NEXT: ::mlir::test_irdl_to_cpp::_10Op
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/207035
More information about the Mlir-commits
mailing list