[flang-commits] [flang] [Flang][OpenMP] Warn about SAVE variables in MAP w/o ALWAYS modifier (PR #209134)

Michael Klemm via flang-commits flang-commits at lists.llvm.org
Thu Jul 23 06:16:16 PDT 2026


https://github.com/mjklemm updated https://github.com/llvm/llvm-project/pull/209134

>From 9917a6599c404b2e3afff4e021189b7139af481b Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Mon, 13 Jul 2026 11:07:40 +0200
Subject: [PATCH 1/6] [Flang][OpenMP] Warn about SAVE variables in MAP w/o
 ALWAYS modifier

When mapping a variable that has the SAVE attribute, a MAP clause is not
effective unless it has the ALWAYS modifier.  The reason is that the
reference count of the variable on the device is infinite and thus no
data transfer occur when ALWAYS is not present.  This is a common source
of errors and the compiler should warn about this behavior.
---
 .../include/flang/Support/Fortran-features.h  |  3 +-
 flang/lib/Semantics/check-omp-structure.cpp   | 31 ++++++++
 flang/lib/Support/Fortran-features.cpp        |  1 +
 .../Semantics/OpenMP/clause-validity01.f90    |  1 +
 .../Semantics/OpenMP/combined-constructs.f90  | 13 ++++
 .../Semantics/OpenMP/declare-mapper04.f90     |  2 +
 .../Semantics/OpenMP/device-constructs.f90    |  1 +
 .../OpenMP/device-omp-initial-invalid.f90     |  6 ++
 .../Semantics/OpenMP/map-save-attribute.f90   | 76 +++++++++++++++++++
 flang/test/Semantics/OpenMP/nested-target.f90 |  2 +
 10 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100644 flang/test/Semantics/OpenMP/map-save-attribute.f90

diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index be4c60afb1cc7..5f181ddaae324 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -87,7 +87,8 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
     RealConstantWidening, VolatileOrAsynchronousTemporary, UnusedVariable,
     UsedUndefinedVariable, BadValueInDeadCode, AssumedTypeSizeDummy,
     MisplacedIgnoreTKR, NamelistParameter, ImpureFinalInPure,
-    IgnoredNoReallocateLHS, ExperimentalOption, IoImpliedDoIndexConflict)
+    IgnoredNoReallocateLHS, ExperimentalOption, IoImpliedDoIndexConflict,
+    OpenMPMapSaveWithoutAlways)
 
 using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
 using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 68d7a166be0fb..bdb6f46682e2b 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -4846,6 +4846,37 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
     }
   }
 
+  // Warn if a variable with implicit or explicit SAVE attribute is mapped
+  // without the ALWAYS map modifier. As per OpenMP specificaiton,
+  // reference-counted map semantics mean subsequent target regions may skip
+  // data transfer for those variables.
+  bool hasAlwaysModifier{
+      OmpGetUniqueModifier<parser::OmpAlwaysModifier>(modifiers) != nullptr};
+  if (!hasAlwaysModifier) {
+    // The ALWAYS modifier may have been canonicalized into an
+    // OmpMapTypeModifier with value Always (see canonicalize-omp.cpp).
+    for (auto *typeMod :
+        OmpGetRepeatableModifier<parser::OmpMapTypeModifier>(modifiers)) {
+      if (typeMod->v == parser::OmpMapTypeModifier::Value::Always) {
+        hasAlwaysModifier = true;
+        break;
+      }
+    }
+  }
+  if (!hasAlwaysModifier) {
+    for (const parser::OmpObject &object : objects.v) {
+      if (const Symbol *sym{GetObjectSymbol(object, /*ultimate=*/true)}) {
+        if (IsSaved(*sym)) {
+          auto maybeSource{GetObjectSource(object)};
+          context_.Warn(common::UsageWarning::OpenMPMapSaveWithoutAlways,
+              maybeSource.value_or(GetContext().clauseSource),
+              "Variable '%s' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions"_warn_en_US,
+              sym->name());
+        }
+      }
+    }
+  }
+
   // If we are an enter or exit map, iterate over the maps and add them to
   // containers that track if the symbol has been referenced in both an
   // enter/exit map in the current scope, if it falls into the category of
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index b9f4c3a804167..0f7d7ede365c7 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -194,6 +194,7 @@ LanguageFeatureControl::LanguageFeatureControl() {
   warnUsage_.set(UsageWarning::ZeroDoStep);
   warnUsage_.set(UsageWarning::UnusedForallIndex);
   warnUsage_.set(UsageWarning::OpenMPUsage);
+  warnUsage_.set(UsageWarning::OpenMPMapSaveWithoutAlways);
   warnUsage_.set(UsageWarning::DataLength);
   warnUsage_.set(UsageWarning::IgnoredDirective);
   warnUsage_.set(UsageWarning::HomonymousSpecific);
diff --git a/flang/test/Semantics/OpenMP/clause-validity01.f90 b/flang/test/Semantics/OpenMP/clause-validity01.f90
index 773fd82aaeb43..4868c3982c712 100644
--- a/flang/test/Semantics/OpenMP/clause-validity01.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity01.f90
@@ -85,6 +85,7 @@
   !$omp end target
 
   !ERROR: ALLOCATE clause is not allowed on TARGET DATA directive
+  !WARNING: Variable 'b' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target data map(from: b) allocate(b)
   do i = 1, N
      z = 2
diff --git a/flang/test/Semantics/OpenMP/combined-constructs.f90 b/flang/test/Semantics/OpenMP/combined-constructs.f90
index c01998b1fa1b0..de13ef1c7db36 100644
--- a/flang/test/Semantics/OpenMP/combined-constructs.f90
+++ b/flang/test/Semantics/OpenMP/combined-constructs.f90
@@ -47,6 +47,7 @@ program main
   enddo
   !$omp end target parallel
 
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target parallel map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -94,6 +95,7 @@ program main
   enddo
   !$omp end target parallel do
 
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target parallel do map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -108,6 +110,7 @@ program main
   enddo
   !$omp end target parallel do
 
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams map(a)
   do i = 1, N
      a(i) = 3.14d0
@@ -201,6 +204,7 @@ program main
   enddo
   !$omp end target teams
 
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -208,6 +212,7 @@ program main
   !$omp end target teams
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS directive
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -215,6 +220,7 @@ program main
   !$omp end target teams
 
 
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams distribute map(a)
   do i = 1, N
      a(i) = 3.14d0
@@ -301,6 +307,7 @@ program main
   enddo
   !$omp end target teams distribute
 
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams distribute map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -308,6 +315,7 @@ program main
   !$omp end target teams distribute
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS DISTRIBUTE directive
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams distribute map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -394,6 +402,7 @@ program main
   enddo
   !$omp end target teams distribute parallel do
 
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -401,6 +410,7 @@ program main
   !$omp end target teams distribute parallel do
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS DISTRIBUTE PARALLEL DO directive
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -408,6 +418,7 @@ program main
   !$omp end target teams distribute parallel do
 
 
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do simd map(a)
   do i = 1, N
      a(i) = 3.14d0
@@ -494,6 +505,7 @@ program main
   enddo
   !$omp end target teams distribute parallel do simd
 
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do simd map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -501,6 +513,7 @@ program main
   !$omp end target teams distribute parallel do simd
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS DISTRIBUTE PARALLEL DO SIMD directive
+  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do simd map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
diff --git a/flang/test/Semantics/OpenMP/declare-mapper04.f90 b/flang/test/Semantics/OpenMP/declare-mapper04.f90
index 2f45e230c3513..99419d848b330 100644
--- a/flang/test/Semantics/OpenMP/declare-mapper04.f90
+++ b/flang/test/Semantics/OpenMP/declare-mapper04.f90
@@ -9,10 +9,12 @@
 end type
 
 !ERROR: DECLARE_MAPPER directive should have a single argument
+!WARNING: Variable 'x' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
 !$omp declare mapper(m1:t1::x, m2:t2::x) map(x, x%y)
 
 integer :: x(10)
 !ERROR: The argument to the DECLARE_MAPPER directive should be a mapper-specifier
+!WARNING: Variable 'x' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
 !$omp declare mapper(x) map(to: x)
 
 end
diff --git a/flang/test/Semantics/OpenMP/device-constructs.f90 b/flang/test/Semantics/OpenMP/device-constructs.f90
index b74304222c835..54cc29a5132ff 100644
--- a/flang/test/Semantics/OpenMP/device-constructs.f90
+++ b/flang/test/Semantics/OpenMP/device-constructs.f90
@@ -12,6 +12,7 @@ program main
   arrayB = 3.14d0
   N = 256
 
+  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target map(arrayA)
   do i = 1, N
      a = 3.14d0
diff --git a/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90 b/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90
index 6b76265bd96f9..70b718d4421fe 100644
--- a/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90
+++ b/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90
@@ -48,16 +48,22 @@ program main
   !$omp end target
 
   ! Also accepted on target data and its data motion variants.
+  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target data map(to: arrayA) device(omp_initial_device)
   !$omp end target data
 
+  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target data map(to: arrayA) device(omp_invalid_device)
   !$omp end target data
 
+  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target enter data map(alloc: arrayA) device(omp_initial_device)
+  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target enter data map(alloc: arrayA) device(omp_invalid_device)
 
+  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target exit data map(delete: arrayA) device(omp_initial_device)
+  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target exit data map(delete: arrayA) device(omp_invalid_device)
 
   !$omp target update to(arrayA) device(omp_initial_device)
diff --git a/flang/test/Semantics/OpenMP/map-save-attribute.f90 b/flang/test/Semantics/OpenMP/map-save-attribute.f90
new file mode 100644
index 0000000000000..f1424d236f8bd
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/map-save-attribute.f90
@@ -0,0 +1,76 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52 -Werror -Wno-experimental-option
+!RUN: %flang -fopenmp -fopenmp-version=52 -Wno-experimental-option -Wno-openmp-map-save-without-always -fsyntax-only %s 2>&1 | FileCheck --allow-empty --check-prefix=NOWARN %s
+!NOWARN-NOT: warning:
+
+! Verify that mapping a variable with the SAVE attribute (implicit or explicit)
+! produces a warning when the ALWAYS map modifier is not specified.
+
+subroutine explicit_save_warn
+  integer, save :: s
+!WARNING: Variable 's' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !$omp target map(tofrom: s)
+  s = s + 1
+  !$omp end target
+end
+
+subroutine explicit_save_with_always
+  integer, save :: s
+  !$omp target map(always, tofrom: s)
+  s = s + 1
+  !$omp end target
+end
+
+subroutine data_init_implicit_save
+  ! A variable initialized in a DATA statement has an implicit SAVE attribute.
+  integer :: s
+  data s /0/
+!WARNING: Variable 's' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !$omp target map(tofrom: s)
+  s = s + 1
+  !$omp end target
+end
+
+subroutine initializer_implicit_save
+  ! A variable with a default initializer has an implicit SAVE attribute.
+  integer :: s = 0
+!WARNING: Variable 's' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !$omp target map(tofrom: s)
+  s = s + 1
+  !$omp end target
+end
+
+subroutine non_save_no_warn(x)
+  integer :: x
+  ! No SAVE attribute -> no warning.
+  !$omp target map(tofrom: x)
+  x = x + 1
+  !$omp end target
+end
+
+subroutine mixed_list
+  integer, save :: s
+  integer :: x
+!WARNING: Variable 's' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !$omp target map(tofrom: s, x)
+  s = s + x
+  !$omp end target
+end
+
+module m
+  ! Module variables have implicit SAVE.
+  integer :: mv = 0
+  integer, save :: mve = 0
+contains
+  subroutine use_module_var
+!WARNING: Variable 'mv' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+!WARNING: Variable 'mve' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+    !$omp target map(tofrom: mv, mve)
+    mv = mv + 1
+    !$omp end target
+  end
+  subroutine use_module_var_always
+    !$omp target map(always, tofrom: mv,mve)
+    mv = mv + 1
+    !$omp end target
+  end
+end module
diff --git a/flang/test/Semantics/OpenMP/nested-target.f90 b/flang/test/Semantics/OpenMP/nested-target.f90
index 59d891b420ee4..0c8ac23715a03 100644
--- a/flang/test/Semantics/OpenMP/nested-target.f90
+++ b/flang/test/Semantics/OpenMP/nested-target.f90
@@ -41,11 +41,13 @@ program main
   allocate(B(N))
   !$omp target
   !PORTABILITY: If TARGET ENTER DATA directive is nested inside TARGET region, the behaviour is unspecified [-Wopenmp-usage]
+  !WARNING: Variable 'b' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target enter data map(alloc:B)
   !$omp end target
 
   !$omp target
   !PORTABILITY: If TARGET EXIT DATA directive is nested inside TARGET region, the behaviour is unspecified [-Wopenmp-usage]
+  !WARNING: Variable 'b' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
   !$omp target exit data map(delete:B)
   !$omp end target
   deallocate(B)

>From 2298ad9ce269ecf67db9393b1e0d0bd65fa88536 Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Mon, 13 Jul 2026 12:45:49 +0200
Subject: [PATCH 2/6] Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot at users.noreply.github.com>
---
 flang/lib/Semantics/check-omp-structure.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index bdb6f46682e2b..559e8b8fb399e 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -4847,7 +4847,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
   }
 
   // Warn if a variable with implicit or explicit SAVE attribute is mapped
-  // without the ALWAYS map modifier. As per OpenMP specificaiton,
+  // without the ALWAYS map modifier. As per OpenMP specification,
   // reference-counted map semantics mean subsequent target regions may skip
   // data transfer for those variables.
   bool hasAlwaysModifier{

>From 0c7c6506add561b470a34c056fb04320745edcb6 Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Mon, 13 Jul 2026 13:50:16 +0200
Subject: [PATCH 3/6] Address Copilot feedback

---
 flang/lib/Semantics/check-omp-structure.cpp   | 40 +++++++----
 .../Semantics/OpenMP/clause-validity01.f90    |  2 +-
 .../Semantics/OpenMP/combined-constructs.f90  | 26 +++----
 .../Semantics/OpenMP/declare-mapper04.f90     |  4 +-
 .../Semantics/OpenMP/device-constructs.f90    |  2 +-
 .../OpenMP/device-omp-initial-invalid.f90     | 12 ++--
 .../Semantics/OpenMP/map-save-attribute.f90   | 67 +++++++++++++++++--
 flang/test/Semantics/OpenMP/nested-target.f90 |  4 +-
 8 files changed, 113 insertions(+), 44 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 559e8b8fb399e..ed446b2ff4824 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -4846,10 +4846,11 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
     }
   }
 
-  // Warn if a variable with implicit or explicit SAVE attribute is mapped
-  // without the ALWAYS map modifier. As per OpenMP specification,
-  // reference-counted map semantics mean subsequent target regions may skip
-  // data transfer for those variables.
+  // Warn if a variable with implicit or explicit SAVE attribute appears in a
+  // map clause without the ALWAYS modifier. Under OpenMP's reference-
+  // counted map semantics, the map operation for such a variable may be
+  // skipped if it is already present on the device, unless ALWAYS is
+  // specified.
   bool hasAlwaysModifier{
       OmpGetUniqueModifier<parser::OmpAlwaysModifier>(modifiers) != nullptr};
   if (!hasAlwaysModifier) {
@@ -4864,16 +4865,31 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
     }
   }
   if (!hasAlwaysModifier) {
+    // Track base symbols already warned about within this clause so that a
+    // list item and one of its components (e.g. `map(x, x%y)`) yield only one
+    // diagnostic per base variable.
+    llvm::SmallPtrSet<const Symbol *, 4> warnedBases;
     for (const parser::OmpObject &object : objects.v) {
-      if (const Symbol *sym{GetObjectSymbol(object, /*ultimate=*/true)}) {
-        if (IsSaved(*sym)) {
-          auto maybeSource{GetObjectSource(object)};
-          context_.Warn(common::UsageWarning::OpenMPMapSaveWithoutAlways,
-              maybeSource.value_or(GetContext().clauseSource),
-              "Variable '%s' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions"_warn_en_US,
-              sym->name());
-        }
+      const Symbol *baseSym{nullptr};
+      if (const parser::Designator *d{GetDesignatorFromObj(object)}) {
+        // For designators (including component references and subscripts),
+        // use the leftmost name so that `x%y` is treated as `x`.
+        baseSym = parser::GetFirstName(*d).symbol;
+      } else {
+        baseSym = GetObjectSymbol(object);
+      }
+      if (!baseSym) {
+        continue;
+      }
+      const Symbol &ultimate{baseSym->GetUltimate()};
+      if (!IsSaved(ultimate) || !warnedBases.insert(&ultimate).second) {
+        continue;
       }
+      auto maybeSource{GetObjectSource(object)};
+      context_.Warn(common::UsageWarning::OpenMPMapSaveWithoutAlways,
+          maybeSource.value_or(GetContext().clauseSource),
+          "Variable '%s' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device"_warn_en_US,
+          ultimate.name());
     }
   }
 
diff --git a/flang/test/Semantics/OpenMP/clause-validity01.f90 b/flang/test/Semantics/OpenMP/clause-validity01.f90
index 4868c3982c712..5284cf5f7cd53 100644
--- a/flang/test/Semantics/OpenMP/clause-validity01.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity01.f90
@@ -85,7 +85,7 @@
   !$omp end target
 
   !ERROR: ALLOCATE clause is not allowed on TARGET DATA directive
-  !WARNING: Variable 'b' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'b' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target data map(from: b) allocate(b)
   do i = 1, N
      z = 2
diff --git a/flang/test/Semantics/OpenMP/combined-constructs.f90 b/flang/test/Semantics/OpenMP/combined-constructs.f90
index de13ef1c7db36..10a6b0bd67492 100644
--- a/flang/test/Semantics/OpenMP/combined-constructs.f90
+++ b/flang/test/Semantics/OpenMP/combined-constructs.f90
@@ -47,7 +47,7 @@ program main
   enddo
   !$omp end target parallel
 
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target parallel map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -95,7 +95,7 @@ program main
   enddo
   !$omp end target parallel do
 
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target parallel do map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -110,7 +110,7 @@ program main
   enddo
   !$omp end target parallel do
 
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams map(a)
   do i = 1, N
      a(i) = 3.14d0
@@ -204,7 +204,7 @@ program main
   enddo
   !$omp end target teams
 
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -212,7 +212,7 @@ program main
   !$omp end target teams
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS directive
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -220,7 +220,7 @@ program main
   !$omp end target teams
 
 
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute map(a)
   do i = 1, N
      a(i) = 3.14d0
@@ -307,7 +307,7 @@ program main
   enddo
   !$omp end target teams distribute
 
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -315,7 +315,7 @@ program main
   !$omp end target teams distribute
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS DISTRIBUTE directive
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -402,7 +402,7 @@ program main
   enddo
   !$omp end target teams distribute parallel do
 
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -410,7 +410,7 @@ program main
   !$omp end target teams distribute parallel do
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS DISTRIBUTE PARALLEL DO directive
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -418,7 +418,7 @@ program main
   !$omp end target teams distribute parallel do
 
 
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do simd map(a)
   do i = 1, N
      a(i) = 3.14d0
@@ -505,7 +505,7 @@ program main
   enddo
   !$omp end target teams distribute parallel do simd
 
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do simd map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -513,7 +513,7 @@ program main
   !$omp end target teams distribute parallel do simd
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS DISTRIBUTE PARALLEL DO SIMD directive
-  !WARNING: Variable 'a' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do simd map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
diff --git a/flang/test/Semantics/OpenMP/declare-mapper04.f90 b/flang/test/Semantics/OpenMP/declare-mapper04.f90
index 99419d848b330..9396cc7d7b4b8 100644
--- a/flang/test/Semantics/OpenMP/declare-mapper04.f90
+++ b/flang/test/Semantics/OpenMP/declare-mapper04.f90
@@ -9,12 +9,12 @@
 end type
 
 !ERROR: DECLARE_MAPPER directive should have a single argument
-!WARNING: Variable 'x' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+!WARNING: Variable 'x' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
 !$omp declare mapper(m1:t1::x, m2:t2::x) map(x, x%y)
 
 integer :: x(10)
 !ERROR: The argument to the DECLARE_MAPPER directive should be a mapper-specifier
-!WARNING: Variable 'x' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+!WARNING: Variable 'x' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
 !$omp declare mapper(x) map(to: x)
 
 end
diff --git a/flang/test/Semantics/OpenMP/device-constructs.f90 b/flang/test/Semantics/OpenMP/device-constructs.f90
index 54cc29a5132ff..7af73d8a58fa2 100644
--- a/flang/test/Semantics/OpenMP/device-constructs.f90
+++ b/flang/test/Semantics/OpenMP/device-constructs.f90
@@ -12,7 +12,7 @@ program main
   arrayB = 3.14d0
   N = 256
 
-  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(arrayA)
   do i = 1, N
      a = 3.14d0
diff --git a/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90 b/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90
index 70b718d4421fe..a480c8c56612a 100644
--- a/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90
+++ b/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90
@@ -48,22 +48,22 @@ program main
   !$omp end target
 
   ! Also accepted on target data and its data motion variants.
-  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target data map(to: arrayA) device(omp_initial_device)
   !$omp end target data
 
-  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target data map(to: arrayA) device(omp_invalid_device)
   !$omp end target data
 
-  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target enter data map(alloc: arrayA) device(omp_initial_device)
-  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target enter data map(alloc: arrayA) device(omp_invalid_device)
 
-  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target exit data map(delete: arrayA) device(omp_initial_device)
-  !WARNING: Variable 'arraya' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target exit data map(delete: arrayA) device(omp_invalid_device)
 
   !$omp target update to(arrayA) device(omp_initial_device)
diff --git a/flang/test/Semantics/OpenMP/map-save-attribute.f90 b/flang/test/Semantics/OpenMP/map-save-attribute.f90
index f1424d236f8bd..102acbbb39945 100644
--- a/flang/test/Semantics/OpenMP/map-save-attribute.f90
+++ b/flang/test/Semantics/OpenMP/map-save-attribute.f90
@@ -3,11 +3,11 @@
 !NOWARN-NOT: warning:
 
 ! Verify that mapping a variable with the SAVE attribute (implicit or explicit)
-! produces a warning when the ALWAYS map modifier is not specified.
+! produces a warning when the ALWAYS modifier is not specified.
 
 subroutine explicit_save_warn
   integer, save :: s
-!WARNING: Variable 's' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+!WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: s)
   s = s + 1
   !$omp end target
@@ -24,7 +24,7 @@ subroutine data_init_implicit_save
   ! A variable initialized in a DATA statement has an implicit SAVE attribute.
   integer :: s
   data s /0/
-!WARNING: Variable 's' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+!WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: s)
   s = s + 1
   !$omp end target
@@ -33,7 +33,7 @@ subroutine data_init_implicit_save
 subroutine initializer_implicit_save
   ! A variable with a default initializer has an implicit SAVE attribute.
   integer :: s = 0
-!WARNING: Variable 's' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+!WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: s)
   s = s + 1
   !$omp end target
@@ -50,7 +50,7 @@ subroutine non_save_no_warn(x)
 subroutine mixed_list
   integer, save :: s
   integer :: x
-!WARNING: Variable 's' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+!WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: s, x)
   s = s + x
   !$omp end target
@@ -62,15 +62,68 @@ module m
   integer, save :: mve = 0
 contains
   subroutine use_module_var
-!WARNING: Variable 'mv' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
-!WARNING: Variable 'mve' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+!WARNING: Variable 'mv' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
+!WARNING: Variable 'mve' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
     !$omp target map(tofrom: mv, mve)
     mv = mv + 1
+    mve = mve + 1
     !$omp end target
   end
   subroutine use_module_var_always
     !$omp target map(always, tofrom: mv,mve)
     mv = mv + 1
+    mve = mve + 1
     !$omp end target
   end
 end module
+
+subroutine explicit_save_allocatable_array
+  integer, allocatable, save :: sa(:)
+  allocate(sa(10))
+!WARNING: Variable 'sa' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
+  !$omp target map(tofrom: sa)
+  sa(1) = sa(1) + 1
+  !$omp end target
+end
+
+subroutine explicit_save_allocatable_array_always
+  integer, allocatable, save :: sa(:)
+  allocate(sa(10))
+  !$omp target map(always, tofrom: sa)
+  sa(1) = sa(1) + 1
+  !$omp end target
+end
+
+module m_alloc
+  ! Module-level allocatable arrays have implicit SAVE.
+  integer, allocatable :: ma(:)
+contains
+  subroutine use_module_allocatable
+    allocate(ma(10))
+!WARNING: Variable 'ma' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
+    !$omp target map(tofrom: ma)
+    ma(1) = ma(1) + 1
+    !$omp end target
+  end
+  subroutine use_module_allocatable_always
+    allocate(ma(10))
+    !$omp target map(always, tofrom: ma)
+    ma(1) = ma(1) + 1
+    !$omp end target
+  end
+end module
+
+subroutine component_dedup
+  type :: t
+    integer :: a
+    integer :: b
+  end type
+  type(t), save :: s
+  ! The base variable is referenced twice (as `s` and as `s%a`), but only one
+  ! warning about the base `s` should be emitted.
+!WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
+  !$omp target map(tofrom: s, s%a)
+  s%a = s%a + 1
+  !$omp end target
+end
+
diff --git a/flang/test/Semantics/OpenMP/nested-target.f90 b/flang/test/Semantics/OpenMP/nested-target.f90
index 0c8ac23715a03..9792e26994e6f 100644
--- a/flang/test/Semantics/OpenMP/nested-target.f90
+++ b/flang/test/Semantics/OpenMP/nested-target.f90
@@ -41,13 +41,13 @@ program main
   allocate(B(N))
   !$omp target
   !PORTABILITY: If TARGET ENTER DATA directive is nested inside TARGET region, the behaviour is unspecified [-Wopenmp-usage]
-  !WARNING: Variable 'b' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'b' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target enter data map(alloc:B)
   !$omp end target
 
   !$omp target
   !PORTABILITY: If TARGET EXIT DATA directive is nested inside TARGET region, the behaviour is unspecified [-Wopenmp-usage]
-  !WARNING: Variable 'b' has the SAVE attribute and is mapped without the ALWAYS map modifier; the host value may not be synchronized with the device on subsequent target regions [-Wopenmp-map-save-without-always]
+  !WARNING: Variable 'b' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target exit data map(delete:B)
   !$omp end target
   deallocate(B)

>From b3fa5d27b618c19da39e9f908cd83bbdd60b476b Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Mon, 13 Jul 2026 20:08:46 +0200
Subject: [PATCH 4/6] Add warning also for COMMON blocks

---
 flang/lib/Semantics/check-omp-structure.cpp   | 23 +++++++++++----
 .../Semantics/OpenMP/map-save-attribute.f90   | 29 +++++++++++++++++++
 2 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index ed446b2ff4824..6054ed1745b7f 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -4882,14 +4882,27 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
         continue;
       }
       const Symbol &ultimate{baseSym->GetUltimate()};
-      if (!IsSaved(ultimate) || !warnedBases.insert(&ultimate).second) {
+      // COMMON block members have static storage that persists across target
+      // regions just like SAVEd variables, so the same reference-counted map
+      // concern applies.
+      bool isSaved{IsSaved(ultimate)};
+      bool inCommon{
+          !isSaved && semantics::FindCommonBlockContaining(ultimate) != nullptr};
+      if ((!isSaved && !inCommon) || !warnedBases.insert(&ultimate).second) {
         continue;
       }
       auto maybeSource{GetObjectSource(object)};
-      context_.Warn(common::UsageWarning::OpenMPMapSaveWithoutAlways,
-          maybeSource.value_or(GetContext().clauseSource),
-          "Variable '%s' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device"_warn_en_US,
-          ultimate.name());
+      if (isSaved) {
+        context_.Warn(common::UsageWarning::OpenMPMapSaveWithoutAlways,
+            maybeSource.value_or(GetContext().clauseSource),
+            "Variable '%s' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device"_warn_en_US,
+            ultimate.name());
+      } else {
+        context_.Warn(common::UsageWarning::OpenMPMapSaveWithoutAlways,
+            maybeSource.value_or(GetContext().clauseSource),
+            "Variable '%s' is a member of a COMMON block and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device"_warn_en_US,
+            ultimate.name());
+      }
     }
   }
 
diff --git a/flang/test/Semantics/OpenMP/map-save-attribute.f90 b/flang/test/Semantics/OpenMP/map-save-attribute.f90
index 102acbbb39945..9763ea9ec17d9 100644
--- a/flang/test/Semantics/OpenMP/map-save-attribute.f90
+++ b/flang/test/Semantics/OpenMP/map-save-attribute.f90
@@ -113,6 +113,35 @@ subroutine use_module_allocatable_always
   end
 end module
 
+subroutine common_block_member
+  integer :: cv
+  common /cb/ cv
+!WARNING: Variable 'cv' is a member of a COMMON block and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
+  !$omp target map(tofrom: cv)
+  cv = cv + 1
+  !$omp end target
+end
+
+subroutine common_block_member_always
+  integer :: cv
+  common /cb/ cv
+  !$omp target map(always, tofrom: cv)
+  cv = cv + 1
+  !$omp end target
+end
+
+subroutine common_block_with_save
+  ! A COMMON block with an explicit SAVE attribute promotes each member's
+  ! SAVE-ness, so the SAVE-worded warning is emitted.
+  integer :: cv
+  common /cbs/ cv
+  save /cbs/
+!WARNING: Variable 'cv' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
+  !$omp target map(tofrom: cv)
+  cv = cv + 1
+  !$omp end target
+end
+
 subroutine component_dedup
   type :: t
     integer :: a

>From 700d38cf32cf2f5a823e696bfd658c1c3511c0c4 Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Mon, 13 Jul 2026 20:32:56 +0200
Subject: [PATCH 5/6] Do not warn when variable hasn't been DECLARE TARGET'ed

---
 flang/lib/Semantics/check-omp-structure.cpp   | 16 +++++--
 .../Semantics/OpenMP/clause-validity01.f90    |  1 -
 .../Semantics/OpenMP/combined-constructs.f90  | 13 ------
 .../Semantics/OpenMP/declare-mapper04.f90     |  2 -
 .../Semantics/OpenMP/device-constructs.f90    |  1 -
 .../OpenMP/device-omp-initial-invalid.f90     |  6 ---
 .../Semantics/OpenMP/map-save-attribute.f90   | 46 +++++++++++++++----
 flang/test/Semantics/OpenMP/nested-target.f90 |  2 -
 8 files changed, 50 insertions(+), 37 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 6054ed1745b7f..dec155e7f41fd 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -4884,11 +4884,19 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
       const Symbol &ultimate{baseSym->GetUltimate()};
       // COMMON block members have static storage that persists across target
       // regions just like SAVEd variables, so the same reference-counted map
-      // concern applies.
+      // concern applies. However, the persistence only matters if the variable
+      // is DECLARE TARGET'd (or belongs to a DECLARE TARGET'd COMMON block),
+      // because that is what makes its device instance persist across regions.
       bool isSaved{IsSaved(ultimate)};
-      bool inCommon{
-          !isSaved && semantics::FindCommonBlockContaining(ultimate) != nullptr};
-      if ((!isSaved && !inCommon) || !warnedBases.insert(&ultimate).second) {
+      const Symbol *commonBlock{semantics::FindCommonBlockContaining(ultimate)};
+      bool inCommon{!isSaved && commonBlock != nullptr};
+      if (!isSaved && !inCommon) {
+        continue;
+      }
+      bool isDeclareTarget{ultimate.test(Symbol::Flag::OmpDeclareTarget) ||
+          (commonBlock &&
+              commonBlock->test(Symbol::Flag::OmpDeclareTarget))};
+      if (!isDeclareTarget || !warnedBases.insert(&ultimate).second) {
         continue;
       }
       auto maybeSource{GetObjectSource(object)};
diff --git a/flang/test/Semantics/OpenMP/clause-validity01.f90 b/flang/test/Semantics/OpenMP/clause-validity01.f90
index 5284cf5f7cd53..773fd82aaeb43 100644
--- a/flang/test/Semantics/OpenMP/clause-validity01.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity01.f90
@@ -85,7 +85,6 @@
   !$omp end target
 
   !ERROR: ALLOCATE clause is not allowed on TARGET DATA directive
-  !WARNING: Variable 'b' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target data map(from: b) allocate(b)
   do i = 1, N
      z = 2
diff --git a/flang/test/Semantics/OpenMP/combined-constructs.f90 b/flang/test/Semantics/OpenMP/combined-constructs.f90
index 10a6b0bd67492..c01998b1fa1b0 100644
--- a/flang/test/Semantics/OpenMP/combined-constructs.f90
+++ b/flang/test/Semantics/OpenMP/combined-constructs.f90
@@ -47,7 +47,6 @@ program main
   enddo
   !$omp end target parallel
 
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target parallel map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -95,7 +94,6 @@ program main
   enddo
   !$omp end target parallel do
 
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target parallel do map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -110,7 +108,6 @@ program main
   enddo
   !$omp end target parallel do
 
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams map(a)
   do i = 1, N
      a(i) = 3.14d0
@@ -204,7 +201,6 @@ program main
   enddo
   !$omp end target teams
 
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -212,7 +208,6 @@ program main
   !$omp end target teams
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS directive
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -220,7 +215,6 @@ program main
   !$omp end target teams
 
 
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute map(a)
   do i = 1, N
      a(i) = 3.14d0
@@ -307,7 +301,6 @@ program main
   enddo
   !$omp end target teams distribute
 
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -315,7 +308,6 @@ program main
   !$omp end target teams distribute
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS DISTRIBUTE directive
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -402,7 +394,6 @@ program main
   enddo
   !$omp end target teams distribute parallel do
 
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -410,7 +401,6 @@ program main
   !$omp end target teams distribute parallel do
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS DISTRIBUTE PARALLEL DO directive
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -418,7 +408,6 @@ program main
   !$omp end target teams distribute parallel do
 
 
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do simd map(a)
   do i = 1, N
      a(i) = 3.14d0
@@ -505,7 +494,6 @@ program main
   enddo
   !$omp end target teams distribute parallel do simd
 
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do simd map(tofrom:a)
   do i = 1, N
      a(i) = 3.14d0
@@ -513,7 +501,6 @@ program main
   !$omp end target teams distribute parallel do simd
 
   !ERROR: Only the ALLOC, FROM, TO, TOFROM map types are permitted for MAP clauses on the TARGET TEAMS DISTRIBUTE PARALLEL DO SIMD directive
-  !WARNING: Variable 'a' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target teams distribute parallel do simd map(delete:a)
   do i = 1, N
      a(i) = 3.14d0
diff --git a/flang/test/Semantics/OpenMP/declare-mapper04.f90 b/flang/test/Semantics/OpenMP/declare-mapper04.f90
index 9396cc7d7b4b8..2f45e230c3513 100644
--- a/flang/test/Semantics/OpenMP/declare-mapper04.f90
+++ b/flang/test/Semantics/OpenMP/declare-mapper04.f90
@@ -9,12 +9,10 @@
 end type
 
 !ERROR: DECLARE_MAPPER directive should have a single argument
-!WARNING: Variable 'x' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
 !$omp declare mapper(m1:t1::x, m2:t2::x) map(x, x%y)
 
 integer :: x(10)
 !ERROR: The argument to the DECLARE_MAPPER directive should be a mapper-specifier
-!WARNING: Variable 'x' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
 !$omp declare mapper(x) map(to: x)
 
 end
diff --git a/flang/test/Semantics/OpenMP/device-constructs.f90 b/flang/test/Semantics/OpenMP/device-constructs.f90
index 7af73d8a58fa2..b74304222c835 100644
--- a/flang/test/Semantics/OpenMP/device-constructs.f90
+++ b/flang/test/Semantics/OpenMP/device-constructs.f90
@@ -12,7 +12,6 @@ program main
   arrayB = 3.14d0
   N = 256
 
-  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(arrayA)
   do i = 1, N
      a = 3.14d0
diff --git a/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90 b/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90
index a480c8c56612a..6b76265bd96f9 100644
--- a/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90
+++ b/flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90
@@ -48,22 +48,16 @@ program main
   !$omp end target
 
   ! Also accepted on target data and its data motion variants.
-  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target data map(to: arrayA) device(omp_initial_device)
   !$omp end target data
 
-  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target data map(to: arrayA) device(omp_invalid_device)
   !$omp end target data
 
-  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target enter data map(alloc: arrayA) device(omp_initial_device)
-  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target enter data map(alloc: arrayA) device(omp_invalid_device)
 
-  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target exit data map(delete: arrayA) device(omp_initial_device)
-  !WARNING: Variable 'arraya' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target exit data map(delete: arrayA) device(omp_invalid_device)
 
   !$omp target update to(arrayA) device(omp_initial_device)
diff --git a/flang/test/Semantics/OpenMP/map-save-attribute.f90 b/flang/test/Semantics/OpenMP/map-save-attribute.f90
index 9763ea9ec17d9..ab3f6fa617ecd 100644
--- a/flang/test/Semantics/OpenMP/map-save-attribute.f90
+++ b/flang/test/Semantics/OpenMP/map-save-attribute.f90
@@ -3,10 +3,14 @@
 !NOWARN-NOT: warning:
 
 ! Verify that mapping a variable with the SAVE attribute (implicit or explicit)
-! produces a warning when the ALWAYS modifier is not specified.
+! or that belongs to a COMMON block produces a warning when the ALWAYS map
+! modifier is not specified -- but only when that variable is also part of a
+! DECLARE TARGET directive, since that is what makes its device instance
+! persist across regions.
 
 subroutine explicit_save_warn
   integer, save :: s
+  !$omp declare target(s)
 !WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: s)
   s = s + 1
@@ -15,15 +19,25 @@ subroutine explicit_save_warn
 
 subroutine explicit_save_with_always
   integer, save :: s
+  !$omp declare target(s)
   !$omp target map(always, tofrom: s)
   s = s + 1
   !$omp end target
 end
 
+subroutine explicit_save_no_declare_target
+  ! SAVE but not DECLARE TARGET -> no warning.
+  integer, save :: s
+  !$omp target map(tofrom: s)
+  s = s + 1
+  !$omp end target
+end
+
 subroutine data_init_implicit_save
   ! A variable initialized in a DATA statement has an implicit SAVE attribute.
   integer :: s
   data s /0/
+  !$omp declare target(s)
 !WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: s)
   s = s + 1
@@ -33,6 +47,7 @@ subroutine data_init_implicit_save
 subroutine initializer_implicit_save
   ! A variable with a default initializer has an implicit SAVE attribute.
   integer :: s = 0
+  !$omp declare target(s)
 !WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: s)
   s = s + 1
@@ -50,6 +65,7 @@ subroutine non_save_no_warn(x)
 subroutine mixed_list
   integer, save :: s
   integer :: x
+  !$omp declare target(s)
 !WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: s, x)
   s = s + x
@@ -60,6 +76,7 @@ module m
   ! Module variables have implicit SAVE.
   integer :: mv = 0
   integer, save :: mve = 0
+  !$omp declare target(mv, mve)
 contains
   subroutine use_module_var
 !WARNING: Variable 'mv' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
@@ -77,8 +94,20 @@ subroutine use_module_var_always
   end
 end module
 
+module m_no_dt
+  ! Module variables have implicit SAVE, but no DECLARE TARGET -> no warning.
+  integer :: mv = 0
+contains
+  subroutine use_it
+    !$omp target map(tofrom: mv)
+    mv = mv + 1
+    !$omp end target
+  end
+end module
+
 subroutine explicit_save_allocatable_array
   integer, allocatable, save :: sa(:)
+  !$omp declare target(sa)
   allocate(sa(10))
 !WARNING: Variable 'sa' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: sa)
@@ -88,6 +117,7 @@ subroutine explicit_save_allocatable_array
 
 subroutine explicit_save_allocatable_array_always
   integer, allocatable, save :: sa(:)
+  !$omp declare target(sa)
   allocate(sa(10))
   !$omp target map(always, tofrom: sa)
   sa(1) = sa(1) + 1
@@ -97,6 +127,7 @@ subroutine explicit_save_allocatable_array_always
 module m_alloc
   ! Module-level allocatable arrays have implicit SAVE.
   integer, allocatable :: ma(:)
+  !$omp declare target(ma)
 contains
   subroutine use_module_allocatable
     allocate(ma(10))
@@ -116,6 +147,7 @@ subroutine use_module_allocatable_always
 subroutine common_block_member
   integer :: cv
   common /cb/ cv
+  !$omp declare target(/cb/)
 !WARNING: Variable 'cv' is a member of a COMMON block and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target map(tofrom: cv)
   cv = cv + 1
@@ -125,18 +157,16 @@ subroutine common_block_member
 subroutine common_block_member_always
   integer :: cv
   common /cb/ cv
+  !$omp declare target(/cb/)
   !$omp target map(always, tofrom: cv)
   cv = cv + 1
   !$omp end target
 end
 
-subroutine common_block_with_save
-  ! A COMMON block with an explicit SAVE attribute promotes each member's
-  ! SAVE-ness, so the SAVE-worded warning is emitted.
+subroutine common_block_no_declare_target
+  ! COMMON block but not DECLARE TARGET -> no warning.
   integer :: cv
-  common /cbs/ cv
-  save /cbs/
-!WARNING: Variable 'cv' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
+  common /cb/ cv
   !$omp target map(tofrom: cv)
   cv = cv + 1
   !$omp end target
@@ -148,6 +178,7 @@ subroutine component_dedup
     integer :: b
   end type
   type(t), save :: s
+  !$omp declare target(s)
   ! The base variable is referenced twice (as `s` and as `s%a`), but only one
   ! warning about the base `s` should be emitted.
 !WARNING: Variable 's' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
@@ -155,4 +186,3 @@ subroutine component_dedup
   s%a = s%a + 1
   !$omp end target
 end
-
diff --git a/flang/test/Semantics/OpenMP/nested-target.f90 b/flang/test/Semantics/OpenMP/nested-target.f90
index 9792e26994e6f..59d891b420ee4 100644
--- a/flang/test/Semantics/OpenMP/nested-target.f90
+++ b/flang/test/Semantics/OpenMP/nested-target.f90
@@ -41,13 +41,11 @@ program main
   allocate(B(N))
   !$omp target
   !PORTABILITY: If TARGET ENTER DATA directive is nested inside TARGET region, the behaviour is unspecified [-Wopenmp-usage]
-  !WARNING: Variable 'b' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target enter data map(alloc:B)
   !$omp end target
 
   !$omp target
   !PORTABILITY: If TARGET EXIT DATA directive is nested inside TARGET region, the behaviour is unspecified [-Wopenmp-usage]
-  !WARNING: Variable 'b' has the SAVE attribute and appears in a MAP clause without the ALWAYS modifier; the map operation may be skipped when the variable is already present on the device [-Wopenmp-map-save-without-always]
   !$omp target exit data map(delete:B)
   !$omp end target
   deallocate(B)

>From 77c36652097c1e29820245d32fab7a93b63dfd8f Mon Sep 17 00:00:00 2001
From: Michael Klemm <michael.klemm at amd.com>
Date: Thu, 23 Jul 2026 15:15:59 +0200
Subject: [PATCH 6/6] Fix formatting

---
 flang/lib/Semantics/check-omp-structure.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index dec155e7f41fd..c13f6d3a63b43 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -4894,8 +4894,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
         continue;
       }
       bool isDeclareTarget{ultimate.test(Symbol::Flag::OmpDeclareTarget) ||
-          (commonBlock &&
-              commonBlock->test(Symbol::Flag::OmpDeclareTarget))};
+          (commonBlock && commonBlock->test(Symbol::Flag::OmpDeclareTarget))};
       if (!isDeclareTarget || !warnedBases.insert(&ultimate).second) {
         continue;
       }



More information about the flang-commits mailing list