[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