xref: /freebsd-src/contrib/llvm-project/llvm/lib/Frontend/OpenMP/OMP.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1fe6060f1SDimitry Andric //===- OMP.cpp ------ Collection of helpers for OpenMP --------------------===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric 
9*0fca6ea1SDimitry Andric #include "llvm/Frontend/OpenMP/OMP.h"
10fe6060f1SDimitry Andric 
11*0fca6ea1SDimitry Andric #include "llvm/ADT/ArrayRef.h"
12*0fca6ea1SDimitry Andric #include "llvm/ADT/STLExtras.h"
13*0fca6ea1SDimitry Andric #include "llvm/ADT/SmallVector.h"
14fe6060f1SDimitry Andric #include "llvm/ADT/StringRef.h"
15fe6060f1SDimitry Andric #include "llvm/ADT/StringSwitch.h"
16fe6060f1SDimitry Andric #include "llvm/Support/ErrorHandling.h"
17fe6060f1SDimitry Andric 
18*0fca6ea1SDimitry Andric #include <algorithm>
19*0fca6ea1SDimitry Andric #include <iterator>
20*0fca6ea1SDimitry Andric #include <type_traits>
21*0fca6ea1SDimitry Andric 
22fe6060f1SDimitry Andric using namespace llvm;
23*0fca6ea1SDimitry Andric using namespace llvm::omp;
24fe6060f1SDimitry Andric 
25fe6060f1SDimitry Andric #define GEN_DIRECTIVES_IMPL
26fe6060f1SDimitry Andric #include "llvm/Frontend/OpenMP/OMP.inc"
27*0fca6ea1SDimitry Andric 
28*0fca6ea1SDimitry Andric static iterator_range<ArrayRef<Directive>::iterator>
29*0fca6ea1SDimitry Andric getFirstCompositeRange(iterator_range<ArrayRef<Directive>::iterator> Leafs) {
30*0fca6ea1SDimitry Andric   // OpenMP Spec 5.2: [17.3, 8-9]
31*0fca6ea1SDimitry Andric   // If directive-name-A and directive-name-B both correspond to loop-
32*0fca6ea1SDimitry Andric   // associated constructs then directive-name is a composite construct
33*0fca6ea1SDimitry Andric   // otherwise directive-name is a combined construct.
34*0fca6ea1SDimitry Andric   //
35*0fca6ea1SDimitry Andric   // In the list of leaf constructs, find the first loop-associated construct,
36*0fca6ea1SDimitry Andric   // this is the beginning of the returned range. Then, starting from the
37*0fca6ea1SDimitry Andric   // immediately following leaf construct, find the first sequence of adjacent
38*0fca6ea1SDimitry Andric   // loop-associated constructs. The last of those is the last one of the
39*0fca6ea1SDimitry Andric   // range, that is, the end of the range is one past that element.
40*0fca6ea1SDimitry Andric   // If such a sequence of adjacent loop-associated directives does not exist,
41*0fca6ea1SDimitry Andric   // return an empty range.
42*0fca6ea1SDimitry Andric   //
43*0fca6ea1SDimitry Andric   // The end of the returned range (including empty range) is intended to be
44*0fca6ea1SDimitry Andric   // a point from which the search for the next range could resume.
45*0fca6ea1SDimitry Andric   //
46*0fca6ea1SDimitry Andric   // Consequently, this function can't return a range with a single leaf
47*0fca6ea1SDimitry Andric   // construct in it.
48*0fca6ea1SDimitry Andric 
49*0fca6ea1SDimitry Andric   auto firstLoopAssociated =
50*0fca6ea1SDimitry Andric       [](iterator_range<ArrayRef<Directive>::iterator> List) {
51*0fca6ea1SDimitry Andric         for (auto It = List.begin(), End = List.end(); It != End; ++It) {
52*0fca6ea1SDimitry Andric           if (getDirectiveAssociation(*It) == Association::Loop)
53*0fca6ea1SDimitry Andric             return It;
54*0fca6ea1SDimitry Andric         }
55*0fca6ea1SDimitry Andric         return List.end();
56*0fca6ea1SDimitry Andric       };
57*0fca6ea1SDimitry Andric 
58*0fca6ea1SDimitry Andric   auto Empty = llvm::make_range(Leafs.end(), Leafs.end());
59*0fca6ea1SDimitry Andric 
60*0fca6ea1SDimitry Andric   auto Begin = firstLoopAssociated(Leafs);
61*0fca6ea1SDimitry Andric   if (Begin == Leafs.end())
62*0fca6ea1SDimitry Andric     return Empty;
63*0fca6ea1SDimitry Andric 
64*0fca6ea1SDimitry Andric   auto End =
65*0fca6ea1SDimitry Andric       firstLoopAssociated(llvm::make_range(std::next(Begin), Leafs.end()));
66*0fca6ea1SDimitry Andric   if (End == Leafs.end())
67*0fca6ea1SDimitry Andric     return Empty;
68*0fca6ea1SDimitry Andric 
69*0fca6ea1SDimitry Andric   for (; End != Leafs.end(); ++End) {
70*0fca6ea1SDimitry Andric     if (getDirectiveAssociation(*End) != Association::Loop)
71*0fca6ea1SDimitry Andric       break;
72*0fca6ea1SDimitry Andric   }
73*0fca6ea1SDimitry Andric   return llvm::make_range(Begin, End);
74*0fca6ea1SDimitry Andric }
75*0fca6ea1SDimitry Andric 
76*0fca6ea1SDimitry Andric namespace llvm::omp {
77*0fca6ea1SDimitry Andric ArrayRef<Directive> getLeafConstructs(Directive D) {
78*0fca6ea1SDimitry Andric   auto Idx = static_cast<std::size_t>(D);
79*0fca6ea1SDimitry Andric   if (Idx >= Directive_enumSize)
80*0fca6ea1SDimitry Andric     return std::nullopt;
81*0fca6ea1SDimitry Andric   const auto *Row = LeafConstructTable[LeafConstructTableOrdering[Idx]];
82*0fca6ea1SDimitry Andric   return ArrayRef(&Row[2], static_cast<int>(Row[1]));
83*0fca6ea1SDimitry Andric }
84*0fca6ea1SDimitry Andric 
85*0fca6ea1SDimitry Andric ArrayRef<Directive> getLeafConstructsOrSelf(Directive D) {
86*0fca6ea1SDimitry Andric   if (auto Leafs = getLeafConstructs(D); !Leafs.empty())
87*0fca6ea1SDimitry Andric     return Leafs;
88*0fca6ea1SDimitry Andric   auto Idx = static_cast<size_t>(D);
89*0fca6ea1SDimitry Andric   assert(Idx < Directive_enumSize && "Invalid directive");
90*0fca6ea1SDimitry Andric   const auto *Row = LeafConstructTable[LeafConstructTableOrdering[Idx]];
91*0fca6ea1SDimitry Andric   // The first entry in the row is the directive itself.
92*0fca6ea1SDimitry Andric   return ArrayRef(&Row[0], &Row[0] + 1);
93*0fca6ea1SDimitry Andric }
94*0fca6ea1SDimitry Andric 
95*0fca6ea1SDimitry Andric ArrayRef<Directive>
96*0fca6ea1SDimitry Andric getLeafOrCompositeConstructs(Directive D, SmallVectorImpl<Directive> &Output) {
97*0fca6ea1SDimitry Andric   using ArrayTy = ArrayRef<Directive>;
98*0fca6ea1SDimitry Andric   using IteratorTy = ArrayTy::iterator;
99*0fca6ea1SDimitry Andric   ArrayRef<Directive> Leafs = getLeafConstructsOrSelf(D);
100*0fca6ea1SDimitry Andric 
101*0fca6ea1SDimitry Andric   IteratorTy Iter = Leafs.begin();
102*0fca6ea1SDimitry Andric   do {
103*0fca6ea1SDimitry Andric     auto Range = getFirstCompositeRange(llvm::make_range(Iter, Leafs.end()));
104*0fca6ea1SDimitry Andric     // All directives before the range are leaf constructs.
105*0fca6ea1SDimitry Andric     for (; Iter != Range.begin(); ++Iter)
106*0fca6ea1SDimitry Andric       Output.push_back(*Iter);
107*0fca6ea1SDimitry Andric     if (!Range.empty()) {
108*0fca6ea1SDimitry Andric       Directive Comp =
109*0fca6ea1SDimitry Andric           getCompoundConstruct(ArrayTy(Range.begin(), Range.end()));
110*0fca6ea1SDimitry Andric       assert(Comp != OMPD_unknown);
111*0fca6ea1SDimitry Andric       Output.push_back(Comp);
112*0fca6ea1SDimitry Andric       Iter = Range.end();
113*0fca6ea1SDimitry Andric       // As of now, a composite construct must contain all constituent leaf
114*0fca6ea1SDimitry Andric       // constructs from some point until the end of all constituent leaf
115*0fca6ea1SDimitry Andric       // constructs.
116*0fca6ea1SDimitry Andric       assert(Iter == Leafs.end() && "Malformed directive");
117*0fca6ea1SDimitry Andric     }
118*0fca6ea1SDimitry Andric   } while (Iter != Leafs.end());
119*0fca6ea1SDimitry Andric 
120*0fca6ea1SDimitry Andric   return Output;
121*0fca6ea1SDimitry Andric }
122*0fca6ea1SDimitry Andric 
123*0fca6ea1SDimitry Andric Directive getCompoundConstruct(ArrayRef<Directive> Parts) {
124*0fca6ea1SDimitry Andric   if (Parts.empty())
125*0fca6ea1SDimitry Andric     return OMPD_unknown;
126*0fca6ea1SDimitry Andric 
127*0fca6ea1SDimitry Andric   // Parts don't have to be leafs, so expand them into leafs first.
128*0fca6ea1SDimitry Andric   // Store the expanded leafs in the same format as rows in the leaf
129*0fca6ea1SDimitry Andric   // table (generated by tablegen).
130*0fca6ea1SDimitry Andric   SmallVector<Directive> RawLeafs(2);
131*0fca6ea1SDimitry Andric   for (Directive P : Parts) {
132*0fca6ea1SDimitry Andric     ArrayRef<Directive> Ls = getLeafConstructs(P);
133*0fca6ea1SDimitry Andric     if (!Ls.empty())
134*0fca6ea1SDimitry Andric       RawLeafs.append(Ls.begin(), Ls.end());
135*0fca6ea1SDimitry Andric     else
136*0fca6ea1SDimitry Andric       RawLeafs.push_back(P);
137*0fca6ea1SDimitry Andric   }
138*0fca6ea1SDimitry Andric 
139*0fca6ea1SDimitry Andric   // RawLeafs will be used as key in the binary search. The search doesn't
140*0fca6ea1SDimitry Andric   // guarantee that the exact same entry will be found (since RawLeafs may
141*0fca6ea1SDimitry Andric   // not correspond to any compound directive). Because of that, we will
142*0fca6ea1SDimitry Andric   // need to compare the search result with the given set of leafs.
143*0fca6ea1SDimitry Andric   // Also, if there is only one leaf in the list, it corresponds to itself,
144*0fca6ea1SDimitry Andric   // no search is necessary.
145*0fca6ea1SDimitry Andric   auto GivenLeafs{ArrayRef<Directive>(RawLeafs).drop_front(2)};
146*0fca6ea1SDimitry Andric   if (GivenLeafs.size() == 1)
147*0fca6ea1SDimitry Andric     return GivenLeafs.front();
148*0fca6ea1SDimitry Andric   RawLeafs[1] = static_cast<Directive>(GivenLeafs.size());
149*0fca6ea1SDimitry Andric 
150*0fca6ea1SDimitry Andric   auto Iter = std::lower_bound(
151*0fca6ea1SDimitry Andric       LeafConstructTable, LeafConstructTableEndDirective,
152*0fca6ea1SDimitry Andric       static_cast<std::decay_t<decltype(*LeafConstructTable)>>(RawLeafs.data()),
153*0fca6ea1SDimitry Andric       [](const llvm::omp::Directive *RowA, const llvm::omp::Directive *RowB) {
154*0fca6ea1SDimitry Andric         const auto *BeginA = &RowA[2];
155*0fca6ea1SDimitry Andric         const auto *EndA = BeginA + static_cast<int>(RowA[1]);
156*0fca6ea1SDimitry Andric         const auto *BeginB = &RowB[2];
157*0fca6ea1SDimitry Andric         const auto *EndB = BeginB + static_cast<int>(RowB[1]);
158*0fca6ea1SDimitry Andric         if (BeginA == EndA && BeginB == EndB)
159*0fca6ea1SDimitry Andric           return static_cast<int>(RowA[0]) < static_cast<int>(RowB[0]);
160*0fca6ea1SDimitry Andric         return std::lexicographical_compare(BeginA, EndA, BeginB, EndB);
161*0fca6ea1SDimitry Andric       });
162*0fca6ea1SDimitry Andric 
163*0fca6ea1SDimitry Andric   if (Iter == std::end(LeafConstructTable))
164*0fca6ea1SDimitry Andric     return OMPD_unknown;
165*0fca6ea1SDimitry Andric 
166*0fca6ea1SDimitry Andric   // Verify that we got a match.
167*0fca6ea1SDimitry Andric   Directive Found = (*Iter)[0];
168*0fca6ea1SDimitry Andric   ArrayRef<Directive> FoundLeafs = getLeafConstructs(Found);
169*0fca6ea1SDimitry Andric   if (FoundLeafs == GivenLeafs)
170*0fca6ea1SDimitry Andric     return Found;
171*0fca6ea1SDimitry Andric   return OMPD_unknown;
172*0fca6ea1SDimitry Andric }
173*0fca6ea1SDimitry Andric 
174*0fca6ea1SDimitry Andric bool isLeafConstruct(Directive D) { return getLeafConstructs(D).empty(); }
175*0fca6ea1SDimitry Andric 
176*0fca6ea1SDimitry Andric bool isCompositeConstruct(Directive D) {
177*0fca6ea1SDimitry Andric   ArrayRef<Directive> Leafs = getLeafConstructsOrSelf(D);
178*0fca6ea1SDimitry Andric   if (Leafs.size() <= 1)
179*0fca6ea1SDimitry Andric     return false;
180*0fca6ea1SDimitry Andric   auto Range = getFirstCompositeRange(Leafs);
181*0fca6ea1SDimitry Andric   return Range.begin() == Leafs.begin() && Range.end() == Leafs.end();
182*0fca6ea1SDimitry Andric }
183*0fca6ea1SDimitry Andric 
184*0fca6ea1SDimitry Andric bool isCombinedConstruct(Directive D) {
185*0fca6ea1SDimitry Andric   // OpenMP Spec 5.2: [17.3, 9-10]
186*0fca6ea1SDimitry Andric   // Otherwise directive-name is a combined construct.
187*0fca6ea1SDimitry Andric   return !getLeafConstructs(D).empty() && !isCompositeConstruct(D);
188*0fca6ea1SDimitry Andric }
189*0fca6ea1SDimitry Andric } // namespace llvm::omp
190