[flang-commits] [flang] [mlir] [flang][acc] Add AA implementation for acc operations (PR #189772)

Razvan Lupusoru via flang-commits flang-commits at lists.llvm.org
Tue Mar 31 17:06:38 PDT 2026


https://github.com/razvanlupusoru updated https://github.com/llvm/llvm-project/pull/189772

>From 08711c874b68b77b320a95c2c067977154d47214 Mon Sep 17 00:00:00 2001
From: Razvan Lupusoru <rlupusoru at nvidia.com>
Date: Tue, 31 Mar 2026 16:45:11 -0700
Subject: [PATCH 1/4] [flang][acc] Add AA implementation for acc operations

This PR extends flang's alias analysis so it can reason about values
that originate from OpenACC data and privatization operations,
including values passed through block arguments.
---
 .../OpenACC/Support/FIROpenACCUtils.h         |  16 +
 .../lib/Optimizer/Analysis/AliasAnalysis.cpp  |  24 +
 flang/lib/Optimizer/Analysis/CMakeLists.txt   |   4 +
 .../OpenACC/Support/FIROpenACCUtils.cpp       |  38 ++
 .../AliasAnalysis/alias-analysis-acc.mlir     | 546 ++++++++++++++++++
 mlir/include/mlir/Dialect/OpenACC/OpenACC.h   |   2 +
 .../mlir/Dialect/OpenACC/OpenACCUtils.h       |   5 +
 .../Dialect/OpenACC/Utils/OpenACCUtils.cpp    |  16 +
 8 files changed, 651 insertions(+)
 create mode 100644 flang/test/Analysis/AliasAnalysis/alias-analysis-acc.mlir

diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
index a8b9bcf5cfd4b..840622b7dd0f9 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
@@ -13,14 +13,30 @@
 #ifndef FORTRAN_OPTIMIZER_OPENACC_SUPPORT_FIROPENACCUTILS_H
 #define FORTRAN_OPTIMIZER_OPENACC_SUPPORT_FIROPENACCUTILS_H
 
+#include "flang/Optimizer/Analysis/AliasAnalysis.h"
 #include "mlir/Dialect/OpenACC/OpenACC.h"
 #include "mlir/IR/Builders.h"
+#include "mlir/IR/Operation.h"
 #include "mlir/IR/Value.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include <string>
 
 namespace fir {
 namespace acc {
 
+/// Memory source for `mappedValue` when it is produced by OpenACC mapping op
+/// `accOp` . Private-like ops return `SourceKind::Allocate`; other data clauses
+/// trace the host variable via `traceValue`.
+///
+/// `accumulatedAttrs` collects Fortran variable attributes from the path from
+/// the queried value to `accOp` (e.g. [hl]fir.declare); they are merged into
+/// the returned source so TARGET/POINTER/INTENT_IN are not dropped.
+fir::AliasAnalysis::Source getSourceFromACCValue(
+    mlir::Value mappedValue, mlir::Operation *accOp,
+    llvm::function_ref<fir::AliasAnalysis::Source(mlir::Value)> traceValue,
+    bool originIsData,
+    fir::AliasAnalysis::Source::Attributes accumulatedAttrs = {});
+
 /// Attempts to extract the variable name from a value by walking through
 /// FIR operations and looking for variable names.
 /// \param v The value to extract the variable name from
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 550e8a3a281d6..77d334657ad9b 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -13,8 +13,11 @@
 #include "flang/Optimizer/Dialect/FIRType.h"
 #include "flang/Optimizer/Dialect/FortranVariableInterface.h"
 #include "flang/Optimizer/HLFIR/HLFIROps.h"
+#include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
 #include "flang/Optimizer/Support/InternalNames.h"
 #include "mlir/Analysis/AliasAnalysis.h"
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/Dialect/OpenACC/OpenACCUtils.h"
 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
 #include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"
 #include "mlir/IR/BuiltinOps.h"
@@ -24,6 +27,7 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
+#include <optional>
 
 using namespace mlir;
 
@@ -680,6 +684,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
     auto opResult = mlir::cast<OpResult>(v);
     assert(opResult.getOwner() == defOp && "v must be a result of defOp");
     ty = opResult.getType();
+    std::optional<AliasAnalysis::Source> accSourceReturn;
     llvm::TypeSwitch<Operation *>(defOp)
         .Case([&](hlfir::AsExprOp op) {
           // TODO: we should probably always report hlfir.as_expr
@@ -937,10 +942,21 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
               !mlir::isa<fir::BaseBoxType>(ty))
             followBoxData = true;
         })
+        .Case<ACC_DATA_ENTRY_AND_INIT_OPS>([&](auto op) {
+          accSourceReturn = fir::acc::getSourceFromACCValue(
+              v, op.getOperation(),
+              [&](mlir::Value x) {
+                return getSource(x, getLastInstantiationPoint);
+              },
+              followingData, attributes);
+          breakFromLoop = true;
+        })
         .Default([&](auto op) {
           defOp = nullptr;
           breakFromLoop = true;
         });
+    if (accSourceReturn)
+      return *accSourceReturn;
   }
   if (!defOp && type == SourceKind::Unknown) {
     // Check if the memory source is coming through a dummy argument.
@@ -956,6 +972,14 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
       // hlfir.eval_in_mem block operands is allocated by the operation.
       type = SourceKind::Allocate;
       ty = v.getType();
+    } else if (mlir::Operation *accOp =
+                   mlir::acc::getACCDataClauseOpForBlockArg(v)) {
+      return fir::acc::getSourceFromACCValue(
+          v, accOp,
+          [&](mlir::Value x) {
+            return getSource(x, getLastInstantiationPoint);
+          },
+          followingData, attributes);
     }
   }
 
diff --git a/flang/lib/Optimizer/Analysis/CMakeLists.txt b/flang/lib/Optimizer/Analysis/CMakeLists.txt
index 398a6d3b88427..c017708e35ddf 100644
--- a/flang/lib/Optimizer/Analysis/CMakeLists.txt
+++ b/flang/lib/Optimizer/Analysis/CMakeLists.txt
@@ -6,22 +6,26 @@ add_flang_library(FIRAnalysis
   DEPENDS
   CUFDialect
   FIRDialect
+  FIROpenACCSupport
   FIRSupport
   HLFIRDialect
 
   LINK_LIBS
   CUFDialect
   FIRDialect
+  FIROpenACCSupport
   FIRSupport
   HLFIRDialect
 
   MLIR_DEPS
   MLIRIR
+  MLIROpenACCDialect
   MLIROpenMPDialect
 
   MLIR_LIBS
   MLIRFuncDialect
   MLIRLLVMDialect
   MLIRMathTransforms
+  MLIROpenACCDialect
   MLIROpenMPDialect
 )
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index 519978dba1afd..39ccd3aad1598 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
+#include "flang/Optimizer/Analysis/AliasAnalysis.h"
 #include "flang/Optimizer/Builder/BoxValue.h"
 #include "flang/Optimizer/Builder/Complex.h"
 #include "flang/Optimizer/Builder/FIRBuilder.h"
@@ -29,6 +30,7 @@
 #include "mlir/Interfaces/ViewLikeInterface.h"
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/Support/raw_ostream.h"
+#include <mlir/Support/LLVM.h>
 
 using namespace mlir;
 
@@ -616,6 +618,42 @@ mlir::SymbolRefAttr fir::acc::createOrGetReductionRecipe(
   return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
 }
 
+static bool isACCPrivateLikeOp(mlir::Operation *op) {
+  if (!op)
+    return false;
+  return mlir::isa<mlir::acc::ReductionInitOp, mlir::acc::PrivateOp,
+                   mlir::acc::FirstprivateOp,
+                   mlir::acc::FirstprivateMapInitialOp>(op);
+}
+
+fir::AliasAnalysis::Source fir::acc::getSourceFromACCValue(
+    mlir::Value mappedValue, mlir::Operation *accOp,
+    llvm::function_ref<fir::AliasAnalysis::Source(mlir::Value)> traceValue,
+    bool originIsData,
+    fir::AliasAnalysis::Source::Attributes accumulatedAttrs) {
+  assert(accOp && "OpenACC mapping op required");
+  if (isACCPrivateLikeOp(accOp))
+    return {{mappedValue, nullptr, originIsData},
+            fir::AliasAnalysis::SourceKind::Allocate,
+            mappedValue.getType(),
+            accumulatedAttrs,
+            false,
+            false};
+
+  // Not private-like: classify using the corresponding host variable's source.
+  //
+  // Caveat: with discrete device memory, host and device copies do not alias
+  // even when this path makes them look related. Alias analysis here is usually
+  // about two values *inside* a compute region, not host-vs-device pointer
+  // queries, so using the host source remains a reasonable tradeoff for
+  // disambiguating in-region uses. Finer modeling would require extending
+  // AliasAnalysis::Source (with address space) and teaching AA to use it.
+  fir::AliasAnalysis::Source traced =
+      traceValue(mlir::acc::getVar(accOp));
+  traced.attributes |= accumulatedAttrs;
+  return traced;
+}
+
 mlir::Value fir::acc::getOriginalDef(mlir::Value value, bool stripDeclare) {
   mlir::Value currentValue = value;
 
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-acc.mlir b/flang/test/Analysis/AliasAnalysis/alias-analysis-acc.mlir
new file mode 100644
index 0000000000000..9fb5ee221691d
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-acc.mlir
@@ -0,0 +1,546 @@
+// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s
+
+// -----
+
+// Two acc.copyin results from distinct host allocas do not alias.
+// CHECK-LABEL: Testing : "testBothOutsideCopyinDistinctHosts"
+// CHECK-DAG: cin_a#0 <-> cin_b#0: NoAlias
+
+func.func @testBothOutsideCopyinDistinctHosts() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a", test.ptr = "cin_a"}
+  %cb = acc.copyin varPtr(%db : !fir.ref<f32>) -> !fir.ref<f32> {name = "b", test.ptr = "cin_b"}
+  return
+}
+
+// -----
+
+// Two acc.copyin results from dummy arguments that are Fortran TARGET variables:
+// they may alias.
+// CHECK-LABEL: Testing : "testBothOutsideCopyinTargetDummyArgsMayAlias"
+// CHECK-DAG: arg_cp_a#0 <-> arg_cp_b#0: MayAlias
+
+func.func @testBothOutsideCopyinTargetDummyArgsMayAlias(%arg0: !fir.ref<f32> {fir.bindc_name = "x"}, %arg1: !fir.ref<f32> {fir.bindc_name = "y"}) {
+  %ds = fir.dummy_scope : !fir.dscope
+  %dx = fir.declare %arg0 dummy_scope %ds arg 1 {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFEex"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+  %dy = fir.declare %arg1 dummy_scope %ds arg 2 {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFEey"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+  %cx = acc.copyin varPtr(%dx : !fir.ref<f32>) -> !fir.ref<f32> {name = "x", test.ptr = "arg_cp_a"}
+  %cy = acc.copyin varPtr(%dy : !fir.ref<f32>) -> !fir.ref<f32> {name = "y", test.ptr = "arg_cp_b"}
+  return
+}
+
+// -----
+
+// Two acc.copyin results mapping the same host ref must alias.
+// CHECK-LABEL: Testing : "testBothOutsideCopyinSameHostMustAlias"
+// CHECK-DAG: out_must_a#0 <-> out_must_b#0: MustAlias
+
+func.func @testBothOutsideCopyinSameHostMustAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca1 = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a", test.ptr = "out_must_a"}
+  %ca2 = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a", test.ptr = "out_must_b"}
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "testBothOutsideCreateDistinctHosts"
+// CHECK-DAG: crt_a#0 <-> crt_b#0: NoAlias
+
+func.func @testBothOutsideCreateDistinctHosts() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ta = acc.create varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a", test.ptr = "crt_a"}
+  %tb = acc.create varPtr(%db : !fir.ref<f32>) -> !fir.ref<f32> {name = "b", test.ptr = "crt_b"}
+  return
+}
+
+// -----
+
+// Same distinct-host copyins as above, but threaded through acc.compute_region
+// block arguments.
+// CHECK-LABEL: Testing : "testComputeRegionCopyinDistinctHostsInsideConvert"
+// CHECK-DAG: cr_dist_a#0 <-> cr_dist_b#0: NoAlias
+
+func.func @testComputeRegionCopyinDistinctHostsInsideConvert() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %cb = acc.copyin varPtr(%db : !fir.ref<f32>) -> !fir.ref<f32> {name = "b"}
+  acc.compute_region ins(%arg0 = %ca, %arg1 = %cb) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_dist_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_dist_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.kernels"}
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "testComputeRegionCreateDistinctHostsInsideConvert"
+// CHECK-DAG: cr_crt_a#0 <-> cr_crt_b#0: NoAlias
+
+func.func @testComputeRegionCreateDistinctHostsInsideConvert() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ta = acc.create varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %tb = acc.create varPtr(%db : !fir.ref<f32>) -> !fir.ref<f32> {name = "b"}
+  acc.compute_region ins(%arg0 = %ta, %arg1 = %tb) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_crt_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_crt_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.kernels"}
+  return
+}
+
+// -----
+
+// Same TARGET dummy copyins as testBothOutsideCopyinTargetDummyArgsMayAlias,
+// through acc.compute_region block args.
+// CHECK-LABEL: Testing : "testComputeRegionCopyinTargetDummiesMayAliasInsideConvert"
+// CHECK-DAG: cr_tgt_a#0 <-> cr_tgt_b#0: MayAlias
+
+func.func @testComputeRegionCopyinTargetDummiesMayAliasInsideConvert(%arg0: !fir.ref<f32> {fir.bindc_name = "x"}, %arg1: !fir.ref<f32> {fir.bindc_name = "y"}) {
+  %ds = fir.dummy_scope : !fir.dscope
+  %dx = fir.declare %arg0 dummy_scope %ds arg 1 {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFEex"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+  %dy = fir.declare %arg1 dummy_scope %ds arg 2 {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFEey"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+  %cx = acc.copyin varPtr(%dx : !fir.ref<f32>) -> !fir.ref<f32> {name = "x"}
+  %cy = acc.copyin varPtr(%dy : !fir.ref<f32>) -> !fir.ref<f32> {name = "y"}
+  acc.compute_region ins(%cr0 = %cx, %cr1 = %cy) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %cr0 {test.ptr = "cr_tgt_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %cr1 {test.ptr = "cr_tgt_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.parallel"}
+  return
+}
+
+// -----
+
+// Single host copyin wired twice through arguments; both block args alias the
+// same mapped host variable.
+// CHECK-LABEL: Testing : "testComputeRegionCopyinSameHostMustAliasInsideConvert"
+// CHECK-DAG: cr_must_a#0 <-> cr_must_b#0: MustAlias
+
+func.func @testComputeRegionCopyinSameHostMustAliasInsideConvert() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.compute_region ins(%arg0 = %ca, %arg1 = %ca) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_must_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_must_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.kernels"}
+  return
+}
+
+// -----
+
+// Distinct-host copyins passed as acc.kernels dataOperands.
+// CHECK-LABEL: Testing : "testKernelsCopyinDistinctHostsInsideConvert"
+// CHECK-DAG: kern_dist_a#0 <-> kern_dist_b#0: NoAlias
+
+func.func @testKernelsCopyinDistinctHostsInsideConvert() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %cb = acc.copyin varPtr(%db : !fir.ref<f32>) -> !fir.ref<f32> {name = "b"}
+  acc.kernels dataOperands(%ca, %cb : !fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %ca {test.ptr = "kern_dist_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %cb {test.ptr = "kern_dist_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "testKernelsCreateDistinctHostsInsideConvert"
+// CHECK-DAG: kern_crt_a#0 <-> kern_crt_b#0: NoAlias
+
+func.func @testKernelsCreateDistinctHostsInsideConvert() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ta = acc.create varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %tb = acc.create varPtr(%db : !fir.ref<f32>) -> !fir.ref<f32> {name = "b"}
+  acc.kernels dataOperands(%ta, %tb : !fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %ta {test.ptr = "kern_crt_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %tb {test.ptr = "kern_crt_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
+// TARGET dummy copyins as acc.kernels dataOperands.
+// CHECK-LABEL: Testing : "testKernelsCopyinTargetDummiesMayAliasInsideConvert"
+// CHECK-DAG: kern_tgt_a#0 <-> kern_tgt_b#0: MayAlias
+
+func.func @testKernelsCopyinTargetDummiesMayAliasInsideConvert(%arg0: !fir.ref<f32> {fir.bindc_name = "x"}, %arg1: !fir.ref<f32> {fir.bindc_name = "y"}) {
+  %ds = fir.dummy_scope : !fir.dscope
+  %dx = fir.declare %arg0 dummy_scope %ds arg 1 {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFEex"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+  %dy = fir.declare %arg1 dummy_scope %ds arg 2 {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFEey"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+  %cx = acc.copyin varPtr(%dx : !fir.ref<f32>) -> !fir.ref<f32> {name = "x"}
+  %cy = acc.copyin varPtr(%dy : !fir.ref<f32>) -> !fir.ref<f32> {name = "y"}
+  acc.kernels dataOperands(%cx, %cy : !fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %cx {test.ptr = "kern_tgt_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %cy {test.ptr = "kern_tgt_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
+// Same copyin value listed twice in dataOperands; both converts must alias.
+// CHECK-LABEL: Testing : "testKernelsCopyinSameHostMustAliasInsideConvert"
+// CHECK-DAG: kern_must_a#0 <-> kern_must_b#0: MustAlias
+
+func.func @testKernelsCopyinSameHostMustAliasInsideConvert() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.kernels dataOperands(%ca : !fir.ref<f32>) {
+    %va = fir.convert %ca {test.ptr = "kern_must_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %ca {test.ptr = "kern_must_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
+// acc.compute_region: both queried values are inside the region; test.ptr is on
+// fir.convert of each captured private (traces block operands without tagging
+// the region op).
+// CHECK-LABEL: Testing : "testComputeRegionPrivateInsideConvert"
+// CHECK-DAG: cr_priv_a#0 <-> cr_priv_b#0: NoAlias
+
+func.func @testComputeRegionPrivateInsideConvert() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %pa = acc.private varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %pb = acc.private varPtr(%db : !fir.ref<f32>) -> !fir.ref<f32> {name = "b"}
+  acc.compute_region ins(%arg0 = %pa, %arg1 = %pb) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_priv_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_priv_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.parallel"}
+  return
+}
+
+// -----
+
+// Same host ref as both acc.copyin and acc.private operands (two copyins would
+// must-alias); private-like mapping is a distinct allocation vs copyin.
+// CHECK-LABEL: Testing : "testComputeRegionCopyinVsPrivateSameHostNoAlias"
+// CHECK-DAG: cr_mix_cp#0 <-> cr_mix_pr#0: NoAlias
+
+func.func @testComputeRegionCopyinVsPrivateSameHostNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %pp = acc.private varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.compute_region ins(%arg0 = %ca, %arg1 = %pp) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_mix_cp"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_mix_pr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.parallel"}
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "testComputeRegionCreateVsPrivateSameHostNoAlias"
+// CHECK-DAG: cr_mix_cr#0 <-> cr_mix_pr2#0: NoAlias
+
+func.func @testComputeRegionCreateVsPrivateSameHostNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ta = acc.create varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %pp = acc.private varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.compute_region ins(%arg0 = %ta, %arg1 = %pp) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_mix_cr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_mix_pr2"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.parallel"}
+  return
+}
+
+// -----
+
+// Same host: acc.copyin vs acc.firstprivate.
+// CHECK-LABEL: Testing : "testComputeRegionCopyinVsFirstprivateSameHostNoAlias"
+// CHECK-DAG: cr_fp_cp#0 <-> cr_fp_pr#0: NoAlias
+
+func.func @testComputeRegionCopyinVsFirstprivateSameHostNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %pf = acc.firstprivate varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.compute_region ins(%arg0 = %ca, %arg1 = %pf) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_fp_cp"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_fp_pr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.parallel"}
+  return
+}
+
+// -----
+
+// Same host: acc.copyin vs acc.firstprivate_map.
+// CHECK-LABEL: Testing : "testComputeRegionCopyinVsFirstprivateMapSameHostNoAlias"
+// CHECK-DAG: cr_fpm_cp#0 <-> cr_fpm_fm#0: NoAlias
+
+func.func @testComputeRegionCopyinVsFirstprivateMapSameHostNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %fm = acc.firstprivate_map varPtr(%da : !fir.ref<f32>) varType(f32) -> !fir.ref<f32> {name = "a"}
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.compute_region ins(%arg0 = %ca, %arg1 = %fm) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_fpm_cp"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_fpm_fm"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.kernels"}
+  return
+}
+
+// -----
+
+// acc.kernels: same host as copyin and acc.private dataOperands.
+// CHECK-LABEL: Testing : "testKernelsCopyinVsPrivateSameHostNoAlias"
+// CHECK-DAG: k_mix_cp#0 <-> k_mix_pr#0: NoAlias
+
+func.func @testKernelsCopyinVsPrivateSameHostNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %pp = acc.private varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.kernels dataOperands(%ca : !fir.ref<f32>) private(%pp : !fir.ref<f32>) {
+    %va = fir.convert %ca {test.ptr = "k_mix_cp"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %pp {test.ptr = "k_mix_pr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "testKernelsCreateVsPrivateSameHostNoAlias"
+// CHECK-DAG: k_mix_cr#0 <-> k_mix_pr2#0: NoAlias
+
+func.func @testKernelsCreateVsPrivateSameHostNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ta = acc.create varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %pp = acc.private varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.kernels dataOperands(%ta : !fir.ref<f32>) private(%pp : !fir.ref<f32>) {
+    %va = fir.convert %ta {test.ptr = "k_mix_cr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %pp {test.ptr = "k_mix_pr2"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
+// acc.kernels: acc.copyin vs acc.firstprivate.
+// CHECK-LABEL: Testing : "testKernelsCopyinVsFirstprivateSameHostNoAlias"
+// CHECK-DAG: k_fp_cp#0 <-> k_fp_pr#0: NoAlias
+
+func.func @testKernelsCopyinVsFirstprivateSameHostNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  %pf = acc.firstprivate varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.kernels dataOperands(%ca : !fir.ref<f32>) firstprivate(%pf : !fir.ref<f32>) {
+    %va = fir.convert %ca {test.ptr = "k_fp_cp"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %pf {test.ptr = "k_fp_pr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
+// acc.kernels: acc.copyin vs acc.firstprivate_map.
+// CHECK-LABEL: Testing : "testKernelsCopyinVsFirstprivateMapSameHostNoAlias"
+// CHECK-DAG: k_fpm_cp#0 <-> k_fpm_fm#0: NoAlias
+
+func.func @testKernelsCopyinVsFirstprivateMapSameHostNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %fm = acc.firstprivate_map varPtr(%da : !fir.ref<f32>) varType(f32) -> !fir.ref<f32> {name = "a"}
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.kernels dataOperands(%ca : !fir.ref<f32>) firstprivate(%fm : !fir.ref<f32>) {
+    %va = fir.convert %ca {test.ptr = "k_fpm_cp"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %fm {test.ptr = "k_fpm_fm"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
+// acc.private inside acc.compute_region; ins carries acc.create from the host ref.
+// CHECK-LABEL: Testing : "testComputeRegionPrivateOpInsideVsInsCreateNoAlias"
+// CHECK-DAG: cr_body_pr#0 <-> cr_body_cr#0: NoAlias
+
+func.func @testComputeRegionPrivateOpInsideVsInsCreateNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %tc = acc.create varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.compute_region ins(%arg0 = %tc) : (!fir.ref<f32>) {
+    %pv = acc.private varPtr(%arg0 : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+    %vb = fir.convert %pv {test.ptr = "cr_body_pr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vc = fir.convert %arg0 {test.ptr = "cr_body_cr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.parallel"}
+  return
+}
+
+// -----
+
+// acc.private inside acc.kernels on the dataOperand copyin of the host ref.
+// CHECK-LABEL: Testing : "testKernelsPrivateOpInsideVsDataCopyinNoAlias"
+// CHECK-DAG: k_body_pr#0 <-> k_body_cp#0: NoAlias
+
+func.func @testKernelsPrivateOpInsideVsDataCopyinNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ca = acc.copyin varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.kernels dataOperands(%ca : !fir.ref<f32>) {
+    %pv = acc.private varPtr(%ca : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+    %vb = fir.convert %pv {test.ptr = "k_body_pr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vc = fir.convert %ca {test.ptr = "k_body_cp"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// -----
+
+acc.reduction.recipe @red_f32_aa : !fir.ref<f32> reduction_operator <add> init {
+^bb0(%arg0: !fir.ref<f32>):
+  %init = fir.alloca f32
+  acc.yield %init : !fir.ref<f32>
+} combiner {
+^bb0(%lhs: !fir.ref<f32>, %rhs: !fir.ref<f32>):
+  %lv = fir.load %lhs : !fir.ref<f32>
+  %rv = fir.load %rhs : !fir.ref<f32>
+  %s = arith.addf %lv, %rv : f32
+  %out = fir.alloca f32
+  fir.store %s to %out : !fir.ref<f32>
+  acc.yield %out : !fir.ref<f32>
+}
+
+// CHECK-LABEL: Testing : "testBothOutsideReductionDistinctHosts"
+// CHECK-DAG: red_a#0 <-> red_b#0: NoAlias
+
+func.func @testBothOutsideReductionDistinctHosts() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ra = acc.reduction varPtr(%da : !fir.ref<f32>) recipe(@red_f32_aa) -> !fir.ref<f32> {name = "a", test.ptr = "red_a"}
+  %rb = acc.reduction varPtr(%db : !fir.ref<f32>) recipe(@red_f32_aa) -> !fir.ref<f32> {name = "b", test.ptr = "red_b"}
+  return
+}
+
+// CHECK-LABEL: Testing : "testComputeRegionReductionDistinctHostsInsideConvert"
+// CHECK-DAG: cr_red_a#0 <-> cr_red_b#0: NoAlias
+
+func.func @testComputeRegionReductionDistinctHostsInsideConvert() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ra = acc.reduction varPtr(%da : !fir.ref<f32>) recipe(@red_f32_aa) -> !fir.ref<f32> {name = "a"}
+  %rb = acc.reduction varPtr(%db : !fir.ref<f32>) recipe(@red_f32_aa) -> !fir.ref<f32> {name = "b"}
+  acc.compute_region ins(%arg0 = %ra, %arg1 = %rb) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_red_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_red_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.parallel"}
+  return
+}
+
+// CHECK-LABEL: Testing : "testKernelsReductionDistinctHostsInsideConvert"
+// CHECK-DAG: kern_red_a#0 <-> kern_red_b#0: NoAlias
+
+func.func @testKernelsReductionDistinctHostsInsideConvert() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %b = fir.alloca f32 {uniq_name = "_QFEb"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ra = acc.reduction varPtr(%da : !fir.ref<f32>) recipe(@red_f32_aa) -> !fir.ref<f32> {name = "a"}
+  %rb = acc.reduction varPtr(%db : !fir.ref<f32>) recipe(@red_f32_aa) -> !fir.ref<f32> {name = "b"}
+  acc.kernels reduction(%ra, %rb : !fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %ra {test.ptr = "kern_red_a"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %rb {test.ptr = "kern_red_b"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.terminator
+  }
+  return
+}
+
+// CHECK-LABEL: Testing : "testComputeRegionReductionVsPrivateSameHostNoAlias"
+// CHECK-DAG: cr_mix_rd#0 <-> cr_mix_pr3#0: NoAlias
+
+func.func @testComputeRegionReductionVsPrivateSameHostNoAlias() {
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %ra = acc.reduction varPtr(%da : !fir.ref<f32>) recipe(@red_f32_aa) -> !fir.ref<f32> {name = "a"}
+  %pp = acc.private varPtr(%da : !fir.ref<f32>) -> !fir.ref<f32> {name = "a"}
+  acc.compute_region ins(%arg0 = %ra, %arg1 = %pp) : (!fir.ref<f32>, !fir.ref<f32>) {
+    %va = fir.convert %arg0 {test.ptr = "cr_mix_rd"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    %vb = fir.convert %arg1 {test.ptr = "cr_mix_pr3"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    acc.yield
+  } {origin = "acc.kernels"}
+  return
+}
+
+// -----
+
+// Allocas inside acc.parallel.
+// CHECK-LABEL: Testing : "testBothInsideParallel"
+// CHECK-DAG: par_a#0 <-> par_b#0: NoAlias
+
+func.func @testBothInsideParallel() {
+  acc.parallel {
+    %a = fir.alloca f32 {uniq_name = "_QFEa", test.ptr = "par_a"}
+    %b = fir.alloca f32 {uniq_name = "_QFEb", test.ptr = "par_b"}
+    acc.yield
+  }
+  return
+}
+
+// -----
+
+// Allocas inside acc.kernels.
+// CHECK-LABEL: Testing : "testBothInsideKernels"
+// CHECK-DAG: kern_a#0 <-> kern_b#0: NoAlias
+
+func.func @testBothInsideKernels() {
+  acc.kernels {
+    %a = fir.alloca f32 {uniq_name = "_QFEa", test.ptr = "kern_a"}
+    %b = fir.alloca f32 {uniq_name = "_QFEb", test.ptr = "kern_b"}
+    acc.terminator
+  }
+  return
+}
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
index 9e7a15feb2e70..682e8ff9784c8 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
@@ -53,6 +53,8 @@
       mlir::acc::UpdateDeviceOp, mlir::acc::UseDeviceOp,                       \
       mlir::acc::ReductionOp, mlir::acc::DeclareDeviceResidentOp,              \
       mlir::acc::DeclareLinkOp, mlir::acc::CacheOp
+#define ACC_DATA_ENTRY_AND_INIT_OPS                                            \
+  ACC_DATA_ENTRY_OPS, mlir::acc::ReductionInitOp
 #define ACC_DATA_EXIT_OPS                                                      \
   mlir::acc::CopyoutOp, mlir::acc::DeleteOp, mlir::acc::DetachOp,              \
       mlir::acc::UpdateHostOp
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
index dd3d34b8252d3..c26ddbd54f1b9 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h
@@ -29,6 +29,11 @@ namespace acc {
 /// `ACC_COMPUTE_CONSTRUCT_OPS`.
 mlir::Operation *getEnclosingComputeOp(mlir::Region &region);
 
+/// If `v` is not a block argument of an `acc.compute_region` body, returns
+/// nullptr. Otherwise maps the block argument to its operand and returns the
+/// defining operation if it is one of `ACC_DATA_ENTRY_OPS`.
+mlir::Operation *getACCDataClauseOpForBlockArg(mlir::Value v);
+
 /// Returns true if this value is only used by `acc.private` operations in the
 /// `region`.
 bool isOnlyUsedByPrivateClauses(mlir::Value val, mlir::Region &region);
diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
index 1c63760a6984b..1cc313206a99f 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp
@@ -25,6 +25,22 @@ mlir::Operation *mlir::acc::getEnclosingComputeOp(mlir::Region &region) {
       .getParentOfType<ACC_COMPUTE_CONSTRUCT_OPS, mlir::acc::ComputeRegionOp>();
 }
 
+mlir::Operation *mlir::acc::getACCDataClauseOpForBlockArg(mlir::Value v) {
+  auto barg = mlir::dyn_cast<mlir::BlockArgument>(v);
+  if (!barg)
+    return nullptr;
+
+  mlir::Block *block = barg.getOwner();
+  auto computeReg =
+      mlir::dyn_cast<mlir::acc::ComputeRegionOp>(block->getParentOp());
+  if (!computeReg || block != computeReg.getBody())
+    return nullptr;
+
+  mlir::Value orig = computeReg.getOperand(barg);
+  mlir::Operation *def = orig.getDefiningOp();
+  return mlir::isa_and_nonnull<ACC_DATA_ENTRY_OPS>(def) ? def : nullptr;
+}
+
 template <typename OpTy>
 static bool isOnlyUsedByOpClauses(mlir::Value val, mlir::Region &region) {
   auto checkIfUsedOnlyByOpInside = [&](mlir::Operation *user) {

>From 3f192a703ce5e2b5ae4b36803f1bf743af8160d2 Mon Sep 17 00:00:00 2001
From: Razvan Lupusoru <rlupusoru at nvidia.com>
Date: Tue, 31 Mar 2026 16:57:26 -0700
Subject: [PATCH 2/4] Add missing deps and small header cleanup

---
 flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h | 1 -
 flang/lib/Optimizer/Analysis/CMakeLists.txt                     | 2 ++
 flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp         | 1 -
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
index 840622b7dd0f9..ee818dbd23dc2 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
@@ -18,7 +18,6 @@
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/Operation.h"
 #include "mlir/IR/Value.h"
-#include "llvm/ADT/STLFunctionalExtras.h"
 #include <string>
 
 namespace fir {
diff --git a/flang/lib/Optimizer/Analysis/CMakeLists.txt b/flang/lib/Optimizer/Analysis/CMakeLists.txt
index c017708e35ddf..b370b9708f825 100644
--- a/flang/lib/Optimizer/Analysis/CMakeLists.txt
+++ b/flang/lib/Optimizer/Analysis/CMakeLists.txt
@@ -20,6 +20,7 @@ add_flang_library(FIRAnalysis
   MLIR_DEPS
   MLIRIR
   MLIROpenACCDialect
+  MLIROpenACCUtils
   MLIROpenMPDialect
 
   MLIR_LIBS
@@ -27,5 +28,6 @@ add_flang_library(FIRAnalysis
   MLIRLLVMDialect
   MLIRMathTransforms
   MLIROpenACCDialect
+  MLIROpenACCUtils
   MLIROpenMPDialect
 )
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index 39ccd3aad1598..562e4cb69821d 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -30,7 +30,6 @@
 #include "mlir/Interfaces/ViewLikeInterface.h"
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/Support/raw_ostream.h"
-#include <mlir/Support/LLVM.h>
 
 using namespace mlir;
 

>From ab2e398a36dab9fd14381cd893aa7ebc5fc5c4f2 Mon Sep 17 00:00:00 2001
From: Razvan Lupusoru <rlupusoru at nvidia.com>
Date: Tue, 31 Mar 2026 17:00:32 -0700
Subject: [PATCH 3/4] fix format

---
 flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index 562e4cb69821d..ff8991d6a4833 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -647,8 +647,7 @@ fir::AliasAnalysis::Source fir::acc::getSourceFromACCValue(
   // queries, so using the host source remains a reasonable tradeoff for
   // disambiguating in-region uses. Finer modeling would require extending
   // AliasAnalysis::Source (with address space) and teaching AA to use it.
-  fir::AliasAnalysis::Source traced =
-      traceValue(mlir::acc::getVar(accOp));
+  fir::AliasAnalysis::Source traced = traceValue(mlir::acc::getVar(accOp));
   traced.attributes |= accumulatedAttrs;
   return traced;
 }

>From 7e8a51db94f31973a0aad34c52d18f1c7e94f578 Mon Sep 17 00:00:00 2001
From: Razvan Lupusoru <rlupusoru at nvidia.com>
Date: Tue, 31 Mar 2026 17:06:11 -0700
Subject: [PATCH 4/4] Rename traceValue to getSourceFn

---
 .../flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h     | 4 ++--
 flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp   | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
index ee818dbd23dc2..497581064b707 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
@@ -25,14 +25,14 @@ namespace acc {
 
 /// Memory source for `mappedValue` when it is produced by OpenACC mapping op
 /// `accOp` . Private-like ops return `SourceKind::Allocate`; other data clauses
-/// trace the host variable via `traceValue`.
+/// classify the host variable via `getSourceFn`.
 ///
 /// `accumulatedAttrs` collects Fortran variable attributes from the path from
 /// the queried value to `accOp` (e.g. [hl]fir.declare); they are merged into
 /// the returned source so TARGET/POINTER/INTENT_IN are not dropped.
 fir::AliasAnalysis::Source getSourceFromACCValue(
     mlir::Value mappedValue, mlir::Operation *accOp,
-    llvm::function_ref<fir::AliasAnalysis::Source(mlir::Value)> traceValue,
+    llvm::function_ref<fir::AliasAnalysis::Source(mlir::Value)> getSourceFn,
     bool originIsData,
     fir::AliasAnalysis::Source::Attributes accumulatedAttrs = {});
 
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index ff8991d6a4833..cd8e190208757 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -627,7 +627,7 @@ static bool isACCPrivateLikeOp(mlir::Operation *op) {
 
 fir::AliasAnalysis::Source fir::acc::getSourceFromACCValue(
     mlir::Value mappedValue, mlir::Operation *accOp,
-    llvm::function_ref<fir::AliasAnalysis::Source(mlir::Value)> traceValue,
+    llvm::function_ref<fir::AliasAnalysis::Source(mlir::Value)> getSourceFn,
     bool originIsData,
     fir::AliasAnalysis::Source::Attributes accumulatedAttrs) {
   assert(accOp && "OpenACC mapping op required");
@@ -647,9 +647,9 @@ fir::AliasAnalysis::Source fir::acc::getSourceFromACCValue(
   // queries, so using the host source remains a reasonable tradeoff for
   // disambiguating in-region uses. Finer modeling would require extending
   // AliasAnalysis::Source (with address space) and teaching AA to use it.
-  fir::AliasAnalysis::Source traced = traceValue(mlir::acc::getVar(accOp));
-  traced.attributes |= accumulatedAttrs;
-  return traced;
+  fir::AliasAnalysis::Source source = getSourceFn(mlir::acc::getVar(accOp));
+  source.attributes |= accumulatedAttrs;
+  return source;
 }
 
 mlir::Value fir::acc::getOriginalDef(mlir::Value value, bool stripDeclare) {



More information about the flang-commits mailing list