[Mlir-commits] [mlir] [OpenMP][mlir] Add Groupprivate op in omp dialect. (PR #162704)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Oct 24 10:54:41 PDT 2025


https://github.com/skc7 updated https://github.com/llvm/llvm-project/pull/162704

>From 38b566ff705e231f7cbd44e4d2ffbe58cffaae2e Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Fri, 26 Sep 2025 10:06:26 +0530
Subject: [PATCH 1/3] [OpenMP][mlir] Add Groupprivate op in omp dialect.

---
 mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 27 ++++++++++++++
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      | 37 +++++++++++++++++++
 mlir/test/Dialect/OpenMP/ops.mlir             | 19 ++++++++++
 3 files changed, 83 insertions(+)

diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index b73091ea0ca536..6a4ce490b0a823 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2224,4 +2224,31 @@ def WorkdistributeOp : OpenMP_Op<"workdistribute"> {
   let assemblyFormat = "$region attr-dict";
 }
 
+//===----------------------------------------------------------------------===//
+// [6.0] groupprivate Directive
+//===----------------------------------------------------------------------===//
+
+def GroupprivateOp : OpenMP_Op<"groupprivate",
+                      [AllTypesMatch<["sym_addr", "gp_addr"]>]> {
+  let summary = "groupprivate directive";
+  let description = [{
+    The groupprivate directive specifies that variables are replicated, with
+    each group having its own copy.
+
+    This operation takes in the address of a symbol that represents the original
+    variable and returns the address of its groupprivate copy. All occurrences of
+    groupprivate variables in a parallel region should use the groupprivate copy
+    returned by this operation.
+
+    The `sym_addr` refers to the address of the symbol, which is a pointer to
+    the original variable.
+  }];
+
+  let arguments = (ins OpenMP_PointerLikeType:$sym_addr);
+  let results = (outs OpenMP_PointerLikeType:$gp_addr);
+  let assemblyFormat = [{
+    $sym_addr `:` type($sym_addr) `->` type($gp_addr) attr-dict
+  }];
+}
+
 #endif // OPENMP_OPS
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 9fcb02eb4be3d0..3fac0f02a400ea 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -6092,6 +6092,40 @@ convertTargetFreeMemOp(Operation &opInst, llvm::IRBuilderBase &builder,
   return success();
 }
 
+/// Converts an OpenMP Groupprivate operation into LLVM IR.
+static LogicalResult
+convertOmpGroupprivate(Operation &opInst, llvm::IRBuilderBase &builder,
+                        LLVM::ModuleTranslation &moduleTranslation) {
+  llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
+  llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
+  auto groupprivateOp = cast<omp::GroupprivateOp>(opInst);
+
+  if (failed(checkImplementationStatus(opInst)))
+    return failure();
+
+  Value symAddr = groupprivateOp.getSymAddr();
+  auto *symOp = symAddr.getDefiningOp();
+
+  if (auto asCast = dyn_cast<LLVM::AddrSpaceCastOp>(symOp))
+    symOp = asCast.getOperand().getDefiningOp();
+
+  if (!isa<LLVM::AddressOfOp>(symOp))
+    return opInst.emitError("Addressing symbol not found");
+  LLVM::AddressOfOp addressOfOp = dyn_cast<LLVM::AddressOfOp>(symOp);
+
+  LLVM::GlobalOp global =
+      addressOfOp.getGlobal(moduleTranslation.symbolTable());
+  llvm::GlobalValue *globalValue = moduleTranslation.lookupGlobal(global);
+
+  if (!ompBuilder->Config.isTargetDevice()) {
+    llvm_unreachable("NYI");
+  } else {
+    moduleTranslation.mapValue(opInst.getResult(0), globalValue);
+  }
+
+  return success();
+}
+
 /// Given an OpenMP MLIR operation, create the corresponding LLVM IR (including
 /// OpenMP runtime calls).
 static LogicalResult
@@ -6275,6 +6309,9 @@ convertHostOrTargetOperation(Operation *op, llvm::IRBuilderBase &builder,
           .Case([&](omp::TargetFreeMemOp) {
             return convertTargetFreeMemOp(*op, builder, moduleTranslation);
           })
+          .Case([&](omp::GroupprivateOp) {
+            return convertOmpGroupprivate(*op, builder, moduleTranslation);
+          })
           .Default([&](Operation *inst) {
             return inst->emitError()
                    << "not yet implemented: " << inst->getName();
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index cbd863f88fd1f1..3f4ff9e42398fb 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -3321,3 +3321,22 @@ func.func @omp_workdistribute() {
   }
   return
 }
+
+llvm.mlir.global internal @_QFgpEx() : i32
+
+func.func @omp_groupprivate() {
+  %0 = arith.constant 1 : i32
+  %1 = arith.constant 2 : i32
+  // CHECK: [[ARG0:%.*]] = llvm.mlir.addressof @_QFgpEx : !llvm.ptr
+  %global_addr = llvm.mlir.addressof @_QFgpEx : !llvm.ptr
+  omp.teams {
+    // CHECK: {{.*}} = omp.groupprivate [[ARG0]] : !llvm.ptr -> !llvm.ptr
+    %group_private_addr_in_teams = omp.groupprivate %global_addr : !llvm.ptr -> !llvm.ptr
+    llvm.store %0, %group_private_addr_in_teams : i32, !llvm.ptr
+    omp.terminator
+  }
+  // CHECK: {{.*}} = omp.groupprivate [[ARG0]] : !llvm.ptr -> !llvm.ptr
+  %group_private_addr_after_teams = omp.groupprivate %global_addr : !llvm.ptr -> !llvm.ptr
+  llvm.store %1, %group_private_addr_after_teams : i32, !llvm.ptr
+  return
+}

>From daee9e92ce162e1fcafef5dc5bc678aceef9c512 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Fri, 10 Oct 2025 22:17:49 +0530
Subject: [PATCH 2/3] update llvmir lowering and tests

---
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      | 25 ++++++++-----
 mlir/test/Target/LLVMIR/openmp-llvm.mlir      | 36 +++++++++++++++++++
 2 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 3fac0f02a400ea..591c8db7a9785d 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -5994,7 +5994,7 @@ static bool isTargetDeviceOp(Operation *op) {
   // by taking it in as an operand, so we must always lower these in
   // some manner or result in an ICE (whether they end up in a no-op
   // or otherwise).
-  if (mlir::isa<omp::ThreadprivateOp>(op))
+  if (mlir::isa<omp::ThreadprivateOp, omp::GroupprivateOp>(op))
     return true;
 
   if (mlir::isa<omp::TargetAllocMemOp>(op) ||
@@ -6095,8 +6095,7 @@ convertTargetFreeMemOp(Operation &opInst, llvm::IRBuilderBase &builder,
 /// Converts an OpenMP Groupprivate operation into LLVM IR.
 static LogicalResult
 convertOmpGroupprivate(Operation &opInst, llvm::IRBuilderBase &builder,
-                        LLVM::ModuleTranslation &moduleTranslation) {
-  llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
+                       LLVM::ModuleTranslation &moduleTranslation) {
   llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
   auto groupprivateOp = cast<omp::GroupprivateOp>(opInst);
 
@@ -6117,12 +6116,20 @@ convertOmpGroupprivate(Operation &opInst, llvm::IRBuilderBase &builder,
       addressOfOp.getGlobal(moduleTranslation.symbolTable());
   llvm::GlobalValue *globalValue = moduleTranslation.lookupGlobal(global);
 
-  if (!ompBuilder->Config.isTargetDevice()) {
-    llvm_unreachable("NYI");
-  } else {
-    moduleTranslation.mapValue(opInst.getResult(0), globalValue);
-  }
-
+  // Get the size of the variable
+  llvm::Type *varType = globalValue->getValueType();
+  llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
+  llvm::DataLayout DL = llvmModule->getDataLayout();
+  uint64_t typeSize = DL.getTypeAllocSize(varType);
+  // Call omp_alloc_shared to allocate memory for groupprivate variable.
+  llvm::FunctionCallee allocSharedFn = ompBuilder->getOrCreateRuntimeFunction(
+      *llvmModule, llvm::omp::OMPRTL___kmpc_alloc_shared);
+  // Call runtime to allocate shared memory for this group
+  llvm::Value *groupPrivatePtr =
+      builder.CreateCall(allocSharedFn, {builder.getInt64(typeSize)});
+  groupPrivatePtr =
+      builder.CreateBitCast(groupPrivatePtr, globalValue->getType());
+  moduleTranslation.mapValue(opInst.getResult(0), groupPrivatePtr);
   return success();
 }
 
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index 8bd33a382197ef..d7c43a88349e30 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -3449,3 +3449,39 @@ llvm.func @nested_task_with_deps() {
 
 // CHECK:         ret void
 // CHECK:       }
+
+// -----
+
+// CHECK: @_QFsubEx = internal global i32 undef
+
+// CHECK-LABEL: @omp_groupprivate
+llvm.func @omp_groupprivate() {
+// CHECK:  [[TMP1:%.*]] = call ptr @__kmpc_alloc_shared(i64 4)
+// CHECK:  store i32 1, ptr [[TMP1]], align 4
+
+// CHECK-LABEL: omp.teams.region{{.*}}
+// CHECK:  [[TMP2:%.*]] = call ptr @__kmpc_alloc_shared(i64 4)
+// CHECK:  store i32 2, ptr [[TMP2]], align 4
+
+  %0 = llvm.mlir.constant(1 : i32) : i32
+  %1 = llvm.mlir.constant(2 : i32) : i32
+  %2 = llvm.mlir.constant(3 : i32) : i32
+
+  %3 = llvm.mlir.addressof @_QFsubEx : !llvm.ptr
+  %4 = omp.groupprivate %3 : !llvm.ptr -> !llvm.ptr
+
+  llvm.store %0, %4 : i32, !llvm.ptr
+
+  omp.teams  {
+    %5 = omp.groupprivate %3 : !llvm.ptr -> !llvm.ptr
+    llvm.store %1, %5 : i32, !llvm.ptr
+    omp.terminator
+  }
+
+  llvm.store %2, %4 : i32, !llvm.ptr
+  llvm.return
+}
+
+llvm.mlir.global internal @_QFsubEx() : i32
+
+// -----

>From d7ebaaf8142d0777be8993de788d4d4bc3a909ea Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Fri, 24 Oct 2025 23:23:58 +0530
Subject: [PATCH 3/3] add device_type attr to groupprivate

---
 mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td |  7 ++-
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      | 61 ++++++++++++++-----
 mlir/test/Dialect/OpenMP/ops.mlir             | 26 +++++---
 3 files changed, 69 insertions(+), 25 deletions(-)

diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 6a4ce490b0a823..5afb281967571c 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2244,10 +2244,13 @@ def GroupprivateOp : OpenMP_Op<"groupprivate",
     the original variable.
   }];
 
-  let arguments = (ins OpenMP_PointerLikeType:$sym_addr);
+  let arguments = (ins
+    OpenMP_PointerLikeType:$sym_addr,
+    OptionalAttr<DeclareTargetDeviceTypeAttr>:$device_type
+  );
   let results = (outs OpenMP_PointerLikeType:$gp_addr);
   let assemblyFormat = [{
-    $sym_addr `:` type($sym_addr) `->` type($gp_addr) attr-dict
+    $sym_addr `:` type($sym_addr) ( `,` `device_type` $device_type^ )? `->` type($gp_addr) attr-dict
   }];
 }
 
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 591c8db7a9785d..fd314c0ae2a7ef 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -6101,6 +6101,28 @@ convertOmpGroupprivate(Operation &opInst, llvm::IRBuilderBase &builder,
 
   if (failed(checkImplementationStatus(opInst)))
     return failure();
+  
+  bool isTargetDevice = ompBuilder->Config.isTargetDevice();
+  auto deviceType = groupprivateOp.getDeviceType();
+  
+  // skip allocation based on device_type
+  bool shouldAllocate = true;
+  if (deviceType.has_value()) {    
+    switch (*deviceType) {
+    case mlir::omp::DeclareTargetDeviceType::host:
+      // Only allocate on host
+      shouldAllocate = !isTargetDevice;
+      break;
+    case mlir::omp::DeclareTargetDeviceType::nohost:
+      // Only allocate on device
+      shouldAllocate = isTargetDevice;
+      break;
+    case mlir::omp::DeclareTargetDeviceType::any:
+      // Allocate on both
+      shouldAllocate = true;
+      break;
+    }
+  }
 
   Value symAddr = groupprivateOp.getSymAddr();
   auto *symOp = symAddr.getDefiningOp();
@@ -6115,21 +6137,32 @@ convertOmpGroupprivate(Operation &opInst, llvm::IRBuilderBase &builder,
   LLVM::GlobalOp global =
       addressOfOp.getGlobal(moduleTranslation.symbolTable());
   llvm::GlobalValue *globalValue = moduleTranslation.lookupGlobal(global);
+  llvm::Value *resultPtr;
 
-  // Get the size of the variable
-  llvm::Type *varType = globalValue->getValueType();
-  llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
-  llvm::DataLayout DL = llvmModule->getDataLayout();
-  uint64_t typeSize = DL.getTypeAllocSize(varType);
-  // Call omp_alloc_shared to allocate memory for groupprivate variable.
-  llvm::FunctionCallee allocSharedFn = ompBuilder->getOrCreateRuntimeFunction(
-      *llvmModule, llvm::omp::OMPRTL___kmpc_alloc_shared);
-  // Call runtime to allocate shared memory for this group
-  llvm::Value *groupPrivatePtr =
-      builder.CreateCall(allocSharedFn, {builder.getInt64(typeSize)});
-  groupPrivatePtr =
-      builder.CreateBitCast(groupPrivatePtr, globalValue->getType());
-  moduleTranslation.mapValue(opInst.getResult(0), groupPrivatePtr);
+  if (shouldAllocate) {
+    // Get the size of the variable
+    llvm::Type *varType = globalValue->getValueType();
+    llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
+    llvm::DataLayout DL = llvmModule->getDataLayout();
+    uint64_t typeSize = DL.getTypeAllocSize(varType);
+    // Call omp_alloc_shared to allocate memory for groupprivate variable.
+    llvm::FunctionCallee allocSharedFn = ompBuilder->getOrCreateRuntimeFunction(
+        *llvmModule, llvm::omp::OMPRTL___kmpc_alloc_shared);
+    // Call runtime to allocate shared memory for this group
+    llvm::Value *groupPrivatePtr =
+        builder.CreateCall(allocSharedFn, {builder.getInt64(typeSize)});
+    resultPtr =
+        builder.CreateBitCast(groupPrivatePtr, globalValue->getType());
+  }
+  else {
+    // Use original global address when not allocating group-private storage
+    resultPtr = moduleTranslation.lookupValue(symAddr);
+    if (!resultPtr) {
+      // Fallback: create address-of for the global
+      resultPtr = builder.CreateBitCast(globalValue, globalValue->getType());
+    }
+  }
+  moduleTranslation.mapValue(opInst.getResult(0), resultPtr);
   return success();
 }
 
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 3f4ff9e42398fb..dc3062af1c1301 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -3324,19 +3324,27 @@ func.func @omp_workdistribute() {
 
 llvm.mlir.global internal @_QFgpEx() : i32
 
-func.func @omp_groupprivate() {
+// CHECK-LABEL: func.func @omp_groupprivate_device_type
+func.func @omp_groupprivate_device_type() {
   %0 = arith.constant 1 : i32
   %1 = arith.constant 2 : i32
   // CHECK: [[ARG0:%.*]] = llvm.mlir.addressof @_QFgpEx : !llvm.ptr
   %global_addr = llvm.mlir.addressof @_QFgpEx : !llvm.ptr
-  omp.teams {
-    // CHECK: {{.*}} = omp.groupprivate [[ARG0]] : !llvm.ptr -> !llvm.ptr
-    %group_private_addr_in_teams = omp.groupprivate %global_addr : !llvm.ptr -> !llvm.ptr
-    llvm.store %0, %group_private_addr_in_teams : i32, !llvm.ptr
-    omp.terminator
-  }
+
   // CHECK: {{.*}} = omp.groupprivate [[ARG0]] : !llvm.ptr -> !llvm.ptr
-  %group_private_addr_after_teams = omp.groupprivate %global_addr : !llvm.ptr -> !llvm.ptr
-  llvm.store %1, %group_private_addr_after_teams : i32, !llvm.ptr
+  %group_private_addr = omp.groupprivate %global_addr : !llvm.ptr -> !llvm.ptr
+
+  // CHECK: {{.*}} = omp.groupprivate [[ARG0]] : !llvm.ptr, device_type (any) -> !llvm.ptr
+  %group_private_any = omp.groupprivate %global_addr : !llvm.ptr, device_type(any) -> !llvm.ptr
+  llvm.store %1, %group_private_any : i32, !llvm.ptr
+
+  // CHECK: {{.*}} = omp.groupprivate [[ARG0]] : !llvm.ptr, device_type (host) -> !llvm.ptr
+  %group_private_host = omp.groupprivate %global_addr : !llvm.ptr, device_type(host) -> !llvm.ptr
+  llvm.store %1, %group_private_host : i32, !llvm.ptr
+
+  // CHECK: {{.*}} = omp.groupprivate [[ARG0]] : !llvm.ptr, device_type (nohost) -> !llvm.ptr
+  %group_private_nohost = omp.groupprivate %global_addr : !llvm.ptr, device_type(nohost) -> !llvm.ptr
+  llvm.store %1, %group_private_nohost : i32, !llvm.ptr
+
   return
 }



More information about the Mlir-commits mailing list