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
24using namespace llvm;
25using namespace llvm::omp;
26
27#define GEN_DIRECTIVES_IMPL
28#include "llvm/Frontend/OpenMP/OMP.inc"
29
30static iterator_range<ArrayRef<Directive>::iterator>
31getFirstCompositeRange(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
78namespace llvm::omp {
79ArrayRef<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
87ArrayRef<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
97ArrayRef<Directive>
98getLeafOrCompositeConstructs(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
125Directive 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
176bool isLeafConstruct(Directive D) { return getLeafConstructs(D).empty(); }
177
178bool 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
186bool 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
192ArrayRef<unsigned> getOpenMPVersions() {
193 static unsigned Versions[]{31, 40, 45, 50, 51, 52, 60};
194 return Versions;
195}
196
197std::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
211std::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