[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