[Mlir-commits] [mlir] [mlir][EmitC] Create a pass to add a reflection map to a class (PR #205464)
Bhavesh M
llvmlistbot at llvm.org
Mon Jul 6 12:50:54 PDT 2026
https://github.com/beamandala updated https://github.com/llvm/llvm-project/pull/205464
>From ce76fb7aa44a3320fdb45acc6e011758ed3afc77 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 1/6] [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 60d4d88576ecc713db0a2cf7ae184308c3cd745f 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 2/6] [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 b6af183f868036d825f4133a2671b67e5a9b643e 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 3/6] [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 8d7429a032c6e0d91d1857ff5ef5c324ac35999f 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 4/6] 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 266f21b78f0534b8bdf8354587cc883a81464a48 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 5/6] 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 044316b2985c962d2a4b29fa700cf6974b5b7d60 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 6/6] 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..b93138987719b 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
More information about the Mlir-commits
mailing list