[Mlir-commits] [mlir] [mlir][EmitC]Add a Reflection Map to a Class (PR #150572)

Jaden Angella llvmlistbot at llvm.org
Tue Jul 29 14:08:23 PDT 2025


https://github.com/Jaddyen updated https://github.com/llvm/llvm-project/pull/150572

>From 78f669ab757b8f59a9240e510f500e23465b2df0 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Fri, 18 Jul 2025 20:35:29 +0000
Subject: [PATCH 01/12] Modeling

---
 .../mlir/Dialect/EmitC/Transforms/Passes.h    |   1 +
 .../mlir/Dialect/EmitC/Transforms/Passes.td   |  35 ++++
 .../EmitC/Transforms/AddReflectionMap.cpp     | 161 ++++++++++++++++++
 .../Dialect/EmitC/Transforms/CMakeLists.txt   |   1 +
 4 files changed, 198 insertions(+)
 create mode 100644 mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp

diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h
index 1af4aa06fa811..259d6c24cd5fc 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.h
@@ -16,6 +16,7 @@ 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 1893c101e735b..031b80362718e 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -53,4 +53,39 @@ def WrapFuncInClassPass : Pass<"wrap-emitc-func-in-class"> {
   let dependentDialects = ["emitc::EmitCDialect"];
 }
 
+def AddReflectionMapPass : Pass<"add-reflection-map"> {
+  let summary =
+      "Add a reflection map function to EmitC classes for runtime field lookup";
+  let description = [{
+        This pass adds a `getBufferForName` function to EmitC classes that enables 
+        runtime lookup of field buffers by their string names. 
+        This enables runtime introspection and dynamic access to class fields by name,
+        which is useful for interfacing with external systems that need to access 
+        tensors/buffers by their semantic names.
+
+        Example transformation:
+        ```mlir
+        emitc.class @MyClass {
+          emitc.field @fieldName0 : !emitc.array<1xf32> {tf_saved_model.index_path = ["another_feature"]}
+          emitc.field @fieldName1 : !emitc.array<1xf32> {tf_saved_model.index_path = ["some_feature"]}
+          emitc.func @execute() { ... }
+        }
+        ```
+
+        Becomes:
+        ```mlir
+        emitc.class @MyClass {
+          emitc.field @fieldName0 : !emitc.array<1xf32> {tf_saved_model.index_path = ["another_feature"]}
+          emitc.field @fieldName1 : !emitc.array<1xf32> {tf_saved_model.index_path = ["some_feature"]}
+          emitc.func @getBufferForName(%name : !emitc.opaque<"std::string_view">) -> !emitc.opaque<"char*"> {
+            %map = "emitc.constant"(){value = #emitc.opaque<"{"another_feature", reinterpret_cast<char*>(&another_feature)}, {"some_feature", reinterpret_cast<char*>(&some_feature)}">} : () -> !emitc.opaque<"std::map<std::string, char*>">
+            return %null : !emitc.opaque<"char*">
+          }
+          emitc.func @execute() { ... }
+        }
+        ```
+    }];
+  let dependentDialects = ["mlir::emitc::EmitCDialect"];
+}
+
 #endif // MLIR_DIALECT_EMITC_TRANSFORMS_PASSES
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
new file mode 100644
index 0000000000000..c74b03aefcefe
--- /dev/null
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -0,0 +1,161 @@
+/* Copyright 2025 The TensorFlow Authors. All Rights Reserved.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#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"
+
+using namespace mlir;
+using namespace emitc;
+
+namespace mlir {
+namespace emitc {
+#define GEN_PASS_DEF_ADDREFLECTIONMAPPASS
+#include "mlir/Dialect/EmitC/Transforms/Passes.h.inc"
+
+namespace {
+class AddReflectionMapPass
+    : public impl::AddReflectionMapPassBase<AddReflectionMapPass> {
+  using AddReflectionMapPassBase::AddReflectionMapPassBase;
+  void runOnOperation() override {
+    Operation *rootOp = getOperation();
+
+    RewritePatternSet patterns(&getContext());
+    populateAddReflectionMapPatterns(patterns);
+
+    walkAndApplyPatterns(rootOp, std::move(patterns));
+  }
+};
+
+} // namespace
+} // namespace emitc
+} // namespace mlir
+
+class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
+public:
+  AddReflectionMapClass(MLIRContext *context)
+      : OpRewritePattern<emitc::ClassOp>(context) {}
+
+  LogicalResult matchAndRewrite(mlir::emitc::ClassOp classOp,
+                                PatternRewriter &rewriter) const override {
+    mlir::MLIRContext *context = rewriter.getContext();
+    emitc::OpaqueType stringViewType =
+        mlir::emitc::OpaqueType::get(rewriter.getContext(), "std::string_view");
+    emitc::OpaqueType charPtrType =
+        mlir::emitc::OpaqueType::get(rewriter.getContext(), "char");
+    emitc::OpaqueType mapType = mlir::emitc::OpaqueType::get(
+        rewriter.getContext(), "const std::map<std::string, char*>");
+
+    FunctionType funcType =
+        rewriter.getFunctionType({stringViewType}, {charPtrType});
+    emitc::FuncOp executeFunc =
+        classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
+    rewriter.setInsertionPoint(executeFunc);
+
+    emitc::FuncOp getBufferFunc = rewriter.create<mlir::emitc::FuncOp>(
+        classOp.getLoc(), "getBufferForName", funcType);
+
+    Block *funcBody = getBufferFunc.addEntryBlock();
+    rewriter.setInsertionPointToStart(funcBody);
+
+    // Collect all field names
+    SmallVector<std::string> fieldNames;
+    classOp.walk([&](mlir::emitc::FieldOp fieldOp) {
+      if (mlir::Attribute attrsAttr =
+              fieldOp->getAttrDictionary().get("attrs")) {
+        if (DictionaryAttr innerDictAttr =
+                dyn_cast<mlir::DictionaryAttr>(attrsAttr)) {
+          auto indexPathAttr =
+              innerDictAttr.getNamed("tf_saved_model.index_path");
+          ArrayAttr arrayAttr =
+              dyn_cast<mlir::ArrayAttr>(indexPathAttr->getValue());
+          if (!arrayAttr.empty()) {
+            StringAttr stringAttr = dyn_cast<mlir::StringAttr>(arrayAttr[0]);
+            std::string indexPath = stringAttr.getValue().str();
+            fieldNames.push_back(indexPath);
+          }
+          if (arrayAttr.size() > 1) {
+            fieldOp.emitError() << "tf_saved_model.index_path attribute must "
+                                   "contain at most one value, but found "
+                                << arrayAttr.size() << " values.";
+            return;
+          }
+        }
+      }
+    });
+
+    std::string mapInitializer = "{{";
+    for (size_t i = 0; i < fieldNames.size(); ++i) {
+      mapInitializer += "\"" + fieldNames[i] + "\", " +
+                        "reinterpret_cast<char*>(&" + fieldNames[i] + ")",
+          mapInitializer += "}";
+      if (i < fieldNames.size() - 1)
+        mapInitializer += ", {";
+    }
+    mapInitializer += "}";
+
+    auto iteratorType = mlir::emitc::OpaqueType::get(
+        context, "std::map<std::string, char*>::const_iterator");
+    auto boolType = rewriter.getI1Type();
+    // 5. Create the constant map
+    auto bufferMap = rewriter.create<emitc::ConstantOp>(
+        classOp.getLoc(), mapType,
+        emitc::OpaqueAttr::get(context, mapInitializer));
+
+    // 6. Get the function argument
+    mlir::Value nameArg = getBufferFunc.getArgument(0);
+
+    // 7. Create the find call
+    auto it = rewriter.create<emitc::CallOpaqueOp>(
+        classOp.getLoc(), iteratorType, rewriter.getStringAttr("find"),
+        mlir::ValueRange{bufferMap.getResult(), nameArg});
+
+    // 8. Create the end call
+    auto endIt = rewriter.create<emitc::CallOpaqueOp>(
+        classOp.getLoc(), iteratorType, rewriter.getStringAttr("end"),
+        bufferMap.getResult());
+
+    // 9. Create the operator== call
+    auto isEnd = rewriter.create<emitc::CallOpaqueOp>(
+        classOp.getLoc(), boolType,
+        "operator==", mlir::ValueRange{it.getResult(0), endIt.getResult(0)});
+
+    // 10. Create the nullptr constant
+    auto nullPtr = rewriter.create<emitc::ConstantOp>(
+        classOp.getLoc(), charPtrType,
+        emitc::OpaqueAttr::get(context, "nullptr"));
+
+    // 11. Create the second call
+    auto second = rewriter.create<emitc::CallOpaqueOp>(
+        classOp.getLoc(), charPtrType, "second", it.getResult(0));
+
+    // 12. Create the conditional
+    auto result = rewriter.create<emitc::ConditionalOp>(
+        classOp.getLoc(), charPtrType, isEnd.getResult(0), nullPtr.getResult(),
+        second.getResult(0));
+
+    // 13. Create return
+    rewriter.create<emitc::ReturnOp>(classOp.getLoc(), result.getResult());
+
+    return success();
+  }
+};
+
+void mlir::emitc::populateAddReflectionMapPatterns(
+    RewritePatternSet &patterns) {
+  patterns.add<AddReflectionMapClass>(patterns.getContext());
+}
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

>From 9be23970d11a0f2ef3dd0afe1c2049386bf49dd3 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Tue, 22 Jul 2025 22:33:45 +0000
Subject: [PATCH 02/12] Add an argument

---
 .../mlir/Dialect/EmitC/Transforms/Passes.td   | 12 ++-
 .../EmitC/Transforms/AddReflectionMap.cpp     | 91 ++++++++-----------
 .../Dialect/EmitC/add_reflection_map.mlir     | 55 +++++++++++
 3 files changed, 101 insertions(+), 57 deletions(-)
 create mode 100644 mlir/test/Dialect/EmitC/add_reflection_map.mlir

diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 031b80362718e..5facdedbd1b4b 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -66,8 +66,8 @@ def AddReflectionMapPass : Pass<"add-reflection-map"> {
         Example transformation:
         ```mlir
         emitc.class @MyClass {
-          emitc.field @fieldName0 : !emitc.array<1xf32> {tf_saved_model.index_path = ["another_feature"]}
-          emitc.field @fieldName1 : !emitc.array<1xf32> {tf_saved_model.index_path = ["some_feature"]}
+          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() { ... }
         }
         ```
@@ -75,8 +75,8 @@ def AddReflectionMapPass : Pass<"add-reflection-map"> {
         Becomes:
         ```mlir
         emitc.class @MyClass {
-          emitc.field @fieldName0 : !emitc.array<1xf32> {tf_saved_model.index_path = ["another_feature"]}
-          emitc.field @fieldName1 : !emitc.array<1xf32> {tf_saved_model.index_path = ["some_feature"]}
+          emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.field_ref = ["another_feature"]}
+          emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.field_ref = ["some_feature"]}
           emitc.func @getBufferForName(%name : !emitc.opaque<"std::string_view">) -> !emitc.opaque<"char*"> {
             %map = "emitc.constant"(){value = #emitc.opaque<"{"another_feature", reinterpret_cast<char*>(&another_feature)}, {"some_feature", reinterpret_cast<char*>(&some_feature)}">} : () -> !emitc.opaque<"std::map<std::string, char*>">
             return %null : !emitc.opaque<"char*">
@@ -86,6 +86,10 @@ def AddReflectionMapPass : Pass<"add-reflection-map"> {
         ```
     }];
   let dependentDialects = ["mlir::emitc::EmitCDialect"];
+  let options = [Option<"namedAttribute", "named-attribute", "std::string",
+                        /*default=*/"",
+                        "Attribute key used to extract field names from fields "
+                        "dictionary attributes">];
 }
 
 #endif // MLIR_DIALECT_EMITC_TRANSFORMS_PASSES
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index c74b03aefcefe..a9c4ae229d56f 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -1,15 +1,10 @@
-/* Copyright 2025 The TensorFlow Authors. All Rights Reserved.
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-    http://www.apache.org/licenses/LICENSE-2.0
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-==============================================================================*/
-
+//===- 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 for 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"
@@ -35,7 +30,7 @@ class AddReflectionMapPass
     Operation *rootOp = getOperation();
 
     RewritePatternSet patterns(&getContext());
-    populateAddReflectionMapPatterns(patterns);
+    populateAddReflectionMapPatterns(patterns, namedAttribute);
 
     walkAndApplyPatterns(rootOp, std::move(patterns));
   }
@@ -47,8 +42,8 @@ class AddReflectionMapPass
 
 class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
 public:
-  AddReflectionMapClass(MLIRContext *context)
-      : OpRewritePattern<emitc::ClassOp>(context) {}
+  AddReflectionMapClass(MLIRContext *context, StringRef attrName)
+      : OpRewritePattern<emitc::ClassOp>(context), attributeName(attrName) {}
 
   LogicalResult matchAndRewrite(mlir::emitc::ClassOp classOp,
                                 PatternRewriter &rewriter) const override {
@@ -73,23 +68,23 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
     rewriter.setInsertionPointToStart(funcBody);
 
     // Collect all field names
-    SmallVector<std::string> fieldNames;
+    std::vector<std::pair<std::string, std::string>> fieldNames;
     classOp.walk([&](mlir::emitc::FieldOp fieldOp) {
       if (mlir::Attribute attrsAttr =
               fieldOp->getAttrDictionary().get("attrs")) {
         if (DictionaryAttr innerDictAttr =
                 dyn_cast<mlir::DictionaryAttr>(attrsAttr)) {
-          auto indexPathAttr =
-              innerDictAttr.getNamed("tf_saved_model.index_path");
+          auto indexPathAttr = innerDictAttr.getNamed(attributeName);
           ArrayAttr arrayAttr =
               dyn_cast<mlir::ArrayAttr>(indexPathAttr->getValue());
           if (!arrayAttr.empty()) {
             StringAttr stringAttr = dyn_cast<mlir::StringAttr>(arrayAttr[0]);
             std::string indexPath = stringAttr.getValue().str();
-            fieldNames.push_back(indexPath);
+            fieldNames.emplace_back(indexPath, fieldOp.getName().str());
           }
           if (arrayAttr.size() > 1) {
-            fieldOp.emitError() << "tf_saved_model.index_path attribute must "
+            fieldOp.emitError() << attributeName
+                                << " attribute must "
                                    "contain at most one value, but found "
                                 << arrayAttr.size() << " values.";
             return;
@@ -98,64 +93,54 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
       }
     });
 
-    std::string mapInitializer = "{{";
+    std::string mapInitializer = "{ ";
     for (size_t i = 0; i < fieldNames.size(); ++i) {
-      mapInitializer += "\"" + fieldNames[i] + "\", " +
-                        "reinterpret_cast<char*>(&" + fieldNames[i] + ")",
-          mapInitializer += "}";
+      mapInitializer += " { \"" + fieldNames[i].first + "\", " +
+                        "reinterpret_cast<char*>(&" + fieldNames[i].second +
+                        ")",
+          mapInitializer += " }";
       if (i < fieldNames.size() - 1)
-        mapInitializer += ", {";
+        mapInitializer += ", ";
     }
-    mapInitializer += "}";
+    mapInitializer += " }";
 
-    auto iteratorType = mlir::emitc::OpaqueType::get(
+    emitc::OpaqueType iteratorType = mlir::emitc::OpaqueType::get(
         context, "std::map<std::string, char*>::const_iterator");
-    auto boolType = rewriter.getI1Type();
-    // 5. Create the constant map
-    auto bufferMap = rewriter.create<emitc::ConstantOp>(
+
+    emitc::ConstantOp bufferMap = rewriter.create<emitc::ConstantOp>(
         classOp.getLoc(), mapType,
         emitc::OpaqueAttr::get(context, mapInitializer));
 
-    // 6. Get the function argument
     mlir::Value nameArg = getBufferFunc.getArgument(0);
-
-    // 7. Create the find call
-    auto it = rewriter.create<emitc::CallOpaqueOp>(
+    emitc::CallOpaqueOp it = rewriter.create<emitc::CallOpaqueOp>(
         classOp.getLoc(), iteratorType, rewriter.getStringAttr("find"),
         mlir::ValueRange{bufferMap.getResult(), nameArg});
-
-    // 8. Create the end call
-    auto endIt = rewriter.create<emitc::CallOpaqueOp>(
+    emitc::CallOpaqueOp endIt = rewriter.create<emitc::CallOpaqueOp>(
         classOp.getLoc(), iteratorType, rewriter.getStringAttr("end"),
         bufferMap.getResult());
-
-    // 9. Create the operator== call
-    auto isEnd = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), boolType,
+    emitc::CallOpaqueOp isEnd = rewriter.create<emitc::CallOpaqueOp>(
+        classOp.getLoc(), rewriter.getI1Type(),
         "operator==", mlir::ValueRange{it.getResult(0), endIt.getResult(0)});
-
-    // 10. Create the nullptr constant
-    auto nullPtr = rewriter.create<emitc::ConstantOp>(
+    emitc::ConstantOp nullPtr = rewriter.create<emitc::ConstantOp>(
         classOp.getLoc(), charPtrType,
         emitc::OpaqueAttr::get(context, "nullptr"));
-
-    // 11. Create the second call
-    auto second = rewriter.create<emitc::CallOpaqueOp>(
+    emitc::CallOpaqueOp second = rewriter.create<emitc::CallOpaqueOp>(
         classOp.getLoc(), charPtrType, "second", it.getResult(0));
 
-    // 12. Create the conditional
-    auto result = rewriter.create<emitc::ConditionalOp>(
+    emitc::ConditionalOp result = rewriter.create<emitc::ConditionalOp>(
         classOp.getLoc(), charPtrType, isEnd.getResult(0), nullPtr.getResult(),
         second.getResult(0));
 
-    // 13. Create return
     rewriter.create<emitc::ReturnOp>(classOp.getLoc(), result.getResult());
 
     return success();
   }
+
+private:
+  StringRef attributeName;
 };
 
-void mlir::emitc::populateAddReflectionMapPatterns(
-    RewritePatternSet &patterns) {
-  patterns.add<AddReflectionMapClass>(patterns.getContext());
+void mlir::emitc::populateAddReflectionMapPatterns(RewritePatternSet &patterns,
+                                                   StringRef namedAttribute) {
+  patterns.add<AddReflectionMapClass>(patterns.getContext(), namedAttribute);
 }
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..f61ee639b22fc
--- /dev/null
+++ b/mlir/test/Dialect/EmitC/add_reflection_map.mlir
@@ -0,0 +1,55 @@
+// RUN: mlir-opt --add-reflection-map="named-attribute=emitc.field_ref" %s | FileCheck %s
+
+emitc.class @mainClass {
+  emitc.field @fieldName0 : !emitc.array<1xf32>  {emitc.field_ref = ["another_feature"]}
+  emitc.field @fieldName1 : !emitc.array<1xf32>  {emitc.field_ref = ["some_feature"]}
+  emitc.field @fieldName2 : !emitc.array<1xf32>  {emitc.field_ref = ["output_0"]}
+  emitc.func @execute() {
+  %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+  %1 = get_field @fieldName0 : !emitc.array<1xf32>
+  %2 = get_field @fieldName1 : !emitc.array<1xf32>
+  %3 = get_field @fieldName2 : !emitc.array<1xf32>
+  %4 = subscript %2[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+  %5 = load %4 : <f32>
+  %6 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+  %7 = load %6 : <f32>
+  %8 = add %5, %7 : (f32, f32) -> f32
+  %9 = subscript %3[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+  assign %8 : f32 to %9 : <f32>
+  return
+  }
+}
+
+// CHECK: module {
+// CHECK-NEXT:   emitc.class @mainClass {
+// 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 @fieldName2 : !emitc.array<1xf32> {emitc.field_ref = ["output_0"]}
+// CHECK-NEXT:     emitc.func @getBufferForName(%arg0: !emitc.opaque<"std::string_view">) -> !emitc.opaque<"char"> {
+// CHECK-NEXT:       %0 = "emitc.constant"() <{value = #emitc.opaque<"{  { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) },  { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) },  { \22output_0\22, reinterpret_cast<char*>(&fieldName2) } }">}> : () -> !emitc.opaque<"const std::map<std::string, char*>">
+// CHECK-NEXT:       %1 = call_opaque "find"(%0, %arg0) : (!emitc.opaque<"const std::map<std::string, char*>">, !emitc.opaque<"std::string_view">) -> !emitc.opaque<"std::map<std::string, char*>::const_iterator">
+// CHECK-NEXT:       %2 = call_opaque "end"(%0) : (!emitc.opaque<"const std::map<std::string, char*>">) -> !emitc.opaque<"std::map<std::string, char*>::const_iterator">
+// CHECK-NEXT:       %3 = call_opaque "operator=="(%1, %2) : (!emitc.opaque<"std::map<std::string, char*>::const_iterator">, !emitc.opaque<"std::map<std::string, char*>::const_iterator">) -> i1
+// CHECK-NEXT:       %4 = "emitc.constant"() <{value = #emitc.opaque<"nullptr">}> : () -> !emitc.opaque<"char">
+// CHECK-NEXT:       %5 = call_opaque "second"(%1) : (!emitc.opaque<"std::map<std::string, char*>::const_iterator">) -> !emitc.opaque<"char">
+// CHECK-NEXT:       %6 = conditional %3, %4, %5 : !emitc.opaque<"char">
+// CHECK-NEXT:       return %6 : !emitc.opaque<"char">
+// CHECK-NEXT:     }
+// CHECK-NEXT:     emitc.func @execute() {
+// CHECK-NEXT:       %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+// CHECK-NEXT:       %1 = get_field @fieldName0 : !emitc.array<1xf32>
+// CHECK-NEXT:       %2 = get_field @fieldName1 : !emitc.array<1xf32>
+// CHECK-NEXT:       %3 = get_field @fieldName2 : !emitc.array<1xf32>
+// CHECK-NEXT:       %4 = subscript %2[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+// CHECK-NEXT:       %5 = load %4 : <f32>
+// CHECK-NEXT:       %6 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+// CHECK-NEXT:       %7 = load %6 : <f32>
+// CHECK-NEXT:       %8 = add %5, %7 : (f32, f32) -> f32
+// CHECK-NEXT:       %9 = subscript %3[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+// CHECK-NEXT:       assign %8 : f32 to %9 : <f32>
+// CHECK-NEXT:       return
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT: }
+ 
+

>From 1b07f074e588ab054fd4af6ebcafcdaf2b3c0ba1 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Fri, 25 Jul 2025 05:49:14 +0000
Subject: [PATCH 03/12] Specify the pass reqs

---
 .../mlir/Dialect/EmitC/Transforms/Passes.td   | 28 +++++----
 .../EmitC/Transforms/AddReflectionMap.cpp     | 58 +++++++++++++++----
 .../Dialect/EmitC/add_reflection_map.mlir     |  2 +
 3 files changed, 66 insertions(+), 22 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 5facdedbd1b4b..b591fecbbe6c1 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -53,33 +53,41 @@ def WrapFuncInClassPass : Pass<"wrap-emitc-func-in-class"> {
   let dependentDialects = ["emitc::EmitCDialect"];
 }
 
-def AddReflectionMapPass : Pass<"add-reflection-map"> {
+def AddReflectionMapPass : Pass<"add-reflection-map", "ModuleOp"> {
   let summary =
       "Add a reflection map function to EmitC classes for runtime field lookup";
   let description = [{
         This pass adds a `getBufferForName` function to EmitC classes that enables 
         runtime lookup of field buffers by their string names. 
-        This enables runtime introspection and dynamic access to class fields by name,
-        which is useful for interfacing with external systems that need to access 
-        tensors/buffers by their semantic names.
+        This would require that the class has fields with attributes and a function named `execute`.
+        The `fieldop` attribute is expected to be a dictionary where:
+        - The keys are `namedAttribute`.
+        - The values are arrays containing a single string attribute.
+
+
+        Example:
 
-        Example transformation:
         ```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:
-        ```mlir
+        // 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.func @getBufferForName(%name : !emitc.opaque<"std::string_view">) -> !emitc.opaque<"char*"> {
-            %map = "emitc.constant"(){value = #emitc.opaque<"{"another_feature", reinterpret_cast<char*>(&another_feature)}, {"some_feature", reinterpret_cast<char*>(&some_feature)}">} : () -> !emitc.opaque<"std::map<std::string, char*>">
-            return %null : !emitc.opaque<"char*">
+            %0 = "emitc.constant"() <{value = #emitc.opaque<"{  { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) },  { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) } }">}> : () -> !emitc.opaque<"const std::map<std::string, char*>">
+            %1 = call_opaque "find"(%0, %arg0) : (!emitc.opaque<"const std::map<std::string, char*>">, !emitc.opaque<"std::string_view">) -> !emitc.opaque<"std::map<std::string, char*>::const_iterator">
+            %2 = call_opaque "end"(%0) : (!emitc.opaque<"const std::map<std::string, char*>">) -> !emitc.opaque<"std::map<std::string, char*>::const_iterator">
+            %3 = call_opaque "operator=="(%1, %2) : (!emitc.opaque<"std::map<std::string, char*>::const_iterator">, !emitc.opaque<"std::map<std::string, char*>::const_iterator">) -> i1
+            %4 = "emitc.constant"() <{value = #emitc.opaque<"nullptr">}> : () -> !emitc.opaque<"char">
+            %5 = call_opaque "second"(%1) : (!emitc.opaque<"std::map<std::string, char*>::const_iterator">) -> !emitc.opaque<"char">
+            %6 = conditional %3, %4, %5 : !emitc.opaque<"char">
+            return %6 : !emitc.opaque<"char">
           }
           emitc.func @execute() { ... }
         }
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index a9c4ae229d56f..854d9adb4adcc 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -23,16 +23,48 @@ namespace emitc {
 #include "mlir/Dialect/EmitC/Transforms/Passes.h.inc"
 
 namespace {
+constexpr const char *kMapLibraryHeader = "map";
+constexpr const char *kStringLibraryHeader = "string";
 class AddReflectionMapPass
     : public impl::AddReflectionMapPassBase<AddReflectionMapPass> {
   using AddReflectionMapPassBase::AddReflectionMapPassBase;
   void runOnOperation() override {
-    Operation *rootOp = getOperation();
+    mlir::ModuleOp module = getOperation();
 
     RewritePatternSet patterns(&getContext());
     populateAddReflectionMapPatterns(patterns, namedAttribute);
 
-    walkAndApplyPatterns(rootOp, std::move(patterns));
+    walkAndApplyPatterns(module, std::move(patterns));
+    bool hasMap = false;
+    bool hasString = false;
+    for (auto &op : *module.getBody()) {
+      emitc::IncludeOp includeOp = llvm::dyn_cast<mlir::emitc::IncludeOp>(op);
+      if (!includeOp)
+        continue;
+      if (includeOp.getIsStandardInclude()) {
+        if (includeOp.getInclude() == kMapLibraryHeader)
+          hasMap = true;
+        if (includeOp.getInclude() == kStringLibraryHeader)
+          hasString = true;
+      }
+    }
+
+    if (hasMap && hasString)
+      return;
+
+    mlir::OpBuilder builder(module.getBody(), module.getBody()->begin());
+    if (!hasMap) {
+      StringAttr includeAttr = builder.getStringAttr(kMapLibraryHeader);
+      builder.create<mlir::emitc::IncludeOp>(
+          module.getLoc(), includeAttr,
+          /*is_standard_include=*/builder.getUnitAttr());
+    }
+    if (!hasString) {
+      StringAttr includeAttr = builder.getStringAttr(kStringLibraryHeader);
+      builder.create<emitc::IncludeOp>(
+          module.getLoc(), includeAttr,
+          /*is_standard_include=*/builder.getUnitAttr());
+    }
   }
 };
 
@@ -50,16 +82,20 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
     mlir::MLIRContext *context = rewriter.getContext();
     emitc::OpaqueType stringViewType =
         mlir::emitc::OpaqueType::get(rewriter.getContext(), "std::string_view");
-    emitc::OpaqueType charPtrType =
+    emitc::OpaqueType charType =
         mlir::emitc::OpaqueType::get(rewriter.getContext(), "char");
     emitc::OpaqueType mapType = mlir::emitc::OpaqueType::get(
         rewriter.getContext(), "const std::map<std::string, char*>");
 
     FunctionType funcType =
-        rewriter.getFunctionType({stringViewType}, {charPtrType});
+        rewriter.getFunctionType({stringViewType}, {charType});
     emitc::FuncOp executeFunc =
         classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
-    rewriter.setInsertionPoint(executeFunc);
+    if (executeFunc)
+      rewriter.setInsertionPoint(executeFunc);
+    else
+      classOp.emitError() << "ClassOp must contain a function named 'execute' "
+                             "to add reflection map";
 
     emitc::FuncOp getBufferFunc = rewriter.create<mlir::emitc::FuncOp>(
         classOp.getLoc(), "getBufferForName", funcType);
@@ -74,9 +110,8 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
               fieldOp->getAttrDictionary().get("attrs")) {
         if (DictionaryAttr innerDictAttr =
                 dyn_cast<mlir::DictionaryAttr>(attrsAttr)) {
-          auto indexPathAttr = innerDictAttr.getNamed(attributeName);
-          ArrayAttr arrayAttr =
-              dyn_cast<mlir::ArrayAttr>(indexPathAttr->getValue());
+          ArrayAttr arrayAttr = dyn_cast<mlir::ArrayAttr>(
+              innerDictAttr.getNamed(attributeName)->getValue());
           if (!arrayAttr.empty()) {
             StringAttr stringAttr = dyn_cast<mlir::StringAttr>(arrayAttr[0]);
             std::string indexPath = stringAttr.getValue().str();
@@ -122,13 +157,12 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
         classOp.getLoc(), rewriter.getI1Type(),
         "operator==", mlir::ValueRange{it.getResult(0), endIt.getResult(0)});
     emitc::ConstantOp nullPtr = rewriter.create<emitc::ConstantOp>(
-        classOp.getLoc(), charPtrType,
-        emitc::OpaqueAttr::get(context, "nullptr"));
+        classOp.getLoc(), charType, emitc::OpaqueAttr::get(context, "nullptr"));
     emitc::CallOpaqueOp second = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), charPtrType, "second", it.getResult(0));
+        classOp.getLoc(), charType, "second", it.getResult(0));
 
     emitc::ConditionalOp result = rewriter.create<emitc::ConditionalOp>(
-        classOp.getLoc(), charPtrType, isEnd.getResult(0), nullPtr.getResult(),
+        classOp.getLoc(), charType, isEnd.getResult(0), nullPtr.getResult(),
         second.getResult(0));
 
     rewriter.create<emitc::ReturnOp>(classOp.getLoc(), result.getResult());
diff --git a/mlir/test/Dialect/EmitC/add_reflection_map.mlir b/mlir/test/Dialect/EmitC/add_reflection_map.mlir
index f61ee639b22fc..607de348e6f79 100644
--- a/mlir/test/Dialect/EmitC/add_reflection_map.mlir
+++ b/mlir/test/Dialect/EmitC/add_reflection_map.mlir
@@ -21,6 +21,8 @@ emitc.class @mainClass {
 }
 
 // CHECK: module {
+// CHECK-NEXT:   emitc.include <"map">
+// CHECK-NEXT:   emitc.include <"string">
 // CHECK-NEXT:   emitc.class @mainClass {
 // 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"]}

>From 608b19b557e8ba2a6b70c0cdd9be0dbc2f2fbae5 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Fri, 25 Jul 2025 21:00:45 +0000
Subject: [PATCH 04/12] small change

---
 .../mlir/Dialect/EmitC/Transforms/Passes.td   |  2 +-
 .../EmitC/Transforms/AddReflectionMap.cpp     | 43 ++++++++++---------
 2 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index b591fecbbe6c1..5815bdc471cb6 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -59,7 +59,7 @@ def AddReflectionMapPass : Pass<"add-reflection-map", "ModuleOp"> {
   let description = [{
         This pass adds a `getBufferForName` function to EmitC classes that enables 
         runtime lookup of field buffers by their string names. 
-        This would require that the class has fields with attributes and a function named `execute`.
+        This requires that the class has fields with attributes and a function named `execute`.
         The `fieldop` attribute is expected to be a dictionary where:
         - The keys are `namedAttribute`.
         - The values are arrays containing a single string attribute.
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index 854d9adb4adcc..b25cd6740e5ba 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -23,8 +23,16 @@ namespace emitc {
 #include "mlir/Dialect/EmitC/Transforms/Passes.h.inc"
 
 namespace {
-constexpr const char *kMapLibraryHeader = "map";
-constexpr const char *kStringLibraryHeader = "string";
+constexpr const char *mapLibraryHeader = "map";
+constexpr const char *stringLibraryHeader = "string";
+
+IncludeOp addHeader(OpBuilder &builder, ModuleOp module, StringRef headerName) {
+  StringAttr includeAttr = builder.getStringAttr(headerName);
+  return builder.create<emitc::IncludeOp>(
+      module.getLoc(), includeAttr,
+      /*is_standard_include=*/builder.getUnitAttr());
+}
+
 class AddReflectionMapPass
     : public impl::AddReflectionMapPassBase<AddReflectionMapPass> {
   using AddReflectionMapPassBase::AddReflectionMapPassBase;
@@ -35,35 +43,28 @@ class AddReflectionMapPass
     populateAddReflectionMapPatterns(patterns, namedAttribute);
 
     walkAndApplyPatterns(module, std::move(patterns));
-    bool hasMap = false;
-    bool hasString = false;
+    bool hasMapHdr = false;
+    bool hasStringHdr = false;
     for (auto &op : *module.getBody()) {
       emitc::IncludeOp includeOp = llvm::dyn_cast<mlir::emitc::IncludeOp>(op);
       if (!includeOp)
         continue;
       if (includeOp.getIsStandardInclude()) {
-        if (includeOp.getInclude() == kMapLibraryHeader)
-          hasMap = true;
-        if (includeOp.getInclude() == kStringLibraryHeader)
-          hasString = true;
+        if (includeOp.getInclude() == mapLibraryHeader)
+          hasMapHdr = true;
+        if (includeOp.getInclude() == stringLibraryHeader)
+          hasStringHdr = true;
       }
+      if (hasMapHdr && hasStringHdr)
+        return;
     }
 
-    if (hasMap && hasString)
-      return;
-
     mlir::OpBuilder builder(module.getBody(), module.getBody()->begin());
-    if (!hasMap) {
-      StringAttr includeAttr = builder.getStringAttr(kMapLibraryHeader);
-      builder.create<mlir::emitc::IncludeOp>(
-          module.getLoc(), includeAttr,
-          /*is_standard_include=*/builder.getUnitAttr());
+    if (!hasMapHdr) {
+      addHeader(builder, module, mapLibraryHeader);
     }
-    if (!hasString) {
-      StringAttr includeAttr = builder.getStringAttr(kStringLibraryHeader);
-      builder.create<emitc::IncludeOp>(
-          module.getLoc(), includeAttr,
-          /*is_standard_include=*/builder.getUnitAttr());
+    if (!hasStringHdr) {
+      addHeader(builder, module, stringLibraryHeader);
     }
   }
 };

>From fa627db0354147d169e2dfca745c47fe4efff831 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Mon, 28 Jul 2025 15:40:25 +0000
Subject: [PATCH 05/12] avoid re-initialization

---
 .../Dialect/EmitC/Transforms/Transforms.h     |  4 +
 .../EmitC/Transforms/AddReflectionMap.cpp     | 74 +++++++------------
 2 files changed, 32 insertions(+), 46 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index bdf6d0985e6db..7abc430347dc3 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -31,6 +31,10 @@ void populateExpressionPatterns(RewritePatternSet &patterns);
 /// Populates 'patterns' with func-related patterns.
 void populateFuncPatterns(RewritePatternSet &patterns);
 
+/// Populates `patterns` with patterns to add reflection map for EmitC classes.
+void populateAddReflectionMapPatterns(RewritePatternSet &patterns,
+                                      StringRef namedAttribute);
+
 } // namespace emitc
 } // namespace mlir
 
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index b25cd6740e5ba..eaad5a6b352cb 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -80,29 +80,10 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
 
   LogicalResult matchAndRewrite(mlir::emitc::ClassOp classOp,
                                 PatternRewriter &rewriter) const override {
-    mlir::MLIRContext *context = rewriter.getContext();
-    emitc::OpaqueType stringViewType =
-        mlir::emitc::OpaqueType::get(rewriter.getContext(), "std::string_view");
-    emitc::OpaqueType charType =
-        mlir::emitc::OpaqueType::get(rewriter.getContext(), "char");
-    emitc::OpaqueType mapType = mlir::emitc::OpaqueType::get(
-        rewriter.getContext(), "const std::map<std::string, char*>");
-
-    FunctionType funcType =
-        rewriter.getFunctionType({stringViewType}, {charType});
-    emitc::FuncOp executeFunc =
-        classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
-    if (executeFunc)
-      rewriter.setInsertionPoint(executeFunc);
-    else
-      classOp.emitError() << "ClassOp must contain a function named 'execute' "
-                             "to add reflection map";
+    MLIRContext *context = rewriter.getContext();
 
-    emitc::FuncOp getBufferFunc = rewriter.create<mlir::emitc::FuncOp>(
-        classOp.getLoc(), "getBufferForName", funcType);
-
-    Block *funcBody = getBufferFunc.addEntryBlock();
-    rewriter.setInsertionPointToStart(funcBody);
+    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;
@@ -129,44 +110,45 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
       }
     });
 
+    // Construct the map initializer string
     std::string mapInitializer = "{ ";
     for (size_t i = 0; i < fieldNames.size(); ++i) {
       mapInitializer += " { \"" + fieldNames[i].first + "\", " +
                         "reinterpret_cast<char*>(&" + fieldNames[i].second +
-                        ")",
-          mapInitializer += " }";
+                        ")";
+      mapInitializer += " }";
       if (i < fieldNames.size() - 1)
         mapInitializer += ", ";
     }
     mapInitializer += " }";
 
-    emitc::OpaqueType iteratorType = mlir::emitc::OpaqueType::get(
-        context, "std::map<std::string, char*>::const_iterator");
+    emitc::OpaqueType returnType = mlir::emitc::OpaqueType::get(
+        context, "const std::map<std::string, char*>");
+
+    emitc::FuncOp executeFunc =
+        classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
+    if (executeFunc)
+      rewriter.setInsertionPoint(executeFunc);
+    else
+      classOp.emitError() << "ClassOp must contain a function named 'execute' "
+                             "to add reflection map";
+
+    // Create the getFeatures function
+    emitc::FuncOp getFeaturesFunc = rewriter.create<mlir::emitc::FuncOp>(
+        classOp.getLoc(), "getFeatures",
+        rewriter.getFunctionType({}, {returnType}));
+
+    // Add the body of the getFeatures function
+    Block *funcBody = getFeaturesFunc.addEntryBlock();
+    rewriter.setInsertionPointToStart(funcBody);
 
+    // Create the constant map
     emitc::ConstantOp bufferMap = rewriter.create<emitc::ConstantOp>(
         classOp.getLoc(), mapType,
         emitc::OpaqueAttr::get(context, mapInitializer));
 
-    mlir::Value nameArg = getBufferFunc.getArgument(0);
-    emitc::CallOpaqueOp it = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), iteratorType, rewriter.getStringAttr("find"),
-        mlir::ValueRange{bufferMap.getResult(), nameArg});
-    emitc::CallOpaqueOp endIt = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), iteratorType, rewriter.getStringAttr("end"),
-        bufferMap.getResult());
-    emitc::CallOpaqueOp isEnd = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), rewriter.getI1Type(),
-        "operator==", mlir::ValueRange{it.getResult(0), endIt.getResult(0)});
-    emitc::ConstantOp nullPtr = rewriter.create<emitc::ConstantOp>(
-        classOp.getLoc(), charType, emitc::OpaqueAttr::get(context, "nullptr"));
-    emitc::CallOpaqueOp second = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), charType, "second", it.getResult(0));
-
-    emitc::ConditionalOp result = rewriter.create<emitc::ConditionalOp>(
-        classOp.getLoc(), charType, isEnd.getResult(0), nullPtr.getResult(),
-        second.getResult(0));
-
-    rewriter.create<emitc::ReturnOp>(classOp.getLoc(), result.getResult());
+    rewriter.create<mlir::emitc::ReturnOp>(classOp.getLoc(),
+                                           bufferMap.getResult());
 
     return success();
   }

>From 6f82da956d942b0914557886e3937793f2ca703b Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Mon, 28 Jul 2025 16:07:41 +0000
Subject: [PATCH 06/12] Revert "avoid re-initialization"

This reverts commit f2dee0d99bc1fb258b6cef57dc150cb637cc4ab3.
---
 .../Dialect/EmitC/Transforms/Transforms.h     |  4 -
 .../EmitC/Transforms/AddReflectionMap.cpp     | 74 ++++++++++++-------
 2 files changed, 46 insertions(+), 32 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index 7abc430347dc3..bdf6d0985e6db 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -31,10 +31,6 @@ void populateExpressionPatterns(RewritePatternSet &patterns);
 /// Populates 'patterns' with func-related patterns.
 void populateFuncPatterns(RewritePatternSet &patterns);
 
-/// Populates `patterns` with patterns to add reflection map for EmitC classes.
-void populateAddReflectionMapPatterns(RewritePatternSet &patterns,
-                                      StringRef namedAttribute);
-
 } // namespace emitc
 } // namespace mlir
 
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index eaad5a6b352cb..b25cd6740e5ba 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -80,10 +80,29 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
 
   LogicalResult matchAndRewrite(mlir::emitc::ClassOp classOp,
                                 PatternRewriter &rewriter) const override {
-    MLIRContext *context = rewriter.getContext();
-
+    mlir::MLIRContext *context = rewriter.getContext();
+    emitc::OpaqueType stringViewType =
+        mlir::emitc::OpaqueType::get(rewriter.getContext(), "std::string_view");
+    emitc::OpaqueType charType =
+        mlir::emitc::OpaqueType::get(rewriter.getContext(), "char");
     emitc::OpaqueType mapType = mlir::emitc::OpaqueType::get(
-        context, "const std::map<std::string, char*>");
+        rewriter.getContext(), "const std::map<std::string, char*>");
+
+    FunctionType funcType =
+        rewriter.getFunctionType({stringViewType}, {charType});
+    emitc::FuncOp executeFunc =
+        classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
+    if (executeFunc)
+      rewriter.setInsertionPoint(executeFunc);
+    else
+      classOp.emitError() << "ClassOp must contain a function named 'execute' "
+                             "to add reflection map";
+
+    emitc::FuncOp getBufferFunc = rewriter.create<mlir::emitc::FuncOp>(
+        classOp.getLoc(), "getBufferForName", funcType);
+
+    Block *funcBody = getBufferFunc.addEntryBlock();
+    rewriter.setInsertionPointToStart(funcBody);
 
     // Collect all field names
     std::vector<std::pair<std::string, std::string>> fieldNames;
@@ -110,45 +129,44 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
       }
     });
 
-    // Construct the map initializer string
     std::string mapInitializer = "{ ";
     for (size_t i = 0; i < fieldNames.size(); ++i) {
       mapInitializer += " { \"" + fieldNames[i].first + "\", " +
                         "reinterpret_cast<char*>(&" + fieldNames[i].second +
-                        ")";
-      mapInitializer += " }";
+                        ")",
+          mapInitializer += " }";
       if (i < fieldNames.size() - 1)
         mapInitializer += ", ";
     }
     mapInitializer += " }";
 
-    emitc::OpaqueType returnType = mlir::emitc::OpaqueType::get(
-        context, "const std::map<std::string, char*>");
-
-    emitc::FuncOp executeFunc =
-        classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
-    if (executeFunc)
-      rewriter.setInsertionPoint(executeFunc);
-    else
-      classOp.emitError() << "ClassOp must contain a function named 'execute' "
-                             "to add reflection map";
-
-    // Create the getFeatures function
-    emitc::FuncOp getFeaturesFunc = rewriter.create<mlir::emitc::FuncOp>(
-        classOp.getLoc(), "getFeatures",
-        rewriter.getFunctionType({}, {returnType}));
-
-    // Add the body of the getFeatures function
-    Block *funcBody = getFeaturesFunc.addEntryBlock();
-    rewriter.setInsertionPointToStart(funcBody);
+    emitc::OpaqueType iteratorType = mlir::emitc::OpaqueType::get(
+        context, "std::map<std::string, char*>::const_iterator");
 
-    // Create the constant map
     emitc::ConstantOp bufferMap = rewriter.create<emitc::ConstantOp>(
         classOp.getLoc(), mapType,
         emitc::OpaqueAttr::get(context, mapInitializer));
 
-    rewriter.create<mlir::emitc::ReturnOp>(classOp.getLoc(),
-                                           bufferMap.getResult());
+    mlir::Value nameArg = getBufferFunc.getArgument(0);
+    emitc::CallOpaqueOp it = rewriter.create<emitc::CallOpaqueOp>(
+        classOp.getLoc(), iteratorType, rewriter.getStringAttr("find"),
+        mlir::ValueRange{bufferMap.getResult(), nameArg});
+    emitc::CallOpaqueOp endIt = rewriter.create<emitc::CallOpaqueOp>(
+        classOp.getLoc(), iteratorType, rewriter.getStringAttr("end"),
+        bufferMap.getResult());
+    emitc::CallOpaqueOp isEnd = rewriter.create<emitc::CallOpaqueOp>(
+        classOp.getLoc(), rewriter.getI1Type(),
+        "operator==", mlir::ValueRange{it.getResult(0), endIt.getResult(0)});
+    emitc::ConstantOp nullPtr = rewriter.create<emitc::ConstantOp>(
+        classOp.getLoc(), charType, emitc::OpaqueAttr::get(context, "nullptr"));
+    emitc::CallOpaqueOp second = rewriter.create<emitc::CallOpaqueOp>(
+        classOp.getLoc(), charType, "second", it.getResult(0));
+
+    emitc::ConditionalOp result = rewriter.create<emitc::ConditionalOp>(
+        classOp.getLoc(), charType, isEnd.getResult(0), nullPtr.getResult(),
+        second.getResult(0));
+
+    rewriter.create<emitc::ReturnOp>(classOp.getLoc(), result.getResult());
 
     return success();
   }

>From 94b0c34027a4f9a86a749f188ac591ede886ee44 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Mon, 28 Jul 2025 16:08:35 +0000
Subject: [PATCH 07/12] Cleaning

---
 mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index b25cd6740e5ba..1bf0747ecf487 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -138,7 +138,7 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
       if (i < fieldNames.size() - 1)
         mapInitializer += ", ";
     }
-    mapInitializer += " }";
+    mapInitializer += " }}";
 
     emitc::OpaqueType iteratorType = mlir::emitc::OpaqueType::get(
         context, "std::map<std::string, char*>::const_iterator");

>From 27a3aeba4248f61cb6bc12a8840453188041ffbb Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Tue, 29 Jul 2025 03:02:32 +0000
Subject: [PATCH 08/12] Return the whole map

---
 .../mlir/Dialect/EmitC/Transforms/Passes.td   | 10 +--
 .../Dialect/EmitC/Transforms/Transforms.h     |  4 ++
 .../EmitC/Transforms/AddReflectionMap.cpp     | 72 +++++++------------
 3 files changed, 33 insertions(+), 53 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 5815bdc471cb6..ebf90e2edf9fd 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -79,15 +79,9 @@ def AddReflectionMapPass : Pass<"add-reflection-map", "ModuleOp"> {
         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 @getBufferForName(%name : !emitc.opaque<"std::string_view">) -> !emitc.opaque<"char*"> {
+          emitc.func @getFeatures() {
             %0 = "emitc.constant"() <{value = #emitc.opaque<"{  { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) },  { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) } }">}> : () -> !emitc.opaque<"const std::map<std::string, char*>">
-            %1 = call_opaque "find"(%0, %arg0) : (!emitc.opaque<"const std::map<std::string, char*>">, !emitc.opaque<"std::string_view">) -> !emitc.opaque<"std::map<std::string, char*>::const_iterator">
-            %2 = call_opaque "end"(%0) : (!emitc.opaque<"const std::map<std::string, char*>">) -> !emitc.opaque<"std::map<std::string, char*>::const_iterator">
-            %3 = call_opaque "operator=="(%1, %2) : (!emitc.opaque<"std::map<std::string, char*>::const_iterator">, !emitc.opaque<"std::map<std::string, char*>::const_iterator">) -> i1
-            %4 = "emitc.constant"() <{value = #emitc.opaque<"nullptr">}> : () -> !emitc.opaque<"char">
-            %5 = call_opaque "second"(%1) : (!emitc.opaque<"std::map<std::string, char*>::const_iterator">) -> !emitc.opaque<"char">
-            %6 = conditional %3, %4, %5 : !emitc.opaque<"char">
-            return %6 : !emitc.opaque<"char">
+            return %0 : !emitc.opaque<"const std::map<std::string, char*>">
           }
           emitc.func @execute() { ... }
         }
diff --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index bdf6d0985e6db..7abc430347dc3 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -31,6 +31,10 @@ void populateExpressionPatterns(RewritePatternSet &patterns);
 /// Populates 'patterns' with func-related patterns.
 void populateFuncPatterns(RewritePatternSet &patterns);
 
+/// Populates `patterns` with patterns to add reflection map for EmitC classes.
+void populateAddReflectionMapPatterns(RewritePatternSet &patterns,
+                                      StringRef namedAttribute);
+
 } // namespace emitc
 } // namespace mlir
 
diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index 1bf0747ecf487..ef7957b5a9cb8 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -80,29 +80,11 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
 
   LogicalResult matchAndRewrite(mlir::emitc::ClassOp classOp,
                                 PatternRewriter &rewriter) const override {
-    mlir::MLIRContext *context = rewriter.getContext();
-    emitc::OpaqueType stringViewType =
-        mlir::emitc::OpaqueType::get(rewriter.getContext(), "std::string_view");
-    emitc::OpaqueType charType =
-        mlir::emitc::OpaqueType::get(rewriter.getContext(), "char");
-    emitc::OpaqueType mapType = mlir::emitc::OpaqueType::get(
-        rewriter.getContext(), "const std::map<std::string, char*>");
-
-    FunctionType funcType =
-        rewriter.getFunctionType({stringViewType}, {charType});
-    emitc::FuncOp executeFunc =
-        classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
-    if (executeFunc)
-      rewriter.setInsertionPoint(executeFunc);
-    else
-      classOp.emitError() << "ClassOp must contain a function named 'execute' "
-                             "to add reflection map";
+    MLIRContext *context = rewriter.getContext();
 
-    emitc::FuncOp getBufferFunc = rewriter.create<mlir::emitc::FuncOp>(
-        classOp.getLoc(), "getBufferForName", funcType);
-
-    Block *funcBody = getBufferFunc.addEntryBlock();
-    rewriter.setInsertionPointToStart(funcBody);
+    // Define the opaque types
+    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;
@@ -138,35 +120,35 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
       if (i < fieldNames.size() - 1)
         mapInitializer += ", ";
     }
-    mapInitializer += " }}";
+    mapInitializer += " }";
 
-    emitc::OpaqueType iteratorType = mlir::emitc::OpaqueType::get(
-        context, "std::map<std::string, char*>::const_iterator");
+    emitc::FuncOp executeFunc =
+        classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
+    if (executeFunc)
+      rewriter.setInsertionPoint(executeFunc);
+    else
+      classOp.emitError() << "ClassOp must contain a function named 'execute' "
+                             "to add reflection map";
+
+    emitc::OpaqueType returnType = mlir::emitc::OpaqueType::get(
+        context, "const std::map<std::string, char*>");
+
+    // Create the getFeatures function
+    emitc::FuncOp getFeaturesFunc = rewriter.create<mlir::emitc::FuncOp>(
+        classOp.getLoc(), "getFeatures",
+        rewriter.getFunctionType({}, {returnType}));
+
+    // Add the body of the getFeatures function
+    Block *funcBody = getFeaturesFunc.addEntryBlock();
+    rewriter.setInsertionPointToStart(funcBody);
 
+    // Create the constant map
     emitc::ConstantOp bufferMap = rewriter.create<emitc::ConstantOp>(
         classOp.getLoc(), mapType,
         emitc::OpaqueAttr::get(context, mapInitializer));
 
-    mlir::Value nameArg = getBufferFunc.getArgument(0);
-    emitc::CallOpaqueOp it = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), iteratorType, rewriter.getStringAttr("find"),
-        mlir::ValueRange{bufferMap.getResult(), nameArg});
-    emitc::CallOpaqueOp endIt = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), iteratorType, rewriter.getStringAttr("end"),
-        bufferMap.getResult());
-    emitc::CallOpaqueOp isEnd = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), rewriter.getI1Type(),
-        "operator==", mlir::ValueRange{it.getResult(0), endIt.getResult(0)});
-    emitc::ConstantOp nullPtr = rewriter.create<emitc::ConstantOp>(
-        classOp.getLoc(), charType, emitc::OpaqueAttr::get(context, "nullptr"));
-    emitc::CallOpaqueOp second = rewriter.create<emitc::CallOpaqueOp>(
-        classOp.getLoc(), charType, "second", it.getResult(0));
-
-    emitc::ConditionalOp result = rewriter.create<emitc::ConditionalOp>(
-        classOp.getLoc(), charType, isEnd.getResult(0), nullPtr.getResult(),
-        second.getResult(0));
-
-    rewriter.create<emitc::ReturnOp>(classOp.getLoc(), result.getResult());
+    rewriter.create<mlir::emitc::ReturnOp>(classOp.getLoc(),
+                                           bufferMap.getResult());
 
     return success();
   }

>From 08ef0920dda32923512e0eb697a38e2cd3a93629 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Tue, 29 Jul 2025 03:20:40 +0000
Subject: [PATCH 09/12] Format the string well

---
 .../EmitC/Transforms/AddReflectionMap.cpp     | 51 ++++++++-----------
 1 file changed, 22 insertions(+), 29 deletions(-)

diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index ef7957b5a9cb8..dbda7fd77d721 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -89,38 +89,32 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
     // Collect all field names
     std::vector<std::pair<std::string, std::string>> fieldNames;
     classOp.walk([&](mlir::emitc::FieldOp fieldOp) {
-      if (mlir::Attribute attrsAttr =
-              fieldOp->getAttrDictionary().get("attrs")) {
-        if (DictionaryAttr innerDictAttr =
-                dyn_cast<mlir::DictionaryAttr>(attrsAttr)) {
-          ArrayAttr arrayAttr = dyn_cast<mlir::ArrayAttr>(
-              innerDictAttr.getNamed(attributeName)->getValue());
-          if (!arrayAttr.empty()) {
-            StringAttr stringAttr = dyn_cast<mlir::StringAttr>(arrayAttr[0]);
-            std::string indexPath = stringAttr.getValue().str();
-            fieldNames.emplace_back(indexPath, fieldOp.getName().str());
-          }
-          if (arrayAttr.size() > 1) {
-            fieldOp.emitError() << attributeName
-                                << " attribute must "
-                                   "contain at most one value, but found "
-                                << arrayAttr.size() << " values.";
-            return;
-          }
-        }
+      if (DictionaryAttr innerDictAttr = dyn_cast<mlir::DictionaryAttr>(
+              fieldOp->getAttrDictionary().get("attrs"))) {
+        ArrayAttr arrayAttr = cast<mlir::ArrayAttr>(
+            innerDictAttr.getNamed(attributeName)->getValue());
+        StringAttr stringAttr = cast<mlir::StringAttr>(arrayAttr[0]);
+        fieldNames.emplace_back(stringAttr.getValue().str(),
+                                fieldOp.getName().str());
+
+      } else {
+        fieldOp.emitError()
+            << "FieldOp must have a dictionary attribute named '"
+            << attributeName << "'"
+            << "with an array containing a string attribute";
       }
     });
 
-    std::string mapInitializer = "{ ";
+    std::stringstream ss;
+    ss << "{ ";
     for (size_t i = 0; i < fieldNames.size(); ++i) {
-      mapInitializer += " { \"" + fieldNames[i].first + "\", " +
-                        "reinterpret_cast<char*>(&" + fieldNames[i].second +
-                        ")",
-          mapInitializer += " }";
-      if (i < fieldNames.size() - 1)
-        mapInitializer += ", ";
+      ss << " { \"" << fieldNames[i].first << "\", reinterpret_cast<char*>(&"
+         << fieldNames[i].second << ") }";
+      if (i < fieldNames.size() - 1) {
+        ss << ", ";
+      }
     }
-    mapInitializer += " }";
+    ss << " }";
 
     emitc::FuncOp executeFunc =
         classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
@@ -144,8 +138,7 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
 
     // Create the constant map
     emitc::ConstantOp bufferMap = rewriter.create<emitc::ConstantOp>(
-        classOp.getLoc(), mapType,
-        emitc::OpaqueAttr::get(context, mapInitializer));
+        classOp.getLoc(), mapType, emitc::OpaqueAttr::get(context, ss.str()));
 
     rewriter.create<mlir::emitc::ReturnOp>(classOp.getLoc(),
                                            bufferMap.getResult());

>From 11bacaffb7bda0f34590a0c0bd93d286941f6126 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Tue, 29 Jul 2025 04:10:25 +0000
Subject: [PATCH 10/12] working test

---
 mlir/test/Dialect/EmitC/add_reflection_map.mlir | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/mlir/test/Dialect/EmitC/add_reflection_map.mlir b/mlir/test/Dialect/EmitC/add_reflection_map.mlir
index 607de348e6f79..e51be7d4b7e10 100644
--- a/mlir/test/Dialect/EmitC/add_reflection_map.mlir
+++ b/mlir/test/Dialect/EmitC/add_reflection_map.mlir
@@ -27,15 +27,9 @@ emitc.class @mainClass {
 // 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 @fieldName2 : !emitc.array<1xf32> {emitc.field_ref = ["output_0"]}
-// CHECK-NEXT:     emitc.func @getBufferForName(%arg0: !emitc.opaque<"std::string_view">) -> !emitc.opaque<"char"> {
-// CHECK-NEXT:       %0 = "emitc.constant"() <{value = #emitc.opaque<"{  { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) },  { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) },  { \22output_0\22, reinterpret_cast<char*>(&fieldName2) } }">}> : () -> !emitc.opaque<"const std::map<std::string, char*>">
-// CHECK-NEXT:       %1 = call_opaque "find"(%0, %arg0) : (!emitc.opaque<"const std::map<std::string, char*>">, !emitc.opaque<"std::string_view">) -> !emitc.opaque<"std::map<std::string, char*>::const_iterator">
-// CHECK-NEXT:       %2 = call_opaque "end"(%0) : (!emitc.opaque<"const std::map<std::string, char*>">) -> !emitc.opaque<"std::map<std::string, char*>::const_iterator">
-// CHECK-NEXT:       %3 = call_opaque "operator=="(%1, %2) : (!emitc.opaque<"std::map<std::string, char*>::const_iterator">, !emitc.opaque<"std::map<std::string, char*>::const_iterator">) -> i1
-// CHECK-NEXT:       %4 = "emitc.constant"() <{value = #emitc.opaque<"nullptr">}> : () -> !emitc.opaque<"char">
-// CHECK-NEXT:       %5 = call_opaque "second"(%1) : (!emitc.opaque<"std::map<std::string, char*>::const_iterator">) -> !emitc.opaque<"char">
-// CHECK-NEXT:       %6 = conditional %3, %4, %5 : !emitc.opaque<"char">
-// CHECK-NEXT:       return %6 : !emitc.opaque<"char">
+// CHECK-NEXT:     emitc.func @getFeatures() -> !emitc.opaque<"const std::map<std::string, char*>"> {
+// CHECK-NEXT:        %0 = "emitc.constant"() <{value = #emitc.opaque<"{  { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) },  { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) },  { \22output_0\22, reinterpret_cast<char*>(&fieldName2) } }">}> : () -> !emitc.opaque<"const std::map<std::string, char*>">
+// CHECK-NEXT:        return %0 : !emitc.opaque<"const std::map<std::string, char*>">
 // CHECK-NEXT:     }
 // CHECK-NEXT:     emitc.func @execute() {
 // CHECK-NEXT:       %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t

>From 7abbc257222d9387af7c4a83be0e1036d4ecccce Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Tue, 29 Jul 2025 20:14:32 +0000
Subject: [PATCH 11/12] make it a member

---
 .../EmitC/Transforms/AddReflectionMap.cpp     | 49 +++++++------------
 .../Dialect/EmitC/add_reflection_map.mlir     | 30 ++++++------
 2 files changed, 34 insertions(+), 45 deletions(-)

diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index dbda7fd77d721..3cc643d06672c 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -13,6 +13,7 @@
 #include "mlir/IR/BuiltinAttributes.h"
 #include "mlir/IR/PatternMatch.h"
 #include "mlir/Transforms/WalkPatternRewriteDriver.h"
+#include "llvm/Support/FormatVariadic.h"
 
 using namespace mlir;
 using namespace emitc;
@@ -60,12 +61,11 @@ class AddReflectionMapPass
     }
 
     mlir::OpBuilder builder(module.getBody(), module.getBody()->begin());
-    if (!hasMapHdr) {
+    if (!hasMapHdr)
       addHeader(builder, module, mapLibraryHeader);
-    }
-    if (!hasStringHdr) {
+
+    if (!hasStringHdr)
       addHeader(builder, module, stringLibraryHeader);
-    }
   }
 };
 
@@ -82,7 +82,6 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
                                 PatternRewriter &rewriter) const override {
     MLIRContext *context = rewriter.getContext();
 
-    // Define the opaque types
     emitc::OpaqueType mapType = mlir::emitc::OpaqueType::get(
         context, "const std::map<std::string, char*>");
 
@@ -105,43 +104,33 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
       }
     });
 
-    std::stringstream ss;
-    ss << "{ ";
+    std::string mapString;
+    mapString += "{ ";
     for (size_t i = 0; i < fieldNames.size(); ++i) {
-      ss << " { \"" << fieldNames[i].first << "\", reinterpret_cast<char*>(&"
-         << fieldNames[i].second << ") }";
-      if (i < fieldNames.size() - 1) {
-        ss << ", ";
-      }
+      mapString += llvm::formatv("{ \"{0}\", reinterpret_cast<char*>(&{1}) }",
+                                 fieldNames[i].first, fieldNames[i].second);
+      if (i < fieldNames.size() - 1)
+        mapString += ", ";
     }
-    ss << " }";
+    mapString += " }";
 
     emitc::FuncOp executeFunc =
         classOp.lookupSymbol<mlir::emitc::FuncOp>("execute");
     if (executeFunc)
       rewriter.setInsertionPoint(executeFunc);
-    else
+    else {
       classOp.emitError() << "ClassOp must contain a function named 'execute' "
                              "to add reflection map";
-
-    emitc::OpaqueType returnType = mlir::emitc::OpaqueType::get(
-        context, "const std::map<std::string, char*>");
-
-    // Create the getFeatures function
-    emitc::FuncOp getFeaturesFunc = rewriter.create<mlir::emitc::FuncOp>(
-        classOp.getLoc(), "getFeatures",
-        rewriter.getFunctionType({}, {returnType}));
-
-    // Add the body of the getFeatures function
-    Block *funcBody = getFeaturesFunc.addEntryBlock();
-    rewriter.setInsertionPointToStart(funcBody);
+      return failure();
+    }
 
     // Create the constant map
-    emitc::ConstantOp bufferMap = rewriter.create<emitc::ConstantOp>(
-        classOp.getLoc(), mapType, emitc::OpaqueAttr::get(context, ss.str()));
+    rewriter.create<emitc::ConstantOp>(
+        classOp.getLoc(), mapType, emitc::OpaqueAttr::get(context, mapString));
 
-    rewriter.create<mlir::emitc::ReturnOp>(classOp.getLoc(),
-                                           bufferMap.getResult());
+    // TODO: Ideally, we would create a function that returns a reference to the
+    // buffer map. However, current limitations in EmitC function support make
+    // this difficult to implement at the moment.
 
     return success();
   }
diff --git a/mlir/test/Dialect/EmitC/add_reflection_map.mlir b/mlir/test/Dialect/EmitC/add_reflection_map.mlir
index e51be7d4b7e10..b557c6d460bd6 100644
--- a/mlir/test/Dialect/EmitC/add_reflection_map.mlir
+++ b/mlir/test/Dialect/EmitC/add_reflection_map.mlir
@@ -27,22 +27,22 @@ emitc.class @mainClass {
 // 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 @fieldName2 : !emitc.array<1xf32> {emitc.field_ref = ["output_0"]}
-// CHECK-NEXT:     emitc.func @getFeatures() -> !emitc.opaque<"const std::map<std::string, char*>"> {
-// CHECK-NEXT:        %0 = "emitc.constant"() <{value = #emitc.opaque<"{  { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) },  { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) },  { \22output_0\22, reinterpret_cast<char*>(&fieldName2) } }">}> : () -> !emitc.opaque<"const std::map<std::string, char*>">
-// CHECK-NEXT:        return %0 : !emitc.opaque<"const std::map<std::string, char*>">
-// CHECK-NEXT:     }
+// CHECK-NEXT:     %0 = "emitc.constant"() <{value = #emitc.opaque<"{  { \22another_feature\22, reinterpret_cast<char*>(&fieldName0) },  
+// CHECK-SAME:     { \22some_feature\22, reinterpret_cast<char*>(&fieldName1) },  
+// CHECK-SAME:     { \22output_0\22, reinterpret_cast<char*>(&fieldName2) } }">}> 
+// CHECK-SAME:     : () -> !emitc.opaque<"const std::map<std::string, char*>">
 // CHECK-NEXT:     emitc.func @execute() {
-// CHECK-NEXT:       %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
-// CHECK-NEXT:       %1 = get_field @fieldName0 : !emitc.array<1xf32>
-// CHECK-NEXT:       %2 = get_field @fieldName1 : !emitc.array<1xf32>
-// CHECK-NEXT:       %3 = get_field @fieldName2 : !emitc.array<1xf32>
-// CHECK-NEXT:       %4 = subscript %2[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
-// CHECK-NEXT:       %5 = load %4 : <f32>
-// CHECK-NEXT:       %6 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
-// CHECK-NEXT:       %7 = load %6 : <f32>
-// CHECK-NEXT:       %8 = add %5, %7 : (f32, f32) -> f32
-// CHECK-NEXT:       %9 = subscript %3[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
-// CHECK-NEXT:       assign %8 : f32 to %9 : <f32>
+// CHECK-NEXT:       %1 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+// CHECK-NEXT:       %2 = get_field @fieldName0 : !emitc.array<1xf32>
+// CHECK-NEXT:       %3 = get_field @fieldName1 : !emitc.array<1xf32>
+// CHECK-NEXT:       %4 = get_field @fieldName2 : !emitc.array<1xf32>
+// CHECK-NEXT:       %5 = subscript %3[%1] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+// CHECK-NEXT:       %6 = load %5 : <f32>
+// CHECK-NEXT:       %7 = subscript %2[%1] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+// CHECK-NEXT:       %8 = load %7 : <f32>
+// CHECK-NEXT:       %9 = add %6, %8 : (f32, f32) -> f32
+// CHECK-NEXT:       %10 = subscript %4[%1] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+// CHECK-NEXT:       assign %9 : f32 to %10 : <f32>
 // CHECK-NEXT:       return
 // CHECK-NEXT:     }
 // CHECK-NEXT:   }

>From 750ba113314ba38033e9500b23a275882ed45de6 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Tue, 29 Jul 2025 21:07:59 +0000
Subject: [PATCH 12/12] use ternary

---
 .../lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
index 3cc643d06672c..0ecdd797c3f42 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/AddReflectionMap.cpp
@@ -96,21 +96,20 @@ class AddReflectionMapClass : public OpRewritePattern<emitc::ClassOp> {
         fieldNames.emplace_back(stringAttr.getValue().str(),
                                 fieldOp.getName().str());
 
-      } else {
+      } else
         fieldOp.emitError()
             << "FieldOp must have a dictionary attribute named '"
             << attributeName << "'"
             << "with an array containing a string attribute";
-      }
+    
     });
 
     std::string mapString;
     mapString += "{ ";
     for (size_t i = 0; i < fieldNames.size(); ++i) {
-      mapString += llvm::formatv("{ \"{0}\", reinterpret_cast<char*>(&{1}) }",
-                                 fieldNames[i].first, fieldNames[i].second);
-      if (i < fieldNames.size() - 1)
-        mapString += ", ";
+      mapString += llvm::formatv(
+          "{ \"{0}\", reinterpret_cast<char*>(&{1}) }{2}", fieldNames[i].first,
+          fieldNames[i].second, (i < fieldNames.size() - 1) ? ", " : "");
     }
     mapString += " }";
 



More information about the Mlir-commits mailing list