|
| 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. |
0 commit comments