================
@@ -609,208 +880,210 @@ class MapInfoFinalizationPass
assert(mapMemberUsers.size() == 1 &&
"OMPMapInfoFinalization currently only supports single users of a
"
"MapInfoOp");
- auto baseAddr = genBaseAddrMap(descriptor, op.getBounds(),
- op.getMapType(), builder, mapperId);
ParentAndPlacement mapUser = mapMemberUsers[0];
- adjustMemberIndices(memberIndices, mapUser.index);
+ adjustMemberIndices(memberIndices, mapUser);
llvm::SmallVector<mlir::Value> newMemberOps;
for (auto v : mapUser.parent.getMembers()) {
newMemberOps.push_back(v);
- if (v == op)
+ if (v == parentOp)
newMemberOps.push_back(baseAddr);
}
mapUser.parent.getMembersMutable().assign(newMemberOps);
mapUser.parent.setMembersIndexAttr(
builder.create2DI64ArrayAttr(memberIndices));
- } else if (!isHasDeviceAddrFlag) {
- auto baseAddr = genBaseAddrMap(descriptor, op.getBounds(),
- op.getMapType(), builder, mapperId);
+ } else {
newMembers.push_back(baseAddr);
- if (!op.getMembers().empty()) {
+ if (!parentOp.getMembers().empty()) {
for (auto &indices : memberIndices)
indices.insert(indices.begin(), 0);
memberIndices.insert(memberIndices.begin(), {0});
newMembersAttr = builder.create2DI64ArrayAttr(memberIndices);
- newMembers.append(op.getMembers().begin(), op.getMembers().end());
+ newMembers.append(parentOp.getMembers().begin(),
+ parentOp.getMembers().end());
} else {
llvm::SmallVector<llvm::SmallVector<int64_t>> memberIdx = {{0}};
newMembersAttr = builder.create2DI64ArrayAttr(memberIdx);
}
}
-
- // Descriptors for objects listed on the `has_device_addr` will always
- // be copied. This is because the descriptor can be rematerialized by the
- // compiler, and so the address of the descriptor for a given object at
- // one place in the code may differ from that address in another place.
- // The contents of the descriptor (the base address in particular) will
- // remain unchanged though.
- mlir::omp::ClauseMapFlags mapType = op.getMapType();
- if (isHasDeviceAddrFlag) {
- mapType |= mlir::omp::ClauseMapFlags::always;
- }
-
- mlir::omp::MapInfoOp newDescParentMapOp = mlir::omp::MapInfoOp::create(
- builder, op->getLoc(), op.getResult().getType(), descriptor,
- mlir::TypeAttr::get(fir::unwrapRefType(descriptor.getType())),
- builder.getAttr<mlir::omp::ClauseMapFlagsAttr>(
- getDescriptorMapType(mapType, target)),
- op.getMapCaptureTypeAttr(), /*varPtrPtr=*/mlir::Value{},
- /*varPtrPtrType=*/mlir::TypeAttr{}, newMembers, newMembersAttr,
- /*bounds=*/mlir::SmallVector<mlir::Value>{},
- /*mapperId=*/mlir::FlatSymbolRefAttr(), op.getNameAttr(),
- /*partial_map=*/builder.getBoolAttr(false));
- op.replaceAllUsesWith(newDescParentMapOp.getResult());
- op->erase();
-
- if (descCanBeDeferred)
- deferrableDesc.push_back(newDescParentMapOp);
-
- return newDescParentMapOp;
}
- // We add all mapped record members not directly used in the target region
- // to the block arguments in front of their parent and we place them into
- // the map operands list for consistency.
+ // This function handles the spliting of allocatable/pointer maps in
+ // Fortran into descriptor, pointer and attach map components, as
+ // well as the handling of ref_ptr, ref_ptee, ref_ptr_ptee and attach
+ // modifier semantics.
//
- // These indirect uses (via accesses to their parent) will still be
- // mapped individually in most cases, and a parent mapping doesn't
- // guarantee the parent will be mapped in its totality, partial
- // mapping is common.
+ // For ref_ptr, we generate a map of the descriptor with user specified
+ // map types and, in the default auto attach case, we generate an
+ // additional attach map which indicates to the runtime to try and attach
+ // the base address to the descriptor if it's available and it's the first
+ // time the ref_ptr has been allocated on the device.
//
- // For example:
- // map(tofrom: x%y)
+ // For ref_ptee, we generate a map of the base address with user specified
+ // map types and, in the default auto attach case, we generate an
+ // additional attach map which indicates to the runtime to try and attach
+ // the base address to the descriptor if it's available and it's the first
+ // time the ref_ptee has been allocated on the device.
//
- // Will generate a mapping for "x" (the parent) and "y" (the member).
- // The parent "x" will not be mapped, but the member "y" will.
- // However, we must have the parent as a BlockArg and MapOperand
- // in these cases, to maintain the correct uses within the region and
- // to help tracking that the member is part of a larger object.
- //
- // In the case of:
- // map(tofrom: x%y, x%z)
- //
- // The parent member becomes more critical, as we perform a partial
- // structure mapping where we link the mapping of the members y
- // and z together via the parent x. We do this at a kernel argument
- // level in LLVM IR and not just MLIR, which is important to maintain
- // similarity to Clang and for the runtime to do the correct thing.
- // However, we still do not map the structure in its totality but
- // rather we generate an un-sized "binding" map entry for it.
- //
- // In the case of:
- // map(tofrom: x, x%y, x%z)
- //
- // We do actually map the entirety of "x", so the explicit mapping of
- // x%y, x%z becomes unnecessary. It is redundant to write this from a
- // Fortran OpenMP perspective (although it is legal), as even if the
- // members were allocatables or pointers, we are mandated by the
- // specification to map these (and any recursive components) in their
- // entirety, which is different to the C++ equivalent, which requires
- // explicit mapping of these segments.
- void addImplicitMembersToTarget(mlir::omp::MapInfoOp op,
- fir::FirOpBuilder &builder,
- mlir::Operation *target) {
- auto mapClauseOwner =
- llvm::dyn_cast_if_present<mlir::omp::MapClauseOwningOpInterface>(
- target);
- // TargetDataOp is technically a MapClauseOwningOpInterface, so we
- // do not need to explicitly check for the extra cases here for use_device
- // addr/ptr
- if (!mapClauseOwner)
- return;
+ // For ref_ptr_ptee, it is the standard descriptor mapping that combines both
+ // of the above, a map is generated for the descriptor and its base address,
+ // similarly in the default auto attach case, we generate an additional
attach
+ // map.
+ void genDescriptorMaps(mlir::omp::MapInfoOp op, fir::FirOpBuilder &builder,
+ mlir::Operation *target) {
+ bool descCanBeDeferred = false;
+ llvm::SmallVector<ParentAndPlacement> mapMemberUsers;
+ getMemberUserList(op, mapMemberUsers);
- auto addOperands = [&](mlir::MutableOperandRange &mutableOpRange,
- mlir::Operation *directiveOp,
- unsigned blockArgInsertIndex = 0) {
- if (!llvm::is_contained(mutableOpRange.getAsOperandRange(),
- op.getResult()))
- return;
+ // TODO: map the addendum segment of the descriptor, similarly to the
+ // base address/data pointer member.
+ bool isHasDeviceAddrFlag = isHasDeviceAddr(op, *target);
+ bool isUseDeviceFlag =
+ isUseDeviceAddr(op, *target) || isUseDevicePtr(op, *target);
+ bool isAttachNever = bitEnumContainsAll(
+ op.getMapType(), mlir::omp::ClauseMapFlags::attach_never);
+ bool isAttachAlways = bitEnumContainsAll(
+ op.getMapType(), mlir::omp::ClauseMapFlags::attach_always);
+ bool isRefPtr = bitEnumContainsAll(op.getMapType(),
+ mlir::omp::ClauseMapFlags::ref_ptr) &&
+ !bitEnumContainsAll(op.getMapType(),
+ mlir::omp::ClauseMapFlags::ref_ptee);
+ bool isRefPtee = bitEnumContainsAll(op.getMapType(),
+ mlir::omp::ClauseMapFlags::ref_ptee) &&
+ !bitEnumContainsAll(op.getMapType(),
+ mlir::omp::ClauseMapFlags::ref_ptr);
- // There doesn't appear to be a simple way to convert MutableOperandRange
- // to a vector currently, so we instead use a for_each to populate our
- // vector.
- llvm::SmallVector<mlir::Value> newMapOps;
- newMapOps.reserve(mutableOpRange.size());
- llvm::for_each(
- mutableOpRange.getAsOperandRange(),
- [&newMapOps](mlir::Value oper) { newMapOps.push_back(oper); });
+ mlir::Value descriptor =
+ getDescriptorFromBoxMap(op, builder, descCanBeDeferred);
+ mlir::FlatSymbolRefAttr mapperId = op.getMapperIdAttr();
- for (auto mapMember : op.getMembers()) {
- if (llvm::is_contained(mutableOpRange.getAsOperandRange(), mapMember))
- continue;
- newMapOps.push_back(mapMember);
- if (directiveOp) {
- directiveOp->getRegion(0).insertArgument(
- blockArgInsertIndex, mapMember.getType(), mapMember.getLoc());
- blockArgInsertIndex++;
- }
+ // If we're a derived type descriptor, that's been flagged as ref_ptr,
+ // but, in the same mapping, we also have members with their own
+ // descriptors also mapped as ref_ptr, then we have to map the parent
+ // derived type descriptors data. This is because the member's ref_ptrs
+ // are parts of the parent and must be mapped and attached as a contiguous
+ // storage block. Relevant for mappings like:
+ //
+ // !$omp target enter data map(ref_ptr, to: obj, obj%arr,
+ // obj%dtype_nest2%arr3, obj%dtype_nest2%scalar_ptr)
+ //
+ // In which a user has basically told us to map the descriptor of obj, but
+ // also bits of its ref_ptee data with its descriptor members.
+ //
+ // TODO: This currently only works for the first level of a
+ // derived-type descriptor chain and will likely need to be extended for
the
+ // case where we do a similar style of mapping for deeper nestings.
+ if (isRefPtr && op.getMembers().empty()) {
+ auto newMapInfoOp = mlir::omp::MapInfoOp::create(
+ builder, op->getLoc(), op.getResult().getType(), descriptor,
+ mlir::TypeAttr::get(fir::unwrapRefType(descriptor.getType())),
+ builder.getAttr<mlir::omp::ClauseMapFlagsAttr>(op.getMapType()),
+ op.getMapCaptureTypeAttr(), /*varPtrPtr=*/op.getVarPtrPtr(),
+ /*varPtrPtrType=*/op.getVarPtrPtrTypeAttr(), op.getMembers(),
+ op.getMembersIndexAttr(),
+ /*bounds=*/mlir::SmallVector<mlir::Value>{},
+ /*mapperId*/ mlir::FlatSymbolRefAttr(), op.getNameAttr(),
+ /*partial_map=*/builder.getBoolAttr(false));
+
+ // If we're a map exiting construct we skip the generation of the
+ // attach map. It should be unnecessary in these cases as it exists to
+ // bind the pointer and pointee and shouldn't increment or decrement
+ // the ref counter on its own. However, equally having it doesn't
+ // cause issues either, it's just ideal to remove the noise where
+ // feasible.
+ // TODO: Extend this to perhaps check for target updates and target data
+ // with release and from applied.
+ if (!llvm::isa<mlir::omp::TargetExitDataOp>(target) && !isAttachNever)
+ genImplicitAttachMap(op, descriptor, mapMemberUsers, target, builder,
+ mlir::omp::ClauseMapFlags::ref_ptr,
+ isAttachAlways);
+ op.replaceAllUsesWith(newMapInfoOp.getResult());
+ op->erase();
+ } else if (isRefPtee) {
+ // NOTE: We replace the descriptor map with the base address map. This
+ // effectively replaces the descriptor's index position in any complex
+ // structure mapping. This is a little different to the
+ // ref_ptr_ptee/default map case, where we effectively insert a new
member
+ // with its own index position and have to nudge all children down an
+ // index. This should be fine but it's worth noting the oddity in case
+ // issues do pop up.
+ auto newMapInfoOp = genBaseAddrMap(descriptor, op, op.getMapType(),
+ builder, /*IsRefPtee=*/true,
mapperId);
+
+ // If we're a map exiting construct, we skip the generation of the attach
+ // map. It should be unnecessary in these cases as it exists to bind the
+ // pointer and pointee and shouldn't increment or decrement the ref
+ // counter on its own. However, equally having it doesn't cause issues
+ // either, it's just ideal to remove the noise where feasible.
+ // TODO: Extend this to perhaps check for target updates and target data
+ // with release and from applied.
+ if (!llvm::isa<mlir::omp::TargetExitDataOp>(target) && !isAttachNever)
+ genImplicitAttachMap(op, descriptor, mapMemberUsers, target, builder,
+ mlir::omp::ClauseMapFlags::ref_ptee,
+ isAttachAlways, newMapInfoOp.getVarPtrPtr());
+ op.replaceAllUsesWith(newMapInfoOp.getResult());
+ op->erase();
+ } else {
+ bool isRefPtrPtee =
+ (op.getMapType() & mlir::omp::ClauseMapFlags::ref_ptr) ==
+ mlir::omp::ClauseMapFlags::ref_ptr &&
+ (op.getMapType() & mlir::omp::ClauseMapFlags::ref_ptee) ==
+ mlir::omp::ClauseMapFlags::ref_ptee;
----------------
agozillon wrote:
This similarly to the previous statement won't work as expected in this
particular case with the bit shift combination, so I've opted to leave out the
merge into a singular bit check but I've swapped to the function, thanks for
the catch!
https://github.com/llvm/llvm-project/pull/177715
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits