[Mlir-commits] [mlir] ab7217a - [acc][flang] Add isDeviceData APIs for device data detection (#176219)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Jan 15 12:56:34 PST 2026


Author: Razvan Lupusoru
Date: 2026-01-15T20:56:26Z
New Revision: ab7217a089fb031816991d91a7dc7ae8c609ce4f

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

LOG: [acc][flang] Add isDeviceData APIs for device data detection (#176219)

Add comprehensive APIs to detect device-resident data across OpenACC
type and operation interfaces. This enables passes to identify data that
is already on the device (e.g., CUF device/managed/constant memory, GPU
address spaces) and handle it appropriately.

New interface methods:
- PointerLikeType::isDeviceData(Value): Returns true if the pointer
points to device data.
- MappableType::isDeviceData(Value): Returns true if the variable
represents device data.
- GlobalVariableOpInterface::isDeviceData(): Returns true if the global
variable is device data.

New utilities in OpenACCUtils:
- acc::isDeviceValue(Value): Checks if a value represents device data by
querying type interfaces, PartialEntityAccessOpInterface for base
entities, and AddressOfGlobalOpInterface for global symbols.
- acc::isValidValueUse(Value, Region): Checks if a value is legal in an
OpenACC region by verifying it comes from a data operation, is only used
by private clauses, or is device data.

Updated isValidSymbolUse to check
GlobalVariableOpInterface::isDeviceData()
for symbols referencing device-resident globals.

FIR implementations check for CUF data attributes (device, managed,
constant, shared, unified) on operations, block arguments, and globals.
The implementation traces through fir.rebox, fir.embox, fir.declare,
hlfir.declare, and fir.address_of to find the underlying data source.

Memref implementations check for gpu::AddressSpaceAttr on the memref
type.

Updated ACCImplicitData to use acc::isDeviceValue for generating
acc.deviceptr clauses for device-resident data instead of
copyin/copyout.

Updated OpenACCSupport::isValidValueUse to fallback to the new
acc::isValidValueUse utility.

Added: 
    

Modified: 
    flang/include/flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h
    flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h
    flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
    flang/lib/Optimizer/Dialect/CUF/Attributes/CUFAttr.cpp
    flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp
    flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
    flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
    flang/test/Transforms/OpenACC/acc-implicit-data.fir
    mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
    mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td
    mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
    mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
    mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
    mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
    mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
    mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
    mlir/test/Dialect/OpenACC/acc-implicit-data.mlir
    mlir/unittests/Dialect/OpenACC/CMakeLists.txt
    mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h b/flang/include/flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h
index 4a250d1cc6c54..17b92501727aa 100644
--- a/flang/include/flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h
+++ b/flang/include/flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h
@@ -112,6 +112,14 @@ cuf::DataAttributeAttr getDataAttr(mlir::Operation *op);
 /// Returns true if the operation has a data attribute with the given value.
 bool hasDataAttr(mlir::Operation *op, cuf::DataAttribute value);
 
+/// Check if a CUDA data attribute represents device data.
+/// Returns true for Device, Managed, Constant, Shared, and Unified attributes.
+bool isDeviceDataAttribute(cuf::DataAttribute attr);
+
+/// Returns true if the operation has a `cuf::DataAttributeAttr`
+/// whose value returns true for `isDeviceDataAttribute`.
+bool hasDeviceDataAttr(mlir::Operation *op);
+
 } // namespace cuf
 
 #endif // FORTRAN_OPTIMIZER_DIALECT_CUF_CUFATTR_H

diff  --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h
index c6f52bbd0c64b..b0252fee7b5a6 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h
@@ -66,6 +66,7 @@ struct GlobalVariableModel
           GlobalVariableModel, fir::GlobalOp> {
   bool isConstant(mlir::Operation *op) const;
   mlir::Region *getInitRegion(mlir::Operation *op) const;
+  bool isDeviceData(mlir::Operation *op) const;
 };
 
 template <typename Op>

diff  --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
index 9db67afeda5e9..1ce69df2d7154 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h
@@ -52,6 +52,8 @@ struct OpenACCPointerLikeModel
   bool genStore(mlir::Type pointer, mlir::OpBuilder &builder,
                 mlir::Location loc, mlir::Value valueToStore,
                 mlir::TypedValue<mlir::acc::PointerLikeType> destPtr) const;
+
+  bool isDeviceData(mlir::Type pointer, mlir::Value var) const;
 };
 
 template <typename T>
@@ -102,6 +104,8 @@ struct OpenACCMappableModel
                         mlir::ValueRange bounds,
                         mlir::acc::ReductionOperator op,
                         mlir::Attribute fastmathFlags) const;
+
+  bool isDeviceData(mlir::Type type, mlir::Value var) const;
 };
 
 } // namespace fir::acc

diff  --git a/flang/lib/Optimizer/Dialect/CUF/Attributes/CUFAttr.cpp b/flang/lib/Optimizer/Dialect/CUF/Attributes/CUFAttr.cpp
index bd0499f406c18..3f58065c85bb9 100644
--- a/flang/lib/Optimizer/Dialect/CUF/Attributes/CUFAttr.cpp
+++ b/flang/lib/Optimizer/Dialect/CUF/Attributes/CUFAttr.cpp
@@ -52,4 +52,18 @@ bool hasDataAttr(mlir::Operation *op, cuf::DataAttribute value) {
   return false;
 }
 
+bool isDeviceDataAttribute(cuf::DataAttribute attr) {
+  return attr == cuf::DataAttribute::Device ||
+         attr == cuf::DataAttribute::Managed ||
+         attr == cuf::DataAttribute::Constant ||
+         attr == cuf::DataAttribute::Shared ||
+         attr == cuf::DataAttribute::Unified;
+}
+
+bool hasDeviceDataAttr(mlir::Operation *op) {
+  if (auto dataAttr = getDataAttr(op))
+    return isDeviceDataAttribute(dataAttr.getValue());
+  return false;
+}
+
 } // namespace cuf

diff  --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp
index dacafb1eeb4b2..5671af91f6ea5 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.cpp
@@ -12,6 +12,7 @@
 
 #include "flang/Optimizer/OpenACC/Support/FIROpenACCOpsInterfaces.h"
 
+#include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h"
 #include "flang/Optimizer/Dialect/FIROps.h"
 #include "flang/Optimizer/HLFIR/HLFIROps.h"
 #include "flang/Optimizer/Support/InternalNames.h"
@@ -84,6 +85,12 @@ mlir::Region *GlobalVariableModel::getInitRegion(mlir::Operation *op) const {
   return globalOp.hasInitializationBody() ? &globalOp.getRegion() : nullptr;
 }
 
+bool GlobalVariableModel::isDeviceData(mlir::Operation *op) const {
+  if (auto dataAttr = cuf::getDataAttr(op))
+    return cuf::isDeviceDataAttribute(dataAttr.getValue());
+  return false;
+}
+
 // Helper to recursively process address-of operations in derived type
 // descriptors and collect all needed fir.globals.
 static void processAddrOfOpInDerivedTypeDescriptor(

diff  --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index 2997428b7b5d2..dde6d678cf255 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -16,6 +16,7 @@
 #include "flang/Optimizer/Builder/FIRBuilder.h"
 #include "flang/Optimizer/Builder/HLFIRTools.h"
 #include "flang/Optimizer/Builder/IntrinsicCall.h"
+#include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h"
 #include "flang/Optimizer/Dialect/FIRCG/CGOps.h"
 #include "flang/Optimizer/Dialect/FIROps.h"
 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
@@ -1487,4 +1488,102 @@ template bool OpenACCPointerLikeModel<fir::LLVMPointerType>::genStore(
     mlir::Value valueToStore,
     mlir::TypedValue<mlir::acc::PointerLikeType> destPtr) const;
 
+/// Check CUDA attributes on a function argument.
+static bool hasCUDADeviceAttrOnFuncArg(mlir::BlockArgument blockArg) {
+  auto *owner = blockArg.getOwner();
+  if (!owner)
+    return false;
+
+  auto *parentOp = owner->getParentOp();
+  if (!parentOp)
+    return false;
+
+  if (auto funcLike = mlir::dyn_cast<mlir::FunctionOpInterface>(parentOp)) {
+    unsigned argIndex = blockArg.getArgNumber();
+    if (argIndex < funcLike.getNumArguments())
+      if (auto attr = funcLike.getArgAttr(argIndex, cuf::getDataAttrName()))
+        if (auto cudaAttr = mlir::dyn_cast<cuf::DataAttributeAttr>(attr))
+          return cuf::isDeviceDataAttribute(cudaAttr.getValue());
+  }
+  return false;
+}
+
+/// Shared implementation for checking if a value represents device data.
+static bool isDeviceDataImpl(mlir::Value var) {
+  // Strip casts to find the underlying value.
+  mlir::Value currentVal = stripCasts(var, /*stripDeclare=*/false);
+
+  if (auto blockArg = mlir::dyn_cast<mlir::BlockArgument>(currentVal))
+    return hasCUDADeviceAttrOnFuncArg(blockArg);
+
+  mlir::Operation *defOp = currentVal.getDefiningOp();
+  assert(defOp && "expected defining op for non-block-argument value");
+
+  // Check for CUDA attributes on the defining operation.
+  if (cuf::hasDeviceDataAttr(defOp))
+    return true;
+
+  // Handle operations that access a partial entity - check if the base entity
+  // is device data.
+  if (auto partialAccess =
+          mlir::dyn_cast<mlir::acc::PartialEntityAccessOpInterface>(defOp))
+    if (mlir::Value base = partialAccess.getBaseEntity())
+      return isDeviceDataImpl(base);
+
+  // Handle fir.embox, fir.rebox, and similar ops via
+  // FortranObjectViewOpInterface to check if the underlying source is device
+  // data.
+  if (auto viewOp = mlir::dyn_cast<fir::FortranObjectViewOpInterface>(defOp))
+    if (mlir::Value source = viewOp.getViewSource(defOp->getResult(0)))
+      return isDeviceDataImpl(source);
+
+  // Handle address_of - check the referenced global.
+  if (auto addrOfIface =
+          mlir::dyn_cast<mlir::acc::AddressOfGlobalOpInterface>(defOp)) {
+    auto symbol = addrOfIface.getSymbol();
+    if (auto global = mlir::SymbolTable::lookupNearestSymbolFrom<
+            mlir::acc::GlobalVariableOpInterface>(defOp, symbol))
+      return global.isDeviceData();
+    return false;
+  }
+
+  return false;
+}
+
+template <typename Ty>
+bool OpenACCPointerLikeModel<Ty>::isDeviceData(mlir::Type pointer,
+                                               mlir::Value var) const {
+  return isDeviceDataImpl(var);
+}
+
+template bool OpenACCPointerLikeModel<fir::ReferenceType>::isDeviceData(
+    mlir::Type, mlir::Value) const;
+template bool
+    OpenACCPointerLikeModel<fir::PointerType>::isDeviceData(mlir::Type,
+                                                            mlir::Value) const;
+template bool
+    OpenACCPointerLikeModel<fir::HeapType>::isDeviceData(mlir::Type,
+                                                         mlir::Value) const;
+template bool OpenACCPointerLikeModel<fir::LLVMPointerType>::isDeviceData(
+    mlir::Type, mlir::Value) const;
+
+template <typename Ty>
+bool OpenACCMappableModel<Ty>::isDeviceData(mlir::Type type,
+                                            mlir::Value var) const {
+  return isDeviceDataImpl(var);
+}
+
+template bool
+    OpenACCMappableModel<fir::BaseBoxType>::isDeviceData(mlir::Type,
+                                                         mlir::Value) const;
+template bool
+    OpenACCMappableModel<fir::ReferenceType>::isDeviceData(mlir::Type,
+                                                           mlir::Value) const;
+template bool
+    OpenACCMappableModel<fir::HeapType>::isDeviceData(mlir::Type,
+                                                      mlir::Value) const;
+template bool
+    OpenACCMappableModel<fir::PointerType>::isDeviceData(mlir::Type,
+                                                         mlir::Value) const;
+
 } // namespace fir::acc

diff  --git a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
index 3296e3cc0073f..bf125eb8d04ef 100644
--- a/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToMemRef.cpp
@@ -215,15 +215,7 @@ bool FIRToMemRef::memrefIsDeviceData(Operation *memref) const {
   if (isa<ACC_DATA_ENTRY_OPS>(memref))
     return true;
 
-  if (cuf::DataAttributeAttr cudaAttr = cuf::getDataAttr(memref)) {
-    cuf::DataAttribute attrValue = cudaAttr.getValue();
-    return attrValue == cuf::DataAttribute::Device ||
-           attrValue == cuf::DataAttribute::Managed ||
-           attrValue == cuf::DataAttribute::Constant ||
-           attrValue == cuf::DataAttribute::Shared ||
-           attrValue == cuf::DataAttribute::Unified;
-  }
-  return false;
+  return cuf::hasDeviceDataAttr(memref);
 }
 
 mlir::Attribute FIRToMemRef::findCudaDataAttr(Value val) const {

diff  --git a/flang/test/Transforms/OpenACC/acc-implicit-data.fir b/flang/test/Transforms/OpenACC/acc-implicit-data.fir
index 058390ab05669..050fe55747d23 100644
--- a/flang/test/Transforms/OpenACC/acc-implicit-data.fir
+++ b/flang/test/Transforms/OpenACC/acc-implicit-data.fir
@@ -356,3 +356,41 @@ func.func @test_acc_declare_deviceptr() {
 // CHECK-NOT: acc.copyin
 // CHECK: acc.deviceptr
 
+
+// -----
+
+// Test that implicit deviceptr is generated for a symbol with CUDA device attribute
+func.func @test_cuda_device_implicit_deviceptr() {
+  %0 = fir.dummy_scope : !fir.dscope
+  %1 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+  %2 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
+  %c0 = arith.constant 0 : index
+  %3 = fir.shape %c0 : (index) -> !fir.shape<1>
+  %4 = fir.embox %2(%3) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
+  fir.store %4 to %1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+  %5:2 = hlfir.declare %1 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>)
+  %c1 = arith.constant 1 : index
+  %c16_i32 = arith.constant 16 : i32
+  %c0_i32 = arith.constant 0 : i32
+  %6 = fir.convert %5#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<none>>
+  %7 = fir.convert %c1 : (index) -> i64
+  %8 = fir.convert %c16_i32 : (i32) -> i64
+  fir.call @_FortranAAllocatableSetBounds(%6, %c0_i32, %7, %8) fastmath<contract> : (!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+  %9 = cuf.allocate %5#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
+  acc.serial {
+    %cst = arith.constant 1.000000e+02 : f32
+    %10 = fir.load %5#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+    %c5 = arith.constant 5 : index
+    %11 = hlfir.designate %10 (%c5)  : (!fir.box<!fir.heap<!fir.array<?xf32>>>, index) -> !fir.ref<f32>
+    hlfir.assign %cst to %11 : f32, !fir.ref<f32>
+    acc.yield
+  }
+  return
+}
+
+func.func private @_FortranAAllocatableSetBounds(!fir.ref<!fir.box<none>>, i32, i64, i64) -> ()
+
+// CHECK-LABEL: func.func @test_cuda_device_implicit_deviceptr
+// CHECK-NOT: acc.copyin
+// CHECK: acc.deviceptr
+// CHECK-NOT: acc.copyout

diff  --git a/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h b/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
index 984eaa8b8d78b..c49452556f0c4 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
+++ b/mlir/include/mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h
@@ -50,6 +50,7 @@
 #ifndef MLIR_DIALECT_OPENACC_ANALYSIS_OPENACCSUPPORT_H
 #define MLIR_DIALECT_OPENACC_ANALYSIS_OPENACCSUPPORT_H
 
+#include "mlir/Dialect/OpenACC/OpenACCUtils.h"
 #include "mlir/IR/Remarks.h"
 #include "mlir/IR/Value.h"
 #include "mlir/Pass/AnalysisManager.h"
@@ -60,13 +61,6 @@
 namespace mlir {
 namespace acc {
 
-// Forward declarations
-enum class RecipeKind : uint32_t;
-bool isValidSymbolUse(Operation *user, SymbolRefAttr symbol,
-                      Operation **definingOpPtr);
-remark::detail::InFlightRemark emitRemark(Operation *op, const Twine &message,
-                                          llvm::StringRef category);
-
 namespace detail {
 /// This class contains internal trait classes used by OpenACCSupport.
 /// It follows the Concept-Model pattern used throughout MLIR (e.g., in
@@ -170,10 +164,10 @@ struct OpenACCSupportTraits {
     }
 
     bool isValidValueUse(Value v, Region &region) final {
-      if constexpr (has_isValidSymbolUse<ImplT>::value)
+      if constexpr (has_isValidValueUse<ImplT>::value)
         return impl.isValidValueUse(v, region);
       else
-        return false;
+        return acc::isValidValueUse(v, region);
     }
 
   private:

diff  --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td
index 44632eb4cdac4..95a8f22a3ddfa 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOpsInterfaces.td
@@ -75,6 +75,10 @@ def GlobalVariableOpInterface : OpInterface<"GlobalVariableOpInterface"> {
       }]>,
     InterfaceMethod<"Get the initialization region (returns nullptr if none)",
       "::mlir::Region*", "getInitRegion", (ins)>,
+    InterfaceMethod<"Check if the global variable is device data",
+      "bool", "isDeviceData", (ins), [{
+        return false;
+      }]>,
   ];
 }
 

diff  --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
index 1d55762e2f7d9..ca60f9bca5cf1 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCTypeInterfaces.td
@@ -220,6 +220,18 @@ def OpenACC_PointerLikeTypeInterface : TypeInterface<"PointerLikeType"> {
         return false;
       }]
     >,
+    InterfaceMethod<
+      /*description=*/[{
+        Returns true if the pointer points to device data.
+      }],
+      /*retTy=*/"bool",
+      /*methodName=*/"isDeviceData",
+      /*args=*/(ins "::mlir::Value":$var),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return false;
+      }]
+    >,
   ];
 }
 
@@ -442,6 +454,18 @@ def OpenACC_MappableTypeInterface : TypeInterface<"MappableType"> {
         return true;
       }]
     >,
+    InterfaceMethod<
+      /*description=*/[{
+        Returns true if the variable represents device data.
+      }],
+      /*retTy=*/"bool",
+      /*methodName=*/"isDeviceData",
+      /*args=*/(ins "::mlir::Value":$var),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return false;
+      }]
+    >,
   ];
 }
 

diff  --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index e3f4e6889ffe8..a436050babce2 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -67,6 +67,23 @@ mlir::Value getBaseEntity(mlir::Value val);
 bool isValidSymbolUse(mlir::Operation *user, mlir::SymbolRefAttr symbol,
                       mlir::Operation **definingOpPtr = nullptr);
 
+/// Check if a value represents device data.
+/// This checks if the value represents device data via the
+/// MappableType, PointerLikeType, and GlobalVariableOpInterface interfaces.
+/// \param val The value to check
+/// \return true if the value is device data, false otherwise
+bool isDeviceValue(mlir::Value val);
+
+/// Check if a value use is valid in an OpenACC region.
+/// This is true if:
+/// - The value is produced by an ACC data entry operation
+/// - The value is device data
+/// - The value is only used by private clauses in the region
+/// \param val The value to check
+/// \param region The OpenACC region
+/// \return true if the value use is valid, false otherwise
+bool isValidValueUse(mlir::Value val, mlir::Region &region);
+
 /// Collects all data clauses that dominate the compute construct.
 /// This includes data clauses from:
 /// - The compute construct itself

diff  --git a/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp b/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
index c487c43e8369c..4fbe3e06c2532 100644
--- a/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
+++ b/mlir/lib/Dialect/OpenACC/Analysis/OpenACCSupport.cpp
@@ -59,7 +59,7 @@ bool OpenACCSupport::isValidSymbolUse(Operation *user, SymbolRefAttr symbol,
 bool OpenACCSupport::isValidValueUse(Value v, Region &region) {
   if (impl)
     return impl->isValidValueUse(v, region);
-  return false;
+  return acc::isValidValueUse(v, region);
 }
 
 } // namespace acc

diff  --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index 6872aa9d26e29..55a438eb3906b 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -245,6 +245,12 @@ struct MemRefPointerLikeModel
     memref::StoreOp::create(builder, loc, valueToStore, memrefValue);
     return true;
   }
+
+  bool isDeviceData(Type pointer, Value var) const {
+    auto memrefTy = cast<T>(pointer);
+    Attribute memSpace = memrefTy.getMemorySpace();
+    return isa_and_nonnull<gpu::AddressSpaceAttr>(memSpace);
+  }
 };
 
 struct LLVMPointerPointerLikeModel
@@ -290,6 +296,12 @@ struct MemrefGlobalVariableModel
     // GlobalOp uses attributes for initialization, not regions
     return nullptr;
   }
+
+  bool isDeviceData(Operation *op) const {
+    auto globalOp = cast<memref::GlobalOp>(op);
+    Attribute memSpace = globalOp.getType().getMemorySpace();
+    return isa_and_nonnull<gpu::AddressSpaceAttr>(memSpace);
+  }
 };
 
 struct GPULaunchOffloadRegionModel

diff  --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
index c4b43c5cb1b27..5e46875ca8a2d 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitData.cpp
@@ -285,16 +285,17 @@ static bool isCandidateForImplicitData(Value val, Region &accRegion,
       !acc::isMappableType(val.getType()))
     return false;
 
-  if (accSupport.isValidValueUse(val, accRegion))
-    return false;
-
   // If this is already coming from a data clause, we do not need to generate
   // another.
   if (isa_and_nonnull<ACC_DATA_ENTRY_OPS>(val.getDefiningOp()))
     return false;
 
-  // If this is only used by private clauses, it is not a real live-in.
-  if (acc::isOnlyUsedByPrivateClauses(val, accRegion))
+  // Device data is a candidate - it will get a deviceptr clause.
+  if (acc::isDeviceValue(val))
+    return true;
+
+  // If it is otherwise valid, skip it.
+  if (accSupport.isValidValueUse(val, accRegion))
     return false;
 
   return true;
@@ -472,7 +473,16 @@ Operation *ACCImplicitData::generateDataClauseOpForCandidate(
                                   /*structured=*/true, /*implicit=*/true,
                                   accSupport.getVariableName(var),
                                   acc::getBounds(op));
-  } else if (isScalar) {
+  }
+
+  if (acc::isDeviceValue(var)) {
+    // If the variable is device data, use deviceptr clause.
+    return acc::DevicePtrOp::create(builder, loc, var,
+                                    /*structured=*/true, /*implicit=*/true,
+                                    accSupport.getVariableName(var));
+  }
+
+  if (isScalar) {
     if (enableImplicitReductionCopy &&
         acc::isOnlyUsedByReductionClauses(var,
                                           computeConstructOp->getRegion(0))) {

diff  --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index e97fd1471e6e0..bd3dda48d44b4 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -182,6 +182,13 @@ bool mlir::acc::isValidSymbolUse(mlir::Operation *user,
                 mlir::acc::FirstprivateRecipeOp>(definingOp))
     return true;
 
+  // Check if the defining op is a global variable that is device data.
+  // Device data is already resident on the device and does not need mapping.
+  if (auto globalVar =
+          mlir::dyn_cast<mlir::acc::GlobalVariableOpInterface>(definingOp))
+    if (globalVar.isDeviceData())
+      return true;
+
   // Check if the defining op is a function
   if (auto func =
           mlir::dyn_cast_if_present<mlir::FunctionOpInterface>(definingOp)) {
@@ -208,6 +215,55 @@ bool mlir::acc::isValidSymbolUse(mlir::Operation *user,
   return hasDeclare;
 }
 
+bool mlir::acc::isDeviceValue(mlir::Value val) {
+  // Check if the value is device data via type interfaces.
+  // Device data is already resident on the device and does not need mapping.
+  if (auto mappableTy = dyn_cast<mlir::acc::MappableType>(val.getType()))
+    if (mappableTy.isDeviceData(val))
+      return true;
+
+  if (auto pointerLikeTy = dyn_cast<mlir::acc::PointerLikeType>(val.getType()))
+    if (pointerLikeTy.isDeviceData(val))
+      return true;
+
+  // Handle operations that access a partial entity - check if the base entity
+  // is device data.
+  if (auto *defOp = val.getDefiningOp()) {
+    if (auto partialAccess =
+            dyn_cast<mlir::acc::PartialEntityAccessOpInterface>(defOp)) {
+      if (mlir::Value base = partialAccess.getBaseEntity())
+        return isDeviceValue(base);
+    }
+
+    // Handle address_of - check if the referenced global is device data.
+    if (auto addrOfIface =
+            dyn_cast<mlir::acc::AddressOfGlobalOpInterface>(defOp)) {
+      auto symbol = addrOfIface.getSymbol();
+      if (auto global = mlir::SymbolTable::lookupNearestSymbolFrom<
+              mlir::acc::GlobalVariableOpInterface>(defOp, symbol))
+        return global.isDeviceData();
+    }
+  }
+
+  return false;
+}
+
+bool mlir::acc::isValidValueUse(mlir::Value val, mlir::Region &region) {
+  // If this is produced by an ACC data entry operation, it is valid.
+  if (isa_and_nonnull<ACC_DATA_ENTRY_OPS>(val.getDefiningOp()))
+    return true;
+
+  // If the value is only used by private clauses, it is not a live-in.
+  if (isOnlyUsedByPrivateClauses(val, region))
+    return true;
+
+  // If this is device data, it is valid.
+  if (isDeviceValue(val))
+    return true;
+
+  return false;
+}
+
 llvm::SmallVector<mlir::Value>
 mlir::acc::getDominatingDataClauses(mlir::Operation *computeConstructOp,
                                     mlir::DominanceInfo &domInfo,

diff  --git a/mlir/test/Dialect/OpenACC/acc-implicit-data.mlir b/mlir/test/Dialect/OpenACC/acc-implicit-data.mlir
index 6909fe6a4eb84..df0dbbfee8b1d 100644
--- a/mlir/test/Dialect/OpenACC/acc-implicit-data.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-implicit-data.mlir
@@ -222,3 +222,40 @@ func.func @test_memref_view(%size: index) {
 // CHECK-LABEL: func.func @test_memref_view
 // CHECK: acc.present varPtr({{.*}} : memref<8x64xf32>) -> memref<8x64xf32> {implicit = true, name = ""}
 
+// -----
+
+// Test device data (memref with GPU address space) - should generate deviceptr
+func.func @test_device_data_in_parallel() {
+  %alloc = memref.alloca() : memref<10xf32, #gpu.address_space<global>>
+  acc.parallel {
+    %c0 = arith.constant 0 : index
+    %load = memref.load %alloc[%c0] : memref<10xf32, #gpu.address_space<global>>
+    acc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: func.func @test_device_data_in_parallel
+// CHECK: acc.deviceptr varPtr({{.*}} : memref<10xf32, #gpu.address_space<global>>) -> memref<10xf32, #gpu.address_space<global>> {implicit = true, name = ""}
+// CHECK-NOT: acc.copyin
+// CHECK-NOT: acc.copyout
+
+// -----
+
+// Test device global (memref.global with GPU address space) - should generate deviceptr
+memref.global @device_global : memref<10xf32, #gpu.address_space<global>>
+
+func.func @test_device_global_in_parallel() {
+  %global = memref.get_global @device_global : memref<10xf32, #gpu.address_space<global>>
+  acc.parallel {
+    %c0 = arith.constant 0 : index
+    %load = memref.load %global[%c0] : memref<10xf32, #gpu.address_space<global>>
+    acc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: func.func @test_device_global_in_parallel
+// CHECK: acc.deviceptr varPtr({{.*}} : memref<10xf32, #gpu.address_space<global>>) -> memref<10xf32, #gpu.address_space<global>> {implicit = true, name = ""}
+// CHECK-NOT: acc.copyin
+// CHECK-NOT: acc.copyout

diff  --git a/mlir/unittests/Dialect/OpenACC/CMakeLists.txt b/mlir/unittests/Dialect/OpenACC/CMakeLists.txt
index 29448d2af5537..ef0c8fb11d0c9 100644
--- a/mlir/unittests/Dialect/OpenACC/CMakeLists.txt
+++ b/mlir/unittests/Dialect/OpenACC/CMakeLists.txt
@@ -10,6 +10,7 @@ mlir_target_link_libraries(MLIROpenACCTests
   MLIRIR
   MLIRAffineDialect
   MLIRFuncDialect
+  MLIRGPUDialect
   MLIRMemRefDialect
   MLIRArithDialect
   MLIROpenACCDialect

diff  --git a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
index 60d87326c0e9b..29186ce826d1b 100644
--- a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
@@ -9,6 +9,7 @@
 #include "mlir/Dialect/OpenACC/OpenACCUtils.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
 #include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Dialect/GPU/IR/GPUDialect.h"
 #include "mlir/Dialect/MemRef/IR/MemRef.h"
 #include "mlir/Dialect/OpenACC/OpenACC.h"
 #include "mlir/IR/BuiltinOps.h"
@@ -29,8 +30,9 @@ using namespace mlir::acc;
 class OpenACCUtilsTest : public ::testing::Test {
 protected:
   OpenACCUtilsTest() : b(&context), loc(UnknownLoc::get(&context)) {
-    context.loadDialect<acc::OpenACCDialect, arith::ArithDialect,
-                        memref::MemRefDialect, func::FuncDialect>();
+    context
+        .loadDialect<acc::OpenACCDialect, arith::ArithDialect, gpu::GPUDialect,
+                     memref::MemRefDialect, func::FuncDialect>();
   }
 
   MLIRContext context;
@@ -1367,3 +1369,280 @@ TEST_F(OpenACCUtilsTest, getDominatingDataClausesEmpty) {
   // Should be empty
   EXPECT_EQ(dataClauses.size(), 0ul);
 }
+
+//===----------------------------------------------------------------------===//
+// isDeviceValue Tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(OpenACCUtilsTest, isDeviceValueMemrefGlobalAddressSpace) {
+  // Test that a memref with GPU global address space is considered device data
+  auto gpuAddressSpace =
+      gpu::AddressSpaceAttr::get(&context, gpu::AddressSpace::Global);
+  auto memrefTy =
+      MemRefType::get({10}, b.getI32Type(), AffineMap(), gpuAddressSpace);
+
+  OwningOpRef<memref::AllocaOp> allocOp =
+      memref::AllocaOp::create(b, loc, memrefTy);
+  Value val = allocOp->getResult();
+
+  // Should return true since memref has GPU global address space
+  EXPECT_TRUE(isDeviceValue(val));
+}
+
+TEST_F(OpenACCUtilsTest, isDeviceValueMemrefWorkgroupAddressSpace) {
+  // Test that a memref with GPU workgroup address space is considered device
+  // data
+  auto gpuAddressSpace =
+      gpu::AddressSpaceAttr::get(&context, gpu::AddressSpace::Workgroup);
+  auto memrefTy =
+      MemRefType::get({10}, b.getI32Type(), AffineMap(), gpuAddressSpace);
+
+  OwningOpRef<memref::AllocaOp> allocOp =
+      memref::AllocaOp::create(b, loc, memrefTy);
+  Value val = allocOp->getResult();
+
+  // Should return true since memref has GPU workgroup address space
+  EXPECT_TRUE(isDeviceValue(val));
+}
+
+TEST_F(OpenACCUtilsTest, isDeviceValueMemrefPrivateAddressSpace) {
+  // Test that a memref with GPU private address space is considered device
+  // data
+  auto gpuAddressSpace =
+      gpu::AddressSpaceAttr::get(&context, gpu::AddressSpace::Private);
+  auto memrefTy =
+      MemRefType::get({10}, b.getI32Type(), AffineMap(), gpuAddressSpace);
+
+  OwningOpRef<memref::AllocaOp> allocOp =
+      memref::AllocaOp::create(b, loc, memrefTy);
+  Value val = allocOp->getResult();
+
+  // Should return true since memref has GPU private address space
+  EXPECT_TRUE(isDeviceValue(val));
+}
+
+TEST_F(OpenACCUtilsTest, isDeviceValueMemrefNoAddressSpace) {
+  // Test that a regular memref without GPU address space is not device data
+  auto memrefTy = MemRefType::get({10}, b.getI32Type());
+
+  OwningOpRef<memref::AllocaOp> allocOp =
+      memref::AllocaOp::create(b, loc, memrefTy);
+  Value val = allocOp->getResult();
+
+  // Should return false since memref has no GPU address space
+  EXPECT_FALSE(isDeviceValue(val));
+}
+
+TEST_F(OpenACCUtilsTest, isDeviceValueNonMappableType) {
+  // Test with a non-mappable type (i32 value)
+  auto constOp = arith::ConstantOp::create(b, loc, b.getI32IntegerAttr(42));
+  Value val = constOp.getResult();
+
+  // Should return false since i32 is not a MappableType or PointerLikeType
+  EXPECT_FALSE(isDeviceValue(val));
+}
+
+TEST_F(OpenACCUtilsTest, isDeviceValueGlobalWithGPUAddressSpace) {
+  // Test that memref.get_global referencing a global with GPU address space
+  // is considered device data
+  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+  Block *moduleBlock = module->getBody();
+
+  OpBuilder::InsertionGuard guard(b);
+  b.setInsertionPointToStart(moduleBlock);
+
+  // Create a memref type with GPU global address space
+  auto gpuAddressSpace =
+      gpu::AddressSpaceAttr::get(&context, gpu::AddressSpace::Global);
+  auto memrefTy =
+      MemRefType::get({10}, b.getI32Type(), AffineMap(), gpuAddressSpace);
+
+  // Create a global op with the GPU address space memref type
+  llvm::StringRef globalName = "device_global";
+  OwningOpRef<memref::GlobalOp> globalOp = memref::GlobalOp::create(
+      b, loc, globalName, /*sym_visibility=*/b.getStringAttr("public"),
+      /*type=*/memrefTy, /*initial_value=*/Attribute(),
+      /*constant=*/false, /*alignment=*/IntegerAttr());
+
+  // Create a get_global that references the device global
+  OwningOpRef<memref::GetGlobalOp> getGlobalOp =
+      memref::GetGlobalOp::create(b, loc, memrefTy, globalName);
+  Value val = getGlobalOp->getResult();
+
+  // Should return true since the global has GPU address space
+  EXPECT_TRUE(isDeviceValue(val));
+}
+
+TEST_F(OpenACCUtilsTest, isDeviceValueGlobalWithoutGPUAddressSpace) {
+  // Test that memref.get_global referencing a regular global is not device data
+  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+  Block *moduleBlock = module->getBody();
+
+  OpBuilder::InsertionGuard guard(b);
+  b.setInsertionPointToStart(moduleBlock);
+
+  // Create a regular memref type without GPU address space
+  auto memrefTy = MemRefType::get({10}, b.getI32Type());
+
+  // Create a global op without GPU address space
+  llvm::StringRef globalName = "host_global";
+  OwningOpRef<memref::GlobalOp> globalOp = memref::GlobalOp::create(
+      b, loc, globalName, /*sym_visibility=*/b.getStringAttr("public"),
+      /*type=*/memrefTy, /*initial_value=*/Attribute(),
+      /*constant=*/false, /*alignment=*/IntegerAttr());
+
+  // Create a get_global that references the host global
+  OwningOpRef<memref::GetGlobalOp> getGlobalOp =
+      memref::GetGlobalOp::create(b, loc, memrefTy, globalName);
+  Value val = getGlobalOp->getResult();
+
+  // Should return false since the global has no GPU address space
+  EXPECT_FALSE(isDeviceValue(val));
+}
+
+//===----------------------------------------------------------------------===//
+// isValidValueUse Tests
+//===----------------------------------------------------------------------===//
+
+TEST_F(OpenACCUtilsTest, isValidValueUseFromDataEntryOp) {
+  // Create a module to hold a function
+  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+  Block *moduleBlock = module->getBody();
+
+  OpBuilder::InsertionGuard guard(b);
+  b.setInsertionPointToStart(moduleBlock);
+
+  // Create a function with a serial region
+  auto funcType = b.getFunctionType({}, {});
+  OwningOpRef<func::FuncOp> funcOp =
+      func::FuncOp::create(b, loc, "test_func", funcType);
+  Block *entryBlock = funcOp->addEntryBlock();
+  b.setInsertionPointToStart(entryBlock);
+
+  // Create a memref and a copyin operation
+  auto memrefTy = MemRefType::get({10}, b.getI32Type());
+  OwningOpRef<memref::AllocaOp> allocOp =
+      memref::AllocaOp::create(b, loc, memrefTy);
+  TypedValue<PointerLikeType> varPtr =
+      cast<TypedValue<PointerLikeType>>(allocOp->getResult());
+
+  OwningOpRef<CopyinOp> copyinOp =
+      CopyinOp::create(b, loc, varPtr, /*structured=*/true, /*implicit=*/false,
+                       /*name=*/"test_var");
+  Value dataClauseResult = copyinOp->getAccVar();
+
+  // Create a serial region
+  OwningOpRef<SerialOp> serialOp =
+      SerialOp::create(b, loc, TypeRange{}, ValueRange{});
+  Region &serialRegion = serialOp->getRegion();
+
+  // Value from data entry op should be valid
+  EXPECT_TRUE(isValidValueUse(dataClauseResult, serialRegion));
+}
+
+TEST_F(OpenACCUtilsTest, isValidValueUseDeviceData) {
+  // Create a module to hold a function
+  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+  Block *moduleBlock = module->getBody();
+
+  OpBuilder::InsertionGuard guard(b);
+  b.setInsertionPointToStart(moduleBlock);
+
+  // Create a function
+  auto funcType = b.getFunctionType({}, {});
+  OwningOpRef<func::FuncOp> funcOp =
+      func::FuncOp::create(b, loc, "test_func", funcType);
+  Block *entryBlock = funcOp->addEntryBlock();
+  b.setInsertionPointToStart(entryBlock);
+
+  // Create a memref with GPU address space (device data)
+  auto gpuAddressSpace =
+      gpu::AddressSpaceAttr::get(&context, gpu::AddressSpace::Global);
+  auto memrefTy =
+      MemRefType::get({10}, b.getI32Type(), AffineMap(), gpuAddressSpace);
+  OwningOpRef<memref::AllocaOp> allocOp =
+      memref::AllocaOp::create(b, loc, memrefTy);
+  Value deviceVal = allocOp->getResult();
+
+  // Create a serial region
+  OwningOpRef<SerialOp> serialOp =
+      SerialOp::create(b, loc, TypeRange{}, ValueRange{});
+  Region &serialRegion = serialOp->getRegion();
+
+  // Device data should be valid
+  EXPECT_TRUE(isValidValueUse(deviceVal, serialRegion));
+}
+
+TEST_F(OpenACCUtilsTest, isValidValueUseOnlyUsedByPrivate) {
+  // Create a module to hold a function
+  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+  Block *moduleBlock = module->getBody();
+
+  OpBuilder::InsertionGuard guard(b);
+  b.setInsertionPointToStart(moduleBlock);
+
+  // Create a function
+  auto funcType = b.getFunctionType({}, {});
+  OwningOpRef<func::FuncOp> funcOp =
+      func::FuncOp::create(b, loc, "test_func", funcType);
+  Block *entryBlock = funcOp->addEntryBlock();
+  b.setInsertionPointToStart(entryBlock);
+
+  // Create a memref
+  auto memrefTy = MemRefType::get({10}, b.getI32Type());
+  OwningOpRef<memref::AllocaOp> allocOp =
+      memref::AllocaOp::create(b, loc, memrefTy);
+  TypedValue<PointerLikeType> varPtr =
+      cast<TypedValue<PointerLikeType>>(allocOp->getResult());
+
+  // Create a serial region with a private clause using the variable
+  OwningOpRef<SerialOp> serialOp =
+      SerialOp::create(b, loc, TypeRange{}, ValueRange{});
+  Region &serialRegion = serialOp->getRegion();
+  Block *serialBlock = b.createBlock(&serialRegion);
+  b.setInsertionPointToStart(serialBlock);
+
+  OwningOpRef<PrivateOp> privateOp = PrivateOp::create(
+      b, loc, varPtr, /*structured=*/true, /*implicit=*/false);
+
+  // Value only used by private clause should be valid
+  EXPECT_TRUE(isValidValueUse(varPtr, serialRegion));
+}
+
+TEST_F(OpenACCUtilsTest, isValidValueUseRegularValue) {
+  // Create a module to hold a function
+  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+  Block *moduleBlock = module->getBody();
+
+  OpBuilder::InsertionGuard guard(b);
+  b.setInsertionPointToStart(moduleBlock);
+
+  // Create a function
+  auto funcType = b.getFunctionType({}, {});
+  OwningOpRef<func::FuncOp> funcOp =
+      func::FuncOp::create(b, loc, "test_func", funcType);
+  Block *entryBlock = funcOp->addEntryBlock();
+  b.setInsertionPointToStart(entryBlock);
+
+  // Create a regular memref without GPU address space
+  auto memrefTy = MemRefType::get({10}, b.getI32Type());
+  OwningOpRef<memref::AllocaOp> allocOp =
+      memref::AllocaOp::create(b, loc, memrefTy);
+  Value regularVal = allocOp->getResult();
+
+  // Create a serial region with a non-private use of the value
+  OwningOpRef<SerialOp> serialOp =
+      SerialOp::create(b, loc, TypeRange{}, ValueRange{});
+  Region &serialRegion = serialOp->getRegion();
+  Block *serialBlock = b.createBlock(&serialRegion);
+  b.setInsertionPointToStart(serialBlock);
+
+  // Add a function call to create a synthetic use of the value inside the
+  // region
+  func::CallOp::create(b, loc, "some_func", TypeRange{},
+                       ValueRange{regularVal});
+
+  // Regular value (not device data, not from data op, not private) should be
+  // invalid
+  EXPECT_FALSE(isValidValueUse(regularVal, serialRegion));
+}


        


More information about the Mlir-commits mailing list