[flang-commits] [flang] [Flang][OpenMP][Sema] Add OpenMP warning when mapping local descriptors to device on enter without a corresponding exit (PR #201060)
via flang-commits
flang-commits at lists.llvm.org
Tue Jun 2 01:39:05 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
Author: agozillon
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/201060.diff
6 Files Affected:
- (modified) flang/include/flang/Semantics/openmp-utils.h (+6)
- (modified) flang/lib/Semantics/check-omp-structure.cpp (+62)
- (modified) flang/lib/Semantics/check-omp-structure.h (+7)
- (modified) flang/lib/Semantics/openmp-utils.cpp (+23)
- (added) flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor-omp61.f90 (+97)
- (added) flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor.f90 (+93)
``````````diff
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 4001b337193a1..15cebca0d6f85 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -106,6 +106,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 ff41f49d88b32..85e47c85f0d5e 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -141,7 +141,35 @@ 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();
}
@@ -151,6 +179,7 @@ void OmpStructureChecker::Enter(const parser::FunctionStmt &x) {
}
void OmpStructureChecker::Enter(const parser::EndFunctionStmt &x) {
+ CheckTempDescriptorMappings();
scopeStack_.pop_back();
}
@@ -160,6 +189,7 @@ void OmpStructureChecker::Enter(const parser::MpSubprogramStmt &x) {
}
void OmpStructureChecker::Enter(const parser::EndMpSubprogramStmt &x) {
+ CheckTempDescriptorMappings();
scopeStack_.pop_back();
}
@@ -4688,6 +4718,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 b3f58335027de..28b3288636c2f 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -400,6 +400,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]--; }
@@ -420,6 +421,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 51dd08d0924b1..32a86eb7dc26c 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -249,6 +249,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
``````````
</details>
https://github.com/llvm/llvm-project/pull/201060
More information about the flang-commits
mailing list