Skip to content

Commit fb46edc

Browse files
committed
[Documentation][NFC] Add document describing descriptor mapping in a little bit of detail, and remove some unneccesary comments from new pass
1 parent f917a25 commit fb46edc

File tree

2 files changed

+95
-4
lines changed

2 files changed

+95
-4
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<!--===- docs/OpenMP-descriptor-management.md
2+
3+
Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
See https://llvm.org/LICENSE.txt for license information.
5+
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
7+
-->
8+
9+
# OpenMP dialect: Fortran descriptor type mapping for offload
10+
11+
The descriptor mapping for OpenMP currently works differently to the planned direction for OpenACC, however,
12+
it is possible and would likely be ideal to align the method with OpenACC in the future. However, at least
13+
currently the OpenMP specification is less descriptive and has less stringent rules around descriptor based
14+
types so does not require as complex a set of descriptor management rules (although, in certain cases
15+
for the interim adopting OpenACC's rules where it makes sense could be useful).
16+
17+
The initial method for mapping Fortran types tied to descriptors for OpenMP offloading is to treat these types
18+
as a special case of OpenMP record type (C/C++ structure/class, Fortran derived type etc.) mapping as far as the
19+
runtime is concerned. Where the box (descriptor information) is the holding container and the underlying
20+
data pointer is contained within the container, and we must generate explicit maps for both the pointer member and
21+
the container. As an example, a small C++ program that is equivalent to the concept described:
22+
23+
```C++
24+
struct mock_descriptor {
25+
long int x;
26+
std::byte x1, x2, x3, x4;
27+
void *pointer;
28+
long int lx[1][3];
29+
};
30+
31+
int main() {
32+
mock_descriptor data;
33+
#pragma omp target map(tofrom: data, data.pointer[:upper_bound])
34+
{
35+
do something...
36+
}
37+
38+
return 0;
39+
}
40+
```
41+
42+
In the above, we have to map both the containing structure, with its non-pointer members and the
43+
data pointed to by the pointer contained within the structure to appropriately access the data. This
44+
is effectively what is done with descriptor types for the time being. Other pointers that are part
45+
of the descriptor container such as the addendum should also be treated as the data pointer is
46+
treated.
47+
48+
Currently, Flang will lower these descriptor types in the OpenMP lowering (lower/OpenMP.cpp) similarly
49+
to all other map types, generating an omp.MapInfoOp containing relevant information required for lowering
50+
the OpenMP dialect to LLVM-IR during the final stages of the MLIR lowering. However, after
51+
the lowering to FIR/HLFIR has been performed an OpenMP dialect specific pass for Fortran,
52+
OMPDescriptorMapInfoGenPass (Optimizer/OMPDescriptorMapInfoGen.cpp) will expand the
53+
omp.MapInfoOp's containing descriptors (which currently will be a BoxType or BoxAddrOp) into multiple
54+
mappings, with one extra per pointer member in the descriptor that is supported on top of the original
55+
descriptor map operation. These pointers members are linked to the parent descriptor by adding them to
56+
the member field of the original descriptor map operation, they are then inserted into the relevant map
57+
owning operation's (omp.TargetOp, omp.DataOp etc.) map operand list and in cases where the owning operation
58+
is IsolatedFromAbove, it also inserts them as BlockArgs to canonicalize the mappings and simplify lowering.
59+
60+
An example transformation by the OMPDescriptorMapInfoGenPass:
61+
62+
```
63+
64+
...
65+
%12 = omp.map_info var_ptr(%1#1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.box<!fir.ptr<!fir.array<?xi32>>>) map_clauses(tofrom) capture(ByRef) bounds(%11) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>> {name = "arg_alloc"}
66+
...
67+
omp.target map_entries(%12 -> %arg1, %13 -> %arg2 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.ref<i32>) {
68+
^bb0(%arg1: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, %arg2: !fir.ref<i32>):
69+
...
70+
71+
====>
72+
73+
...
74+
%12 = fir.box_offset %1#1 base_addr : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>
75+
%13 = omp.map_info var_ptr(%12 : !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>, !fir.array<?xi32>) map_clauses(tofrom) capture(ByRef) bounds(%11) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>> {name = ""}
76+
%14 = omp.map_info var_ptr(%1#1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.box<!fir.ptr<!fir.array<?xi32>>>) map_clauses(tofrom) capture(ByRef) members(%13 : !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>> {name = "arg_alloc"}
77+
...
78+
omp.target map_entries(%13 -> %arg1, %14 -> %arg2, %15 -> %arg3 : !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.ref<i32>) {
79+
^bb0(%arg1: !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>, %arg2: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, %arg3: !fir.ref<i32>):
80+
...
81+
82+
```
83+
84+
In later stages of the compilation flow when the OpenMP dialect is being lowered to LLVM-IR these descriptor
85+
mappings are treated as if they were structure mappings with explicit member maps on the same directive as
86+
their parent was mapped.
87+
88+
This method is generic in the sense that the OpenMP diaelct doesn't need to understand that it is mapping a
89+
Fortran type containing a descriptor, it just thinks it's a record type from either Fortran or C++. However,
90+
it is a little rigid in how the descriptor mappings are handled as there is no specialisation or possibility
91+
to specialise the mappings for possible edge cases without poluting the dialect or lowering with further
92+
knowledge of Fortran and the FIR dialect. In the case that this kind of specialisation is required or
93+
desired then the methodology described by OpenACC which utilises runtime functions to handle specialised mappings
94+
for dialects may be a more desirable approach to move towards. For the moment this method appears sufficient as
95+
far as the OpenMP specification and current testing can show.

flang/lib/Optimizer/Transforms/OMPDescriptorMapInfoGen.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ class OMPDescriptorMapInfoGenPass
8080
op.getMembersMutable().append(descriptorBaseAddrMembers);
8181
op.getBoundsMutable().assign(llvm::SmallVector<mlir::Value>{});
8282

83-
// could use a template to generalise to other TargetOps
8483
if (auto mapClauseOwner =
8584
llvm::dyn_cast<mlir::omp::MapClauseOwningOpInterface>(target)) {
8685
llvm::SmallVector<mlir::Value> newMapOps;
@@ -117,9 +116,6 @@ class OMPDescriptorMapInfoGenPass
117116
fir::KindMapping kindMap = fir::getKindMapping(getOperation());
118117
fir::FirOpBuilder builder{getOperation(), std::move(kindMap)};
119118

120-
// 2) look into adding some documentation on this.
121-
// 3) fix tests.
122-
123119
getOperation()->walk([&](mlir::omp::MapInfoOp op) {
124120
if (fir::isTypeWithDescriptor(op.getVarType()) ||
125121
mlir::isa_and_present<fir::BoxAddrOp>(

0 commit comments

Comments
 (0)