[flang-commits] [flang] [flang][OpenMP] Make object identity more precise (PR #94495)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Wed Jun 5 11:41:12 PDT 2024
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/94495
>From 828beb6622142c1d175a61a2664398fc91d36192 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Tue, 4 Jun 2024 15:57:38 -0500
Subject: [PATCH 1/2] [flang][OpenMP] Add `sym()` member function to
omp::Object
The object identity requires more than just `Symbol`. Don't use `id()`
to get the Symbol associated with the object, becase the return value
will need to change. Instead use `sym()` which is added for that reason.
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 16 ++++++++--------
flang/lib/Lower/OpenMP/ClauseProcessor.h | 8 ++++----
flang/lib/Lower/OpenMP/Clauses.h | 7 ++++++-
flang/lib/Lower/OpenMP/DataSharingProcessor.cpp | 2 +-
flang/lib/Lower/OpenMP/OpenMP.cpp | 2 +-
flang/lib/Lower/OpenMP/ReductionProcessor.cpp | 8 ++++----
flang/lib/Lower/OpenMP/Utils.cpp | 6 +++---
7 files changed, 27 insertions(+), 22 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 68619f699ebb2..d289f2fdfab26 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -175,7 +175,7 @@ static void addUseDeviceClause(
useDeviceLocs.push_back(operand.getLoc());
}
for (const omp::Object &object : objects)
- useDeviceSyms.push_back(object.id());
+ useDeviceSyms.push_back(object.sym());
}
static void convertLoopBounds(lower::AbstractConverter &converter,
@@ -525,7 +525,7 @@ bool ClauseProcessor::processCopyin() const {
bool hasCopyin = findRepeatableClause<omp::clause::Copyin>(
[&](const omp::clause::Copyin &clause, const parser::CharBlock &) {
for (const omp::Object &object : clause.v) {
- semantics::Symbol *sym = object.id();
+ semantics::Symbol *sym = object.sym();
assert(sym && "Expecting symbol");
if (const auto *commonDetails =
sym->detailsIf<semantics::CommonBlockDetails>()) {
@@ -698,7 +698,7 @@ bool ClauseProcessor::processCopyprivate(
bool hasCopyPrivate = findRepeatableClause<clause::Copyprivate>(
[&](const clause::Copyprivate &clause, const parser::CharBlock &) {
for (const Object &object : clause.v) {
- semantics::Symbol *sym = object.id();
+ semantics::Symbol *sym = object.sym();
if (const auto *commonDetails =
sym->detailsIf<semantics::CommonBlockDetails>()) {
for (const auto &mem : commonDetails->objects())
@@ -739,7 +739,7 @@ bool ClauseProcessor::processDepend(mlir::omp::DependClauseOps &result) const {
"array sections not supported for task depend");
}
- semantics::Symbol *sym = object.id();
+ semantics::Symbol *sym = object.sym();
const mlir::Value variable = converter.getSymbolAddress(*sym);
result.dependVars.push_back(variable);
}
@@ -870,11 +870,11 @@ bool ClauseProcessor::processMap(
lower::AddrAndBoundsInfo info =
lower::gatherDataOperandAddrAndBounds<mlir::omp::MapBoundsOp,
mlir::omp::MapBoundsType>(
- converter, firOpBuilder, semaCtx, stmtCtx, *object.id(),
+ converter, firOpBuilder, semaCtx, stmtCtx, *object.sym(),
object.ref(), clauseLocation, asFortran, bounds,
treatIndexAsSection);
- auto origSymbol = converter.getSymbolAddress(*object.id());
+ auto origSymbol = converter.getSymbolAddress(*object.sym());
mlir::Value symAddr = info.addr;
if (origSymbol && fir::isTypeWithDescriptor(origSymbol.getType()))
symAddr = origSymbol;
@@ -894,12 +894,12 @@ bool ClauseProcessor::processMap(
mapTypeBits),
mlir::omp::VariableCaptureKind::ByRef, symAddr.getType());
- if (object.id()->owner().IsDerivedType()) {
+ if (object.sym()->owner().IsDerivedType()) {
addChildIndexAndMapToParent(object, parentMemberIndices, mapOp,
semaCtx);
} else {
result.mapVars.push_back(mapOp);
- ptrMapSyms->push_back(object.id());
+ ptrMapSyms->push_back(object.sym());
if (mapSymTypes)
mapSymTypes->push_back(symAddr.getType());
if (mapSymLocs)
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h
index 4d3d4448e8f03..28f26697c1f50 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.h
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h
@@ -205,11 +205,11 @@ bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
lower::AddrAndBoundsInfo info =
lower::gatherDataOperandAddrAndBounds<mlir::omp::MapBoundsOp,
mlir::omp::MapBoundsType>(
- converter, firOpBuilder, semaCtx, stmtCtx, *object.id(),
+ converter, firOpBuilder, semaCtx, stmtCtx, *object.sym(),
object.ref(), clauseLocation, asFortran, bounds,
treatIndexAsSection);
- auto origSymbol = converter.getSymbolAddress(*object.id());
+ auto origSymbol = converter.getSymbolAddress(*object.sym());
mlir::Value symAddr = info.addr;
if (origSymbol && fir::isTypeWithDescriptor(origSymbol.getType()))
symAddr = origSymbol;
@@ -226,12 +226,12 @@ bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
mapTypeBits),
mlir::omp::VariableCaptureKind::ByRef, symAddr.getType());
- if (object.id()->owner().IsDerivedType()) {
+ if (object.sym()->owner().IsDerivedType()) {
addChildIndexAndMapToParent(object, parentMemberIndices, mapOp,
semaCtx);
} else {
result.mapVars.push_back(mapOp);
- mapSymbols.push_back(object.id());
+ mapSymbols.push_back(object.sym());
}
}
});
diff --git a/flang/lib/Lower/OpenMP/Clauses.h b/flang/lib/Lower/OpenMP/Clauses.h
index 5391b134e979d..f7cd0ea83ad12 100644
--- a/flang/lib/Lower/OpenMP/Clauses.h
+++ b/flang/lib/Lower/OpenMP/Clauses.h
@@ -21,6 +21,10 @@
#include <type_traits>
#include <utility>
+namespace Fortran::semantics {
+class Symbol;
+}
+
namespace Fortran::lower::omp {
using namespace Fortran;
using SomeExpr = semantics::SomeExpr;
@@ -45,7 +49,8 @@ struct ObjectT<Fortran::lower::omp::IdTy, Fortran::lower::omp::ExprTy> {
using IdTy = Fortran::lower::omp::IdTy;
using ExprTy = Fortran::lower::omp::ExprTy;
- const IdTy &id() const { return symbol; }
+ IdTy id() const { return symbol; }
+ Fortran::semantics::Symbol *sym() const { return symbol; }
const std::optional<ExprTy> &ref() const { return designator; }
IdTy symbol;
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 557a9685024c5..b206040c237c5 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -139,7 +139,7 @@ void DataSharingProcessor::collectOmpObjectListSymbol(
const omp::ObjectList &objects,
llvm::SetVector<const semantics::Symbol *> &symbolSet) {
for (const omp::Object &object : objects)
- symbolSet.insert(object.id());
+ symbolSet.insert(object.sym());
}
void DataSharingProcessor::collectSymbolsForPrivatization() {
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index af9e2af24619b..f84440d95ec11 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1434,7 +1434,7 @@ genSectionsOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
mlir::OpBuilder::InsertPoint insp = builder.saveInsertionPoint();
const auto &objList = std::get<ObjectList>(lastp->t);
for (const Object &object : objList) {
- semantics::Symbol *sym = object.id();
+ semantics::Symbol *sym = object.sym();
converter.copyHostAssociateVar(*sym, &insp);
}
}
diff --git a/flang/lib/Lower/OpenMP/ReductionProcessor.cpp b/flang/lib/Lower/OpenMP/ReductionProcessor.cpp
index 1a63e316ef068..60e933f5bc1f7 100644
--- a/flang/lib/Lower/OpenMP/ReductionProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ReductionProcessor.cpp
@@ -37,7 +37,7 @@ namespace omp {
ReductionProcessor::ReductionIdentifier ReductionProcessor::getReductionType(
const omp::clause::ProcedureDesignator &pd) {
auto redType = llvm::StringSwitch<std::optional<ReductionIdentifier>>(
- getRealName(pd.v.id()).ToString())
+ getRealName(pd.v.sym()).ToString())
.Case("max", ReductionIdentifier::MAX)
.Case("min", ReductionIdentifier::MIN)
.Case("iand", ReductionIdentifier::IAND)
@@ -72,7 +72,7 @@ ReductionProcessor::ReductionIdentifier ReductionProcessor::getReductionType(
bool ReductionProcessor::supportedIntrinsicProcReduction(
const omp::clause::ProcedureDesignator &pd) {
- semantics::Symbol *sym = pd.v.id();
+ semantics::Symbol *sym = pd.v.sym();
if (!sym->GetUltimate().attrs().test(semantics::Attr::INTRINSIC))
return false;
auto redType = llvm::StringSwitch<bool>(getRealName(sym).ToString())
@@ -707,7 +707,7 @@ void ReductionProcessor::addDeclareReduction(
// should happen byref
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
for (const Object &object : objectList) {
- const semantics::Symbol *symbol = object.id();
+ const semantics::Symbol *symbol = object.sym();
if (reductionSymbols)
reductionSymbols->push_back(symbol);
mlir::Value symVal = converter.getSymbolAddress(*symbol);
@@ -825,7 +825,7 @@ ReductionProcessor::getRealName(const semantics::Symbol *symbol) {
const semantics::SourceName
ReductionProcessor::getRealName(const omp::clause::ProcedureDesignator &pd) {
- return getRealName(pd.v.id());
+ return getRealName(pd.v.sym());
}
int ReductionProcessor::getOperationIdentity(ReductionIdentifier redId,
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index 4d665e6dd34c5..eff915f569f27 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -55,7 +55,7 @@ void genObjectList(const ObjectList &objects,
lower::AbstractConverter &converter,
llvm::SmallVectorImpl<mlir::Value> &operands) {
for (const Object &object : objects) {
- const semantics::Symbol *sym = object.id();
+ const semantics::Symbol *sym = object.sym();
assert(sym && "Expected Symbol");
if (mlir::Value variable = converter.getSymbolAddress(*sym)) {
operands.push_back(variable);
@@ -107,7 +107,7 @@ void gatherFuncAndVarSyms(
const ObjectList &objects, mlir::omp::DeclareTargetCaptureClause clause,
llvm::SmallVectorImpl<DeclareTargetCapturePair> &symbolAndClause) {
for (const Object &object : objects)
- symbolAndClause.emplace_back(clause, *object.id());
+ symbolAndClause.emplace_back(clause, *object.sym());
}
mlir::omp::MapInfoOp
@@ -175,7 +175,7 @@ generateMemberPlacementIndices(const Object &object,
semantics::SemanticsContext &semaCtx) {
auto compObj = getComponentObject(object, semaCtx);
while (compObj) {
- indices.push_back(getComponentPlacementInParent(compObj->id()));
+ indices.push_back(getComponentPlacementInParent(compObj->sym()));
compObj =
getComponentObject(getBaseObject(compObj.value(), semaCtx), semaCtx);
}
>From 1c266fa056044e2baa6cd4262fdf9a4cd74fd14d Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 5 Jun 2024 10:09:09 -0500
Subject: [PATCH 2/2] [flang][OpenMP] Make object identity more precise
Derived type components may use a given `Symbol` regardless of what
parent objects they are a part of. Because of that, simply using a
symbol address is not sufficient to determine object identity.
Make the designator a part of the IdTy. To compare identities, when
symbols are equal (and non-null), compare the designators.
---
flang/lib/Lower/OpenMP/Clauses.h | 50 ++++++++++++++++---
flang/lib/Lower/OpenMP/Utils.cpp | 2 +-
flang/test/Lower/OpenMP/map-component-ref.f90 | 41 +++++++++++----
3 files changed, 75 insertions(+), 18 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/Clauses.h b/flang/lib/Lower/OpenMP/Clauses.h
index f7cd0ea83ad12..98fb5dcf7722e 100644
--- a/flang/lib/Lower/OpenMP/Clauses.h
+++ b/flang/lib/Lower/OpenMP/Clauses.h
@@ -36,30 +36,64 @@ struct TypeTy : public evaluate::SomeType {
bool operator==(const TypeTy &t) const { return true; }
};
-using IdTy = semantics::Symbol *;
+template <typename ExprTy>
+struct IdTyTemplate {
+ // "symbol" is always non-null for id's of actual objects.
+ Fortran::semantics::Symbol *symbol;
+ std::optional<ExprTy> designator;
+
+ bool operator==(const IdTyTemplate &other) const {
+ // If symbols are different, then the objects are different.
+ if (symbol != other.symbol)
+ return false;
+ if (symbol == nullptr)
+ return true;
+ // Equal symbols don't necessarily indicate identical objects,
+ // for example, a derived object component may use a single symbol,
+ // which will refer to different objects for different designators,
+ // e.g. a%c and b%c.
+ return designator == other.designator;
+ }
+
+ operator bool() const { return symbol != nullptr; }
+};
+
using ExprTy = SomeExpr;
template <typename T>
using List = tomp::ListT<T>;
} // namespace Fortran::lower::omp
+// Specialization of the ObjectT template
namespace tomp::type {
template <>
-struct ObjectT<Fortran::lower::omp::IdTy, Fortran::lower::omp::ExprTy> {
- using IdTy = Fortran::lower::omp::IdTy;
+struct ObjectT<Fortran::lower::omp::IdTyTemplate<Fortran::lower::omp::ExprTy>,
+ Fortran::lower::omp::ExprTy> {
+ using IdTy = Fortran::lower::omp::IdTyTemplate<Fortran::lower::omp::ExprTy>;
using ExprTy = Fortran::lower::omp::ExprTy;
- IdTy id() const { return symbol; }
- Fortran::semantics::Symbol *sym() const { return symbol; }
- const std::optional<ExprTy> &ref() const { return designator; }
+ IdTy id() const { return identity; }
+ Fortran::semantics::Symbol *sym() const { return identity.symbol; }
+ const std::optional<ExprTy> &ref() const { return identity.designator; }
- IdTy symbol;
- std::optional<ExprTy> designator;
+ IdTy identity;
};
} // namespace tomp::type
namespace Fortran::lower::omp {
+using IdTy = IdTyTemplate<ExprTy>;
+}
+namespace std {
+template <>
+struct hash<Fortran::lower::omp::IdTy> {
+ size_t operator()(const Fortran::lower::omp::IdTy &id) const {
+ return static_cast<size_t>(reinterpret_cast<uintptr_t>(id.symbol));
+ }
+};
+} // namespace std
+
+namespace Fortran::lower::omp {
using Object = tomp::ObjectT<IdTy, ExprTy>;
using ObjectList = tomp::ObjectListT<IdTy, ExprTy>;
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index eff915f569f27..da94352a84a7c 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -188,7 +188,7 @@ void addChildIndexAndMapToParent(
std::map<const semantics::Symbol *,
llvm::SmallVector<OmpMapMemberIndicesData>> &parentMemberIndices,
mlir::omp::MapInfoOp &mapOp, semantics::SemanticsContext &semaCtx) {
- std::optional<evaluate::DataRef> dataRef = ExtractDataRef(object.designator);
+ std::optional<evaluate::DataRef> dataRef = ExtractDataRef(object.ref());
assert(dataRef.has_value() &&
"DataRef could not be extracted during mapping of derived type "
"cannot proceed");
diff --git a/flang/test/Lower/OpenMP/map-component-ref.f90 b/flang/test/Lower/OpenMP/map-component-ref.f90
index 2c582667f38d3..21b56ab303acd 100644
--- a/flang/test/Lower/OpenMP/map-component-ref.f90
+++ b/flang/test/Lower/OpenMP/map-component-ref.f90
@@ -1,21 +1,22 @@
! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
! RUN: bbc -fopenmp -emit-hlfir %s -o - | FileCheck %s
-! CHECK: %[[V0:[0-9]+]] = fir.alloca !fir.type<_QFfooTt0{a0:i32,a1:i32}> {bindc_name = "a", uniq_name = "_QFfooEa"}
-! CHECK: %[[V1:[0-9]+]]:2 = hlfir.declare %[[V0]] {uniq_name = "_QFfooEa"} : (!fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>) -> (!fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>, !fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>)
-! CHECK: %[[V2:[0-9]+]] = hlfir.designate %[[V1]]#0{"a1"} : (!fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>) -> !fir.ref<i32>
+! CHECK-LABEL: func.func @_QPfoo1
+! CHECK: %[[V0:[0-9]+]] = fir.alloca !fir.type<_QFfoo1Tt0{a0:i32,a1:i32}> {bindc_name = "a", uniq_name = "_QFfoo1Ea"}
+! CHECK: %[[V1:[0-9]+]]:2 = hlfir.declare %[[V0]] {uniq_name = "_QFfoo1Ea"} : (!fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>) -> (!fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>, !fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>)
+! CHECK: %[[V2:[0-9]+]] = hlfir.designate %[[V1]]#0{"a1"} : (!fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>) -> !fir.ref<i32>
! CHECK: %[[V3:[0-9]+]] = omp.map.info var_ptr(%[[V2]] : !fir.ref<i32>, i32) map_clauses(tofrom) capture(ByRef) -> !fir.ref<i32> {name = "a%a1"}
-! CHECK: %[[V4:[0-9]+]] = omp.map.info var_ptr(%[[V1]]#1 : !fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>, !fir.type<_QFfooTt0{a0:i32,a1:i32}>) map_clauses(tofrom) capture(ByRef) members(%[[V3]] : [1] : !fir.ref<i32>) -> !fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>> {name = "a", partial_map = true}
-! CHECK: omp.target map_entries(%[[V3]] -> %arg0, %[[V4]] -> %arg1 : !fir.ref<i32>, !fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>) {
-! CHECK: ^bb0(%arg0: !fir.ref<i32>, %arg1: !fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>):
-! CHECK: %[[V5:[0-9]+]]:2 = hlfir.declare %arg1 {uniq_name = "_QFfooEa"} : (!fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>) -> (!fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>, !fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>)
+! CHECK: %[[V4:[0-9]+]] = omp.map.info var_ptr(%[[V1]]#1 : !fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>, !fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>) map_clauses(tofrom) capture(ByRef) members(%[[V3]] : [1] : !fir.ref<i32>) -> !fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>> {name = "a", partial_map = true}
+! CHECK: omp.target map_entries(%[[V3]] -> %arg0, %[[V4]] -> %arg1 : !fir.ref<i32>, !fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>) {
+! CHECK: ^bb0(%arg0: !fir.ref<i32>, %arg1: !fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>):
+! CHECK: %[[V5:[0-9]+]]:2 = hlfir.declare %arg1 {uniq_name = "_QFfoo1Ea"} : (!fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>) -> (!fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>, !fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>)
! CHECK: %c0_i32 = arith.constant 0 : i32
-! CHECK: %[[V6:[0-9]+]] = hlfir.designate %[[V5]]#0{"a1"} : (!fir.ref<!fir.type<_QFfooTt0{a0:i32,a1:i32}>>) -> !fir.ref<i32>
+! CHECK: %[[V6:[0-9]+]] = hlfir.designate %[[V5]]#0{"a1"} : (!fir.ref<!fir.type<_QFfoo1Tt0{a0:i32,a1:i32}>>) -> !fir.ref<i32>
! CHECK: hlfir.assign %c0_i32 to %[[V6]] : i32, !fir.ref<i32>
! CHECK: omp.terminator
! CHECK: }
-subroutine foo()
+subroutine foo1()
implicit none
type t0
@@ -29,3 +30,25 @@ subroutine foo()
!$omp end target
end
+
+! CHECK-LABEL: func.func @_QPfoo2
+! CHECK-DAG: omp.map.info var_ptr(%{{[0-9]+}} : {{.*}} map_clauses(to) capture(ByRef) bounds(%{{[0-9]+}}) -> {{.*}} {name = "t%b(1_8)%a(1)"}
+! CHECK-DAG: omp.map.info var_ptr(%{{[0-9]+}} : {{.*}} map_clauses(from) capture(ByRef) bounds(%{{[0-9]+}}) -> {{.*}} {name = "u%b(1_8)%a(1)"}
+subroutine foo2()
+ implicit none
+
+ type t0
+ integer :: a(10)
+ end type
+
+ type t1
+ type(t0) :: b(10)
+ end type
+
+ type(t1) :: t, u
+
+!$omp target map(to: t%b(1)%a(1)) map(from: u%b(1)%a(1))
+ t%b(1)%a(1) = u%b(1)%a(1)
+!$omp end target
+
+end
More information about the flang-commits
mailing list