19548b74aSJohannes Doerfert //===-- IPO/OpenMPOpt.cpp - Collection of OpenMP specific optimizations ---===// 29548b74aSJohannes Doerfert // 39548b74aSJohannes Doerfert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 49548b74aSJohannes Doerfert // See https://llvm.org/LICENSE.txt for license information. 59548b74aSJohannes Doerfert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 69548b74aSJohannes Doerfert // 79548b74aSJohannes Doerfert //===----------------------------------------------------------------------===// 89548b74aSJohannes Doerfert // 99548b74aSJohannes Doerfert // OpenMP specific optimizations: 109548b74aSJohannes Doerfert // 119548b74aSJohannes Doerfert // - Deduplication of runtime calls, e.g., omp_get_thread_num. 129548b74aSJohannes Doerfert // 139548b74aSJohannes Doerfert //===----------------------------------------------------------------------===// 149548b74aSJohannes Doerfert 159548b74aSJohannes Doerfert #include "llvm/Transforms/IPO/OpenMPOpt.h" 169548b74aSJohannes Doerfert 179548b74aSJohannes Doerfert #include "llvm/ADT/EnumeratedArray.h" 189548b74aSJohannes Doerfert #include "llvm/ADT/Statistic.h" 199548b74aSJohannes Doerfert #include "llvm/Analysis/CallGraph.h" 209548b74aSJohannes Doerfert #include "llvm/Analysis/CallGraphSCCPass.h" 214d4ea9acSHuber, Joseph #include "llvm/Analysis/OptimizationRemarkEmitter.h" 229548b74aSJohannes Doerfert #include "llvm/Frontend/OpenMP/OMPConstants.h" 23e28936f6SJohannes Doerfert #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" 249548b74aSJohannes Doerfert #include "llvm/InitializePasses.h" 259548b74aSJohannes Doerfert #include "llvm/Support/CommandLine.h" 269548b74aSJohannes Doerfert #include "llvm/Transforms/IPO.h" 277cfd267cSsstefan1 #include "llvm/Transforms/IPO/Attributor.h" 289548b74aSJohannes Doerfert #include "llvm/Transforms/Utils/CallGraphUpdater.h" 299548b74aSJohannes Doerfert 309548b74aSJohannes Doerfert using namespace llvm; 319548b74aSJohannes Doerfert using namespace omp; 329548b74aSJohannes Doerfert 339548b74aSJohannes Doerfert #define DEBUG_TYPE "openmp-opt" 349548b74aSJohannes Doerfert 359548b74aSJohannes Doerfert static cl::opt<bool> DisableOpenMPOptimizations( 369548b74aSJohannes Doerfert "openmp-opt-disable", cl::ZeroOrMore, 379548b74aSJohannes Doerfert cl::desc("Disable OpenMP specific optimizations."), cl::Hidden, 389548b74aSJohannes Doerfert cl::init(false)); 399548b74aSJohannes Doerfert 400f426935Ssstefan1 static cl::opt<bool> PrintICVValues("openmp-print-icv-values", cl::init(false), 410f426935Ssstefan1 cl::Hidden); 42e8039ad4SJohannes Doerfert static cl::opt<bool> PrintOpenMPKernels("openmp-print-gpu-kernels", 43e8039ad4SJohannes Doerfert cl::init(false), cl::Hidden); 440f426935Ssstefan1 45496f8e5bSHamilton Tobon Mosquera static cl::opt<bool> HideMemoryTransferLatency( 46496f8e5bSHamilton Tobon Mosquera "openmp-hide-memory-transfer-latency", 47496f8e5bSHamilton Tobon Mosquera cl::desc("[WIP] Tries to hide the latency of host to device memory" 48496f8e5bSHamilton Tobon Mosquera " transfers"), 49496f8e5bSHamilton Tobon Mosquera cl::Hidden, cl::init(false)); 50496f8e5bSHamilton Tobon Mosquera 51496f8e5bSHamilton Tobon Mosquera 529548b74aSJohannes Doerfert STATISTIC(NumOpenMPRuntimeCallsDeduplicated, 539548b74aSJohannes Doerfert "Number of OpenMP runtime calls deduplicated"); 5455eb714aSRoman Lebedev STATISTIC(NumOpenMPParallelRegionsDeleted, 5555eb714aSRoman Lebedev "Number of OpenMP parallel regions deleted"); 569548b74aSJohannes Doerfert STATISTIC(NumOpenMPRuntimeFunctionsIdentified, 579548b74aSJohannes Doerfert "Number of OpenMP runtime functions identified"); 589548b74aSJohannes Doerfert STATISTIC(NumOpenMPRuntimeFunctionUsesIdentified, 599548b74aSJohannes Doerfert "Number of OpenMP runtime function uses identified"); 60e8039ad4SJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernels, 61e8039ad4SJohannes Doerfert "Number of OpenMP target region entry points (=kernels) identified"); 625b0581aeSJohannes Doerfert STATISTIC( 635b0581aeSJohannes Doerfert NumOpenMPParallelRegionsReplacedInGPUStateMachine, 645b0581aeSJohannes Doerfert "Number of OpenMP parallel regions replaced with ID in GPU state machines"); 659548b74aSJohannes Doerfert 66263c4a3cSrathod-sahaab #if !defined(NDEBUG) 679548b74aSJohannes Doerfert static constexpr auto TAG = "[" DEBUG_TYPE "]"; 68a50c0b0dSMikael Holmen #endif 699548b74aSJohannes Doerfert 70624d34afSJohannes Doerfert /// Apply \p CB to all uses of \p F. If \p LookThroughConstantExprUses is 71624d34afSJohannes Doerfert /// true, constant expression users are not given to \p CB but their uses are 72624d34afSJohannes Doerfert /// traversed transitively. 73624d34afSJohannes Doerfert template <typename CBTy> 74624d34afSJohannes Doerfert static void foreachUse(Function &F, CBTy CB, 75624d34afSJohannes Doerfert bool LookThroughConstantExprUses = true) { 76624d34afSJohannes Doerfert SmallVector<Use *, 8> Worklist(make_pointer_range(F.uses())); 77624d34afSJohannes Doerfert 78624d34afSJohannes Doerfert for (unsigned idx = 0; idx < Worklist.size(); ++idx) { 79624d34afSJohannes Doerfert Use &U = *Worklist[idx]; 80624d34afSJohannes Doerfert 81624d34afSJohannes Doerfert // Allow use in constant bitcasts and simply look through them. 82624d34afSJohannes Doerfert if (LookThroughConstantExprUses && isa<ConstantExpr>(U.getUser())) { 83624d34afSJohannes Doerfert for (Use &CEU : cast<ConstantExpr>(U.getUser())->uses()) 84624d34afSJohannes Doerfert Worklist.push_back(&CEU); 85624d34afSJohannes Doerfert continue; 86624d34afSJohannes Doerfert } 87624d34afSJohannes Doerfert 88624d34afSJohannes Doerfert CB(U); 89624d34afSJohannes Doerfert } 90624d34afSJohannes Doerfert } 91624d34afSJohannes Doerfert 929548b74aSJohannes Doerfert namespace { 939548b74aSJohannes Doerfert 94b8235d2bSsstefan1 struct AAICVTracker; 95b8235d2bSsstefan1 967cfd267cSsstefan1 /// OpenMP specific information. For now, stores RFIs and ICVs also needed for 977cfd267cSsstefan1 /// Attributor runs. 987cfd267cSsstefan1 struct OMPInformationCache : public InformationCache { 997cfd267cSsstefan1 OMPInformationCache(Module &M, AnalysisGetter &AG, 100624d34afSJohannes Doerfert BumpPtrAllocator &Allocator, SetVector<Function *> &CGSCC, 101e8039ad4SJohannes Doerfert SmallPtrSetImpl<Kernel> &Kernels) 102624d34afSJohannes Doerfert : InformationCache(M, AG, Allocator, &CGSCC), OMPBuilder(M), 103624d34afSJohannes Doerfert Kernels(Kernels) { 104624d34afSJohannes Doerfert initializeModuleSlice(CGSCC); 105624d34afSJohannes Doerfert 10661238d26Ssstefan1 OMPBuilder.initialize(); 1079548b74aSJohannes Doerfert initializeRuntimeFunctions(); 1080f426935Ssstefan1 initializeInternalControlVars(); 1099548b74aSJohannes Doerfert } 1109548b74aSJohannes Doerfert 1110f426935Ssstefan1 /// Generic information that describes an internal control variable. 1120f426935Ssstefan1 struct InternalControlVarInfo { 1130f426935Ssstefan1 /// The kind, as described by InternalControlVar enum. 1140f426935Ssstefan1 InternalControlVar Kind; 1150f426935Ssstefan1 1160f426935Ssstefan1 /// The name of the ICV. 1170f426935Ssstefan1 StringRef Name; 1180f426935Ssstefan1 1190f426935Ssstefan1 /// Environment variable associated with this ICV. 1200f426935Ssstefan1 StringRef EnvVarName; 1210f426935Ssstefan1 1220f426935Ssstefan1 /// Initial value kind. 1230f426935Ssstefan1 ICVInitValue InitKind; 1240f426935Ssstefan1 1250f426935Ssstefan1 /// Initial value. 1260f426935Ssstefan1 ConstantInt *InitValue; 1270f426935Ssstefan1 1280f426935Ssstefan1 /// Setter RTL function associated with this ICV. 1290f426935Ssstefan1 RuntimeFunction Setter; 1300f426935Ssstefan1 1310f426935Ssstefan1 /// Getter RTL function associated with this ICV. 1320f426935Ssstefan1 RuntimeFunction Getter; 1330f426935Ssstefan1 1340f426935Ssstefan1 /// RTL Function corresponding to the override clause of this ICV 1350f426935Ssstefan1 RuntimeFunction Clause; 1360f426935Ssstefan1 }; 1370f426935Ssstefan1 1389548b74aSJohannes Doerfert /// Generic information that describes a runtime function 1399548b74aSJohannes Doerfert struct RuntimeFunctionInfo { 1408855fec3SJohannes Doerfert 1419548b74aSJohannes Doerfert /// The kind, as described by the RuntimeFunction enum. 1429548b74aSJohannes Doerfert RuntimeFunction Kind; 1439548b74aSJohannes Doerfert 1449548b74aSJohannes Doerfert /// The name of the function. 1459548b74aSJohannes Doerfert StringRef Name; 1469548b74aSJohannes Doerfert 1479548b74aSJohannes Doerfert /// Flag to indicate a variadic function. 1489548b74aSJohannes Doerfert bool IsVarArg; 1499548b74aSJohannes Doerfert 1509548b74aSJohannes Doerfert /// The return type of the function. 1519548b74aSJohannes Doerfert Type *ReturnType; 1529548b74aSJohannes Doerfert 1539548b74aSJohannes Doerfert /// The argument types of the function. 1549548b74aSJohannes Doerfert SmallVector<Type *, 8> ArgumentTypes; 1559548b74aSJohannes Doerfert 1569548b74aSJohannes Doerfert /// The declaration if available. 157f09f4b26SJohannes Doerfert Function *Declaration = nullptr; 1589548b74aSJohannes Doerfert 1599548b74aSJohannes Doerfert /// Uses of this runtime function per function containing the use. 1608855fec3SJohannes Doerfert using UseVector = SmallVector<Use *, 16>; 1618855fec3SJohannes Doerfert 162b8235d2bSsstefan1 /// Clear UsesMap for runtime function. 163b8235d2bSsstefan1 void clearUsesMap() { UsesMap.clear(); } 164b8235d2bSsstefan1 16554bd3751SJohannes Doerfert /// Boolean conversion that is true if the runtime function was found. 16654bd3751SJohannes Doerfert operator bool() const { return Declaration; } 16754bd3751SJohannes Doerfert 1688855fec3SJohannes Doerfert /// Return the vector of uses in function \p F. 1698855fec3SJohannes Doerfert UseVector &getOrCreateUseVector(Function *F) { 170b8235d2bSsstefan1 std::shared_ptr<UseVector> &UV = UsesMap[F]; 1718855fec3SJohannes Doerfert if (!UV) 172b8235d2bSsstefan1 UV = std::make_shared<UseVector>(); 1738855fec3SJohannes Doerfert return *UV; 1748855fec3SJohannes Doerfert } 1758855fec3SJohannes Doerfert 1768855fec3SJohannes Doerfert /// Return the vector of uses in function \p F or `nullptr` if there are 1778855fec3SJohannes Doerfert /// none. 1788855fec3SJohannes Doerfert const UseVector *getUseVector(Function &F) const { 17995e57072SDavid Blaikie auto I = UsesMap.find(&F); 18095e57072SDavid Blaikie if (I != UsesMap.end()) 18195e57072SDavid Blaikie return I->second.get(); 18295e57072SDavid Blaikie return nullptr; 1838855fec3SJohannes Doerfert } 1848855fec3SJohannes Doerfert 1858855fec3SJohannes Doerfert /// Return how many functions contain uses of this runtime function. 1868855fec3SJohannes Doerfert size_t getNumFunctionsWithUses() const { return UsesMap.size(); } 1879548b74aSJohannes Doerfert 1889548b74aSJohannes Doerfert /// Return the number of arguments (or the minimal number for variadic 1899548b74aSJohannes Doerfert /// functions). 1909548b74aSJohannes Doerfert size_t getNumArgs() const { return ArgumentTypes.size(); } 1919548b74aSJohannes Doerfert 1929548b74aSJohannes Doerfert /// Run the callback \p CB on each use and forget the use if the result is 1939548b74aSJohannes Doerfert /// true. The callback will be fed the function in which the use was 1949548b74aSJohannes Doerfert /// encountered as second argument. 195624d34afSJohannes Doerfert void foreachUse(SmallVectorImpl<Function *> &SCC, 196624d34afSJohannes Doerfert function_ref<bool(Use &, Function &)> CB) { 197624d34afSJohannes Doerfert for (Function *F : SCC) 198624d34afSJohannes Doerfert foreachUse(CB, F); 199e099c7b6Ssstefan1 } 200e099c7b6Ssstefan1 201e099c7b6Ssstefan1 /// Run the callback \p CB on each use within the function \p F and forget 202e099c7b6Ssstefan1 /// the use if the result is true. 203624d34afSJohannes Doerfert void foreachUse(function_ref<bool(Use &, Function &)> CB, Function *F) { 2048855fec3SJohannes Doerfert SmallVector<unsigned, 8> ToBeDeleted; 2059548b74aSJohannes Doerfert ToBeDeleted.clear(); 206e099c7b6Ssstefan1 2078855fec3SJohannes Doerfert unsigned Idx = 0; 208624d34afSJohannes Doerfert UseVector &UV = getOrCreateUseVector(F); 209e099c7b6Ssstefan1 2108855fec3SJohannes Doerfert for (Use *U : UV) { 211e099c7b6Ssstefan1 if (CB(*U, *F)) 2128855fec3SJohannes Doerfert ToBeDeleted.push_back(Idx); 2138855fec3SJohannes Doerfert ++Idx; 2148855fec3SJohannes Doerfert } 2158855fec3SJohannes Doerfert 2168855fec3SJohannes Doerfert // Remove the to-be-deleted indices in reverse order as prior 217b726c557SJohannes Doerfert // modifications will not modify the smaller indices. 2188855fec3SJohannes Doerfert while (!ToBeDeleted.empty()) { 2198855fec3SJohannes Doerfert unsigned Idx = ToBeDeleted.pop_back_val(); 2208855fec3SJohannes Doerfert UV[Idx] = UV.back(); 2218855fec3SJohannes Doerfert UV.pop_back(); 2229548b74aSJohannes Doerfert } 2239548b74aSJohannes Doerfert } 2248855fec3SJohannes Doerfert 2258855fec3SJohannes Doerfert private: 2268855fec3SJohannes Doerfert /// Map from functions to all uses of this runtime function contained in 2278855fec3SJohannes Doerfert /// them. 228b8235d2bSsstefan1 DenseMap<Function *, std::shared_ptr<UseVector>> UsesMap; 2299548b74aSJohannes Doerfert }; 2309548b74aSJohannes Doerfert 231624d34afSJohannes Doerfert /// Initialize the ModuleSlice member based on \p SCC. ModuleSlices contains 232624d34afSJohannes Doerfert /// (a subset of) all functions that we can look at during this SCC traversal. 233624d34afSJohannes Doerfert /// This includes functions (transitively) called from the SCC and the 234624d34afSJohannes Doerfert /// (transitive) callers of SCC functions. We also can look at a function if 235624d34afSJohannes Doerfert /// there is a "reference edge", i.a., if the function somehow uses (!=calls) 236624d34afSJohannes Doerfert /// a function in the SCC or a caller of a function in the SCC. 237624d34afSJohannes Doerfert void initializeModuleSlice(SetVector<Function *> &SCC) { 238624d34afSJohannes Doerfert ModuleSlice.insert(SCC.begin(), SCC.end()); 239624d34afSJohannes Doerfert 240624d34afSJohannes Doerfert SmallPtrSet<Function *, 16> Seen; 241624d34afSJohannes Doerfert SmallVector<Function *, 16> Worklist(SCC.begin(), SCC.end()); 242624d34afSJohannes Doerfert while (!Worklist.empty()) { 243624d34afSJohannes Doerfert Function *F = Worklist.pop_back_val(); 244624d34afSJohannes Doerfert ModuleSlice.insert(F); 245624d34afSJohannes Doerfert 246624d34afSJohannes Doerfert for (Instruction &I : instructions(*F)) 247624d34afSJohannes Doerfert if (auto *CB = dyn_cast<CallBase>(&I)) 248624d34afSJohannes Doerfert if (Function *Callee = CB->getCalledFunction()) 249624d34afSJohannes Doerfert if (Seen.insert(Callee).second) 250624d34afSJohannes Doerfert Worklist.push_back(Callee); 251624d34afSJohannes Doerfert } 252624d34afSJohannes Doerfert 253624d34afSJohannes Doerfert Seen.clear(); 254624d34afSJohannes Doerfert Worklist.append(SCC.begin(), SCC.end()); 255624d34afSJohannes Doerfert while (!Worklist.empty()) { 256624d34afSJohannes Doerfert Function *F = Worklist.pop_back_val(); 257624d34afSJohannes Doerfert ModuleSlice.insert(F); 258624d34afSJohannes Doerfert 259624d34afSJohannes Doerfert // Traverse all transitive uses. 260624d34afSJohannes Doerfert foreachUse(*F, [&](Use &U) { 261624d34afSJohannes Doerfert if (auto *UsrI = dyn_cast<Instruction>(U.getUser())) 262624d34afSJohannes Doerfert if (Seen.insert(UsrI->getFunction()).second) 263624d34afSJohannes Doerfert Worklist.push_back(UsrI->getFunction()); 264624d34afSJohannes Doerfert }); 265624d34afSJohannes Doerfert } 266624d34afSJohannes Doerfert } 267624d34afSJohannes Doerfert 2687cfd267cSsstefan1 /// The slice of the module we are allowed to look at. 269624d34afSJohannes Doerfert SmallPtrSet<Function *, 8> ModuleSlice; 2707cfd267cSsstefan1 2717cfd267cSsstefan1 /// An OpenMP-IR-Builder instance 2727cfd267cSsstefan1 OpenMPIRBuilder OMPBuilder; 2737cfd267cSsstefan1 2747cfd267cSsstefan1 /// Map from runtime function kind to the runtime function description. 2757cfd267cSsstefan1 EnumeratedArray<RuntimeFunctionInfo, RuntimeFunction, 2767cfd267cSsstefan1 RuntimeFunction::OMPRTL___last> 2777cfd267cSsstefan1 RFIs; 2787cfd267cSsstefan1 2790f426935Ssstefan1 /// Map from ICV kind to the ICV description. 2800f426935Ssstefan1 EnumeratedArray<InternalControlVarInfo, InternalControlVar, 2810f426935Ssstefan1 InternalControlVar::ICV___last> 2820f426935Ssstefan1 ICVs; 2830f426935Ssstefan1 2840f426935Ssstefan1 /// Helper to initialize all internal control variable information for those 2850f426935Ssstefan1 /// defined in OMPKinds.def. 2860f426935Ssstefan1 void initializeInternalControlVars() { 2870f426935Ssstefan1 #define ICV_RT_SET(_Name, RTL) \ 2880f426935Ssstefan1 { \ 2890f426935Ssstefan1 auto &ICV = ICVs[_Name]; \ 2900f426935Ssstefan1 ICV.Setter = RTL; \ 2910f426935Ssstefan1 } 2920f426935Ssstefan1 #define ICV_RT_GET(Name, RTL) \ 2930f426935Ssstefan1 { \ 2940f426935Ssstefan1 auto &ICV = ICVs[Name]; \ 2950f426935Ssstefan1 ICV.Getter = RTL; \ 2960f426935Ssstefan1 } 2970f426935Ssstefan1 #define ICV_DATA_ENV(Enum, _Name, _EnvVarName, Init) \ 2980f426935Ssstefan1 { \ 2990f426935Ssstefan1 auto &ICV = ICVs[Enum]; \ 3000f426935Ssstefan1 ICV.Name = _Name; \ 3010f426935Ssstefan1 ICV.Kind = Enum; \ 3020f426935Ssstefan1 ICV.InitKind = Init; \ 3030f426935Ssstefan1 ICV.EnvVarName = _EnvVarName; \ 3040f426935Ssstefan1 switch (ICV.InitKind) { \ 305951e43f3Ssstefan1 case ICV_IMPLEMENTATION_DEFINED: \ 3060f426935Ssstefan1 ICV.InitValue = nullptr; \ 3070f426935Ssstefan1 break; \ 308951e43f3Ssstefan1 case ICV_ZERO: \ 3096aab27baSsstefan1 ICV.InitValue = ConstantInt::get( \ 3106aab27baSsstefan1 Type::getInt32Ty(OMPBuilder.Int32->getContext()), 0); \ 3110f426935Ssstefan1 break; \ 312951e43f3Ssstefan1 case ICV_FALSE: \ 3136aab27baSsstefan1 ICV.InitValue = ConstantInt::getFalse(OMPBuilder.Int1->getContext()); \ 3140f426935Ssstefan1 break; \ 315951e43f3Ssstefan1 case ICV_LAST: \ 3160f426935Ssstefan1 break; \ 3170f426935Ssstefan1 } \ 3180f426935Ssstefan1 } 3190f426935Ssstefan1 #include "llvm/Frontend/OpenMP/OMPKinds.def" 3200f426935Ssstefan1 } 3210f426935Ssstefan1 3227cfd267cSsstefan1 /// Returns true if the function declaration \p F matches the runtime 3237cfd267cSsstefan1 /// function types, that is, return type \p RTFRetType, and argument types 3247cfd267cSsstefan1 /// \p RTFArgTypes. 3257cfd267cSsstefan1 static bool declMatchesRTFTypes(Function *F, Type *RTFRetType, 3267cfd267cSsstefan1 SmallVector<Type *, 8> &RTFArgTypes) { 3277cfd267cSsstefan1 // TODO: We should output information to the user (under debug output 3287cfd267cSsstefan1 // and via remarks). 3297cfd267cSsstefan1 3307cfd267cSsstefan1 if (!F) 3317cfd267cSsstefan1 return false; 3327cfd267cSsstefan1 if (F->getReturnType() != RTFRetType) 3337cfd267cSsstefan1 return false; 3347cfd267cSsstefan1 if (F->arg_size() != RTFArgTypes.size()) 3357cfd267cSsstefan1 return false; 3367cfd267cSsstefan1 3377cfd267cSsstefan1 auto RTFTyIt = RTFArgTypes.begin(); 3387cfd267cSsstefan1 for (Argument &Arg : F->args()) { 3397cfd267cSsstefan1 if (Arg.getType() != *RTFTyIt) 3407cfd267cSsstefan1 return false; 3417cfd267cSsstefan1 3427cfd267cSsstefan1 ++RTFTyIt; 3437cfd267cSsstefan1 } 3447cfd267cSsstefan1 3457cfd267cSsstefan1 return true; 3467cfd267cSsstefan1 } 3477cfd267cSsstefan1 348b726c557SJohannes Doerfert // Helper to collect all uses of the declaration in the UsesMap. 349b8235d2bSsstefan1 unsigned collectUses(RuntimeFunctionInfo &RFI, bool CollectStats = true) { 3507cfd267cSsstefan1 unsigned NumUses = 0; 3517cfd267cSsstefan1 if (!RFI.Declaration) 3527cfd267cSsstefan1 return NumUses; 3537cfd267cSsstefan1 OMPBuilder.addAttributes(RFI.Kind, *RFI.Declaration); 3547cfd267cSsstefan1 355b8235d2bSsstefan1 if (CollectStats) { 3567cfd267cSsstefan1 NumOpenMPRuntimeFunctionsIdentified += 1; 3577cfd267cSsstefan1 NumOpenMPRuntimeFunctionUsesIdentified += RFI.Declaration->getNumUses(); 358b8235d2bSsstefan1 } 3597cfd267cSsstefan1 3607cfd267cSsstefan1 // TODO: We directly convert uses into proper calls and unknown uses. 3617cfd267cSsstefan1 for (Use &U : RFI.Declaration->uses()) { 3627cfd267cSsstefan1 if (Instruction *UserI = dyn_cast<Instruction>(U.getUser())) { 3637cfd267cSsstefan1 if (ModuleSlice.count(UserI->getFunction())) { 3647cfd267cSsstefan1 RFI.getOrCreateUseVector(UserI->getFunction()).push_back(&U); 3657cfd267cSsstefan1 ++NumUses; 3667cfd267cSsstefan1 } 3677cfd267cSsstefan1 } else { 3687cfd267cSsstefan1 RFI.getOrCreateUseVector(nullptr).push_back(&U); 3697cfd267cSsstefan1 ++NumUses; 3707cfd267cSsstefan1 } 3717cfd267cSsstefan1 } 3727cfd267cSsstefan1 return NumUses; 373b8235d2bSsstefan1 } 3747cfd267cSsstefan1 375b8235d2bSsstefan1 // Helper function to recollect uses of all runtime functions. 376b8235d2bSsstefan1 void recollectUses() { 377b8235d2bSsstefan1 for (int Idx = 0; Idx < RFIs.size(); ++Idx) { 378b8235d2bSsstefan1 auto &RFI = RFIs[static_cast<RuntimeFunction>(Idx)]; 379b8235d2bSsstefan1 RFI.clearUsesMap(); 380b8235d2bSsstefan1 collectUses(RFI, /*CollectStats*/ false); 381b8235d2bSsstefan1 } 382b8235d2bSsstefan1 } 383b8235d2bSsstefan1 384b8235d2bSsstefan1 /// Helper to initialize all runtime function information for those defined 385b8235d2bSsstefan1 /// in OpenMPKinds.def. 386b8235d2bSsstefan1 void initializeRuntimeFunctions() { 3877cfd267cSsstefan1 Module &M = *((*ModuleSlice.begin())->getParent()); 3887cfd267cSsstefan1 3896aab27baSsstefan1 // Helper macros for handling __VA_ARGS__ in OMP_RTL 3906aab27baSsstefan1 #define OMP_TYPE(VarName, ...) \ 3916aab27baSsstefan1 Type *VarName = OMPBuilder.VarName; \ 3926aab27baSsstefan1 (void)VarName; 3936aab27baSsstefan1 3946aab27baSsstefan1 #define OMP_ARRAY_TYPE(VarName, ...) \ 3956aab27baSsstefan1 ArrayType *VarName##Ty = OMPBuilder.VarName##Ty; \ 3966aab27baSsstefan1 (void)VarName##Ty; \ 3976aab27baSsstefan1 PointerType *VarName##PtrTy = OMPBuilder.VarName##PtrTy; \ 3986aab27baSsstefan1 (void)VarName##PtrTy; 3996aab27baSsstefan1 4006aab27baSsstefan1 #define OMP_FUNCTION_TYPE(VarName, ...) \ 4016aab27baSsstefan1 FunctionType *VarName = OMPBuilder.VarName; \ 4026aab27baSsstefan1 (void)VarName; \ 4036aab27baSsstefan1 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \ 4046aab27baSsstefan1 (void)VarName##Ptr; 4056aab27baSsstefan1 4066aab27baSsstefan1 #define OMP_STRUCT_TYPE(VarName, ...) \ 4076aab27baSsstefan1 StructType *VarName = OMPBuilder.VarName; \ 4086aab27baSsstefan1 (void)VarName; \ 4096aab27baSsstefan1 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \ 4106aab27baSsstefan1 (void)VarName##Ptr; 4116aab27baSsstefan1 4127cfd267cSsstefan1 #define OMP_RTL(_Enum, _Name, _IsVarArg, _ReturnType, ...) \ 4137cfd267cSsstefan1 { \ 4147cfd267cSsstefan1 SmallVector<Type *, 8> ArgsTypes({__VA_ARGS__}); \ 4157cfd267cSsstefan1 Function *F = M.getFunction(_Name); \ 4166aab27baSsstefan1 if (declMatchesRTFTypes(F, OMPBuilder._ReturnType, ArgsTypes)) { \ 4177cfd267cSsstefan1 auto &RFI = RFIs[_Enum]; \ 4187cfd267cSsstefan1 RFI.Kind = _Enum; \ 4197cfd267cSsstefan1 RFI.Name = _Name; \ 4207cfd267cSsstefan1 RFI.IsVarArg = _IsVarArg; \ 4216aab27baSsstefan1 RFI.ReturnType = OMPBuilder._ReturnType; \ 4227cfd267cSsstefan1 RFI.ArgumentTypes = std::move(ArgsTypes); \ 4237cfd267cSsstefan1 RFI.Declaration = F; \ 424b8235d2bSsstefan1 unsigned NumUses = collectUses(RFI); \ 4257cfd267cSsstefan1 (void)NumUses; \ 4267cfd267cSsstefan1 LLVM_DEBUG({ \ 4277cfd267cSsstefan1 dbgs() << TAG << RFI.Name << (RFI.Declaration ? "" : " not") \ 4287cfd267cSsstefan1 << " found\n"; \ 4297cfd267cSsstefan1 if (RFI.Declaration) \ 4307cfd267cSsstefan1 dbgs() << TAG << "-> got " << NumUses << " uses in " \ 4317cfd267cSsstefan1 << RFI.getNumFunctionsWithUses() \ 4327cfd267cSsstefan1 << " different functions.\n"; \ 4337cfd267cSsstefan1 }); \ 4347cfd267cSsstefan1 } \ 4357cfd267cSsstefan1 } 4367cfd267cSsstefan1 #include "llvm/Frontend/OpenMP/OMPKinds.def" 4377cfd267cSsstefan1 4387cfd267cSsstefan1 // TODO: We should attach the attributes defined in OMPKinds.def. 4397cfd267cSsstefan1 } 440e8039ad4SJohannes Doerfert 441e8039ad4SJohannes Doerfert /// Collection of known kernels (\see Kernel) in the module. 442e8039ad4SJohannes Doerfert SmallPtrSetImpl<Kernel> &Kernels; 4437cfd267cSsstefan1 }; 4447cfd267cSsstefan1 4457cfd267cSsstefan1 struct OpenMPOpt { 4467cfd267cSsstefan1 4477cfd267cSsstefan1 using OptimizationRemarkGetter = 4487cfd267cSsstefan1 function_ref<OptimizationRemarkEmitter &(Function *)>; 4497cfd267cSsstefan1 4507cfd267cSsstefan1 OpenMPOpt(SmallVectorImpl<Function *> &SCC, CallGraphUpdater &CGUpdater, 4517cfd267cSsstefan1 OptimizationRemarkGetter OREGetter, 452b8235d2bSsstefan1 OMPInformationCache &OMPInfoCache, Attributor &A) 45377b79d79SMehdi Amini : M(*(*SCC.begin())->getParent()), SCC(SCC), CGUpdater(CGUpdater), 454b8235d2bSsstefan1 OREGetter(OREGetter), OMPInfoCache(OMPInfoCache), A(A) {} 4557cfd267cSsstefan1 4569548b74aSJohannes Doerfert /// Run all OpenMP optimizations on the underlying SCC/ModuleSlice. 4579548b74aSJohannes Doerfert bool run() { 45854bd3751SJohannes Doerfert if (SCC.empty()) 45954bd3751SJohannes Doerfert return false; 46054bd3751SJohannes Doerfert 4619548b74aSJohannes Doerfert bool Changed = false; 4629548b74aSJohannes Doerfert 4639548b74aSJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "Run on SCC with " << SCC.size() 46477b79d79SMehdi Amini << " functions in a slice with " 46577b79d79SMehdi Amini << OMPInfoCache.ModuleSlice.size() << " functions\n"); 4669548b74aSJohannes Doerfert 467e8039ad4SJohannes Doerfert if (PrintICVValues) 468e8039ad4SJohannes Doerfert printICVs(); 469e8039ad4SJohannes Doerfert if (PrintOpenMPKernels) 470e8039ad4SJohannes Doerfert printKernels(); 471e8039ad4SJohannes Doerfert 4725b0581aeSJohannes Doerfert Changed |= rewriteDeviceCodeStateMachine(); 4735b0581aeSJohannes Doerfert 474e8039ad4SJohannes Doerfert Changed |= runAttributor(); 475e8039ad4SJohannes Doerfert 476e8039ad4SJohannes Doerfert // Recollect uses, in case Attributor deleted any. 477e8039ad4SJohannes Doerfert OMPInfoCache.recollectUses(); 478e8039ad4SJohannes Doerfert 479e8039ad4SJohannes Doerfert Changed |= deduplicateRuntimeCalls(); 480e8039ad4SJohannes Doerfert Changed |= deleteParallelRegions(); 481496f8e5bSHamilton Tobon Mosquera if (HideMemoryTransferLatency) 482496f8e5bSHamilton Tobon Mosquera Changed |= hideMemTransfersLatency(); 483e8039ad4SJohannes Doerfert 484e8039ad4SJohannes Doerfert return Changed; 485e8039ad4SJohannes Doerfert } 486e8039ad4SJohannes Doerfert 4870f426935Ssstefan1 /// Print initial ICV values for testing. 4880f426935Ssstefan1 /// FIXME: This should be done from the Attributor once it is added. 489e8039ad4SJohannes Doerfert void printICVs() const { 4900f426935Ssstefan1 InternalControlVar ICVs[] = {ICV_nthreads, ICV_active_levels, ICV_cancel}; 4910f426935Ssstefan1 4920f426935Ssstefan1 for (Function *F : OMPInfoCache.ModuleSlice) { 4930f426935Ssstefan1 for (auto ICV : ICVs) { 4940f426935Ssstefan1 auto ICVInfo = OMPInfoCache.ICVs[ICV]; 4950f426935Ssstefan1 auto Remark = [&](OptimizationRemark OR) { 4960f426935Ssstefan1 return OR << "OpenMP ICV " << ore::NV("OpenMPICV", ICVInfo.Name) 4970f426935Ssstefan1 << " Value: " 4980f426935Ssstefan1 << (ICVInfo.InitValue 4990f426935Ssstefan1 ? ICVInfo.InitValue->getValue().toString(10, true) 5000f426935Ssstefan1 : "IMPLEMENTATION_DEFINED"); 5010f426935Ssstefan1 }; 5020f426935Ssstefan1 5030f426935Ssstefan1 emitRemarkOnFunction(F, "OpenMPICVTracker", Remark); 5040f426935Ssstefan1 } 5050f426935Ssstefan1 } 5060f426935Ssstefan1 } 5070f426935Ssstefan1 508e8039ad4SJohannes Doerfert /// Print OpenMP GPU kernels for testing. 509e8039ad4SJohannes Doerfert void printKernels() const { 510e8039ad4SJohannes Doerfert for (Function *F : SCC) { 511e8039ad4SJohannes Doerfert if (!OMPInfoCache.Kernels.count(F)) 512e8039ad4SJohannes Doerfert continue; 513b8235d2bSsstefan1 514e8039ad4SJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 515e8039ad4SJohannes Doerfert return OR << "OpenMP GPU kernel " 516e8039ad4SJohannes Doerfert << ore::NV("OpenMPGPUKernel", F->getName()) << "\n"; 517e8039ad4SJohannes Doerfert }; 518b8235d2bSsstefan1 519e8039ad4SJohannes Doerfert emitRemarkOnFunction(F, "OpenMPGPU", Remark); 520e8039ad4SJohannes Doerfert } 5219548b74aSJohannes Doerfert } 5229548b74aSJohannes Doerfert 5237cfd267cSsstefan1 /// Return the call if \p U is a callee use in a regular call. If \p RFI is 5247cfd267cSsstefan1 /// given it has to be the callee or a nullptr is returned. 5257cfd267cSsstefan1 static CallInst *getCallIfRegularCall( 5267cfd267cSsstefan1 Use &U, OMPInformationCache::RuntimeFunctionInfo *RFI = nullptr) { 5277cfd267cSsstefan1 CallInst *CI = dyn_cast<CallInst>(U.getUser()); 5287cfd267cSsstefan1 if (CI && CI->isCallee(&U) && !CI->hasOperandBundles() && 5297cfd267cSsstefan1 (!RFI || CI->getCalledFunction() == RFI->Declaration)) 5307cfd267cSsstefan1 return CI; 5317cfd267cSsstefan1 return nullptr; 5327cfd267cSsstefan1 } 5337cfd267cSsstefan1 5347cfd267cSsstefan1 /// Return the call if \p V is a regular call. If \p RFI is given it has to be 5357cfd267cSsstefan1 /// the callee or a nullptr is returned. 5367cfd267cSsstefan1 static CallInst *getCallIfRegularCall( 5377cfd267cSsstefan1 Value &V, OMPInformationCache::RuntimeFunctionInfo *RFI = nullptr) { 5387cfd267cSsstefan1 CallInst *CI = dyn_cast<CallInst>(&V); 5397cfd267cSsstefan1 if (CI && !CI->hasOperandBundles() && 5407cfd267cSsstefan1 (!RFI || CI->getCalledFunction() == RFI->Declaration)) 5417cfd267cSsstefan1 return CI; 5427cfd267cSsstefan1 return nullptr; 5437cfd267cSsstefan1 } 5447cfd267cSsstefan1 5459548b74aSJohannes Doerfert private: 5469d38f98dSJohannes Doerfert /// Try to delete parallel regions if possible. 547e565db49SJohannes Doerfert bool deleteParallelRegions() { 548e565db49SJohannes Doerfert const unsigned CallbackCalleeOperand = 2; 549e565db49SJohannes Doerfert 5507cfd267cSsstefan1 OMPInformationCache::RuntimeFunctionInfo &RFI = 5517cfd267cSsstefan1 OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call]; 5527cfd267cSsstefan1 553e565db49SJohannes Doerfert if (!RFI.Declaration) 554e565db49SJohannes Doerfert return false; 555e565db49SJohannes Doerfert 556e565db49SJohannes Doerfert bool Changed = false; 557e565db49SJohannes Doerfert auto DeleteCallCB = [&](Use &U, Function &) { 558e565db49SJohannes Doerfert CallInst *CI = getCallIfRegularCall(U); 559e565db49SJohannes Doerfert if (!CI) 560e565db49SJohannes Doerfert return false; 561e565db49SJohannes Doerfert auto *Fn = dyn_cast<Function>( 562e565db49SJohannes Doerfert CI->getArgOperand(CallbackCalleeOperand)->stripPointerCasts()); 563e565db49SJohannes Doerfert if (!Fn) 564e565db49SJohannes Doerfert return false; 565e565db49SJohannes Doerfert if (!Fn->onlyReadsMemory()) 566e565db49SJohannes Doerfert return false; 567e565db49SJohannes Doerfert if (!Fn->hasFnAttribute(Attribute::WillReturn)) 568e565db49SJohannes Doerfert return false; 569e565db49SJohannes Doerfert 570e565db49SJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "Delete read-only parallel region in " 571e565db49SJohannes Doerfert << CI->getCaller()->getName() << "\n"); 5724d4ea9acSHuber, Joseph 5734d4ea9acSHuber, Joseph auto Remark = [&](OptimizationRemark OR) { 5744d4ea9acSHuber, Joseph return OR << "Parallel region in " 5754d4ea9acSHuber, Joseph << ore::NV("OpenMPParallelDelete", CI->getCaller()->getName()) 5764d4ea9acSHuber, Joseph << " deleted"; 5774d4ea9acSHuber, Joseph }; 5784d4ea9acSHuber, Joseph emitRemark<OptimizationRemark>(CI, "OpenMPParallelRegionDeletion", 5794d4ea9acSHuber, Joseph Remark); 5804d4ea9acSHuber, Joseph 581e565db49SJohannes Doerfert CGUpdater.removeCallSite(*CI); 582e565db49SJohannes Doerfert CI->eraseFromParent(); 583e565db49SJohannes Doerfert Changed = true; 58455eb714aSRoman Lebedev ++NumOpenMPParallelRegionsDeleted; 585e565db49SJohannes Doerfert return true; 586e565db49SJohannes Doerfert }; 587e565db49SJohannes Doerfert 588624d34afSJohannes Doerfert RFI.foreachUse(SCC, DeleteCallCB); 589e565db49SJohannes Doerfert 590e565db49SJohannes Doerfert return Changed; 591e565db49SJohannes Doerfert } 592e565db49SJohannes Doerfert 593b726c557SJohannes Doerfert /// Try to eliminate runtime calls by reusing existing ones. 5949548b74aSJohannes Doerfert bool deduplicateRuntimeCalls() { 5959548b74aSJohannes Doerfert bool Changed = false; 5969548b74aSJohannes Doerfert 597e28936f6SJohannes Doerfert RuntimeFunction DeduplicableRuntimeCallIDs[] = { 598e28936f6SJohannes Doerfert OMPRTL_omp_get_num_threads, 599e28936f6SJohannes Doerfert OMPRTL_omp_in_parallel, 600e28936f6SJohannes Doerfert OMPRTL_omp_get_cancellation, 601e28936f6SJohannes Doerfert OMPRTL_omp_get_thread_limit, 602e28936f6SJohannes Doerfert OMPRTL_omp_get_supported_active_levels, 603e28936f6SJohannes Doerfert OMPRTL_omp_get_level, 604e28936f6SJohannes Doerfert OMPRTL_omp_get_ancestor_thread_num, 605e28936f6SJohannes Doerfert OMPRTL_omp_get_team_size, 606e28936f6SJohannes Doerfert OMPRTL_omp_get_active_level, 607e28936f6SJohannes Doerfert OMPRTL_omp_in_final, 608e28936f6SJohannes Doerfert OMPRTL_omp_get_proc_bind, 609e28936f6SJohannes Doerfert OMPRTL_omp_get_num_places, 610e28936f6SJohannes Doerfert OMPRTL_omp_get_num_procs, 611e28936f6SJohannes Doerfert OMPRTL_omp_get_place_num, 612e28936f6SJohannes Doerfert OMPRTL_omp_get_partition_num_places, 613e28936f6SJohannes Doerfert OMPRTL_omp_get_partition_place_nums}; 614e28936f6SJohannes Doerfert 615bc93c2d7SMarek Kurdej // Global-tid is handled separately. 6169548b74aSJohannes Doerfert SmallSetVector<Value *, 16> GTIdArgs; 6179548b74aSJohannes Doerfert collectGlobalThreadIdArguments(GTIdArgs); 6189548b74aSJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "Found " << GTIdArgs.size() 6199548b74aSJohannes Doerfert << " global thread ID arguments\n"); 6209548b74aSJohannes Doerfert 6219548b74aSJohannes Doerfert for (Function *F : SCC) { 622e28936f6SJohannes Doerfert for (auto DeduplicableRuntimeCallID : DeduplicableRuntimeCallIDs) 6237cfd267cSsstefan1 deduplicateRuntimeCalls(*F, 6247cfd267cSsstefan1 OMPInfoCache.RFIs[DeduplicableRuntimeCallID]); 625e28936f6SJohannes Doerfert 626e28936f6SJohannes Doerfert // __kmpc_global_thread_num is special as we can replace it with an 627e28936f6SJohannes Doerfert // argument in enough cases to make it worth trying. 6289548b74aSJohannes Doerfert Value *GTIdArg = nullptr; 6299548b74aSJohannes Doerfert for (Argument &Arg : F->args()) 6309548b74aSJohannes Doerfert if (GTIdArgs.count(&Arg)) { 6319548b74aSJohannes Doerfert GTIdArg = &Arg; 6329548b74aSJohannes Doerfert break; 6339548b74aSJohannes Doerfert } 6349548b74aSJohannes Doerfert Changed |= deduplicateRuntimeCalls( 6357cfd267cSsstefan1 *F, OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num], GTIdArg); 6369548b74aSJohannes Doerfert } 6379548b74aSJohannes Doerfert 6389548b74aSJohannes Doerfert return Changed; 6399548b74aSJohannes Doerfert } 6409548b74aSJohannes Doerfert 641496f8e5bSHamilton Tobon Mosquera /// Tries to hide the latency of runtime calls that involve host to 642496f8e5bSHamilton Tobon Mosquera /// device memory transfers by splitting them into their "issue" and "wait" 643496f8e5bSHamilton Tobon Mosquera /// versions. The "issue" is moved upwards as much as possible. The "wait" is 644496f8e5bSHamilton Tobon Mosquera /// moved downards as much as possible. The "issue" issues the memory transfer 645496f8e5bSHamilton Tobon Mosquera /// asynchronously, returning a handle. The "wait" waits in the returned 646496f8e5bSHamilton Tobon Mosquera /// handle for the memory transfer to finish. 647496f8e5bSHamilton Tobon Mosquera bool hideMemTransfersLatency() { 648496f8e5bSHamilton Tobon Mosquera auto &RFI = OMPInfoCache.RFIs[OMPRTL___tgt_target_data_begin_mapper]; 649496f8e5bSHamilton Tobon Mosquera bool Changed = false; 650496f8e5bSHamilton Tobon Mosquera auto SplitMemTransfers = [&](Use &U, Function &Decl) { 651496f8e5bSHamilton Tobon Mosquera auto *RTCall = getCallIfRegularCall(U, &RFI); 652496f8e5bSHamilton Tobon Mosquera if (!RTCall) 653496f8e5bSHamilton Tobon Mosquera return false; 654496f8e5bSHamilton Tobon Mosquera 655496f8e5bSHamilton Tobon Mosquera bool WasSplit = splitTargetDataBeginRTC(RTCall); 656496f8e5bSHamilton Tobon Mosquera Changed |= WasSplit; 657496f8e5bSHamilton Tobon Mosquera return WasSplit; 658496f8e5bSHamilton Tobon Mosquera }; 659496f8e5bSHamilton Tobon Mosquera RFI.foreachUse(SCC, SplitMemTransfers); 660496f8e5bSHamilton Tobon Mosquera 661496f8e5bSHamilton Tobon Mosquera return Changed; 662496f8e5bSHamilton Tobon Mosquera } 663496f8e5bSHamilton Tobon Mosquera 664496f8e5bSHamilton Tobon Mosquera /// Splits \p RuntimeCall into its "issue" and "wait" counterparts. 665496f8e5bSHamilton Tobon Mosquera bool splitTargetDataBeginRTC(CallInst *RuntimeCall) { 666496f8e5bSHamilton Tobon Mosquera auto &IRBuilder = OMPInfoCache.OMPBuilder; 667496f8e5bSHamilton Tobon Mosquera // Add "issue" runtime call declaration: 668496f8e5bSHamilton Tobon Mosquera // declare %struct.tgt_async_info @__tgt_target_data_begin_issue(i64, i32, 669496f8e5bSHamilton Tobon Mosquera // i8**, i8**, i64*, i64*) 670496f8e5bSHamilton Tobon Mosquera FunctionCallee IssueDecl = IRBuilder.getOrCreateRuntimeFunction( 671496f8e5bSHamilton Tobon Mosquera M, OMPRTL___tgt_target_data_begin_mapper_issue); 672496f8e5bSHamilton Tobon Mosquera 673496f8e5bSHamilton Tobon Mosquera // Change RuntimeCall call site for its asynchronous version. 674496f8e5bSHamilton Tobon Mosquera SmallVector<Value *, 8> Args; 675496f8e5bSHamilton Tobon Mosquera for (auto &Arg : RuntimeCall->args()) 676496f8e5bSHamilton Tobon Mosquera Args.push_back(Arg.get()); 677496f8e5bSHamilton Tobon Mosquera 678496f8e5bSHamilton Tobon Mosquera CallInst *IssueCallsite = 679496f8e5bSHamilton Tobon Mosquera CallInst::Create(IssueDecl, Args, "handle", RuntimeCall); 680496f8e5bSHamilton Tobon Mosquera RuntimeCall->eraseFromParent(); 681496f8e5bSHamilton Tobon Mosquera 682496f8e5bSHamilton Tobon Mosquera // Add "wait" runtime call declaration: 683496f8e5bSHamilton Tobon Mosquera // declare void @__tgt_target_data_begin_wait(i64, %struct.__tgt_async_info) 684496f8e5bSHamilton Tobon Mosquera FunctionCallee WaitDecl = IRBuilder.getOrCreateRuntimeFunction( 685496f8e5bSHamilton Tobon Mosquera M, OMPRTL___tgt_target_data_begin_mapper_wait); 686496f8e5bSHamilton Tobon Mosquera 687496f8e5bSHamilton Tobon Mosquera // Add call site to WaitDecl. 688496f8e5bSHamilton Tobon Mosquera Value *WaitParams[2] = { 689496f8e5bSHamilton Tobon Mosquera IssueCallsite->getArgOperand(0), // device_id. 690496f8e5bSHamilton Tobon Mosquera IssueCallsite // returned handle. 691496f8e5bSHamilton Tobon Mosquera }; 692496f8e5bSHamilton Tobon Mosquera CallInst::Create(WaitDecl, WaitParams, /*NameStr=*/"", 693496f8e5bSHamilton Tobon Mosquera IssueCallsite->getNextNode()); 694496f8e5bSHamilton Tobon Mosquera 695496f8e5bSHamilton Tobon Mosquera return true; 696496f8e5bSHamilton Tobon Mosquera } 697496f8e5bSHamilton Tobon Mosquera 698dc3b5b00SJohannes Doerfert static Value *combinedIdentStruct(Value *CurrentIdent, Value *NextIdent, 699dc3b5b00SJohannes Doerfert bool GlobalOnly, bool &SingleChoice) { 700dc3b5b00SJohannes Doerfert if (CurrentIdent == NextIdent) 701dc3b5b00SJohannes Doerfert return CurrentIdent; 702dc3b5b00SJohannes Doerfert 703396b7253SJohannes Doerfert // TODO: Figure out how to actually combine multiple debug locations. For 704dc3b5b00SJohannes Doerfert // now we just keep an existing one if there is a single choice. 705dc3b5b00SJohannes Doerfert if (!GlobalOnly || isa<GlobalValue>(NextIdent)) { 706dc3b5b00SJohannes Doerfert SingleChoice = !CurrentIdent; 707dc3b5b00SJohannes Doerfert return NextIdent; 708dc3b5b00SJohannes Doerfert } 709396b7253SJohannes Doerfert return nullptr; 710396b7253SJohannes Doerfert } 711396b7253SJohannes Doerfert 712396b7253SJohannes Doerfert /// Return an `struct ident_t*` value that represents the ones used in the 713396b7253SJohannes Doerfert /// calls of \p RFI inside of \p F. If \p GlobalOnly is true, we will not 714396b7253SJohannes Doerfert /// return a local `struct ident_t*`. For now, if we cannot find a suitable 715396b7253SJohannes Doerfert /// return value we create one from scratch. We also do not yet combine 716396b7253SJohannes Doerfert /// information, e.g., the source locations, see combinedIdentStruct. 7177cfd267cSsstefan1 Value * 7187cfd267cSsstefan1 getCombinedIdentFromCallUsesIn(OMPInformationCache::RuntimeFunctionInfo &RFI, 7197cfd267cSsstefan1 Function &F, bool GlobalOnly) { 720dc3b5b00SJohannes Doerfert bool SingleChoice = true; 721396b7253SJohannes Doerfert Value *Ident = nullptr; 722396b7253SJohannes Doerfert auto CombineIdentStruct = [&](Use &U, Function &Caller) { 723396b7253SJohannes Doerfert CallInst *CI = getCallIfRegularCall(U, &RFI); 724396b7253SJohannes Doerfert if (!CI || &F != &Caller) 725396b7253SJohannes Doerfert return false; 726396b7253SJohannes Doerfert Ident = combinedIdentStruct(Ident, CI->getArgOperand(0), 727dc3b5b00SJohannes Doerfert /* GlobalOnly */ true, SingleChoice); 728396b7253SJohannes Doerfert return false; 729396b7253SJohannes Doerfert }; 730624d34afSJohannes Doerfert RFI.foreachUse(SCC, CombineIdentStruct); 731396b7253SJohannes Doerfert 732dc3b5b00SJohannes Doerfert if (!Ident || !SingleChoice) { 733396b7253SJohannes Doerfert // The IRBuilder uses the insertion block to get to the module, this is 734396b7253SJohannes Doerfert // unfortunate but we work around it for now. 7357cfd267cSsstefan1 if (!OMPInfoCache.OMPBuilder.getInsertionPoint().getBlock()) 7367cfd267cSsstefan1 OMPInfoCache.OMPBuilder.updateToLocation(OpenMPIRBuilder::InsertPointTy( 737396b7253SJohannes Doerfert &F.getEntryBlock(), F.getEntryBlock().begin())); 738396b7253SJohannes Doerfert // Create a fallback location if non was found. 739396b7253SJohannes Doerfert // TODO: Use the debug locations of the calls instead. 7407cfd267cSsstefan1 Constant *Loc = OMPInfoCache.OMPBuilder.getOrCreateDefaultSrcLocStr(); 7417cfd267cSsstefan1 Ident = OMPInfoCache.OMPBuilder.getOrCreateIdent(Loc); 742396b7253SJohannes Doerfert } 743396b7253SJohannes Doerfert return Ident; 744396b7253SJohannes Doerfert } 745396b7253SJohannes Doerfert 746b726c557SJohannes Doerfert /// Try to eliminate calls of \p RFI in \p F by reusing an existing one or 7479548b74aSJohannes Doerfert /// \p ReplVal if given. 7487cfd267cSsstefan1 bool deduplicateRuntimeCalls(Function &F, 7497cfd267cSsstefan1 OMPInformationCache::RuntimeFunctionInfo &RFI, 7509548b74aSJohannes Doerfert Value *ReplVal = nullptr) { 7518855fec3SJohannes Doerfert auto *UV = RFI.getUseVector(F); 7528855fec3SJohannes Doerfert if (!UV || UV->size() + (ReplVal != nullptr) < 2) 753b1fbf438SRoman Lebedev return false; 754b1fbf438SRoman Lebedev 7557cfd267cSsstefan1 LLVM_DEBUG( 7567cfd267cSsstefan1 dbgs() << TAG << "Deduplicate " << UV->size() << " uses of " << RFI.Name 7577cfd267cSsstefan1 << (ReplVal ? " with an existing value\n" : "\n") << "\n"); 7587cfd267cSsstefan1 759ab3da5ddSMichael Liao assert((!ReplVal || (isa<Argument>(ReplVal) && 760ab3da5ddSMichael Liao cast<Argument>(ReplVal)->getParent() == &F)) && 7619548b74aSJohannes Doerfert "Unexpected replacement value!"); 762396b7253SJohannes Doerfert 763396b7253SJohannes Doerfert // TODO: Use dominance to find a good position instead. 7646aab27baSsstefan1 auto CanBeMoved = [this](CallBase &CB) { 765396b7253SJohannes Doerfert unsigned NumArgs = CB.getNumArgOperands(); 766396b7253SJohannes Doerfert if (NumArgs == 0) 767396b7253SJohannes Doerfert return true; 7686aab27baSsstefan1 if (CB.getArgOperand(0)->getType() != OMPInfoCache.OMPBuilder.IdentPtr) 769396b7253SJohannes Doerfert return false; 770396b7253SJohannes Doerfert for (unsigned u = 1; u < NumArgs; ++u) 771396b7253SJohannes Doerfert if (isa<Instruction>(CB.getArgOperand(u))) 772396b7253SJohannes Doerfert return false; 773396b7253SJohannes Doerfert return true; 774396b7253SJohannes Doerfert }; 775396b7253SJohannes Doerfert 7769548b74aSJohannes Doerfert if (!ReplVal) { 7778855fec3SJohannes Doerfert for (Use *U : *UV) 7789548b74aSJohannes Doerfert if (CallInst *CI = getCallIfRegularCall(*U, &RFI)) { 779396b7253SJohannes Doerfert if (!CanBeMoved(*CI)) 780396b7253SJohannes Doerfert continue; 7814d4ea9acSHuber, Joseph 7824d4ea9acSHuber, Joseph auto Remark = [&](OptimizationRemark OR) { 7834d4ea9acSHuber, Joseph auto newLoc = &*F.getEntryBlock().getFirstInsertionPt(); 7844d4ea9acSHuber, Joseph return OR << "OpenMP runtime call " 7854d4ea9acSHuber, Joseph << ore::NV("OpenMPOptRuntime", RFI.Name) << " moved to " 7864d4ea9acSHuber, Joseph << ore::NV("OpenMPRuntimeMoves", newLoc->getDebugLoc()); 7874d4ea9acSHuber, Joseph }; 7884d4ea9acSHuber, Joseph emitRemark<OptimizationRemark>(CI, "OpenMPRuntimeCodeMotion", Remark); 7894d4ea9acSHuber, Joseph 7909548b74aSJohannes Doerfert CI->moveBefore(&*F.getEntryBlock().getFirstInsertionPt()); 7919548b74aSJohannes Doerfert ReplVal = CI; 7929548b74aSJohannes Doerfert break; 7939548b74aSJohannes Doerfert } 7949548b74aSJohannes Doerfert if (!ReplVal) 7959548b74aSJohannes Doerfert return false; 7969548b74aSJohannes Doerfert } 7979548b74aSJohannes Doerfert 798396b7253SJohannes Doerfert // If we use a call as a replacement value we need to make sure the ident is 799396b7253SJohannes Doerfert // valid at the new location. For now we just pick a global one, either 800396b7253SJohannes Doerfert // existing and used by one of the calls, or created from scratch. 801396b7253SJohannes Doerfert if (CallBase *CI = dyn_cast<CallBase>(ReplVal)) { 802396b7253SJohannes Doerfert if (CI->getNumArgOperands() > 0 && 8036aab27baSsstefan1 CI->getArgOperand(0)->getType() == OMPInfoCache.OMPBuilder.IdentPtr) { 804396b7253SJohannes Doerfert Value *Ident = getCombinedIdentFromCallUsesIn(RFI, F, 805396b7253SJohannes Doerfert /* GlobalOnly */ true); 806396b7253SJohannes Doerfert CI->setArgOperand(0, Ident); 807396b7253SJohannes Doerfert } 808396b7253SJohannes Doerfert } 809396b7253SJohannes Doerfert 8109548b74aSJohannes Doerfert bool Changed = false; 8119548b74aSJohannes Doerfert auto ReplaceAndDeleteCB = [&](Use &U, Function &Caller) { 8129548b74aSJohannes Doerfert CallInst *CI = getCallIfRegularCall(U, &RFI); 8139548b74aSJohannes Doerfert if (!CI || CI == ReplVal || &F != &Caller) 8149548b74aSJohannes Doerfert return false; 8159548b74aSJohannes Doerfert assert(CI->getCaller() == &F && "Unexpected call!"); 8164d4ea9acSHuber, Joseph 8174d4ea9acSHuber, Joseph auto Remark = [&](OptimizationRemark OR) { 8184d4ea9acSHuber, Joseph return OR << "OpenMP runtime call " 8194d4ea9acSHuber, Joseph << ore::NV("OpenMPOptRuntime", RFI.Name) << " deduplicated"; 8204d4ea9acSHuber, Joseph }; 8214d4ea9acSHuber, Joseph emitRemark<OptimizationRemark>(CI, "OpenMPRuntimeDeduplicated", Remark); 8224d4ea9acSHuber, Joseph 8239548b74aSJohannes Doerfert CGUpdater.removeCallSite(*CI); 8249548b74aSJohannes Doerfert CI->replaceAllUsesWith(ReplVal); 8259548b74aSJohannes Doerfert CI->eraseFromParent(); 8269548b74aSJohannes Doerfert ++NumOpenMPRuntimeCallsDeduplicated; 8279548b74aSJohannes Doerfert Changed = true; 8289548b74aSJohannes Doerfert return true; 8299548b74aSJohannes Doerfert }; 830624d34afSJohannes Doerfert RFI.foreachUse(SCC, ReplaceAndDeleteCB); 8319548b74aSJohannes Doerfert 8329548b74aSJohannes Doerfert return Changed; 8339548b74aSJohannes Doerfert } 8349548b74aSJohannes Doerfert 8359548b74aSJohannes Doerfert /// Collect arguments that represent the global thread id in \p GTIdArgs. 8369548b74aSJohannes Doerfert void collectGlobalThreadIdArguments(SmallSetVector<Value *, 16> >IdArgs) { 8379548b74aSJohannes Doerfert // TODO: Below we basically perform a fixpoint iteration with a pessimistic 8389548b74aSJohannes Doerfert // initialization. We could define an AbstractAttribute instead and 8399548b74aSJohannes Doerfert // run the Attributor here once it can be run as an SCC pass. 8409548b74aSJohannes Doerfert 8419548b74aSJohannes Doerfert // Helper to check the argument \p ArgNo at all call sites of \p F for 8429548b74aSJohannes Doerfert // a GTId. 8439548b74aSJohannes Doerfert auto CallArgOpIsGTId = [&](Function &F, unsigned ArgNo, CallInst &RefCI) { 8449548b74aSJohannes Doerfert if (!F.hasLocalLinkage()) 8459548b74aSJohannes Doerfert return false; 8469548b74aSJohannes Doerfert for (Use &U : F.uses()) { 8479548b74aSJohannes Doerfert if (CallInst *CI = getCallIfRegularCall(U)) { 8489548b74aSJohannes Doerfert Value *ArgOp = CI->getArgOperand(ArgNo); 8499548b74aSJohannes Doerfert if (CI == &RefCI || GTIdArgs.count(ArgOp) || 8507cfd267cSsstefan1 getCallIfRegularCall( 8517cfd267cSsstefan1 *ArgOp, &OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num])) 8529548b74aSJohannes Doerfert continue; 8539548b74aSJohannes Doerfert } 8549548b74aSJohannes Doerfert return false; 8559548b74aSJohannes Doerfert } 8569548b74aSJohannes Doerfert return true; 8579548b74aSJohannes Doerfert }; 8589548b74aSJohannes Doerfert 8599548b74aSJohannes Doerfert // Helper to identify uses of a GTId as GTId arguments. 8609548b74aSJohannes Doerfert auto AddUserArgs = [&](Value >Id) { 8619548b74aSJohannes Doerfert for (Use &U : GTId.uses()) 8629548b74aSJohannes Doerfert if (CallInst *CI = dyn_cast<CallInst>(U.getUser())) 8639548b74aSJohannes Doerfert if (CI->isArgOperand(&U)) 8649548b74aSJohannes Doerfert if (Function *Callee = CI->getCalledFunction()) 8659548b74aSJohannes Doerfert if (CallArgOpIsGTId(*Callee, U.getOperandNo(), *CI)) 8669548b74aSJohannes Doerfert GTIdArgs.insert(Callee->getArg(U.getOperandNo())); 8679548b74aSJohannes Doerfert }; 8689548b74aSJohannes Doerfert 8699548b74aSJohannes Doerfert // The argument users of __kmpc_global_thread_num calls are GTIds. 8707cfd267cSsstefan1 OMPInformationCache::RuntimeFunctionInfo &GlobThreadNumRFI = 8717cfd267cSsstefan1 OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num]; 8727cfd267cSsstefan1 873624d34afSJohannes Doerfert GlobThreadNumRFI.foreachUse(SCC, [&](Use &U, Function &F) { 8748855fec3SJohannes Doerfert if (CallInst *CI = getCallIfRegularCall(U, &GlobThreadNumRFI)) 8759548b74aSJohannes Doerfert AddUserArgs(*CI); 8768855fec3SJohannes Doerfert return false; 8778855fec3SJohannes Doerfert }); 8789548b74aSJohannes Doerfert 8799548b74aSJohannes Doerfert // Transitively search for more arguments by looking at the users of the 8809548b74aSJohannes Doerfert // ones we know already. During the search the GTIdArgs vector is extended 8819548b74aSJohannes Doerfert // so we cannot cache the size nor can we use a range based for. 8829548b74aSJohannes Doerfert for (unsigned u = 0; u < GTIdArgs.size(); ++u) 8839548b74aSJohannes Doerfert AddUserArgs(*GTIdArgs[u]); 8849548b74aSJohannes Doerfert } 8859548b74aSJohannes Doerfert 8865b0581aeSJohannes Doerfert /// Kernel (=GPU) optimizations and utility functions 8875b0581aeSJohannes Doerfert /// 8885b0581aeSJohannes Doerfert ///{{ 8895b0581aeSJohannes Doerfert 8905b0581aeSJohannes Doerfert /// Check if \p F is a kernel, hence entry point for target offloading. 8915b0581aeSJohannes Doerfert bool isKernel(Function &F) { return OMPInfoCache.Kernels.count(&F); } 8925b0581aeSJohannes Doerfert 8935b0581aeSJohannes Doerfert /// Cache to remember the unique kernel for a function. 8945b0581aeSJohannes Doerfert DenseMap<Function *, Optional<Kernel>> UniqueKernelMap; 8955b0581aeSJohannes Doerfert 8965b0581aeSJohannes Doerfert /// Find the unique kernel that will execute \p F, if any. 8975b0581aeSJohannes Doerfert Kernel getUniqueKernelFor(Function &F); 8985b0581aeSJohannes Doerfert 8995b0581aeSJohannes Doerfert /// Find the unique kernel that will execute \p I, if any. 9005b0581aeSJohannes Doerfert Kernel getUniqueKernelFor(Instruction &I) { 9015b0581aeSJohannes Doerfert return getUniqueKernelFor(*I.getFunction()); 9025b0581aeSJohannes Doerfert } 9035b0581aeSJohannes Doerfert 9045b0581aeSJohannes Doerfert /// Rewrite the device (=GPU) code state machine create in non-SPMD mode in 9055b0581aeSJohannes Doerfert /// the cases we can avoid taking the address of a function. 9065b0581aeSJohannes Doerfert bool rewriteDeviceCodeStateMachine(); 9075b0581aeSJohannes Doerfert 9085b0581aeSJohannes Doerfert /// 9095b0581aeSJohannes Doerfert ///}} 9105b0581aeSJohannes Doerfert 9114d4ea9acSHuber, Joseph /// Emit a remark generically 9124d4ea9acSHuber, Joseph /// 9134d4ea9acSHuber, Joseph /// This template function can be used to generically emit a remark. The 9144d4ea9acSHuber, Joseph /// RemarkKind should be one of the following: 9154d4ea9acSHuber, Joseph /// - OptimizationRemark to indicate a successful optimization attempt 9164d4ea9acSHuber, Joseph /// - OptimizationRemarkMissed to report a failed optimization attempt 9174d4ea9acSHuber, Joseph /// - OptimizationRemarkAnalysis to provide additional information about an 9184d4ea9acSHuber, Joseph /// optimization attempt 9194d4ea9acSHuber, Joseph /// 9204d4ea9acSHuber, Joseph /// The remark is built using a callback function provided by the caller that 9214d4ea9acSHuber, Joseph /// takes a RemarkKind as input and returns a RemarkKind. 9224d4ea9acSHuber, Joseph template <typename RemarkKind, 9234d4ea9acSHuber, Joseph typename RemarkCallBack = function_ref<RemarkKind(RemarkKind &&)>> 9244d4ea9acSHuber, Joseph void emitRemark(Instruction *Inst, StringRef RemarkName, 925e8039ad4SJohannes Doerfert RemarkCallBack &&RemarkCB) const { 9264d4ea9acSHuber, Joseph Function *F = Inst->getParent()->getParent(); 9274d4ea9acSHuber, Joseph auto &ORE = OREGetter(F); 9284d4ea9acSHuber, Joseph 9297cfd267cSsstefan1 ORE.emit( 9307cfd267cSsstefan1 [&]() { return RemarkCB(RemarkKind(DEBUG_TYPE, RemarkName, Inst)); }); 9314d4ea9acSHuber, Joseph } 9324d4ea9acSHuber, Joseph 9330f426935Ssstefan1 /// Emit a remark on a function. Since only OptimizationRemark is supporting 9340f426935Ssstefan1 /// this, it can't be made generic. 935e8039ad4SJohannes Doerfert void 936e8039ad4SJohannes Doerfert emitRemarkOnFunction(Function *F, StringRef RemarkName, 937e8039ad4SJohannes Doerfert function_ref<OptimizationRemark(OptimizationRemark &&)> 938e8039ad4SJohannes Doerfert &&RemarkCB) const { 9390f426935Ssstefan1 auto &ORE = OREGetter(F); 9400f426935Ssstefan1 9410f426935Ssstefan1 ORE.emit([&]() { 9420f426935Ssstefan1 return RemarkCB(OptimizationRemark(DEBUG_TYPE, RemarkName, F)); 9430f426935Ssstefan1 }); 9440f426935Ssstefan1 } 9450f426935Ssstefan1 946b726c557SJohannes Doerfert /// The underlying module. 9479548b74aSJohannes Doerfert Module &M; 9489548b74aSJohannes Doerfert 9499548b74aSJohannes Doerfert /// The SCC we are operating on. 950ee17263aSJohannes Doerfert SmallVectorImpl<Function *> &SCC; 9519548b74aSJohannes Doerfert 9529548b74aSJohannes Doerfert /// Callback to update the call graph, the first argument is a removed call, 9539548b74aSJohannes Doerfert /// the second an optional replacement call. 9549548b74aSJohannes Doerfert CallGraphUpdater &CGUpdater; 9559548b74aSJohannes Doerfert 9564d4ea9acSHuber, Joseph /// Callback to get an OptimizationRemarkEmitter from a Function * 9574d4ea9acSHuber, Joseph OptimizationRemarkGetter OREGetter; 9584d4ea9acSHuber, Joseph 9597cfd267cSsstefan1 /// OpenMP-specific information cache. Also Used for Attributor runs. 9607cfd267cSsstefan1 OMPInformationCache &OMPInfoCache; 961b8235d2bSsstefan1 962b8235d2bSsstefan1 /// Attributor instance. 963b8235d2bSsstefan1 Attributor &A; 964b8235d2bSsstefan1 965b8235d2bSsstefan1 /// Helper function to run Attributor on SCC. 966b8235d2bSsstefan1 bool runAttributor() { 967b8235d2bSsstefan1 if (SCC.empty()) 968b8235d2bSsstefan1 return false; 969b8235d2bSsstefan1 970b8235d2bSsstefan1 registerAAs(); 971b8235d2bSsstefan1 972b8235d2bSsstefan1 ChangeStatus Changed = A.run(); 973b8235d2bSsstefan1 974b8235d2bSsstefan1 LLVM_DEBUG(dbgs() << "[Attributor] Done with " << SCC.size() 975b8235d2bSsstefan1 << " functions, result: " << Changed << ".\n"); 976b8235d2bSsstefan1 977b8235d2bSsstefan1 return Changed == ChangeStatus::CHANGED; 978b8235d2bSsstefan1 } 979b8235d2bSsstefan1 980b8235d2bSsstefan1 /// Populate the Attributor with abstract attribute opportunities in the 981b8235d2bSsstefan1 /// function. 982b8235d2bSsstefan1 void registerAAs() { 983*b0b32e64Ssstefan1 if (SCC.empty()) 984*b0b32e64Ssstefan1 return; 985b8235d2bSsstefan1 986*b0b32e64Ssstefan1 // Create CallSite AA for all Getters. 987*b0b32e64Ssstefan1 for (int Idx = 0; Idx < OMPInfoCache.ICVs.size() - 1; ++Idx) { 988*b0b32e64Ssstefan1 auto ICVInfo = OMPInfoCache.ICVs[static_cast<InternalControlVar>(Idx)]; 989*b0b32e64Ssstefan1 990*b0b32e64Ssstefan1 auto &GetterRFI = OMPInfoCache.RFIs[ICVInfo.Getter]; 991*b0b32e64Ssstefan1 992*b0b32e64Ssstefan1 auto CreateAA = [&](Use &U, Function &Caller) { 993*b0b32e64Ssstefan1 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &GetterRFI); 994*b0b32e64Ssstefan1 if (!CI) 995*b0b32e64Ssstefan1 return false; 996*b0b32e64Ssstefan1 997*b0b32e64Ssstefan1 auto &CB = cast<CallBase>(*CI); 998*b0b32e64Ssstefan1 999*b0b32e64Ssstefan1 IRPosition CBPos = IRPosition::callsite_function(CB); 1000*b0b32e64Ssstefan1 A.getOrCreateAAFor<AAICVTracker>(CBPos); 1001*b0b32e64Ssstefan1 return false; 1002*b0b32e64Ssstefan1 }; 1003*b0b32e64Ssstefan1 1004*b0b32e64Ssstefan1 GetterRFI.foreachUse(SCC, CreateAA); 1005b8235d2bSsstefan1 } 1006b8235d2bSsstefan1 } 1007b8235d2bSsstefan1 }; 1008b8235d2bSsstefan1 10095b0581aeSJohannes Doerfert Kernel OpenMPOpt::getUniqueKernelFor(Function &F) { 10105b0581aeSJohannes Doerfert if (!OMPInfoCache.ModuleSlice.count(&F)) 10115b0581aeSJohannes Doerfert return nullptr; 10125b0581aeSJohannes Doerfert 10135b0581aeSJohannes Doerfert // Use a scope to keep the lifetime of the CachedKernel short. 10145b0581aeSJohannes Doerfert { 10155b0581aeSJohannes Doerfert Optional<Kernel> &CachedKernel = UniqueKernelMap[&F]; 10165b0581aeSJohannes Doerfert if (CachedKernel) 10175b0581aeSJohannes Doerfert return *CachedKernel; 10185b0581aeSJohannes Doerfert 10195b0581aeSJohannes Doerfert // TODO: We should use an AA to create an (optimistic and callback 10205b0581aeSJohannes Doerfert // call-aware) call graph. For now we stick to simple patterns that 10215b0581aeSJohannes Doerfert // are less powerful, basically the worst fixpoint. 10225b0581aeSJohannes Doerfert if (isKernel(F)) { 10235b0581aeSJohannes Doerfert CachedKernel = Kernel(&F); 10245b0581aeSJohannes Doerfert return *CachedKernel; 10255b0581aeSJohannes Doerfert } 10265b0581aeSJohannes Doerfert 10275b0581aeSJohannes Doerfert CachedKernel = nullptr; 10285b0581aeSJohannes Doerfert if (!F.hasLocalLinkage()) 10295b0581aeSJohannes Doerfert return nullptr; 10305b0581aeSJohannes Doerfert } 10315b0581aeSJohannes Doerfert 10325b0581aeSJohannes Doerfert auto GetUniqueKernelForUse = [&](const Use &U) -> Kernel { 10335b0581aeSJohannes Doerfert if (auto *Cmp = dyn_cast<ICmpInst>(U.getUser())) { 10345b0581aeSJohannes Doerfert // Allow use in equality comparisons. 10355b0581aeSJohannes Doerfert if (Cmp->isEquality()) 10365b0581aeSJohannes Doerfert return getUniqueKernelFor(*Cmp); 10375b0581aeSJohannes Doerfert return nullptr; 10385b0581aeSJohannes Doerfert } 10395b0581aeSJohannes Doerfert if (auto *CB = dyn_cast<CallBase>(U.getUser())) { 10405b0581aeSJohannes Doerfert // Allow direct calls. 10415b0581aeSJohannes Doerfert if (CB->isCallee(&U)) 10425b0581aeSJohannes Doerfert return getUniqueKernelFor(*CB); 10435b0581aeSJohannes Doerfert // Allow the use in __kmpc_kernel_prepare_parallel calls. 10445b0581aeSJohannes Doerfert if (Function *Callee = CB->getCalledFunction()) 10455b0581aeSJohannes Doerfert if (Callee->getName() == "__kmpc_kernel_prepare_parallel") 10465b0581aeSJohannes Doerfert return getUniqueKernelFor(*CB); 10475b0581aeSJohannes Doerfert return nullptr; 10485b0581aeSJohannes Doerfert } 10495b0581aeSJohannes Doerfert // Disallow every other use. 10505b0581aeSJohannes Doerfert return nullptr; 10515b0581aeSJohannes Doerfert }; 10525b0581aeSJohannes Doerfert 10535b0581aeSJohannes Doerfert // TODO: In the future we want to track more than just a unique kernel. 10545b0581aeSJohannes Doerfert SmallPtrSet<Kernel, 2> PotentialKernels; 10555b0581aeSJohannes Doerfert foreachUse(F, [&](const Use &U) { 10565b0581aeSJohannes Doerfert PotentialKernels.insert(GetUniqueKernelForUse(U)); 10575b0581aeSJohannes Doerfert }); 10585b0581aeSJohannes Doerfert 10595b0581aeSJohannes Doerfert Kernel K = nullptr; 10605b0581aeSJohannes Doerfert if (PotentialKernels.size() == 1) 10615b0581aeSJohannes Doerfert K = *PotentialKernels.begin(); 10625b0581aeSJohannes Doerfert 10635b0581aeSJohannes Doerfert // Cache the result. 10645b0581aeSJohannes Doerfert UniqueKernelMap[&F] = K; 10655b0581aeSJohannes Doerfert 10665b0581aeSJohannes Doerfert return K; 10675b0581aeSJohannes Doerfert } 10685b0581aeSJohannes Doerfert 10695b0581aeSJohannes Doerfert bool OpenMPOpt::rewriteDeviceCodeStateMachine() { 10705b0581aeSJohannes Doerfert OMPInformationCache::RuntimeFunctionInfo &KernelPrepareParallelRFI = 10715b0581aeSJohannes Doerfert OMPInfoCache.RFIs[OMPRTL___kmpc_kernel_prepare_parallel]; 10725b0581aeSJohannes Doerfert 10735b0581aeSJohannes Doerfert bool Changed = false; 10745b0581aeSJohannes Doerfert if (!KernelPrepareParallelRFI) 10755b0581aeSJohannes Doerfert return Changed; 10765b0581aeSJohannes Doerfert 10775b0581aeSJohannes Doerfert for (Function *F : SCC) { 10785b0581aeSJohannes Doerfert 10795b0581aeSJohannes Doerfert // Check if the function is uses in a __kmpc_kernel_prepare_parallel call at 10805b0581aeSJohannes Doerfert // all. 10815b0581aeSJohannes Doerfert bool UnknownUse = false; 1082fec1f210SJohannes Doerfert bool KernelPrepareUse = false; 10835b0581aeSJohannes Doerfert unsigned NumDirectCalls = 0; 10845b0581aeSJohannes Doerfert 10855b0581aeSJohannes Doerfert SmallVector<Use *, 2> ToBeReplacedStateMachineUses; 10865b0581aeSJohannes Doerfert foreachUse(*F, [&](Use &U) { 10875b0581aeSJohannes Doerfert if (auto *CB = dyn_cast<CallBase>(U.getUser())) 10885b0581aeSJohannes Doerfert if (CB->isCallee(&U)) { 10895b0581aeSJohannes Doerfert ++NumDirectCalls; 10905b0581aeSJohannes Doerfert return; 10915b0581aeSJohannes Doerfert } 10925b0581aeSJohannes Doerfert 109381db6144SMichael Liao if (isa<ICmpInst>(U.getUser())) { 10945b0581aeSJohannes Doerfert ToBeReplacedStateMachineUses.push_back(&U); 10955b0581aeSJohannes Doerfert return; 10965b0581aeSJohannes Doerfert } 1097fec1f210SJohannes Doerfert if (!KernelPrepareUse && OpenMPOpt::getCallIfRegularCall( 1098fec1f210SJohannes Doerfert *U.getUser(), &KernelPrepareParallelRFI)) { 1099fec1f210SJohannes Doerfert KernelPrepareUse = true; 11005b0581aeSJohannes Doerfert ToBeReplacedStateMachineUses.push_back(&U); 11015b0581aeSJohannes Doerfert return; 11025b0581aeSJohannes Doerfert } 11035b0581aeSJohannes Doerfert UnknownUse = true; 11045b0581aeSJohannes Doerfert }); 11055b0581aeSJohannes Doerfert 1106fec1f210SJohannes Doerfert // Do not emit a remark if we haven't seen a __kmpc_kernel_prepare_parallel 1107fec1f210SJohannes Doerfert // use. 1108fec1f210SJohannes Doerfert if (!KernelPrepareUse) 11095b0581aeSJohannes Doerfert continue; 11105b0581aeSJohannes Doerfert 1111fec1f210SJohannes Doerfert { 1112fec1f210SJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 1113fec1f210SJohannes Doerfert return OR << "Found a parallel region that is called in a target " 1114fec1f210SJohannes Doerfert "region but not part of a combined target construct nor " 1115fec1f210SJohannes Doerfert "nesed inside a target construct without intermediate " 1116fec1f210SJohannes Doerfert "code. This can lead to excessive register usage for " 1117fec1f210SJohannes Doerfert "unrelated target regions in the same translation unit " 1118fec1f210SJohannes Doerfert "due to spurious call edges assumed by ptxas."; 1119fec1f210SJohannes Doerfert }; 1120fec1f210SJohannes Doerfert emitRemarkOnFunction(F, "OpenMPParallelRegionInNonSPMD", Remark); 1121fec1f210SJohannes Doerfert } 1122fec1f210SJohannes Doerfert 1123fec1f210SJohannes Doerfert // If this ever hits, we should investigate. 1124fec1f210SJohannes Doerfert // TODO: Checking the number of uses is not a necessary restriction and 1125fec1f210SJohannes Doerfert // should be lifted. 1126fec1f210SJohannes Doerfert if (UnknownUse || NumDirectCalls != 1 || 1127fec1f210SJohannes Doerfert ToBeReplacedStateMachineUses.size() != 2) { 1128fec1f210SJohannes Doerfert { 1129fec1f210SJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 1130fec1f210SJohannes Doerfert return OR << "Parallel region is used in " 1131fec1f210SJohannes Doerfert << (UnknownUse ? "unknown" : "unexpected") 1132fec1f210SJohannes Doerfert << " ways; will not attempt to rewrite the state machine."; 1133fec1f210SJohannes Doerfert }; 1134fec1f210SJohannes Doerfert emitRemarkOnFunction(F, "OpenMPParallelRegionInNonSPMD", Remark); 1135fec1f210SJohannes Doerfert } 11365b0581aeSJohannes Doerfert continue; 1137fec1f210SJohannes Doerfert } 11385b0581aeSJohannes Doerfert 11395b0581aeSJohannes Doerfert // Even if we have __kmpc_kernel_prepare_parallel calls, we (for now) give 11405b0581aeSJohannes Doerfert // up if the function is not called from a unique kernel. 11415b0581aeSJohannes Doerfert Kernel K = getUniqueKernelFor(*F); 1142fec1f210SJohannes Doerfert if (!K) { 1143fec1f210SJohannes Doerfert { 1144fec1f210SJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 1145fec1f210SJohannes Doerfert return OR << "Parallel region is not known to be called from a " 1146fec1f210SJohannes Doerfert "unique single target region, maybe the surrounding " 1147fec1f210SJohannes Doerfert "function has external linkage?; will not attempt to " 1148fec1f210SJohannes Doerfert "rewrite the state machine use."; 1149fec1f210SJohannes Doerfert }; 1150fec1f210SJohannes Doerfert emitRemarkOnFunction(F, "OpenMPParallelRegionInMultipleKernesl", 1151fec1f210SJohannes Doerfert Remark); 1152fec1f210SJohannes Doerfert } 11535b0581aeSJohannes Doerfert continue; 1154fec1f210SJohannes Doerfert } 11555b0581aeSJohannes Doerfert 11565b0581aeSJohannes Doerfert // We now know F is a parallel body function called only from the kernel K. 11575b0581aeSJohannes Doerfert // We also identified the state machine uses in which we replace the 11585b0581aeSJohannes Doerfert // function pointer by a new global symbol for identification purposes. This 11595b0581aeSJohannes Doerfert // ensures only direct calls to the function are left. 11605b0581aeSJohannes Doerfert 1161fec1f210SJohannes Doerfert { 1162fec1f210SJohannes Doerfert auto RemarkParalleRegion = [&](OptimizationRemark OR) { 1163fec1f210SJohannes Doerfert return OR << "Specialize parallel region that is only reached from a " 1164fec1f210SJohannes Doerfert "single target region to avoid spurious call edges and " 1165fec1f210SJohannes Doerfert "excessive register usage in other target regions. " 1166fec1f210SJohannes Doerfert "(parallel region ID: " 1167fec1f210SJohannes Doerfert << ore::NV("OpenMPParallelRegion", F->getName()) 1168fec1f210SJohannes Doerfert << ", kernel ID: " 1169fec1f210SJohannes Doerfert << ore::NV("OpenMPTargetRegion", K->getName()) << ")"; 1170fec1f210SJohannes Doerfert }; 1171fec1f210SJohannes Doerfert emitRemarkOnFunction(F, "OpenMPParallelRegionInNonSPMD", 1172fec1f210SJohannes Doerfert RemarkParalleRegion); 1173fec1f210SJohannes Doerfert auto RemarkKernel = [&](OptimizationRemark OR) { 1174fec1f210SJohannes Doerfert return OR << "Target region containing the parallel region that is " 1175fec1f210SJohannes Doerfert "specialized. (parallel region ID: " 1176fec1f210SJohannes Doerfert << ore::NV("OpenMPParallelRegion", F->getName()) 1177fec1f210SJohannes Doerfert << ", kernel ID: " 1178fec1f210SJohannes Doerfert << ore::NV("OpenMPTargetRegion", K->getName()) << ")"; 1179fec1f210SJohannes Doerfert }; 1180fec1f210SJohannes Doerfert emitRemarkOnFunction(K, "OpenMPParallelRegionInNonSPMD", RemarkKernel); 1181fec1f210SJohannes Doerfert } 1182fec1f210SJohannes Doerfert 11835b0581aeSJohannes Doerfert Module &M = *F->getParent(); 11845b0581aeSJohannes Doerfert Type *Int8Ty = Type::getInt8Ty(M.getContext()); 11855b0581aeSJohannes Doerfert 11865b0581aeSJohannes Doerfert auto *ID = new GlobalVariable( 11875b0581aeSJohannes Doerfert M, Int8Ty, /* isConstant */ true, GlobalValue::PrivateLinkage, 11885b0581aeSJohannes Doerfert UndefValue::get(Int8Ty), F->getName() + ".ID"); 11895b0581aeSJohannes Doerfert 11905b0581aeSJohannes Doerfert for (Use *U : ToBeReplacedStateMachineUses) 11915b0581aeSJohannes Doerfert U->set(ConstantExpr::getBitCast(ID, U->get()->getType())); 11925b0581aeSJohannes Doerfert 11935b0581aeSJohannes Doerfert ++NumOpenMPParallelRegionsReplacedInGPUStateMachine; 11945b0581aeSJohannes Doerfert 11955b0581aeSJohannes Doerfert Changed = true; 11965b0581aeSJohannes Doerfert } 11975b0581aeSJohannes Doerfert 11985b0581aeSJohannes Doerfert return Changed; 11995b0581aeSJohannes Doerfert } 12005b0581aeSJohannes Doerfert 1201b8235d2bSsstefan1 /// Abstract Attribute for tracking ICV values. 1202b8235d2bSsstefan1 struct AAICVTracker : public StateWrapper<BooleanState, AbstractAttribute> { 1203b8235d2bSsstefan1 using Base = StateWrapper<BooleanState, AbstractAttribute>; 1204b8235d2bSsstefan1 AAICVTracker(const IRPosition &IRP, Attributor &A) : Base(IRP) {} 1205b8235d2bSsstefan1 1206*b0b32e64Ssstefan1 void initialize(Attributor &A) override { 1207*b0b32e64Ssstefan1 Function *F = getAnchorScope(); 1208*b0b32e64Ssstefan1 if (!F || !A.isFunctionIPOAmendable(*F)) 1209*b0b32e64Ssstefan1 indicatePessimisticFixpoint(); 1210*b0b32e64Ssstefan1 } 1211*b0b32e64Ssstefan1 1212b8235d2bSsstefan1 /// Returns true if value is assumed to be tracked. 1213b8235d2bSsstefan1 bool isAssumedTracked() const { return getAssumed(); } 1214b8235d2bSsstefan1 1215b8235d2bSsstefan1 /// Returns true if value is known to be tracked. 1216b8235d2bSsstefan1 bool isKnownTracked() const { return getAssumed(); } 1217b8235d2bSsstefan1 1218b8235d2bSsstefan1 /// Create an abstract attribute biew for the position \p IRP. 1219b8235d2bSsstefan1 static AAICVTracker &createForPosition(const IRPosition &IRP, Attributor &A); 1220b8235d2bSsstefan1 1221b8235d2bSsstefan1 /// Return the value with which \p I can be replaced for specific \p ICV. 1222*b0b32e64Ssstefan1 virtual Optional<Value *> getReplacementValue(InternalControlVar ICV, 1223*b0b32e64Ssstefan1 const Instruction *I, 1224*b0b32e64Ssstefan1 Attributor &A) const { 1225*b0b32e64Ssstefan1 return None; 1226*b0b32e64Ssstefan1 } 1227*b0b32e64Ssstefan1 1228*b0b32e64Ssstefan1 /// Return an assumed unique ICV value if a single candidate is found. If 1229*b0b32e64Ssstefan1 /// there cannot be one, return a nullptr. If it is not clear yet, return the 1230*b0b32e64Ssstefan1 /// Optional::NoneType. 1231*b0b32e64Ssstefan1 virtual Optional<Value *> 1232*b0b32e64Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const = 0; 1233*b0b32e64Ssstefan1 1234*b0b32e64Ssstefan1 // Currently only nthreads is being tracked. 1235*b0b32e64Ssstefan1 // this array will only grow with time. 1236*b0b32e64Ssstefan1 InternalControlVar TrackableICVs[1] = {ICV_nthreads}; 1237b8235d2bSsstefan1 1238b8235d2bSsstefan1 /// See AbstractAttribute::getName() 1239b8235d2bSsstefan1 const std::string getName() const override { return "AAICVTracker"; } 1240b8235d2bSsstefan1 1241233af895SLuofan Chen /// See AbstractAttribute::getIdAddr() 1242233af895SLuofan Chen const char *getIdAddr() const override { return &ID; } 1243233af895SLuofan Chen 1244233af895SLuofan Chen /// This function should return true if the type of the \p AA is AAICVTracker 1245233af895SLuofan Chen static bool classof(const AbstractAttribute *AA) { 1246233af895SLuofan Chen return (AA->getIdAddr() == &ID); 1247233af895SLuofan Chen } 1248233af895SLuofan Chen 1249b8235d2bSsstefan1 static const char ID; 1250b8235d2bSsstefan1 }; 1251b8235d2bSsstefan1 1252b8235d2bSsstefan1 struct AAICVTrackerFunction : public AAICVTracker { 1253b8235d2bSsstefan1 AAICVTrackerFunction(const IRPosition &IRP, Attributor &A) 1254b8235d2bSsstefan1 : AAICVTracker(IRP, A) {} 1255b8235d2bSsstefan1 1256b8235d2bSsstefan1 // FIXME: come up with better string. 1257*b0b32e64Ssstefan1 const std::string getAsStr() const override { return "ICVTrackerFunction"; } 1258b8235d2bSsstefan1 1259b8235d2bSsstefan1 // FIXME: come up with some stats. 1260b8235d2bSsstefan1 void trackStatistics() const override {} 1261b8235d2bSsstefan1 1262*b0b32e64Ssstefan1 /// We don't manifest anything for this AA. 1263b8235d2bSsstefan1 ChangeStatus manifest(Attributor &A) override { 1264*b0b32e64Ssstefan1 return ChangeStatus::UNCHANGED; 1265b8235d2bSsstefan1 } 1266b8235d2bSsstefan1 1267b8235d2bSsstefan1 // Map of ICV to their values at specific program point. 1268*b0b32e64Ssstefan1 EnumeratedArray<DenseMap<Instruction *, Value *>, InternalControlVar, 1269b8235d2bSsstefan1 InternalControlVar::ICV___last> 1270*b0b32e64Ssstefan1 ICVReplacementValuesMap; 1271b8235d2bSsstefan1 1272b8235d2bSsstefan1 ChangeStatus updateImpl(Attributor &A) override { 1273b8235d2bSsstefan1 ChangeStatus HasChanged = ChangeStatus::UNCHANGED; 1274b8235d2bSsstefan1 1275b8235d2bSsstefan1 Function *F = getAnchorScope(); 1276b8235d2bSsstefan1 1277b8235d2bSsstefan1 auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 1278b8235d2bSsstefan1 1279b8235d2bSsstefan1 for (InternalControlVar ICV : TrackableICVs) { 1280b8235d2bSsstefan1 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter]; 1281b8235d2bSsstefan1 1282*b0b32e64Ssstefan1 auto &ValuesMap = ICVReplacementValuesMap[ICV]; 1283b8235d2bSsstefan1 auto TrackValues = [&](Use &U, Function &) { 1284b8235d2bSsstefan1 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U); 1285b8235d2bSsstefan1 if (!CI) 1286b8235d2bSsstefan1 return false; 1287b8235d2bSsstefan1 1288b8235d2bSsstefan1 // FIXME: handle setters with more that 1 arguments. 1289b8235d2bSsstefan1 /// Track new value. 1290*b0b32e64Ssstefan1 if (ValuesMap.insert(std::make_pair(CI, CI->getArgOperand(0))).second) 1291b8235d2bSsstefan1 HasChanged = ChangeStatus::CHANGED; 1292b8235d2bSsstefan1 1293b8235d2bSsstefan1 return false; 1294b8235d2bSsstefan1 }; 1295b8235d2bSsstefan1 1296*b0b32e64Ssstefan1 auto CallCheck = [&](Instruction &I) { 1297*b0b32e64Ssstefan1 Optional<Value *> ReplVal = getValueForCall(A, &I, ICV); 1298*b0b32e64Ssstefan1 if (ReplVal.hasValue() && 1299*b0b32e64Ssstefan1 ValuesMap.insert(std::make_pair(&I, *ReplVal)).second) 1300*b0b32e64Ssstefan1 HasChanged = ChangeStatus::CHANGED; 1301*b0b32e64Ssstefan1 1302*b0b32e64Ssstefan1 return true; 1303*b0b32e64Ssstefan1 }; 1304*b0b32e64Ssstefan1 1305*b0b32e64Ssstefan1 // Track all changes of an ICV. 1306b8235d2bSsstefan1 SetterRFI.foreachUse(TrackValues, F); 1307*b0b32e64Ssstefan1 1308*b0b32e64Ssstefan1 A.checkForAllInstructions(CallCheck, *this, {Instruction::Call}, 1309*b0b32e64Ssstefan1 /* CheckBBLivenessOnly */ true); 1310*b0b32e64Ssstefan1 1311*b0b32e64Ssstefan1 /// TODO: Figure out a way to avoid adding entry in 1312*b0b32e64Ssstefan1 /// ICVReplacementValuesMap 1313*b0b32e64Ssstefan1 Instruction *Entry = &F->getEntryBlock().front(); 1314*b0b32e64Ssstefan1 if (HasChanged == ChangeStatus::CHANGED && !ValuesMap.count(Entry)) 1315*b0b32e64Ssstefan1 ValuesMap.insert(std::make_pair(Entry, nullptr)); 1316b8235d2bSsstefan1 } 1317b8235d2bSsstefan1 1318b8235d2bSsstefan1 return HasChanged; 1319b8235d2bSsstefan1 } 1320b8235d2bSsstefan1 1321*b0b32e64Ssstefan1 /// Hepler to check if \p I is a call and get the value for it if it is 1322*b0b32e64Ssstefan1 /// unique. 1323*b0b32e64Ssstefan1 Optional<Value *> getValueForCall(Attributor &A, const Instruction *I, 1324*b0b32e64Ssstefan1 InternalControlVar &ICV) const { 1325b8235d2bSsstefan1 1326*b0b32e64Ssstefan1 const auto *CB = dyn_cast<CallBase>(I); 1327*b0b32e64Ssstefan1 if (!CB) 1328*b0b32e64Ssstefan1 return None; 1329*b0b32e64Ssstefan1 1330b8235d2bSsstefan1 auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 1331b8235d2bSsstefan1 auto &GetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Getter]; 1332*b0b32e64Ssstefan1 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter]; 1333*b0b32e64Ssstefan1 Function *CalledFunction = CB->getCalledFunction(); 1334b8235d2bSsstefan1 1335*b0b32e64Ssstefan1 if (CalledFunction == GetterRFI.Declaration) 1336*b0b32e64Ssstefan1 return None; 1337*b0b32e64Ssstefan1 if (CalledFunction == SetterRFI.Declaration) { 1338*b0b32e64Ssstefan1 if (ICVReplacementValuesMap[ICV].count(I)) 1339*b0b32e64Ssstefan1 return ICVReplacementValuesMap[ICV].lookup(I); 1340*b0b32e64Ssstefan1 1341*b0b32e64Ssstefan1 return nullptr; 1342*b0b32e64Ssstefan1 } 1343*b0b32e64Ssstefan1 1344*b0b32e64Ssstefan1 // Since we don't know, assume it changes the ICV. 1345*b0b32e64Ssstefan1 if (CalledFunction->isDeclaration()) 1346*b0b32e64Ssstefan1 return nullptr; 1347*b0b32e64Ssstefan1 1348*b0b32e64Ssstefan1 const auto &ICVTrackingAA = 1349*b0b32e64Ssstefan1 A.getAAFor<AAICVTracker>(*this, IRPosition::callsite_returned(*CB)); 1350*b0b32e64Ssstefan1 1351*b0b32e64Ssstefan1 if (ICVTrackingAA.isAssumedTracked()) 1352*b0b32e64Ssstefan1 return ICVTrackingAA.getUniqueReplacementValue(ICV); 1353*b0b32e64Ssstefan1 1354*b0b32e64Ssstefan1 // If we don't know, assume it changes. 1355*b0b32e64Ssstefan1 return nullptr; 1356*b0b32e64Ssstefan1 } 1357*b0b32e64Ssstefan1 1358*b0b32e64Ssstefan1 // We don't check unique value for a function, so return None. 1359*b0b32e64Ssstefan1 Optional<Value *> 1360*b0b32e64Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 1361*b0b32e64Ssstefan1 return None; 1362*b0b32e64Ssstefan1 } 1363*b0b32e64Ssstefan1 1364*b0b32e64Ssstefan1 /// Return the value with which \p I can be replaced for specific \p ICV. 1365*b0b32e64Ssstefan1 Optional<Value *> getReplacementValue(InternalControlVar ICV, 1366*b0b32e64Ssstefan1 const Instruction *I, 1367*b0b32e64Ssstefan1 Attributor &A) const override { 1368*b0b32e64Ssstefan1 const auto &ValuesMap = ICVReplacementValuesMap[ICV]; 1369*b0b32e64Ssstefan1 if (ValuesMap.count(I)) 1370*b0b32e64Ssstefan1 return ValuesMap.lookup(I); 1371*b0b32e64Ssstefan1 1372*b0b32e64Ssstefan1 SmallVector<const Instruction *, 16> Worklist; 1373*b0b32e64Ssstefan1 SmallPtrSet<const Instruction *, 16> Visited; 1374*b0b32e64Ssstefan1 Worklist.push_back(I); 1375*b0b32e64Ssstefan1 1376*b0b32e64Ssstefan1 Optional<Value *> ReplVal; 1377*b0b32e64Ssstefan1 1378*b0b32e64Ssstefan1 while (!Worklist.empty()) { 1379*b0b32e64Ssstefan1 const Instruction *CurrInst = Worklist.pop_back_val(); 1380*b0b32e64Ssstefan1 if (!Visited.insert(CurrInst).second) 1381b8235d2bSsstefan1 continue; 1382b8235d2bSsstefan1 1383*b0b32e64Ssstefan1 const BasicBlock *CurrBB = CurrInst->getParent(); 1384*b0b32e64Ssstefan1 1385*b0b32e64Ssstefan1 // Go up and look for all potential setters/calls that might change the 1386*b0b32e64Ssstefan1 // ICV. 1387*b0b32e64Ssstefan1 while ((CurrInst = CurrInst->getPrevNode())) { 1388*b0b32e64Ssstefan1 if (ValuesMap.count(CurrInst)) { 1389*b0b32e64Ssstefan1 Optional<Value *> NewReplVal = ValuesMap.lookup(CurrInst); 1390*b0b32e64Ssstefan1 // Unknown value, track new. 1391*b0b32e64Ssstefan1 if (!ReplVal.hasValue()) { 1392*b0b32e64Ssstefan1 ReplVal = NewReplVal; 1393*b0b32e64Ssstefan1 break; 1394*b0b32e64Ssstefan1 } 1395*b0b32e64Ssstefan1 1396*b0b32e64Ssstefan1 // If we found a new value, we can't know the icv value anymore. 1397*b0b32e64Ssstefan1 if (NewReplVal.hasValue()) 1398*b0b32e64Ssstefan1 if (ReplVal != NewReplVal) 1399b8235d2bSsstefan1 return nullptr; 1400b8235d2bSsstefan1 1401*b0b32e64Ssstefan1 break; 1402b8235d2bSsstefan1 } 1403b8235d2bSsstefan1 1404*b0b32e64Ssstefan1 Optional<Value *> NewReplVal = getValueForCall(A, CurrInst, ICV); 1405*b0b32e64Ssstefan1 if (!NewReplVal.hasValue()) 1406*b0b32e64Ssstefan1 continue; 1407*b0b32e64Ssstefan1 1408*b0b32e64Ssstefan1 // Unknown value, track new. 1409*b0b32e64Ssstefan1 if (!ReplVal.hasValue()) { 1410*b0b32e64Ssstefan1 ReplVal = NewReplVal; 1411*b0b32e64Ssstefan1 break; 1412b8235d2bSsstefan1 } 1413b8235d2bSsstefan1 1414*b0b32e64Ssstefan1 // if (NewReplVal.hasValue()) 1415*b0b32e64Ssstefan1 // We found a new value, we can't know the icv value anymore. 1416*b0b32e64Ssstefan1 if (ReplVal != NewReplVal) 1417b8235d2bSsstefan1 return nullptr; 1418b8235d2bSsstefan1 } 1419*b0b32e64Ssstefan1 1420*b0b32e64Ssstefan1 // If we are in the same BB and we have a value, we are done. 1421*b0b32e64Ssstefan1 if (CurrBB == I->getParent() && ReplVal.hasValue()) 1422*b0b32e64Ssstefan1 return ReplVal; 1423*b0b32e64Ssstefan1 1424*b0b32e64Ssstefan1 // Go through all predecessors and add terminators for analysis. 1425*b0b32e64Ssstefan1 for (const BasicBlock *Pred : predecessors(CurrBB)) 1426*b0b32e64Ssstefan1 if (const Instruction *Terminator = Pred->getTerminator()) 1427*b0b32e64Ssstefan1 Worklist.push_back(Terminator); 1428*b0b32e64Ssstefan1 } 1429*b0b32e64Ssstefan1 1430*b0b32e64Ssstefan1 return ReplVal; 1431*b0b32e64Ssstefan1 } 1432*b0b32e64Ssstefan1 }; 1433*b0b32e64Ssstefan1 1434*b0b32e64Ssstefan1 struct AAICVTrackerFunctionReturned : AAICVTracker { 1435*b0b32e64Ssstefan1 AAICVTrackerFunctionReturned(const IRPosition &IRP, Attributor &A) 1436*b0b32e64Ssstefan1 : AAICVTracker(IRP, A) {} 1437*b0b32e64Ssstefan1 1438*b0b32e64Ssstefan1 // FIXME: come up with better string. 1439*b0b32e64Ssstefan1 const std::string getAsStr() const override { 1440*b0b32e64Ssstefan1 return "ICVTrackerFunctionReturned"; 1441*b0b32e64Ssstefan1 } 1442*b0b32e64Ssstefan1 1443*b0b32e64Ssstefan1 // FIXME: come up with some stats. 1444*b0b32e64Ssstefan1 void trackStatistics() const override {} 1445*b0b32e64Ssstefan1 1446*b0b32e64Ssstefan1 /// We don't manifest anything for this AA. 1447*b0b32e64Ssstefan1 ChangeStatus manifest(Attributor &A) override { 1448*b0b32e64Ssstefan1 return ChangeStatus::UNCHANGED; 1449*b0b32e64Ssstefan1 } 1450*b0b32e64Ssstefan1 1451*b0b32e64Ssstefan1 // Map of ICV to their values at specific program point. 1452*b0b32e64Ssstefan1 EnumeratedArray<Optional<Value *>, InternalControlVar, 1453*b0b32e64Ssstefan1 InternalControlVar::ICV___last> 1454*b0b32e64Ssstefan1 ICVReplacementValuesMap; 1455*b0b32e64Ssstefan1 1456*b0b32e64Ssstefan1 /// Return the value with which \p I can be replaced for specific \p ICV. 1457*b0b32e64Ssstefan1 Optional<Value *> 1458*b0b32e64Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 1459*b0b32e64Ssstefan1 return ICVReplacementValuesMap[ICV]; 1460*b0b32e64Ssstefan1 } 1461*b0b32e64Ssstefan1 1462*b0b32e64Ssstefan1 ChangeStatus updateImpl(Attributor &A) override { 1463*b0b32e64Ssstefan1 ChangeStatus Changed = ChangeStatus::UNCHANGED; 1464*b0b32e64Ssstefan1 const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 1465*b0b32e64Ssstefan1 *this, IRPosition::function(*getAnchorScope())); 1466*b0b32e64Ssstefan1 1467*b0b32e64Ssstefan1 if (!ICVTrackingAA.isAssumedTracked()) 1468*b0b32e64Ssstefan1 return indicatePessimisticFixpoint(); 1469*b0b32e64Ssstefan1 1470*b0b32e64Ssstefan1 for (InternalControlVar ICV : TrackableICVs) { 1471*b0b32e64Ssstefan1 Optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV]; 1472*b0b32e64Ssstefan1 Optional<Value *> UniqueICVValue; 1473*b0b32e64Ssstefan1 1474*b0b32e64Ssstefan1 auto CheckReturnInst = [&](Instruction &I) { 1475*b0b32e64Ssstefan1 Optional<Value *> NewReplVal = 1476*b0b32e64Ssstefan1 ICVTrackingAA.getReplacementValue(ICV, &I, A); 1477*b0b32e64Ssstefan1 1478*b0b32e64Ssstefan1 // If we found a second ICV value there is no unique returned value. 1479*b0b32e64Ssstefan1 if (UniqueICVValue.hasValue() && UniqueICVValue != NewReplVal) 1480*b0b32e64Ssstefan1 return false; 1481*b0b32e64Ssstefan1 1482*b0b32e64Ssstefan1 UniqueICVValue = NewReplVal; 1483*b0b32e64Ssstefan1 1484*b0b32e64Ssstefan1 return true; 1485*b0b32e64Ssstefan1 }; 1486*b0b32e64Ssstefan1 1487*b0b32e64Ssstefan1 if (!A.checkForAllInstructions(CheckReturnInst, *this, {Instruction::Ret}, 1488*b0b32e64Ssstefan1 /* CheckBBLivenessOnly */ true)) 1489*b0b32e64Ssstefan1 UniqueICVValue = nullptr; 1490*b0b32e64Ssstefan1 1491*b0b32e64Ssstefan1 if (UniqueICVValue == ReplVal) 1492*b0b32e64Ssstefan1 continue; 1493*b0b32e64Ssstefan1 1494*b0b32e64Ssstefan1 ReplVal = UniqueICVValue; 1495*b0b32e64Ssstefan1 Changed = ChangeStatus::CHANGED; 1496*b0b32e64Ssstefan1 } 1497*b0b32e64Ssstefan1 1498*b0b32e64Ssstefan1 return Changed; 1499*b0b32e64Ssstefan1 } 1500*b0b32e64Ssstefan1 }; 1501*b0b32e64Ssstefan1 1502*b0b32e64Ssstefan1 struct AAICVTrackerCallSite : AAICVTracker { 1503*b0b32e64Ssstefan1 AAICVTrackerCallSite(const IRPosition &IRP, Attributor &A) 1504*b0b32e64Ssstefan1 : AAICVTracker(IRP, A) {} 1505*b0b32e64Ssstefan1 1506*b0b32e64Ssstefan1 void initialize(Attributor &A) override { 1507*b0b32e64Ssstefan1 Function *F = getAnchorScope(); 1508*b0b32e64Ssstefan1 if (!F || !A.isFunctionIPOAmendable(*F)) 1509*b0b32e64Ssstefan1 indicatePessimisticFixpoint(); 1510*b0b32e64Ssstefan1 1511*b0b32e64Ssstefan1 // We only initialize this AA for getters, so we need to know which ICV it 1512*b0b32e64Ssstefan1 // gets. 1513*b0b32e64Ssstefan1 auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 1514*b0b32e64Ssstefan1 for (InternalControlVar ICV : TrackableICVs) { 1515*b0b32e64Ssstefan1 auto ICVInfo = OMPInfoCache.ICVs[ICV]; 1516*b0b32e64Ssstefan1 auto &Getter = OMPInfoCache.RFIs[ICVInfo.Getter]; 1517*b0b32e64Ssstefan1 if (Getter.Declaration == getAssociatedFunction()) { 1518*b0b32e64Ssstefan1 AssociatedICV = ICVInfo.Kind; 1519*b0b32e64Ssstefan1 return; 1520*b0b32e64Ssstefan1 } 1521*b0b32e64Ssstefan1 } 1522*b0b32e64Ssstefan1 1523*b0b32e64Ssstefan1 /// Unknown ICV. 1524*b0b32e64Ssstefan1 indicatePessimisticFixpoint(); 1525*b0b32e64Ssstefan1 } 1526*b0b32e64Ssstefan1 1527*b0b32e64Ssstefan1 ChangeStatus manifest(Attributor &A) override { 1528*b0b32e64Ssstefan1 if (!ReplVal.hasValue() || !ReplVal.getValue()) 1529*b0b32e64Ssstefan1 return ChangeStatus::UNCHANGED; 1530*b0b32e64Ssstefan1 1531*b0b32e64Ssstefan1 A.changeValueAfterManifest(*getCtxI(), **ReplVal); 1532*b0b32e64Ssstefan1 A.deleteAfterManifest(*getCtxI()); 1533*b0b32e64Ssstefan1 1534*b0b32e64Ssstefan1 return ChangeStatus::CHANGED; 1535*b0b32e64Ssstefan1 } 1536*b0b32e64Ssstefan1 1537*b0b32e64Ssstefan1 // FIXME: come up with better string. 1538*b0b32e64Ssstefan1 const std::string getAsStr() const override { return "ICVTrackerCallSite"; } 1539*b0b32e64Ssstefan1 1540*b0b32e64Ssstefan1 // FIXME: come up with some stats. 1541*b0b32e64Ssstefan1 void trackStatistics() const override {} 1542*b0b32e64Ssstefan1 1543*b0b32e64Ssstefan1 InternalControlVar AssociatedICV; 1544*b0b32e64Ssstefan1 Optional<Value *> ReplVal; 1545*b0b32e64Ssstefan1 1546*b0b32e64Ssstefan1 ChangeStatus updateImpl(Attributor &A) override { 1547*b0b32e64Ssstefan1 const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 1548*b0b32e64Ssstefan1 *this, IRPosition::function(*getAnchorScope())); 1549*b0b32e64Ssstefan1 1550*b0b32e64Ssstefan1 // We don't have any information, so we assume it changes the ICV. 1551*b0b32e64Ssstefan1 if (!ICVTrackingAA.isAssumedTracked()) 1552*b0b32e64Ssstefan1 return indicatePessimisticFixpoint(); 1553*b0b32e64Ssstefan1 1554*b0b32e64Ssstefan1 Optional<Value *> NewReplVal = 1555*b0b32e64Ssstefan1 ICVTrackingAA.getReplacementValue(AssociatedICV, getCtxI(), A); 1556*b0b32e64Ssstefan1 1557*b0b32e64Ssstefan1 if (ReplVal == NewReplVal) 1558*b0b32e64Ssstefan1 return ChangeStatus::UNCHANGED; 1559*b0b32e64Ssstefan1 1560*b0b32e64Ssstefan1 ReplVal = NewReplVal; 1561*b0b32e64Ssstefan1 return ChangeStatus::CHANGED; 1562*b0b32e64Ssstefan1 } 1563*b0b32e64Ssstefan1 1564*b0b32e64Ssstefan1 // Return the value with which associated value can be replaced for specific 1565*b0b32e64Ssstefan1 // \p ICV. 1566*b0b32e64Ssstefan1 Optional<Value *> 1567*b0b32e64Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 1568*b0b32e64Ssstefan1 return ReplVal; 1569*b0b32e64Ssstefan1 } 1570*b0b32e64Ssstefan1 }; 1571*b0b32e64Ssstefan1 1572*b0b32e64Ssstefan1 struct AAICVTrackerCallSiteReturned : AAICVTracker { 1573*b0b32e64Ssstefan1 AAICVTrackerCallSiteReturned(const IRPosition &IRP, Attributor &A) 1574*b0b32e64Ssstefan1 : AAICVTracker(IRP, A) {} 1575*b0b32e64Ssstefan1 1576*b0b32e64Ssstefan1 // FIXME: come up with better string. 1577*b0b32e64Ssstefan1 const std::string getAsStr() const override { 1578*b0b32e64Ssstefan1 return "ICVTrackerCallSiteReturned"; 1579*b0b32e64Ssstefan1 } 1580*b0b32e64Ssstefan1 1581*b0b32e64Ssstefan1 // FIXME: come up with some stats. 1582*b0b32e64Ssstefan1 void trackStatistics() const override {} 1583*b0b32e64Ssstefan1 1584*b0b32e64Ssstefan1 /// We don't manifest anything for this AA. 1585*b0b32e64Ssstefan1 ChangeStatus manifest(Attributor &A) override { 1586*b0b32e64Ssstefan1 return ChangeStatus::UNCHANGED; 1587*b0b32e64Ssstefan1 } 1588*b0b32e64Ssstefan1 1589*b0b32e64Ssstefan1 // Map of ICV to their values at specific program point. 1590*b0b32e64Ssstefan1 EnumeratedArray<Optional<Value *>, InternalControlVar, 1591*b0b32e64Ssstefan1 InternalControlVar::ICV___last> 1592*b0b32e64Ssstefan1 ICVReplacementValuesMap; 1593*b0b32e64Ssstefan1 1594*b0b32e64Ssstefan1 /// Return the value with which associated value can be replaced for specific 1595*b0b32e64Ssstefan1 /// \p ICV. 1596*b0b32e64Ssstefan1 Optional<Value *> 1597*b0b32e64Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 1598*b0b32e64Ssstefan1 return ICVReplacementValuesMap[ICV]; 1599*b0b32e64Ssstefan1 } 1600*b0b32e64Ssstefan1 1601*b0b32e64Ssstefan1 ChangeStatus updateImpl(Attributor &A) override { 1602*b0b32e64Ssstefan1 ChangeStatus Changed = ChangeStatus::UNCHANGED; 1603*b0b32e64Ssstefan1 const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 1604*b0b32e64Ssstefan1 *this, IRPosition::returned(*getAssociatedFunction())); 1605*b0b32e64Ssstefan1 1606*b0b32e64Ssstefan1 // We don't have any information, so we assume it changes the ICV. 1607*b0b32e64Ssstefan1 if (!ICVTrackingAA.isAssumedTracked()) 1608*b0b32e64Ssstefan1 return indicatePessimisticFixpoint(); 1609*b0b32e64Ssstefan1 1610*b0b32e64Ssstefan1 for (InternalControlVar ICV : TrackableICVs) { 1611*b0b32e64Ssstefan1 Optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV]; 1612*b0b32e64Ssstefan1 Optional<Value *> NewReplVal = 1613*b0b32e64Ssstefan1 ICVTrackingAA.getUniqueReplacementValue(ICV); 1614*b0b32e64Ssstefan1 1615*b0b32e64Ssstefan1 if (ReplVal == NewReplVal) 1616*b0b32e64Ssstefan1 continue; 1617*b0b32e64Ssstefan1 1618*b0b32e64Ssstefan1 ReplVal = NewReplVal; 1619*b0b32e64Ssstefan1 Changed = ChangeStatus::CHANGED; 1620*b0b32e64Ssstefan1 } 1621*b0b32e64Ssstefan1 return Changed; 1622*b0b32e64Ssstefan1 } 16239548b74aSJohannes Doerfert }; 16249548b74aSJohannes Doerfert } // namespace 16259548b74aSJohannes Doerfert 1626b8235d2bSsstefan1 const char AAICVTracker::ID = 0; 1627b8235d2bSsstefan1 1628b8235d2bSsstefan1 AAICVTracker &AAICVTracker::createForPosition(const IRPosition &IRP, 1629b8235d2bSsstefan1 Attributor &A) { 1630b8235d2bSsstefan1 AAICVTracker *AA = nullptr; 1631b8235d2bSsstefan1 switch (IRP.getPositionKind()) { 1632b8235d2bSsstefan1 case IRPosition::IRP_INVALID: 1633b8235d2bSsstefan1 case IRPosition::IRP_FLOAT: 1634b8235d2bSsstefan1 case IRPosition::IRP_ARGUMENT: 1635b8235d2bSsstefan1 case IRPosition::IRP_CALL_SITE_ARGUMENT: 1636*b0b32e64Ssstefan1 llvm_unreachable("ICVTracker: invalid IRPosition!"); 1637b8235d2bSsstefan1 case IRPosition::IRP_FUNCTION: 1638b8235d2bSsstefan1 AA = new (A.Allocator) AAICVTrackerFunction(IRP, A); 1639b8235d2bSsstefan1 break; 1640*b0b32e64Ssstefan1 case IRPosition::IRP_RETURNED: 1641*b0b32e64Ssstefan1 AA = new (A.Allocator) AAICVTrackerFunctionReturned(IRP, A); 1642*b0b32e64Ssstefan1 break; 1643*b0b32e64Ssstefan1 case IRPosition::IRP_CALL_SITE_RETURNED: 1644*b0b32e64Ssstefan1 AA = new (A.Allocator) AAICVTrackerCallSiteReturned(IRP, A); 1645*b0b32e64Ssstefan1 break; 1646*b0b32e64Ssstefan1 case IRPosition::IRP_CALL_SITE: 1647*b0b32e64Ssstefan1 AA = new (A.Allocator) AAICVTrackerCallSite(IRP, A); 1648*b0b32e64Ssstefan1 break; 1649b8235d2bSsstefan1 } 1650b8235d2bSsstefan1 1651b8235d2bSsstefan1 return *AA; 1652b8235d2bSsstefan1 } 1653b8235d2bSsstefan1 16549548b74aSJohannes Doerfert PreservedAnalyses OpenMPOptPass::run(LazyCallGraph::SCC &C, 16559548b74aSJohannes Doerfert CGSCCAnalysisManager &AM, 16569548b74aSJohannes Doerfert LazyCallGraph &CG, CGSCCUpdateResult &UR) { 16579548b74aSJohannes Doerfert if (!containsOpenMP(*C.begin()->getFunction().getParent(), OMPInModule)) 16589548b74aSJohannes Doerfert return PreservedAnalyses::all(); 16599548b74aSJohannes Doerfert 16609548b74aSJohannes Doerfert if (DisableOpenMPOptimizations) 16619548b74aSJohannes Doerfert return PreservedAnalyses::all(); 16629548b74aSJohannes Doerfert 1663ee17263aSJohannes Doerfert SmallVector<Function *, 16> SCC; 1664351d234dSRoman Lebedev // If there are kernels in the module, we have to run on all SCC's. 1665351d234dSRoman Lebedev bool SCCIsInteresting = !OMPInModule.getKernels().empty(); 1666351d234dSRoman Lebedev for (LazyCallGraph::Node &N : C) { 1667351d234dSRoman Lebedev Function *Fn = &N.getFunction(); 1668351d234dSRoman Lebedev SCC.push_back(Fn); 16699548b74aSJohannes Doerfert 1670351d234dSRoman Lebedev // Do we already know that the SCC contains kernels, 1671351d234dSRoman Lebedev // or that OpenMP functions are called from this SCC? 1672351d234dSRoman Lebedev if (SCCIsInteresting) 1673351d234dSRoman Lebedev continue; 1674351d234dSRoman Lebedev // If not, let's check that. 1675351d234dSRoman Lebedev SCCIsInteresting |= OMPInModule.containsOMPRuntimeCalls(Fn); 1676351d234dSRoman Lebedev } 1677351d234dSRoman Lebedev 1678351d234dSRoman Lebedev if (!SCCIsInteresting || SCC.empty()) 16799548b74aSJohannes Doerfert return PreservedAnalyses::all(); 16809548b74aSJohannes Doerfert 16814d4ea9acSHuber, Joseph FunctionAnalysisManager &FAM = 16824d4ea9acSHuber, Joseph AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); 16837cfd267cSsstefan1 16847cfd267cSsstefan1 AnalysisGetter AG(FAM); 16857cfd267cSsstefan1 16867cfd267cSsstefan1 auto OREGetter = [&FAM](Function *F) -> OptimizationRemarkEmitter & { 16874d4ea9acSHuber, Joseph return FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F); 16884d4ea9acSHuber, Joseph }; 16894d4ea9acSHuber, Joseph 16909548b74aSJohannes Doerfert CallGraphUpdater CGUpdater; 16919548b74aSJohannes Doerfert CGUpdater.initialize(CG, C, AM, UR); 16927cfd267cSsstefan1 16937cfd267cSsstefan1 SetVector<Function *> Functions(SCC.begin(), SCC.end()); 16947cfd267cSsstefan1 BumpPtrAllocator Allocator; 16957cfd267cSsstefan1 OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, Allocator, 1696624d34afSJohannes Doerfert /*CGSCC*/ Functions, OMPInModule.getKernels()); 16977cfd267cSsstefan1 1698*b0b32e64Ssstefan1 SetVector<Function *> ModuleSlice(InfoCache.ModuleSlice.begin(), 1699*b0b32e64Ssstefan1 InfoCache.ModuleSlice.end()); 1700*b0b32e64Ssstefan1 Attributor A(ModuleSlice, InfoCache, CGUpdater); 1701b8235d2bSsstefan1 17029548b74aSJohannes Doerfert // TODO: Compute the module slice we are allowed to look at. 1703b8235d2bSsstefan1 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A); 17049548b74aSJohannes Doerfert bool Changed = OMPOpt.run(); 1705694ded37SGiorgis Georgakoudis if (Changed) 1706694ded37SGiorgis Georgakoudis return PreservedAnalyses::none(); 1707694ded37SGiorgis Georgakoudis 17089548b74aSJohannes Doerfert return PreservedAnalyses::all(); 17099548b74aSJohannes Doerfert } 17109548b74aSJohannes Doerfert 17119548b74aSJohannes Doerfert namespace { 17129548b74aSJohannes Doerfert 17139548b74aSJohannes Doerfert struct OpenMPOptLegacyPass : public CallGraphSCCPass { 17149548b74aSJohannes Doerfert CallGraphUpdater CGUpdater; 17159548b74aSJohannes Doerfert OpenMPInModule OMPInModule; 17169548b74aSJohannes Doerfert static char ID; 17179548b74aSJohannes Doerfert 17189548b74aSJohannes Doerfert OpenMPOptLegacyPass() : CallGraphSCCPass(ID) { 17199548b74aSJohannes Doerfert initializeOpenMPOptLegacyPassPass(*PassRegistry::getPassRegistry()); 17209548b74aSJohannes Doerfert } 17219548b74aSJohannes Doerfert 17229548b74aSJohannes Doerfert void getAnalysisUsage(AnalysisUsage &AU) const override { 17239548b74aSJohannes Doerfert CallGraphSCCPass::getAnalysisUsage(AU); 17249548b74aSJohannes Doerfert } 17259548b74aSJohannes Doerfert 17269548b74aSJohannes Doerfert bool doInitialization(CallGraph &CG) override { 17279548b74aSJohannes Doerfert // Disable the pass if there is no OpenMP (runtime call) in the module. 17289548b74aSJohannes Doerfert containsOpenMP(CG.getModule(), OMPInModule); 17299548b74aSJohannes Doerfert return false; 17309548b74aSJohannes Doerfert } 17319548b74aSJohannes Doerfert 17329548b74aSJohannes Doerfert bool runOnSCC(CallGraphSCC &CGSCC) override { 17339548b74aSJohannes Doerfert if (!containsOpenMP(CGSCC.getCallGraph().getModule(), OMPInModule)) 17349548b74aSJohannes Doerfert return false; 17359548b74aSJohannes Doerfert if (DisableOpenMPOptimizations || skipSCC(CGSCC)) 17369548b74aSJohannes Doerfert return false; 17379548b74aSJohannes Doerfert 1738ee17263aSJohannes Doerfert SmallVector<Function *, 16> SCC; 1739351d234dSRoman Lebedev // If there are kernels in the module, we have to run on all SCC's. 1740351d234dSRoman Lebedev bool SCCIsInteresting = !OMPInModule.getKernels().empty(); 1741351d234dSRoman Lebedev for (CallGraphNode *CGN : CGSCC) { 1742351d234dSRoman Lebedev Function *Fn = CGN->getFunction(); 1743351d234dSRoman Lebedev if (!Fn || Fn->isDeclaration()) 1744351d234dSRoman Lebedev continue; 1745ee17263aSJohannes Doerfert SCC.push_back(Fn); 17469548b74aSJohannes Doerfert 1747351d234dSRoman Lebedev // Do we already know that the SCC contains kernels, 1748351d234dSRoman Lebedev // or that OpenMP functions are called from this SCC? 1749351d234dSRoman Lebedev if (SCCIsInteresting) 1750351d234dSRoman Lebedev continue; 1751351d234dSRoman Lebedev // If not, let's check that. 1752351d234dSRoman Lebedev SCCIsInteresting |= OMPInModule.containsOMPRuntimeCalls(Fn); 1753351d234dSRoman Lebedev } 1754351d234dSRoman Lebedev 1755351d234dSRoman Lebedev if (!SCCIsInteresting || SCC.empty()) 17569548b74aSJohannes Doerfert return false; 17579548b74aSJohannes Doerfert 17589548b74aSJohannes Doerfert CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph(); 17599548b74aSJohannes Doerfert CGUpdater.initialize(CG, CGSCC); 17609548b74aSJohannes Doerfert 17614d4ea9acSHuber, Joseph // Maintain a map of functions to avoid rebuilding the ORE 17624d4ea9acSHuber, Joseph DenseMap<Function *, std::unique_ptr<OptimizationRemarkEmitter>> OREMap; 17634d4ea9acSHuber, Joseph auto OREGetter = [&OREMap](Function *F) -> OptimizationRemarkEmitter & { 17644d4ea9acSHuber, Joseph std::unique_ptr<OptimizationRemarkEmitter> &ORE = OREMap[F]; 17654d4ea9acSHuber, Joseph if (!ORE) 17664d4ea9acSHuber, Joseph ORE = std::make_unique<OptimizationRemarkEmitter>(F); 17674d4ea9acSHuber, Joseph return *ORE; 17684d4ea9acSHuber, Joseph }; 17694d4ea9acSHuber, Joseph 17707cfd267cSsstefan1 AnalysisGetter AG; 17717cfd267cSsstefan1 SetVector<Function *> Functions(SCC.begin(), SCC.end()); 17727cfd267cSsstefan1 BumpPtrAllocator Allocator; 1773e8039ad4SJohannes Doerfert OMPInformationCache InfoCache( 1774e8039ad4SJohannes Doerfert *(Functions.back()->getParent()), AG, Allocator, 1775624d34afSJohannes Doerfert /*CGSCC*/ Functions, OMPInModule.getKernels()); 17767cfd267cSsstefan1 1777*b0b32e64Ssstefan1 SetVector<Function *> ModuleSlice(InfoCache.ModuleSlice.begin(), 1778*b0b32e64Ssstefan1 InfoCache.ModuleSlice.end()); 1779*b0b32e64Ssstefan1 Attributor A(ModuleSlice, InfoCache, CGUpdater); 1780b8235d2bSsstefan1 17819548b74aSJohannes Doerfert // TODO: Compute the module slice we are allowed to look at. 1782b8235d2bSsstefan1 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A); 17839548b74aSJohannes Doerfert return OMPOpt.run(); 17849548b74aSJohannes Doerfert } 17859548b74aSJohannes Doerfert 17869548b74aSJohannes Doerfert bool doFinalization(CallGraph &CG) override { return CGUpdater.finalize(); } 17879548b74aSJohannes Doerfert }; 17889548b74aSJohannes Doerfert 17899548b74aSJohannes Doerfert } // end anonymous namespace 17909548b74aSJohannes Doerfert 1791e8039ad4SJohannes Doerfert void OpenMPInModule::identifyKernels(Module &M) { 1792e8039ad4SJohannes Doerfert 1793e8039ad4SJohannes Doerfert NamedMDNode *MD = M.getOrInsertNamedMetadata("nvvm.annotations"); 1794e8039ad4SJohannes Doerfert if (!MD) 1795e8039ad4SJohannes Doerfert return; 1796e8039ad4SJohannes Doerfert 1797e8039ad4SJohannes Doerfert for (auto *Op : MD->operands()) { 1798e8039ad4SJohannes Doerfert if (Op->getNumOperands() < 2) 1799e8039ad4SJohannes Doerfert continue; 1800e8039ad4SJohannes Doerfert MDString *KindID = dyn_cast<MDString>(Op->getOperand(1)); 1801e8039ad4SJohannes Doerfert if (!KindID || KindID->getString() != "kernel") 1802e8039ad4SJohannes Doerfert continue; 1803e8039ad4SJohannes Doerfert 1804e8039ad4SJohannes Doerfert Function *KernelFn = 1805e8039ad4SJohannes Doerfert mdconst::dyn_extract_or_null<Function>(Op->getOperand(0)); 1806e8039ad4SJohannes Doerfert if (!KernelFn) 1807e8039ad4SJohannes Doerfert continue; 1808e8039ad4SJohannes Doerfert 1809e8039ad4SJohannes Doerfert ++NumOpenMPTargetRegionKernels; 1810e8039ad4SJohannes Doerfert 1811e8039ad4SJohannes Doerfert Kernels.insert(KernelFn); 1812e8039ad4SJohannes Doerfert } 1813e8039ad4SJohannes Doerfert } 1814e8039ad4SJohannes Doerfert 18159548b74aSJohannes Doerfert bool llvm::omp::containsOpenMP(Module &M, OpenMPInModule &OMPInModule) { 18169548b74aSJohannes Doerfert if (OMPInModule.isKnown()) 18179548b74aSJohannes Doerfert return OMPInModule; 1818dce6bc18SJohannes Doerfert 1819351d234dSRoman Lebedev auto RecordFunctionsContainingUsesOf = [&](Function *F) { 1820351d234dSRoman Lebedev for (User *U : F->users()) 1821351d234dSRoman Lebedev if (auto *I = dyn_cast<Instruction>(U)) 1822351d234dSRoman Lebedev OMPInModule.FuncsWithOMPRuntimeCalls.insert(I->getFunction()); 1823351d234dSRoman Lebedev }; 1824351d234dSRoman Lebedev 1825dce6bc18SJohannes Doerfert // MSVC doesn't like long if-else chains for some reason and instead just 1826dce6bc18SJohannes Doerfert // issues an error. Work around it.. 1827dce6bc18SJohannes Doerfert do { 18289548b74aSJohannes Doerfert #define OMP_RTL(_Enum, _Name, ...) \ 1829351d234dSRoman Lebedev if (Function *F = M.getFunction(_Name)) { \ 1830351d234dSRoman Lebedev RecordFunctionsContainingUsesOf(F); \ 1831dce6bc18SJohannes Doerfert OMPInModule = true; \ 1832dce6bc18SJohannes Doerfert } 18339548b74aSJohannes Doerfert #include "llvm/Frontend/OpenMP/OMPKinds.def" 1834dce6bc18SJohannes Doerfert } while (false); 1835e8039ad4SJohannes Doerfert 1836e8039ad4SJohannes Doerfert // Identify kernels once. TODO: We should split the OMPInformationCache into a 1837e8039ad4SJohannes Doerfert // module and an SCC part. The kernel information, among other things, could 1838e8039ad4SJohannes Doerfert // go into the module part. 1839e8039ad4SJohannes Doerfert if (OMPInModule.isKnown() && OMPInModule) { 1840e8039ad4SJohannes Doerfert OMPInModule.identifyKernels(M); 1841e8039ad4SJohannes Doerfert return true; 1842e8039ad4SJohannes Doerfert } 1843e8039ad4SJohannes Doerfert 18449548b74aSJohannes Doerfert return OMPInModule = false; 18459548b74aSJohannes Doerfert } 18469548b74aSJohannes Doerfert 18479548b74aSJohannes Doerfert char OpenMPOptLegacyPass::ID = 0; 18489548b74aSJohannes Doerfert 18499548b74aSJohannes Doerfert INITIALIZE_PASS_BEGIN(OpenMPOptLegacyPass, "openmpopt", 18509548b74aSJohannes Doerfert "OpenMP specific optimizations", false, false) 18519548b74aSJohannes Doerfert INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) 18529548b74aSJohannes Doerfert INITIALIZE_PASS_END(OpenMPOptLegacyPass, "openmpopt", 18539548b74aSJohannes Doerfert "OpenMP specific optimizations", false, false) 18549548b74aSJohannes Doerfert 18559548b74aSJohannes Doerfert Pass *llvm::createOpenMPOptLegacyPass() { return new OpenMPOptLegacyPass(); } 1856