xref: /llvm-project/llvm/lib/Frontend/OpenMP/OMP.cpp (revision fb4ecada815ceee37536a26b4ff5ce231226b23e)
1d709dcc0SValentin Clement //===- OMP.cpp ------ Collection of helpers for OpenMP --------------------===//
2d709dcc0SValentin Clement //
3d709dcc0SValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4d709dcc0SValentin Clement // See https://llvm.org/LICENSE.txt for license information.
5d709dcc0SValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d709dcc0SValentin Clement //
7d709dcc0SValentin Clement //===----------------------------------------------------------------------===//
8d709dcc0SValentin Clement 
9141ebdd2SKrzysztof Parzyszek #include "llvm/Frontend/OpenMP/OMP.h"
10d709dcc0SValentin Clement 
1140137ff0SKrzysztof Parzyszek #include "llvm/ADT/ArrayRef.h"
1240137ff0SKrzysztof Parzyszek #include "llvm/ADT/SmallVector.h"
13d709dcc0SValentin Clement #include "llvm/ADT/StringRef.h"
14f3bfc563SJohannes Doerfert #include "llvm/Demangle/Demangle.h"
15f3bfc563SJohannes Doerfert #include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
16d709dcc0SValentin Clement #include "llvm/Support/ErrorHandling.h"
17d709dcc0SValentin Clement 
1840137ff0SKrzysztof Parzyszek #include <algorithm>
19f3bfc563SJohannes Doerfert #include <cstdio>
2040137ff0SKrzysztof Parzyszek #include <iterator>
21f3bfc563SJohannes Doerfert #include <string>
2240137ff0SKrzysztof Parzyszek #include <type_traits>
2340137ff0SKrzysztof Parzyszek 
24d709dcc0SValentin Clement using namespace llvm;
2540137ff0SKrzysztof Parzyszek using namespace llvm::omp;
26d709dcc0SValentin Clement 
27d709dcc0SValentin Clement #define GEN_DIRECTIVES_IMPL
28d709dcc0SValentin Clement #include "llvm/Frontend/OpenMP/OMP.inc"
2940137ff0SKrzysztof Parzyszek 
30d577518dSKrzysztof Parzyszek static iterator_range<ArrayRef<Directive>::iterator>
31d577518dSKrzysztof Parzyszek getFirstCompositeRange(iterator_range<ArrayRef<Directive>::iterator> Leafs) {
32d577518dSKrzysztof Parzyszek   // OpenMP Spec 5.2: [17.3, 8-9]
33d577518dSKrzysztof Parzyszek   // If directive-name-A and directive-name-B both correspond to loop-
34d577518dSKrzysztof Parzyszek   // associated constructs then directive-name is a composite construct
35d577518dSKrzysztof Parzyszek   // otherwise directive-name is a combined construct.
36d577518dSKrzysztof Parzyszek   //
37d577518dSKrzysztof Parzyszek   // In the list of leaf constructs, find the first loop-associated construct,
38d577518dSKrzysztof Parzyszek   // this is the beginning of the returned range. Then, starting from the
39d577518dSKrzysztof Parzyszek   // immediately following leaf construct, find the first sequence of adjacent
40d577518dSKrzysztof Parzyszek   // loop-associated constructs. The last of those is the last one of the
41d577518dSKrzysztof Parzyszek   // range, that is, the end of the range is one past that element.
42d577518dSKrzysztof Parzyszek   // If such a sequence of adjacent loop-associated directives does not exist,
43d577518dSKrzysztof Parzyszek   // return an empty range.
44d577518dSKrzysztof Parzyszek   //
45d577518dSKrzysztof Parzyszek   // The end of the returned range (including empty range) is intended to be
46d577518dSKrzysztof Parzyszek   // a point from which the search for the next range could resume.
47d577518dSKrzysztof Parzyszek   //
48d577518dSKrzysztof Parzyszek   // Consequently, this function can't return a range with a single leaf
49d577518dSKrzysztof Parzyszek   // construct in it.
50d577518dSKrzysztof Parzyszek 
51d577518dSKrzysztof Parzyszek   auto firstLoopAssociated =
52d577518dSKrzysztof Parzyszek       [](iterator_range<ArrayRef<Directive>::iterator> List) {
53d577518dSKrzysztof Parzyszek         for (auto It = List.begin(), End = List.end(); It != End; ++It) {
54d577518dSKrzysztof Parzyszek           if (getDirectiveAssociation(*It) == Association::Loop)
55d577518dSKrzysztof Parzyszek             return It;
56d577518dSKrzysztof Parzyszek         }
57d577518dSKrzysztof Parzyszek         return List.end();
58d577518dSKrzysztof Parzyszek       };
59d577518dSKrzysztof Parzyszek 
60d577518dSKrzysztof Parzyszek   auto Empty = llvm::make_range(Leafs.end(), Leafs.end());
61d577518dSKrzysztof Parzyszek 
62d577518dSKrzysztof Parzyszek   auto Begin = firstLoopAssociated(Leafs);
63d577518dSKrzysztof Parzyszek   if (Begin == Leafs.end())
64d577518dSKrzysztof Parzyszek     return Empty;
65d577518dSKrzysztof Parzyszek 
66d577518dSKrzysztof Parzyszek   auto End =
67d577518dSKrzysztof Parzyszek       firstLoopAssociated(llvm::make_range(std::next(Begin), Leafs.end()));
68d577518dSKrzysztof Parzyszek   if (End == Leafs.end())
69d577518dSKrzysztof Parzyszek     return Empty;
70d577518dSKrzysztof Parzyszek 
71d577518dSKrzysztof Parzyszek   for (; End != Leafs.end(); ++End) {
72d577518dSKrzysztof Parzyszek     if (getDirectiveAssociation(*End) != Association::Loop)
73d577518dSKrzysztof Parzyszek       break;
74d577518dSKrzysztof Parzyszek   }
75d577518dSKrzysztof Parzyszek   return llvm::make_range(Begin, End);
76d577518dSKrzysztof Parzyszek }
77d577518dSKrzysztof Parzyszek 
7840137ff0SKrzysztof Parzyszek namespace llvm::omp {
7940137ff0SKrzysztof Parzyszek ArrayRef<Directive> getLeafConstructs(Directive D) {
8040137ff0SKrzysztof Parzyszek   auto Idx = static_cast<std::size_t>(D);
8140137ff0SKrzysztof Parzyszek   if (Idx >= Directive_enumSize)
82e03f4271SJay Foad     return {};
8340137ff0SKrzysztof Parzyszek   const auto *Row = LeafConstructTable[LeafConstructTableOrdering[Idx]];
8440137ff0SKrzysztof Parzyszek   return ArrayRef(&Row[2], static_cast<int>(Row[1]));
8540137ff0SKrzysztof Parzyszek }
8640137ff0SKrzysztof Parzyszek 
87d577518dSKrzysztof Parzyszek ArrayRef<Directive> getLeafConstructsOrSelf(Directive D) {
88d577518dSKrzysztof Parzyszek   if (auto Leafs = getLeafConstructs(D); !Leafs.empty())
89d577518dSKrzysztof Parzyszek     return Leafs;
90d577518dSKrzysztof Parzyszek   auto Idx = static_cast<size_t>(D);
91d577518dSKrzysztof Parzyszek   assert(Idx < Directive_enumSize && "Invalid directive");
92d577518dSKrzysztof Parzyszek   const auto *Row = LeafConstructTable[LeafConstructTableOrdering[Idx]];
93d577518dSKrzysztof Parzyszek   // The first entry in the row is the directive itself.
94d577518dSKrzysztof Parzyszek   return ArrayRef(&Row[0], &Row[0] + 1);
95d577518dSKrzysztof Parzyszek }
96d577518dSKrzysztof Parzyszek 
97d577518dSKrzysztof Parzyszek ArrayRef<Directive>
98d577518dSKrzysztof Parzyszek getLeafOrCompositeConstructs(Directive D, SmallVectorImpl<Directive> &Output) {
99d577518dSKrzysztof Parzyszek   using ArrayTy = ArrayRef<Directive>;
100d577518dSKrzysztof Parzyszek   using IteratorTy = ArrayTy::iterator;
101d577518dSKrzysztof Parzyszek   ArrayRef<Directive> Leafs = getLeafConstructsOrSelf(D);
102d577518dSKrzysztof Parzyszek 
103d577518dSKrzysztof Parzyszek   IteratorTy Iter = Leafs.begin();
104d577518dSKrzysztof Parzyszek   do {
105d577518dSKrzysztof Parzyszek     auto Range = getFirstCompositeRange(llvm::make_range(Iter, Leafs.end()));
106d577518dSKrzysztof Parzyszek     // All directives before the range are leaf constructs.
107d577518dSKrzysztof Parzyszek     for (; Iter != Range.begin(); ++Iter)
108d577518dSKrzysztof Parzyszek       Output.push_back(*Iter);
109d577518dSKrzysztof Parzyszek     if (!Range.empty()) {
110d577518dSKrzysztof Parzyszek       Directive Comp =
111d577518dSKrzysztof Parzyszek           getCompoundConstruct(ArrayTy(Range.begin(), Range.end()));
112d577518dSKrzysztof Parzyszek       assert(Comp != OMPD_unknown);
113d577518dSKrzysztof Parzyszek       Output.push_back(Comp);
114d577518dSKrzysztof Parzyszek       Iter = Range.end();
115d577518dSKrzysztof Parzyszek       // As of now, a composite construct must contain all constituent leaf
116d577518dSKrzysztof Parzyszek       // constructs from some point until the end of all constituent leaf
117d577518dSKrzysztof Parzyszek       // constructs.
118d577518dSKrzysztof Parzyszek       assert(Iter == Leafs.end() && "Malformed directive");
119d577518dSKrzysztof Parzyszek     }
120d577518dSKrzysztof Parzyszek   } while (Iter != Leafs.end());
121d577518dSKrzysztof Parzyszek 
122d577518dSKrzysztof Parzyszek   return Output;
123d577518dSKrzysztof Parzyszek }
124d577518dSKrzysztof Parzyszek 
12540137ff0SKrzysztof Parzyszek Directive getCompoundConstruct(ArrayRef<Directive> Parts) {
12640137ff0SKrzysztof Parzyszek   if (Parts.empty())
12740137ff0SKrzysztof Parzyszek     return OMPD_unknown;
12840137ff0SKrzysztof Parzyszek 
12940137ff0SKrzysztof Parzyszek   // Parts don't have to be leafs, so expand them into leafs first.
13040137ff0SKrzysztof Parzyszek   // Store the expanded leafs in the same format as rows in the leaf
13140137ff0SKrzysztof Parzyszek   // table (generated by tablegen).
13240137ff0SKrzysztof Parzyszek   SmallVector<Directive> RawLeafs(2);
13340137ff0SKrzysztof Parzyszek   for (Directive P : Parts) {
13440137ff0SKrzysztof Parzyszek     ArrayRef<Directive> Ls = getLeafConstructs(P);
13540137ff0SKrzysztof Parzyszek     if (!Ls.empty())
13640137ff0SKrzysztof Parzyszek       RawLeafs.append(Ls.begin(), Ls.end());
13740137ff0SKrzysztof Parzyszek     else
13840137ff0SKrzysztof Parzyszek       RawLeafs.push_back(P);
13940137ff0SKrzysztof Parzyszek   }
14040137ff0SKrzysztof Parzyszek 
14140137ff0SKrzysztof Parzyszek   // RawLeafs will be used as key in the binary search. The search doesn't
14240137ff0SKrzysztof Parzyszek   // guarantee that the exact same entry will be found (since RawLeafs may
14340137ff0SKrzysztof Parzyszek   // not correspond to any compound directive). Because of that, we will
14440137ff0SKrzysztof Parzyszek   // need to compare the search result with the given set of leafs.
14540137ff0SKrzysztof Parzyszek   // Also, if there is only one leaf in the list, it corresponds to itself,
14640137ff0SKrzysztof Parzyszek   // no search is necessary.
14740137ff0SKrzysztof Parzyszek   auto GivenLeafs{ArrayRef<Directive>(RawLeafs).drop_front(2)};
14840137ff0SKrzysztof Parzyszek   if (GivenLeafs.size() == 1)
14940137ff0SKrzysztof Parzyszek     return GivenLeafs.front();
15040137ff0SKrzysztof Parzyszek   RawLeafs[1] = static_cast<Directive>(GivenLeafs.size());
15140137ff0SKrzysztof Parzyszek 
15240137ff0SKrzysztof Parzyszek   auto Iter = std::lower_bound(
15340137ff0SKrzysztof Parzyszek       LeafConstructTable, LeafConstructTableEndDirective,
15440137ff0SKrzysztof Parzyszek       static_cast<std::decay_t<decltype(*LeafConstructTable)>>(RawLeafs.data()),
15540137ff0SKrzysztof Parzyszek       [](const llvm::omp::Directive *RowA, const llvm::omp::Directive *RowB) {
15640137ff0SKrzysztof Parzyszek         const auto *BeginA = &RowA[2];
15740137ff0SKrzysztof Parzyszek         const auto *EndA = BeginA + static_cast<int>(RowA[1]);
15840137ff0SKrzysztof Parzyszek         const auto *BeginB = &RowB[2];
15940137ff0SKrzysztof Parzyszek         const auto *EndB = BeginB + static_cast<int>(RowB[1]);
16040137ff0SKrzysztof Parzyszek         if (BeginA == EndA && BeginB == EndB)
16140137ff0SKrzysztof Parzyszek           return static_cast<int>(RowA[0]) < static_cast<int>(RowB[0]);
16240137ff0SKrzysztof Parzyszek         return std::lexicographical_compare(BeginA, EndA, BeginB, EndB);
16340137ff0SKrzysztof Parzyszek       });
16440137ff0SKrzysztof Parzyszek 
16540137ff0SKrzysztof Parzyszek   if (Iter == std::end(LeafConstructTable))
16640137ff0SKrzysztof Parzyszek     return OMPD_unknown;
16740137ff0SKrzysztof Parzyszek 
16840137ff0SKrzysztof Parzyszek   // Verify that we got a match.
16940137ff0SKrzysztof Parzyszek   Directive Found = (*Iter)[0];
17040137ff0SKrzysztof Parzyszek   ArrayRef<Directive> FoundLeafs = getLeafConstructs(Found);
17140137ff0SKrzysztof Parzyszek   if (FoundLeafs == GivenLeafs)
17240137ff0SKrzysztof Parzyszek     return Found;
17340137ff0SKrzysztof Parzyszek   return OMPD_unknown;
17440137ff0SKrzysztof Parzyszek }
17570d3ddb2SKrzysztof Parzyszek 
17670d3ddb2SKrzysztof Parzyszek bool isLeafConstruct(Directive D) { return getLeafConstructs(D).empty(); }
17770d3ddb2SKrzysztof Parzyszek 
17870d3ddb2SKrzysztof Parzyszek bool isCompositeConstruct(Directive D) {
179d577518dSKrzysztof Parzyszek   ArrayRef<Directive> Leafs = getLeafConstructsOrSelf(D);
180d577518dSKrzysztof Parzyszek   if (Leafs.size() <= 1)
18170d3ddb2SKrzysztof Parzyszek     return false;
182d577518dSKrzysztof Parzyszek   auto Range = getFirstCompositeRange(Leafs);
183d577518dSKrzysztof Parzyszek   return Range.begin() == Leafs.begin() && Range.end() == Leafs.end();
18470d3ddb2SKrzysztof Parzyszek }
18570d3ddb2SKrzysztof Parzyszek 
18670d3ddb2SKrzysztof Parzyszek bool isCombinedConstruct(Directive D) {
18770d3ddb2SKrzysztof Parzyszek   // OpenMP Spec 5.2: [17.3, 9-10]
18870d3ddb2SKrzysztof Parzyszek   // Otherwise directive-name is a combined construct.
18970d3ddb2SKrzysztof Parzyszek   return !getLeafConstructs(D).empty() && !isCompositeConstruct(D);
19070d3ddb2SKrzysztof Parzyszek }
191f3bfc563SJohannes Doerfert 
192*fb4ecadaSKrzysztof Parzyszek ArrayRef<unsigned> getOpenMPVersions() {
193*fb4ecadaSKrzysztof Parzyszek   static unsigned Versions[]{45, 50, 51, 52, 60};
194*fb4ecadaSKrzysztof Parzyszek   return Versions;
195*fb4ecadaSKrzysztof Parzyszek }
196*fb4ecadaSKrzysztof Parzyszek 
197f3bfc563SJohannes Doerfert std::string prettifyFunctionName(StringRef FunctionName) {
198f3bfc563SJohannes Doerfert   // Internalized functions have the right name, but simply a suffix.
199f3bfc563SJohannes Doerfert   if (FunctionName.ends_with(".internalized"))
200f3bfc563SJohannes Doerfert     return FunctionName.drop_back(sizeof("internalized")).str() +
201f3bfc563SJohannes Doerfert            " (internalized)";
202f3bfc563SJohannes Doerfert   unsigned LineNo = 0;
203f3bfc563SJohannes Doerfert   auto ParentName = deconstructOpenMPKernelName(FunctionName, LineNo);
204f3bfc563SJohannes Doerfert   if (LineNo == 0)
205f3bfc563SJohannes Doerfert     return FunctionName.str();
206f3bfc563SJohannes Doerfert   return ("omp target in " + ParentName + " @ " + std::to_string(LineNo) +
207f3bfc563SJohannes Doerfert           " (" + FunctionName + ")")
208f3bfc563SJohannes Doerfert       .str();
209f3bfc563SJohannes Doerfert }
210f3bfc563SJohannes Doerfert 
211f3bfc563SJohannes Doerfert std::string deconstructOpenMPKernelName(StringRef KernelName,
212f3bfc563SJohannes Doerfert                                         unsigned &LineNo) {
213f3bfc563SJohannes Doerfert 
214f3bfc563SJohannes Doerfert   // Only handle functions with an OpenMP kernel prefix for now. Naming scheme:
215f3bfc563SJohannes Doerfert   // __omp_offloading_<hex_hash1>_<hex_hash2>_<name>_l<line>_[<count>_]<suffix>
216f3bfc563SJohannes Doerfert   if (!KernelName.starts_with(TargetRegionEntryInfo::KernelNamePrefix))
217f3bfc563SJohannes Doerfert     return "";
218f3bfc563SJohannes Doerfert 
219f3bfc563SJohannes Doerfert   auto PrettyName = KernelName.drop_front(
220f3bfc563SJohannes Doerfert       sizeof(TargetRegionEntryInfo::KernelNamePrefix) - /*'\0'*/ 1);
221f3bfc563SJohannes Doerfert   for (int I = 0; I < 3; ++I) {
222f3bfc563SJohannes Doerfert     PrettyName = PrettyName.drop_while([](char c) { return c != '_'; });
223f3bfc563SJohannes Doerfert     PrettyName = PrettyName.drop_front();
224f3bfc563SJohannes Doerfert   }
225f3bfc563SJohannes Doerfert 
226f3bfc563SJohannes Doerfert   // Look for the last '_l<line>'.
227f3bfc563SJohannes Doerfert   size_t LineIdx = PrettyName.rfind("_l");
228f3bfc563SJohannes Doerfert   if (LineIdx == StringRef::npos)
229f3bfc563SJohannes Doerfert     return "";
230f3bfc563SJohannes Doerfert   if (PrettyName.drop_front(LineIdx + 2).consumeInteger(10, LineNo))
231f3bfc563SJohannes Doerfert     return "";
232f3bfc563SJohannes Doerfert   return demangle(PrettyName.take_front(LineIdx));
233f3bfc563SJohannes Doerfert }
23440137ff0SKrzysztof Parzyszek } // namespace llvm::omp
235