[flang-commits] [flang] 1e592bb - [Flang][OpenMP] Support nested derived types in DO CONCURRENT device conversion (#218963)
via flang-commits
flang-commits at lists.llvm.org
Fri Sep 18 10:55:26 PDT 2026
Author: Arth Srivastava
Date: 2026-09-18T19:55:21+02:00
New Revision: 1e592bb44245a9ae44115fe78be1b07251dc3d9e
URL: https://github.com/llvm/llvm-project/commit/1e592bb44245a9ae44115fe78be1b07251dc3d9e
DIFF: https://github.com/llvm/llvm-project/commit/1e592bb44245a9ae44115fe78be1b07251dc3d9e.diff
LOG: [Flang][OpenMP] Support nested derived types in DO CONCURRENT device conversion (#218963)
Fixes #218760.
When converting `DO CONCURRENT` loops to OpenMP device targets, Flang
previously aborted whenever a live-in derived type contained a nested
derived-type component.
Nested derived types containing only non-allocatable components do not
require special handling: mapping the containing record covers the
storage of its nested components. When a nested component contains an
allocatable member, however, an implicit mapper is required to properly
map the dynamically allocated storage.
This patch removes the unconditional nested-record NYI and updates the
implicit mapper check to recurse through the Fortran element type of
derived-type components. In particular, this handles array-valued nested
derived-type components, which are lowered as `fir::SequenceType`.
Tests are added in
`flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90` to
verify plain record mapping, nested implicit mapper generation, and
chained mappers for array components. An AMDGPU offload test is also
added in
`offload/test/offloading/fortran/do-concurrent-to-omp-nested-derived-type.f90`
to verify device kernel execution and the computed results.
Reusing visible user-defined default declare mappers for derived-type
live-ins remains a separate pre-existing limitation in
`DoConcurrentConversion` and is not addressed by this patch.
---------
Co-authored-by: agozillon <Andrew.Gozillon at amd.com>
Added:
flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90
offload/test/offloading/fortran/do-concurrent-to-omp-nested-derived-type.f90
Modified:
flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
Removed:
################################################################################
diff --git a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
index 4dc6aa4750eaa..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 {
@@ -596,34 +613,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 requiresImplicitMapper =
+ recordType && recordHasAllocatableMember(recordType);
mlir::FlatSymbolRefAttr mapperId;
- if (requiresImplcitMapper) {
+ if (requiresImplicitMapper) {
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..3924e156e12c4
--- /dev/null
+++ b/flang/test/Transforms/DoConcurrent/nested_derived_type_device.f90
@@ -0,0 +1,72 @@
+! 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 (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
+
+module nested_alloc_mod
+ implicit none
+ type :: inner_alloc_t
+ real, allocatable :: values(:)
+ end type
+
+ type :: outer_alloc_t
+ type(inner_alloc_t) :: inner(2)
+ end type
+end module nested_alloc_mod
+
+! 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
+
+ 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)
+! CHECK-NOT: mapper(
+! CHECK-SAME: 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(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")
diff --git a/offload/test/offloading/fortran/do-concurrent-to-omp-nested-derived-type.f90 b/offload/test/offloading/fortran/do-concurrent-to-omp-nested-derived-type.f90
new file mode 100644
index 0000000000000..e8e91fd7ef07e
--- /dev/null
+++ b/offload/test/offloading/fortran/do-concurrent-to-omp-nested-derived-type.f90
@@ -0,0 +1,59 @@
+! Verifies that `do concurrent` correctly lowers map for basic nested derived
+! types, correctly mapping the required components of the derived type.
+! REQUIRES: flang, amdgpu
+
+! RUN: %libomptarget-compile-fortran-generic -fdo-concurrent-to-openmp=device
+! RUN: env LIBOMPTARGET_INFO=16 %libomptarget-run-generic 2>&1 | %fcheck-generic
+program main
+ implicit none
+
+ type :: alloc_buffer
+ integer :: i
+ real, allocatable :: data(:)
+ end type alloc_buffer
+
+ type :: buffer
+ integer :: i
+ real :: data(8)
+ end type buffer
+
+ type :: array_dt
+ type(buffer) :: buf
+ end type array_dt
+
+ type :: alloc_array_dt
+ type(alloc_buffer) :: buf(2)
+ end type alloc_array_dt
+
+ integer, parameter :: n = 8
+ integer :: i
+ type(alloc_array_dt) :: aad
+ type(array_dt) :: ad
+
+ allocate(aad%buf(1)%data(n), source=0.0)
+ allocate(aad%buf(2)%data(n), source=0.0)
+
+ do concurrent(i=1:n)
+ aad%buf(1)%data(i) = real(i)
+ aad%buf(2)%data(i) = real(i * 2)
+ end do
+
+ do concurrent(i=1:n)
+ ad%buf%data(i) = real(i)
+ end do
+
+ if (sum(ad%buf%data) == 36.0 .and. &
+ sum(aad%buf(1)%data) == 36.0 .and. &
+ sum(aad%buf(2)%data) == 72.0) then
+ print *, "PASS"
+ else
+ print *, "FAIL"
+ end if
+
+ deallocate(aad%buf(1)%data)
+ deallocate(aad%buf(2)%data)
+end program main
+
+! CHECK: PluginInterface device {{[0-9]+}} info: Launching kernel {{.*}}
+! CHECK: PluginInterface device {{[0-9]+}} info: Launching kernel {{.*}}
+! CHECK: PASS
More information about the flang-commits
mailing list