[Mlir-commits] [mlir] 33e8502 - [mlir][EmitC] Make `GlobalOps` `FieldOps` in wrap-emitc-func-in-class pass (#203641)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Jun 22 14:07:47 PDT 2026


Author: Bhavesh M
Date: 2026-06-22T14:07:42-07:00
New Revision: 33e8502336c76591172fbae37209d31c1e90c7c6

URL: https://github.com/llvm/llvm-project/commit/33e8502336c76591172fbae37209d31c1e90c7c6
DIFF: https://github.com/llvm/llvm-project/commit/33e8502336c76591172fbae37209d31c1e90c7c6.diff

LOG: [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)

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
    mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
    mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
    mlir/test/Dialect/EmitC/wrap-func-in-class.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
index 40ecef33448d7..c34c3303a6ab3 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Passes.td
@@ -20,7 +20,7 @@ def FormExpressionsPass : Pass<"form-expressions"> {
   let dependentDialects = ["emitc::EmitCDialect"];
 }
 
-def WrapFuncInClassPass : Pass<"wrap-emitc-func-in-class"> {
+def WrapFuncInClassPass : Pass<"wrap-emitc-func-in-class", "ModuleOp"> {
   let summary = "Wrap functions in classes, using arguments as fields.";
   let description = [{
     This pass transforms `emitc.func` operations into `emitc.class` operations.

diff  --git a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
index 962bdb3c032bf..791e545a8edcf 100644
--- a/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/EmitC/Transforms/Transforms.h
@@ -11,6 +11,7 @@
 
 #include "mlir/Dialect/EmitC/IR/EmitC.h"
 #include "mlir/IR/PatternMatch.h"
+#include "llvm/ADT/DenseMap.h"
 
 namespace mlir {
 namespace emitc {
@@ -32,7 +33,9 @@ void populateExpressionPatterns(RewritePatternSet &patterns);
 // The WrapFuncInClass pass.
 //===----------------------------------------------------------------------===//
 
-void populateWrapFuncInClass(RewritePatternSet &patterns, StringRef fName);
+void populateWrapFuncInClass(
+    RewritePatternSet &patterns, StringRef funcName,
+    DenseMap<FuncOp, llvm::DenseSet<GlobalOp>> &globalsToMove);
 
 } // namespace emitc
 } // namespace mlir

diff  --git a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
index fc8acd616ba70..aaceddb1e1b61 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
@@ -13,7 +13,10 @@
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/BuiltinAttributes.h"
 #include "mlir/IR/PatternMatch.h"
+#include "mlir/IR/SymbolTable.h"
 #include "mlir/Transforms/WalkPatternRewriteDriver.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
 
 using namespace mlir;
 using namespace emitc;
@@ -28,12 +31,32 @@ struct WrapFuncInClassPass
     : public impl::WrapFuncInClassPassBase<WrapFuncInClassPass> {
   using WrapFuncInClassPassBase::WrapFuncInClassPassBase;
   void runOnOperation() override {
-    Operation *rootOp = getOperation();
+    mlir::ModuleOp moduleOp = getOperation();
+
+    DenseMap<FuncOp, llvm::DenseSet<GlobalOp>> globalsUsedByFuncs;
+
+    SymbolTableCollection symbolTable;
+    moduleOp.walk([&globalsUsedByFuncs, &symbolTable](FuncOp funcOp) {
+      funcOp.walk([&globalsUsedByFuncs, &symbolTable,
+                   &funcOp](GetGlobalOp getGlobalOp) {
+        if (auto globalOp = symbolTable.lookupNearestSymbolFrom<GlobalOp>(
+                getGlobalOp, getGlobalOp.getNameAttr())) {
+          globalsUsedByFuncs[funcOp].insert(globalOp);
+        }
+      });
+    });
 
     RewritePatternSet patterns(&getContext());
-    populateWrapFuncInClass(patterns, funcName);
+    populateWrapFuncInClass(patterns, funcName, globalsUsedByFuncs);
+
+    walkAndApplyPatterns(moduleOp, std::move(patterns));
 
-    walkAndApplyPatterns(rootOp, std::move(patterns));
+    DenseSet<GlobalOp> globalsToErase;
+    for (auto &[_, globals] : globalsUsedByFuncs)
+      globalsToErase.insert_range(globals);
+
+    for (GlobalOp globalOp : globalsToErase)
+      globalOp.erase();
   }
 };
 
@@ -41,12 +64,15 @@ struct WrapFuncInClassPass
 } // namespace emitc
 } // namespace mlir
 
-class WrapFuncInClass : public OpRewritePattern<emitc::FuncOp> {
+class WrapFuncInClass : public OpRewritePattern<FuncOp> {
 public:
-  WrapFuncInClass(MLIRContext *context, StringRef funcName)
-      : OpRewritePattern<emitc::FuncOp>(context), funcName(funcName) {}
+  WrapFuncInClass(
+      MLIRContext *context, StringRef funcName,
+      const DenseMap<FuncOp, llvm::DenseSet<GlobalOp>> &globalsToMove)
+      : OpRewritePattern<FuncOp>(context), funcName(funcName),
+        globalsToMove(globalsToMove) {}
 
-  LogicalResult matchAndRewrite(emitc::FuncOp funcOp,
+  LogicalResult matchAndRewrite(FuncOp funcOp,
                                 PatternRewriter &rewriter) const override {
 
     auto className = funcOp.getSymNameAttr().str() + "Class";
@@ -64,19 +90,26 @@ class WrapFuncInClass : public OpRewritePattern<emitc::FuncOp> {
       TypeAttr typeAttr = TypeAttr::get(val.getType());
       fields.push_back({fieldName, typeAttr});
 
-      FieldOp fieldop = emitc::FieldOp::create(rewriter, funcOp->getLoc(),
-                                               fieldName, typeAttr, nullptr);
+      FieldOp fieldop = FieldOp::create(rewriter, funcOp->getLoc(), fieldName,
+                                        typeAttr, nullptr);
 
       if (argAttrs && idx < argAttrs->size()) {
         fieldop->setDiscardableAttrs(funcOp.getArgAttrDict(idx));
       }
     }
 
+    auto globalsIt = globalsToMove.find(funcOp);
+    if (globalsIt != globalsToMove.end()) {
+      for (auto global : globalsIt->second) {
+        FieldOp::create(rewriter, funcOp->getLoc(), global.getSymNameAttr(),
+                        global.getTypeAttr(), global.getInitialValueAttr());
+      }
+    }
+
     rewriter.setInsertionPointToEnd(&newClassOp.getBody().front());
     FunctionType funcType = funcOp.getFunctionType();
     Location loc = funcOp.getLoc();
-    FuncOp newFuncOp =
-        emitc::FuncOp::create(rewriter, loc, (funcName), funcType);
+    FuncOp newFuncOp = FuncOp::create(rewriter, loc, (funcName), funcType);
 
     rewriter.createBlock(&newFuncOp.getBody());
     newFuncOp.getBody().takeBody(funcOp.getBody());
@@ -86,7 +119,7 @@ class WrapFuncInClass : public OpRewritePattern<emitc::FuncOp> {
     newArguments.reserve(fields.size());
     for (auto &[fieldName, attr] : fields) {
       GetFieldOp arg =
-          emitc::GetFieldOp::create(rewriter, loc, attr.getValue(), fieldName);
+          GetFieldOp::create(rewriter, loc, attr.getValue(), fieldName);
       newArguments.push_back(arg);
     }
 
@@ -99,6 +132,14 @@ class WrapFuncInClass : public OpRewritePattern<emitc::FuncOp> {
     if (failed(newFuncOp.eraseArguments(argsToErase)))
       newFuncOp->emitOpError("failed to erase all arguments using BitVector");
 
+    newFuncOp.walk([&](GetGlobalOp getGlobalOp) {
+      rewriter.setInsertionPoint(getGlobalOp);
+      GetFieldOp getFieldOp =
+          GetFieldOp::create(rewriter, getGlobalOp.getLoc(),
+                             getGlobalOp.getType(), getGlobalOp.getNameAttr());
+      rewriter.replaceOp(getGlobalOp, getFieldOp);
+    });
+
     rewriter.replaceOp(funcOp, newClassOp);
     return success();
   }
@@ -107,9 +148,14 @@ class WrapFuncInClass : public OpRewritePattern<emitc::FuncOp> {
   /// Name of the newly generated member function with body matching the input
   /// function.
   std::string funcName;
+
+  /// Map of FuncOp and the GlobalOps it uses which need to be moved into the
+  /// ClassOp wrapper.
+  DenseMap<FuncOp, llvm::DenseSet<GlobalOp>> globalsToMove;
 };
 
-void mlir::emitc::populateWrapFuncInClass(RewritePatternSet &patterns,
-                                          StringRef funcName) {
-  patterns.add<WrapFuncInClass>(patterns.getContext(), funcName);
+void mlir::emitc::populateWrapFuncInClass(
+    RewritePatternSet &patterns, StringRef funcName,
+    DenseMap<FuncOp, DenseSet<GlobalOp>> &globalsToMove) {
+  patterns.add<WrapFuncInClass>(patterns.getContext(), funcName, globalsToMove);
 }

diff  --git a/mlir/test/Dialect/EmitC/wrap-func-in-class.mlir b/mlir/test/Dialect/EmitC/wrap-func-in-class.mlir
index cb5f99d31e9da..7d5b3d30a64ba 100644
--- a/mlir/test/Dialect/EmitC/wrap-func-in-class.mlir
+++ b/mlir/test/Dialect/EmitC/wrap-func-in-class.mlir
@@ -58,3 +58,153 @@ module attributes { } {
 
 // EXECUTE-NOT: operator
 // EXECUTE: execute()
+
+// -----
+// Tests that GlobalOps are moved into the ClassOp wrapper correctly as fields
+
+module attributes { } {
+  emitc.global static const @global_arr : !emitc.array<1xi8> = dense<0>
+  emitc.func @foo() {
+    %0 = emitc.get_global @global_arr : !emitc.array<1xi8>
+    emitc.return
+  }
+}
+
+// CHECK:   emitc.class @fooClass {
+// CHECK:     emitc.field @global_arr : !emitc.array<1xi8> = dense<0>
+// CHECK:     emitc.func @"operator()"() {
+// CHECK:       %0 = get_field @global_arr : !emitc.array<1xi8>
+// CHECK:       return
+// CHECK:     }
+// CHECK:   }
+
+// EXECUTE-NOT: operator
+// EXECUTE: execute()
+
+// -----
+// Tests that only GlobalOps that are used within a function are moved into the
+// ClassOp wrapper as fields
+
+module attributes { } {
+  emitc.global static const @global_arr : !emitc.array<1xi8> = dense<0>
+  emitc.global static const @global_arr2 : !emitc.array<1xi8> = dense<0>
+  emitc.func @foo() {
+    %0 = emitc.get_global @global_arr : !emitc.array<1xi8>
+    emitc.return
+  }
+}
+
+// CHECK:   module {
+// CHECK-NEXT:     emitc.global static const @global_arr2 : !emitc.array<1xi8> = dense<0>
+// CHECK-NEXT:     emitc.class @fooClass {
+// CHECK-NEXT:       emitc.field @global_arr : !emitc.array<1xi8> = dense<0>
+// CHECK-NEXT:       emitc.func @"operator()"() {
+// CHECK-NEXT:         %0 = get_field @global_arr : !emitc.array<1xi8>
+// CHECK-NEXT:         return
+// CHECK-NEXT:       }
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+
+// EXECUTE-NOT: operator
+// EXECUTE: execute()
+
+// -----
+// Tests that when multiple functions use 
diff erent globals, only the used globals
+// are moved into their respective ClassOp wrappers as fields.
+
+module attributes { } {
+  emitc.global static const @global_arr1 : !emitc.array<1xi8> = dense<0>
+  emitc.global static const @global_arr2 : !emitc.array<1xi8> = dense<0>
+  emitc.global static const @global_arr3 : !emitc.array<1xi8> = dense<0>
+  emitc.func @foo() {
+    %0 = emitc.get_global @global_arr1 : !emitc.array<1xi8>
+    emitc.return
+  }
+  emitc.func @bar() {
+    %0 = emitc.get_global @global_arr2 : !emitc.array<1xi8>
+    emitc.return
+  }
+}
+
+// CHECK:   module {
+// CHECK-NEXT:     emitc.global static const @global_arr3 : !emitc.array<1xi8> = dense<0>
+// CHECK-NEXT:     emitc.class @fooClass {
+// CHECK-NEXT:       emitc.field @global_arr1 : !emitc.array<1xi8> = dense<0>
+// CHECK-NEXT:       emitc.func @"operator()"() {
+// CHECK-NEXT:         %0 = get_field @global_arr1 : !emitc.array<1xi8>
+// CHECK-NEXT:         return
+// CHECK-NEXT:       }
+// CHECK-NEXT:     }
+// CHECK-NEXT:     emitc.class @barClass {
+// CHECK-NEXT:       emitc.field @global_arr2 : !emitc.array<1xi8> = dense<0>
+// CHECK-NEXT:       emitc.func @"operator()"() {
+// CHECK-NEXT:         %0 = get_field @global_arr2 : !emitc.array<1xi8>
+// CHECK-NEXT:         return
+// CHECK-NEXT:       }
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+
+// EXECUTE-NOT: operator
+// EXECUTE: execute()
+
+// -----
+// Tests that when multiple functions use the same global, the global is moved
+// into each ClassOp wrapper as a field and erased from the module.
+
+module attributes { } {
+  emitc.global static const @global_arr : !emitc.array<1xi8> = dense<0>
+  emitc.func @foo() {
+    %0 = emitc.get_global @global_arr : !emitc.array<1xi8>
+    emitc.return
+  }
+  emitc.func @bar() {
+    %0 = emitc.get_global @global_arr : !emitc.array<1xi8>
+    emitc.return
+  }
+}
+
+// CHECK:   module {
+// CHECK-NEXT:     emitc.class @fooClass {
+// CHECK-NEXT:       emitc.field @global_arr : !emitc.array<1xi8> = dense<0>
+// CHECK-NEXT:       emitc.func @"operator()"() {
+// CHECK-NEXT:         %0 = get_field @global_arr : !emitc.array<1xi8>
+// CHECK-NEXT:         return
+// CHECK-NEXT:       }
+// CHECK-NEXT:     }
+// CHECK-NEXT:     emitc.class @barClass {
+// CHECK-NEXT:       emitc.field @global_arr : !emitc.array<1xi8> = dense<0>
+// CHECK-NEXT:       emitc.func @"operator()"() {
+// CHECK-NEXT:         %0 = get_field @global_arr : !emitc.array<1xi8>
+// CHECK-NEXT:         return
+// CHECK-NEXT:       }
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+
+// EXECUTE-NOT: operator
+// EXECUTE: execute()
+
+// -----
+// Tests that multiple uses of the same global in a function result in a single field.
+
+module attributes { } {
+  emitc.global static const @global_arr : !emitc.array<1xi8> = dense<0>
+  emitc.func @foo() {
+    %0 = emitc.get_global @global_arr : !emitc.array<1xi8>
+    %1 = emitc.get_global @global_arr : !emitc.array<1xi8>
+    emitc.return
+  }
+}
+
+// CHECK:   module {
+// CHECK-NEXT:     emitc.class @fooClass {
+// CHECK-NEXT:       emitc.field @global_arr : !emitc.array<1xi8> = dense<0>
+// CHECK-NEXT:       emitc.func @"operator()"() {
+// CHECK-NEXT:         %0 = get_field @global_arr : !emitc.array<1xi8>
+// CHECK-NEXT:         %1 = get_field @global_arr : !emitc.array<1xi8>
+// CHECK-NEXT:         return
+// CHECK-NEXT:       }
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+
+// EXECUTE-NOT: operator
+// EXECUTE: execute()


        


More information about the Mlir-commits mailing list