[flang-commits] [flang] [mlir] [flang][OpenMP] Support allocate clauses on scope constructs (PR #221229)

via flang-commits flang-commits at lists.llvm.org
Mon Sep 7 10:52:41 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-llvm

Author: Sairudra More (Saieiei)

<details>
<summary>Changes</summary>

Flang already lowers OpenMP 5.2 `allocate` clauses on `scope` constructs to `omp.scope`, but LLVM IR translation previously rejected them.

This change maps allocation operands to their corresponding private or firstprivate slots and reuses the existing allocator-backed private-storage and OpenMPIRBuilder allocation/free paths.

Support is currently limited to host-side fixed-size intrinsic scalar private and firstprivate variables, including SAVE variables and individually listed COMMON-block members. Arrays, descriptors, pointers, allocatables, derived types, named COMMON-block list items, and target/device allocation remain unsupported.

Assisted-by: GitHub Copilot

---

Patch is 66.79 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/221229.diff


15 Files Affected:

- (modified) flang/docs/OpenMPSupport.md (+1-2) 
- (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+81-69) 
- (added) flang/test/Integration/OpenMP/allocate-clause-equivalence.f90 (+47) 
- (added) flang/test/Integration/OpenMP/scope-allocate-clause.f90 (+225) 
- (modified) flang/test/Lower/OpenMP/Todo/allocate-clause-unsupported.f90 (+10) 
- (added) flang/test/Lower/OpenMP/Todo/scope-allocate-clause-unsupported.f90 (+55) 
- (modified) flang/test/Lower/OpenMP/scope.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/target-scope.f90 (+1-1) 
- (modified) mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp (+6-1) 
- (modified) mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp (+90-43) 
- (modified) mlir/test/Dialect/OpenMP/invalid.mlir (+73) 
- (modified) mlir/test/Dialect/OpenMP/ops.mlir (+37) 
- (modified) mlir/test/Target/LLVMIR/openmp-allocate-clause.mlir (+62-1) 
- (added) mlir/test/Target/LLVMIR/openmp-scope-allocate-clause.mlir (+303) 
- (modified) mlir/test/Target/LLVMIR/openmp-todo.mlir (-11) 


``````````diff
diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index 5a5285421e724..8b92911fbbcf7 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -226,7 +226,7 @@ Parser/Semantics, MLIR, Lowering, or the OpenMPIRBuilder.
 | otherwise clause on metadirectives | <span class="good">done</span> | | Lowering handles `OmpClause::Otherwise` as the fallback variant in `genMetadirective` (`flang/lib/Lower/OpenMP/OpenMP.cpp`); the legacy `default` clause spelling is also handled. | [llvm/llvm-project#194402](https://github.com/llvm/llvm-project/pull/194402), [llvm/llvm-project#194424](https://github.com/llvm/llvm-project/pull/194424) |
 | doacross with omp_cur_iteration | <span class="progress">in progress</span> | dreachem | Implement parser/semantics validation for `omp_cur_iteration` placement and lowering of doacross dependence tokens, then add semantics+lowering tests for source/sink combinations. | |
 | implicit map type for target enter and exit data | <span class="good">done</span> | | Target enter data applies implicit `to` mapping; target exit data applies implicit `from` mapping. Lowering via `getImplicitMapTypeAndKind` and MapInfoFinalization pass. Comprehensive test coverage in `target-enter-data-default-openmp52.f90` for OpenMP 5.2+ including allocatable descriptor handling. | [llvm/llvm-project#174665](https://github.com/llvm/llvm-project/pull/174665), [llvm/llvm-project#193851](https://github.com/llvm/llvm-project/pull/193851) |
-| allocate and firstprivate on scope directive | <span class="part">partial</span> | | Firstprivate on scope is fully supported and tested (`scope.f90`). Allocate clause on scope is parsed/lowered to MLIR but not yet implemented in LLVM IR translation layer (checkImplementationStatus in OpenMPToLLVMIRTranslation.cpp blocks it). | [llvm/llvm-project#193098](https://github.com/llvm/llvm-project/pull/193098) |
+| allocate and firstprivate on scope directive | <span class="part">partial</span> | | Firstprivate on scope is fully supported and tested (`scope.f90`). Host-side allocate clauses on scope use allocator-backed storage for fixed-size intrinsic scalar PRIVATE/FIRSTPRIVATE items with omitted/default/explicit allocator and ALIGN (`scope-allocate-clause.f90`, `openmp-scope-allocate-clause.mlir`), including SAVE entities and individually listed common-block members. Arrays, descriptors, pointers, allocatables, derived types, named common-block list items, and target/device scope allocation remain unsupported. | [llvm/llvm-project#193098](https://github.com/llvm/llvm-project/pull/193098) |
 | loop consistency changes for order clause | <span class="none">unclaimed</span> | | Extend semantic loop-consistency checks for updated ORDER rules and add diagnostics tests for invalid nest/ordering combinations. | |
 | keep original base pointer on map without matched candidate | <span class="none">unclaimed</span> | | Update map finalization so unmatched candidates preserve original base-pointer mapping semantics; add lowering tests for pointer-member mapping regressions. | |
 | pure procedure support for certain directives | <span class="good">done</span> | ShashwathiNavada | Semantics enforce that metadirective, assumption directives (ASSUME/ASSUMES), NOTHING, ERROR, and the loop-transforming constructs TILE/UNROLL are permitted in a Fortran PURE procedure, in addition to the baseline of SIMD and declarative directives; other directives are rejected with a version-aware diagnostic. | [llvm/llvm-project#212676](https://github.com/llvm/llvm-project/pull/212676) |
@@ -285,4 +285,3 @@ The OpenMP spec requires intrinsic- or pointer-assignments, which include (as pe
 - `expr equalop x` is an allowed condition in ATOMIC UPDATE COMPARE. [1]
 
 [1] Code generation for ATOMIC UPDATE COMPARE is not implemented yet.
-
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 870955b189245..75db4b29f706a 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2607,7 +2607,7 @@ static void genScopeClauses(lower::AbstractConverter &converter,
                             mlir::omp::ScopeOperands &clauseOps,
                             llvm::SmallVectorImpl<Object> &reductionObjects) {
   ClauseProcessor cp(converter, semaCtx, clauses);
-  cp.processAllocate(clauseOps);
+  cp.processAllocate(clauseOps, /*supportAlignment=*/true);
   cp.processNowait(clauseOps);
   cp.processReduction(loc, clauseOps, reductionObjects);
 }
@@ -3452,6 +3452,83 @@ genOrderedRegionOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
       queue, item, clauseOps);
 }
 
+/// Private operand order follows privatization expansion rather than the
+/// ALLOCATE clause's list order.
+template <typename ClauseOpsT>
+static void mapAllocateClauseToPrivateSlots(mlir::Location loc,
+                                            const List<Clause> &clauses,
+                                            const ObjectEntryBlockArgs &args,
+                                            ClauseOpsT &clauseOps) {
+  if (clauseOps.allocateVars.empty())
+    return;
+
+  llvm::DenseMap<const semantics::Symbol *, int64_t> privateSlots;
+  int64_t privateSlot = 0;
+  auto addPrivateSlot = [&](const semantics::Symbol &symbol) {
+    if (!privateSlots.try_emplace(&symbol.GetUltimate(), privateSlot).second)
+      fir::emitFatalError(
+          loc, "symbol with multiple private storage slots on one construct");
+    ++privateSlot;
+  };
+  for (const Object &object : args.priv.objects) {
+    const semantics::Symbol *symbol = object.sym();
+    if (!symbol)
+      fir::emitFatalError(loc, "private item without a semantic symbol");
+    // A privatized common block contributes one private operand per member,
+    // so slot numbering must follow the same expansion.
+    if (const auto *commonDetails =
+            symbol->detailsIf<semantics::CommonBlockDetails>()) {
+      for (const auto &member : commonDetails->objects())
+        addPrivateSlot(*member);
+    } else {
+      addPrivateSlot(*symbol);
+    }
+  }
+
+  llvm::DenseSet<const semantics::Symbol *> allocateSymbols;
+  for (const Clause &clause : clauses) {
+    if (clause.id != llvm::omp::Clause::OMPC_allocate)
+      continue;
+    const auto &allocate = std::get<clause::Allocate>(clause.u);
+    const auto &objects = std::get<ObjectList>(allocate.t);
+    for (const Object &object : objects) {
+      const semantics::Symbol *symbol = object.sym();
+      if (!symbol)
+        fir::emitFatalError(loc,
+                            "ALLOCATE clause item without a semantic symbol");
+      const semantics::Symbol *ultimate = &symbol->GetUltimate();
+      if (!allocateSymbols.insert(ultimate).second)
+        TODO(loc, "ALLOCATE clause item appears more than once");
+
+      auto privateSlot = privateSlots.find(ultimate);
+      if (privateSlot == privateSlots.end())
+        fir::emitFatalError(
+            loc, "ALLOCATE clause item without private storage slot");
+
+      auto type = evaluate::DynamicType::From(*ultimate);
+      bool supportedDataSharing =
+          symbol->test(semantics::Symbol::Flag::OmpPrivate) ||
+          symbol->test(semantics::Symbol::Flag::OmpFirstPrivate);
+      bool supportedType = ultimate->Rank() == 0 &&
+                           !semantics::IsAllocatableOrPointer(*ultimate) &&
+                           type &&
+                           type->category() != common::TypeCategory::Derived &&
+                           !type->RequiresDescriptor() &&
+                           !type->HasDeferredOrAssumedTypeParameter();
+      if (!supportedDataSharing || !supportedType)
+        TODO(loc,
+             "ALLOCATE clause currently supports only fixed-size intrinsic "
+             "scalar PRIVATE or FIRSTPRIVATE items");
+
+      clauseOps.allocatePrivateIndices.push_back(privateSlot->second);
+    }
+  }
+
+  if (clauseOps.allocatePrivateIndices.size() != clauseOps.allocateVars.size())
+    fir::emitFatalError(loc,
+                        "incomplete ALLOCATE clause private storage mapping");
+}
+
 static mlir::omp::ParallelOp
 genParallelOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
               semantics::SemanticsContext &semaCtx,
@@ -3463,74 +3540,7 @@ genParallelOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
   assert((!enableDelayedPrivatization || dsp) &&
          "expected valid DataSharingProcessor");
 
-  if (!clauseOps.allocateVars.empty()) {
-    llvm::DenseMap<const semantics::Symbol *, int64_t> privateSlots;
-    int64_t privateSlot = 0;
-    auto addPrivateSlot = [&](const semantics::Symbol &symbol) {
-      if (!privateSlots.try_emplace(&symbol.GetUltimate(), privateSlot).second)
-        fir::emitFatalError(
-            loc, "symbol with multiple private storage slots on one construct");
-      ++privateSlot;
-    };
-    for (const Object &object : args.priv.objects) {
-      const semantics::Symbol *symbol = object.sym();
-      if (!symbol)
-        fir::emitFatalError(loc, "private item without a semantic symbol");
-      // A privatized common block contributes one private operand per member,
-      // so slot numbering must follow the same expansion.
-      if (const auto *commonDetails =
-              symbol->detailsIf<semantics::CommonBlockDetails>()) {
-        for (const auto &member : commonDetails->objects())
-          addPrivateSlot(*member);
-      } else {
-        addPrivateSlot(*symbol);
-      }
-    }
-
-    llvm::DenseSet<const semantics::Symbol *> allocateSymbols;
-    for (const Clause &clause : item->clauses) {
-      if (clause.id != llvm::omp::Clause::OMPC_allocate)
-        continue;
-      const auto &allocate = std::get<clause::Allocate>(clause.u);
-      const auto &objects = std::get<ObjectList>(allocate.t);
-      for (const Object &object : objects) {
-        const semantics::Symbol *symbol = object.sym();
-        if (!symbol)
-          fir::emitFatalError(loc,
-                              "ALLOCATE clause item without a semantic symbol");
-        const semantics::Symbol *ultimate = &symbol->GetUltimate();
-        if (!allocateSymbols.insert(ultimate).second)
-          TODO(loc, "ALLOCATE clause item appears more than once");
-
-        auto privateSlot = privateSlots.find(ultimate);
-        if (privateSlot == privateSlots.end())
-          fir::emitFatalError(
-              loc, "ALLOCATE clause item without private storage slot");
-
-        auto type = evaluate::DynamicType::From(*ultimate);
-        bool supportedDataSharing =
-            symbol->test(semantics::Symbol::Flag::OmpPrivate) ||
-            symbol->test(semantics::Symbol::Flag::OmpFirstPrivate);
-        bool supportedType =
-            ultimate->Rank() == 0 &&
-            !semantics::IsAllocatableOrPointer(*ultimate) && type &&
-            type->category() != common::TypeCategory::Derived &&
-            !type->RequiresDescriptor() &&
-            !type->HasDeferredOrAssumedTypeParameter();
-        if (!supportedDataSharing || !supportedType)
-          TODO(loc,
-               "ALLOCATE clause currently supports only fixed-size intrinsic "
-               "scalar PRIVATE or FIRSTPRIVATE items");
-
-        clauseOps.allocatePrivateIndices.push_back(privateSlot->second);
-      }
-    }
-
-    if (clauseOps.allocatePrivateIndices.size() !=
-        clauseOps.allocateVars.size())
-      fir::emitFatalError(loc,
-                          "incomplete ALLOCATE clause private storage mapping");
-  }
+  mapAllocateClauseToPrivateSlots(loc, item->clauses, args, clauseOps);
 
   OpWithBodyGenInfo genInfo =
       OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,
@@ -3891,6 +3901,8 @@ genScopeOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
   args.reduction.objects = reductionObjects;
   args.reduction.vars = clauseOps.reductionVars;
 
+  mapAllocateClauseToPrivateSlots(loc, item->clauses, args, clauseOps);
+
   return genOpWithBody<mlir::omp::ScopeOp>(
       OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,
                         llvm::omp::Directive::OMPD_scope)
diff --git a/flang/test/Integration/OpenMP/allocate-clause-equivalence.f90 b/flang/test/Integration/OpenMP/allocate-clause-equivalence.f90
new file mode 100644
index 0000000000000..2ea7706f75905
--- /dev/null
+++ b/flang/test/Integration/OpenMP/allocate-clause-equivalence.f90
@@ -0,0 +1,47 @@
+!===----------------------------------------------------------------------===!
+! This directory can be used to add Integration tests involving multiple
+! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
+! contain executable tests. We should only add tests here sparingly and only
+! if there is no other way to test. Repeat this message in each test that is
+! added to this directory and sub-directories.
+!===----------------------------------------------------------------------===!
+
+! RUN: %flang_fc1 -fsyntax-only %openmp_flags -fopenmp-version=52 %s
+! RUN: %flang_fc1 -emit-hlfir %openmp_flags -fopenmp-version=52 -o - %s | FileCheck %s --check-prefix=HLFIR
+! RUN: %flang_fc1 -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s | FileCheck %s --check-prefix=LLVM
+
+subroutine scope_equivalence()
+  integer :: x, y
+  equivalence (x, y)
+  !$omp scope private(x, y) allocate(x, y)
+    x = 1
+    call consume(x)
+    y = 2
+    call consume(y)
+  !$omp end scope
+end subroutine
+
+! HLFIR-LABEL: func.func @_QPscope_equivalence
+! HLFIR: omp.scope allocate(
+! HLFIR-SAME: allocate_private_indices([0, 1])
+! LLVM-LABEL: define void @scope_equivalence_
+! LLVM: call ptr @__kmpc_alloc
+! LLVM: call ptr @__kmpc_alloc
+
+subroutine parallel_equivalence()
+  integer :: x, y
+  equivalence (x, y)
+  !$omp parallel private(x, y) allocate(x, y)
+    x = 1
+    call consume(x)
+    y = 2
+    call consume(y)
+  !$omp end parallel
+end subroutine
+
+! HLFIR-LABEL: func.func @_QPparallel_equivalence
+! HLFIR: omp.parallel allocate(
+! HLFIR-SAME: allocate_private_indices([0, 1])
+! LLVM-LABEL: define internal void @parallel_equivalence_..omp_par
+! LLVM: call ptr @__kmpc_alloc
+! LLVM: call ptr @__kmpc_alloc
diff --git a/flang/test/Integration/OpenMP/scope-allocate-clause.f90 b/flang/test/Integration/OpenMP/scope-allocate-clause.f90
new file mode 100644
index 0000000000000..50bd1ab288d8d
--- /dev/null
+++ b/flang/test/Integration/OpenMP/scope-allocate-clause.f90
@@ -0,0 +1,225 @@
+!===----------------------------------------------------------------------===!
+! This directory can be used to add Integration tests involving multiple
+! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
+! contain executable tests. We should only add tests here sparingly and only
+! if there is no other way to test. Repeat this message in each test that is
+! added to this directory and sub-directories.
+!===----------------------------------------------------------------------===!
+
+! This test checks lowering of the ALLOCATE clause on the OpenMP SCOPE
+! construct: PRIVATE with omitted/default allocator, PRIVATE with an explicit
+! allocator, FIRSTPRIVATE with allocator-backed storage, SAVE and individual
+! COMMON members, ALIGN, and an allocation-list order that differs from the
+! private-list order.
+
+! RUN: %flang_fc1 -emit-hlfir %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s --check-prefix=LLVM
+! RUN: not %flang_fc1 -fsyntax-only %openmp_flags -fopenmp-version=51 %s 2>&1 | FileCheck %s --check-prefix=VERSION51
+
+! VERSION51: error: {{.*}}ALLOCATE
+
+subroutine scope_allocator_omitted(x, y)
+  integer :: x, y
+  !$omp scope private(x, y) allocate(x)
+    x = 1
+    y = 2
+  !$omp end scope
+end subroutine
+
+! CHECK-LABEL: func.func @_QPscope_allocator_omitted
+! CHECK: %[[NULL_ALLOC:.*]] = arith.constant 0 : i32
+! CHECK: omp.scope allocate(%[[NULL_ALLOC]] : i32 -> %[[X:.*]]#0 : !fir.ref<i32>) allocate_private_indices([0])
+! CHECK-SAME: private({{.*}} %[[X]]#0 -> %[[X_PRIVATE:.*]], {{.*}} -> %[[Y_PRIVATE:.*]] : !fir.ref<i32>, !fir.ref<i32>) {
+
+subroutine scope_allocator_explicit(x, allocator)
+  use iso_c_binding, only : c_intptr_t
+  integer :: x
+  integer(c_intptr_t), intent(in) :: allocator
+  !$omp scope private(x) allocate(allocator(allocator): x)
+    x = 1
+  !$omp end scope
+end subroutine
+
+! CHECK-LABEL: func.func @_QPscope_allocator_explicit
+! CHECK: %[[ALLOCATOR:.*]] = fir.load %{{.*}} : !fir.ref<i64>
+! CHECK: omp.scope allocate(%[[ALLOCATOR]] : i64 -> %[[X:.*]]#0 : !fir.ref<i32>) allocate_private_indices([0])
+! CHECK-SAME: private({{.*}} %[[X]]#0 -> {{.*}} : !fir.ref<i32>) {
+
+subroutine scope_allocator_firstprivate(x)
+  integer :: x
+  !$omp scope firstprivate(x) allocate(x)
+    x = x + 1
+  !$omp end scope
+end subroutine
+
+! CHECK-LABEL: func.func @_QPscope_allocator_firstprivate
+! CHECK: omp.scope allocate({{.*}} : i32 -> %[[X:.*]]#0 : !fir.ref<i32>) allocate_private_indices([0])
+! CHECK-SAME: private({{.*}} %[[X]]#0 -> %[[X_PRIVATE:.*]] : !fir.ref<i32>) {
+! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X_PRIVATE]]
+! CHECK: hlfir.assign %{{.*}} to %[[X_DECL]]#0
+
+subroutine scope_allocator_align(x, y)
+  integer :: x, y
+  !$omp scope private(x, y) allocate(x) allocate(align(64): y)
+    x = 1
+    y = 2
+  !$omp end scope
+end subroutine
+
+! CHECK-LABEL: func.func @_QPscope_allocator_align
+! CHECK: omp.scope allocate(
+! CHECK-SAME: allocate_alignments([0, 64]) allocate_private_indices([0, 1])
+! CHECK-SAME: private(
+
+subroutine scope_allocator_order(x, y, z, allocator_xy, allocator_z)
+  use iso_c_binding, only : c_intptr_t
+  integer :: x, y, z
+  integer(c_intptr_t), intent(in) :: allocator_xy, allocator_z
+  !$omp scope private(x, y, z) &
+  !$omp& allocate(allocator(allocator_xy): y, x) &
+  !$omp& allocate(allocator(allocator_z): z)
+    x = 1
+    y = 2
+    z = 3
+  !$omp end scope
+end subroutine
+
+! CHECK-LABEL: func.func @_QPscope_allocator_order
+! CHECK: omp.scope allocate(
+! CHECK-SAME: allocate_private_indices([1, 0, 2])
+! CHECK-SAME: private(
+
+subroutine scope_allocator_dynamic(x, allocators, n)
+  use iso_c_binding, only : c_intptr_t
+  integer, intent(in) :: n
+  integer(c_intptr_t), intent(in) :: allocators(n)
+  integer :: x, i
+  do i = 1, n
+    !$omp scope firstprivate(x) allocate(allocator(allocators(i)): x)
+      x = x + i
+    !$omp end scope
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPscope_allocator_dynamic
+! CHECK: fir.do_loop
+! CHECK: %[[DYNAMIC_ALLOCATOR:.*]] = fir.load %{{.*}} : !fir.ref<i64>
+! CHECK: omp.scope allocate(%[[DYNAMIC_ALLOCATOR]] : i64 -> %[[X:.*]]#0 : !fir.ref<i32>) allocate_private_indices([0])
+! CHECK-SAME: private({{.*}} %[[X]]#0 -> {{.*}} : !fir.ref<i32>) {
+
+! LLVM-LABEL: define void @scope_allocator_dynamic_
+! LLVM-NOT: call ptr @__kmpc_{{(aligned_)?}}alloc
+! LLVM: omp.region.after_alloca:
+! LLVM-NOT: call ptr @__kmpc_{{(aligned_)?}}alloc
+! LLVM: br label %[[LOOP:[0-9]+]]
+! LLVM: [[LOOP]]:
+! LLVM-NOT: call ptr @__kmpc_{{(aligned_)?}}alloc
+! LLVM: br i1 {{.*}}, label %[[BODY:[0-9]+]], label %[[EXIT:[0-9]+]]
+! LLVM: [[BODY]]:
+! LLVM: %[[DYNAMIC_ALLOCATOR:.*]] = load i64
+! LLVM: %[[ALLOCATOR_HANDLE:.*]] = inttoptr i64 %[[DYNAMIC_ALLOCATOR]] to ptr
+! LLVM: %[[ALLOC:.*]] = call ptr @__kmpc_alloc({{.*}}, i64 4, ptr %[[ALLOCATOR_HANDLE]])
+! LLVM-NOT: call ptr @__kmpc_{{(aligned_)?}}alloc
+! LLVM-NOT: call void @__kmpc_free
+! LLVM: store i32 {{.*}}, ptr %[[ALLOC]]
+! LLVM-NOT: call ptr @__kmpc_{{(aligned_)?}}alloc
+! LLVM-NOT: call void @__kmpc_free
+! LLVM: call void @__kmpc_free({{.*}}, ptr %[[ALLOC]], ptr %[[ALLOCATOR_HANDLE]])
+! LLVM-NOT: call void @__kmpc_free
+! LLVM: br label %[[LOOP]]
+! LLVM: [[EXIT]]:
+! LLVM: ret void
+
+subroutine scope_allocator_private_save()
+  integer, save :: x
+  !$omp scope private(x) allocate(x)
+    x = 11
+    call consume(x)
+  !$omp end scope
+end subroutine
+
+! CHECK-LABEL: func.func @_QPscope_allocator_private_save
+! CHECK: omp.scope allocate({{.*}} -> %[[SAVE:.*]]#0 : !fir.ref<i32>) allocate_private_indices([0])
+! CHECK-SAME: private({{.*}} %[[SAVE]]#0 -> %[[SAVE_PRIVATE:.*]] : !fir.ref<i32>) {
+! CHECK: %[[SAVE_DECL:.*]]:2 = hlfir.declare %[[SAVE_PRIVATE]]
+! CHECK: hlfir.assign %c11_i32 to %[[SAVE_DECL]]#0
+! CHECK: fir.call @_QPconsume(%[[SAVE_DECL]]#0)
+
+! LLVM...
[truncated]

``````````

</details>


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


More information about the flang-commits mailing list