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

via flang-commits flang-commits at lists.llvm.org
Mon Jul 13 03:37:51 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-semantics

Author: Michael Klemm (mjklemm)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/209134.diff


10 Files Affected:

- (modified) flang/include/flang/Support/Fortran-features.h (+2-1) 
- (modified) flang/lib/Semantics/check-omp-structure.cpp (+31) 
- (modified) flang/lib/Support/Fortran-features.cpp (+1) 
- (modified) flang/test/Semantics/OpenMP/clause-validity01.f90 (+1) 
- (modified) flang/test/Semantics/OpenMP/combined-constructs.f90 (+13) 
- (modified) flang/test/Semantics/OpenMP/declare-mapper04.f90 (+2) 
- (modified) flang/test/Semantics/OpenMP/device-constructs.f90 (+1) 
- (modified) flang/test/Semantics/OpenMP/device-omp-initial-invalid.f90 (+6) 
- (added) flang/test/Semantics/OpenMP/map-save-attribute.f90 (+76) 
- (modified) flang/test/Semantics/OpenMP/nested-target.f90 (+2) 


``````````diff
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)

``````````

</details>


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


More information about the flang-commits mailing list