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