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