| 1 | //===- OMP.cpp ------ Collection of helpers for OpenMP --------------------===// |
| 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 | #include "llvm/Frontend/OpenMP/OMP.h" |
| 10 | |
| 11 | #include "llvm/ADT/ArrayRef.h" |
| 12 | #include "llvm/ADT/SmallVector.h" |
| 13 | #include "llvm/ADT/StringRef.h" |
| 14 | #include "llvm/Demangle/Demangle.h" |
| 15 | #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" |
| 16 | #include "llvm/Support/ErrorHandling.h" |
| 17 | |
| 18 | #include <algorithm> |
| 19 | #include <cstdio> |
| 20 | #include <iterator> |
| 21 | #include <string> |
| 22 | #include <type_traits> |
| 23 | |
| 24 | using namespace llvm; |
| 25 | using namespace llvm::omp; |
| 26 | |
| 27 | #define GEN_DIRECTIVES_IMPL |
| 28 | #include "llvm/Frontend/OpenMP/OMP.inc" |
| 29 | |
| 30 | static iterator_range<ArrayRef<Directive>::iterator> |
| 31 | getFirstCompositeRange(iterator_range<ArrayRef<Directive>::iterator> Leafs) { |
| 32 | // OpenMP Spec 5.2: [17.3, 8-9] |
| 33 | // If directive-name-A and directive-name-B both correspond to loop- |
| 34 | // associated constructs then directive-name is a composite construct |
| 35 | // otherwise directive-name is a combined construct. |
| 36 | // |
| 37 | // In the list of leaf constructs, find the first loop-associated construct, |
| 38 | // this is the beginning of the returned range. Then, starting from the |
| 39 | // immediately following leaf construct, find the first sequence of adjacent |
| 40 | // loop-associated constructs. The last of those is the last one of the |
| 41 | // range, that is, the end of the range is one past that element. |
| 42 | // If such a sequence of adjacent loop-associated directives does not exist, |
| 43 | // return an empty range. |
| 44 | // |
| 45 | // The end of the returned range (including empty range) is intended to be |
| 46 | // a point from which the search for the next range could resume. |
| 47 | // |
| 48 | // Consequently, this function can't return a range with a single leaf |
| 49 | // construct in it. |
| 50 | |
| 51 | auto firstLoopAssociated = |
| 52 | [](iterator_range<ArrayRef<Directive>::iterator> List) { |
| 53 | for (auto It = List.begin(), End = List.end(); It != End; ++It) { |
| 54 | if (getDirectiveAssociation(Dir: *It) == Association::Loop) |
| 55 | return It; |
| 56 | } |
| 57 | return List.end(); |
| 58 | }; |
| 59 | |
| 60 | auto Empty = llvm::make_range(x: Leafs.end(), y: Leafs.end()); |
| 61 | |
| 62 | auto Begin = firstLoopAssociated(Leafs); |
| 63 | if (Begin == Leafs.end()) |
| 64 | return Empty; |
| 65 | |
| 66 | auto End = |
| 67 | firstLoopAssociated(llvm::make_range(x: std::next(x: Begin), y: Leafs.end())); |
| 68 | if (End == Leafs.end()) |
| 69 | return Empty; |
| 70 | |
| 71 | for (; End != Leafs.end(); ++End) { |
| 72 | if (getDirectiveAssociation(Dir: *End) != Association::Loop) |
| 73 | break; |
| 74 | } |
| 75 | return llvm::make_range(x: Begin, y: End); |
| 76 | } |
| 77 | |
| 78 | namespace llvm::omp { |
| 79 | ArrayRef<Directive> getLeafConstructs(Directive D) { |
| 80 | auto Idx = static_cast<std::size_t>(D); |
| 81 | if (Idx >= Directive_enumSize) |
| 82 | return {}; |
| 83 | const auto *Row = LeafConstructTable[LeafConstructTableOrdering[Idx]]; |
| 84 | return ArrayRef(&Row[2], static_cast<int>(Row[1])); |
| 85 | } |
| 86 | |
| 87 | ArrayRef<Directive> getLeafConstructsOrSelf(Directive D) { |
| 88 | if (auto Leafs = getLeafConstructs(D); !Leafs.empty()) |
| 89 | return Leafs; |
| 90 | auto Idx = static_cast<size_t>(D); |
| 91 | assert(Idx < Directive_enumSize && "Invalid directive" ); |
| 92 | const auto *Row = LeafConstructTable[LeafConstructTableOrdering[Idx]]; |
| 93 | // The first entry in the row is the directive itself. |
| 94 | return ArrayRef(&Row[0], &Row[0] + 1); |
| 95 | } |
| 96 | |
| 97 | ArrayRef<Directive> |
| 98 | getLeafOrCompositeConstructs(Directive D, SmallVectorImpl<Directive> &Output) { |
| 99 | using ArrayTy = ArrayRef<Directive>; |
| 100 | using IteratorTy = ArrayTy::iterator; |
| 101 | ArrayRef<Directive> Leafs = getLeafConstructsOrSelf(D); |
| 102 | |
| 103 | IteratorTy Iter = Leafs.begin(); |
| 104 | do { |
| 105 | auto Range = getFirstCompositeRange(Leafs: llvm::make_range(x: Iter, y: Leafs.end())); |
| 106 | // All directives before the range are leaf constructs. |
| 107 | for (; Iter != Range.begin(); ++Iter) |
| 108 | Output.push_back(Elt: *Iter); |
| 109 | if (!Range.empty()) { |
| 110 | Directive Comp = |
| 111 | getCompoundConstruct(Parts: ArrayTy(Range.begin(), Range.end())); |
| 112 | assert(Comp != OMPD_unknown); |
| 113 | Output.push_back(Elt: Comp); |
| 114 | Iter = Range.end(); |
| 115 | // As of now, a composite construct must contain all constituent leaf |
| 116 | // constructs from some point until the end of all constituent leaf |
| 117 | // constructs. |
| 118 | assert(Iter == Leafs.end() && "Malformed directive" ); |
| 119 | } |
| 120 | } while (Iter != Leafs.end()); |
| 121 | |
| 122 | return Output; |
| 123 | } |
| 124 | |
| 125 | Directive getCompoundConstruct(ArrayRef<Directive> Parts) { |
| 126 | if (Parts.empty()) |
| 127 | return OMPD_unknown; |
| 128 | |
| 129 | // Parts don't have to be leafs, so expand them into leafs first. |
| 130 | // Store the expanded leafs in the same format as rows in the leaf |
| 131 | // table (generated by tablegen). |
| 132 | SmallVector<Directive> RawLeafs(2); |
| 133 | for (Directive P : Parts) { |
| 134 | ArrayRef<Directive> Ls = getLeafConstructs(D: P); |
| 135 | if (!Ls.empty()) |
| 136 | RawLeafs.append(in_start: Ls.begin(), in_end: Ls.end()); |
| 137 | else |
| 138 | RawLeafs.push_back(Elt: P); |
| 139 | } |
| 140 | |
| 141 | // RawLeafs will be used as key in the binary search. The search doesn't |
| 142 | // guarantee that the exact same entry will be found (since RawLeafs may |
| 143 | // not correspond to any compound directive). Because of that, we will |
| 144 | // need to compare the search result with the given set of leafs. |
| 145 | // Also, if there is only one leaf in the list, it corresponds to itself, |
| 146 | // no search is necessary. |
| 147 | auto GivenLeafs{ArrayRef<Directive>(RawLeafs).drop_front(N: 2)}; |
| 148 | if (GivenLeafs.size() == 1) |
| 149 | return GivenLeafs.front(); |
| 150 | RawLeafs[1] = static_cast<Directive>(GivenLeafs.size()); |
| 151 | |
| 152 | auto Iter = std::lower_bound( |
| 153 | first: LeafConstructTable, last: LeafConstructTableEndDirective, |
| 154 | val: static_cast<std::decay_t<decltype(*LeafConstructTable)>>(RawLeafs.data()), |
| 155 | comp: [](const llvm::omp::Directive *RowA, const llvm::omp::Directive *RowB) { |
| 156 | const auto *BeginA = &RowA[2]; |
| 157 | const auto *EndA = BeginA + static_cast<int>(RowA[1]); |
| 158 | const auto *BeginB = &RowB[2]; |
| 159 | const auto *EndB = BeginB + static_cast<int>(RowB[1]); |
| 160 | if (BeginA == EndA && BeginB == EndB) |
| 161 | return static_cast<int>(RowA[0]) < static_cast<int>(RowB[0]); |
| 162 | return std::lexicographical_compare(first1: BeginA, last1: EndA, first2: BeginB, last2: EndB); |
| 163 | }); |
| 164 | |
| 165 | if (Iter == std::end(arr: LeafConstructTable)) |
| 166 | return OMPD_unknown; |
| 167 | |
| 168 | // Verify that we got a match. |
| 169 | Directive Found = (*Iter)[0]; |
| 170 | ArrayRef<Directive> FoundLeafs = getLeafConstructs(D: Found); |
| 171 | if (FoundLeafs == GivenLeafs) |
| 172 | return Found; |
| 173 | return OMPD_unknown; |
| 174 | } |
| 175 | |
| 176 | bool isLeafConstruct(Directive D) { return getLeafConstructs(D).empty(); } |
| 177 | |
| 178 | bool isCompositeConstruct(Directive D) { |
| 179 | ArrayRef<Directive> Leafs = getLeafConstructsOrSelf(D); |
| 180 | if (Leafs.size() <= 1) |
| 181 | return false; |
| 182 | auto Range = getFirstCompositeRange(Leafs); |
| 183 | return Range.begin() == Leafs.begin() && Range.end() == Leafs.end(); |
| 184 | } |
| 185 | |
| 186 | bool isCombinedConstruct(Directive D) { |
| 187 | // OpenMP Spec 5.2: [17.3, 9-10] |
| 188 | // Otherwise directive-name is a combined construct. |
| 189 | return !getLeafConstructs(D).empty() && !isCompositeConstruct(D); |
| 190 | } |
| 191 | |
| 192 | ArrayRef<unsigned> getOpenMPVersions() { |
| 193 | static unsigned Versions[]{31, 40, 45, 50, 51, 52, 60}; |
| 194 | return Versions; |
| 195 | } |
| 196 | |
| 197 | std::string prettifyFunctionName(StringRef FunctionName) { |
| 198 | // Internalized functions have the right name, but simply a suffix. |
| 199 | if (FunctionName.ends_with(Suffix: ".internalized" )) |
| 200 | return FunctionName.drop_back(N: sizeof("internalized" )).str() + |
| 201 | " (internalized)" ; |
| 202 | unsigned LineNo = 0; |
| 203 | auto ParentName = deconstructOpenMPKernelName(KernelName: FunctionName, LineNo); |
| 204 | if (LineNo == 0) |
| 205 | return FunctionName.str(); |
| 206 | return ("omp target in " + ParentName + " @ " + std::to_string(val: LineNo) + |
| 207 | " (" + FunctionName + ")" ) |
| 208 | .str(); |
| 209 | } |
| 210 | |
| 211 | std::string deconstructOpenMPKernelName(StringRef KernelName, |
| 212 | unsigned &LineNo) { |
| 213 | |
| 214 | // Only handle functions with an OpenMP kernel prefix for now. Naming scheme: |
| 215 | // __omp_offloading_<hex_hash1>_<hex_hash2>_<name>_l<line>_[<count>_]<suffix> |
| 216 | if (!KernelName.starts_with(Prefix: TargetRegionEntryInfo::KernelNamePrefix)) |
| 217 | return "" ; |
| 218 | |
| 219 | auto PrettyName = KernelName.drop_front( |
| 220 | N: sizeof(TargetRegionEntryInfo::KernelNamePrefix) - /*'\0'*/ 1); |
| 221 | for (int I = 0; I < 3; ++I) { |
| 222 | PrettyName = PrettyName.drop_while(F: [](char c) { return c != '_'; }); |
| 223 | PrettyName = PrettyName.drop_front(); |
| 224 | } |
| 225 | |
| 226 | // Look for the last '_l<line>'. |
| 227 | size_t LineIdx = PrettyName.rfind(Str: "_l" ); |
| 228 | if (LineIdx == StringRef::npos) |
| 229 | return "" ; |
| 230 | if (PrettyName.drop_front(N: LineIdx + 2).consumeInteger(Radix: 10, Result&: LineNo)) |
| 231 | return "" ; |
| 232 | return demangle(MangledName: PrettyName.take_front(N: LineIdx)); |
| 233 | } |
| 234 | } // namespace llvm::omp |
| 235 | |