[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