[Mlir-commits] [mlir] [mlir][EmitC] Create a pass to add a reflection map to a class (PR #205464)
Bhavesh M
llvmlistbot at llvm.org
Sun Jul 19 14:27:45 PDT 2026
https://github.com/beamandala updated https://github.com/llvm/llvm-project/pull/205464
>From ad1f5134c8a574c016d2dd2c0241afef08781a48 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 23 Jun 2026 17:32:22 -0700
Subject: [PATCH 01/22] [mlir][EmitC] Create a pass to add a reflection map to
a class
This creates the `add-reflection-map` pass to add a reflection map and a
helper method to EmitC classes for runtime field lookup.
Details:
- Add headers to import `map` and `string` if not already present.
- For every `ClassOp`:
- Collect all the `FieldOp`s that contain the attribute whose
value we want as the reflection map key. We ignore all
`FieldOp`s that contain an attribute present in
`excludedFieldAttrs` and if there's a `FieldOp` that neither
has the attribute we're looking for and isn't excluded by the
presence of one of the `excludedFieldAttrs`, emit and error.
- Create the reflection map where the key is the value of the
attribute in each applicable `FieldOp`'s attribute dictionary
and the value is a pointer to the field's contents.
- Create a `getBufferForName` method which serves as a getter
method for the reflection map.
Based on PR #150572. Key differences:
- Adds a `excluded-field-attrs` option which is a list of attributes
that if present on a field exclude it from the reflection map.
- Adds a helper method called `getBufferForName` which given a string
key returns a pointer to the field's buffer.
Co-authored-by: [Jaddyen](https://github.com/Jaddyen)
---
.../mlir/Dialect/EmitC/Transforms/Passes.h | 2 +
.../mlir/Dialect/EmitC/Transforms/Passes.td | 52 +++++
.../Dialect/EmitC/Transforms/Transforms.h | 7 +
.../EmitC/Transforms/AddReflectionMap.cpp | 181 ++++++++++++++++++
.../Dialect/EmitC/Transforms/CMakeLists.txt | 1 +
.../Dialect/EmitC/add-reflection-map.mlir | 58 ++++++
6 files changed, 301 insertions(+)
create mode 100644 mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
create mode 100644 mlir/test/Dialect/EmitC/add-reflection-map.mlir
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h
index 1af4aa06fa811..56596110a1e90 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h
@@ -16,6 +16,8 @@ namespace emitc {
#define GEN_PASS_DECL_FORMEXPRESSIONSPASS
#define GEN_PASS_DECL_WRAPFUNCINCLASSPASS
+#define GEN_PASS_DECL_ADDREFLECTIONMAPPASS
+
#include "mlir/Dialect/EmitC/Transforms/Passes.h.inc"
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index c34c3303a6ab3..6da57c07a8047 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -60,4 +60,56 @@ def WrapFuncInClassPass : Pass<"wrap-emitc-func-in-class", "ModuleOp"> {
];
}
+def AddReflectionMapPass : Pass<"add-reflection-map", "ModuleOp"> {
+ let summary = "Add a reflection map and a helper method to EmitC classes for runtime field lookup.";
+ let description = [{
+ This pass adds a `reflectionMap` field and an accompanying `getBufferForName` method to
+ EmitC classes, enabling runtime lookup of class fields by name.
+ This requires that the class has fields with attributes.
+ Each `emitc.field` is expected to have an attribute (configured by the
+ `field-attr-name` option) that is an array containing a single string
+ attribute.
+
+
+ Example:
+
+ ```mlir
+ emitc.class @MyClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+ emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
+ emitc.func @execute() { ... }
+ }
+
+ Becomes:
+
+ emitc.class @MyClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+ emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
+ emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
+ #emitc.opaque<"{ { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) }, { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) } }">
+ emitc.func @getBufferForName(%arg0: !emitc.opaque<"std::string">)
+ -> !emitc.ptr<!emitc.opaque<"char">> {
+ %0 = get_field @reflectionMap
+ : !emitc.opaque<"const std::map<std::string, char*>">
+ %1 = member_call_opaque %0 "at"(%arg0)
+ : !emitc.opaque<"const std::map<std::string, char*>">,
+ (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
+ return %1 : !emitc.ptr<!emitc.opaque<"char">>
+ }
+ emitc.func @execute() { ... }
+ }
+ ```
+ }];
+ let dependentDialects = ["emitc::EmitCDialect"];
+ let options = [
+ Option<"fieldAttrName", "field-attr-name", "std::string",
+ /*default=*/"",
+ "Attribute key used to extract field names from a field's "
+ "attribute dictionary">,
+ ListOption<"excludedFieldAttrs", "excluded-field-attrs", "std::string",
+ "Attribute keys that, if present on a field, exclude it "
+ "from the reflection map">
+ ];
+}
+
#endif // MLIR_DIALECT_EMITC_TRANSFORMS_PASSES
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index 791e545a8edcf..8eadfe9a2460d 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -12,6 +12,7 @@
#include "mlir/Dialect/EmitC/IR/EmitC.h"
#include "mlir/IR/PatternMatch.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
namespace mlir {
namespace emitc {
@@ -37,6 +38,12 @@ void populateWrapFuncInClass(
RewritePatternSet &patterns, StringRef funcName,
DenseMap<FuncOp, llvm::DenseSet<GlobalOp>> &globalsToMove);
+//===----------------------------------------------------------------------===//
+// The AddReflectionMap pass.
+//===----------------------------------------------------------------------===//
+
+void populateAddReflectionMapPatterns(RewritePatternSet &patterns, StringRef fieldAttrName, ArrayRef<std::string> excludedFieldAttrs);
+
} // namespace emitc
} // namespace mlir
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
new file mode 100644
index 0000000000000..c1588463ffb7a
--- /dev/null
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -0,0 +1,181 @@
+//===- AddReflectionMap.cpp - Add a reflection map to a class --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/EmitC/IR/EmitC.h"
+#include "mlir/Dialect/EmitC/Transforms/Passes.h"
+#include "mlir/Dialect/EmitC/Transforms/Transforms.h"
+#include "mlir/IR/Attributes.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/PatternMatch.h"
+#include "mlir/Transforms/WalkPatternRewriteDriver.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace mlir;
+using namespace emitc;
+
+namespace mlir {
+namespace emitc {
+#define GEN_PASS_DEF_ADDREFLECTIONMAPPASS
+#include "mlir/Dialect/EmitC/Transforms/Passes.h.inc"
+
+namespace {
+constexpr const char *mapLibraryHeader = "map";
+constexpr const char *stringLibraryHeader = "string";
+
+IncludeOp addHeader(OpBuilder &builder, ModuleOp module, StringRef headerName) {
+ StringAttr includeAttr = builder.getStringAttr(headerName);
+ return IncludeOp::create(
+ builder, module.getLoc(), includeAttr,
+ /*is_standard_include=*/builder.getUnitAttr());
+}
+
+class AddReflectionMapPass
+ : public impl::AddReflectionMapPassBase<AddReflectionMapPass> {
+ using AddReflectionMapPassBase::AddReflectionMapPassBase;
+ void runOnOperation() override {
+ mlir::ModuleOp moduleOp = getOperation();
+
+ RewritePatternSet patterns(&getContext());
+ populateAddReflectionMapPatterns(patterns, fieldAttrName, excludedFieldAttrs);
+
+ walkAndApplyPatterns(moduleOp, std::move(patterns));
+ bool hasMapHdr = false;
+ bool hasStringHdr = false;
+ for (auto &op : *moduleOp.getBody()) {
+ IncludeOp includeOp = llvm::dyn_cast<IncludeOp>(op);
+ if (!includeOp)
+ continue;
+
+ if (includeOp.getIsStandardInclude()) {
+ auto include = includeOp.getInclude();
+
+ hasMapHdr = include == mapLibraryHeader;
+ hasStringHdr = include == stringLibraryHeader;
+ }
+
+ if (hasMapHdr && hasStringHdr)
+ return;
+ }
+
+ mlir::OpBuilder builder(moduleOp.getBody(), moduleOp.getBody()->begin());
+ if (!hasMapHdr)
+ addHeader(builder, moduleOp, mapLibraryHeader);
+
+ if (!hasStringHdr)
+ addHeader(builder, moduleOp, stringLibraryHeader);
+ }
+};
+
+} // namespace
+} // namespace emitc
+} // namespace mlir
+
+class AddReflectionMapClass : public OpRewritePattern<ClassOp> {
+public:
+ AddReflectionMapClass(MLIRContext *context, StringRef attrName,
+ llvm::ArrayRef<std::string> excludedFieldAttrs)
+ : OpRewritePattern<ClassOp>(context), fieldAttrName(attrName),
+ excludedFieldAttrs(excludedFieldAttrs.begin(), excludedFieldAttrs.end()) {}
+
+ LogicalResult matchAndRewrite(ClassOp classOp,
+ PatternRewriter &rewriter) const override {
+ MLIRContext *context = rewriter.getContext();
+
+ emitc::OpaqueType mapType = mlir::emitc::OpaqueType::get(
+ context, "const std::map<std::string, char*>");
+
+ // Collect all field names
+ std::vector<std::pair<std::string, std::string>> fieldNames;
+ classOp.walk([&](FieldOp fieldOp) {
+ if (Attribute attr = fieldOp->getAttrDictionary().get(fieldAttrName)) {
+ if (ArrayAttr arrayAttr = dyn_cast<mlir::ArrayAttr>(attr)) {
+ StringAttr stringAttr = cast<mlir::StringAttr>(arrayAttr[0]);
+ fieldNames.emplace_back(stringAttr.getValue().str(),
+ fieldOp.getName().str());
+ return;
+ }
+ }
+
+ bool shouldIgnore = false;
+ for (const std::string &ignoreAttr : excludedFieldAttrs) {
+ if (fieldOp->hasAttr(ignoreAttr)) {
+ shouldIgnore = true;
+ break;
+ }
+ }
+
+ if (shouldIgnore)
+ return;
+
+ fieldOp.emitError()
+ << "FieldOp must have a dictionary attribute named '"
+ << fieldAttrName << "'"
+ << "with an array containing a string attribute";
+ });
+
+ std::string reflectionMapContents;
+ reflectionMapContents += "{ ";
+ for (size_t i = 0, numFields = fieldNames.size(); i < numFields; ++i) {
+ reflectionMapContents += llvm::formatv(
+ "{ \"{0}\", reinterpret_cast<char*>(&{1}) }{2}", fieldNames[i].first,
+ fieldNames[i].second, (i < numFields - 1) ? ", " : "");
+ }
+ reflectionMapContents += " }";
+
+ if (FuncOp executeFunc =
+ classOp.lookupSymbol<FuncOp>("operator()"))
+ rewriter.setInsertionPoint(executeFunc);
+ else {
+ classOp.emitError() << "ClassOp must contain a function named 'operator()' "
+ "to add reflection map";
+ return failure();
+ }
+
+ FieldOp reflectionMapField = FieldOp::create(
+ rewriter, classOp.getLoc(), rewriter.getStringAttr("reflectionMap"),
+ TypeAttr::get(mapType), emitc::OpaqueAttr::get(context, reflectionMapContents));
+
+ // Create getBufferForName method
+ emitc::OpaqueType nameType = emitc::OpaqueType::get(rewriter.getContext(), "std::string");
+ emitc::OpaqueType charType = emitc::OpaqueType::get(rewriter.getContext(), "char");
+ emitc::PointerType valType = emitc::PointerType::get(rewriter.getContext(), charType);
+ FuncOp getBufferForNameFunc = FuncOp::create(
+ rewriter, reflectionMapField->getLoc(), "getBufferForName",
+ FunctionType::get(rewriter.getContext(), {nameType}, {valType}));
+
+ Block *body = rewriter.createBlock(&getBufferForNameFunc.getBody(), {}, {nameType}, {reflectionMapField->getLoc()});
+ rewriter.setInsertionPointToStart(body);
+ GetFieldOp mapField = GetFieldOp::create(
+ rewriter, reflectionMapField->getLoc(), mapType, "reflectionMap");
+ Value nameArg = body->getArgument(0);
+ MemberCallOpaqueOp lookupCall = MemberCallOpaqueOp::create(
+ rewriter, reflectionMapField->getLoc(), valType, mapField.getResult(),
+ "at", ArrayAttr{}, ArrayAttr{}, ValueRange{nameArg});
+ ReturnOp::create(rewriter, reflectionMapField->getLoc(), lookupCall.getResult(0));
+
+ return success();
+ }
+
+private:
+ /// The name of the attribute on FieldOps that contains the field name
+ /// metadata for the reflection map.
+ StringRef fieldAttrName;
+
+ /// Attributes that, if present on a field, exclude it from the
+ /// reflection map.
+ llvm::SmallVector<std::string> excludedFieldAttrs;
+};
+
+void mlir::emitc::populateAddReflectionMapPatterns(
+ RewritePatternSet &patterns, StringRef fieldAttrName,
+ llvm::ArrayRef<std::string> excludedFieldAttrs) {
+ patterns.add<AddReflectionMapClass>(patterns.getContext(), fieldAttrName,
+ excludedFieldAttrs);
+}
\ No newline at end of file
diff --git a/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt b/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
index baf67afc30072..dd8f014dc4737 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
+++ b/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
@@ -3,6 +3,7 @@ add_mlir_dialect_library(MLIREmitCTransforms
FormExpressions.cpp
TypeConversions.cpp
WrapFuncInClass.cpp
+ AddReflectionMap.cpp
ADDITIONAL_HEADER_DIRS
${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/EmitC/Transforms
diff --git a/mlir/test/Dialect/EmitC/add-reflection-map.mlir b/mlir/test/Dialect/EmitC/add-reflection-map.mlir
new file mode 100644
index 0000000000000..a1ec0356e19f4
--- /dev/null
+++ b/mlir/test/Dialect/EmitC/add-reflection-map.mlir
@@ -0,0 +1,58 @@
+// RUN: mlir-opt -split-input-file --add-reflection-map="field-attr-name=emitc.field_ref excluded-field-attrs="emitc.other_field"" %s | FileCheck %s
+
+
+// Tests that a reflection map is created for fields with a certain attribute.
+
+emitc.class @actionClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+ emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
+ emitc.func @"operator()"() {
+ %0 = get_field @fieldName0 : !emitc.array<1xf32>
+ return
+ }
+}
+
+// CHECK: emitc.class @actionClass {
+// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
+// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
+// CHECK-SAME: #emitc.opaque<"{ { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) }, { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) } }">
+// CHECK-NEXT: emitc.func @getBufferForName(%{{.*}}: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
+// CHECK-NEXT: %[[MAP0:.*]] = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
+// CHECK-NEXT: %[[VAL0:.*]] = member_call_opaque %[[MAP0]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
+// CHECK-NEXT: return %[[VAL0]] : !emitc.ptr<!emitc.opaque<"char">>
+// CHECK-NEXT: }
+// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: %{{.*}} = get_field @fieldName0 : !emitc.array<1xf32>
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+// -----
+// Test that a reflection map is created for fields with a certain named attribute
+// but not ones with an attribute present in the ignore-attributes option.
+
+emitc.class @actionClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+ emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
+ emitc.func @"operator()"() {
+ %0 = get_field @fieldName0 : !emitc.array<1xf32>
+ return
+ }
+}
+
+// CHECK: emitc.class @actionClass {
+// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
+// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
+// CHECK-SAME: #emitc.opaque<"{ { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) } }">
+// CHECK-NEXT: emitc.func @getBufferForName(%{{.*}}: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
+// CHECK-NEXT: %[[MAP1:.*]] = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
+// CHECK-NEXT: %[[VAL1:.*]] = member_call_opaque %[[MAP1]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
+// CHECK-NEXT: return %[[VAL1]] : !emitc.ptr<!emitc.opaque<"char">>
+// CHECK-NEXT: }
+// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: %{{.*}} = get_field @fieldName0 : !emitc.array<1xf32>
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+// CHECK-NEXT: }
\ No newline at end of file
>From fce6ac458f4eaeb0a701082864f0f9dda68fb422 Mon Sep 17 00:00:00 2001
From: Bhavesh M <85327930+beamandala at users.noreply.github.com>
Date: Tue, 23 Jun 2026 02:37:42 +0530
Subject: [PATCH 02/22] [mlir][EmitC] Make `GlobalOps` `FieldOps` in
wrap-emitc-func-in-class pass (#203641)
Update the `WrapFuncInClassPass` pass so that `GlobalOp`s are moved into
the `ClassOp` as `FieldOps`. This respects MLIR's behavior of resolving
references to the closest parent operation that defines a symbol table
which is the `ClassOp` that we are creating in this pass.
Without this change, references to a `GlobalOp` in `GetGlobalOp` are
failing to resolve.
Details:
- Identify `GlobalOp`s
- Create a `FieldOp` within the `ClassOp` for each `GlobalOp`
- Delete the `GlobalOp`s after all functions have been wrapped in a
class. Doing this after every function can cause an error when multiple
functions refer to the same `GlobalOp`(s) which would be deleted after
the first function is wrapped in a class.
Also renamed `fName` parameter in `populateWrapFuncInClass` to
`funcName` to match naming in `WrapFuncInClass`.
Based on PR #153452. Key differences:
- No size is set for the `globalsToMove` `SmallVector` type because I'm
not sure if the number of global variables is consistent across
different models.
- `GlobalOp`s are deleted after all functions have been processed.
- Instead of directly cloning the `GlobalOp`, an equivalent `FieldOp` is
created
- `GetGlobalOp`s are translated to `GetFieldOp`s
Co-authored-by: [Jaddyen](https://github.com/Jaddyen)
---
mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index 8eadfe9a2460d..4c8d3978af0f1 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -13,6 +13,7 @@
#include "mlir/IR/PatternMatch.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/DenseMap.h"
namespace mlir {
namespace emitc {
>From 66bed5c47b37810c550c08f63a8f6dcf918c621d Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 23 Jun 2026 17:32:22 -0700
Subject: [PATCH 03/22] [mlir][EmitC] Create a pass to add a reflection map to
a class
This creates the `add-reflection-map` pass to add a reflection map and a
helper method to EmitC classes for runtime field lookup.
Details:
- Add headers to import `map` and `string` if not already present.
- For every `ClassOp`:
- Collect all the `FieldOp`s that contain the attribute whose
value we want as the reflection map key. We ignore all
`FieldOp`s that contain an attribute present in
`excludedFieldAttrs` and if there's a `FieldOp` that neither
has the attribute we're looking for and isn't excluded by the
presence of one of the `excludedFieldAttrs`, emit and error.
- Create the reflection map where the key is the value of the
attribute in each applicable `FieldOp`'s attribute dictionary
and the value is a pointer to the field's contents.
- Create a `getBufferForName` method which serves as a getter
method for the reflection map.
Based on PR #150572. Key differences:
- Adds a `excluded-field-attrs` option which is a list of attributes
that if present on a field exclude it from the reflection map.
- Adds a helper method called `getBufferForName` which given a string
key returns a pointer to the field's buffer.
Co-authored-by: [Jaddyen](https://github.com/Jaddyen)
---
mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index 4c8d3978af0f1..5a4781dc31ea8 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -14,6 +14,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
namespace mlir {
namespace emitc {
>From 1b949e08c522bf1befdba3fc0612dcad5d04a02a Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Wed, 24 Jun 2026 10:31:17 -0700
Subject: [PATCH 04/22] Formatting
---
.../Dialect/EmitC/Transforms/Transforms.h | 6 +--
.../EmitC/Transforms/AddReflectionMap.cpp | 47 +++++++++++--------
2 files changed, 30 insertions(+), 23 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index 5a4781dc31ea8..8f1784f61f540 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -13,8 +13,6 @@
#include "mlir/IR/PatternMatch.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringRef.h"
namespace mlir {
namespace emitc {
@@ -44,7 +42,9 @@ void populateWrapFuncInClass(
// The AddReflectionMap pass.
//===----------------------------------------------------------------------===//
-void populateAddReflectionMapPatterns(RewritePatternSet &patterns, StringRef fieldAttrName, ArrayRef<std::string> excludedFieldAttrs);
+void populateAddReflectionMapPatterns(RewritePatternSet &patterns,
+ StringRef fieldAttrName,
+ ArrayRef<std::string> excludedFieldAttrs);
} // namespace emitc
} // namespace mlir
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index c1588463ffb7a..d444f3abbf4b1 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -31,9 +31,8 @@ constexpr const char *stringLibraryHeader = "string";
IncludeOp addHeader(OpBuilder &builder, ModuleOp module, StringRef headerName) {
StringAttr includeAttr = builder.getStringAttr(headerName);
- return IncludeOp::create(
- builder, module.getLoc(), includeAttr,
- /*is_standard_include=*/builder.getUnitAttr());
+ return IncludeOp::create(builder, module.getLoc(), includeAttr,
+ /*is_standard_include=*/builder.getUnitAttr());
}
class AddReflectionMapPass
@@ -43,7 +42,8 @@ class AddReflectionMapPass
mlir::ModuleOp moduleOp = getOperation();
RewritePatternSet patterns(&getContext());
- populateAddReflectionMapPatterns(patterns, fieldAttrName, excludedFieldAttrs);
+ populateAddReflectionMapPatterns(patterns, fieldAttrName,
+ excludedFieldAttrs);
walkAndApplyPatterns(moduleOp, std::move(patterns));
bool hasMapHdr = false;
@@ -55,7 +55,7 @@ class AddReflectionMapPass
if (includeOp.getIsStandardInclude()) {
auto include = includeOp.getInclude();
-
+
hasMapHdr = include == mapLibraryHeader;
hasStringHdr = include == stringLibraryHeader;
}
@@ -82,7 +82,8 @@ class AddReflectionMapClass : public OpRewritePattern<ClassOp> {
AddReflectionMapClass(MLIRContext *context, StringRef attrName,
llvm::ArrayRef<std::string> excludedFieldAttrs)
: OpRewritePattern<ClassOp>(context), fieldAttrName(attrName),
- excludedFieldAttrs(excludedFieldAttrs.begin(), excludedFieldAttrs.end()) {}
+ excludedFieldAttrs(excludedFieldAttrs.begin(),
+ excludedFieldAttrs.end()) {}
LogicalResult matchAndRewrite(ClassOp classOp,
PatternRewriter &rewriter) const override {
@@ -114,10 +115,9 @@ class AddReflectionMapClass : public OpRewritePattern<ClassOp> {
if (shouldIgnore)
return;
- fieldOp.emitError()
- << "FieldOp must have a dictionary attribute named '"
- << fieldAttrName << "'"
- << "with an array containing a string attribute";
+ fieldOp.emitError() << "FieldOp must have a dictionary attribute named '"
+ << fieldAttrName << "'"
+ << "with an array containing a string attribute";
});
std::string reflectionMapContents;
@@ -129,28 +129,34 @@ class AddReflectionMapClass : public OpRewritePattern<ClassOp> {
}
reflectionMapContents += " }";
- if (FuncOp executeFunc =
- classOp.lookupSymbol<FuncOp>("operator()"))
+ if (FuncOp executeFunc = classOp.lookupSymbol<FuncOp>("operator()"))
rewriter.setInsertionPoint(executeFunc);
else {
- classOp.emitError() << "ClassOp must contain a function named 'operator()' "
- "to add reflection map";
+ classOp.emitError()
+ << "ClassOp must contain a function named 'operator()' "
+ "to add reflection map";
return failure();
}
FieldOp reflectionMapField = FieldOp::create(
rewriter, classOp.getLoc(), rewriter.getStringAttr("reflectionMap"),
- TypeAttr::get(mapType), emitc::OpaqueAttr::get(context, reflectionMapContents));
+ TypeAttr::get(mapType),
+ emitc::OpaqueAttr::get(context, reflectionMapContents));
// Create getBufferForName method
- emitc::OpaqueType nameType = emitc::OpaqueType::get(rewriter.getContext(), "std::string");
- emitc::OpaqueType charType = emitc::OpaqueType::get(rewriter.getContext(), "char");
- emitc::PointerType valType = emitc::PointerType::get(rewriter.getContext(), charType);
+ emitc::OpaqueType nameType =
+ emitc::OpaqueType::get(rewriter.getContext(), "std::string");
+ emitc::OpaqueType charType =
+ emitc::OpaqueType::get(rewriter.getContext(), "char");
+ emitc::PointerType valType =
+ emitc::PointerType::get(rewriter.getContext(), charType);
FuncOp getBufferForNameFunc = FuncOp::create(
rewriter, reflectionMapField->getLoc(), "getBufferForName",
FunctionType::get(rewriter.getContext(), {nameType}, {valType}));
- Block *body = rewriter.createBlock(&getBufferForNameFunc.getBody(), {}, {nameType}, {reflectionMapField->getLoc()});
+ Block *body =
+ rewriter.createBlock(&getBufferForNameFunc.getBody(), {}, {nameType},
+ {reflectionMapField->getLoc()});
rewriter.setInsertionPointToStart(body);
GetFieldOp mapField = GetFieldOp::create(
rewriter, reflectionMapField->getLoc(), mapType, "reflectionMap");
@@ -158,7 +164,8 @@ class AddReflectionMapClass : public OpRewritePattern<ClassOp> {
MemberCallOpaqueOp lookupCall = MemberCallOpaqueOp::create(
rewriter, reflectionMapField->getLoc(), valType, mapField.getResult(),
"at", ArrayAttr{}, ArrayAttr{}, ValueRange{nameArg});
- ReturnOp::create(rewriter, reflectionMapField->getLoc(), lookupCall.getResult(0));
+ ReturnOp::create(rewriter, reflectionMapField->getLoc(),
+ lookupCall.getResult(0));
return success();
}
>From ab36f41166fd803ff45b2be3ca958125d82e940d Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Fri, 26 Jun 2026 16:04:13 -0700
Subject: [PATCH 05/22] Address review comments
---
.../mlir/Dialect/EmitC/Transforms/Passes.h | 2 +-
.../mlir/Dialect/EmitC/Transforms/Passes.td | 10 ++-
.../Dialect/EmitC/Transforms/Transforms.h | 6 +-
.../Dialect/EmitC/Transforms/CMakeLists.txt | 2 +-
...ectionMap.cpp => MLGOAddReflectionMap.cpp} | 76 +++++++++--------
...-map.mlir => mlgo-add-reflection-map.mlir} | 84 +++++++++++++++++--
.../Target/Cpp/mlgo-add-reflection-map.mlir | 57 +++++++++++++
7 files changed, 188 insertions(+), 49 deletions(-)
rename mlir/lib/Dialect/EmitC/Transforms/{AddReflectionMap.cpp => MLGOAddReflectionMap.cpp} (71%)
rename mlir/test/Dialect/EmitC/{add-reflection-map.mlir => mlgo-add-reflection-map.mlir} (52%)
create mode 100644 mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h
index 56596110a1e90..4f46d63774625 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h
@@ -16,7 +16,7 @@ namespace emitc {
#define GEN_PASS_DECL_FORMEXPRESSIONSPASS
#define GEN_PASS_DECL_WRAPFUNCINCLASSPASS
-#define GEN_PASS_DECL_ADDREFLECTIONMAPPASS
+#define GEN_PASS_DECL_MLGOADDREFLECTIONMAPPASS
#include "mlir/Dialect/EmitC/Transforms/Passes.h.inc"
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 6da57c07a8047..3d17f53032a7a 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -60,7 +60,7 @@ def WrapFuncInClassPass : Pass<"wrap-emitc-func-in-class", "ModuleOp"> {
];
}
-def AddReflectionMapPass : Pass<"add-reflection-map", "ModuleOp"> {
+def MLGOAddReflectionMapPass : Pass<"mlgo-add-reflection-map", "ModuleOp"> {
let summary = "Add a reflection map and a helper method to EmitC classes for runtime field lookup.";
let description = [{
This pass adds a `reflectionMap` field and an accompanying `getBufferForName` method to
@@ -68,9 +68,11 @@ def AddReflectionMapPass : Pass<"add-reflection-map", "ModuleOp"> {
This requires that the class has fields with attributes.
Each `emitc.field` is expected to have an attribute (configured by the
`field-attr-name` option) that is an array containing a single string
- attribute.
-
-
+ attribute. If a field possesses both the attribute specified by
+ `field-attr-name` and an attribute specified in `excluded-field-attrs`,
+ the `field-attr-name` attribute takes precedence and the field will be
+ included in the reflection map.
+
Example:
```mlir
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index 8f1784f61f540..6425614e1fbcd 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -42,9 +42,9 @@ void populateWrapFuncInClass(
// The AddReflectionMap pass.
//===----------------------------------------------------------------------===//
-void populateAddReflectionMapPatterns(RewritePatternSet &patterns,
- StringRef fieldAttrName,
- ArrayRef<std::string> excludedFieldAttrs);
+void populateMLGOAddReflectionMapPatterns(
+ RewritePatternSet &patterns, StringRef fieldAttrName,
+ ArrayRef<std::string> excludedFieldAttrs);
} // namespace emitc
} // namespace mlir
diff --git a/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt b/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
index dd8f014dc4737..ede2cb5e208f0 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
+++ b/mlir/lib/Dialect/EmitC/Transforms/CMakeLists.txt
@@ -3,7 +3,7 @@ add_mlir_dialect_library(MLIREmitCTransforms
FormExpressions.cpp
TypeConversions.cpp
WrapFuncInClass.cpp
- AddReflectionMap.cpp
+ MLGOAddReflectionMap.cpp
ADDITIONAL_HEADER_DIRS
${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/EmitC/Transforms
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
similarity index 71%
rename from mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
rename to mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index d444f3abbf4b1..9f506b10c722a 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -1,7 +1,7 @@
-//===- AddReflectionMap.cpp - Add a reflection map to a class --------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt license information.
+// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -22,7 +22,7 @@ using namespace emitc;
namespace mlir {
namespace emitc {
-#define GEN_PASS_DEF_ADDREFLECTIONMAPPASS
+#define GEN_PASS_DEF_MLGOADDREFLECTIONMAPPASS
#include "mlir/Dialect/EmitC/Transforms/Passes.h.inc"
namespace {
@@ -35,15 +35,15 @@ IncludeOp addHeader(OpBuilder &builder, ModuleOp module, StringRef headerName) {
/*is_standard_include=*/builder.getUnitAttr());
}
-class AddReflectionMapPass
- : public impl::AddReflectionMapPassBase<AddReflectionMapPass> {
- using AddReflectionMapPassBase::AddReflectionMapPassBase;
+class MLGOAddReflectionMapPass
+ : public impl::MLGOAddReflectionMapPassBase<MLGOAddReflectionMapPass> {
+ using MLGOAddReflectionMapPassBase::MLGOAddReflectionMapPassBase;
void runOnOperation() override {
mlir::ModuleOp moduleOp = getOperation();
RewritePatternSet patterns(&getContext());
- populateAddReflectionMapPatterns(patterns, fieldAttrName,
- excludedFieldAttrs);
+ populateMLGOAddReflectionMapPatterns(patterns, fieldAttrName,
+ excludedFieldAttrs);
walkAndApplyPatterns(moduleOp, std::move(patterns));
bool hasMapHdr = false;
@@ -56,8 +56,8 @@ class AddReflectionMapPass
if (includeOp.getIsStandardInclude()) {
auto include = includeOp.getInclude();
- hasMapHdr = include == mapLibraryHeader;
- hasStringHdr = include == stringLibraryHeader;
+ hasMapHdr |= include == mapLibraryHeader;
+ hasStringHdr |= include == stringLibraryHeader;
}
if (hasMapHdr && hasStringHdr)
@@ -77,10 +77,10 @@ class AddReflectionMapPass
} // namespace emitc
} // namespace mlir
-class AddReflectionMapClass : public OpRewritePattern<ClassOp> {
+class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
public:
- AddReflectionMapClass(MLIRContext *context, StringRef attrName,
- llvm::ArrayRef<std::string> excludedFieldAttrs)
+ MLGOAddReflectionMapClass(MLIRContext *context, StringRef attrName,
+ llvm::ArrayRef<std::string> excludedFieldAttrs)
: OpRewritePattern<ClassOp>(context), fieldAttrName(attrName),
excludedFieldAttrs(excludedFieldAttrs.begin(),
excludedFieldAttrs.end()) {}
@@ -93,33 +93,35 @@ class AddReflectionMapClass : public OpRewritePattern<ClassOp> {
context, "const std::map<std::string, char*>");
// Collect all field names
- std::vector<std::pair<std::string, std::string>> fieldNames;
+ std::vector<std::pair<StringRef, StringRef>> fieldNames;
+ bool hasError = false;
classOp.walk([&](FieldOp fieldOp) {
- if (Attribute attr = fieldOp->getAttrDictionary().get(fieldAttrName)) {
- if (ArrayAttr arrayAttr = dyn_cast<mlir::ArrayAttr>(attr)) {
- StringAttr stringAttr = cast<mlir::StringAttr>(arrayAttr[0]);
- fieldNames.emplace_back(stringAttr.getValue().str(),
- fieldOp.getName().str());
- return;
- }
- }
-
- bool shouldIgnore = false;
- for (const std::string &ignoreAttr : excludedFieldAttrs) {
- if (fieldOp->hasAttr(ignoreAttr)) {
- shouldIgnore = true;
- break;
+ if (auto arrayAttr =
+ dyn_cast_if_present<ArrayAttr>(fieldOp->getAttr(fieldAttrName))) {
+ if (!arrayAttr.empty()) {
+ if (auto stringAttr = dyn_cast<StringAttr>(arrayAttr[0])) {
+ fieldNames.emplace_back(stringAttr.getValue(), fieldOp.getName());
+ return;
+ }
}
}
+ bool shouldIgnore =
+ llvm::any_of(excludedFieldAttrs, [&fieldOp](StringRef ignoreAttr) {
+ return fieldOp->hasAttr(ignoreAttr);
+ });
if (shouldIgnore)
return;
fieldOp.emitError() << "FieldOp must have a dictionary attribute named '"
- << fieldAttrName << "'"
+ << fieldAttrName << "' "
<< "with an array containing a string attribute";
+ hasError = true;
});
+ if (hasError || fieldNames.empty())
+ return failure();
+
std::string reflectionMapContents;
reflectionMapContents += "{ ";
for (size_t i = 0, numFields = fieldNames.size(); i < numFields; ++i) {
@@ -138,6 +140,14 @@ class AddReflectionMapClass : public OpRewritePattern<ClassOp> {
return failure();
}
+ // To generate the following C++ code
+ // const std::map<std::string, char*> reflectionMap = {
+ // { "another_feature", reinterpret_cast<char*>(&fieldName0) },
+ // { "some_feature", reinterpret_cast<char*>(&fieldName1) },
+ // ...
+ // };
+ // This can be used to retrieve a pointer to the field's contents given the
+ // attribute string identifying the field
FieldOp reflectionMapField = FieldOp::create(
rewriter, classOp.getLoc(), rewriter.getStringAttr("reflectionMap"),
TypeAttr::get(mapType),
@@ -180,9 +190,9 @@ class AddReflectionMapClass : public OpRewritePattern<ClassOp> {
llvm::SmallVector<std::string> excludedFieldAttrs;
};
-void mlir::emitc::populateAddReflectionMapPatterns(
+void mlir::emitc::populateMLGOAddReflectionMapPatterns(
RewritePatternSet &patterns, StringRef fieldAttrName,
llvm::ArrayRef<std::string> excludedFieldAttrs) {
- patterns.add<AddReflectionMapClass>(patterns.getContext(), fieldAttrName,
- excludedFieldAttrs);
-}
\ No newline at end of file
+ patterns.add<MLGOAddReflectionMapClass>(patterns.getContext(), fieldAttrName,
+ excludedFieldAttrs);
+}
diff --git a/mlir/test/Dialect/EmitC/add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
similarity index 52%
rename from mlir/test/Dialect/EmitC/add-reflection-map.mlir
rename to mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index a1ec0356e19f4..2bea6cfaf2677 100644
--- a/mlir/test/Dialect/EmitC/add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -1,7 +1,7 @@
-// RUN: mlir-opt -split-input-file --add-reflection-map="field-attr-name=emitc.field_ref excluded-field-attrs="emitc.other_field"" %s | FileCheck %s
+// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="field-attr-name=emitc.field_ref \
+// RUN: excluded-field-attrs="emitc.other_field"" -verify-diagnostics %s | FileCheck %s
-
-// Tests that a reflection map is created for fields with a certain attribute.
+/// Tests that a reflection map is created for fields with a certain attribute.
emitc.class @actionClass {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
@@ -29,10 +29,11 @@ emitc.class @actionClass {
// CHECK-NEXT: }
// -----
-// Test that a reflection map is created for fields with a certain named attribute
-// but not ones with an attribute present in the ignore-attributes option.
-emitc.class @actionClass {
+/// Test that a reflection map is created for fields with a certain named attribute
+/// but not ones with an attribute present in the ignore-attributes option.
+
+emitc.class @actionClassExcluded {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
emitc.func @"operator()"() {
@@ -41,7 +42,7 @@ emitc.class @actionClass {
}
}
-// CHECK: emitc.class @actionClass {
+// CHECK: emitc.class @actionClassExcluded {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
@@ -55,4 +56,73 @@ emitc.class @actionClass {
// CHECK-NEXT: %{{.*}} = get_field @fieldName0 : !emitc.array<1xf32>
// CHECK-NEXT: return
// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+// -----
+
+/// Test that the pass leaves IR unchanged if fields don't have any attributes
+
+emitc.class @actionClassNoAttrs {
+ // expected-error @below {{FieldOp must have a dictionary attribute named 'emitc.field_ref' with an array containing a string attribute}}
+ emitc.field @fieldName0 : !emitc.array<1xf32>
+ emitc.func @"operator()"() {
+ return
+ }
+}
+
+// CHECK-LABEL: emitc.class @actionClassNoAttrs {
+// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32>
+// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+// -----
+
+/// Test that the pass leaves IR unchanged if the ClassOp doesn't have any fields
+
+emitc.class @actionClassNoFields {
+ emitc.func @"operator()"() {
+ return
+ }
+}
+
+// CHECK-LABEL: emitc.class @actionClassNoFields {
+// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+// -----
+
+/// Test that the pass returns with a match failure if the ClassOp doesn't have
+/// a FunctionOp named operator()
+
+// expected-error @below {{ClassOp must contain a function named 'operator()' to add reflection map}}
+emitc.class @actionClassNoOperator {
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+}
+
+// CHECK-LABEL: emitc.class @actionClassNoOperator {
+// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+// CHECK-NEXT: }
+
+// -----
+
+/// Test that the pass returns with a match failure if a FieldOp has the specified
+/// dictionary attribute with an array containing a type other than string
+
+emitc.class @actionClassNonStringAttr {
+ // expected-error @below {{FieldOp must have a dictionary attribute named 'emitc.field_ref' with an array containing a string attribute}}
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
+ emitc.func @"operator()"() {
+ return
+ }
+}
+
+// CHECK-LABEL: emitc.class @actionClassNonStringAttr {
+// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
+// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: return
+// CHECK-NEXT: }
// CHECK-NEXT: }
\ No newline at end of file
diff --git a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
new file mode 100644
index 0000000000000..eb8a8e58c8b77
--- /dev/null
+++ b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
@@ -0,0 +1,57 @@
+// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="field-attr-name=emitc.field_ref excluded-field-attrs=emitc.other_field" %s | mlir-translate -mlir-to-cpp | FileCheck %s
+
+// Test that a reflection map and lookup function are generated in the class.
+
+emitc.class @actionClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+ emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
+ emitc.func @"operator()"() {
+ %0 = get_field @fieldName0 : !emitc.array<1xf32>
+ return
+ }
+}
+
+// CHECK: #include <map>
+// CHECK-NEXT: #include <string>
+// CHECK-NEXT: class actionClass {
+// CHECK-NEXT: public:
+// CHECK-NEXT: float fieldName0[1];
+// CHECK-NEXT: float fieldName1[1];
+// CHECK-NEXT: const std::map<std::string, char*> reflectionMap = { { "another_feature", reinterpret_cast<char*>(&fieldName0) }, { "some_feature", reinterpret_cast<char*>(&fieldName1) } };
+// CHECK-NEXT: char* getBufferForName(std::string [[VAL_1:v[0-9]+]]) {
+// CHECK-NEXT: char* [[VAL_2:v[0-9]+]] = reflectionMap.at([[VAL_1]]);
+// CHECK-NEXT: return [[VAL_2]];
+// CHECK-NEXT: }
+// CHECK-NEXT: void operator()() {
+// CHECK-NEXT: return;
+// CHECK-NEXT: }
+// CHECK-NEXT: };
+
+// -----
+
+// Test that fields with excluded attributes are ignored.
+
+emitc.class @actionClassExcluded {
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+ emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
+ emitc.func @"operator()"() {
+ %0 = get_field @fieldName0 : !emitc.array<1xf32>
+ return
+ }
+}
+
+// CHECK: #include <map>
+// CHECK-NEXT: #include <string>
+// CHECK-NEXT: class actionClassExcluded {
+// CHECK-NEXT: public:
+// CHECK-NEXT: float fieldName0[1];
+// CHECK-NEXT: float fieldName1[1];
+// CHECK-NEXT: const std::map<std::string, char*> reflectionMap = { { "another_feature", reinterpret_cast<char*>(&fieldName0) } };
+// CHECK-NEXT: char* getBufferForName(std::string [[VAL_1:v[0-9]+]]) {
+// CHECK-NEXT: char* [[VAL_2:v[0-9]+]] = reflectionMap.at([[VAL_1]]);
+// CHECK-NEXT: return [[VAL_2]];
+// CHECK-NEXT: }
+// CHECK-NEXT: void operator()() {
+// CHECK-NEXT: return;
+// CHECK-NEXT: }
+// CHECK-NEXT: };
>From 0b94b71ff7e2b25032b3203b9a58d9fee2780ae4 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Mon, 6 Jul 2026 12:50:33 -0700
Subject: [PATCH 06/22] update MLGOAddReflectionMap to use notifyMatchFailure,
update example to be more minimal and clear about the transformation, use
range based for loop in reflection map construction
---
.../mlir/Dialect/EmitC/Transforms/Passes.td | 24 ++++++------------
.../EmitC/Transforms/MLGOAddReflectionMap.cpp | 25 +++++++++++--------
.../EmitC/mlgo-add-reflection-map.mlir | 5 +---
3 files changed, 24 insertions(+), 30 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 3d17f53032a7a..29b053f3469c7 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -77,28 +77,20 @@ def MLGOAddReflectionMapPass : Pass<"mlgo-add-reflection-map", "ModuleOp"> {
```mlir
emitc.class @MyClass {
- emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
- emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
- emitc.func @execute() { ... }
+ emitc.field @field0 : !emitc.array<1xf32> {emitc.field_ref = ["feature0"]}
+ emitc.func @operator()() { ... }
}
Becomes:
emitc.class @MyClass {
- emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
- emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
- emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
- #emitc.opaque<"{ { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) }, { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) } }">
- emitc.func @getBufferForName(%arg0: !emitc.opaque<"std::string">)
- -> !emitc.ptr<!emitc.opaque<"char">> {
- %0 = get_field @reflectionMap
- : !emitc.opaque<"const std::map<std::string, char*>">
- %1 = member_call_opaque %0 "at"(%arg0)
- : !emitc.opaque<"const std::map<std::string, char*>">,
- (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
- return %1 : !emitc.ptr<!emitc.opaque<"char">>
+ emitc.field @field0 : !emitc.array<1xf32> {emitc.field_ref = ["feature0"]}
+ // reflectionMap maps the string "feature0" to the address of @field0
+ emitc.field @reflectionMap : const std::map<string, char*> = {{"feature0", &field0}}
+ emitc.func @getBufferForName(%name: string) -> char* {
+ return reflectionMap.at(%name)
}
- emitc.func @execute() { ... }
+ emitc.func @operator()() { ... }
}
```
}];
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index 9f506b10c722a..05a850c0e7551 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -113,9 +113,11 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
if (shouldIgnore)
return;
- fieldOp.emitError() << "FieldOp must have a dictionary attribute named '"
- << fieldAttrName << "' "
- << "with an array containing a string attribute";
+ (void)rewriter.notifyMatchFailure(fieldOp, [&](Diagnostic &diag) {
+ diag << "FieldOp must have a dictionary attribute named '"
+ << fieldAttrName << "' "
+ << "with an array containing a string attribute";
+ });
hasError = true;
});
@@ -124,20 +126,23 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
std::string reflectionMapContents;
reflectionMapContents += "{ ";
- for (size_t i = 0, numFields = fieldNames.size(); i < numFields; ++i) {
+ bool first = true;
+ for (const auto &[name, value] : fieldNames) {
+ if (!first)
+ reflectionMapContents += ", ";
+
+ first = false;
reflectionMapContents += llvm::formatv(
- "{ \"{0}\", reinterpret_cast<char*>(&{1}) }{2}", fieldNames[i].first,
- fieldNames[i].second, (i < numFields - 1) ? ", " : "");
+ "{ \"{0}\", reinterpret_cast<char*>(&{1}) }", name, value);
}
reflectionMapContents += " }";
if (FuncOp executeFunc = classOp.lookupSymbol<FuncOp>("operator()"))
rewriter.setInsertionPoint(executeFunc);
else {
- classOp.emitError()
- << "ClassOp must contain a function named 'operator()' "
- "to add reflection map";
- return failure();
+ return rewriter.notifyMatchFailure(
+ classOp, "ClassOp must contain a function named 'operator()' "
+ "to add reflection map");
}
// To generate the following C++ code
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index 2bea6cfaf2677..1f8647910ada9 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -1,5 +1,5 @@
// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="field-attr-name=emitc.field_ref \
-// RUN: excluded-field-attrs="emitc.other_field"" -verify-diagnostics %s | FileCheck %s
+// RUN: excluded-field-attrs="emitc.other_field"" %s | FileCheck %s
/// Tests that a reflection map is created for fields with a certain attribute.
@@ -63,7 +63,6 @@ emitc.class @actionClassExcluded {
/// Test that the pass leaves IR unchanged if fields don't have any attributes
emitc.class @actionClassNoAttrs {
- // expected-error @below {{FieldOp must have a dictionary attribute named 'emitc.field_ref' with an array containing a string attribute}}
emitc.field @fieldName0 : !emitc.array<1xf32>
emitc.func @"operator()"() {
return
@@ -98,7 +97,6 @@ emitc.class @actionClassNoFields {
/// Test that the pass returns with a match failure if the ClassOp doesn't have
/// a FunctionOp named operator()
-// expected-error @below {{ClassOp must contain a function named 'operator()' to add reflection map}}
emitc.class @actionClassNoOperator {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
}
@@ -113,7 +111,6 @@ emitc.class @actionClassNoOperator {
/// dictionary attribute with an array containing a type other than string
emitc.class @actionClassNonStringAttr {
- // expected-error @below {{FieldOp must have a dictionary attribute named 'emitc.field_ref' with an array containing a string attribute}}
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
emitc.func @"operator()"() {
return
>From d5e8301536841a90c281fd46a84414bb2507bd42 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 14 Jul 2026 12:59:46 -0700
Subject: [PATCH 07/22] Update tests with formatting and wording changes,
update pass description, only add headers if there was a match
---
.../mlir/Dialect/EmitC/Transforms/Passes.td | 4 +--
.../EmitC/Transforms/MLGOAddReflectionMap.cpp | 16 ++++++++-
.../EmitC/mlgo-add-reflection-map.mlir | 8 +++--
.../Target/Cpp/mlgo-add-reflection-map.mlir | 34 +++++++++++++++----
4 files changed, 51 insertions(+), 11 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 29b053f3469c7..1e245e00c9b2d 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -78,7 +78,7 @@ def MLGOAddReflectionMapPass : Pass<"mlgo-add-reflection-map", "ModuleOp"> {
```mlir
emitc.class @MyClass {
emitc.field @field0 : !emitc.array<1xf32> {emitc.field_ref = ["feature0"]}
- emitc.func @operator()() { ... }
+ emitc.func @execute() { ... }
}
Becomes:
@@ -90,7 +90,7 @@ def MLGOAddReflectionMapPass : Pass<"mlgo-add-reflection-map", "ModuleOp"> {
emitc.func @getBufferForName(%name: string) -> char* {
return reflectionMap.at(%name)
}
- emitc.func @operator()() { ... }
+ emitc.func @execute() { ... }
}
```
}];
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index 05a850c0e7551..666d9bc01dfdf 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -29,6 +29,15 @@ namespace {
constexpr const char *mapLibraryHeader = "map";
constexpr const char *stringLibraryHeader = "string";
+struct PatternMatchListener : public RewriterBase::Listener {
+ bool patternApplied = false;
+
+ void notifyOperationInserted(Operation *op,
+ OpBuilder::InsertPoint previous) override {
+ patternApplied = true;
+ }
+};
+
IncludeOp addHeader(OpBuilder &builder, ModuleOp module, StringRef headerName) {
StringAttr includeAttr = builder.getStringAttr(headerName);
return IncludeOp::create(builder, module.getLoc(), includeAttr,
@@ -45,7 +54,12 @@ class MLGOAddReflectionMapPass
populateMLGOAddReflectionMapPatterns(patterns, fieldAttrName,
excludedFieldAttrs);
- walkAndApplyPatterns(moduleOp, std::move(patterns));
+ PatternMatchListener listener;
+ walkAndApplyPatterns(moduleOp, std::move(patterns), &listener);
+
+ if (!listener.patternApplied)
+ return;
+
bool hasMapHdr = false;
bool hasStringHdr = false;
for (auto &op : *moduleOp.getBody()) {
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index 1f8647910ada9..030de796b308c 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -60,7 +60,7 @@ emitc.class @actionClassExcluded {
// -----
-/// Test that the pass leaves IR unchanged if fields don't have any attributes
+/// Test that the pass leaves IR unchanged if fields don't have any attributes (match failure)
emitc.class @actionClassNoAttrs {
emitc.field @fieldName0 : !emitc.array<1xf32>
@@ -69,6 +69,7 @@ emitc.class @actionClassNoAttrs {
}
}
+// CHECK-NOT: emitc.include
// CHECK-LABEL: emitc.class @actionClassNoAttrs {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32>
// CHECK-NEXT: emitc.func @"operator()"() {
@@ -78,7 +79,7 @@ emitc.class @actionClassNoAttrs {
// -----
-/// Test that the pass leaves IR unchanged if the ClassOp doesn't have any fields
+/// Test that the pass leaves IR unchanged if the ClassOp doesn't have any fields (match failure)
emitc.class @actionClassNoFields {
emitc.func @"operator()"() {
@@ -86,6 +87,7 @@ emitc.class @actionClassNoFields {
}
}
+// CHECK-NOT: emitc.include
// CHECK-LABEL: emitc.class @actionClassNoFields {
// CHECK-NEXT: emitc.func @"operator()"() {
// CHECK-NEXT: return
@@ -101,6 +103,7 @@ emitc.class @actionClassNoOperator {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
}
+// CHECK-NOT: emitc.include
// CHECK-LABEL: emitc.class @actionClassNoOperator {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: }
@@ -117,6 +120,7 @@ emitc.class @actionClassNonStringAttr {
}
}
+// CHECK-NOT: emitc.include
// CHECK-LABEL: emitc.class @actionClassNonStringAttr {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
// CHECK-NEXT: emitc.func @"operator()"() {
diff --git a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
index eb8a8e58c8b77..e37b09bc6d611 100644
--- a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
@@ -1,6 +1,6 @@
// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="field-attr-name=emitc.field_ref excluded-field-attrs=emitc.other_field" %s | mlir-translate -mlir-to-cpp | FileCheck %s
-// Test that a reflection map and lookup function are generated in the class.
+/// Test that a reflection map and lookup function are generated in the class.
emitc.class @actionClass {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
@@ -17,10 +17,11 @@ emitc.class @actionClass {
// CHECK-NEXT: public:
// CHECK-NEXT: float fieldName0[1];
// CHECK-NEXT: float fieldName1[1];
-// CHECK-NEXT: const std::map<std::string, char*> reflectionMap = { { "another_feature", reinterpret_cast<char*>(&fieldName0) }, { "some_feature", reinterpret_cast<char*>(&fieldName1) } };
-// CHECK-NEXT: char* getBufferForName(std::string [[VAL_1:v[0-9]+]]) {
-// CHECK-NEXT: char* [[VAL_2:v[0-9]+]] = reflectionMap.at([[VAL_1]]);
-// CHECK-NEXT: return [[VAL_2]];
+// CHECK-NEXT: const std::map<std::string, char*> reflectionMap = { { "another_feature", reinterpret_cast<char*>(&fieldName0) },
+// CHECK-SAME: { "some_feature", reinterpret_cast<char*>(&fieldName1) } };
+// CHECK-NEXT: char* getBufferForName(std::string [[ARG:v[0-9]+]]) {
+// CHECK-NEXT: char* [[VAR:v[0-9]+]] = reflectionMap.at([[ARG]]);
+// CHECK-NEXT: return [[VAR]];
// CHECK-NEXT: }
// CHECK-NEXT: void operator()() {
// CHECK-NEXT: return;
@@ -29,7 +30,7 @@ emitc.class @actionClass {
// -----
-// Test that fields with excluded attributes are ignored.
+/// Test that fields with excluded attributes are ignored.
emitc.class @actionClassExcluded {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
@@ -55,3 +56,24 @@ emitc.class @actionClassExcluded {
// CHECK-NEXT: return;
// CHECK-NEXT: }
// CHECK-NEXT: };
+
+// -----
+
+/// Test that translation doesn't add headers if the class does not match the pass.
+
+emitc.class @actionClassNoAttrs {
+ emitc.field @fieldName0 : !emitc.array<1xf32>
+ emitc.func @"operator()"() {
+ return
+ }
+}
+
+// CHECK-NOT: #include <map>
+// CHECK-NOT: #include <string>
+// CHECK: class actionClassNoAttrs {
+// CHECK-NEXT: public:
+// CHECK-NEXT: float fieldName0[1];
+// CHECK-NEXT: void operator()() {
+// CHECK-NEXT: return;
+// CHECK-NEXT: }
+// CHECK-NEXT: };
>From 72d872a2e29df289955aedc5d85733a5f2d50b49 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 14 Jul 2026 13:20:12 -0700
Subject: [PATCH 08/22] Update naming of classes in tests to be generic
---
.../EmitC/mlgo-add-reflection-map.mlir | 24 +++++++++----------
.../Target/Cpp/mlgo-add-reflection-map.mlir | 12 +++++-----
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index 030de796b308c..bd6733f982fe0 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -3,7 +3,7 @@
/// Tests that a reflection map is created for fields with a certain attribute.
-emitc.class @actionClass {
+emitc.class @foo {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
emitc.func @"operator()"() {
@@ -12,7 +12,7 @@ emitc.class @actionClass {
}
}
-// CHECK: emitc.class @actionClass {
+// CHECK: emitc.class @foo {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
@@ -33,7 +33,7 @@ emitc.class @actionClass {
/// Test that a reflection map is created for fields with a certain named attribute
/// but not ones with an attribute present in the ignore-attributes option.
-emitc.class @actionClassExcluded {
+emitc.class @fooExcluded {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
emitc.func @"operator()"() {
@@ -42,7 +42,7 @@ emitc.class @actionClassExcluded {
}
}
-// CHECK: emitc.class @actionClassExcluded {
+// CHECK: emitc.class @fooExcluded {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
@@ -62,7 +62,7 @@ emitc.class @actionClassExcluded {
/// Test that the pass leaves IR unchanged if fields don't have any attributes (match failure)
-emitc.class @actionClassNoAttrs {
+emitc.class @fooNoAttrs {
emitc.field @fieldName0 : !emitc.array<1xf32>
emitc.func @"operator()"() {
return
@@ -70,7 +70,7 @@ emitc.class @actionClassNoAttrs {
}
// CHECK-NOT: emitc.include
-// CHECK-LABEL: emitc.class @actionClassNoAttrs {
+// CHECK-LABEL: emitc.class @fooNoAttrs {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32>
// CHECK-NEXT: emitc.func @"operator()"() {
// CHECK-NEXT: return
@@ -81,14 +81,14 @@ emitc.class @actionClassNoAttrs {
/// Test that the pass leaves IR unchanged if the ClassOp doesn't have any fields (match failure)
-emitc.class @actionClassNoFields {
+emitc.class @fooNoFields {
emitc.func @"operator()"() {
return
}
}
// CHECK-NOT: emitc.include
-// CHECK-LABEL: emitc.class @actionClassNoFields {
+// CHECK-LABEL: emitc.class @fooNoFields {
// CHECK-NEXT: emitc.func @"operator()"() {
// CHECK-NEXT: return
// CHECK-NEXT: }
@@ -99,12 +99,12 @@ emitc.class @actionClassNoFields {
/// Test that the pass returns with a match failure if the ClassOp doesn't have
/// a FunctionOp named operator()
-emitc.class @actionClassNoOperator {
+emitc.class @fooNoOperator {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
}
// CHECK-NOT: emitc.include
-// CHECK-LABEL: emitc.class @actionClassNoOperator {
+// CHECK-LABEL: emitc.class @fooNoOperator {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: }
@@ -113,7 +113,7 @@ emitc.class @actionClassNoOperator {
/// Test that the pass returns with a match failure if a FieldOp has the specified
/// dictionary attribute with an array containing a type other than string
-emitc.class @actionClassNonStringAttr {
+emitc.class @fooNonStringAttr {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
emitc.func @"operator()"() {
return
@@ -121,7 +121,7 @@ emitc.class @actionClassNonStringAttr {
}
// CHECK-NOT: emitc.include
-// CHECK-LABEL: emitc.class @actionClassNonStringAttr {
+// CHECK-LABEL: emitc.class @fooNonStringAttr {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
// CHECK-NEXT: emitc.func @"operator()"() {
// CHECK-NEXT: return
diff --git a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
index e37b09bc6d611..d0eab613672a7 100644
--- a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
@@ -2,7 +2,7 @@
/// Test that a reflection map and lookup function are generated in the class.
-emitc.class @actionClass {
+emitc.class @foo {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
emitc.func @"operator()"() {
@@ -13,7 +13,7 @@ emitc.class @actionClass {
// CHECK: #include <map>
// CHECK-NEXT: #include <string>
-// CHECK-NEXT: class actionClass {
+// CHECK-NEXT: class foo {
// CHECK-NEXT: public:
// CHECK-NEXT: float fieldName0[1];
// CHECK-NEXT: float fieldName1[1];
@@ -32,7 +32,7 @@ emitc.class @actionClass {
/// Test that fields with excluded attributes are ignored.
-emitc.class @actionClassExcluded {
+emitc.class @fooExcluded {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
emitc.func @"operator()"() {
@@ -43,7 +43,7 @@ emitc.class @actionClassExcluded {
// CHECK: #include <map>
// CHECK-NEXT: #include <string>
-// CHECK-NEXT: class actionClassExcluded {
+// CHECK-NEXT: class fooExcluded {
// CHECK-NEXT: public:
// CHECK-NEXT: float fieldName0[1];
// CHECK-NEXT: float fieldName1[1];
@@ -61,7 +61,7 @@ emitc.class @actionClassExcluded {
/// Test that translation doesn't add headers if the class does not match the pass.
-emitc.class @actionClassNoAttrs {
+emitc.class @fooNoAttrs {
emitc.field @fieldName0 : !emitc.array<1xf32>
emitc.func @"operator()"() {
return
@@ -70,7 +70,7 @@ emitc.class @actionClassNoAttrs {
// CHECK-NOT: #include <map>
// CHECK-NOT: #include <string>
-// CHECK: class actionClassNoAttrs {
+// CHECK: class fooNoAttrs {
// CHECK-NEXT: public:
// CHECK-NEXT: float fieldName0[1];
// CHECK-NEXT: void operator()() {
>From d3c18f1c82a794408eea80cf63e8ca4fbcdd2fae Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 14 Jul 2026 13:29:53 -0700
Subject: [PATCH 09/22] Set a filecheck pattern variable for \22 escape
character to make test more readable
---
mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index bd6733f982fe0..2220d419b677c 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -1,5 +1,5 @@
// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="field-attr-name=emitc.field_ref \
-// RUN: excluded-field-attrs="emitc.other_field"" %s | FileCheck %s
+// RUN: excluded-field-attrs="emitc.other_field"" %s | FileCheck %s -DQUOTE="\[[QUOTE]]"
/// Tests that a reflection map is created for fields with a certain attribute.
@@ -16,7 +16,7 @@ emitc.class @foo {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
-// CHECK-SAME: #emitc.opaque<"{ { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) }, { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) } }">
+// CHECK-SAME: #emitc.opaque<"{ { [[QUOTE]]another_feature[[QUOTE]], reinterpret_cast<char*>(&fieldName0) }, { [[QUOTE]]some_feature[[QUOTE]], reinterpret_cast<char*>(&fieldName1) } }">
// CHECK-NEXT: emitc.func @getBufferForName(%{{.*}}: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
// CHECK-NEXT: %[[MAP0:.*]] = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
// CHECK-NEXT: %[[VAL0:.*]] = member_call_opaque %[[MAP0]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
@@ -46,7 +46,7 @@ emitc.class @fooExcluded {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
-// CHECK-SAME: #emitc.opaque<"{ { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) } }">
+// CHECK-SAME: #emitc.opaque<"{ { [[QUOTE]]another_feature[[QUOTE]], reinterpret_cast<char*>(&fieldName0) } }">
// CHECK-NEXT: emitc.func @getBufferForName(%{{.*}}: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
// CHECK-NEXT: %[[MAP1:.*]] = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
// CHECK-NEXT: %[[VAL1:.*]] = member_call_opaque %[[MAP1]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
>From 076af93ded9db57ab7e0f3663ae7883ef6c6f05b Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 14 Jul 2026 16:20:05 -0700
Subject: [PATCH 10/22] Relax the restriction where pass only matched classes
with operator()() present
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 2 +-
.../EmitC/Transforms/MLGOAddReflectionMap.cpp | 13 ++++++------
.../EmitC/mlgo-add-reflection-map.mlir | 20 ++++++++++++-------
3 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index d42c74e963769..0636232906f1b 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1801,7 +1801,7 @@ def EmitC_ClassOp
let extraClassDeclaration = [{
// Returns the body block containing class members and methods.
- Block &getBlock();
+ Block &getBlock() { return getBody().front(); }
}];
let assemblyFormat =
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index 666d9bc01dfdf..12b173074e5bb 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -151,13 +151,12 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
}
reflectionMapContents += " }";
- if (FuncOp executeFunc = classOp.lookupSymbol<FuncOp>("operator()"))
- rewriter.setInsertionPoint(executeFunc);
- else {
- return rewriter.notifyMatchFailure(
- classOp, "ClassOp must contain a function named 'operator()' "
- "to add reflection map");
- }
+ auto funcs = classOp.getBlock().getOps<FuncOp>();
+ auto it = funcs.begin();
+ if (it != funcs.end())
+ rewriter.setInsertionPoint(*it);
+ else
+ rewriter.setInsertionPointToEnd(&classOp.getBlock());
// To generate the following C++ code
// const std::map<std::string, char*> reflectionMap = {
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index 2220d419b677c..3374838aeac34 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -1,5 +1,5 @@
// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="field-attr-name=emitc.field_ref \
-// RUN: excluded-field-attrs="emitc.other_field"" %s | FileCheck %s -DQUOTE="\[[QUOTE]]"
+// RUN: excluded-field-attrs="emitc.other_field"" %s | FileCheck %s '-D$QUOTE=\22'
/// Tests that a reflection map is created for fields with a certain attribute.
@@ -16,7 +16,7 @@ emitc.class @foo {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
-// CHECK-SAME: #emitc.opaque<"{ { [[QUOTE]]another_feature[[QUOTE]], reinterpret_cast<char*>(&fieldName0) }, { [[QUOTE]]some_feature[[QUOTE]], reinterpret_cast<char*>(&fieldName1) } }">
+// CHECK-SAME: #emitc.opaque<"{ { [[$QUOTE]]another_feature[[$QUOTE]], reinterpret_cast<char*>(&fieldName0) }, { [[$QUOTE]]some_feature[[$QUOTE]], reinterpret_cast<char*>(&fieldName1) } }">
// CHECK-NEXT: emitc.func @getBufferForName(%{{.*}}: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
// CHECK-NEXT: %[[MAP0:.*]] = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
// CHECK-NEXT: %[[VAL0:.*]] = member_call_opaque %[[MAP0]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
@@ -46,7 +46,7 @@ emitc.class @fooExcluded {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
-// CHECK-SAME: #emitc.opaque<"{ { [[QUOTE]]another_feature[[QUOTE]], reinterpret_cast<char*>(&fieldName0) } }">
+// CHECK-SAME: #emitc.opaque<"{ { [[$QUOTE]]another_feature[[$QUOTE]], reinterpret_cast<char*>(&fieldName0) } }">
// CHECK-NEXT: emitc.func @getBufferForName(%{{.*}}: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
// CHECK-NEXT: %[[MAP1:.*]] = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
// CHECK-NEXT: %[[VAL1:.*]] = member_call_opaque %[[MAP1]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
@@ -96,16 +96,22 @@ emitc.class @fooNoFields {
// -----
-/// Test that the pass returns with a match failure if the ClassOp doesn't have
-/// a FunctionOp named operator()
+/// Test that a reflection map is still created in the case that there are no
+/// functions in the class
emitc.class @fooNoOperator {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
}
-// CHECK-NOT: emitc.include
// CHECK-LABEL: emitc.class @fooNoOperator {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
+// CHECK-SAME: #emitc.opaque<"{ { [[$QUOTE]]another_feature[[$QUOTE]], reinterpret_cast<char*>(&fieldName0) } }">
+// CHECK-NEXT: emitc.func @getBufferForName(%{{.*}}: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
+// CHECK-NEXT: %[[MAP0:.*]] = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
+// CHECK-NEXT: %[[VAL0:.*]] = member_call_opaque %[[MAP0]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
+// CHECK-NEXT: return %[[VAL0]] : !emitc.ptr<!emitc.opaque<"char">>
+// CHECK-NEXT: }
// CHECK-NEXT: }
// -----
@@ -126,4 +132,4 @@ emitc.class @fooNonStringAttr {
// CHECK-NEXT: emitc.func @"operator()"() {
// CHECK-NEXT: return
// CHECK-NEXT: }
-// CHECK-NEXT: }
\ No newline at end of file
+// CHECK-NEXT: }
>From ef2ab7ac726afd849958ce3f97fdb48a4e3f3f0f Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 14 Jul 2026 16:23:11 -0700
Subject: [PATCH 11/22] Simplify test
---
mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir | 2 --
1 file changed, 2 deletions(-)
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index 3374838aeac34..5e7780dc23f78 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -7,7 +7,6 @@ emitc.class @foo {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
emitc.func @"operator()"() {
- %0 = get_field @fieldName0 : !emitc.array<1xf32>
return
}
}
@@ -23,7 +22,6 @@ emitc.class @foo {
// CHECK-NEXT: return %[[VAL0]] : !emitc.ptr<!emitc.opaque<"char">>
// CHECK-NEXT: }
// CHECK-NEXT: emitc.func @"operator()"() {
-// CHECK-NEXT: %{{.*}} = get_field @fieldName0 : !emitc.array<1xf32>
// CHECK-NEXT: return
// CHECK-NEXT: }
// CHECK-NEXT: }
>From aeeb53b5ff87882f41a42a0f4b4eaca9db7d6fbe Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 14 Jul 2026 16:25:12 -0700
Subject: [PATCH 12/22] Update function name in tests to be general
---
.../EmitC/mlgo-add-reflection-map.mlir | 20 +++++++++----------
.../Target/Cpp/mlgo-add-reflection-map.mlir | 12 +++++------
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index 5e7780dc23f78..d18c1583cfd38 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -6,7 +6,7 @@
emitc.class @foo {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
- emitc.func @"operator()"() {
+ emitc.func @bar() {
return
}
}
@@ -21,7 +21,7 @@ emitc.class @foo {
// CHECK-NEXT: %[[VAL0:.*]] = member_call_opaque %[[MAP0]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
// CHECK-NEXT: return %[[VAL0]] : !emitc.ptr<!emitc.opaque<"char">>
// CHECK-NEXT: }
-// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: emitc.func @bar() {
// CHECK-NEXT: return
// CHECK-NEXT: }
// CHECK-NEXT: }
@@ -34,7 +34,7 @@ emitc.class @foo {
emitc.class @fooExcluded {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
- emitc.func @"operator()"() {
+ emitc.func @bar() {
%0 = get_field @fieldName0 : !emitc.array<1xf32>
return
}
@@ -50,7 +50,7 @@ emitc.class @fooExcluded {
// CHECK-NEXT: %[[VAL1:.*]] = member_call_opaque %[[MAP1]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
// CHECK-NEXT: return %[[VAL1]] : !emitc.ptr<!emitc.opaque<"char">>
// CHECK-NEXT: }
-// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: emitc.func @bar() {
// CHECK-NEXT: %{{.*}} = get_field @fieldName0 : !emitc.array<1xf32>
// CHECK-NEXT: return
// CHECK-NEXT: }
@@ -62,7 +62,7 @@ emitc.class @fooExcluded {
emitc.class @fooNoAttrs {
emitc.field @fieldName0 : !emitc.array<1xf32>
- emitc.func @"operator()"() {
+ emitc.func @bar() {
return
}
}
@@ -70,7 +70,7 @@ emitc.class @fooNoAttrs {
// CHECK-NOT: emitc.include
// CHECK-LABEL: emitc.class @fooNoAttrs {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32>
-// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: emitc.func @bar() {
// CHECK-NEXT: return
// CHECK-NEXT: }
// CHECK-NEXT: }
@@ -80,14 +80,14 @@ emitc.class @fooNoAttrs {
/// Test that the pass leaves IR unchanged if the ClassOp doesn't have any fields (match failure)
emitc.class @fooNoFields {
- emitc.func @"operator()"() {
+ emitc.func @bar() {
return
}
}
// CHECK-NOT: emitc.include
// CHECK-LABEL: emitc.class @fooNoFields {
-// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: emitc.func @bar() {
// CHECK-NEXT: return
// CHECK-NEXT: }
// CHECK-NEXT: }
@@ -119,7 +119,7 @@ emitc.class @fooNoOperator {
emitc.class @fooNonStringAttr {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
- emitc.func @"operator()"() {
+ emitc.func @bar() {
return
}
}
@@ -127,7 +127,7 @@ emitc.class @fooNonStringAttr {
// CHECK-NOT: emitc.include
// CHECK-LABEL: emitc.class @fooNonStringAttr {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
-// CHECK-NEXT: emitc.func @"operator()"() {
+// CHECK-NEXT: emitc.func @bar() {
// CHECK-NEXT: return
// CHECK-NEXT: }
// CHECK-NEXT: }
diff --git a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
index d0eab613672a7..8aca1583f6313 100644
--- a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
@@ -5,7 +5,7 @@
emitc.class @foo {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
- emitc.func @"operator()"() {
+ emitc.func @bar() {
%0 = get_field @fieldName0 : !emitc.array<1xf32>
return
}
@@ -23,7 +23,7 @@ emitc.class @foo {
// CHECK-NEXT: char* [[VAR:v[0-9]+]] = reflectionMap.at([[ARG]]);
// CHECK-NEXT: return [[VAR]];
// CHECK-NEXT: }
-// CHECK-NEXT: void operator()() {
+// CHECK-NEXT: void bar() {
// CHECK-NEXT: return;
// CHECK-NEXT: }
// CHECK-NEXT: };
@@ -35,7 +35,7 @@ emitc.class @foo {
emitc.class @fooExcluded {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
- emitc.func @"operator()"() {
+ emitc.func @bar() {
%0 = get_field @fieldName0 : !emitc.array<1xf32>
return
}
@@ -52,7 +52,7 @@ emitc.class @fooExcluded {
// CHECK-NEXT: char* [[VAL_2:v[0-9]+]] = reflectionMap.at([[VAL_1]]);
// CHECK-NEXT: return [[VAL_2]];
// CHECK-NEXT: }
-// CHECK-NEXT: void operator()() {
+// CHECK-NEXT: void bar() {
// CHECK-NEXT: return;
// CHECK-NEXT: }
// CHECK-NEXT: };
@@ -63,7 +63,7 @@ emitc.class @fooExcluded {
emitc.class @fooNoAttrs {
emitc.field @fieldName0 : !emitc.array<1xf32>
- emitc.func @"operator()"() {
+ emitc.func @bar() {
return
}
}
@@ -73,7 +73,7 @@ emitc.class @fooNoAttrs {
// CHECK: class fooNoAttrs {
// CHECK-NEXT: public:
// CHECK-NEXT: float fieldName0[1];
-// CHECK-NEXT: void operator()() {
+// CHECK-NEXT: void bar() {
// CHECK-NEXT: return;
// CHECK-NEXT: }
// CHECK-NEXT: };
>From 835b1bebfe57f56923b339f64b7cb84e0c927066 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Tue, 14 Jul 2026 18:26:20 -0700
Subject: [PATCH 13/22] Switched pass input for field attr name to list to
match the exclude list.
---
.../mlir/Dialect/EmitC/Transforms/Passes.td | 5 +--
.../Dialect/EmitC/Transforms/Transforms.h | 2 +-
.../EmitC/Transforms/MLGOAddReflectionMap.cpp | 41 +++++++++++--------
.../EmitC/mlgo-add-reflection-map.mlir | 29 ++++++++++++-
.../Target/Cpp/mlgo-add-reflection-map.mlir | 2 +-
5 files changed, 56 insertions(+), 23 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 1e245e00c9b2d..328852a369e28 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -96,9 +96,8 @@ def MLGOAddReflectionMapPass : Pass<"mlgo-add-reflection-map", "ModuleOp"> {
}];
let dependentDialects = ["emitc::EmitCDialect"];
let options = [
- Option<"fieldAttrName", "field-attr-name", "std::string",
- /*default=*/"",
- "Attribute key used to extract field names from a field's "
+ ListOption<"includedFieldAttrs", "included-field-attrs", "std::string",
+ "Attribute keys used to extract field names from a field's "
"attribute dictionary">,
ListOption<"excludedFieldAttrs", "excluded-field-attrs", "std::string",
"Attribute keys that, if present on a field, exclude it "
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index 6425614e1fbcd..b3899a2b070d9 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -43,7 +43,7 @@ void populateWrapFuncInClass(
//===----------------------------------------------------------------------===//
void populateMLGOAddReflectionMapPatterns(
- RewritePatternSet &patterns, StringRef fieldAttrName,
+ RewritePatternSet &patterns, ArrayRef<std::string> includedFieldAttrs,
ArrayRef<std::string> excludedFieldAttrs);
} // namespace emitc
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index 12b173074e5bb..afd1bf5b9b56b 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -15,6 +15,7 @@
#include "mlir/IR/PatternMatch.h"
#include "mlir/Transforms/WalkPatternRewriteDriver.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/FormatVariadic.h"
using namespace mlir;
@@ -51,7 +52,7 @@ class MLGOAddReflectionMapPass
mlir::ModuleOp moduleOp = getOperation();
RewritePatternSet patterns(&getContext());
- populateMLGOAddReflectionMapPatterns(patterns, fieldAttrName,
+ populateMLGOAddReflectionMapPatterns(patterns, includedFieldAttrs,
excludedFieldAttrs);
PatternMatchListener listener;
@@ -93,9 +94,11 @@ class MLGOAddReflectionMapPass
class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
public:
- MLGOAddReflectionMapClass(MLIRContext *context, StringRef attrName,
+ MLGOAddReflectionMapClass(MLIRContext *context,
+ llvm::ArrayRef<std::string> includedFieldAttrs,
llvm::ArrayRef<std::string> excludedFieldAttrs)
- : OpRewritePattern<ClassOp>(context), fieldAttrName(attrName),
+ : OpRewritePattern<ClassOp>(context),
+ includedFieldAttrs(includedFieldAttrs),
excludedFieldAttrs(excludedFieldAttrs.begin(),
excludedFieldAttrs.end()) {}
@@ -110,13 +113,16 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
std::vector<std::pair<StringRef, StringRef>> fieldNames;
bool hasError = false;
classOp.walk([&](FieldOp fieldOp) {
- if (auto arrayAttr =
- dyn_cast_if_present<ArrayAttr>(fieldOp->getAttr(fieldAttrName))) {
- if (!arrayAttr.empty()) {
- if (auto stringAttr = dyn_cast<StringAttr>(arrayAttr[0])) {
- fieldNames.emplace_back(stringAttr.getValue(), fieldOp.getName());
- return;
- }
+ for (const auto &attr : includedFieldAttrs) {
+ auto arrayAttr = dyn_cast_if_present<ArrayAttr>(fieldOp->getAttr(attr));
+
+ if (!arrayAttr)
+ continue;
+
+ if (!arrayAttr.empty() && isa<StringAttr>(arrayAttr[0])) {
+ fieldNames.emplace_back(cast<StringAttr>(arrayAttr[0]).getValue(),
+ fieldOp.getName());
+ return;
}
}
@@ -129,7 +135,7 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
(void)rewriter.notifyMatchFailure(fieldOp, [&](Diagnostic &diag) {
diag << "FieldOp must have a dictionary attribute named '"
- << fieldAttrName << "' "
+ << includedFieldAttrs << "' "
<< "with an array containing a string attribute";
});
hasError = true;
@@ -199,9 +205,10 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
}
private:
- /// The name of the attribute on FieldOps that contains the field name
- /// metadata for the reflection map.
- StringRef fieldAttrName;
+ /// The names of the attributes on FieldOps that contain the field name
+ /// metadata for the reflection map. The pass matches the first attribute
+ /// present in the order they are specified in this list.
+ llvm::SmallVector<std::string> includedFieldAttrs;
/// Attributes that, if present on a field, exclude it from the
/// reflection map.
@@ -209,8 +216,8 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
};
void mlir::emitc::populateMLGOAddReflectionMapPatterns(
- RewritePatternSet &patterns, StringRef fieldAttrName,
+ RewritePatternSet &patterns, llvm::ArrayRef<std::string> includedFieldAttrs,
llvm::ArrayRef<std::string> excludedFieldAttrs) {
- patterns.add<MLGOAddReflectionMapClass>(patterns.getContext(), fieldAttrName,
- excludedFieldAttrs);
+ patterns.add<MLGOAddReflectionMapClass>(
+ patterns.getContext(), includedFieldAttrs, excludedFieldAttrs);
}
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index d18c1583cfd38..ff5f44a1ac959 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="field-attr-name=emitc.field_ref \
+// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="included-field-attrs=emitc.field_ref,emitc.field_ref_2 \
// RUN: excluded-field-attrs="emitc.other_field"" %s | FileCheck %s '-D$QUOTE=\22'
/// Tests that a reflection map is created for fields with a certain attribute.
@@ -131,3 +131,30 @@ emitc.class @fooNonStringAttr {
// CHECK-NEXT: return
// CHECK-NEXT: }
// CHECK-NEXT: }
+
+// -----
+
+/// Test that the pass matches one of the multiple included attributes.
+
+emitc.class @fooMultipleAttrs {
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref_2 = ["another_feature"]}
+ emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
+ emitc.func @bar() {
+ return
+ }
+}
+
+// CHECK-LABEL: emitc.class @fooMultipleAttrs {
+// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref_2 = ["another_feature"]}
+// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
+// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
+// CHECK-SAME: #emitc.opaque<"{ { [[$QUOTE]]another_feature[[$QUOTE]], reinterpret_cast<char*>(&fieldName0) }, { [[$QUOTE]]some_feature[[$QUOTE]], reinterpret_cast<char*>(&fieldName1) } }">
+// CHECK-NEXT: emitc.func @getBufferForName(%{{.*}}: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
+// CHECK-NEXT: %[[MAP:.*]] = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
+// CHECK-NEXT: %[[VAL:.*]] = member_call_opaque %[[MAP]] "at"({{.*}}) : !emitc.opaque<"const std::map<std::string, char*>">, (!emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">>
+// CHECK-NEXT: return %[[VAL]] : !emitc.ptr<!emitc.opaque<"char">>
+// CHECK-NEXT: }
+// CHECK-NEXT: emitc.func @bar() {
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+// CHECK-NEXT: }
diff --git a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
index 8aca1583f6313..41329771562da 100644
--- a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="field-attr-name=emitc.field_ref excluded-field-attrs=emitc.other_field" %s | mlir-translate -mlir-to-cpp | FileCheck %s
+// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="included-field-attrs=emitc.field_ref excluded-field-attrs=emitc.other_field" %s | mlir-translate -mlir-to-cpp | FileCheck %s
/// Test that a reflection map and lookup function are generated in the class.
>From 35c5a45e4714fe5bba82a1cdac20a62078acd414 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Wed, 15 Jul 2026 12:09:54 -0700
Subject: [PATCH 14/22] Add comments
---
.../EmitC/Transforms/MLGOAddReflectionMap.cpp | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index afd1bf5b9b56b..881da3f4aee87 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -58,9 +58,12 @@ class MLGOAddReflectionMapPass
PatternMatchListener listener;
walkAndApplyPatterns(moduleOp, std::move(patterns), &listener);
+ // If nothing was matched, no reflection maps were added, removing the need
+ // to add include headers for map and string
if (!listener.patternApplied)
return;
+ // Check if the map and/or string headers are already present
bool hasMapHdr = false;
bool hasStringHdr = false;
for (auto &op : *moduleOp.getBody()) {
@@ -109,7 +112,9 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
emitc::OpaqueType mapType = mlir::emitc::OpaqueType::get(
context, "const std::map<std::string, char*>");
- // Collect all field names
+ // Collect the names of all FieldOps that have one of the attributes in
+ // includedFieldAttrs to use the first element of the array attribute
+ // as the reflection map key
std::vector<std::pair<StringRef, StringRef>> fieldNames;
bool hasError = false;
classOp.walk([&](FieldOp fieldOp) {
@@ -126,6 +131,8 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
}
}
+ // If one of the attributes in excludedFieldAttrs is present,
+ // don't add this FieldOp
bool shouldIgnore =
llvm::any_of(excludedFieldAttrs, [&fieldOp](StringRef ignoreAttr) {
return fieldOp->hasAttr(ignoreAttr);
@@ -144,6 +151,7 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
if (hasError || fieldNames.empty())
return failure();
+ // Create reflection map contents
std::string reflectionMapContents;
reflectionMapContents += "{ ";
bool first = true;
@@ -157,6 +165,8 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
}
reflectionMapContents += " }";
+ // Set insertion point before the first function or after all fields
+ // if there are no functions within the class
auto funcs = classOp.getBlock().getOps<FuncOp>();
auto it = funcs.begin();
if (it != funcs.end())
>From e8af11de937a1571b252c3d85b9bf8894eaa9209 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Wed, 15 Jul 2026 12:28:08 -0700
Subject: [PATCH 15/22] Update test comments
---
mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir | 8 ++++----
mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index ff5f44a1ac959..f46bf4528048f 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -58,7 +58,7 @@ emitc.class @fooExcluded {
// -----
-/// Test that the pass leaves IR unchanged if fields don't have any attributes (match failure)
+/// Test that the pass bails out and leaves IR unchanged if fields don't have any attributes
emitc.class @fooNoAttrs {
emitc.field @fieldName0 : !emitc.array<1xf32>
@@ -77,7 +77,7 @@ emitc.class @fooNoAttrs {
// -----
-/// Test that the pass leaves IR unchanged if the ClassOp doesn't have any fields (match failure)
+/// Test that the pass bails out and leaves IR unchanged if the ClassOp doesn't have any fields
emitc.class @fooNoFields {
emitc.func @bar() {
@@ -114,8 +114,8 @@ emitc.class @fooNoOperator {
// -----
-/// Test that the pass returns with a match failure if a FieldOp has the specified
-/// dictionary attribute with an array containing a type other than string
+/// Test that the pass bails out if a FieldOp has the specified dictionary attribute
+/// with an array containing a type other than string
emitc.class @fooNonStringAttr {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
diff --git a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
index 41329771562da..e4c851b2889ff 100644
--- a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
@@ -59,7 +59,7 @@ emitc.class @fooExcluded {
// -----
-/// Test that translation doesn't add headers if the class does not match the pass.
+/// Test that headers aren't added if the pass bails out.
emitc.class @fooNoAttrs {
emitc.field @fieldName0 : !emitc.array<1xf32>
>From 1e7499d5aad7c38d328740c96b6b530129ca2823 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Wed, 15 Jul 2026 12:37:17 -0700
Subject: [PATCH 16/22] Document OpRewritePattern
---
.../EmitC/Transforms/MLGOAddReflectionMap.cpp | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index 881da3f4aee87..23ade8ded7568 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -95,6 +95,36 @@ class MLGOAddReflectionMapPass
} // namespace emitc
} // namespace mlir
+/// Rewrites a `emitc::ClassOp` to generate a reflection map of member fields
+/// and a lookup method.
+///
+/// Fields to be mapped are identified via `includedFieldAttrs` attributes (e.g.,
+/// `emitc.field_ref`). Fields containing `excludedFieldAttrs` are skipped. All
+/// other fields must match one of the inclusion/exclusion filters, otherwise the
+/// pattern fails.
+///
+/// Before:
+/// ```mlir
+/// emitc.class @foo {
+/// emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+/// emitc.func @bar() { return }
+/// }
+/// ```
+///
+/// After:
+/// ```mlir
+/// emitc.class @foo {
+/// emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+/// emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
+/// #emitc.opaque<"{ { \"another_feature\", reinterpret_cast<char*>(&fieldName0) } }">
+/// emitc.func @getBufferForName(%name: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
+/// %map = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
+/// %ptr = member_call_opaque %map "at"(%name) : ...
+/// return %ptr : !emitc.ptr<!emitc.opaque<"char">>
+/// }
+/// emitc.func @bar() { return }
+/// }
+/// ```
class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
public:
MLGOAddReflectionMapClass(MLIRContext *context,
>From 654a2088ef8503ac1df77eb98381c688dac7818e Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Wed, 15 Jul 2026 12:39:59 -0700
Subject: [PATCH 17/22] Formatting
---
.../EmitC/Transforms/MLGOAddReflectionMap.cpp | 29 ++++++++++---------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index 23ade8ded7568..adc6bcbe7f8be 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -98,29 +98,32 @@ class MLGOAddReflectionMapPass
/// Rewrites a `emitc::ClassOp` to generate a reflection map of member fields
/// and a lookup method.
///
-/// Fields to be mapped are identified via `includedFieldAttrs` attributes (e.g.,
-/// `emitc.field_ref`). Fields containing `excludedFieldAttrs` are skipped. All
-/// other fields must match one of the inclusion/exclusion filters, otherwise the
-/// pattern fails.
+/// Fields to be mapped are identified via `includedFieldAttrs` attributes
+/// (e.g., `emitc.field_ref`). Fields containing `excludedFieldAttrs` are
+/// skipped. All other fields must match one of the inclusion/exclusion filters,
+/// otherwise the pattern fails.
///
/// Before:
/// ```mlir
/// emitc.class @foo {
-/// emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
-/// emitc.func @bar() { return }
+/// emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref =
+/// ["another_feature"]} emitc.func @bar() { return }
/// }
/// ```
///
/// After:
/// ```mlir
/// emitc.class @foo {
-/// emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
-/// emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
-/// #emitc.opaque<"{ { \"another_feature\", reinterpret_cast<char*>(&fieldName0) } }">
-/// emitc.func @getBufferForName(%name: !emitc.opaque<"std::string">) -> !emitc.ptr<!emitc.opaque<"char">> {
-/// %map = get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>">
-/// %ptr = member_call_opaque %map "at"(%name) : ...
-/// return %ptr : !emitc.ptr<!emitc.opaque<"char">>
+/// emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref =
+/// ["another_feature"]} emitc.field @reflectionMap : !emitc.opaque<"const
+/// std::map<std::string, char*>"> =
+/// #emitc.opaque<"{ { \"another_feature\",
+/// reinterpret_cast<char*>(&fieldName0) } }">
+/// emitc.func @getBufferForName(%name: !emitc.opaque<"std::string">) ->
+/// !emitc.ptr<!emitc.opaque<"char">> {
+/// %map = get_field @reflectionMap : !emitc.opaque<"const
+/// std::map<std::string, char*>"> %ptr = member_call_opaque %map
+/// "at"(%name) : ... return %ptr : !emitc.ptr<!emitc.opaque<"char">>
/// }
/// emitc.func @bar() { return }
/// }
>From 3c32f1e81cc3ea002d79d2a02c8c5a3e20f6f735 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Wed, 15 Jul 2026 12:43:52 -0700
Subject: [PATCH 18/22] Update pass documentation
---
mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 328852a369e28..5c0dfe7bf69c1 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -67,10 +67,10 @@ def MLGOAddReflectionMapPass : Pass<"mlgo-add-reflection-map", "ModuleOp"> {
EmitC classes, enabling runtime lookup of class fields by name.
This requires that the class has fields with attributes.
Each `emitc.field` is expected to have an attribute (configured by the
- `field-attr-name` option) that is an array containing a single string
- attribute. If a field possesses both the attribute specified by
- `field-attr-name` and an attribute specified in `excluded-field-attrs`,
- the `field-attr-name` attribute takes precedence and the field will be
+ `included-field-attrs` option) that is an array containing a single string
+ attribute. If a field possesses both an attribute specified by
+ `included-field-attrs` and an attribute specified in `excluded-field-attrs`,
+ the `included-field-attrs` attribute takes precedence and the field will be
included in the reflection map.
Example:
>From e63ba3ad0ee3b589f979e6fc06a5edb8e5f6e91c Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Thu, 16 Jul 2026 12:23:18 -0700
Subject: [PATCH 19/22] Update tests to follow naming guidelines
---
.../EmitC/mlgo-add-reflection-map.mlir | 24 +++++++++----------
.../Target/Cpp/mlgo-add-reflection-map.mlir | 8 +++----
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index f46bf4528048f..1d5ecc61b118b 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -31,7 +31,7 @@ emitc.class @foo {
/// Test that a reflection map is created for fields with a certain named attribute
/// but not ones with an attribute present in the ignore-attributes option.
-emitc.class @fooExcluded {
+emitc.class @foo_excluded_attrs {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
emitc.func @bar() {
@@ -40,7 +40,7 @@ emitc.class @fooExcluded {
}
}
-// CHECK: emitc.class @fooExcluded {
+// CHECK: emitc.class @foo_excluded_attrs {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
@@ -60,7 +60,7 @@ emitc.class @fooExcluded {
/// Test that the pass bails out and leaves IR unchanged if fields don't have any attributes
-emitc.class @fooNoAttrs {
+emitc.class @negative_foo_no_attrs {
emitc.field @fieldName0 : !emitc.array<1xf32>
emitc.func @bar() {
return
@@ -68,7 +68,7 @@ emitc.class @fooNoAttrs {
}
// CHECK-NOT: emitc.include
-// CHECK-LABEL: emitc.class @fooNoAttrs {
+// CHECK-LABEL: emitc.class @negative_foo_no_attrs {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32>
// CHECK-NEXT: emitc.func @bar() {
// CHECK-NEXT: return
@@ -79,14 +79,14 @@ emitc.class @fooNoAttrs {
/// Test that the pass bails out and leaves IR unchanged if the ClassOp doesn't have any fields
-emitc.class @fooNoFields {
+emitc.class @negative_foo_no_fields {
emitc.func @bar() {
return
}
}
// CHECK-NOT: emitc.include
-// CHECK-LABEL: emitc.class @fooNoFields {
+// CHECK-LABEL: emitc.class @negative_foo_no_fields {
// CHECK-NEXT: emitc.func @bar() {
// CHECK-NEXT: return
// CHECK-NEXT: }
@@ -97,11 +97,11 @@ emitc.class @fooNoFields {
/// Test that a reflection map is still created in the case that there are no
/// functions in the class
-emitc.class @fooNoOperator {
+emitc.class @foo_no_operator {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
}
-// CHECK-LABEL: emitc.class @fooNoOperator {
+// CHECK-LABEL: emitc.class @foo_no_operator {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
// CHECK-SAME: #emitc.opaque<"{ { [[$QUOTE]]another_feature[[$QUOTE]], reinterpret_cast<char*>(&fieldName0) } }">
@@ -117,7 +117,7 @@ emitc.class @fooNoOperator {
/// Test that the pass bails out if a FieldOp has the specified dictionary attribute
/// with an array containing a type other than string
-emitc.class @fooNonStringAttr {
+emitc.class @negative_foo_non_string_attr {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
emitc.func @bar() {
return
@@ -125,7 +125,7 @@ emitc.class @fooNonStringAttr {
}
// CHECK-NOT: emitc.include
-// CHECK-LABEL: emitc.class @fooNonStringAttr {
+// CHECK-LABEL: emitc.class @negative_foo_non_string_attr {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = [1]}
// CHECK-NEXT: emitc.func @bar() {
// CHECK-NEXT: return
@@ -136,7 +136,7 @@ emitc.class @fooNonStringAttr {
/// Test that the pass matches one of the multiple included attributes.
-emitc.class @fooMultipleAttrs {
+emitc.class @foo_multiple_attrs {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref_2 = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
emitc.func @bar() {
@@ -144,7 +144,7 @@ emitc.class @fooMultipleAttrs {
}
}
-// CHECK-LABEL: emitc.class @fooMultipleAttrs {
+// CHECK-LABEL: emitc.class @foo_multiple_attrs {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref_2 = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
diff --git a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
index e4c851b2889ff..6a047851767d7 100644
--- a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
@@ -32,7 +32,7 @@ emitc.class @foo {
/// Test that fields with excluded attributes are ignored.
-emitc.class @fooExcluded {
+emitc.class @foo_excluded_attrs {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
emitc.func @bar() {
@@ -43,7 +43,7 @@ emitc.class @fooExcluded {
// CHECK: #include <map>
// CHECK-NEXT: #include <string>
-// CHECK-NEXT: class fooExcluded {
+// CHECK-NEXT: class foo_excluded_attrs {
// CHECK-NEXT: public:
// CHECK-NEXT: float fieldName0[1];
// CHECK-NEXT: float fieldName1[1];
@@ -61,7 +61,7 @@ emitc.class @fooExcluded {
/// Test that headers aren't added if the pass bails out.
-emitc.class @fooNoAttrs {
+emitc.class @negative_foo_no_attrs {
emitc.field @fieldName0 : !emitc.array<1xf32>
emitc.func @bar() {
return
@@ -70,7 +70,7 @@ emitc.class @fooNoAttrs {
// CHECK-NOT: #include <map>
// CHECK-NOT: #include <string>
-// CHECK: class fooNoAttrs {
+// CHECK: class negative_foo_no_attrs {
// CHECK-NEXT: public:
// CHECK-NEXT: float fieldName0[1];
// CHECK-NEXT: void bar() {
>From 595ac3a6b1f9f47e4a53e286f8834fcdc27304c5 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Thu, 16 Jul 2026 14:28:43 -0700
Subject: [PATCH 20/22] Remove excluded-field-attrs option
---
.../mlir/Dialect/EmitC/Transforms/Passes.td | 17 +++-----
.../Dialect/EmitC/Transforms/Transforms.h | 3 +-
.../EmitC/Transforms/MLGOAddReflectionMap.cpp | 43 ++++---------------
.../EmitC/mlgo-add-reflection-map.mlir | 35 ++++++++++++---
.../Target/Cpp/mlgo-add-reflection-map.mlir | 8 ++--
5 files changed, 47 insertions(+), 59 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 5c0dfe7bf69c1..bf526db3a3af1 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -63,15 +63,13 @@ def WrapFuncInClassPass : Pass<"wrap-emitc-func-in-class", "ModuleOp"> {
def MLGOAddReflectionMapPass : Pass<"mlgo-add-reflection-map", "ModuleOp"> {
let summary = "Add a reflection map and a helper method to EmitC classes for runtime field lookup.";
let description = [{
- This pass adds a `reflectionMap` field and an accompanying `getBufferForName` method to
- EmitC classes, enabling runtime lookup of class fields by name.
- This requires that the class has fields with attributes.
+ This pass adds a `reflectionMap` field and an accompanying `getBufferForName`
+ method to EmitC classes, enabling runtime lookup of class fields by name.
Each `emitc.field` is expected to have an attribute (configured by the
`included-field-attrs` option) that is an array containing a single string
- attribute. If a field possesses both an attribute specified by
- `included-field-attrs` and an attribute specified in `excluded-field-attrs`,
- the `included-field-attrs` attribute takes precedence and the field will be
- included in the reflection map.
+ attribute which will be used as the reflection map key. If none of the specified
+ attributes are present on the field, the field will be excluded from the
+ reflection map.
Example:
@@ -98,10 +96,7 @@ def MLGOAddReflectionMapPass : Pass<"mlgo-add-reflection-map", "ModuleOp"> {
let options = [
ListOption<"includedFieldAttrs", "included-field-attrs", "std::string",
"Attribute keys used to extract field names from a field's "
- "attribute dictionary">,
- ListOption<"excludedFieldAttrs", "excluded-field-attrs", "std::string",
- "Attribute keys that, if present on a field, exclude it "
- "from the reflection map">
+ "attribute dictionary">
];
}
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index b3899a2b070d9..795ea58b6b558 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -43,8 +43,7 @@ void populateWrapFuncInClass(
//===----------------------------------------------------------------------===//
void populateMLGOAddReflectionMapPatterns(
- RewritePatternSet &patterns, ArrayRef<std::string> includedFieldAttrs,
- ArrayRef<std::string> excludedFieldAttrs);
+ RewritePatternSet &patterns, ArrayRef<std::string> includedFieldAttrs);
} // namespace emitc
} // namespace mlir
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index adc6bcbe7f8be..d176515018521 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -52,8 +52,7 @@ class MLGOAddReflectionMapPass
mlir::ModuleOp moduleOp = getOperation();
RewritePatternSet patterns(&getContext());
- populateMLGOAddReflectionMapPatterns(patterns, includedFieldAttrs,
- excludedFieldAttrs);
+ populateMLGOAddReflectionMapPatterns(patterns, includedFieldAttrs);
PatternMatchListener listener;
walkAndApplyPatterns(moduleOp, std::move(patterns), &listener);
@@ -99,9 +98,8 @@ class MLGOAddReflectionMapPass
/// and a lookup method.
///
/// Fields to be mapped are identified via `includedFieldAttrs` attributes
-/// (e.g., `emitc.field_ref`). Fields containing `excludedFieldAttrs` are
-/// skipped. All other fields must match one of the inclusion/exclusion filters,
-/// otherwise the pattern fails.
+/// (e.g., `emitc.field_ref`). Fields that do not have a matching attribute
+/// are omitted from the reflection map.
///
/// Before:
/// ```mlir
@@ -131,12 +129,9 @@ class MLGOAddReflectionMapPass
class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
public:
MLGOAddReflectionMapClass(MLIRContext *context,
- llvm::ArrayRef<std::string> includedFieldAttrs,
- llvm::ArrayRef<std::string> excludedFieldAttrs)
+ llvm::ArrayRef<std::string> includedFieldAttrs)
: OpRewritePattern<ClassOp>(context),
- includedFieldAttrs(includedFieldAttrs),
- excludedFieldAttrs(excludedFieldAttrs.begin(),
- excludedFieldAttrs.end()) {}
+ includedFieldAttrs(includedFieldAttrs) {}
LogicalResult matchAndRewrite(ClassOp classOp,
PatternRewriter &rewriter) const override {
@@ -149,7 +144,6 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
// includedFieldAttrs to use the first element of the array attribute
// as the reflection map key
std::vector<std::pair<StringRef, StringRef>> fieldNames;
- bool hasError = false;
classOp.walk([&](FieldOp fieldOp) {
for (const auto &attr : includedFieldAttrs) {
auto arrayAttr = dyn_cast_if_present<ArrayAttr>(fieldOp->getAttr(attr));
@@ -163,25 +157,9 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
return;
}
}
-
- // If one of the attributes in excludedFieldAttrs is present,
- // don't add this FieldOp
- bool shouldIgnore =
- llvm::any_of(excludedFieldAttrs, [&fieldOp](StringRef ignoreAttr) {
- return fieldOp->hasAttr(ignoreAttr);
- });
- if (shouldIgnore)
- return;
-
- (void)rewriter.notifyMatchFailure(fieldOp, [&](Diagnostic &diag) {
- diag << "FieldOp must have a dictionary attribute named '"
- << includedFieldAttrs << "' "
- << "with an array containing a string attribute";
- });
- hasError = true;
});
- if (hasError || fieldNames.empty())
+ if (fieldNames.empty())
return failure();
// Create reflection map contents
@@ -252,15 +230,10 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
/// metadata for the reflection map. The pass matches the first attribute
/// present in the order they are specified in this list.
llvm::SmallVector<std::string> includedFieldAttrs;
-
- /// Attributes that, if present on a field, exclude it from the
- /// reflection map.
- llvm::SmallVector<std::string> excludedFieldAttrs;
};
void mlir::emitc::populateMLGOAddReflectionMapPatterns(
- RewritePatternSet &patterns, llvm::ArrayRef<std::string> includedFieldAttrs,
- llvm::ArrayRef<std::string> excludedFieldAttrs) {
+ RewritePatternSet &patterns, llvm::ArrayRef<std::string> includedFieldAttrs) {
patterns.add<MLGOAddReflectionMapClass>(
- patterns.getContext(), includedFieldAttrs, excludedFieldAttrs);
+ patterns.getContext(), includedFieldAttrs);
}
diff --git a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
index 1d5ecc61b118b..5fe768953f050 100644
--- a/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Dialect/EmitC/mlgo-add-reflection-map.mlir
@@ -1,7 +1,7 @@
-// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="included-field-attrs=emitc.field_ref,emitc.field_ref_2 \
-// RUN: excluded-field-attrs="emitc.other_field"" %s | FileCheck %s '-D$QUOTE=\22'
+// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="included-field-attrs=emitc.field_ref,emitc.field_ref_2" %s | FileCheck %s '-D$QUOTE=\22'
-/// Tests that a reflection map is created for fields with a certain attribute.
+/// Tests that a reflection map is created for fields with an attribute in the
+/// included-field-attrs option.
emitc.class @foo {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
@@ -28,10 +28,10 @@ emitc.class @foo {
// -----
-/// Test that a reflection map is created for fields with a certain named attribute
-/// but not ones with an attribute present in the ignore-attributes option.
+/// Tests that a reflection map is created for fields in the included-field-attrs
+/// option and skips fields without matching attributes.
-emitc.class @foo_excluded_attrs {
+emitc.class @foo_mixed_attrs {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
emitc.func @bar() {
@@ -40,7 +40,7 @@ emitc.class @foo_excluded_attrs {
}
}
-// CHECK: emitc.class @foo_excluded_attrs {
+// CHECK: emitc.class @foo_mixed_attrs {
// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
// CHECK-NEXT: emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, char*>"> =
@@ -58,6 +58,27 @@ emitc.class @foo_excluded_attrs {
// -----
+/// Test that the pass bails out and leaves IR unchanged if no fields contain
+/// any of the attributes specified in included-field-attrs
+
+emitc.class @foo_unsupported_attr {
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.other_field = ["another_feature"]}
+ emitc.func @bar() {
+ %0 = get_field @fieldName0 : !emitc.array<1xf32>
+ return
+ }
+}
+
+// CHECK: emitc.class @foo_unsupported_attr {
+// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.other_field = ["another_feature"]}
+// CHECK-NEXT: emitc.func @bar() {
+// CHECK-NEXT: %{{.*}} = get_field @fieldName0 : !emitc.array<1xf32>
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+// -----
+
/// Test that the pass bails out and leaves IR unchanged if fields don't have any attributes
emitc.class @negative_foo_no_attrs {
diff --git a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
index 6a047851767d7..489b6af79fe2f 100644
--- a/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
+++ b/mlir/test/Target/Cpp/mlgo-add-reflection-map.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="included-field-attrs=emitc.field_ref excluded-field-attrs=emitc.other_field" %s | mlir-translate -mlir-to-cpp | FileCheck %s
+// RUN: mlir-opt -split-input-file --mlgo-add-reflection-map="included-field-attrs=emitc.field_ref" %s | mlir-translate -mlir-to-cpp | FileCheck %s
/// Test that a reflection map and lookup function are generated in the class.
@@ -30,9 +30,9 @@ emitc.class @foo {
// -----
-/// Test that fields with excluded attributes are ignored.
+/// Test that fields without included attributes are ignored.
-emitc.class @foo_excluded_attrs {
+emitc.class @foo_unsupported_attrs {
emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.other_field = ["some_feature"]}
emitc.func @bar() {
@@ -43,7 +43,7 @@ emitc.class @foo_excluded_attrs {
// CHECK: #include <map>
// CHECK-NEXT: #include <string>
-// CHECK-NEXT: class foo_excluded_attrs {
+// CHECK-NEXT: class foo_unsupported_attrs {
// CHECK-NEXT: public:
// CHECK-NEXT: float fieldName0[1];
// CHECK-NEXT: float fieldName1[1];
>From ced3fee1253bfae065d04d545f50624c6a5fdda5 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Thu, 16 Jul 2026 14:44:59 -0700
Subject: [PATCH 21/22] Formatting
---
mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index d176515018521..02a6d2d58255b 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -233,7 +233,8 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
};
void mlir::emitc::populateMLGOAddReflectionMapPatterns(
- RewritePatternSet &patterns, llvm::ArrayRef<std::string> includedFieldAttrs) {
- patterns.add<MLGOAddReflectionMapClass>(
- patterns.getContext(), includedFieldAttrs);
+ RewritePatternSet &patterns,
+ llvm::ArrayRef<std::string> includedFieldAttrs) {
+ patterns.add<MLGOAddReflectionMapClass>(patterns.getContext(),
+ includedFieldAttrs);
}
>From fcfa12876f117d186fbfe8ffe97b686741b765f6 Mon Sep 17 00:00:00 2001
From: Bhavesh Mandalapu <bmandalapu at google.com>
Date: Sun, 19 Jul 2026 14:27:19 -0700
Subject: [PATCH 22/22] Fix comment formatting
---
mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index 02a6d2d58255b..6900e0d989b92 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -105,7 +105,8 @@ class MLGOAddReflectionMapPass
/// ```mlir
/// emitc.class @foo {
/// emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref =
-/// ["another_feature"]} emitc.func @bar() { return }
+/// ["another_feature"]}
+/// emitc.func @bar() { return }
/// }
/// ```
///
More information about the Mlir-commits
mailing list