[flang-commits] [flang] [Flang][OpenMP] Avoid aborting on nested derived types in DO CONCURRENT device conversion (PR #218963)
Arth Srivastava via flang-commits
flang-commits at lists.llvm.org
Thu Sep 3 13:46:02 PDT 2026
https://github.com/Blazearth updated https://github.com/llvm/llvm-project/pull/218963
>From 559c14d73804dedd48e480114ab170b8ba6d70a1 Mon Sep 17 00:00:00 2001
From: Arth Srivastava <arthsrivastava1 at gmail.com>
Date: Wed, 26 Aug 2026 21:03:00 +0530
Subject: [PATCH 1/2] [Flang][OpenMP] Support DO CONCURRENT device conversion
for nested derived types
In DoConcurrentConversion, avoid aborting when a live-in derived type
contains nested derived-type components. Use fir::isRecordWithAllocatableMember
to determine if implicit default declare mappers are required, and safely
handle absent mangler callbacks during recursive mapper generation.
Fixes #218760
---
.../OpenMP/DoConcurrentConversion.cpp | 25 ++-----
.../nested_derived_type_device.f90 | 69 +++++++++++++++++++
2 files changed, 73 insertions(+), 21 deletions(-)
create mode 100644 flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90
diff --git a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
index 4dc6aa4750eaa..091dfb44a1b07 100644
--- a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
+++ b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
@@ -596,34 +596,17 @@ class DoConcurrentConversion
llvm::SmallVector<mlir::Value> boundsOps;
genBoundsOps(builder, liveIn, rawAddr, boundsOps);
- auto asRecordType = [&](mlir::Type eleType) {
- return mlir::dyn_cast<fir::RecordType>(
- fir::getDerivedType(fir::unwrapRefType(eleType)));
- };
-
- fir::RecordType recordType = asRecordType(eleType);
-
- bool requiresImplcitMapper = [&]() {
- if (!recordType)
- return false;
-
- for (auto [fieldName, fieldType] : recordType.getTypeList()) {
- if (fir::isAllocatableType(fieldType))
- return true;
+ fir::RecordType recordType = mlir::dyn_cast<fir::RecordType>(
+ fir::getDerivedType(fir::unwrapRefType(eleType)));
- if (asRecordType(fieldType))
- TODO(liveIn.getLoc(), "Nested record types are not supported yet.");
- }
-
- return false;
- }();
+ bool requiresImplcitMapper =
+ recordType && fir::isRecordWithAllocatableMember(recordType);
mlir::FlatSymbolRefAttr mapperId;
if (requiresImplcitMapper) {
std::string mapperIdName =
Fortran::utils::openmp::getCanonicalDefaultDeclareMapperName(
recordType);
- // TODO Add a mangler callback once nested record types are supported.
mapperId = Fortran::utils::openmp::getOrGenImplicitDefaultDeclareMapper(
builder, liveIn.getLoc(), recordType, mapperIdName);
}
diff --git a/flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90 b/flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90
new file mode 100644
index 0000000000000..d53d8de7a6304
--- /dev/null
+++ b/flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90
@@ -0,0 +1,69 @@
+! Regression test for https://github.com/llvm/llvm-project/issues/218760
+! The DO CONCURRENT -> OpenMP device conversion used to abort on arrays
+! whose element type contains a nested derived-type component. Verify that
+! a nested derived type with no allocatable members does not require an
+! implicit mapper and converts successfully, while a nested derived type
+! with allocatable members properly generates implicit mappers.
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fdo-concurrent-to-openmp=device %s -o - \
+! RUN: | FileCheck %s
+
+module nested_alloc_mod
+ implicit none
+ type :: inner_alloc_t
+ real, allocatable :: values(:)
+ end type
+
+ type :: outer_alloc_t
+ type(inner_alloc_t) :: inner
+ end type
+end module nested_alloc_mod
+
+! CHECK-DAG: omp.declare_mapper @[[INNER_MAPPER:.*inner_alloc_t.*]] : !fir.type<{{.*}}inner_alloc_t{{.*}}>
+! CHECK-DAG: omp.declare_mapper @[[OUTER_MAPPER:.*outer_alloc_t.*]] : !fir.type<{{.*}}outer_alloc_t{{.*}}>
+
+subroutine nested_derived()
+ implicit none
+
+ type :: inner_t
+ integer :: x
+ end type
+
+ type :: outer_t
+ type(inner_t) :: member
+ end type
+
+ type(outer_t) :: a(4)
+ integer :: i
+
+ do concurrent (i = 1:4)
+ a(i)%member%x = i
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @{{.*}}nested_derived()
+! CHECK: %[[ARR_A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "{{.*}}a"}
+! CHECK: omp.map.info var_ptr(%[[ARR_A]]#1 : {{.*}}) map_clauses(implicit, tofrom) capture(ByRef) {{.*}} name("{{.*}}a")
+! CHECK: omp.target
+! CHECK: omp.teams
+! CHECK: omp.parallel
+! CHECK: omp.distribute
+! CHECK: omp.wsloop
+! CHECK: omp.loop_nest
+
+subroutine nested_derived_alloc()
+ use nested_alloc_mod
+ implicit none
+
+ type(outer_alloc_t) :: a(4)
+ integer :: i
+
+ do concurrent (i = 1:4)
+ a(1)%inner%values = [1.0, 2.0]
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @{{.*}}nested_derived_alloc()
+! CHECK: %[[ARR_ALLOC:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "{{.*}}a"}
+! CHECK: omp.map.info var_ptr(%[[ARR_ALLOC]]#1 : {{.*}}) map_clauses(implicit, tofrom) capture(ByRef) mapper(@[[OUTER_MAPPER]]) {{.*}} name("{{.*}}a")
+
>From e6c3751336cf1239aa76c2c9112f0eba5e05035e Mon Sep 17 00:00:00 2001
From: Arth Srivastava <arthsrivastava1 at gmail.com>
Date: Fri, 4 Sep 2026 02:15:44 +0530
Subject: [PATCH 2/2] [Flang][OpenMP] Support nested derived type array
components in DO CONCURRENT
In DoConcurrentConversion, inspect Fortran element types when checking for
allocatable members in derived types, so that records containing array
components of derived types with allocatable members properly generate
implicit declare mappers.
Update nested_derived_type_device.f90 to verify the array component case
and mapper chaining.
---
.../OpenMP/DoConcurrentConversion.cpp | 23 ++++++++++++++++---
.../nested_derived_type_device.f90 | 14 ++++++-----
2 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
index 091dfb44a1b07..ee677a5d2c7f4 100644
--- a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
+++ b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
@@ -548,6 +548,23 @@ class DoConcurrentConversion
/*dataExvIsAssumedSize=*/false, rawAddr.getLoc());
}
+ static bool recordHasAllocatableMember(fir::RecordType recordType) {
+ for (auto [fieldName, fieldType] : recordType.getTypeList()) {
+ if (fir::isPointerType(fir::unwrapRefType(fieldType)))
+ continue;
+
+ if (fir::isAllocatableType(fieldType))
+ return true;
+
+ if (auto nestedRec = mlir::dyn_cast<fir::RecordType>(
+ fir::getFortranElementType(fieldType)))
+ if (recordHasAllocatableMember(nestedRec))
+ return true;
+ }
+
+ return false;
+ }
+
mlir::omp::MapInfoOp
genMapInfoOpForLiveIn(fir::FirOpBuilder &builder, mlir::Value liveIn,
bool isReductionVar = false) const {
@@ -599,11 +616,11 @@ class DoConcurrentConversion
fir::RecordType recordType = mlir::dyn_cast<fir::RecordType>(
fir::getDerivedType(fir::unwrapRefType(eleType)));
- bool requiresImplcitMapper =
- recordType && fir::isRecordWithAllocatableMember(recordType);
+ bool requiresImplicitMapper =
+ recordType && recordHasAllocatableMember(recordType);
mlir::FlatSymbolRefAttr mapperId;
- if (requiresImplcitMapper) {
+ if (requiresImplicitMapper) {
std::string mapperIdName =
Fortran::utils::openmp::getCanonicalDefaultDeclareMapperName(
recordType);
diff --git a/flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90 b/flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90
index d53d8de7a6304..c8f96b3c11acd 100644
--- a/flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90
+++ b/flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90
@@ -3,7 +3,8 @@
! whose element type contains a nested derived-type component. Verify that
! a nested derived type with no allocatable members does not require an
! implicit mapper and converts successfully, while a nested derived type
-! with allocatable members properly generates implicit mappers.
+! with allocatable members properly generates implicit mappers (including
+! when the nested component is an array of records).
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fdo-concurrent-to-openmp=device %s -o - \
! RUN: | FileCheck %s
@@ -15,12 +16,13 @@ module nested_alloc_mod
end type
type :: outer_alloc_t
- type(inner_alloc_t) :: inner
+ type(inner_alloc_t) :: inner(2)
end type
end module nested_alloc_mod
-! CHECK-DAG: omp.declare_mapper @[[INNER_MAPPER:.*inner_alloc_t.*]] : !fir.type<{{.*}}inner_alloc_t{{.*}}>
-! CHECK-DAG: omp.declare_mapper @[[OUTER_MAPPER:.*outer_alloc_t.*]] : !fir.type<{{.*}}outer_alloc_t{{.*}}>
+! CHECK: omp.declare_mapper @[[INNER_MAPPER:.*inner_alloc_t.*]] : !fir.type<{{.*}}inner_alloc_t{{.*}}>
+! CHECK: omp.declare_mapper @[[OUTER_MAPPER:.*outer_alloc_t.*]] : !fir.type<{{.*}}outer_alloc_t{{.*}}> {
+! CHECK: omp.map.info {{.*}} mapper(@[[INNER_MAPPER]])
subroutine nested_derived()
implicit none
@@ -43,6 +45,7 @@ subroutine nested_derived()
! CHECK-LABEL: func.func @{{.*}}nested_derived()
! CHECK: %[[ARR_A:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "{{.*}}a"}
+! CHECK-NOT: mapper(
! CHECK: omp.map.info var_ptr(%[[ARR_A]]#1 : {{.*}}) map_clauses(implicit, tofrom) capture(ByRef) {{.*}} name("{{.*}}a")
! CHECK: omp.target
! CHECK: omp.teams
@@ -59,11 +62,10 @@ subroutine nested_derived_alloc()
integer :: i
do concurrent (i = 1:4)
- a(1)%inner%values = [1.0, 2.0]
+ a(1)%inner(1)%values = [1.0, 2.0]
end do
end subroutine
! CHECK-LABEL: func.func @{{.*}}nested_derived_alloc()
! CHECK: %[[ARR_ALLOC:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "{{.*}}a"}
! CHECK: omp.map.info var_ptr(%[[ARR_ALLOC]]#1 : {{.*}}) map_clauses(implicit, tofrom) capture(ByRef) mapper(@[[OUTER_MAPPER]]) {{.*}} name("{{.*}}a")
-
More information about the flang-commits
mailing list