[flang-commits] [flang] e1110da - [Flang][OpenMP][Sema] Add OpenMP warning when mapping local descriptors to device on enter without a corresponding exit (#201060)
via flang-commits
flang-commits at lists.llvm.org
Wed Jun 10 01:14:41 PDT 2026
Author: agozillon
Date: 2026-06-10T10:14:36+02:00
New Revision: e1110dabc49d10c9353d6af7555c56f55fb0cca8
URL: https://github.com/llvm/llvm-project/commit/e1110dabc49d10c9353d6af7555c56f55fb0cca8
DIFF: https://github.com/llvm/llvm-project/commit/e1110dabc49d10c9353d6af7555c56f55fb0cca8.diff
LOG: [Flang][OpenMP][Sema] Add OpenMP warning when mapping local descriptors to device on enter without a corresponding exit (#201060)
This PR aims to add a new warning to Flang that will emit when a user
tries to map a local/temporary descriptor to device on an enter
directive without also applying it to a corresponding exit directive.
This problem can cause some pretty unique and difficult to track down
errors in programs as it can result in a user unintentionally locking
into place a stack allocated descriptor that has fallen out of scope,
which can result in a later clash with another stack allocated variable
that's being mapped and just happens to reside in the old descriptor
address range.
So this PR attempts to warn about this problem to prevent users doing
so, it's of note that we handle some of these cases in our
MapInfoFinalization pass, but I believe we should still include these
cases for portability reasons and incase we ever backtrack on our
decision to silently support some of these cases.
Made this warning as it was a suggestion from Michael Klemm and seemed
like a good PR to add to guide users to avoid this pattern (as it
unfortunately seems to be a common one that pops up). I'll perhaps look
into an optimization pass that tries to resolve some of these cases
silently in the future, but this will have to do in the meantime.
Added:
flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor-omp61.f90
flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor.f90
Modified:
flang/include/flang/Semantics/openmp-utils.h
flang/lib/Semantics/check-omp-structure.cpp
flang/lib/Semantics/check-omp-structure.h
flang/lib/Semantics/openmp-utils.cpp
Removed:
################################################################################
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index e0358eafe487c..754d14df9f07c 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -119,6 +119,12 @@ const Symbol *GetHostSymbol(const Symbol &sym);
bool IsMapEnteringType(parser::OmpMapType::Value type);
bool IsMapExitingType(parser::OmpMapType::Value type);
+// Returns true if the symbol has a temporary stack-allocated descriptor.
+// This includes assumed-shape and assumed-rank dummy arguments that are
+// not allocatable or pointer. These descriptors are created on the caller's
+// stack and become invalid after the function returns.
+bool HasTemporaryStackDescriptor(const Symbol &symbol);
+
MaybeExpr GetEvaluateExpr(const parser::Expr &parserExpr);
template <typename T> MaybeExpr GetEvaluateExpr(const T &inp) {
return GetEvaluateExpr(parser::UnwrapRef<parser::Expr>(inp));
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 9c2516d39947d..8651678bb2f8a 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -142,7 +142,27 @@ void OmpStructureChecker::Enter(const parser::SubroutineStmt &x) {
scopeStack_.push_back(sym->scope());
}
+void OmpStructureChecker::CheckTempDescriptorMappings() {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ for (const auto &[symbol, source] : tempDescriptorEnterMaps_) {
+ if (tempDescriptorExitMaps_.find(symbol) == tempDescriptorExitMaps_.end()) {
+ if (version >= 61) {
+ context_.Warn(common::UsageWarning::OpenMPUsage, source,
+ "The map of '%s' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference. To avoid mapping the descriptor utilize OpenMP's ref_ptee reference modifier to map just the data"_warn_en_US,
+ symbol->name());
+ } else {
+ context_.Warn(common::UsageWarning::OpenMPUsage, source,
+ "The map of '%s' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference"_warn_en_US,
+ symbol->name());
+ }
+ }
+ }
+ tempDescriptorEnterMaps_.clear();
+ tempDescriptorExitMaps_.clear();
+}
+
void OmpStructureChecker::Enter(const parser::EndSubroutineStmt &x) {
+ CheckTempDescriptorMappings();
scopeStack_.pop_back();
}
@@ -152,6 +172,7 @@ void OmpStructureChecker::Enter(const parser::FunctionStmt &x) {
}
void OmpStructureChecker::Enter(const parser::EndFunctionStmt &x) {
+ CheckTempDescriptorMappings();
scopeStack_.pop_back();
}
@@ -161,6 +182,7 @@ void OmpStructureChecker::Enter(const parser::MpSubprogramStmt &x) {
}
void OmpStructureChecker::Enter(const parser::EndMpSubprogramStmt &x) {
+ CheckTempDescriptorMappings();
scopeStack_.pop_back();
}
@@ -4703,6 +4725,38 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
}
}
}
+
+ // 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
+ // having a temporary stack descriptor. If we have reference modifiers, we
+ // ignore the warning and trust that the user knows what they are doing
+ // already, as they are aware the type comes with a descriptor and pointer
+ // combination.
+ //
+ // We will utilise this information to emit a warning later if the neccesary
+ // conditions are met, where we have an enter map without a corresponding exit
+ // in the current scope.
+ bool hasRefModifier{
+ OmpGetUniqueModifier<parser::OmpRefModifier>(modifiers) != nullptr};
+ if (!hasRefModifier &&
+ (llvm::is_contained(leafs, Directive::OMPD_target_enter_data) ||
+ llvm::is_contained(leafs, Directive::OMPD_target_exit_data))) {
+ for (const parser::OmpObject &object : objects.v) {
+ if (const Symbol *sym{GetObjectSymbol(object, /*ultimate=*/true)}) {
+ if (HasTemporaryStackDescriptor(*sym)) {
+ auto maybeSource{GetObjectSource(object)};
+ parser::CharBlock source{
+ maybeSource.value_or(GetContext().clauseSource)};
+ if (llvm::is_contained(leafs, Directive::OMPD_target_enter_data)) {
+ tempDescriptorEnterMaps_.emplace(sym, source);
+ } else {
+ tempDescriptorExitMaps_.insert(sym);
+ }
+ }
+ }
+ }
+ }
}
void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 4d4dc88533d81..e601273d81f67 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -407,6 +407,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
const parser::OmpClause &initClause);
void CheckAllowedRequiresClause(llvm::omp::Clause clause);
void AddEndDirectiveClauses(const parser::OmpClauseList &clauses);
+ void CheckTempDescriptorMappings();
void EnterDirectiveNest(const int index) { directiveNest_[index]++; }
void ExitDirectiveNest(const int index) { directiveNest_[index]--; }
@@ -429,6 +430,12 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
int allocateDirectiveLevel_{0};
parser::CharBlock visitedAtomicSource_;
+ // Track symbols with temporary stack descriptors mapped in TARGET ENTER DATA
+ // and symbols mapped in TARGET EXIT DATA within the current function scope.
+ // Used to warn about potential issues with mapping temporary descriptors.
+ std::multimap<const Symbol *, parser::CharBlock> tempDescriptorEnterMaps_;
+ std::set<const Symbol *> tempDescriptorExitMaps_;
+
// Stack of nested DO loops and OpenMP constructs.
// This is used to verify DO loop nest for DOACROSS, and branches into
// and out of OpenMP constructs.
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index b02ef81176a63..1079537dc64e4 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -312,6 +312,29 @@ bool IsMapExitingType(parser::OmpMapType::Value type) {
}
}
+// This function aims to return true when a symbol is going to result
+// in a temporary stack descriptor being allocated for it in the
+// lowering that may pose an issue for data mapping if left on
+// device accidentally.
+bool HasTemporaryStackDescriptor(const Symbol &symbol) {
+ const Symbol &ultimate(symbol.GetUltimate());
+ bool isDummy = IsDummy(ultimate);
+
+ if (IsAllocatableOrPointer(ultimate)) {
+ return !isDummy;
+ }
+
+ if (!isDummy) {
+ return false;
+ }
+
+ if (const auto *obj = ultimate.detailsIf<ObjectEntityDetails>()) {
+ return obj->IsAssumedShape() || obj->IsAssumedRank();
+ }
+
+ return false;
+}
+
static MaybeExpr GetEvaluateExprFromTyped(const parser::TypedExpr &typedExpr) {
// ForwardOwningPointer typedExpr
// `- GenericExprWrapper ^.get()
diff --git a/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor-omp61.f90 b/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor-omp61.f90
new file mode 100644
index 0000000000000..e766b1b44d0fa
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor-omp61.f90
@@ -0,0 +1,97 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=61 -Werror -Wno-experimental-option
+
+! Check for OpenMP 6.1+ specific warning that includes ref_ptee suggestion
+! when mapping variables with temporary stack descriptors on TARGET ENTER DATA
+! without a corresponding TARGET EXIT DATA.
+
+subroutine test_assumed_shape_warning(arr)
+ integer, intent(inout), dimension(:,:) :: arr(:)
+ !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference. To avoid mapping the descriptor utilize OpenMP's ref_ptee reference modifier to map just the data [-Wopenmp-usage]
+ !$omp target enter data map(to: arr)
+end subroutine
+
+subroutine test_assumed_rank_warning(arr)
+ integer, intent(inout) :: arr(..)
+ !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference. To avoid mapping the descriptor utilize OpenMP's ref_ptee reference modifier to map just the data [-Wopenmp-usage]
+ !$omp target enter data map(to: arr)
+end subroutine
+
+subroutine test_local_allocatable_warning()
+ integer, allocatable :: local_arr(:)
+ allocate(local_arr(100))
+ !WARNING: The map of 'local_arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference. To avoid mapping the descriptor utilize OpenMP's ref_ptee reference modifier to map just the data [-Wopenmp-usage]
+ !$omp target enter data map(to: local_arr)
+ deallocate(local_arr)
+end subroutine
+
+subroutine test_local_pointer_warning()
+ integer, pointer :: local_ptr(:)
+ allocate(local_ptr(100))
+ !WARNING: The map of 'local_ptr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference. To avoid mapping the descriptor utilize OpenMP's ref_ptee reference modifier to map just the data [-Wopenmp-usage]
+ !$omp target enter data map(to: local_ptr)
+ deallocate(local_ptr)
+end subroutine
+
+module test_module
+contains
+ subroutine test_module_procedure_warning(arr)
+ integer, intent(inout) :: arr(:)
+ !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference. To avoid mapping the descriptor utilize OpenMP's ref_ptee reference modifier to map just the data [-Wopenmp-usage]
+ !$omp target enter data map(to: arr)
+ end subroutine
+
+ subroutine test_module_procedure_with_exit(arr)
+ integer, intent(inout) :: arr(:)
+ !$omp target enter data map(to: arr)
+ !$omp target exit data map(from: arr)
+ end subroutine
+end module
+
+! Test cases where warnings should not be emitted, the test_errors.py script
+! should fail if we emit errors for these that are not checked, so no need to
+! verify with an explicit check.
+
+subroutine test_ref_ptee_no_warning(arr)
+ integer, intent(inout) :: arr(:)
+ !$omp target enter data map(ref_ptee, to: arr)
+end subroutine
+
+subroutine test_ref_ptr_no_warning(arr)
+ integer, intent(inout) :: arr(:)
+ !$omp target enter data map(ref_ptr, to: arr)
+end subroutine
+
+subroutine test_ref_ptr_ptee_no_warning(arr)
+ integer, intent(inout) :: arr(:)
+ !$omp target enter data map(ref_ptr_ptee, to: arr)
+end subroutine
+
+subroutine test_with_exit_data(arr)
+ integer, intent(inout) :: arr(:)
+ !$omp target enter data map(to: arr)
+ !$omp target exit data map(from: arr)
+end subroutine
+
+subroutine test_explicit_shape_no_warning(arr, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: arr(n)
+ !$omp target enter data map(to: arr)
+end subroutine
+
+subroutine test_local_allocatable_with_exit()
+ integer, allocatable :: local_arr(:)
+ allocate(local_arr(100))
+ !$omp target enter data map(to: local_arr)
+ !$omp target exit data map(from: local_arr)
+ deallocate(local_arr)
+end subroutine
+
+subroutine test_allocatable_dummy_no_warning(arr)
+ integer, allocatable, intent(inout) :: arr(:)
+ !$omp target enter data map(to: arr)
+end subroutine
+
+subroutine test_pointer_dummy_no_warning(ptr)
+ integer, pointer, intent(inout) :: ptr(:)
+ !$omp target enter data map(to: ptr)
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor.f90 b/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor.f90
new file mode 100644
index 0000000000000..bd1eb98ebec60
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor.f90
@@ -0,0 +1,93 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -Werror -fopenmp-version=52 -Wno-experimental-option
+
+! Check for warning when mapping variables with temporary stack descriptors
+! (assumed-shape, assumed-rank, local allocatables, local pointers) on
+! TARGET ENTER DATA without a corresponding TARGET EXIT DATA in the same scope.
+
+subroutine test_assumed_shape_warning(arr)
+ integer, intent(inout) :: arr(:)
+ !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference [-Wopenmp-usage]
+ !$omp target enter data map(to: arr)
+end subroutine
+
+subroutine test_assumed_shape_2d_warning(arr)
+ integer, intent(inout) :: arr(:,:)
+ !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference [-Wopenmp-usage]
+ !$omp target enter data map(to: arr)
+end subroutine
+
+subroutine test_assumed_rank_warning(arr)
+ integer, intent(inout) :: arr(..)
+ !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference [-Wopenmp-usage]
+ !$omp target enter data map(to: arr)
+end subroutine
+
+subroutine test_local_pointer_warning()
+ integer, pointer :: local_ptr(:)
+ allocate(local_ptr(100))
+ !WARNING: The map of 'local_ptr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference [-Wopenmp-usage]
+ !$omp target enter data map(to: local_ptr)
+ deallocate(local_ptr)
+end subroutine
+
+subroutine test_local_allocatable_warning()
+ integer, allocatable :: local_arr(:)
+ allocate(local_arr(100))
+ !WARNING: The map of 'local_arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference [-Wopenmp-usage]
+ !$omp target enter data map(to: local_arr)
+ deallocate(local_arr)
+end subroutine
+
+module test_module
+contains
+ subroutine test_module_procedure_warning(arr)
+ integer, intent(inout) :: arr(:)
+ !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference [-Wopenmp-usage]
+ !$omp target enter data map(to: arr)
+ end subroutine
+
+ subroutine test_module_procedure_with_exit(arr)
+ integer, intent(inout) :: arr(:)
+ !$omp target enter data map(to: arr)
+ !$omp target exit data map(from: arr)
+ end subroutine
+end module
+
+! Test cases where warnings should not be emitted, the test_errors.py script
+! should fail if we emit errors for these that are not checked, so no need to
+! verify with an explicit check.
+
+subroutine test_pointer_dummy_no_warning(ptr)
+ integer, pointer, intent(inout) :: ptr(:)
+ !$omp target enter data map(to: ptr)
+end subroutine
+
+subroutine test_allocatable_dummy_no_warning(arr)
+ integer, allocatable, intent(inout) :: arr(:)
+ !$omp target enter data map(to: arr)
+end subroutine
+
+subroutine test_with_exit_data(arr)
+ integer, intent(inout) :: arr(:)
+ !$omp target enter data map(to: arr)
+ !$omp target exit data map(from: arr)
+end subroutine
+
+subroutine test_explicit_shape_no_warning(arr, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: arr(n)
+ !$omp target enter data map(to: arr)
+end subroutine
+
+subroutine test_assumed_size_no_warning(arr)
+ integer, intent(inout) :: arr(*)
+ !$omp target enter data map(to: arr(1:10))
+end subroutine
+
+subroutine test_local_allocatable_with_exit()
+ integer, allocatable :: local_arr(:)
+ allocate(local_arr(100))
+ !$omp target enter data map(to: local_arr)
+ !$omp target exit data map(from: local_arr)
+ deallocate(local_arr)
+end subroutine
More information about the flang-commits
mailing list