Skip to content

[mlir][emitc] Adding a reflection option to the class #146133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

Jaddyen
Copy link
Contributor

@Jaddyen Jaddyen commented Jun 27, 2025

This new op would allow us to add reflection to the classes we now have in emitc.

@Jaddyen Jaddyen marked this pull request as ready for review July 1, 2025 21:08
@llvmbot
Copy link
Member

llvmbot commented Jul 1, 2025

@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-emitc

Author: Jaden Angella (Jaddyen)

Changes

This new op would allow us to add reflection to the classes we now have in emitc.


Full diff: https://github.com/llvm/llvm-project/pull/146133.diff

4 Files Affected:

  • (modified) mlir/include/mlir/Dialect/EmitC/IR/EmitC.td (+40-5)
  • (modified) mlir/lib/Dialect/EmitC/IR/EmitC.cpp (-3)
  • (modified) mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp (+16-6)
  • (modified) mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir (+4-3)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 91ee89919e58e..f5d163a45b08e 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1644,17 +1644,14 @@ def EmitC_FieldOp : EmitC_Op<"field", [Symbol]> {
     Example:
 
     ```mlir
-    // Example with an attribute:
-    emitc.field @fieldName0 : !emitc.array<1xf32>  {emitc.opaque = "another_feature"}
     // Example with no attribute:
     emitc.field @fieldName0 : !emitc.array<1xf32>
     ```
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name, TypeAttr:$type,
-      OptionalAttr<AnyAttr>:$attrs);
+  let arguments = (ins SymbolNameAttr:$sym_name, TypeAttr:$type);
 
-  let assemblyFormat = [{ $sym_name `:` $type ($attrs^)? attr-dict}];
+  let assemblyFormat = [{ $sym_name `:` $type attr-dict}];
 
   let hasVerifier = 1;
 }
@@ -1679,4 +1676,42 @@ 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 [ @field1, @field2, @field3 ]
+    ```
+
+    This generates C++ code like:
+
+    ```cpp
+    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 {
+      auto it = _reflection_map.find(name);
+      return (it == _reflection_map.end()) ? nullptr : it->second;
+    }
+    ```
+  }];
+
+  let arguments = (ins Arg<OptionalAttr<ArrayAttr>, "field names">:$fields);
+
+  let results = (outs);
+  let assemblyFormat = "$fields attr-dict";
+}
+
 #endif // MLIR_DIALECT_EMITC_IR_EMITC
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 27298e892e599..b0ff4ed7bb688 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -1415,9 +1415,6 @@ LogicalResult FieldOp::verify() {
   if (!symName || symName.getValue().empty())
     return emitOpError("field must have a non-empty symbol name");
 
-  if (!getAttrs())
-    return success();
-
   return success();
 }
 
diff --git a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
index 17d436f6df028..3e627708c6f3e 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
@@ -53,22 +53,32 @@ 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>(namedAttribute);
+        bufferFieldAttrs.push_back(name);
+      }
       TypeAttr typeAttr = TypeAttr::get(val.getType());
       fields.push_back({fieldName, typeAttr});
-      rewriter.create<emitc::FieldOp>(funcOp.getLoc(), fieldName, typeAttr,
-                                      argAttr);
+      rewriter.create<emitc::FieldOp>(funcOp.getLoc(), fieldName, typeAttr);
+    }
+
+    if (!bufferFieldAttrs.empty()) {
+      ArrayAttr fieldsArrayAttr = rewriter.getArrayAttr(bufferFieldAttrs);
+      rewriter.create<emitc::BufferMapOp>(funcOp.getLoc(), fieldsArrayAttr);
     }
 
     rewriter.setInsertionPointToEnd(&newClassOp.getBody().front());
diff --git a/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir b/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir
index c67a0c197fcd9..364840adb3e5f 100644
--- a/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir
+++ b/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir
@@ -19,9 +19,10 @@ module attributes { } {
 
 // CHECK: module {
 // CHECK-NEXT:   emitc.class @modelClass {
-// CHECK-NEXT:     emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.name_hint = "another_feature"}
-// CHECK-NEXT:     emitc.field @fieldName1 : !emitc.array<1xf32>  {emitc.name_hint = "some_feature"}
-// CHECK-NEXT:     emitc.field @fieldName2 : !emitc.array<1xf32>  {emitc.name_hint = "output_0"}
+// CHECK-NEXT:     emitc.field @fieldName0 : !emitc.array<1xf32> 
+// CHECK-NEXT:     emitc.field @fieldName1 : !emitc.array<1xf32>  
+// CHECK-NEXT:     emitc.field @fieldName2 : !emitc.array<1xf32>  
+// CHECK-NEXT:     emitc.buffer_map ["another_feature", "some_feature", "output_0"]
 // CHECK-NEXT:     emitc.func @execute() {
 // CHECK-NEXT:       get_field @fieldName0 : !emitc.array<1xf32>
 // CHECK-NEXT:       get_field @fieldName1 : !emitc.array<1xf32>

@Jaddyen Jaddyen mentioned this pull request Jul 1, 2025
@@ -1679,4 +1676,42 @@ def EmitC_GetFieldOp
let assemblyFormat = "$field_name `:` type($result) attr-dict";
}

def BufferMapOp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that this operation is far more high level than all other emitc operations, so it doesn't fit into the dialect.
If you want to generate code like shown below, you could create a buffer_map op in a different dialect, and create a lowering pass that turns it into existing emitc ops corresponding to the source code shown below (we have ops for global variables, functions, calls, ternaries).

If you are missing any low level ops (e.g. do we have a reinterpret_cast one?), then those would be better additions to the emitc dialect.

@Jaddyen Jaddyen marked this pull request as draft July 2, 2025 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants