[MemProf] Templatize CallStackRadixTreeBuilder (NFC) (#117014)
[llvm-project.git] / flang / docs / OpenMP-descriptor-management.md
blob66c153914f70dad165dee44b33a74cbc24494f66
1 <!--===- docs/OpenMP-descriptor-management.md
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
7 -->
9 # OpenMP dialect: Fortran descriptor type mapping for offload
11 The initial method for mapping Fortran types tied to descriptors for OpenMP offloading is to treat these types 
12 as a special case of OpenMP record type (C/C++ structure/class, Fortran derived type etc.) mapping as far as the 
13 runtime is concerned. Where the box (descriptor information) is the holding container and the underlying 
14 data pointer is contained within the container, and we must generate explicit maps for both the pointer member and
15 the container. As an example, a small C++ program that is equivalent to the concept described, with the 
16 `mock_descriptor` class being representative of the class utilised for descriptors in Clang:
18 ```C++
19 struct mock_descriptor {
20   long int x;
21   std::byte x1, x2, x3, x4;
22   void *pointer;
23   long int lx[1][3];
26 int main() {
27 mock_descriptor data;
28 #pragma omp target map(tofrom: data, data.pointer[:upper_bound])
30     do something... 
33  return 0;
35 ```
37 In the above, we have to map both the containing structure, with its non-pointer members and the
38 data pointed to by the pointer contained within the structure to appropriately access the data. This 
39 is effectively what is done with descriptor types for the time being. Other pointers that are part 
40 of the descriptor container such as the addendum should also be treated as the data pointer is 
41 treated.
43 Currently, Flang will lower these descriptor types in the OpenMP lowering (lower/OpenMP.cpp) similarly
44 to all other map types, generating an omp.MapInfoOp containing relevant information required for lowering
45 the OpenMP dialect to LLVM-IR during the final stages of the MLIR lowering. However, after 
46 the lowering to FIR/HLFIR has been performed an OpenMP dialect specific pass for Fortran, 
47 `MapInfoFinalizationPass` (Optimizer/OpenMP/MapInfoFinalization.cpp) will expand the 
48 `omp.MapInfoOp`'s containing descriptors (which currently will be a `BoxType` or `BoxAddrOp`) into multiple 
49 mappings, with one extra per pointer member in the descriptor that is supported on top of the original
50 descriptor map operation. These pointers members are linked to the parent descriptor by adding them to 
51 the member field of the original descriptor map operation, they are then inserted into the relevant map
52 owning operation's (`omp.TargetOp`, `omp.TargetDataOp` etc.) map operand list and in cases where the owning
53 operation is `IsolatedFromAbove`, it also inserts them as `BlockArgs` to canonicalize the mappings and
54 simplify lowering.
56 An example transformation by the `MapInfoFinalizationPass`:
58 ```
60 ...
61 %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"}
62 ...
63 omp.target map_entries(%12 -> %arg1, %13 -> %arg2 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.ref<i32>) {
64     ^bb0(%arg1: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, %arg2: !fir.ref<i32>):
65 ...
67 ====>
69 ...
70 %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>>>
71 %13 = omp.map.info var_ptr(%1#1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.array<?xi32>) var_ptr_ptr(%12 : !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>) map_clauses(tofrom) capture(ByRef) bounds(%11) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>> {name = ""}
72 %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"}
73 ...
74 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>) {
75     ^bb0(%arg1: !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>, %arg2: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, %arg3: !fir.ref<i32>):
76 ...
78 ```
80 In later stages of the compilation flow when the OpenMP dialect is being lowered to LLVM-IR these descriptor
81 mappings are treated as if they were structure mappings with explicit member maps on the same directive as 
82 their parent was mapped. 
84 This implementation utilises the member field of the `map_info` operation to indicate that the pointer 
85 descriptor elements which are contained in their own `map_info` operation are part of their respective 
86 parent descriptor. This allows the descriptor containing the descriptor pointer member to be mapped
87 as a composite entity during lowering, with the correct mappings being generated to tie them together,
88 allowing the OpenMP runtime to map them correctly, attaching the pointer member to the parent
89 structure so it can be accessed during execution. If we opt to not treat the descriptor as a single 
90 entity we have issues with the member being correctly attached to the parent and being accessible,
91 this can cause runtime segfaults on the device when we try to access the data through the parent. It
92 may be possible to avoid this member mapping, treating them as individual entities, but treating a 
93 composite mapping as an individual mapping could lead to problems such as the runtime taking 
94 liberties with the mapping it usually wouldn't if it knew they were linked, we would also have to 
95 be careful to maintian the correct order of mappings as we lower, if we misorder the maps, it'd be
96 possible to overwrite already written data, e.g. if we write the descriptor data pointer first, and
97 then the containing descriptor, we would overwrite the descriptor data pointer with the incorrect 
98 address.
100 This method is generic in the sense that the OpenMP dialect doesn't need to understand that it is mapping a 
101 Fortran type containing a descriptor, it just thinks it's a record type from either Fortran or C++. However,
102 it is a little rigid in how the descriptor mappings are handled as there is no specialisation or possibility
103 to specialise the mappings for possible edge cases without polluting the dialect or lowering with further
104 knowledge of Fortran and the FIR dialect.
106 # OpenMP dialect differences from OpenACC dialect
108 The descriptor mapping for OpenMP currently works differently to the planned direction for OpenACC, however, 
109 it is possible and would likely be ideal to align the method with OpenACC in the future. 
111 Currently the OpenMP specification is less descriptive and has less stringent rules around descriptor based
112 types so does not require as complex a set of descriptor management rules as OpenACC (although, in certain 
113 cases for the interim adopting OpenACC's rules where it makes sense could be useful). To handle the more 
114 complex descriptor mapping rules OpenACC has opted to utilise a more runtime oriented approach, where 
115 specialized runtime functions for handling descriptor mapping for OpenACC are created and these runtime 
116 function handles are attatched to a special OpenACC dialect operation. When this operation is lowered it 
117 will lower to the attatched OpenACC descriptor mapping runtime function. This sounds like it will work 
118 (no implementation yet) similarly to some of the existing HLFIR operations which optionally lower to 
119 Fortran runtime calls. 
121 This methodology described by OpenACC which utilises runtime functions to handle specialised mappings allows
122 more flexibility as a significant amount of the mapping logic can be moved into the runtime from the compiler.
123 It also allows specialisation of the mapping for fortran specific types. This may be a desireable approach
124 to take for OpenMP in the future, in particular if we find need to specialise mapping further for 
125 descriptors or other Fortran types. However, for the moment the currently chosen implementation for OpenMP
126 appears sufficient as far as the OpenMP specification and current testing can show.