[flang-commits] [flang] [llvm] [flang][OpenMP] Fix use_device_addr handling for COMMON blocks in target data (PR #217105)

Sairudra More via flang-commits flang-commits at lists.llvm.org
Thu Aug 27 08:15:26 PDT 2026


https://github.com/Saieiei updated https://github.com/llvm/llvm-project/pull/217105

>From e20b22e3751d15b0782c459dd259966e5e75d633 Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Tue, 18 Aug 2026 10:54:38 -0500
Subject: [PATCH 1/5] Fix use_device_addr for COMMON blocks in target data

---
 flang/lib/Lower/OpenMP/ClauseProcessor.cpp    |  25 ++-
 flang/lib/Lower/OpenMP/OpenMP.cpp             |  31 ++-
 flang/lib/Semantics/resolve-directives.cpp    |  26 ++-
 ...rget-data-use-device-addr-common-block.f90 | 197 ++++++++++++++++++
 .../OpenMP/use-device-addr-common-block.f90   | 102 +++++++++
 5 files changed, 375 insertions(+), 6 deletions(-)
 create mode 100644 flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
 create mode 100644 flang/test/Semantics/OpenMP/use-device-addr-common-block.f90

diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 5a554def351ea..70cc1b621ca14 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -2204,6 +2204,15 @@ bool ClauseProcessor::processEnter(
 bool ClauseProcessor::processUseDeviceAddr(
     lower::StatementContext &stmtCtx, mlir::omp::UseDeviceAddrClauseOps &result,
     llvm::SmallVectorImpl<Object> &useDeviceObjects) const {
+  llvm::SmallPtrSet<const semantics::Symbol *, 4> mappedCommonBlocks;
+  findRepeatableClause<omp::clause::Map>(
+      [&](const omp::clause::Map &clause, const parser::CharBlock &) {
+        for (const Object &object : std::get<ObjectList>(clause.t)) {
+          if (object.sym()->has<semantics::CommonBlockDetails>())
+            mappedCommonBlocks.insert(&object.sym()->GetUltimate());
+        }
+      });
+
   std::map<Object, OmpMapParentAndMemberData> parentMemberIndices;
   bool clauseFound = findRepeatableClause<omp::clause::UseDeviceAddr>(
       [&](const omp::clause::UseDeviceAddr &clause,
@@ -2211,7 +2220,21 @@ bool ClauseProcessor::processUseDeviceAddr(
         mlir::Location location = converter.genLocation(source);
         mlir::omp::ClauseMapFlags mapTypeBits =
             mlir::omp::ClauseMapFlags::return_param;
-        processMapObjects(stmtCtx, location, clause.v, mapTypeBits,
+        ObjectList objects;
+        // Keep the aggregate only when it pairs with a whole-COMMON map on
+        // this directive; otherwise preserve named-COMMON member equivalence.
+        for (const Object &object : clause.v) {
+          auto *details =
+              object.sym()->detailsIf<semantics::CommonBlockDetails>();
+          if (!details ||
+              mappedCommonBlocks.contains(&object.sym()->GetUltimate())) {
+            objects.push_back(object);
+            continue;
+          }
+          for (const auto &member : details->objects())
+            objects.push_back(Object{&*member, std::nullopt});
+        }
+        processMapObjects(stmtCtx, location, objects, mapTypeBits,
                           parentMemberIndices, result.useDeviceAddrVars,
                           useDeviceObjects);
       });
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index ae1eb897c9348..c3c1317aa6ef7 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -133,11 +133,13 @@ struct ObjectEntryBlockArgs {
   ObjectEntryBlockArgsEntry taskReduction;
   ObjectEntryBlockArgsEntry useDeviceAddr;
   ObjectEntryBlockArgsEntry useDevicePtr;
+  std::size_t sourceUseDeviceAddrCount{0};
 
   bool isValid() const {
     return hasDeviceAddr.isValid() && inReduction.isValid() && map.isValid() &&
            priv.isValid() && reduction.isValid() && taskReduction.isValid() &&
-           useDeviceAddr.isValid() && useDevicePtr.isValid();
+           useDeviceAddr.isValid() && useDevicePtr.isValid() &&
+           sourceUseDeviceAddrCount <= useDeviceAddr.objects.size();
   }
 
   llvm::SmallVector<const semantics::Symbol *> getSyms() const {
@@ -2252,6 +2254,12 @@ static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info,
   marker->erase();
 }
 
+static void genIntermediateCommonBlockAccessors(
+    Fortran::lower::AbstractConverter &converter,
+    const mlir::Location &currentLocation,
+    llvm::ArrayRef<mlir::BlockArgument> mapBlockArgs,
+    llvm::ArrayRef<const Fortran::semantics::Symbol *> mapSyms);
+
 static void genBodyOfTargetDataOp(
     lower::AbstractConverter &converter, lower::SymMap &symTable,
     semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,
@@ -2262,6 +2270,15 @@ static void genBodyOfTargetDataOp(
 
   genEntryBlock(firOpBuilder, args.asEntryBlockArgs(), dataOp.getRegion());
   bindEntryBlockArgs(converter, dataOp, args);
+  auto argIface = llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(*dataOp);
+  llvm::SmallVector<const semantics::Symbol *> sourceUseDeviceAddrSyms{
+      args.useDeviceAddr.getSyms()};
+  genIntermediateCommonBlockAccessors(
+      converter, currentLocation,
+      argIface.getUseDeviceAddrBlockArgs().take_front(
+          args.sourceUseDeviceAddrCount),
+      llvm::ArrayRef(sourceUseDeviceAddrSyms)
+          .take_front(args.sourceUseDeviceAddrCount));
 
   // Insert dummy instruction to remember the insertion position. The
   // marker will be deleted by clean up passes since there are no uses.
@@ -2299,7 +2316,7 @@ static void genBodyOfTargetDataOp(
 // When the scope changes, the bindings to the intermediate accessors should
 // be dropped in place of the original symbol bindings.
 //
-// This is for utilisation with TargetOp.
+// This is for utilisation with TargetOp and TargetDataOp.
 static void genIntermediateCommonBlockAccessors(
     Fortran::lower::AbstractConverter &converter,
     const mlir::Location &currentLocation,
@@ -2749,12 +2766,15 @@ static void genTargetDataClauses(
     lower::StatementContext &stmtCtx, const List<Clause> &clauses,
     mlir::Location loc, mlir::omp::TargetDataOperands &clauseOps,
     llvm::SmallVectorImpl<Object> &useDeviceAddrObjects,
-    llvm::SmallVectorImpl<Object> &useDevicePtrObjects) {
+    llvm::SmallVectorImpl<Object> &useDevicePtrObjects,
+    std::size_t &sourceUseDeviceAddrCount) {
   ClauseProcessor cp(converter, semaCtx, clauses);
   cp.processDevice(stmtCtx, clauseOps);
   cp.processIf(llvm::omp::Directive::OMPD_target_data, clauseOps);
   cp.processMap(loc, stmtCtx, clauseOps);
   cp.processUseDeviceAddr(stmtCtx, clauseOps, useDeviceAddrObjects);
+  // Record the source UDA prefix before non-C_PTR UDP operands are promoted.
+  sourceUseDeviceAddrCount = useDeviceAddrObjects.size();
   cp.processUseDevicePtr(stmtCtx, clauseOps, useDevicePtrObjects);
 
   // This function implements the deprecated functionality of use_device_ptr
@@ -4091,8 +4111,10 @@ static mlir::omp::TargetDataOp genTargetDataOp(
     const ConstructQueue &queue, ConstructQueue::const_iterator item) {
   mlir::omp::TargetDataOperands clauseOps;
   llvm::SmallVector<Object> useDeviceAddrObjects, useDevicePtrObjects;
+  std::size_t sourceUseDeviceAddrCount;
   genTargetDataClauses(converter, semaCtx, stmtCtx, item->clauses, loc,
-                       clauseOps, useDeviceAddrObjects, useDevicePtrObjects);
+                       clauseOps, useDeviceAddrObjects, useDevicePtrObjects,
+                       sourceUseDeviceAddrCount);
 
   auto targetDataOp = mlir::omp::TargetDataOp::create(
       converter.getFirOpBuilder(), loc, clauseOps);
@@ -4107,6 +4129,7 @@ static mlir::omp::TargetDataOp genTargetDataOp(
   args.useDeviceAddr.vars = useDeviceAddrBaseValues;
   args.useDevicePtr.objects = useDevicePtrObjects;
   args.useDevicePtr.vars = useDevicePtrBaseValues;
+  args.sourceUseDeviceAddrCount = sourceUseDeviceAddrCount;
 
   genBodyOfTargetDataOp(converter, symTable, semaCtx, eval, targetDataOp, args,
                         loc, queue, item);
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 15bb84d7e486f..73dedc79b8528 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -61,6 +61,7 @@ template <typename T> class DirectiveAttributeVisitor {
     Scope &scope;
     Symbol::Flag defaultDSA{Symbol::Flag::AccShared}; // TODOACC
     std::map<const Symbol *, Symbol::Flag> objectWithDSA;
+    std::map<const Symbol *, Symbol::Flag> commonBlockClauseFlags;
     std::map<parser::OmpVariableCategory::Value,
         parser::OmpDefaultmapClause::ImplicitBehavior>
         defaultMap;
@@ -3194,7 +3195,30 @@ void OmpAttributeVisitor::ResolveOmpCommonBlock(
   Symbol *originalCB{ResolveOmpCommonBlockName(&cbName)};
   if (auto *symbol{cbResolved ? name.symbol : originalCB}) {
     if (!dataCopyingAttributeFlags.test(ompFlag)) {
-      CheckMultipleAppearances(name, *symbol, Symbol::Flag::OmpCommonBlock);
+      auto isMapFlag{[](Symbol::Flag flag) {
+        return flag == Symbol::Flag::OmpMapTo ||
+            flag == Symbol::Flag::OmpMapFrom ||
+            flag == Symbol::Flag::OmpMapToFrom ||
+            flag == Symbol::Flag::OmpMapStorage ||
+            flag == Symbol::Flag::OmpMapDelete;
+      }};
+      const Symbol *commonBlock{&symbol->GetUltimate()};
+      auto [it, inserted]{GetContext().commonBlockClauseFlags.try_emplace(
+          commonBlock, ompFlag)};
+      bool mapUseDeviceAddrPair{
+          GetContext().directive == llvm::omp::Directive::OMPD_target_data &&
+          !inserted &&
+          ((isMapFlag(it->second) &&
+               ompFlag == Symbol::Flag::OmpUseDeviceAddr) ||
+              (it->second == Symbol::Flag::OmpUseDeviceAddr &&
+                  isMapFlag(ompFlag)))};
+      if (mapUseDeviceAddrPair) {
+        // Mark the one permitted overlap as consumed so a third appearance is
+        // still diagnosed.
+        it->second = Symbol::Flag::OmpCommonBlock;
+      } else {
+        CheckMultipleAppearances(name, *symbol, Symbol::Flag::OmpCommonBlock);
+      }
     }
     // 2.15.3 When a named common block appears in a list, it has the
     // same meaning as if every explicit member of the common block
diff --git a/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90 b/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
new file mode 100644
index 0000000000000..fa3689e386a68
--- /dev/null
+++ b/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
@@ -0,0 +1,197 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-llvm -fopenmp -fopenmp-version=50 %s -o /dev/null
+
+! CHECK-LABEL: func.func @_QPstandalone_common
+! CHECK: %[[STANDALONE_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "x"}
+! CHECK: %[[STANDALONE_Y_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "y"}
+! CHECK: omp.target_data use_device_addr(%[[STANDALONE_X_MAP]] -> %[[STANDALONE_X_ARG:.*]], %[[STANDALONE_Y_MAP]] -> %[[STANDALONE_Y_ARG:.*]] : {{.*}}) {
+! CHECK: %[[STANDALONE_X:.*]]:2 = hlfir.declare %[[STANDALONE_X_ARG]]
+! CHECK: %[[STANDALONE_Y:.*]]:2 = hlfir.declare %[[STANDALONE_Y_ARG]]
+! CHECK: %[[STANDALONE_LOAD:.*]] = fir.load %[[STANDALONE_Y]]#0
+! CHECK: %[[STANDALONE_SUM:.*]] = arith.addi %[[STANDALONE_LOAD]]
+! CHECK: hlfir.assign %[[STANDALONE_SUM]] to %[[STANDALONE_X]]#0
+subroutine standalone_common
+  integer :: x, y
+  common /standalone/ x, y
+  !$omp target data use_device_addr(/standalone/)
+    x = y + 1
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPall_cptr_common
+! CHECK-NOT: use_device_ptr
+! CHECK: %[[CPTR_P_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "p"}
+! CHECK: %[[CPTR_Q_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "q"}
+! CHECK: omp.target_data use_device_addr(%[[CPTR_P_MAP]] -> %[[CPTR_P_ARG:.*]], %[[CPTR_Q_MAP]] -> %[[CPTR_Q_ARG:.*]] : {{.*}}) {
+! CHECK-NOT: use_device_ptr
+! CHECK: %[[CPTR_P:.*]]:2 = hlfir.declare %[[CPTR_P_ARG]]
+! CHECK: %[[CPTR_Q:.*]]:2 = hlfir.declare %[[CPTR_Q_ARG]]
+! CHECK: hlfir.assign %[[CPTR_Q]]#0 to %[[CPTR_P]]#0
+subroutine all_cptr_common
+  use iso_c_binding, only : c_ptr
+  type(c_ptr) :: p, q
+  common /all_cptr/ p, q
+  !$omp target data use_device_addr(/all_cptr/)
+    p = q
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmixed_common
+! CHECK-NOT: use_device_ptr
+! CHECK: %[[MIXED_P_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "p"}
+! CHECK: %[[MIXED_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "x"}
+! CHECK: omp.target_data use_device_addr(%[[MIXED_P_MAP]] -> %[[MIXED_P_ARG:.*]], %[[MIXED_X_MAP]] -> %[[MIXED_X_ARG:.*]] : {{.*}}) {
+! CHECK-NOT: use_device_ptr
+! CHECK: %[[MIXED_P:.*]]:2 = hlfir.declare %[[MIXED_P_ARG]]
+! CHECK: %[[MIXED_X:.*]]:2 = hlfir.declare %[[MIXED_X_ARG]]
+! CHECK: %[[MIXED_LOAD:.*]] = fir.load %[[MIXED_X]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[MIXED_X]]#0
+subroutine mixed_common
+  use iso_c_binding, only : c_ptr
+  type(c_ptr) :: p
+  integer :: x
+  common /mixed/ p, x
+  !$omp target data use_device_addr(/mixed/)
+    x = x + 1
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmapped_common
+! CHECK: %[[MAPPED_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {name = "mapped"}
+! CHECK: %[[MAPPED_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "mapped"}
+! CHECK: omp.target_data map_entries(%[[MAPPED_MAP]] : {{.*}}) use_device_addr(%[[MAPPED_UDA]] -> %[[MAPPED_ARG:.*]] : {{.*}}) {
+! CHECK: %[[MAPPED_X_COORD:.*]] = fir.coordinate_of %[[MAPPED_ARG]], %{{.*}}
+! CHECK: %[[MAPPED_X_REF:.*]] = fir.convert %[[MAPPED_X_COORD]]
+! CHECK: %[[MAPPED_X:.*]]:2 = hlfir.declare %[[MAPPED_X_REF]] storage(%[[MAPPED_ARG]][0])
+! CHECK: %[[MAPPED_Y_COORD:.*]] = fir.coordinate_of %[[MAPPED_ARG]], %{{.*}}
+! CHECK: %[[MAPPED_Y_REF:.*]] = fir.convert %[[MAPPED_Y_COORD]]
+! CHECK: %[[MAPPED_Y:.*]]:2 = hlfir.declare %[[MAPPED_Y_REF]] storage(%[[MAPPED_ARG]][4])
+! CHECK: %[[MAPPED_LOAD:.*]] = fir.load %[[MAPPED_Y]]#0
+! CHECK: %[[MAPPED_SUM:.*]] = arith.addi %[[MAPPED_LOAD]]
+! CHECK: hlfir.assign %[[MAPPED_SUM]] to %[[MAPPED_X]]#0
+subroutine mapped_common
+  integer :: x, y
+  common /mapped/ x, y
+  !$omp target data map(tofrom: /mapped/) use_device_addr(/mapped/)
+    x = y + 1
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPexplicit_map_common
+! CHECK: %[[EXPLICIT_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {name = "x"}
+! CHECK: %[[EXPLICIT_Y_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {name = "y"}
+! CHECK: %[[EXPLICIT_X_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "x"}
+! CHECK: %[[EXPLICIT_Y_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "y"}
+! CHECK: omp.target_data map_entries(%[[EXPLICIT_X_MAP]], %[[EXPLICIT_Y_MAP]] : {{.*}}) use_device_addr(%[[EXPLICIT_X_UDA]] -> %[[EXPLICIT_X_ARG:.*]], %[[EXPLICIT_Y_UDA]] -> %[[EXPLICIT_Y_ARG:.*]] : {{.*}}) {
+! CHECK: %[[EXPLICIT_X:.*]]:2 = hlfir.declare %[[EXPLICIT_X_ARG]]
+! CHECK: %[[EXPLICIT_Y:.*]]:2 = hlfir.declare %[[EXPLICIT_Y_ARG]]
+! CHECK: %[[EXPLICIT_LOAD:.*]] = fir.load %[[EXPLICIT_Y]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[EXPLICIT_X]]#0
+subroutine explicit_map_common
+  integer :: x, y
+  common /explicit_map/ x, y
+  !$omp target data map(tofrom: x, y) use_device_addr(/explicit_map/)
+    x = y + 1
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPouter_map_common
+! CHECK: %[[OUTER_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {name = "outer_map"}
+! CHECK: omp.target_data map_entries(%[[OUTER_MAP]] : {{.*}}) {
+! CHECK: %[[OUTER_X_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "x"}
+! CHECK: %[[OUTER_Y_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "y"}
+! CHECK: omp.target_data use_device_addr(%[[OUTER_X_UDA]] -> %[[OUTER_X_ARG:.*]], %[[OUTER_Y_UDA]] -> %[[OUTER_Y_ARG:.*]] : {{.*}}) {
+! CHECK: %[[OUTER_X:.*]]:2 = hlfir.declare %[[OUTER_X_ARG]]
+! CHECK: %[[OUTER_Y:.*]]:2 = hlfir.declare %[[OUTER_Y_ARG]]
+! CHECK: %[[OUTER_LOAD:.*]] = fir.load %[[OUTER_Y]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[OUTER_X]]#0
+subroutine outer_map_common
+  integer :: x, y
+  common /outer_map/ x, y
+  !$omp target data map(tofrom: /outer_map/)
+    !$omp target data use_device_addr(/outer_map/)
+      x = y + 1
+    !$omp end target data
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPreordered_common
+! CHECK: %[[SECOND_R_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "r"}
+! CHECK: %[[SECOND_S_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "s"}
+! CHECK: %[[PTR_CHILD:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = ""}
+! CHECK: %[[PTR_MAP:.*]] = omp.map.info {{.*}} members(%[[PTR_CHILD]] {{.*}}) {{.*}} {name = "ptr"}
+! CHECK: %[[PTR_ATTACH:.*]] = omp.map.info {{.*}} map_clauses(attach, ref_ptr, ref_ptee) {{.*}} {name = "ptr"}
+! CHECK: %[[FIRST_A_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "a"}
+! CHECK: %[[FIRST_B_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "b"}
+! CHECK: omp.target_data map_entries(%[[PTR_ATTACH]] : {{.*}}) use_device_addr(%[[SECOND_R_MAP]] -> %[[SECOND_R_ARG:.*]], %[[SECOND_S_MAP]] -> %[[SECOND_S_ARG:.*]], %[[PTR_MAP]] -> %[[PTR_ARG:.*]], %[[FIRST_A_MAP]] -> %[[FIRST_A_ARG:.*]], %[[FIRST_B_MAP]] -> %[[FIRST_B_ARG:.*]], %[[PTR_CHILD]] -> %[[PTR_CHILD_ARG:.*]] : {{.*}}) {
+! CHECK: %[[SECOND_R:.*]]:2 = hlfir.declare %[[SECOND_R_ARG]]
+! CHECK: %[[SECOND_S:.*]]:2 = hlfir.declare %[[SECOND_S_ARG]]
+! CHECK: %[[PTR:.*]]:2 = hlfir.declare %[[PTR_ARG]]
+! CHECK: %[[FIRST_A:.*]]:2 = hlfir.declare %[[FIRST_A_ARG]]
+! CHECK: %[[FIRST_B:.*]]:2 = hlfir.declare %[[FIRST_B_ARG]]
+! CHECK: %[[SECOND_LOAD:.*]] = fir.load %[[SECOND_S]]#0
+! CHECK: %[[FIRST_LOAD:.*]] = fir.load %[[FIRST_A]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[SECOND_R]]#0
+! CHECK: %[[FIRST_B_LOAD:.*]] = fir.load %[[FIRST_B]]#0
+! CHECK: %[[PTR_LOAD:.*]] = fir.load %[[PTR]]#0
+subroutine reordered_common(ptr)
+  integer, pointer :: ptr
+  integer :: a, b
+  real :: r, s
+  common /first/ a, b
+  common /second/ r, s
+  !$omp target data use_device_addr(/second/, ptr, /first/)
+    r = s + real(a)
+    ptr = b
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPnested_common
+! CHECK: %[[HOST_X:.*]]:2 = hlfir.declare {{.*}} storage({{.*}}[0])
+! CHECK: %[[HOST_Y:.*]]:2 = hlfir.declare {{.*}} storage({{.*}}[4])
+! CHECK: omp.target_data use_device_addr({{.*}} -> %[[OUTER_X_ARG:.*]], {{.*}} -> %[[OUTER_Y_ARG:.*]] : {{.*}}) {
+! CHECK: %[[NESTED_OUTER_X:.*]]:2 = hlfir.declare %[[OUTER_X_ARG]]
+! CHECK: %[[NESTED_OUTER_Y:.*]]:2 = hlfir.declare %[[OUTER_Y_ARG]]
+! CHECK: %[[OUTER_INITIAL_LOAD:.*]] = fir.load %[[NESTED_OUTER_Y]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[NESTED_OUTER_X]]#0
+! CHECK: omp.target_data use_device_addr({{.*}} -> %[[INNER_X_ARG:.*]], {{.*}} -> %[[INNER_Y_ARG:.*]] : {{.*}}) {
+! CHECK: %[[NESTED_INNER_X:.*]]:2 = hlfir.declare %[[INNER_X_ARG]]
+! CHECK: %[[NESTED_INNER_Y:.*]]:2 = hlfir.declare %[[INNER_Y_ARG]]
+! CHECK: %[[INNER_LOAD:.*]] = fir.load %[[NESTED_INNER_X]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[NESTED_INNER_Y]]#0
+! CHECK: %[[OUTER_RESTORED_LOAD:.*]] = fir.load %[[NESTED_OUTER_Y]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[NESTED_OUTER_X]]#0
+! CHECK: %[[HOST_RESTORED_LOAD:.*]] = fir.load %[[HOST_X]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[HOST_Y]]#0
+subroutine nested_common
+  integer :: x, y
+  common /nested/ x, y
+  !$omp target data use_device_addr(/nested/)
+    x = y + 1
+    !$omp target data use_device_addr(/nested/)
+      y = x + 2
+    !$omp end target data
+    x = y + 3
+  !$omp end target data
+  y = x + 4
+end subroutine
+
+! CHECK-LABEL: func.func @_QPunstructured_common
+! CHECK: omp.target_data use_device_addr({{.*}} -> %[[UNSTRUCTURED_X_ARG:.*]], {{.*}} -> %[[UNSTRUCTURED_Y_ARG:.*]] : {{.*}}) {
+! CHECK: %[[UNSTRUCTURED_X:.*]]:2 = hlfir.declare %[[UNSTRUCTURED_X_ARG]]
+! CHECK: %[[UNSTRUCTURED_Y:.*]]:2 = hlfir.declare %[[UNSTRUCTURED_Y_ARG]]
+! CHECK: %[[UNSTRUCTURED_Y_LOAD:.*]] = fir.load %[[UNSTRUCTURED_Y]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[UNSTRUCTURED_X]]#0
+! CHECK: cf.br ^[[DEST:.*]]
+! CHECK: ^[[DEST]]:
+! CHECK: %[[UNSTRUCTURED_X_LOAD:.*]] = fir.load %[[UNSTRUCTURED_X]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[UNSTRUCTURED_Y]]#0
+subroutine unstructured_common
+  integer :: x, y
+  common /unstructured/ x, y
+  !$omp target data use_device_addr(/unstructured/)
+    x = y + 1
+    goto 10
+10  y = x + 2
+  !$omp end target data
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/use-device-addr-common-block.f90 b/flang/test/Semantics/OpenMP/use-device-addr-common-block.f90
new file mode 100644
index 0000000000000..cd5e52e3e4c9d
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/use-device-addr-common-block.f90
@@ -0,0 +1,102 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=50 -Wno-openmp-usage
+
+subroutine valid_map_use_device_addr
+  integer :: x, y
+  common /valid/ x, y
+
+  !$omp target data map(tofrom: /valid/) use_device_addr(/valid/)
+  !$omp end target data
+
+  !$omp target data use_device_addr(/valid/) map(from: /valid/)
+  !$omp end target data
+
+  !$omp target data map(alloc: /valid/) use_device_addr(/valid/)
+  !$omp end target data
+end subroutine
+
+subroutine duplicate_and_exclusive_clauses
+  integer :: x, y
+  common /duplicates/ x, y
+
+  !ERROR: List item 'duplicates' present at multiple USE_DEVICE_ADDR clauses
+  !ERROR: 'duplicates' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data use_device_addr(/duplicates/) use_device_addr(/duplicates/)
+  !$omp end target data
+
+  !ERROR: 'duplicates' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data use_device_ptr(/duplicates/) use_device_ptr(/duplicates/)
+  !$omp end target data
+
+  !ERROR: 'duplicates' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data use_device_ptr(/duplicates/) use_device_addr(/duplicates/)
+  !$omp end target data
+
+  !ERROR: 'duplicates' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data use_device_addr(/duplicates/) use_device_ptr(/duplicates/)
+  !$omp end target data
+end subroutine
+
+subroutine third_appearance
+  integer :: x, y
+  common /third/ x, y
+
+  !ERROR: 'third' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data map(tofrom: /third/) use_device_addr(/third/) map(from: /third/)
+  !$omp end target data
+
+  !ERROR: List item 'third' present at multiple USE_DEVICE_ADDR clauses
+  !ERROR: 'third' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data map(tofrom: /third/) use_device_addr(/third/) use_device_addr(/third/)
+  !$omp end target data
+
+  !ERROR: 'third' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data use_device_addr(/third/) map(tofrom: /third/) map(from: /third/)
+  !$omp end target data
+
+  !ERROR: List item 'third' present at multiple USE_DEVICE_ADDR clauses
+  !ERROR: 'third' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data use_device_addr(/third/) map(tofrom: /third/) use_device_addr(/third/)
+  !$omp end target data
+end subroutine
+
+subroutine map_use_device_ptr_non_cptr
+  integer :: x, y
+  common /non_cptr/ x, y
+
+  !ERROR: 'non_cptr' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data map(tofrom: /non_cptr/) use_device_ptr(/non_cptr/)
+  !$omp end target data
+
+  !ERROR: 'non_cptr' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data use_device_ptr(/non_cptr/) map(tofrom: /non_cptr/)
+  !$omp end target data
+end subroutine
+
+subroutine map_use_device_ptr_cptr
+  use iso_c_binding, only : c_ptr
+  type(c_ptr) :: p, q
+  common /all_cptr/ p, q
+
+  !ERROR: 'all_cptr' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data map(tofrom: /all_cptr/) use_device_ptr(/all_cptr/)
+  !$omp end target data
+
+  !ERROR: 'all_cptr' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data use_device_ptr(/all_cptr/) map(tofrom: /all_cptr/)
+  !$omp end target data
+end subroutine
+
+subroutine map_use_device_ptr_mixed
+  use iso_c_binding, only : c_ptr
+  type(c_ptr) :: p
+  integer :: x
+  common /mixed/ p, x
+
+  !ERROR: 'mixed' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data map(tofrom: /mixed/) use_device_ptr(/mixed/)
+  !$omp end target data
+
+  !ERROR: 'mixed' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target data use_device_ptr(/mixed/) map(tofrom: /mixed/)
+  !$omp end target data
+end subroutine

>From 8f705886d1e127ccfcd123d6424870030ff0b57c Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Wed, 19 Aug 2026 03:00:18 -0500
Subject: [PATCH 2/5] [flang][OpenMP] Use OpenMP 5.1 for COMMON UDA test

---
 .../Lower/OpenMP/target-data-use-device-addr-common-block.f90 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90 b/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
index fa3689e386a68..cf85e77126258 100644
--- a/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
+++ b/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
@@ -1,5 +1,5 @@
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %s -o - | FileCheck %s
-! RUN: %flang_fc1 -emit-llvm -fopenmp -fopenmp-version=50 %s -o /dev/null
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=51 %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-llvm -fopenmp -fopenmp-version=51 %s -o /dev/null
 
 ! CHECK-LABEL: func.func @_QPstandalone_common
 ! CHECK: %[[STANDALONE_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "x"}

>From 75cd7606d3478c1d01771faed6f1189f2184dc66 Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Tue, 25 Aug 2026 07:31:26 -0500
Subject: [PATCH 3/5] [flang][OpenMP] Add COMMON UDA offload test

---
 ...rget-data-use-device-addr-common-block.f90 | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 offload/test/offloading/fortran/target-data-use-device-addr-common-block.f90

diff --git a/offload/test/offloading/fortran/target-data-use-device-addr-common-block.f90 b/offload/test/offloading/fortran/target-data-use-device-addr-common-block.f90
new file mode 100644
index 0000000000000..1dd976fe02b5e
--- /dev/null
+++ b/offload/test/offloading/fortran/target-data-use-device-addr-common-block.f90
@@ -0,0 +1,93 @@
+! REQUIRES: flang, amdgpu
+! RUN: %libomptarget-compile-fortran-run-and-check-generic
+
+program target_data_use_device_addr_common_block
+  use iso_c_binding, only : c_associated, c_f_pointer, c_loc, c_ptr
+  use omp_lib, only : omp_get_default_device, omp_get_mapped_ptr
+  implicit none
+
+  integer, target :: aggregate_x, aggregate_y, member_x, member_y
+  integer, pointer :: device_aggregate_x, device_aggregate_y
+  integer, pointer :: device_member_x, device_member_y
+  type(c_ptr) :: host_aggregate_x, host_aggregate_y
+  type(c_ptr) :: host_member_x, host_member_y
+  type(c_ptr) :: uda_aggregate_x, uda_aggregate_y
+  type(c_ptr) :: uda_member_x, uda_member_y
+  type(c_ptr) :: mapped_aggregate_x, mapped_aggregate_y
+  type(c_ptr) :: mapped_member_x, mapped_member_y
+  integer :: device
+
+  common /aggregate/ aggregate_x, aggregate_y
+  common /members/ member_x, member_y
+
+  aggregate_x = 10
+  aggregate_y = 20
+  member_x = 30
+  member_y = 40
+  device = omp_get_default_device()
+
+  host_aggregate_x = c_loc(aggregate_x)
+  host_aggregate_y = c_loc(aggregate_y)
+  host_member_x = c_loc(member_x)
+  host_member_y = c_loc(member_y)
+
+  !$omp target enter data map(to: member_x, member_y)
+
+  ! The aggregate COMMON returns one device base whose second member requires
+  ! an offset. The pre-mapped COMMON returns one device address per member.
+  !$omp target data map(tofrom: /aggregate/) &
+  !$omp& use_device_addr(/aggregate/, /members/)
+    uda_aggregate_x = c_loc(aggregate_x)
+    uda_aggregate_y = c_loc(aggregate_y)
+    uda_member_x = c_loc(member_x)
+    uda_member_y = c_loc(member_y)
+
+    mapped_aggregate_x = omp_get_mapped_ptr(host_aggregate_x, device)
+    mapped_aggregate_y = omp_get_mapped_ptr(host_aggregate_y, device)
+    mapped_member_x = omp_get_mapped_ptr(host_member_x, device)
+    mapped_member_y = omp_get_mapped_ptr(host_member_y, device)
+
+    if (.not. c_associated(uda_aggregate_x, mapped_aggregate_x)) then
+      print *, "FAIL: aggregate first member address"
+      stop 1
+    end if
+    if (.not. c_associated(uda_aggregate_y, mapped_aggregate_y)) then
+      print *, "FAIL: aggregate second member address"
+      stop 1
+    end if
+    if (.not. c_associated(uda_member_x, mapped_member_x)) then
+      print *, "FAIL: expanded first member address"
+      stop 1
+    end if
+    if (.not. c_associated(uda_member_y, mapped_member_y)) then
+      print *, "FAIL: expanded second member address"
+      stop 1
+    end if
+
+    call c_f_pointer(uda_aggregate_x, device_aggregate_x)
+    call c_f_pointer(uda_aggregate_y, device_aggregate_y)
+    call c_f_pointer(uda_member_x, device_member_x)
+    call c_f_pointer(uda_member_y, device_member_y)
+
+    !$omp target has_device_addr(device_aggregate_x, device_aggregate_y, &
+    !$omp& device_member_x, device_member_y)
+      device_aggregate_x = device_aggregate_x + 1
+      device_aggregate_y = device_aggregate_y + 2
+      device_member_x = device_member_x + 3
+      device_member_y = device_member_y + 4
+    !$omp end target
+  !$omp end target data
+
+  !$omp target exit data map(from: member_x, member_y)
+
+  if (aggregate_x /= 11 .or. aggregate_y /= 22 .or. &
+      member_x /= 33 .or. member_y /= 44) then
+    print *, "FAIL: incorrect values", aggregate_x, aggregate_y, member_x, &
+        member_y
+    stop 2
+  end if
+
+  print *, "PASS"
+end program
+
+! CHECK: PASS

>From c7060d815b611f8f954c3472fe47a4e36ead4d9f Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Tue, 25 Aug 2026 09:59:35 -0500
Subject: [PATCH 4/5] [flang][OpenMP] Make COMMON UDA checks printer-compatible

---
 ...rget-data-use-device-addr-common-block.f90 | 44 +++++++++----------
 1 file changed, 22 insertions(+), 22 deletions(-)

diff --git a/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90 b/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
index cf85e77126258..7ec3ccc110aa3 100644
--- a/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
+++ b/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
@@ -2,8 +2,8 @@
 ! RUN: %flang_fc1 -emit-llvm -fopenmp -fopenmp-version=51 %s -o /dev/null
 
 ! CHECK-LABEL: func.func @_QPstandalone_common
-! CHECK: %[[STANDALONE_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "x"}
-! CHECK: %[[STANDALONE_Y_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "y"}
+! CHECK: %[[STANDALONE_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "x"\}|name\("x"\))}}
+! CHECK: %[[STANDALONE_Y_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "y"\}|name\("y"\))}}
 ! CHECK: omp.target_data use_device_addr(%[[STANDALONE_X_MAP]] -> %[[STANDALONE_X_ARG:.*]], %[[STANDALONE_Y_MAP]] -> %[[STANDALONE_Y_ARG:.*]] : {{.*}}) {
 ! CHECK: %[[STANDALONE_X:.*]]:2 = hlfir.declare %[[STANDALONE_X_ARG]]
 ! CHECK: %[[STANDALONE_Y:.*]]:2 = hlfir.declare %[[STANDALONE_Y_ARG]]
@@ -20,8 +20,8 @@ subroutine standalone_common
 
 ! CHECK-LABEL: func.func @_QPall_cptr_common
 ! CHECK-NOT: use_device_ptr
-! CHECK: %[[CPTR_P_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "p"}
-! CHECK: %[[CPTR_Q_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "q"}
+! CHECK: %[[CPTR_P_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "p"\}|name\("p"\))}}
+! CHECK: %[[CPTR_Q_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "q"\}|name\("q"\))}}
 ! CHECK: omp.target_data use_device_addr(%[[CPTR_P_MAP]] -> %[[CPTR_P_ARG:.*]], %[[CPTR_Q_MAP]] -> %[[CPTR_Q_ARG:.*]] : {{.*}}) {
 ! CHECK-NOT: use_device_ptr
 ! CHECK: %[[CPTR_P:.*]]:2 = hlfir.declare %[[CPTR_P_ARG]]
@@ -38,8 +38,8 @@ subroutine all_cptr_common
 
 ! CHECK-LABEL: func.func @_QPmixed_common
 ! CHECK-NOT: use_device_ptr
-! CHECK: %[[MIXED_P_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "p"}
-! CHECK: %[[MIXED_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "x"}
+! CHECK: %[[MIXED_P_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "p"\}|name\("p"\))}}
+! CHECK: %[[MIXED_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "x"\}|name\("x"\))}}
 ! CHECK: omp.target_data use_device_addr(%[[MIXED_P_MAP]] -> %[[MIXED_P_ARG:.*]], %[[MIXED_X_MAP]] -> %[[MIXED_X_ARG:.*]] : {{.*}}) {
 ! CHECK-NOT: use_device_ptr
 ! CHECK: %[[MIXED_P:.*]]:2 = hlfir.declare %[[MIXED_P_ARG]]
@@ -57,8 +57,8 @@ subroutine mixed_common
 end subroutine
 
 ! CHECK-LABEL: func.func @_QPmapped_common
-! CHECK: %[[MAPPED_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {name = "mapped"}
-! CHECK: %[[MAPPED_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "mapped"}
+! CHECK: %[[MAPPED_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {{(\{name = "mapped"\}|name\("mapped"\))}}
+! CHECK: %[[MAPPED_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "mapped"\}|name\("mapped"\))}}
 ! CHECK: omp.target_data map_entries(%[[MAPPED_MAP]] : {{.*}}) use_device_addr(%[[MAPPED_UDA]] -> %[[MAPPED_ARG:.*]] : {{.*}}) {
 ! CHECK: %[[MAPPED_X_COORD:.*]] = fir.coordinate_of %[[MAPPED_ARG]], %{{.*}}
 ! CHECK: %[[MAPPED_X_REF:.*]] = fir.convert %[[MAPPED_X_COORD]]
@@ -78,10 +78,10 @@ subroutine mapped_common
 end subroutine
 
 ! CHECK-LABEL: func.func @_QPexplicit_map_common
-! CHECK: %[[EXPLICIT_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {name = "x"}
-! CHECK: %[[EXPLICIT_Y_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {name = "y"}
-! CHECK: %[[EXPLICIT_X_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "x"}
-! CHECK: %[[EXPLICIT_Y_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "y"}
+! CHECK: %[[EXPLICIT_X_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {{(\{name = "x"\}|name\("x"\))}}
+! CHECK: %[[EXPLICIT_Y_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {{(\{name = "y"\}|name\("y"\))}}
+! CHECK: %[[EXPLICIT_X_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "x"\}|name\("x"\))}}
+! CHECK: %[[EXPLICIT_Y_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "y"\}|name\("y"\))}}
 ! CHECK: omp.target_data map_entries(%[[EXPLICIT_X_MAP]], %[[EXPLICIT_Y_MAP]] : {{.*}}) use_device_addr(%[[EXPLICIT_X_UDA]] -> %[[EXPLICIT_X_ARG:.*]], %[[EXPLICIT_Y_UDA]] -> %[[EXPLICIT_Y_ARG:.*]] : {{.*}}) {
 ! CHECK: %[[EXPLICIT_X:.*]]:2 = hlfir.declare %[[EXPLICIT_X_ARG]]
 ! CHECK: %[[EXPLICIT_Y:.*]]:2 = hlfir.declare %[[EXPLICIT_Y_ARG]]
@@ -96,10 +96,10 @@ subroutine explicit_map_common
 end subroutine
 
 ! CHECK-LABEL: func.func @_QPouter_map_common
-! CHECK: %[[OUTER_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {name = "outer_map"}
+! CHECK: %[[OUTER_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {{(\{name = "outer_map"\}|name\("outer_map"\))}}
 ! CHECK: omp.target_data map_entries(%[[OUTER_MAP]] : {{.*}}) {
-! CHECK: %[[OUTER_X_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "x"}
-! CHECK: %[[OUTER_Y_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "y"}
+! CHECK: %[[OUTER_X_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "x"\}|name\("x"\))}}
+! CHECK: %[[OUTER_Y_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "y"\}|name\("y"\))}}
 ! CHECK: omp.target_data use_device_addr(%[[OUTER_X_UDA]] -> %[[OUTER_X_ARG:.*]], %[[OUTER_Y_UDA]] -> %[[OUTER_Y_ARG:.*]] : {{.*}}) {
 ! CHECK: %[[OUTER_X:.*]]:2 = hlfir.declare %[[OUTER_X_ARG]]
 ! CHECK: %[[OUTER_Y:.*]]:2 = hlfir.declare %[[OUTER_Y_ARG]]
@@ -116,13 +116,13 @@ subroutine outer_map_common
 end subroutine
 
 ! CHECK-LABEL: func.func @_QPreordered_common
-! CHECK: %[[SECOND_R_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "r"}
-! CHECK: %[[SECOND_S_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "s"}
-! CHECK: %[[PTR_CHILD:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = ""}
-! CHECK: %[[PTR_MAP:.*]] = omp.map.info {{.*}} members(%[[PTR_CHILD]] {{.*}}) {{.*}} {name = "ptr"}
-! CHECK: %[[PTR_ATTACH:.*]] = omp.map.info {{.*}} map_clauses(attach, ref_ptr, ref_ptee) {{.*}} {name = "ptr"}
-! CHECK: %[[FIRST_A_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "a"}
-! CHECK: %[[FIRST_B_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {name = "b"}
+! CHECK: %[[SECOND_R_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "r"\}|name\("r"\))}}
+! CHECK: %[[SECOND_S_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "s"\}|name\("s"\))}}
+! CHECK: %[[PTR_CHILD:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = ""\}|name\(""\))}}
+! CHECK: %[[PTR_MAP:.*]] = omp.map.info {{.*}} members(%[[PTR_CHILD]] {{.*}}) {{.*}}{{(\{name = "ptr"\}|name\("ptr"\))}}
+! CHECK: %[[PTR_ATTACH:.*]] = omp.map.info {{.*}} map_clauses(attach, ref_ptr, ref_ptee) {{.*}} {{(\{name = "ptr"\}|name\("ptr"\))}}
+! CHECK: %[[FIRST_A_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "a"\}|name\("a"\))}}
+! CHECK: %[[FIRST_B_MAP:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "b"\}|name\("b"\))}}
 ! CHECK: omp.target_data map_entries(%[[PTR_ATTACH]] : {{.*}}) use_device_addr(%[[SECOND_R_MAP]] -> %[[SECOND_R_ARG:.*]], %[[SECOND_S_MAP]] -> %[[SECOND_S_ARG:.*]], %[[PTR_MAP]] -> %[[PTR_ARG:.*]], %[[FIRST_A_MAP]] -> %[[FIRST_A_ARG:.*]], %[[FIRST_B_MAP]] -> %[[FIRST_B_ARG:.*]], %[[PTR_CHILD]] -> %[[PTR_CHILD_ARG:.*]] : {{.*}}) {
 ! CHECK: %[[SECOND_R:.*]]:2 = hlfir.declare %[[SECOND_R_ARG]]
 ! CHECK: %[[SECOND_S:.*]]:2 = hlfir.declare %[[SECOND_S_ARG]]

>From a3d5e789fabef10fe6d833efcdb2297b325ed7cd Mon Sep 17 00:00:00 2001
From: Sairudra More <sairudra60 at gmail.com>
Date: Thu, 27 Aug 2026 09:28:42 -0500
Subject: [PATCH 5/5] [flang][OpenMP] Allow repeated map clauses on named
 COMMON blocks

A named COMMON block previously permitted at most one map/use_device_addr
overlap before diagnosing later appearances as data-sharing conflicts,
even though OpenMP allows a list item to appear in more than one map
clause on the same directive. Track the full set of clause flags seen
for each COMMON block so any number of map appearances are accepted,
at most one use_device_addr is still allowed alongside them on
target data, and duplicate use_device_addr/use_device_ptr and
unrelated data-sharing conflicts continue to be diagnosed.
---
 flang/lib/Semantics/resolve-directives.cpp    | 41 ++++++------
 ...rget-data-use-device-addr-common-block.f90 | 41 +++++++++++-
 .../OpenMP/use-device-addr-common-block.f90   | 62 ++++++++++++++++---
 3 files changed, 111 insertions(+), 33 deletions(-)

diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 73dedc79b8528..a1798e578509a 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -61,7 +61,7 @@ template <typename T> class DirectiveAttributeVisitor {
     Scope &scope;
     Symbol::Flag defaultDSA{Symbol::Flag::AccShared}; // TODOACC
     std::map<const Symbol *, Symbol::Flag> objectWithDSA;
-    std::map<const Symbol *, Symbol::Flag> commonBlockClauseFlags;
+    std::map<const Symbol *, Symbol::Flags> commonBlockClauseFlags;
     std::map<parser::OmpVariableCategory::Value,
         parser::OmpDefaultmapClause::ImplicitBehavior>
         defaultMap;
@@ -3195,28 +3195,25 @@ void OmpAttributeVisitor::ResolveOmpCommonBlock(
   Symbol *originalCB{ResolveOmpCommonBlockName(&cbName)};
   if (auto *symbol{cbResolved ? name.symbol : originalCB}) {
     if (!dataCopyingAttributeFlags.test(ompFlag)) {
-      auto isMapFlag{[](Symbol::Flag flag) {
-        return flag == Symbol::Flag::OmpMapTo ||
-            flag == Symbol::Flag::OmpMapFrom ||
-            flag == Symbol::Flag::OmpMapToFrom ||
-            flag == Symbol::Flag::OmpMapStorage ||
-            flag == Symbol::Flag::OmpMapDelete;
-      }};
+      const Symbol::Flags mapFlags{Symbol::Flag::OmpMapTo,
+          Symbol::Flag::OmpMapFrom, Symbol::Flag::OmpMapToFrom,
+          Symbol::Flag::OmpMapStorage, Symbol::Flag::OmpMapDelete};
       const Symbol *commonBlock{&symbol->GetUltimate()};
-      auto [it, inserted]{GetContext().commonBlockClauseFlags.try_emplace(
-          commonBlock, ompFlag)};
-      bool mapUseDeviceAddrPair{
-          GetContext().directive == llvm::omp::Directive::OMPD_target_data &&
-          !inserted &&
-          ((isMapFlag(it->second) &&
-               ompFlag == Symbol::Flag::OmpUseDeviceAddr) ||
-              (it->second == Symbol::Flag::OmpUseDeviceAddr &&
-                  isMapFlag(ompFlag)))};
-      if (mapUseDeviceAddrPair) {
-        // Mark the one permitted overlap as consumed so a third appearance is
-        // still diagnosed.
-        it->second = Symbol::Flag::OmpCommonBlock;
-      } else {
+      auto [it, inserted]{
+          GetContext().commonBlockClauseFlags.try_emplace(commonBlock)};
+      bool isTargetData{
+          GetContext().directive == llvm::omp::Directive::OMPD_target_data};
+      Symbol::Flags allowedRepeatFlags{mapFlags};
+      if (isTargetData) {
+        allowedRepeatFlags.set(Symbol::Flag::OmpUseDeviceAddr);
+      }
+      bool allowRepeatedAppearance{!inserted &&
+          (it->second & ~allowedRepeatFlags).none() &&
+          (mapFlags.test(ompFlag) ||
+              (isTargetData && ompFlag == Symbol::Flag::OmpUseDeviceAddr &&
+                  !it->second.test(Symbol::Flag::OmpUseDeviceAddr)))};
+      it->second.set(ompFlag);
+      if (!allowRepeatedAppearance) {
         CheckMultipleAppearances(name, *symbol, Symbol::Flag::OmpCommonBlock);
       }
     }
diff --git a/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90 b/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
index 7ec3ccc110aa3..2d18437f97cbf 100644
--- a/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
+++ b/flang/test/Lower/OpenMP/target-data-use-device-addr-common-block.f90
@@ -57,9 +57,10 @@ subroutine mixed_common
 end subroutine
 
 ! CHECK-LABEL: func.func @_QPmapped_common
-! CHECK: %[[MAPPED_MAP:.*]] = omp.map.info {{.*}} map_clauses(tofrom) {{.*}} {{(\{name = "mapped"\}|name\("mapped"\))}}
+! CHECK: %[[MAPPED_FROM:.*]] = omp.map.info {{.*}} map_clauses(from) {{.*}} {{(\{name = "mapped"\}|name\("mapped"\))}}
+! CHECK: %[[MAPPED_TO:.*]] = omp.map.info {{.*}} map_clauses(to) {{.*}} {{(\{name = "mapped"\}|name\("mapped"\))}}
 ! CHECK: %[[MAPPED_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "mapped"\}|name\("mapped"\))}}
-! CHECK: omp.target_data map_entries(%[[MAPPED_MAP]] : {{.*}}) use_device_addr(%[[MAPPED_UDA]] -> %[[MAPPED_ARG:.*]] : {{.*}}) {
+! CHECK: omp.target_data map_entries(%[[MAPPED_FROM]], %[[MAPPED_TO]] : {{.*}}) use_device_addr(%[[MAPPED_UDA]] -> %[[MAPPED_ARG:.*]] : {{.*}}) {
 ! CHECK: %[[MAPPED_X_COORD:.*]] = fir.coordinate_of %[[MAPPED_ARG]], %{{.*}}
 ! CHECK: %[[MAPPED_X_REF:.*]] = fir.convert %[[MAPPED_X_COORD]]
 ! CHECK: %[[MAPPED_X:.*]]:2 = hlfir.declare %[[MAPPED_X_REF]] storage(%[[MAPPED_ARG]][0])
@@ -72,7 +73,41 @@ subroutine mixed_common
 subroutine mapped_common
   integer :: x, y
   common /mapped/ x, y
-  !$omp target data map(tofrom: /mapped/) use_device_addr(/mapped/)
+  !$omp target data map(from: /mapped/) map(to: /mapped/) use_device_addr(/mapped/)
+    x = y + 1
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPuse_device_addr_first
+! CHECK: %[[UDA_FIRST_FROM:.*]] = omp.map.info {{.*}} map_clauses(from) {{.*}} {{(\{name = "uda_first"\}|name\("uda_first"\))}}
+! CHECK: %[[UDA_FIRST_TO:.*]] = omp.map.info {{.*}} map_clauses(to) {{.*}} {{(\{name = "uda_first"\}|name\("uda_first"\))}}
+! CHECK: %[[UDA_FIRST_UDA:.*]] = omp.map.info {{.*}} map_clauses(return_param) {{.*}} {{(\{name = "uda_first"\}|name\("uda_first"\))}}
+! CHECK: omp.target_data map_entries(%[[UDA_FIRST_FROM]], %[[UDA_FIRST_TO]] : {{.*}}) use_device_addr(%[[UDA_FIRST_UDA]] -> %[[UDA_FIRST_ARG:.*]] : {{.*}}) {
+! CHECK: %[[UDA_FIRST_X_COORD:.*]] = fir.coordinate_of %[[UDA_FIRST_ARG]], %{{.*}}
+! CHECK: %[[UDA_FIRST_X_REF:.*]] = fir.convert %[[UDA_FIRST_X_COORD]]
+! CHECK: %[[UDA_FIRST_X:.*]]:2 = hlfir.declare %[[UDA_FIRST_X_REF]] storage(%[[UDA_FIRST_ARG]][0])
+! CHECK: %[[UDA_FIRST_Y_COORD:.*]] = fir.coordinate_of %[[UDA_FIRST_ARG]], %{{.*}}
+! CHECK: %[[UDA_FIRST_Y_REF:.*]] = fir.convert %[[UDA_FIRST_Y_COORD]]
+! CHECK: %[[UDA_FIRST_Y:.*]]:2 = hlfir.declare %[[UDA_FIRST_Y_REF]] storage(%[[UDA_FIRST_ARG]][4])
+! CHECK: %[[UDA_FIRST_LOAD:.*]] = fir.load %[[UDA_FIRST_Y]]#0
+! CHECK: hlfir.assign %{{.*}} to %[[UDA_FIRST_X]]#0
+subroutine use_device_addr_first
+  integer :: x, y
+  common /uda_first/ x, y
+  !$omp target data use_device_addr(/uda_first/) map(from: /uda_first/) map(to: /uda_first/)
+    x = y + 1
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrepeated_map_common
+! CHECK: %[[REPEATED_FROM:.*]] = omp.map.info {{.*}} map_clauses(from) {{.*}} {{(\{name = "repeated_map"\}|name\("repeated_map"\))}}
+! CHECK: %[[REPEATED_TO_1:.*]] = omp.map.info {{.*}} map_clauses(to) {{.*}} {{(\{name = "repeated_map"\}|name\("repeated_map"\))}}
+! CHECK: %[[REPEATED_TO_2:.*]] = omp.map.info {{.*}} map_clauses(to) {{.*}} {{(\{name = "repeated_map"\}|name\("repeated_map"\))}}
+! CHECK: omp.target_data map_entries(%[[REPEATED_FROM]], %[[REPEATED_TO_1]], %[[REPEATED_TO_2]] : {{.*}}) {
+subroutine repeated_map_common
+  integer :: x, y
+  common /repeated_map/ x, y
+  !$omp target data map(from: /repeated_map/) map(to: /repeated_map/) map(to: /repeated_map/)
     x = y + 1
   !$omp end target data
 end subroutine
diff --git a/flang/test/Semantics/OpenMP/use-device-addr-common-block.f90 b/flang/test/Semantics/OpenMP/use-device-addr-common-block.f90
index cd5e52e3e4c9d..574b47831bd1b 100644
--- a/flang/test/Semantics/OpenMP/use-device-addr-common-block.f90
+++ b/flang/test/Semantics/OpenMP/use-device-addr-common-block.f90
@@ -1,5 +1,9 @@
 ! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=50 -Wno-openmp-usage
 
+! OpenMP 5.0 allows map list items to share original storage when they are the
+! same variable or array section. A named COMMON block has the same meaning as
+! its explicit members, so repeating its map clauses is valid.
+
 subroutine valid_map_use_device_addr
   integer :: x, y
   common /valid/ x, y
@@ -14,6 +18,28 @@ subroutine valid_map_use_device_addr
   !$omp end target data
 end subroutine
 
+subroutine repeated_maps
+  integer :: x, y
+  common /repeated/ x, y
+
+  !$omp target data map(from: /repeated/) map(to: /repeated/)
+  !$omp end target data
+
+  !$omp target data map(to: /repeated/) map(to: /repeated/) map(to: /repeated/)
+  !$omp end target data
+end subroutine
+
+subroutine repeated_map_controls
+  integer :: x, y, scalar
+  common /controls/ x, y
+
+  !$omp target data map(from: x, y) map(to: x, y) map(to: x, y)
+  !$omp end target data
+
+  !$omp target data map(from: scalar) map(to: scalar) map(to: scalar)
+  !$omp end target data
+end subroutine
+
 subroutine duplicate_and_exclusive_clauses
   integer :: x, y
   common /duplicates/ x, y
@@ -36,29 +62,49 @@ subroutine duplicate_and_exclusive_clauses
   !$omp end target data
 end subroutine
 
-subroutine third_appearance
+subroutine repeated_maps_with_use_device_addr
   integer :: x, y
   common /third/ x, y
 
-  !ERROR: 'third' appears in more than one data-sharing clause on the same OpenMP directive
-  !$omp target data map(tofrom: /third/) use_device_addr(/third/) map(from: /third/)
+  !$omp target data map(from: /third/) map(to: /third/) use_device_addr(/third/)
   !$omp end target data
 
-  !ERROR: List item 'third' present at multiple USE_DEVICE_ADDR clauses
-  !ERROR: 'third' appears in more than one data-sharing clause on the same OpenMP directive
-  !$omp target data map(tofrom: /third/) use_device_addr(/third/) use_device_addr(/third/)
+  !$omp target data map(from: /third/) use_device_addr(/third/) map(to: /third/)
+  !$omp end target data
+
+  !$omp target data use_device_addr(/third/) map(from: /third/) map(to: /third/)
   !$omp end target data
 
+  !ERROR: List item 'third' present at multiple USE_DEVICE_ADDR clauses
   !ERROR: 'third' appears in more than one data-sharing clause on the same OpenMP directive
-  !$omp target data use_device_addr(/third/) map(tofrom: /third/) map(from: /third/)
+  !$omp target data map(from: /third/) map(to: /third/) use_device_addr(/third/) use_device_addr(/third/)
   !$omp end target data
 
   !ERROR: List item 'third' present at multiple USE_DEVICE_ADDR clauses
   !ERROR: 'third' appears in more than one data-sharing clause on the same OpenMP directive
-  !$omp target data use_device_addr(/third/) map(tofrom: /third/) use_device_addr(/third/)
+  !$omp target data use_device_addr(/third/) map(from: /third/) map(to: /third/) use_device_addr(/third/)
+  !$omp end target data
+end subroutine
+
+subroutine nested_repeated_maps
+  integer :: x, y
+  common /nested_repeated/ x, y
+
+  !$omp target data map(from: /nested_repeated/) use_device_addr(/nested_repeated/) map(to: /nested_repeated/)
+    !$omp target data use_device_addr(/nested_repeated/) map(from: /nested_repeated/) map(to: /nested_repeated/)
+    !$omp end target data
   !$omp end target data
 end subroutine
 
+subroutine unrelated_data_sharing_conflict
+  integer :: x, y
+  common /conflict/ x, y
+
+  !ERROR: 'conflict' appears in more than one data-sharing clause on the same OpenMP directive
+  !$omp target map(to: /conflict/) map(from: /conflict/) private(/conflict/)
+  !$omp end target
+end subroutine
+
 subroutine map_use_device_ptr_non_cptr
   integer :: x, y
   common /non_cptr/ x, y



More information about the flang-commits mailing list