[flang-commits] [flang] [Flang][OpenMP] Prevent unrequired implicit maps for private variables on composite directives (PR #217121)

Leandro Lupori via flang-commits flang-commits at lists.llvm.org
Fri Aug 28 07:43:22 PDT 2026


================
@@ -3952,6 +3967,147 @@ static void collectSymbolsWithDynamicSubstring(
   symbolsWithDynamicSubstring = visitor.symbolsWithDynamicSubstring;
 }
 
+// Reverse search the composite/combined directive and return the first leaf
+// carrying a private clause, the OpenMP specification dictates that the first
+// leaf that can legally have a private clause becomes the owner.
+//
+// The function intentionally skips the node representing the root target
+// directive by setting the end (begin) point to the second leaf, the private
+// clauses on a standalone target region should apply through regular
+// privatization means. Otherwise, if we find no relevant leaf node (carrying a
+// private clause) we return the end of the iterator.
+static ConstructQueue::const_iterator
+findInnermostPrivateLeaf(ConstructQueue::const_iterator item,
+                         const ConstructQueue &queue) {
+  auto hasPrivate = [](ConstructQueue::const_iterator it) {
+    return llvm::any_of(it->clauses, [](const Clause &clause) {
+      return std::holds_alternative<clause::Private>(clause.u);
+    });
+  };
+  auto begin = std::next(item);
+  for (auto it = queue.end(); it != begin;) {
+    --it;
+    if (hasPrivate(it))
+      return it;
+  }
+  return queue.end();
+}
+
+// Verify if it's safe to drop the map and materialize a clone. Certain cases
+// still currently require a map to be OpenMP specification compliant, a
+// rewording of the specfications privatization rules made to be a bit more
+// concise, if the type is an:
+//
+//  Allocatable -
+//    1) If the status is unallocated, the private allocatable is unallocated
+//    2) If the allocation status is allocated, the private allocatable is
+//    allocated 3) If it's an array, the allocatables shape and bounds will be
+//    the same.
+//  Pointer - The intial status of a private pointer is undefined.
+//  Other - If the type of the list item has default initialization, the new
+//  list item has default initialization. Otherwise, it's undefined.
+//
+// From the above list, it is currently fine to drop maps and materialize clones
+// for scalars, pointers, and constant arrays. As their creation does not
+// neccesitate referencing dynamic extent/shape/allocation-status values defined
+// outside the target region. Types that fall into this category and that need a
+// map still (just to populate the initial state) are allocatables (and by
+// extension this unfortunately precludes derived types with allocatables in
+// them for now), and assumed shape arrays (size being illegal on a private
+// clause). Currently we leave these as they were before, but in the future we
+// can minimize the mapping by sending across exactly what we require to be
+// specification compliant.
+static bool isTargetLocalCloneable(const semantics::Symbol &sym,
+                                   semantics::SemanticsContext &semaCtx) {
+  const semantics::Symbol &ult = sym.GetUltimate();
+  // Allocatable descriptors cannot be cloned and have their implicit map
+  // blocked, the clone requires the host descriptor for allocation
+  // status/bounds.
+  if (semantics::IsAllocatable(ult))
+    return false;
+
+  // A derived type with an allocatable ultimate component cannot be cloned and
+  // have its implicit map dropped for the same reasons we currently have to
+  // do it for the allocatable case.
+  if (const semantics::DeclTypeSpec *declType = ult.GetType())
+    if (const semantics::DerivedTypeSpec *derived = declType->AsDerived())
+      if (semantics::FindAllocatableUltimateComponent(*derived))
+        return false;
+
+  // Pointers initial status is undefined, so we can just materialize it with
+  // the correct typing, but we need to be a little more careful with anything
+  // else that isn't a scalar and verify that it has constant extents and shape.
+  if (ult.Rank() > 0 && !semantics::IsPointer(ult)) {
+    evaluate::FoldingContext &foldingContext = semaCtx.foldingContext();
+    std::optional<evaluate::Shape> shape =
+        evaluate::GetShape(foldingContext, ult);
+    std::optional<evaluate::ConstantSubscripts> extents =
+        evaluate::AsConstantExtents(foldingContext, shape);
+    if (!extents || evaluate::HasNegativeExtent(*extents))
+      return false;
+  }
+  return true;
+}
+
+// Discover the `private` list items of the inner leaf construct of a
+// combined/composite clause e.g. the "s" in `target teams distribute
+// private(a)`. These are not privatized on the target itself, and without
----------------
luporl wrote:

```suggestion
// combined/composite clause e.g. the "s" in `target teams distribute
// private(s)`. These are not privatized on the target itself, and without
```

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


More information about the flang-commits mailing list