[Mlir-commits] [mlir] [mlir][emitc] Adding a reflection option to the class (PR #146133)

Jaden Angella llvmlistbot at llvm.org
Fri Jun 27 11:31:12 PDT 2025


https://github.com/Jaddyen created https://github.com/llvm/llvm-project/pull/146133

In considering how we can add reflection to the classes, a suggestion would be a completely new op that would then be lowered into a buffer_map that the class can use.
This is a proposal for how this can be done.

>From a469dc0b2b79be876f43214024c1efdc44ed7e19 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Thu, 26 Jun 2025 21:28:05 +0000
Subject: [PATCH 1/2] Started on the buffer_map

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td   | 40 +++++++++++++++++++
 .../EmitC/Transforms/WrapFuncInClass.cpp      | 23 ++++++++---
 2 files changed, 58 insertions(+), 5 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 91ee89919e58e..69076261ddd2d 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1679,4 +1679,44 @@ def EmitC_GetFieldOp
   let assemblyFormat = "$field_name `:` type($result) attr-dict";
 }
 
+def BufferMapOp
+    : EmitC_Op<"buffer_map", [Pure,
+                              DeclareOpInterfaceMethods<OpAsmOpInterface>]> {
+  let summary = "Creates a buffer map for field access";
+  let description = [{
+    The `emitc.buffer_map` operation generates a C++ std::map that maps field names
+    to their memory addresses for efficient runtime field access. This operation
+    collects fields with buffer attributes and creates the necessary lookup 
+    infrastructure.
+
+    Example:
+
+    ```mlir
+    emitc.buffer_map reflection_map [ @field1, @field2, @field3 ]
+    ```
+
+    This generates C++ code like:
+
+    ```cpp
+    const std::map<std::string, char*> _reflection_map {
+      { "field1", reinterpret_cast<char*>(&field1) },
+      { "field2", reinterpret_cast<char*>(&field2) },
+      { "field3", reinterpret_cast<char*>(&field3) }
+    };
+    
+    char* getBufferForName(const std::string& name) const {
+      auto it = _reflection_map.find(name);
+      return (it == _reflection_map.end()) ? nullptr : it->second;
+    }
+    ```
+  }];
+
+  let arguments = (ins SymbolNameAttr:$sym_name,
+      Arg<OptionalAttr<ArrayAttr>, "field names">:$fields);
+
+  let results = (outs);
+  let builders = [];
+  let assemblyFormat = "$sym_name  $fields attr-dict";
+}
+
 #endif // MLIR_DIALECT_EMITC_IR_EMITC
diff --git a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
index 17d436f6df028..4f2ecfb640d2a 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
@@ -53,22 +53,35 @@ class WrapFuncInClass : public OpRewritePattern<emitc::FuncOp> {
     ClassOp newClassOp = rewriter.create<ClassOp>(funcOp.getLoc(), className);
 
     SmallVector<std::pair<StringAttr, TypeAttr>> fields;
+    SmallVector<Attribute> bufferFieldAttrs;
     rewriter.createBlock(&newClassOp.getBody());
     rewriter.setInsertionPointToStart(&newClassOp.getBody().front());
 
     auto argAttrs = funcOp.getArgAttrs();
     for (auto [idx, val] : llvm::enumerate(funcOp.getArguments())) {
       StringAttr fieldName;
-      Attribute argAttr = nullptr;
 
       fieldName = rewriter.getStringAttr("fieldName" + std::to_string(idx));
-      if (argAttrs && idx < argAttrs->size())
-        argAttr = (*argAttrs)[idx];
-
+      if (argAttrs && idx < argAttrs->size()) {
+        mlir::DictionaryAttr dictAttr =
+            dyn_cast_or_null<mlir::DictionaryAttr>((*argAttrs)[idx]);
+        const mlir::Attribute namedAttribute =
+            dictAttr.getNamed(attributeName)->getValue();
+
+        auto name =
+            cast<mlir::StringAttr>(cast<mlir::ArrayAttr>(namedAttribute)[0]);
+        bufferFieldAttrs.push_back(name);
+      }
       TypeAttr typeAttr = TypeAttr::get(val.getType());
       fields.push_back({fieldName, typeAttr});
       rewriter.create<emitc::FieldOp>(funcOp.getLoc(), fieldName, typeAttr,
-                                      argAttr);
+                                      nullptr);
+    }
+
+    if (!bufferFieldAttrs.empty()) {
+      ArrayAttr fieldsArrayAttr = rewriter.getArrayAttr(bufferFieldAttrs);
+      rewriter.create<emitc::BufferMapOp>(funcOp.getLoc(), "reflection_map",
+                                          fieldsArrayAttr);
     }
 
     rewriter.setInsertionPointToEnd(&newClassOp.getBody().front());

>From ec067c786d205206cfaf4ea3b5d94df9fce68e92 Mon Sep 17 00:00:00 2001
From: Jaddyen <ajaden at google.com>
Date: Fri, 27 Jun 2025 18:26:28 +0000
Subject: [PATCH 2/2] Remove sym_name

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td      | 16 +++++++---------
 .../Dialect/EmitC/Transforms/WrapFuncInClass.cpp |  3 +--
 2 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 69076261ddd2d..919648dc7fb1d 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1692,16 +1692,16 @@ def BufferMapOp
     Example:
 
     ```mlir
-    emitc.buffer_map reflection_map [ @field1, @field2, @field3 ]
+    emitc.buffer_map [ @field1, @field2, @field3 ]
     ```
 
     This generates C++ code like:
 
     ```cpp
-    const std::map<std::string, char*> _reflection_map {
-      { "field1", reinterpret_cast<char*>(&field1) },
-      { "field2", reinterpret_cast<char*>(&field2) },
-      { "field3", reinterpret_cast<char*>(&field3) }
+    const std::map<std::string, char*> _buffer_map {
+      { "some_feature", reinterpret_cast<char*>(&some_feature) },
+      { "another_feature", reinterpret_cast<char*>(&another_feature) },
+      { "input_tense", reinterpret_cast<char*>(&input_tense) }
     };
     
     char* getBufferForName(const std::string& name) const {
@@ -1711,12 +1711,10 @@ def BufferMapOp
     ```
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name,
-      Arg<OptionalAttr<ArrayAttr>, "field names">:$fields);
+  let arguments = (ins Arg<OptionalAttr<ArrayAttr>, "field names">:$fields);
 
   let results = (outs);
-  let builders = [];
-  let assemblyFormat = "$sym_name  $fields attr-dict";
+  let assemblyFormat = "$fields attr-dict";
 }
 
 #endif // MLIR_DIALECT_EMITC_IR_EMITC
diff --git a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
index 4f2ecfb640d2a..b4fd50fa4fd95 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
@@ -80,8 +80,7 @@ class WrapFuncInClass : public OpRewritePattern<emitc::FuncOp> {
 
     if (!bufferFieldAttrs.empty()) {
       ArrayAttr fieldsArrayAttr = rewriter.getArrayAttr(bufferFieldAttrs);
-      rewriter.create<emitc::BufferMapOp>(funcOp.getLoc(), "reflection_map",
-                                          fieldsArrayAttr);
+      rewriter.create<emitc::BufferMapOp>(funcOp.getLoc(), fieldsArrayAttr);
     }
 
     rewriter.setInsertionPointToEnd(&newClassOp.getBody().front());



More information about the Mlir-commits mailing list