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. 12ca1560daSJoseph Huber // - Replacing globalized device memory with stack memory. 13ca1560daSJoseph Huber // - Replacing globalized device memory with shared memory. 14b910a109SJoseph Huber // - Parallel region merging. 15b910a109SJoseph Huber // - Transforming generic-mode device kernels to SPMD mode. 16b910a109SJoseph Huber // - Specializing the state machine for generic-mode device kernels. 179548b74aSJohannes Doerfert // 189548b74aSJohannes Doerfert //===----------------------------------------------------------------------===// 199548b74aSJohannes Doerfert 209548b74aSJohannes Doerfert #include "llvm/Transforms/IPO/OpenMPOpt.h" 219548b74aSJohannes Doerfert 229548b74aSJohannes Doerfert #include "llvm/ADT/EnumeratedArray.h" 2318283125SJoseph Huber #include "llvm/ADT/PostOrderIterator.h" 249548b74aSJohannes Doerfert #include "llvm/ADT/Statistic.h" 259548b74aSJohannes Doerfert #include "llvm/Analysis/CallGraph.h" 269548b74aSJohannes Doerfert #include "llvm/Analysis/CallGraphSCCPass.h" 274d4ea9acSHuber, Joseph #include "llvm/Analysis/OptimizationRemarkEmitter.h" 283a6bfcf2SGiorgis Georgakoudis #include "llvm/Analysis/ValueTracking.h" 299548b74aSJohannes Doerfert #include "llvm/Frontend/OpenMP/OMPConstants.h" 30e28936f6SJohannes Doerfert #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" 31d9659bf6SJohannes Doerfert #include "llvm/IR/Assumptions.h" 32d9659bf6SJohannes Doerfert #include "llvm/IR/DiagnosticInfo.h" 33514c033dSJohannes Doerfert #include "llvm/IR/GlobalValue.h" 34d9659bf6SJohannes Doerfert #include "llvm/IR/Instruction.h" 3568abc3d2SJoseph Huber #include "llvm/IR/IntrinsicInst.h" 369548b74aSJohannes Doerfert #include "llvm/InitializePasses.h" 379548b74aSJohannes Doerfert #include "llvm/Support/CommandLine.h" 389548b74aSJohannes Doerfert #include "llvm/Transforms/IPO.h" 397cfd267cSsstefan1 #include "llvm/Transforms/IPO/Attributor.h" 403a6bfcf2SGiorgis Georgakoudis #include "llvm/Transforms/Utils/BasicBlockUtils.h" 419548b74aSJohannes Doerfert #include "llvm/Transforms/Utils/CallGraphUpdater.h" 4297517055SGiorgis Georgakoudis #include "llvm/Transforms/Utils/CodeExtractor.h" 439548b74aSJohannes Doerfert 449548b74aSJohannes Doerfert using namespace llvm; 459548b74aSJohannes Doerfert using namespace omp; 469548b74aSJohannes Doerfert 479548b74aSJohannes Doerfert #define DEBUG_TYPE "openmp-opt" 489548b74aSJohannes Doerfert 499548b74aSJohannes Doerfert static cl::opt<bool> DisableOpenMPOptimizations( 509548b74aSJohannes Doerfert "openmp-opt-disable", cl::ZeroOrMore, 519548b74aSJohannes Doerfert cl::desc("Disable OpenMP specific optimizations."), cl::Hidden, 529548b74aSJohannes Doerfert cl::init(false)); 539548b74aSJohannes Doerfert 543a6bfcf2SGiorgis Georgakoudis static cl::opt<bool> EnableParallelRegionMerging( 553a6bfcf2SGiorgis Georgakoudis "openmp-opt-enable-merging", cl::ZeroOrMore, 563a6bfcf2SGiorgis Georgakoudis cl::desc("Enable the OpenMP region merging optimization."), cl::Hidden, 573a6bfcf2SGiorgis Georgakoudis cl::init(false)); 583a6bfcf2SGiorgis Georgakoudis 590f426935Ssstefan1 static cl::opt<bool> PrintICVValues("openmp-print-icv-values", cl::init(false), 600f426935Ssstefan1 cl::Hidden); 61e8039ad4SJohannes Doerfert static cl::opt<bool> PrintOpenMPKernels("openmp-print-gpu-kernels", 62e8039ad4SJohannes Doerfert cl::init(false), cl::Hidden); 630f426935Ssstefan1 64496f8e5bSHamilton Tobon Mosquera static cl::opt<bool> HideMemoryTransferLatency( 65496f8e5bSHamilton Tobon Mosquera "openmp-hide-memory-transfer-latency", 66496f8e5bSHamilton Tobon Mosquera cl::desc("[WIP] Tries to hide the latency of host to device memory" 67496f8e5bSHamilton Tobon Mosquera " transfers"), 68496f8e5bSHamilton Tobon Mosquera cl::Hidden, cl::init(false)); 69496f8e5bSHamilton Tobon Mosquera 709548b74aSJohannes Doerfert STATISTIC(NumOpenMPRuntimeCallsDeduplicated, 719548b74aSJohannes Doerfert "Number of OpenMP runtime calls deduplicated"); 7255eb714aSRoman Lebedev STATISTIC(NumOpenMPParallelRegionsDeleted, 7355eb714aSRoman Lebedev "Number of OpenMP parallel regions deleted"); 749548b74aSJohannes Doerfert STATISTIC(NumOpenMPRuntimeFunctionsIdentified, 759548b74aSJohannes Doerfert "Number of OpenMP runtime functions identified"); 769548b74aSJohannes Doerfert STATISTIC(NumOpenMPRuntimeFunctionUsesIdentified, 779548b74aSJohannes Doerfert "Number of OpenMP runtime function uses identified"); 78e8039ad4SJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernels, 79e8039ad4SJohannes Doerfert "Number of OpenMP target region entry points (=kernels) identified"); 80514c033dSJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernelsSPMD, 81514c033dSJohannes Doerfert "Number of OpenMP target region entry points (=kernels) executed in " 82514c033dSJohannes Doerfert "SPMD-mode instead of generic-mode"); 83d9659bf6SJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernelsWithoutStateMachine, 84d9659bf6SJohannes Doerfert "Number of OpenMP target region entry points (=kernels) executed in " 85d9659bf6SJohannes Doerfert "generic-mode without a state machines"); 86d9659bf6SJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback, 87d9659bf6SJohannes Doerfert "Number of OpenMP target region entry points (=kernels) executed in " 88d9659bf6SJohannes Doerfert "generic-mode with customized state machines with fallback"); 89d9659bf6SJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback, 90d9659bf6SJohannes Doerfert "Number of OpenMP target region entry points (=kernels) executed in " 91d9659bf6SJohannes Doerfert "generic-mode with customized state machines without fallback"); 925b0581aeSJohannes Doerfert STATISTIC( 935b0581aeSJohannes Doerfert NumOpenMPParallelRegionsReplacedInGPUStateMachine, 945b0581aeSJohannes Doerfert "Number of OpenMP parallel regions replaced with ID in GPU state machines"); 953a6bfcf2SGiorgis Georgakoudis STATISTIC(NumOpenMPParallelRegionsMerged, 963a6bfcf2SGiorgis Georgakoudis "Number of OpenMP parallel regions merged"); 976fc51c9fSJoseph Huber STATISTIC(NumBytesMovedToSharedMemory, 986fc51c9fSJoseph Huber "Amount of memory pushed to shared memory"); 999548b74aSJohannes Doerfert 100263c4a3cSrathod-sahaab #if !defined(NDEBUG) 1019548b74aSJohannes Doerfert static constexpr auto TAG = "[" DEBUG_TYPE "]"; 102a50c0b0dSMikael Holmen #endif 1039548b74aSJohannes Doerfert 1049548b74aSJohannes Doerfert namespace { 1059548b74aSJohannes Doerfert 1066fc51c9fSJoseph Huber enum class AddressSpace : unsigned { 1076fc51c9fSJoseph Huber Generic = 0, 1086fc51c9fSJoseph Huber Global = 1, 1096fc51c9fSJoseph Huber Shared = 3, 1106fc51c9fSJoseph Huber Constant = 4, 1116fc51c9fSJoseph Huber Local = 5, 1126fc51c9fSJoseph Huber }; 1136fc51c9fSJoseph Huber 1146fc51c9fSJoseph Huber struct AAHeapToShared; 1156fc51c9fSJoseph Huber 116b8235d2bSsstefan1 struct AAICVTracker; 117b8235d2bSsstefan1 1187cfd267cSsstefan1 /// OpenMP specific information. For now, stores RFIs and ICVs also needed for 1197cfd267cSsstefan1 /// Attributor runs. 1207cfd267cSsstefan1 struct OMPInformationCache : public InformationCache { 1217cfd267cSsstefan1 OMPInformationCache(Module &M, AnalysisGetter &AG, 122624d34afSJohannes Doerfert BumpPtrAllocator &Allocator, SetVector<Function *> &CGSCC, 123e8039ad4SJohannes Doerfert SmallPtrSetImpl<Kernel> &Kernels) 124624d34afSJohannes Doerfert : InformationCache(M, AG, Allocator, &CGSCC), OMPBuilder(M), 125624d34afSJohannes Doerfert Kernels(Kernels) { 126624d34afSJohannes Doerfert 12761238d26Ssstefan1 OMPBuilder.initialize(); 1289548b74aSJohannes Doerfert initializeRuntimeFunctions(); 1290f426935Ssstefan1 initializeInternalControlVars(); 1309548b74aSJohannes Doerfert } 1319548b74aSJohannes Doerfert 1320f426935Ssstefan1 /// Generic information that describes an internal control variable. 1330f426935Ssstefan1 struct InternalControlVarInfo { 1340f426935Ssstefan1 /// The kind, as described by InternalControlVar enum. 1350f426935Ssstefan1 InternalControlVar Kind; 1360f426935Ssstefan1 1370f426935Ssstefan1 /// The name of the ICV. 1380f426935Ssstefan1 StringRef Name; 1390f426935Ssstefan1 1400f426935Ssstefan1 /// Environment variable associated with this ICV. 1410f426935Ssstefan1 StringRef EnvVarName; 1420f426935Ssstefan1 1430f426935Ssstefan1 /// Initial value kind. 1440f426935Ssstefan1 ICVInitValue InitKind; 1450f426935Ssstefan1 1460f426935Ssstefan1 /// Initial value. 1470f426935Ssstefan1 ConstantInt *InitValue; 1480f426935Ssstefan1 1490f426935Ssstefan1 /// Setter RTL function associated with this ICV. 1500f426935Ssstefan1 RuntimeFunction Setter; 1510f426935Ssstefan1 1520f426935Ssstefan1 /// Getter RTL function associated with this ICV. 1530f426935Ssstefan1 RuntimeFunction Getter; 1540f426935Ssstefan1 1550f426935Ssstefan1 /// RTL Function corresponding to the override clause of this ICV 1560f426935Ssstefan1 RuntimeFunction Clause; 1570f426935Ssstefan1 }; 1580f426935Ssstefan1 1599548b74aSJohannes Doerfert /// Generic information that describes a runtime function 1609548b74aSJohannes Doerfert struct RuntimeFunctionInfo { 1618855fec3SJohannes Doerfert 1629548b74aSJohannes Doerfert /// The kind, as described by the RuntimeFunction enum. 1639548b74aSJohannes Doerfert RuntimeFunction Kind; 1649548b74aSJohannes Doerfert 1659548b74aSJohannes Doerfert /// The name of the function. 1669548b74aSJohannes Doerfert StringRef Name; 1679548b74aSJohannes Doerfert 1689548b74aSJohannes Doerfert /// Flag to indicate a variadic function. 1699548b74aSJohannes Doerfert bool IsVarArg; 1709548b74aSJohannes Doerfert 1719548b74aSJohannes Doerfert /// The return type of the function. 1729548b74aSJohannes Doerfert Type *ReturnType; 1739548b74aSJohannes Doerfert 1749548b74aSJohannes Doerfert /// The argument types of the function. 1759548b74aSJohannes Doerfert SmallVector<Type *, 8> ArgumentTypes; 1769548b74aSJohannes Doerfert 1779548b74aSJohannes Doerfert /// The declaration if available. 178f09f4b26SJohannes Doerfert Function *Declaration = nullptr; 1799548b74aSJohannes Doerfert 1809548b74aSJohannes Doerfert /// Uses of this runtime function per function containing the use. 1818855fec3SJohannes Doerfert using UseVector = SmallVector<Use *, 16>; 1828855fec3SJohannes Doerfert 183b8235d2bSsstefan1 /// Clear UsesMap for runtime function. 184b8235d2bSsstefan1 void clearUsesMap() { UsesMap.clear(); } 185b8235d2bSsstefan1 18654bd3751SJohannes Doerfert /// Boolean conversion that is true if the runtime function was found. 18754bd3751SJohannes Doerfert operator bool() const { return Declaration; } 18854bd3751SJohannes Doerfert 1898855fec3SJohannes Doerfert /// Return the vector of uses in function \p F. 1908855fec3SJohannes Doerfert UseVector &getOrCreateUseVector(Function *F) { 191b8235d2bSsstefan1 std::shared_ptr<UseVector> &UV = UsesMap[F]; 1928855fec3SJohannes Doerfert if (!UV) 193b8235d2bSsstefan1 UV = std::make_shared<UseVector>(); 1948855fec3SJohannes Doerfert return *UV; 1958855fec3SJohannes Doerfert } 1968855fec3SJohannes Doerfert 1978855fec3SJohannes Doerfert /// Return the vector of uses in function \p F or `nullptr` if there are 1988855fec3SJohannes Doerfert /// none. 1998855fec3SJohannes Doerfert const UseVector *getUseVector(Function &F) const { 20095e57072SDavid Blaikie auto I = UsesMap.find(&F); 20195e57072SDavid Blaikie if (I != UsesMap.end()) 20295e57072SDavid Blaikie return I->second.get(); 20395e57072SDavid Blaikie return nullptr; 2048855fec3SJohannes Doerfert } 2058855fec3SJohannes Doerfert 2068855fec3SJohannes Doerfert /// Return how many functions contain uses of this runtime function. 2078855fec3SJohannes Doerfert size_t getNumFunctionsWithUses() const { return UsesMap.size(); } 2089548b74aSJohannes Doerfert 2099548b74aSJohannes Doerfert /// Return the number of arguments (or the minimal number for variadic 2109548b74aSJohannes Doerfert /// functions). 2119548b74aSJohannes Doerfert size_t getNumArgs() const { return ArgumentTypes.size(); } 2129548b74aSJohannes Doerfert 2139548b74aSJohannes Doerfert /// Run the callback \p CB on each use and forget the use if the result is 2149548b74aSJohannes Doerfert /// true. The callback will be fed the function in which the use was 2159548b74aSJohannes Doerfert /// encountered as second argument. 216624d34afSJohannes Doerfert void foreachUse(SmallVectorImpl<Function *> &SCC, 217624d34afSJohannes Doerfert function_ref<bool(Use &, Function &)> CB) { 218624d34afSJohannes Doerfert for (Function *F : SCC) 219624d34afSJohannes Doerfert foreachUse(CB, F); 220e099c7b6Ssstefan1 } 221e099c7b6Ssstefan1 222e099c7b6Ssstefan1 /// Run the callback \p CB on each use within the function \p F and forget 223e099c7b6Ssstefan1 /// the use if the result is true. 224624d34afSJohannes Doerfert void foreachUse(function_ref<bool(Use &, Function &)> CB, Function *F) { 2258855fec3SJohannes Doerfert SmallVector<unsigned, 8> ToBeDeleted; 2269548b74aSJohannes Doerfert ToBeDeleted.clear(); 227e099c7b6Ssstefan1 2288855fec3SJohannes Doerfert unsigned Idx = 0; 229624d34afSJohannes Doerfert UseVector &UV = getOrCreateUseVector(F); 230e099c7b6Ssstefan1 2318855fec3SJohannes Doerfert for (Use *U : UV) { 232e099c7b6Ssstefan1 if (CB(*U, *F)) 2338855fec3SJohannes Doerfert ToBeDeleted.push_back(Idx); 2348855fec3SJohannes Doerfert ++Idx; 2358855fec3SJohannes Doerfert } 2368855fec3SJohannes Doerfert 2378855fec3SJohannes Doerfert // Remove the to-be-deleted indices in reverse order as prior 238b726c557SJohannes Doerfert // modifications will not modify the smaller indices. 2398855fec3SJohannes Doerfert while (!ToBeDeleted.empty()) { 2408855fec3SJohannes Doerfert unsigned Idx = ToBeDeleted.pop_back_val(); 2418855fec3SJohannes Doerfert UV[Idx] = UV.back(); 2428855fec3SJohannes Doerfert UV.pop_back(); 2439548b74aSJohannes Doerfert } 2449548b74aSJohannes Doerfert } 2458855fec3SJohannes Doerfert 2468855fec3SJohannes Doerfert private: 2478855fec3SJohannes Doerfert /// Map from functions to all uses of this runtime function contained in 2488855fec3SJohannes Doerfert /// them. 249b8235d2bSsstefan1 DenseMap<Function *, std::shared_ptr<UseVector>> UsesMap; 250d9659bf6SJohannes Doerfert 251d9659bf6SJohannes Doerfert public: 252d9659bf6SJohannes Doerfert /// Iterators for the uses of this runtime function. 253d9659bf6SJohannes Doerfert decltype(UsesMap)::iterator begin() { return UsesMap.begin(); } 254d9659bf6SJohannes Doerfert decltype(UsesMap)::iterator end() { return UsesMap.end(); } 2559548b74aSJohannes Doerfert }; 2569548b74aSJohannes Doerfert 2577cfd267cSsstefan1 /// An OpenMP-IR-Builder instance 2587cfd267cSsstefan1 OpenMPIRBuilder OMPBuilder; 2597cfd267cSsstefan1 2607cfd267cSsstefan1 /// Map from runtime function kind to the runtime function description. 2617cfd267cSsstefan1 EnumeratedArray<RuntimeFunctionInfo, RuntimeFunction, 2627cfd267cSsstefan1 RuntimeFunction::OMPRTL___last> 2637cfd267cSsstefan1 RFIs; 2647cfd267cSsstefan1 265d9659bf6SJohannes Doerfert /// Map from function declarations/definitions to their runtime enum type. 266d9659bf6SJohannes Doerfert DenseMap<Function *, RuntimeFunction> RuntimeFunctionIDMap; 267d9659bf6SJohannes Doerfert 2680f426935Ssstefan1 /// Map from ICV kind to the ICV description. 2690f426935Ssstefan1 EnumeratedArray<InternalControlVarInfo, InternalControlVar, 2700f426935Ssstefan1 InternalControlVar::ICV___last> 2710f426935Ssstefan1 ICVs; 2720f426935Ssstefan1 2730f426935Ssstefan1 /// Helper to initialize all internal control variable information for those 2740f426935Ssstefan1 /// defined in OMPKinds.def. 2750f426935Ssstefan1 void initializeInternalControlVars() { 2760f426935Ssstefan1 #define ICV_RT_SET(_Name, RTL) \ 2770f426935Ssstefan1 { \ 2780f426935Ssstefan1 auto &ICV = ICVs[_Name]; \ 2790f426935Ssstefan1 ICV.Setter = RTL; \ 2800f426935Ssstefan1 } 2810f426935Ssstefan1 #define ICV_RT_GET(Name, RTL) \ 2820f426935Ssstefan1 { \ 2830f426935Ssstefan1 auto &ICV = ICVs[Name]; \ 2840f426935Ssstefan1 ICV.Getter = RTL; \ 2850f426935Ssstefan1 } 2860f426935Ssstefan1 #define ICV_DATA_ENV(Enum, _Name, _EnvVarName, Init) \ 2870f426935Ssstefan1 { \ 2880f426935Ssstefan1 auto &ICV = ICVs[Enum]; \ 2890f426935Ssstefan1 ICV.Name = _Name; \ 2900f426935Ssstefan1 ICV.Kind = Enum; \ 2910f426935Ssstefan1 ICV.InitKind = Init; \ 2920f426935Ssstefan1 ICV.EnvVarName = _EnvVarName; \ 2930f426935Ssstefan1 switch (ICV.InitKind) { \ 294951e43f3Ssstefan1 case ICV_IMPLEMENTATION_DEFINED: \ 2950f426935Ssstefan1 ICV.InitValue = nullptr; \ 2960f426935Ssstefan1 break; \ 297951e43f3Ssstefan1 case ICV_ZERO: \ 2986aab27baSsstefan1 ICV.InitValue = ConstantInt::get( \ 2996aab27baSsstefan1 Type::getInt32Ty(OMPBuilder.Int32->getContext()), 0); \ 3000f426935Ssstefan1 break; \ 301951e43f3Ssstefan1 case ICV_FALSE: \ 3026aab27baSsstefan1 ICV.InitValue = ConstantInt::getFalse(OMPBuilder.Int1->getContext()); \ 3030f426935Ssstefan1 break; \ 304951e43f3Ssstefan1 case ICV_LAST: \ 3050f426935Ssstefan1 break; \ 3060f426935Ssstefan1 } \ 3070f426935Ssstefan1 } 3080f426935Ssstefan1 #include "llvm/Frontend/OpenMP/OMPKinds.def" 3090f426935Ssstefan1 } 3100f426935Ssstefan1 3117cfd267cSsstefan1 /// Returns true if the function declaration \p F matches the runtime 3127cfd267cSsstefan1 /// function types, that is, return type \p RTFRetType, and argument types 3137cfd267cSsstefan1 /// \p RTFArgTypes. 3147cfd267cSsstefan1 static bool declMatchesRTFTypes(Function *F, Type *RTFRetType, 3157cfd267cSsstefan1 SmallVector<Type *, 8> &RTFArgTypes) { 3167cfd267cSsstefan1 // TODO: We should output information to the user (under debug output 3177cfd267cSsstefan1 // and via remarks). 3187cfd267cSsstefan1 3197cfd267cSsstefan1 if (!F) 3207cfd267cSsstefan1 return false; 3217cfd267cSsstefan1 if (F->getReturnType() != RTFRetType) 3227cfd267cSsstefan1 return false; 3237cfd267cSsstefan1 if (F->arg_size() != RTFArgTypes.size()) 3247cfd267cSsstefan1 return false; 3257cfd267cSsstefan1 3267cfd267cSsstefan1 auto RTFTyIt = RTFArgTypes.begin(); 3277cfd267cSsstefan1 for (Argument &Arg : F->args()) { 3287cfd267cSsstefan1 if (Arg.getType() != *RTFTyIt) 3297cfd267cSsstefan1 return false; 3307cfd267cSsstefan1 3317cfd267cSsstefan1 ++RTFTyIt; 3327cfd267cSsstefan1 } 3337cfd267cSsstefan1 3347cfd267cSsstefan1 return true; 3357cfd267cSsstefan1 } 3367cfd267cSsstefan1 337b726c557SJohannes Doerfert // Helper to collect all uses of the declaration in the UsesMap. 338b8235d2bSsstefan1 unsigned collectUses(RuntimeFunctionInfo &RFI, bool CollectStats = true) { 3397cfd267cSsstefan1 unsigned NumUses = 0; 3407cfd267cSsstefan1 if (!RFI.Declaration) 3417cfd267cSsstefan1 return NumUses; 3427cfd267cSsstefan1 OMPBuilder.addAttributes(RFI.Kind, *RFI.Declaration); 3437cfd267cSsstefan1 344b8235d2bSsstefan1 if (CollectStats) { 3457cfd267cSsstefan1 NumOpenMPRuntimeFunctionsIdentified += 1; 3467cfd267cSsstefan1 NumOpenMPRuntimeFunctionUsesIdentified += RFI.Declaration->getNumUses(); 347b8235d2bSsstefan1 } 3487cfd267cSsstefan1 3497cfd267cSsstefan1 // TODO: We directly convert uses into proper calls and unknown uses. 3507cfd267cSsstefan1 for (Use &U : RFI.Declaration->uses()) { 3517cfd267cSsstefan1 if (Instruction *UserI = dyn_cast<Instruction>(U.getUser())) { 3527cfd267cSsstefan1 if (ModuleSlice.count(UserI->getFunction())) { 3537cfd267cSsstefan1 RFI.getOrCreateUseVector(UserI->getFunction()).push_back(&U); 3547cfd267cSsstefan1 ++NumUses; 3557cfd267cSsstefan1 } 3567cfd267cSsstefan1 } else { 3577cfd267cSsstefan1 RFI.getOrCreateUseVector(nullptr).push_back(&U); 3587cfd267cSsstefan1 ++NumUses; 3597cfd267cSsstefan1 } 3607cfd267cSsstefan1 } 3617cfd267cSsstefan1 return NumUses; 362b8235d2bSsstefan1 } 3637cfd267cSsstefan1 36497517055SGiorgis Georgakoudis // Helper function to recollect uses of a runtime function. 36597517055SGiorgis Georgakoudis void recollectUsesForFunction(RuntimeFunction RTF) { 36697517055SGiorgis Georgakoudis auto &RFI = RFIs[RTF]; 367b8235d2bSsstefan1 RFI.clearUsesMap(); 368b8235d2bSsstefan1 collectUses(RFI, /*CollectStats*/ false); 369b8235d2bSsstefan1 } 37097517055SGiorgis Georgakoudis 37197517055SGiorgis Georgakoudis // Helper function to recollect uses of all runtime functions. 37297517055SGiorgis Georgakoudis void recollectUses() { 37397517055SGiorgis Georgakoudis for (int Idx = 0; Idx < RFIs.size(); ++Idx) 37497517055SGiorgis Georgakoudis recollectUsesForFunction(static_cast<RuntimeFunction>(Idx)); 375b8235d2bSsstefan1 } 376b8235d2bSsstefan1 377b8235d2bSsstefan1 /// Helper to initialize all runtime function information for those defined 378b8235d2bSsstefan1 /// in OpenMPKinds.def. 379b8235d2bSsstefan1 void initializeRuntimeFunctions() { 3807cfd267cSsstefan1 Module &M = *((*ModuleSlice.begin())->getParent()); 3817cfd267cSsstefan1 3826aab27baSsstefan1 // Helper macros for handling __VA_ARGS__ in OMP_RTL 3836aab27baSsstefan1 #define OMP_TYPE(VarName, ...) \ 3846aab27baSsstefan1 Type *VarName = OMPBuilder.VarName; \ 3856aab27baSsstefan1 (void)VarName; 3866aab27baSsstefan1 3876aab27baSsstefan1 #define OMP_ARRAY_TYPE(VarName, ...) \ 3886aab27baSsstefan1 ArrayType *VarName##Ty = OMPBuilder.VarName##Ty; \ 3896aab27baSsstefan1 (void)VarName##Ty; \ 3906aab27baSsstefan1 PointerType *VarName##PtrTy = OMPBuilder.VarName##PtrTy; \ 3916aab27baSsstefan1 (void)VarName##PtrTy; 3926aab27baSsstefan1 3936aab27baSsstefan1 #define OMP_FUNCTION_TYPE(VarName, ...) \ 3946aab27baSsstefan1 FunctionType *VarName = OMPBuilder.VarName; \ 3956aab27baSsstefan1 (void)VarName; \ 3966aab27baSsstefan1 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \ 3976aab27baSsstefan1 (void)VarName##Ptr; 3986aab27baSsstefan1 3996aab27baSsstefan1 #define OMP_STRUCT_TYPE(VarName, ...) \ 4006aab27baSsstefan1 StructType *VarName = OMPBuilder.VarName; \ 4016aab27baSsstefan1 (void)VarName; \ 4026aab27baSsstefan1 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \ 4036aab27baSsstefan1 (void)VarName##Ptr; 4046aab27baSsstefan1 4057cfd267cSsstefan1 #define OMP_RTL(_Enum, _Name, _IsVarArg, _ReturnType, ...) \ 4067cfd267cSsstefan1 { \ 4077cfd267cSsstefan1 SmallVector<Type *, 8> ArgsTypes({__VA_ARGS__}); \ 4087cfd267cSsstefan1 Function *F = M.getFunction(_Name); \ 409eef6601bSJoseph Huber RTLFunctions.insert(F); \ 4106aab27baSsstefan1 if (declMatchesRTFTypes(F, OMPBuilder._ReturnType, ArgsTypes)) { \ 411d9659bf6SJohannes Doerfert RuntimeFunctionIDMap[F] = _Enum; \ 4127cfd267cSsstefan1 auto &RFI = RFIs[_Enum]; \ 4137cfd267cSsstefan1 RFI.Kind = _Enum; \ 4147cfd267cSsstefan1 RFI.Name = _Name; \ 4157cfd267cSsstefan1 RFI.IsVarArg = _IsVarArg; \ 4166aab27baSsstefan1 RFI.ReturnType = OMPBuilder._ReturnType; \ 4177cfd267cSsstefan1 RFI.ArgumentTypes = std::move(ArgsTypes); \ 4187cfd267cSsstefan1 RFI.Declaration = F; \ 419b8235d2bSsstefan1 unsigned NumUses = collectUses(RFI); \ 4207cfd267cSsstefan1 (void)NumUses; \ 4217cfd267cSsstefan1 LLVM_DEBUG({ \ 4227cfd267cSsstefan1 dbgs() << TAG << RFI.Name << (RFI.Declaration ? "" : " not") \ 4237cfd267cSsstefan1 << " found\n"; \ 4247cfd267cSsstefan1 if (RFI.Declaration) \ 4257cfd267cSsstefan1 dbgs() << TAG << "-> got " << NumUses << " uses in " \ 4267cfd267cSsstefan1 << RFI.getNumFunctionsWithUses() \ 4277cfd267cSsstefan1 << " different functions.\n"; \ 4287cfd267cSsstefan1 }); \ 4297cfd267cSsstefan1 } \ 4307cfd267cSsstefan1 } 4317cfd267cSsstefan1 #include "llvm/Frontend/OpenMP/OMPKinds.def" 4327cfd267cSsstefan1 4337cfd267cSsstefan1 // TODO: We should attach the attributes defined in OMPKinds.def. 4347cfd267cSsstefan1 } 435e8039ad4SJohannes Doerfert 436e8039ad4SJohannes Doerfert /// Collection of known kernels (\see Kernel) in the module. 437e8039ad4SJohannes Doerfert SmallPtrSetImpl<Kernel> &Kernels; 438eef6601bSJoseph Huber 439eef6601bSJoseph Huber /// Collection of known OpenMP runtime functions.. 440eef6601bSJoseph Huber DenseSet<const Function *> RTLFunctions; 4417cfd267cSsstefan1 }; 4427cfd267cSsstefan1 443d9659bf6SJohannes Doerfert template <typename Ty, bool InsertInvalidates = true> 444d9659bf6SJohannes Doerfert struct BooleanStateWithPtrSetVector : public BooleanState { 445d9659bf6SJohannes Doerfert 446d9659bf6SJohannes Doerfert bool contains(Ty *Elem) const { return Set.contains(Elem); } 447d9659bf6SJohannes Doerfert bool insert(Ty *Elem) { 448d9659bf6SJohannes Doerfert if (InsertInvalidates) 449d9659bf6SJohannes Doerfert BooleanState::indicatePessimisticFixpoint(); 450d9659bf6SJohannes Doerfert return Set.insert(Elem); 451d9659bf6SJohannes Doerfert } 452d9659bf6SJohannes Doerfert 453d9659bf6SJohannes Doerfert Ty *operator[](int Idx) const { return Set[Idx]; } 454d9659bf6SJohannes Doerfert bool operator==(const BooleanStateWithPtrSetVector &RHS) const { 455d9659bf6SJohannes Doerfert return BooleanState::operator==(RHS) && Set == RHS.Set; 456d9659bf6SJohannes Doerfert } 457d9659bf6SJohannes Doerfert bool operator!=(const BooleanStateWithPtrSetVector &RHS) const { 458d9659bf6SJohannes Doerfert return !(*this == RHS); 459d9659bf6SJohannes Doerfert } 460d9659bf6SJohannes Doerfert 461d9659bf6SJohannes Doerfert bool empty() const { return Set.empty(); } 462d9659bf6SJohannes Doerfert size_t size() const { return Set.size(); } 463d9659bf6SJohannes Doerfert 464d9659bf6SJohannes Doerfert /// "Clamp" this state with \p RHS. 465d9659bf6SJohannes Doerfert BooleanStateWithPtrSetVector & 466d9659bf6SJohannes Doerfert operator^=(const BooleanStateWithPtrSetVector &RHS) { 467d9659bf6SJohannes Doerfert BooleanState::operator^=(RHS); 468d9659bf6SJohannes Doerfert Set.insert(RHS.Set.begin(), RHS.Set.end()); 469d9659bf6SJohannes Doerfert return *this; 470d9659bf6SJohannes Doerfert } 471d9659bf6SJohannes Doerfert 472d9659bf6SJohannes Doerfert private: 473d9659bf6SJohannes Doerfert /// A set to keep track of elements. 474d9659bf6SJohannes Doerfert SetVector<Ty *> Set; 475d9659bf6SJohannes Doerfert 476d9659bf6SJohannes Doerfert public: 477d9659bf6SJohannes Doerfert typename decltype(Set)::iterator begin() { return Set.begin(); } 478d9659bf6SJohannes Doerfert typename decltype(Set)::iterator end() { return Set.end(); } 479d9659bf6SJohannes Doerfert typename decltype(Set)::const_iterator begin() const { return Set.begin(); } 480d9659bf6SJohannes Doerfert typename decltype(Set)::const_iterator end() const { return Set.end(); } 481d9659bf6SJohannes Doerfert }; 482d9659bf6SJohannes Doerfert 483d9659bf6SJohannes Doerfert struct KernelInfoState : AbstractState { 484d9659bf6SJohannes Doerfert /// Flag to track if we reached a fixpoint. 485d9659bf6SJohannes Doerfert bool IsAtFixpoint = false; 486d9659bf6SJohannes Doerfert 487d9659bf6SJohannes Doerfert /// The parallel regions (identified by the outlined parallel functions) that 488d9659bf6SJohannes Doerfert /// can be reached from the associated function. 489d9659bf6SJohannes Doerfert BooleanStateWithPtrSetVector<Function, /* InsertInvalidates */ false> 490d9659bf6SJohannes Doerfert ReachedKnownParallelRegions; 491d9659bf6SJohannes Doerfert 492d9659bf6SJohannes Doerfert /// State to track what parallel region we might reach. 493d9659bf6SJohannes Doerfert BooleanStateWithPtrSetVector<CallBase> ReachedUnknownParallelRegions; 494d9659bf6SJohannes Doerfert 495514c033dSJohannes Doerfert /// State to track if we are in SPMD-mode, assumed or know, and why we decided 496e8439ec8SGiorgis Georgakoudis /// we cannot be. If it is assumed, then RequiresFullRuntime should also be 497e8439ec8SGiorgis Georgakoudis /// false. 498514c033dSJohannes Doerfert BooleanStateWithPtrSetVector<Instruction> SPMDCompatibilityTracker; 499514c033dSJohannes Doerfert 500d9659bf6SJohannes Doerfert /// The __kmpc_target_init call in this kernel, if any. If we find more than 501d9659bf6SJohannes Doerfert /// one we abort as the kernel is malformed. 502d9659bf6SJohannes Doerfert CallBase *KernelInitCB = nullptr; 503d9659bf6SJohannes Doerfert 504d9659bf6SJohannes Doerfert /// The __kmpc_target_deinit call in this kernel, if any. If we find more than 505d9659bf6SJohannes Doerfert /// one we abort as the kernel is malformed. 506d9659bf6SJohannes Doerfert CallBase *KernelDeinitCB = nullptr; 507d9659bf6SJohannes Doerfert 508ca662297SShilei Tian /// Flag to indicate if the associated function is a kernel entry. 509ca662297SShilei Tian bool IsKernelEntry = false; 510ca662297SShilei Tian 511ca662297SShilei Tian /// State to track what kernel entries can reach the associated function. 512ca662297SShilei Tian BooleanStateWithPtrSetVector<Function, false> ReachingKernelEntries; 513ca662297SShilei Tian 514d9659bf6SJohannes Doerfert /// Abstract State interface 515d9659bf6SJohannes Doerfert ///{ 516d9659bf6SJohannes Doerfert 517d9659bf6SJohannes Doerfert KernelInfoState() {} 518d9659bf6SJohannes Doerfert KernelInfoState(bool BestState) { 519d9659bf6SJohannes Doerfert if (!BestState) 520d9659bf6SJohannes Doerfert indicatePessimisticFixpoint(); 521d9659bf6SJohannes Doerfert } 522d9659bf6SJohannes Doerfert 523d9659bf6SJohannes Doerfert /// See AbstractState::isValidState(...) 524d9659bf6SJohannes Doerfert bool isValidState() const override { return true; } 525d9659bf6SJohannes Doerfert 526d9659bf6SJohannes Doerfert /// See AbstractState::isAtFixpoint(...) 527d9659bf6SJohannes Doerfert bool isAtFixpoint() const override { return IsAtFixpoint; } 528d9659bf6SJohannes Doerfert 529d9659bf6SJohannes Doerfert /// See AbstractState::indicatePessimisticFixpoint(...) 530d9659bf6SJohannes Doerfert ChangeStatus indicatePessimisticFixpoint() override { 531d9659bf6SJohannes Doerfert IsAtFixpoint = true; 532514c033dSJohannes Doerfert SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 533d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions.indicatePessimisticFixpoint(); 534d9659bf6SJohannes Doerfert return ChangeStatus::CHANGED; 535d9659bf6SJohannes Doerfert } 536d9659bf6SJohannes Doerfert 537d9659bf6SJohannes Doerfert /// See AbstractState::indicateOptimisticFixpoint(...) 538d9659bf6SJohannes Doerfert ChangeStatus indicateOptimisticFixpoint() override { 539d9659bf6SJohannes Doerfert IsAtFixpoint = true; 540d9659bf6SJohannes Doerfert return ChangeStatus::UNCHANGED; 541d9659bf6SJohannes Doerfert } 542d9659bf6SJohannes Doerfert 543d9659bf6SJohannes Doerfert /// Return the assumed state 544d9659bf6SJohannes Doerfert KernelInfoState &getAssumed() { return *this; } 545d9659bf6SJohannes Doerfert const KernelInfoState &getAssumed() const { return *this; } 546d9659bf6SJohannes Doerfert 547d9659bf6SJohannes Doerfert bool operator==(const KernelInfoState &RHS) const { 548514c033dSJohannes Doerfert if (SPMDCompatibilityTracker != RHS.SPMDCompatibilityTracker) 549514c033dSJohannes Doerfert return false; 550d9659bf6SJohannes Doerfert if (ReachedKnownParallelRegions != RHS.ReachedKnownParallelRegions) 551d9659bf6SJohannes Doerfert return false; 552d9659bf6SJohannes Doerfert if (ReachedUnknownParallelRegions != RHS.ReachedUnknownParallelRegions) 553d9659bf6SJohannes Doerfert return false; 554ca662297SShilei Tian if (ReachingKernelEntries != RHS.ReachingKernelEntries) 555ca662297SShilei Tian return false; 556d9659bf6SJohannes Doerfert return true; 557d9659bf6SJohannes Doerfert } 558d9659bf6SJohannes Doerfert 559d9659bf6SJohannes Doerfert /// Return empty set as the best state of potential values. 560d9659bf6SJohannes Doerfert static KernelInfoState getBestState() { return KernelInfoState(true); } 561d9659bf6SJohannes Doerfert 562d9659bf6SJohannes Doerfert static KernelInfoState getBestState(KernelInfoState &KIS) { 563d9659bf6SJohannes Doerfert return getBestState(); 564d9659bf6SJohannes Doerfert } 565d9659bf6SJohannes Doerfert 566d9659bf6SJohannes Doerfert /// Return full set as the worst state of potential values. 567d9659bf6SJohannes Doerfert static KernelInfoState getWorstState() { return KernelInfoState(false); } 568d9659bf6SJohannes Doerfert 569d9659bf6SJohannes Doerfert /// "Clamp" this state with \p KIS. 570d9659bf6SJohannes Doerfert KernelInfoState operator^=(const KernelInfoState &KIS) { 571d9659bf6SJohannes Doerfert // Do not merge two different _init and _deinit call sites. 572d9659bf6SJohannes Doerfert if (KIS.KernelInitCB) { 573d9659bf6SJohannes Doerfert if (KernelInitCB && KernelInitCB != KIS.KernelInitCB) 574d9659bf6SJohannes Doerfert indicatePessimisticFixpoint(); 575d9659bf6SJohannes Doerfert KernelInitCB = KIS.KernelInitCB; 576d9659bf6SJohannes Doerfert } 577d9659bf6SJohannes Doerfert if (KIS.KernelDeinitCB) { 578d9659bf6SJohannes Doerfert if (KernelDeinitCB && KernelDeinitCB != KIS.KernelDeinitCB) 579d9659bf6SJohannes Doerfert indicatePessimisticFixpoint(); 580d9659bf6SJohannes Doerfert KernelDeinitCB = KIS.KernelDeinitCB; 581d9659bf6SJohannes Doerfert } 582514c033dSJohannes Doerfert SPMDCompatibilityTracker ^= KIS.SPMDCompatibilityTracker; 583d9659bf6SJohannes Doerfert ReachedKnownParallelRegions ^= KIS.ReachedKnownParallelRegions; 584d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions ^= KIS.ReachedUnknownParallelRegions; 585d9659bf6SJohannes Doerfert return *this; 586d9659bf6SJohannes Doerfert } 587d9659bf6SJohannes Doerfert 588d9659bf6SJohannes Doerfert KernelInfoState operator&=(const KernelInfoState &KIS) { 589d9659bf6SJohannes Doerfert return (*this ^= KIS); 590d9659bf6SJohannes Doerfert } 591d9659bf6SJohannes Doerfert 592d9659bf6SJohannes Doerfert ///} 593d9659bf6SJohannes Doerfert }; 594d9659bf6SJohannes Doerfert 5958931add6SHamilton Tobon Mosquera /// Used to map the values physically (in the IR) stored in an offload 5968931add6SHamilton Tobon Mosquera /// array, to a vector in memory. 5978931add6SHamilton Tobon Mosquera struct OffloadArray { 5988931add6SHamilton Tobon Mosquera /// Physical array (in the IR). 5998931add6SHamilton Tobon Mosquera AllocaInst *Array = nullptr; 6008931add6SHamilton Tobon Mosquera /// Mapped values. 6018931add6SHamilton Tobon Mosquera SmallVector<Value *, 8> StoredValues; 6028931add6SHamilton Tobon Mosquera /// Last stores made in the offload array. 6038931add6SHamilton Tobon Mosquera SmallVector<StoreInst *, 8> LastAccesses; 6048931add6SHamilton Tobon Mosquera 6058931add6SHamilton Tobon Mosquera OffloadArray() = default; 6068931add6SHamilton Tobon Mosquera 6078931add6SHamilton Tobon Mosquera /// Initializes the OffloadArray with the values stored in \p Array before 6088931add6SHamilton Tobon Mosquera /// instruction \p Before is reached. Returns false if the initialization 6098931add6SHamilton Tobon Mosquera /// fails. 6108931add6SHamilton Tobon Mosquera /// This MUST be used immediately after the construction of the object. 6118931add6SHamilton Tobon Mosquera bool initialize(AllocaInst &Array, Instruction &Before) { 6128931add6SHamilton Tobon Mosquera if (!Array.getAllocatedType()->isArrayTy()) 6138931add6SHamilton Tobon Mosquera return false; 6148931add6SHamilton Tobon Mosquera 6158931add6SHamilton Tobon Mosquera if (!getValues(Array, Before)) 6168931add6SHamilton Tobon Mosquera return false; 6178931add6SHamilton Tobon Mosquera 6188931add6SHamilton Tobon Mosquera this->Array = &Array; 6198931add6SHamilton Tobon Mosquera return true; 6208931add6SHamilton Tobon Mosquera } 6218931add6SHamilton Tobon Mosquera 622da8bec47SJoseph Huber static const unsigned DeviceIDArgNum = 1; 623da8bec47SJoseph Huber static const unsigned BasePtrsArgNum = 3; 624da8bec47SJoseph Huber static const unsigned PtrsArgNum = 4; 625da8bec47SJoseph Huber static const unsigned SizesArgNum = 5; 6261d3d9b9cSHamilton Tobon Mosquera 6278931add6SHamilton Tobon Mosquera private: 6288931add6SHamilton Tobon Mosquera /// Traverses the BasicBlock where \p Array is, collecting the stores made to 6298931add6SHamilton Tobon Mosquera /// \p Array, leaving StoredValues with the values stored before the 6308931add6SHamilton Tobon Mosquera /// instruction \p Before is reached. 6318931add6SHamilton Tobon Mosquera bool getValues(AllocaInst &Array, Instruction &Before) { 6328931add6SHamilton Tobon Mosquera // Initialize container. 633d08d490aSJohannes Doerfert const uint64_t NumValues = Array.getAllocatedType()->getArrayNumElements(); 6348931add6SHamilton Tobon Mosquera StoredValues.assign(NumValues, nullptr); 6358931add6SHamilton Tobon Mosquera LastAccesses.assign(NumValues, nullptr); 6368931add6SHamilton Tobon Mosquera 6378931add6SHamilton Tobon Mosquera // TODO: This assumes the instruction \p Before is in the same 6388931add6SHamilton Tobon Mosquera // BasicBlock as Array. Make it general, for any control flow graph. 6398931add6SHamilton Tobon Mosquera BasicBlock *BB = Array.getParent(); 6408931add6SHamilton Tobon Mosquera if (BB != Before.getParent()) 6418931add6SHamilton Tobon Mosquera return false; 6428931add6SHamilton Tobon Mosquera 6438931add6SHamilton Tobon Mosquera const DataLayout &DL = Array.getModule()->getDataLayout(); 6448931add6SHamilton Tobon Mosquera const unsigned int PointerSize = DL.getPointerSize(); 6458931add6SHamilton Tobon Mosquera 6468931add6SHamilton Tobon Mosquera for (Instruction &I : *BB) { 6478931add6SHamilton Tobon Mosquera if (&I == &Before) 6488931add6SHamilton Tobon Mosquera break; 6498931add6SHamilton Tobon Mosquera 6508931add6SHamilton Tobon Mosquera if (!isa<StoreInst>(&I)) 6518931add6SHamilton Tobon Mosquera continue; 6528931add6SHamilton Tobon Mosquera 6538931add6SHamilton Tobon Mosquera auto *S = cast<StoreInst>(&I); 6548931add6SHamilton Tobon Mosquera int64_t Offset = -1; 655d08d490aSJohannes Doerfert auto *Dst = 656d08d490aSJohannes Doerfert GetPointerBaseWithConstantOffset(S->getPointerOperand(), Offset, DL); 6578931add6SHamilton Tobon Mosquera if (Dst == &Array) { 6588931add6SHamilton Tobon Mosquera int64_t Idx = Offset / PointerSize; 6598931add6SHamilton Tobon Mosquera StoredValues[Idx] = getUnderlyingObject(S->getValueOperand()); 6608931add6SHamilton Tobon Mosquera LastAccesses[Idx] = S; 6618931add6SHamilton Tobon Mosquera } 6628931add6SHamilton Tobon Mosquera } 6638931add6SHamilton Tobon Mosquera 6648931add6SHamilton Tobon Mosquera return isFilled(); 6658931add6SHamilton Tobon Mosquera } 6668931add6SHamilton Tobon Mosquera 6678931add6SHamilton Tobon Mosquera /// Returns true if all values in StoredValues and 6688931add6SHamilton Tobon Mosquera /// LastAccesses are not nullptrs. 6698931add6SHamilton Tobon Mosquera bool isFilled() { 6708931add6SHamilton Tobon Mosquera const unsigned NumValues = StoredValues.size(); 6718931add6SHamilton Tobon Mosquera for (unsigned I = 0; I < NumValues; ++I) { 6728931add6SHamilton Tobon Mosquera if (!StoredValues[I] || !LastAccesses[I]) 6738931add6SHamilton Tobon Mosquera return false; 6748931add6SHamilton Tobon Mosquera } 6758931add6SHamilton Tobon Mosquera 6768931add6SHamilton Tobon Mosquera return true; 6778931add6SHamilton Tobon Mosquera } 6788931add6SHamilton Tobon Mosquera }; 6798931add6SHamilton Tobon Mosquera 6807cfd267cSsstefan1 struct OpenMPOpt { 6817cfd267cSsstefan1 6827cfd267cSsstefan1 using OptimizationRemarkGetter = 6837cfd267cSsstefan1 function_ref<OptimizationRemarkEmitter &(Function *)>; 6847cfd267cSsstefan1 6857cfd267cSsstefan1 OpenMPOpt(SmallVectorImpl<Function *> &SCC, CallGraphUpdater &CGUpdater, 6867cfd267cSsstefan1 OptimizationRemarkGetter OREGetter, 687b8235d2bSsstefan1 OMPInformationCache &OMPInfoCache, Attributor &A) 68877b79d79SMehdi Amini : M(*(*SCC.begin())->getParent()), SCC(SCC), CGUpdater(CGUpdater), 689b8235d2bSsstefan1 OREGetter(OREGetter), OMPInfoCache(OMPInfoCache), A(A) {} 6907cfd267cSsstefan1 691a2281419SJoseph Huber /// Check if any remarks are enabled for openmp-opt 692a2281419SJoseph Huber bool remarksEnabled() { 693a2281419SJoseph Huber auto &Ctx = M.getContext(); 694a2281419SJoseph Huber return Ctx.getDiagHandlerPtr()->isAnyRemarkEnabled(DEBUG_TYPE); 695a2281419SJoseph Huber } 696a2281419SJoseph Huber 6979548b74aSJohannes Doerfert /// Run all OpenMP optimizations on the underlying SCC/ModuleSlice. 698b2ad63d3SJoseph Huber bool run(bool IsModulePass) { 69954bd3751SJohannes Doerfert if (SCC.empty()) 70054bd3751SJohannes Doerfert return false; 70154bd3751SJohannes Doerfert 7029548b74aSJohannes Doerfert bool Changed = false; 7039548b74aSJohannes Doerfert 7049548b74aSJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "Run on SCC with " << SCC.size() 70577b79d79SMehdi Amini << " functions in a slice with " 70677b79d79SMehdi Amini << OMPInfoCache.ModuleSlice.size() << " functions\n"); 7079548b74aSJohannes Doerfert 708b2ad63d3SJoseph Huber if (IsModulePass) { 709d9659bf6SJohannes Doerfert Changed |= runAttributor(IsModulePass); 71018283125SJoseph Huber 7116fc51c9fSJoseph Huber // Recollect uses, in case Attributor deleted any. 7126fc51c9fSJoseph Huber OMPInfoCache.recollectUses(); 7136fc51c9fSJoseph Huber 714b2ad63d3SJoseph Huber if (remarksEnabled()) 715b2ad63d3SJoseph Huber analysisGlobalization(); 716b2ad63d3SJoseph Huber } else { 717e8039ad4SJohannes Doerfert if (PrintICVValues) 718e8039ad4SJohannes Doerfert printICVs(); 719e8039ad4SJohannes Doerfert if (PrintOpenMPKernels) 720e8039ad4SJohannes Doerfert printKernels(); 721e8039ad4SJohannes Doerfert 722d9659bf6SJohannes Doerfert Changed |= runAttributor(IsModulePass); 723e8039ad4SJohannes Doerfert 724e8039ad4SJohannes Doerfert // Recollect uses, in case Attributor deleted any. 725e8039ad4SJohannes Doerfert OMPInfoCache.recollectUses(); 726e8039ad4SJohannes Doerfert 727e8039ad4SJohannes Doerfert Changed |= deleteParallelRegions(); 728d9659bf6SJohannes Doerfert Changed |= rewriteDeviceCodeStateMachine(); 729d9659bf6SJohannes Doerfert 730496f8e5bSHamilton Tobon Mosquera if (HideMemoryTransferLatency) 731496f8e5bSHamilton Tobon Mosquera Changed |= hideMemTransfersLatency(); 7323a6bfcf2SGiorgis Georgakoudis Changed |= deduplicateRuntimeCalls(); 7333a6bfcf2SGiorgis Georgakoudis if (EnableParallelRegionMerging) { 7343a6bfcf2SGiorgis Georgakoudis if (mergeParallelRegions()) { 7353a6bfcf2SGiorgis Georgakoudis deduplicateRuntimeCalls(); 7363a6bfcf2SGiorgis Georgakoudis Changed = true; 7373a6bfcf2SGiorgis Georgakoudis } 7383a6bfcf2SGiorgis Georgakoudis } 739b2ad63d3SJoseph Huber } 740e8039ad4SJohannes Doerfert 741e8039ad4SJohannes Doerfert return Changed; 742e8039ad4SJohannes Doerfert } 743e8039ad4SJohannes Doerfert 7440f426935Ssstefan1 /// Print initial ICV values for testing. 7450f426935Ssstefan1 /// FIXME: This should be done from the Attributor once it is added. 746e8039ad4SJohannes Doerfert void printICVs() const { 747cb9cfa0dSsstefan1 InternalControlVar ICVs[] = {ICV_nthreads, ICV_active_levels, ICV_cancel, 748cb9cfa0dSsstefan1 ICV_proc_bind}; 7490f426935Ssstefan1 7500f426935Ssstefan1 for (Function *F : OMPInfoCache.ModuleSlice) { 7510f426935Ssstefan1 for (auto ICV : ICVs) { 7520f426935Ssstefan1 auto ICVInfo = OMPInfoCache.ICVs[ICV]; 7532db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 7542db182ffSJoseph Huber return ORA << "OpenMP ICV " << ore::NV("OpenMPICV", ICVInfo.Name) 7550f426935Ssstefan1 << " Value: " 7560f426935Ssstefan1 << (ICVInfo.InitValue 75761cdaf66SSimon Pilgrim ? toString(ICVInfo.InitValue->getValue(), 10, true) 7580f426935Ssstefan1 : "IMPLEMENTATION_DEFINED"); 7590f426935Ssstefan1 }; 7600f426935Ssstefan1 7612db182ffSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(F, "OpenMPICVTracker", Remark); 7620f426935Ssstefan1 } 7630f426935Ssstefan1 } 7640f426935Ssstefan1 } 7650f426935Ssstefan1 766e8039ad4SJohannes Doerfert /// Print OpenMP GPU kernels for testing. 767e8039ad4SJohannes Doerfert void printKernels() const { 768e8039ad4SJohannes Doerfert for (Function *F : SCC) { 769e8039ad4SJohannes Doerfert if (!OMPInfoCache.Kernels.count(F)) 770e8039ad4SJohannes Doerfert continue; 771b8235d2bSsstefan1 7722db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 7732db182ffSJoseph Huber return ORA << "OpenMP GPU kernel " 774e8039ad4SJohannes Doerfert << ore::NV("OpenMPGPUKernel", F->getName()) << "\n"; 775e8039ad4SJohannes Doerfert }; 776b8235d2bSsstefan1 7772db182ffSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(F, "OpenMPGPU", Remark); 778e8039ad4SJohannes Doerfert } 7799548b74aSJohannes Doerfert } 7809548b74aSJohannes Doerfert 7817cfd267cSsstefan1 /// Return the call if \p U is a callee use in a regular call. If \p RFI is 7827cfd267cSsstefan1 /// given it has to be the callee or a nullptr is returned. 7837cfd267cSsstefan1 static CallInst *getCallIfRegularCall( 7847cfd267cSsstefan1 Use &U, OMPInformationCache::RuntimeFunctionInfo *RFI = nullptr) { 7857cfd267cSsstefan1 CallInst *CI = dyn_cast<CallInst>(U.getUser()); 7867cfd267cSsstefan1 if (CI && CI->isCallee(&U) && !CI->hasOperandBundles() && 7877cfd267cSsstefan1 (!RFI || CI->getCalledFunction() == RFI->Declaration)) 7887cfd267cSsstefan1 return CI; 7897cfd267cSsstefan1 return nullptr; 7907cfd267cSsstefan1 } 7917cfd267cSsstefan1 7927cfd267cSsstefan1 /// Return the call if \p V is a regular call. If \p RFI is given it has to be 7937cfd267cSsstefan1 /// the callee or a nullptr is returned. 7947cfd267cSsstefan1 static CallInst *getCallIfRegularCall( 7957cfd267cSsstefan1 Value &V, OMPInformationCache::RuntimeFunctionInfo *RFI = nullptr) { 7967cfd267cSsstefan1 CallInst *CI = dyn_cast<CallInst>(&V); 7977cfd267cSsstefan1 if (CI && !CI->hasOperandBundles() && 7987cfd267cSsstefan1 (!RFI || CI->getCalledFunction() == RFI->Declaration)) 7997cfd267cSsstefan1 return CI; 8007cfd267cSsstefan1 return nullptr; 8017cfd267cSsstefan1 } 8027cfd267cSsstefan1 8039548b74aSJohannes Doerfert private: 8043a6bfcf2SGiorgis Georgakoudis /// Merge parallel regions when it is safe. 8053a6bfcf2SGiorgis Georgakoudis bool mergeParallelRegions() { 8063a6bfcf2SGiorgis Georgakoudis const unsigned CallbackCalleeOperand = 2; 8073a6bfcf2SGiorgis Georgakoudis const unsigned CallbackFirstArgOperand = 3; 8083a6bfcf2SGiorgis Georgakoudis using InsertPointTy = OpenMPIRBuilder::InsertPointTy; 8093a6bfcf2SGiorgis Georgakoudis 8103a6bfcf2SGiorgis Georgakoudis // Check if there are any __kmpc_fork_call calls to merge. 8113a6bfcf2SGiorgis Georgakoudis OMPInformationCache::RuntimeFunctionInfo &RFI = 8123a6bfcf2SGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call]; 8133a6bfcf2SGiorgis Georgakoudis 8143a6bfcf2SGiorgis Georgakoudis if (!RFI.Declaration) 8153a6bfcf2SGiorgis Georgakoudis return false; 8163a6bfcf2SGiorgis Georgakoudis 81797517055SGiorgis Georgakoudis // Unmergable calls that prevent merging a parallel region. 81897517055SGiorgis Georgakoudis OMPInformationCache::RuntimeFunctionInfo UnmergableCallsInfo[] = { 81997517055SGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_push_proc_bind], 82097517055SGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_push_num_threads], 82197517055SGiorgis Georgakoudis }; 8223a6bfcf2SGiorgis Georgakoudis 8233a6bfcf2SGiorgis Georgakoudis bool Changed = false; 8243a6bfcf2SGiorgis Georgakoudis LoopInfo *LI = nullptr; 8253a6bfcf2SGiorgis Georgakoudis DominatorTree *DT = nullptr; 8263a6bfcf2SGiorgis Georgakoudis 8273a6bfcf2SGiorgis Georgakoudis SmallDenseMap<BasicBlock *, SmallPtrSet<Instruction *, 4>> BB2PRMap; 8283a6bfcf2SGiorgis Georgakoudis 8293a6bfcf2SGiorgis Georgakoudis BasicBlock *StartBB = nullptr, *EndBB = nullptr; 8303a6bfcf2SGiorgis Georgakoudis auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, 8313a6bfcf2SGiorgis Georgakoudis BasicBlock &ContinuationIP) { 8323a6bfcf2SGiorgis Georgakoudis BasicBlock *CGStartBB = CodeGenIP.getBlock(); 8333a6bfcf2SGiorgis Georgakoudis BasicBlock *CGEndBB = 8343a6bfcf2SGiorgis Georgakoudis SplitBlock(CGStartBB, &*CodeGenIP.getPoint(), DT, LI); 8353a6bfcf2SGiorgis Georgakoudis assert(StartBB != nullptr && "StartBB should not be null"); 8363a6bfcf2SGiorgis Georgakoudis CGStartBB->getTerminator()->setSuccessor(0, StartBB); 8373a6bfcf2SGiorgis Georgakoudis assert(EndBB != nullptr && "EndBB should not be null"); 8383a6bfcf2SGiorgis Georgakoudis EndBB->getTerminator()->setSuccessor(0, CGEndBB); 8393a6bfcf2SGiorgis Georgakoudis }; 8403a6bfcf2SGiorgis Georgakoudis 841240dd924SAlex Zinenko auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &, 842240dd924SAlex Zinenko Value &Inner, Value *&ReplacementValue) -> InsertPointTy { 843240dd924SAlex Zinenko ReplacementValue = &Inner; 8443a6bfcf2SGiorgis Georgakoudis return CodeGenIP; 8453a6bfcf2SGiorgis Georgakoudis }; 8463a6bfcf2SGiorgis Georgakoudis 8473a6bfcf2SGiorgis Georgakoudis auto FiniCB = [&](InsertPointTy CodeGenIP) {}; 8483a6bfcf2SGiorgis Georgakoudis 84997517055SGiorgis Georgakoudis /// Create a sequential execution region within a merged parallel region, 85097517055SGiorgis Georgakoudis /// encapsulated in a master construct with a barrier for synchronization. 85197517055SGiorgis Georgakoudis auto CreateSequentialRegion = [&](Function *OuterFn, 85297517055SGiorgis Georgakoudis BasicBlock *OuterPredBB, 85397517055SGiorgis Georgakoudis Instruction *SeqStartI, 85497517055SGiorgis Georgakoudis Instruction *SeqEndI) { 85597517055SGiorgis Georgakoudis // Isolate the instructions of the sequential region to a separate 85697517055SGiorgis Georgakoudis // block. 85797517055SGiorgis Georgakoudis BasicBlock *ParentBB = SeqStartI->getParent(); 85897517055SGiorgis Georgakoudis BasicBlock *SeqEndBB = 85997517055SGiorgis Georgakoudis SplitBlock(ParentBB, SeqEndI->getNextNode(), DT, LI); 86097517055SGiorgis Georgakoudis BasicBlock *SeqAfterBB = 86197517055SGiorgis Georgakoudis SplitBlock(SeqEndBB, &*SeqEndBB->getFirstInsertionPt(), DT, LI); 86297517055SGiorgis Georgakoudis BasicBlock *SeqStartBB = 86397517055SGiorgis Georgakoudis SplitBlock(ParentBB, SeqStartI, DT, LI, nullptr, "seq.par.merged"); 86497517055SGiorgis Georgakoudis 86597517055SGiorgis Georgakoudis assert(ParentBB->getUniqueSuccessor() == SeqStartBB && 86697517055SGiorgis Georgakoudis "Expected a different CFG"); 86797517055SGiorgis Georgakoudis const DebugLoc DL = ParentBB->getTerminator()->getDebugLoc(); 86897517055SGiorgis Georgakoudis ParentBB->getTerminator()->eraseFromParent(); 86997517055SGiorgis Georgakoudis 87097517055SGiorgis Georgakoudis auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, 87197517055SGiorgis Georgakoudis BasicBlock &ContinuationIP) { 87297517055SGiorgis Georgakoudis BasicBlock *CGStartBB = CodeGenIP.getBlock(); 87397517055SGiorgis Georgakoudis BasicBlock *CGEndBB = 87497517055SGiorgis Georgakoudis SplitBlock(CGStartBB, &*CodeGenIP.getPoint(), DT, LI); 87597517055SGiorgis Georgakoudis assert(SeqStartBB != nullptr && "SeqStartBB should not be null"); 87697517055SGiorgis Georgakoudis CGStartBB->getTerminator()->setSuccessor(0, SeqStartBB); 87797517055SGiorgis Georgakoudis assert(SeqEndBB != nullptr && "SeqEndBB should not be null"); 87897517055SGiorgis Georgakoudis SeqEndBB->getTerminator()->setSuccessor(0, CGEndBB); 87997517055SGiorgis Georgakoudis }; 88097517055SGiorgis Georgakoudis auto FiniCB = [&](InsertPointTy CodeGenIP) {}; 88197517055SGiorgis Georgakoudis 88297517055SGiorgis Georgakoudis // Find outputs from the sequential region to outside users and 88397517055SGiorgis Georgakoudis // broadcast their values to them. 88497517055SGiorgis Georgakoudis for (Instruction &I : *SeqStartBB) { 88597517055SGiorgis Georgakoudis SmallPtrSet<Instruction *, 4> OutsideUsers; 88697517055SGiorgis Georgakoudis for (User *Usr : I.users()) { 88797517055SGiorgis Georgakoudis Instruction &UsrI = *cast<Instruction>(Usr); 88897517055SGiorgis Georgakoudis // Ignore outputs to LT intrinsics, code extraction for the merged 88997517055SGiorgis Georgakoudis // parallel region will fix them. 89097517055SGiorgis Georgakoudis if (UsrI.isLifetimeStartOrEnd()) 89197517055SGiorgis Georgakoudis continue; 89297517055SGiorgis Georgakoudis 89397517055SGiorgis Georgakoudis if (UsrI.getParent() != SeqStartBB) 89497517055SGiorgis Georgakoudis OutsideUsers.insert(&UsrI); 89597517055SGiorgis Georgakoudis } 89697517055SGiorgis Georgakoudis 89797517055SGiorgis Georgakoudis if (OutsideUsers.empty()) 89897517055SGiorgis Georgakoudis continue; 89997517055SGiorgis Georgakoudis 90097517055SGiorgis Georgakoudis // Emit an alloca in the outer region to store the broadcasted 90197517055SGiorgis Georgakoudis // value. 90297517055SGiorgis Georgakoudis const DataLayout &DL = M.getDataLayout(); 90397517055SGiorgis Georgakoudis AllocaInst *AllocaI = new AllocaInst( 90497517055SGiorgis Georgakoudis I.getType(), DL.getAllocaAddrSpace(), nullptr, 90597517055SGiorgis Georgakoudis I.getName() + ".seq.output.alloc", &OuterFn->front().front()); 90697517055SGiorgis Georgakoudis 90797517055SGiorgis Georgakoudis // Emit a store instruction in the sequential BB to update the 90897517055SGiorgis Georgakoudis // value. 90997517055SGiorgis Georgakoudis new StoreInst(&I, AllocaI, SeqStartBB->getTerminator()); 91097517055SGiorgis Georgakoudis 91197517055SGiorgis Georgakoudis // Emit a load instruction and replace the use of the output value 91297517055SGiorgis Georgakoudis // with it. 91397517055SGiorgis Georgakoudis for (Instruction *UsrI : OutsideUsers) { 9145b70c12fSJohannes Doerfert LoadInst *LoadI = new LoadInst( 9155b70c12fSJohannes Doerfert I.getType(), AllocaI, I.getName() + ".seq.output.load", UsrI); 91697517055SGiorgis Georgakoudis UsrI->replaceUsesOfWith(&I, LoadI); 91797517055SGiorgis Georgakoudis } 91897517055SGiorgis Georgakoudis } 91997517055SGiorgis Georgakoudis 92097517055SGiorgis Georgakoudis OpenMPIRBuilder::LocationDescription Loc( 92197517055SGiorgis Georgakoudis InsertPointTy(ParentBB, ParentBB->end()), DL); 92297517055SGiorgis Georgakoudis InsertPointTy SeqAfterIP = 92397517055SGiorgis Georgakoudis OMPInfoCache.OMPBuilder.createMaster(Loc, BodyGenCB, FiniCB); 92497517055SGiorgis Georgakoudis 92597517055SGiorgis Georgakoudis OMPInfoCache.OMPBuilder.createBarrier(SeqAfterIP, OMPD_parallel); 92697517055SGiorgis Georgakoudis 92797517055SGiorgis Georgakoudis BranchInst::Create(SeqAfterBB, SeqAfterIP.getBlock()); 92897517055SGiorgis Georgakoudis 92997517055SGiorgis Georgakoudis LLVM_DEBUG(dbgs() << TAG << "After sequential inlining " << *OuterFn 93097517055SGiorgis Georgakoudis << "\n"); 93197517055SGiorgis Georgakoudis }; 93297517055SGiorgis Georgakoudis 9333a6bfcf2SGiorgis Georgakoudis // Helper to merge the __kmpc_fork_call calls in MergableCIs. They are all 9343a6bfcf2SGiorgis Georgakoudis // contained in BB and only separated by instructions that can be 9353a6bfcf2SGiorgis Georgakoudis // redundantly executed in parallel. The block BB is split before the first 9363a6bfcf2SGiorgis Georgakoudis // call (in MergableCIs) and after the last so the entire region we merge 9373a6bfcf2SGiorgis Georgakoudis // into a single parallel region is contained in a single basic block 9383a6bfcf2SGiorgis Georgakoudis // without any other instructions. We use the OpenMPIRBuilder to outline 9393a6bfcf2SGiorgis Georgakoudis // that block and call the resulting function via __kmpc_fork_call. 9403a6bfcf2SGiorgis Georgakoudis auto Merge = [&](SmallVectorImpl<CallInst *> &MergableCIs, BasicBlock *BB) { 9413a6bfcf2SGiorgis Georgakoudis // TODO: Change the interface to allow single CIs expanded, e.g, to 9423a6bfcf2SGiorgis Georgakoudis // include an outer loop. 9433a6bfcf2SGiorgis Georgakoudis assert(MergableCIs.size() > 1 && "Assumed multiple mergable CIs"); 9443a6bfcf2SGiorgis Georgakoudis 9453a6bfcf2SGiorgis Georgakoudis auto Remark = [&](OptimizationRemark OR) { 946eef6601bSJoseph Huber OR << "Parallel region merged with parallel region" 947eef6601bSJoseph Huber << (MergableCIs.size() > 2 ? "s" : "") << " at "; 94823b0ab2aSKazu Hirata for (auto *CI : llvm::drop_begin(MergableCIs)) { 9493a6bfcf2SGiorgis Georgakoudis OR << ore::NV("OpenMPParallelMerge", CI->getDebugLoc()); 9503a6bfcf2SGiorgis Georgakoudis if (CI != MergableCIs.back()) 9513a6bfcf2SGiorgis Georgakoudis OR << ", "; 9523a6bfcf2SGiorgis Georgakoudis } 953eef6601bSJoseph Huber return OR << "."; 9543a6bfcf2SGiorgis Georgakoudis }; 9553a6bfcf2SGiorgis Georgakoudis 9562c31d5ebSJoseph Huber emitRemark<OptimizationRemark>(MergableCIs.front(), "OMP150", Remark); 9573a6bfcf2SGiorgis Georgakoudis 9583a6bfcf2SGiorgis Georgakoudis Function *OriginalFn = BB->getParent(); 9593a6bfcf2SGiorgis Georgakoudis LLVM_DEBUG(dbgs() << TAG << "Merge " << MergableCIs.size() 9603a6bfcf2SGiorgis Georgakoudis << " parallel regions in " << OriginalFn->getName() 9613a6bfcf2SGiorgis Georgakoudis << "\n"); 9623a6bfcf2SGiorgis Georgakoudis 9633a6bfcf2SGiorgis Georgakoudis // Isolate the calls to merge in a separate block. 9643a6bfcf2SGiorgis Georgakoudis EndBB = SplitBlock(BB, MergableCIs.back()->getNextNode(), DT, LI); 9653a6bfcf2SGiorgis Georgakoudis BasicBlock *AfterBB = 9663a6bfcf2SGiorgis Georgakoudis SplitBlock(EndBB, &*EndBB->getFirstInsertionPt(), DT, LI); 9673a6bfcf2SGiorgis Georgakoudis StartBB = SplitBlock(BB, MergableCIs.front(), DT, LI, nullptr, 9683a6bfcf2SGiorgis Georgakoudis "omp.par.merged"); 9693a6bfcf2SGiorgis Georgakoudis 9703a6bfcf2SGiorgis Georgakoudis assert(BB->getUniqueSuccessor() == StartBB && "Expected a different CFG"); 9713a6bfcf2SGiorgis Georgakoudis const DebugLoc DL = BB->getTerminator()->getDebugLoc(); 9723a6bfcf2SGiorgis Georgakoudis BB->getTerminator()->eraseFromParent(); 9733a6bfcf2SGiorgis Georgakoudis 97497517055SGiorgis Georgakoudis // Create sequential regions for sequential instructions that are 97597517055SGiorgis Georgakoudis // in-between mergable parallel regions. 97697517055SGiorgis Georgakoudis for (auto *It = MergableCIs.begin(), *End = MergableCIs.end() - 1; 97797517055SGiorgis Georgakoudis It != End; ++It) { 97897517055SGiorgis Georgakoudis Instruction *ForkCI = *It; 97997517055SGiorgis Georgakoudis Instruction *NextForkCI = *(It + 1); 98097517055SGiorgis Georgakoudis 98197517055SGiorgis Georgakoudis // Continue if there are not in-between instructions. 98297517055SGiorgis Georgakoudis if (ForkCI->getNextNode() == NextForkCI) 98397517055SGiorgis Georgakoudis continue; 98497517055SGiorgis Georgakoudis 98597517055SGiorgis Georgakoudis CreateSequentialRegion(OriginalFn, BB, ForkCI->getNextNode(), 98697517055SGiorgis Georgakoudis NextForkCI->getPrevNode()); 98797517055SGiorgis Georgakoudis } 98897517055SGiorgis Georgakoudis 9893a6bfcf2SGiorgis Georgakoudis OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BB, BB->end()), 9903a6bfcf2SGiorgis Georgakoudis DL); 9913a6bfcf2SGiorgis Georgakoudis IRBuilder<>::InsertPoint AllocaIP( 9923a6bfcf2SGiorgis Georgakoudis &OriginalFn->getEntryBlock(), 9933a6bfcf2SGiorgis Georgakoudis OriginalFn->getEntryBlock().getFirstInsertionPt()); 9943a6bfcf2SGiorgis Georgakoudis // Create the merged parallel region with default proc binding, to 9953a6bfcf2SGiorgis Georgakoudis // avoid overriding binding settings, and without explicit cancellation. 996e5dba2d7SMichael Kruse InsertPointTy AfterIP = OMPInfoCache.OMPBuilder.createParallel( 9973a6bfcf2SGiorgis Georgakoudis Loc, AllocaIP, BodyGenCB, PrivCB, FiniCB, nullptr, nullptr, 9983a6bfcf2SGiorgis Georgakoudis OMP_PROC_BIND_default, /* IsCancellable */ false); 9993a6bfcf2SGiorgis Georgakoudis BranchInst::Create(AfterBB, AfterIP.getBlock()); 10003a6bfcf2SGiorgis Georgakoudis 10013a6bfcf2SGiorgis Georgakoudis // Perform the actual outlining. 1002b1191206SMichael Kruse OMPInfoCache.OMPBuilder.finalize(OriginalFn, 1003b1191206SMichael Kruse /* AllowExtractorSinking */ true); 10043a6bfcf2SGiorgis Georgakoudis 10053a6bfcf2SGiorgis Georgakoudis Function *OutlinedFn = MergableCIs.front()->getCaller(); 10063a6bfcf2SGiorgis Georgakoudis 10073a6bfcf2SGiorgis Georgakoudis // Replace the __kmpc_fork_call calls with direct calls to the outlined 10083a6bfcf2SGiorgis Georgakoudis // callbacks. 10093a6bfcf2SGiorgis Georgakoudis SmallVector<Value *, 8> Args; 10103a6bfcf2SGiorgis Georgakoudis for (auto *CI : MergableCIs) { 10113a6bfcf2SGiorgis Georgakoudis Value *Callee = 10123a6bfcf2SGiorgis Georgakoudis CI->getArgOperand(CallbackCalleeOperand)->stripPointerCasts(); 10133a6bfcf2SGiorgis Georgakoudis FunctionType *FT = 10143a6bfcf2SGiorgis Georgakoudis cast<FunctionType>(Callee->getType()->getPointerElementType()); 10153a6bfcf2SGiorgis Georgakoudis Args.clear(); 10163a6bfcf2SGiorgis Georgakoudis Args.push_back(OutlinedFn->getArg(0)); 10173a6bfcf2SGiorgis Georgakoudis Args.push_back(OutlinedFn->getArg(1)); 10183a6bfcf2SGiorgis Georgakoudis for (unsigned U = CallbackFirstArgOperand, E = CI->getNumArgOperands(); 10193a6bfcf2SGiorgis Georgakoudis U < E; ++U) 10203a6bfcf2SGiorgis Georgakoudis Args.push_back(CI->getArgOperand(U)); 10213a6bfcf2SGiorgis Georgakoudis 10223a6bfcf2SGiorgis Georgakoudis CallInst *NewCI = CallInst::Create(FT, Callee, Args, "", CI); 10233a6bfcf2SGiorgis Georgakoudis if (CI->getDebugLoc()) 10243a6bfcf2SGiorgis Georgakoudis NewCI->setDebugLoc(CI->getDebugLoc()); 10253a6bfcf2SGiorgis Georgakoudis 10263a6bfcf2SGiorgis Georgakoudis // Forward parameter attributes from the callback to the callee. 10273a6bfcf2SGiorgis Georgakoudis for (unsigned U = CallbackFirstArgOperand, E = CI->getNumArgOperands(); 10283a6bfcf2SGiorgis Georgakoudis U < E; ++U) 10293a6bfcf2SGiorgis Georgakoudis for (const Attribute &A : CI->getAttributes().getParamAttributes(U)) 10303a6bfcf2SGiorgis Georgakoudis NewCI->addParamAttr( 10313a6bfcf2SGiorgis Georgakoudis U - (CallbackFirstArgOperand - CallbackCalleeOperand), A); 10323a6bfcf2SGiorgis Georgakoudis 10333a6bfcf2SGiorgis Georgakoudis // Emit an explicit barrier to replace the implicit fork-join barrier. 10343a6bfcf2SGiorgis Georgakoudis if (CI != MergableCIs.back()) { 10353a6bfcf2SGiorgis Georgakoudis // TODO: Remove barrier if the merged parallel region includes the 10363a6bfcf2SGiorgis Georgakoudis // 'nowait' clause. 1037e5dba2d7SMichael Kruse OMPInfoCache.OMPBuilder.createBarrier( 10383a6bfcf2SGiorgis Georgakoudis InsertPointTy(NewCI->getParent(), 10393a6bfcf2SGiorgis Georgakoudis NewCI->getNextNode()->getIterator()), 10403a6bfcf2SGiorgis Georgakoudis OMPD_parallel); 10413a6bfcf2SGiorgis Georgakoudis } 10423a6bfcf2SGiorgis Georgakoudis 10433a6bfcf2SGiorgis Georgakoudis CI->eraseFromParent(); 10443a6bfcf2SGiorgis Georgakoudis } 10453a6bfcf2SGiorgis Georgakoudis 10463a6bfcf2SGiorgis Georgakoudis assert(OutlinedFn != OriginalFn && "Outlining failed"); 10477fea561eSArthur Eubanks CGUpdater.registerOutlinedFunction(*OriginalFn, *OutlinedFn); 10483a6bfcf2SGiorgis Georgakoudis CGUpdater.reanalyzeFunction(*OriginalFn); 10493a6bfcf2SGiorgis Georgakoudis 10503a6bfcf2SGiorgis Georgakoudis NumOpenMPParallelRegionsMerged += MergableCIs.size(); 10513a6bfcf2SGiorgis Georgakoudis 10523a6bfcf2SGiorgis Georgakoudis return true; 10533a6bfcf2SGiorgis Georgakoudis }; 10543a6bfcf2SGiorgis Georgakoudis 10553a6bfcf2SGiorgis Georgakoudis // Helper function that identifes sequences of 10563a6bfcf2SGiorgis Georgakoudis // __kmpc_fork_call uses in a basic block. 10573a6bfcf2SGiorgis Georgakoudis auto DetectPRsCB = [&](Use &U, Function &F) { 10583a6bfcf2SGiorgis Georgakoudis CallInst *CI = getCallIfRegularCall(U, &RFI); 10593a6bfcf2SGiorgis Georgakoudis BB2PRMap[CI->getParent()].insert(CI); 10603a6bfcf2SGiorgis Georgakoudis 10613a6bfcf2SGiorgis Georgakoudis return false; 10623a6bfcf2SGiorgis Georgakoudis }; 10633a6bfcf2SGiorgis Georgakoudis 10643a6bfcf2SGiorgis Georgakoudis BB2PRMap.clear(); 10653a6bfcf2SGiorgis Georgakoudis RFI.foreachUse(SCC, DetectPRsCB); 10663a6bfcf2SGiorgis Georgakoudis SmallVector<SmallVector<CallInst *, 4>, 4> MergableCIsVector; 10673a6bfcf2SGiorgis Georgakoudis // Find mergable parallel regions within a basic block that are 10683a6bfcf2SGiorgis Georgakoudis // safe to merge, that is any in-between instructions can safely 10693a6bfcf2SGiorgis Georgakoudis // execute in parallel after merging. 10703a6bfcf2SGiorgis Georgakoudis // TODO: support merging across basic-blocks. 10713a6bfcf2SGiorgis Georgakoudis for (auto &It : BB2PRMap) { 10723a6bfcf2SGiorgis Georgakoudis auto &CIs = It.getSecond(); 10733a6bfcf2SGiorgis Georgakoudis if (CIs.size() < 2) 10743a6bfcf2SGiorgis Georgakoudis continue; 10753a6bfcf2SGiorgis Georgakoudis 10763a6bfcf2SGiorgis Georgakoudis BasicBlock *BB = It.getFirst(); 10773a6bfcf2SGiorgis Georgakoudis SmallVector<CallInst *, 4> MergableCIs; 10783a6bfcf2SGiorgis Georgakoudis 107997517055SGiorgis Georgakoudis /// Returns true if the instruction is mergable, false otherwise. 108097517055SGiorgis Georgakoudis /// A terminator instruction is unmergable by definition since merging 108197517055SGiorgis Georgakoudis /// works within a BB. Instructions before the mergable region are 108297517055SGiorgis Georgakoudis /// mergable if they are not calls to OpenMP runtime functions that may 108397517055SGiorgis Georgakoudis /// set different execution parameters for subsequent parallel regions. 108497517055SGiorgis Georgakoudis /// Instructions in-between parallel regions are mergable if they are not 108597517055SGiorgis Georgakoudis /// calls to any non-intrinsic function since that may call a non-mergable 108697517055SGiorgis Georgakoudis /// OpenMP runtime function. 108797517055SGiorgis Georgakoudis auto IsMergable = [&](Instruction &I, bool IsBeforeMergableRegion) { 108897517055SGiorgis Georgakoudis // We do not merge across BBs, hence return false (unmergable) if the 108997517055SGiorgis Georgakoudis // instruction is a terminator. 109097517055SGiorgis Georgakoudis if (I.isTerminator()) 109197517055SGiorgis Georgakoudis return false; 109297517055SGiorgis Georgakoudis 109397517055SGiorgis Georgakoudis if (!isa<CallInst>(&I)) 109497517055SGiorgis Georgakoudis return true; 109597517055SGiorgis Georgakoudis 109697517055SGiorgis Georgakoudis CallInst *CI = cast<CallInst>(&I); 109797517055SGiorgis Georgakoudis if (IsBeforeMergableRegion) { 109897517055SGiorgis Georgakoudis Function *CalledFunction = CI->getCalledFunction(); 109997517055SGiorgis Georgakoudis if (!CalledFunction) 110097517055SGiorgis Georgakoudis return false; 110197517055SGiorgis Georgakoudis // Return false (unmergable) if the call before the parallel 110297517055SGiorgis Georgakoudis // region calls an explicit affinity (proc_bind) or number of 110397517055SGiorgis Georgakoudis // threads (num_threads) compiler-generated function. Those settings 110497517055SGiorgis Georgakoudis // may be incompatible with following parallel regions. 110597517055SGiorgis Georgakoudis // TODO: ICV tracking to detect compatibility. 110697517055SGiorgis Georgakoudis for (const auto &RFI : UnmergableCallsInfo) { 110797517055SGiorgis Georgakoudis if (CalledFunction == RFI.Declaration) 110897517055SGiorgis Georgakoudis return false; 110997517055SGiorgis Georgakoudis } 111097517055SGiorgis Georgakoudis } else { 111197517055SGiorgis Georgakoudis // Return false (unmergable) if there is a call instruction 111297517055SGiorgis Georgakoudis // in-between parallel regions when it is not an intrinsic. It 111397517055SGiorgis Georgakoudis // may call an unmergable OpenMP runtime function in its callpath. 111497517055SGiorgis Georgakoudis // TODO: Keep track of possible OpenMP calls in the callpath. 111597517055SGiorgis Georgakoudis if (!isa<IntrinsicInst>(CI)) 111697517055SGiorgis Georgakoudis return false; 111797517055SGiorgis Georgakoudis } 111897517055SGiorgis Georgakoudis 111997517055SGiorgis Georgakoudis return true; 112097517055SGiorgis Georgakoudis }; 11213a6bfcf2SGiorgis Georgakoudis // Find maximal number of parallel region CIs that are safe to merge. 112297517055SGiorgis Georgakoudis for (auto It = BB->begin(), End = BB->end(); It != End;) { 112397517055SGiorgis Georgakoudis Instruction &I = *It; 112497517055SGiorgis Georgakoudis ++It; 112597517055SGiorgis Georgakoudis 11263a6bfcf2SGiorgis Georgakoudis if (CIs.count(&I)) { 11273a6bfcf2SGiorgis Georgakoudis MergableCIs.push_back(cast<CallInst>(&I)); 11283a6bfcf2SGiorgis Georgakoudis continue; 11293a6bfcf2SGiorgis Georgakoudis } 11303a6bfcf2SGiorgis Georgakoudis 113197517055SGiorgis Georgakoudis // Continue expanding if the instruction is mergable. 113297517055SGiorgis Georgakoudis if (IsMergable(I, MergableCIs.empty())) 11333a6bfcf2SGiorgis Georgakoudis continue; 11343a6bfcf2SGiorgis Georgakoudis 113597517055SGiorgis Georgakoudis // Forward the instruction iterator to skip the next parallel region 113697517055SGiorgis Georgakoudis // since there is an unmergable instruction which can affect it. 113797517055SGiorgis Georgakoudis for (; It != End; ++It) { 113897517055SGiorgis Georgakoudis Instruction &SkipI = *It; 113997517055SGiorgis Georgakoudis if (CIs.count(&SkipI)) { 114097517055SGiorgis Georgakoudis LLVM_DEBUG(dbgs() << TAG << "Skip parallel region " << SkipI 114197517055SGiorgis Georgakoudis << " due to " << I << "\n"); 114297517055SGiorgis Georgakoudis ++It; 114397517055SGiorgis Georgakoudis break; 114497517055SGiorgis Georgakoudis } 114597517055SGiorgis Georgakoudis } 114697517055SGiorgis Georgakoudis 114797517055SGiorgis Georgakoudis // Store mergable regions found. 11483a6bfcf2SGiorgis Georgakoudis if (MergableCIs.size() > 1) { 11493a6bfcf2SGiorgis Georgakoudis MergableCIsVector.push_back(MergableCIs); 11503a6bfcf2SGiorgis Georgakoudis LLVM_DEBUG(dbgs() << TAG << "Found " << MergableCIs.size() 11513a6bfcf2SGiorgis Georgakoudis << " parallel regions in block " << BB->getName() 11523a6bfcf2SGiorgis Georgakoudis << " of function " << BB->getParent()->getName() 11533a6bfcf2SGiorgis Georgakoudis << "\n";); 11543a6bfcf2SGiorgis Georgakoudis } 11553a6bfcf2SGiorgis Georgakoudis 11563a6bfcf2SGiorgis Georgakoudis MergableCIs.clear(); 11573a6bfcf2SGiorgis Georgakoudis } 11583a6bfcf2SGiorgis Georgakoudis 11593a6bfcf2SGiorgis Georgakoudis if (!MergableCIsVector.empty()) { 11603a6bfcf2SGiorgis Georgakoudis Changed = true; 11613a6bfcf2SGiorgis Georgakoudis 11623a6bfcf2SGiorgis Georgakoudis for (auto &MergableCIs : MergableCIsVector) 11633a6bfcf2SGiorgis Georgakoudis Merge(MergableCIs, BB); 1164b2ad63d3SJoseph Huber MergableCIsVector.clear(); 11653a6bfcf2SGiorgis Georgakoudis } 11663a6bfcf2SGiorgis Georgakoudis } 11673a6bfcf2SGiorgis Georgakoudis 11683a6bfcf2SGiorgis Georgakoudis if (Changed) { 116997517055SGiorgis Georgakoudis /// Re-collect use for fork calls, emitted barrier calls, and 117097517055SGiorgis Georgakoudis /// any emitted master/end_master calls. 117197517055SGiorgis Georgakoudis OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_fork_call); 117297517055SGiorgis Georgakoudis OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_barrier); 117397517055SGiorgis Georgakoudis OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_master); 117497517055SGiorgis Georgakoudis OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_end_master); 11753a6bfcf2SGiorgis Georgakoudis } 11763a6bfcf2SGiorgis Georgakoudis 11773a6bfcf2SGiorgis Georgakoudis return Changed; 11783a6bfcf2SGiorgis Georgakoudis } 11793a6bfcf2SGiorgis Georgakoudis 11809d38f98dSJohannes Doerfert /// Try to delete parallel regions if possible. 1181e565db49SJohannes Doerfert bool deleteParallelRegions() { 1182e565db49SJohannes Doerfert const unsigned CallbackCalleeOperand = 2; 1183e565db49SJohannes Doerfert 11847cfd267cSsstefan1 OMPInformationCache::RuntimeFunctionInfo &RFI = 11857cfd267cSsstefan1 OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call]; 11867cfd267cSsstefan1 1187e565db49SJohannes Doerfert if (!RFI.Declaration) 1188e565db49SJohannes Doerfert return false; 1189e565db49SJohannes Doerfert 1190e565db49SJohannes Doerfert bool Changed = false; 1191e565db49SJohannes Doerfert auto DeleteCallCB = [&](Use &U, Function &) { 1192e565db49SJohannes Doerfert CallInst *CI = getCallIfRegularCall(U); 1193e565db49SJohannes Doerfert if (!CI) 1194e565db49SJohannes Doerfert return false; 1195e565db49SJohannes Doerfert auto *Fn = dyn_cast<Function>( 1196e565db49SJohannes Doerfert CI->getArgOperand(CallbackCalleeOperand)->stripPointerCasts()); 1197e565db49SJohannes Doerfert if (!Fn) 1198e565db49SJohannes Doerfert return false; 1199e565db49SJohannes Doerfert if (!Fn->onlyReadsMemory()) 1200e565db49SJohannes Doerfert return false; 1201e565db49SJohannes Doerfert if (!Fn->hasFnAttribute(Attribute::WillReturn)) 1202e565db49SJohannes Doerfert return false; 1203e565db49SJohannes Doerfert 1204e565db49SJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "Delete read-only parallel region in " 1205e565db49SJohannes Doerfert << CI->getCaller()->getName() << "\n"); 12064d4ea9acSHuber, Joseph 12074d4ea9acSHuber, Joseph auto Remark = [&](OptimizationRemark OR) { 1208eef6601bSJoseph Huber return OR << "Removing parallel region with no side-effects."; 12094d4ea9acSHuber, Joseph }; 12102c31d5ebSJoseph Huber emitRemark<OptimizationRemark>(CI, "OMP160", Remark); 12114d4ea9acSHuber, Joseph 1212e565db49SJohannes Doerfert CGUpdater.removeCallSite(*CI); 1213e565db49SJohannes Doerfert CI->eraseFromParent(); 1214e565db49SJohannes Doerfert Changed = true; 121555eb714aSRoman Lebedev ++NumOpenMPParallelRegionsDeleted; 1216e565db49SJohannes Doerfert return true; 1217e565db49SJohannes Doerfert }; 1218e565db49SJohannes Doerfert 1219624d34afSJohannes Doerfert RFI.foreachUse(SCC, DeleteCallCB); 1220e565db49SJohannes Doerfert 1221e565db49SJohannes Doerfert return Changed; 1222e565db49SJohannes Doerfert } 1223e565db49SJohannes Doerfert 1224b726c557SJohannes Doerfert /// Try to eliminate runtime calls by reusing existing ones. 12259548b74aSJohannes Doerfert bool deduplicateRuntimeCalls() { 12269548b74aSJohannes Doerfert bool Changed = false; 12279548b74aSJohannes Doerfert 1228e28936f6SJohannes Doerfert RuntimeFunction DeduplicableRuntimeCallIDs[] = { 1229e28936f6SJohannes Doerfert OMPRTL_omp_get_num_threads, 1230e28936f6SJohannes Doerfert OMPRTL_omp_in_parallel, 1231e28936f6SJohannes Doerfert OMPRTL_omp_get_cancellation, 1232e28936f6SJohannes Doerfert OMPRTL_omp_get_thread_limit, 1233e28936f6SJohannes Doerfert OMPRTL_omp_get_supported_active_levels, 1234e28936f6SJohannes Doerfert OMPRTL_omp_get_level, 1235e28936f6SJohannes Doerfert OMPRTL_omp_get_ancestor_thread_num, 1236e28936f6SJohannes Doerfert OMPRTL_omp_get_team_size, 1237e28936f6SJohannes Doerfert OMPRTL_omp_get_active_level, 1238e28936f6SJohannes Doerfert OMPRTL_omp_in_final, 1239e28936f6SJohannes Doerfert OMPRTL_omp_get_proc_bind, 1240e28936f6SJohannes Doerfert OMPRTL_omp_get_num_places, 1241e28936f6SJohannes Doerfert OMPRTL_omp_get_num_procs, 1242e28936f6SJohannes Doerfert OMPRTL_omp_get_place_num, 1243e28936f6SJohannes Doerfert OMPRTL_omp_get_partition_num_places, 1244e28936f6SJohannes Doerfert OMPRTL_omp_get_partition_place_nums}; 1245e28936f6SJohannes Doerfert 1246bc93c2d7SMarek Kurdej // Global-tid is handled separately. 12479548b74aSJohannes Doerfert SmallSetVector<Value *, 16> GTIdArgs; 12489548b74aSJohannes Doerfert collectGlobalThreadIdArguments(GTIdArgs); 12499548b74aSJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "Found " << GTIdArgs.size() 12509548b74aSJohannes Doerfert << " global thread ID arguments\n"); 12519548b74aSJohannes Doerfert 12529548b74aSJohannes Doerfert for (Function *F : SCC) { 1253e28936f6SJohannes Doerfert for (auto DeduplicableRuntimeCallID : DeduplicableRuntimeCallIDs) 12544e29d256Sserge-sans-paille Changed |= deduplicateRuntimeCalls( 12554e29d256Sserge-sans-paille *F, OMPInfoCache.RFIs[DeduplicableRuntimeCallID]); 1256e28936f6SJohannes Doerfert 1257e28936f6SJohannes Doerfert // __kmpc_global_thread_num is special as we can replace it with an 1258e28936f6SJohannes Doerfert // argument in enough cases to make it worth trying. 12599548b74aSJohannes Doerfert Value *GTIdArg = nullptr; 12609548b74aSJohannes Doerfert for (Argument &Arg : F->args()) 12619548b74aSJohannes Doerfert if (GTIdArgs.count(&Arg)) { 12629548b74aSJohannes Doerfert GTIdArg = &Arg; 12639548b74aSJohannes Doerfert break; 12649548b74aSJohannes Doerfert } 12659548b74aSJohannes Doerfert Changed |= deduplicateRuntimeCalls( 12667cfd267cSsstefan1 *F, OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num], GTIdArg); 12679548b74aSJohannes Doerfert } 12689548b74aSJohannes Doerfert 12699548b74aSJohannes Doerfert return Changed; 12709548b74aSJohannes Doerfert } 12719548b74aSJohannes Doerfert 1272496f8e5bSHamilton Tobon Mosquera /// Tries to hide the latency of runtime calls that involve host to 1273496f8e5bSHamilton Tobon Mosquera /// device memory transfers by splitting them into their "issue" and "wait" 1274496f8e5bSHamilton Tobon Mosquera /// versions. The "issue" is moved upwards as much as possible. The "wait" is 1275496f8e5bSHamilton Tobon Mosquera /// moved downards as much as possible. The "issue" issues the memory transfer 1276496f8e5bSHamilton Tobon Mosquera /// asynchronously, returning a handle. The "wait" waits in the returned 1277496f8e5bSHamilton Tobon Mosquera /// handle for the memory transfer to finish. 1278496f8e5bSHamilton Tobon Mosquera bool hideMemTransfersLatency() { 1279496f8e5bSHamilton Tobon Mosquera auto &RFI = OMPInfoCache.RFIs[OMPRTL___tgt_target_data_begin_mapper]; 1280496f8e5bSHamilton Tobon Mosquera bool Changed = false; 1281496f8e5bSHamilton Tobon Mosquera auto SplitMemTransfers = [&](Use &U, Function &Decl) { 1282496f8e5bSHamilton Tobon Mosquera auto *RTCall = getCallIfRegularCall(U, &RFI); 1283496f8e5bSHamilton Tobon Mosquera if (!RTCall) 1284496f8e5bSHamilton Tobon Mosquera return false; 1285496f8e5bSHamilton Tobon Mosquera 12868931add6SHamilton Tobon Mosquera OffloadArray OffloadArrays[3]; 12878931add6SHamilton Tobon Mosquera if (!getValuesInOffloadArrays(*RTCall, OffloadArrays)) 12888931add6SHamilton Tobon Mosquera return false; 12898931add6SHamilton Tobon Mosquera 12908931add6SHamilton Tobon Mosquera LLVM_DEBUG(dumpValuesInOffloadArrays(OffloadArrays)); 12918931add6SHamilton Tobon Mosquera 1292bd2fa181SHamilton Tobon Mosquera // TODO: Check if can be moved upwards. 1293bd2fa181SHamilton Tobon Mosquera bool WasSplit = false; 1294bd2fa181SHamilton Tobon Mosquera Instruction *WaitMovementPoint = canBeMovedDownwards(*RTCall); 1295bd2fa181SHamilton Tobon Mosquera if (WaitMovementPoint) 1296bd2fa181SHamilton Tobon Mosquera WasSplit = splitTargetDataBeginRTC(*RTCall, *WaitMovementPoint); 1297bd2fa181SHamilton Tobon Mosquera 1298496f8e5bSHamilton Tobon Mosquera Changed |= WasSplit; 1299496f8e5bSHamilton Tobon Mosquera return WasSplit; 1300496f8e5bSHamilton Tobon Mosquera }; 1301496f8e5bSHamilton Tobon Mosquera RFI.foreachUse(SCC, SplitMemTransfers); 1302496f8e5bSHamilton Tobon Mosquera 1303496f8e5bSHamilton Tobon Mosquera return Changed; 1304496f8e5bSHamilton Tobon Mosquera } 1305496f8e5bSHamilton Tobon Mosquera 1306a2281419SJoseph Huber void analysisGlobalization() { 13076fc51c9fSJoseph Huber auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared]; 130882453e75SJoseph Huber 130982453e75SJoseph Huber auto CheckGlobalization = [&](Use &U, Function &Decl) { 1310a2281419SJoseph Huber if (CallInst *CI = getCallIfRegularCall(U, &RFI)) { 131144feacc7SJoseph Huber auto Remark = [&](OptimizationRemarkMissed ORM) { 131244feacc7SJoseph Huber return ORM 1313a2281419SJoseph Huber << "Found thread data sharing on the GPU. " 1314a2281419SJoseph Huber << "Expect degraded performance due to data globalization."; 1315a2281419SJoseph Huber }; 13162c31d5ebSJoseph Huber emitRemark<OptimizationRemarkMissed>(CI, "OMP112", Remark); 1317a2281419SJoseph Huber } 1318a2281419SJoseph Huber 1319a2281419SJoseph Huber return false; 1320a2281419SJoseph Huber }; 1321a2281419SJoseph Huber 132282453e75SJoseph Huber RFI.foreachUse(SCC, CheckGlobalization); 132382453e75SJoseph Huber } 1324a2281419SJoseph Huber 13258931add6SHamilton Tobon Mosquera /// Maps the values stored in the offload arrays passed as arguments to 13268931add6SHamilton Tobon Mosquera /// \p RuntimeCall into the offload arrays in \p OAs. 13278931add6SHamilton Tobon Mosquera bool getValuesInOffloadArrays(CallInst &RuntimeCall, 13288931add6SHamilton Tobon Mosquera MutableArrayRef<OffloadArray> OAs) { 13298931add6SHamilton Tobon Mosquera assert(OAs.size() == 3 && "Need space for three offload arrays!"); 13308931add6SHamilton Tobon Mosquera 13318931add6SHamilton Tobon Mosquera // A runtime call that involves memory offloading looks something like: 13328931add6SHamilton Tobon Mosquera // call void @__tgt_target_data_begin_mapper(arg0, arg1, 13338931add6SHamilton Tobon Mosquera // i8** %offload_baseptrs, i8** %offload_ptrs, i64* %offload_sizes, 13348931add6SHamilton Tobon Mosquera // ...) 13358931add6SHamilton Tobon Mosquera // So, the idea is to access the allocas that allocate space for these 13368931add6SHamilton Tobon Mosquera // offload arrays, offload_baseptrs, offload_ptrs, offload_sizes. 13378931add6SHamilton Tobon Mosquera // Therefore: 13388931add6SHamilton Tobon Mosquera // i8** %offload_baseptrs. 13391d3d9b9cSHamilton Tobon Mosquera Value *BasePtrsArg = 13401d3d9b9cSHamilton Tobon Mosquera RuntimeCall.getArgOperand(OffloadArray::BasePtrsArgNum); 13418931add6SHamilton Tobon Mosquera // i8** %offload_ptrs. 13421d3d9b9cSHamilton Tobon Mosquera Value *PtrsArg = RuntimeCall.getArgOperand(OffloadArray::PtrsArgNum); 13438931add6SHamilton Tobon Mosquera // i8** %offload_sizes. 13441d3d9b9cSHamilton Tobon Mosquera Value *SizesArg = RuntimeCall.getArgOperand(OffloadArray::SizesArgNum); 13458931add6SHamilton Tobon Mosquera 13468931add6SHamilton Tobon Mosquera // Get values stored in **offload_baseptrs. 13478931add6SHamilton Tobon Mosquera auto *V = getUnderlyingObject(BasePtrsArg); 13488931add6SHamilton Tobon Mosquera if (!isa<AllocaInst>(V)) 13498931add6SHamilton Tobon Mosquera return false; 13508931add6SHamilton Tobon Mosquera auto *BasePtrsArray = cast<AllocaInst>(V); 13518931add6SHamilton Tobon Mosquera if (!OAs[0].initialize(*BasePtrsArray, RuntimeCall)) 13528931add6SHamilton Tobon Mosquera return false; 13538931add6SHamilton Tobon Mosquera 13548931add6SHamilton Tobon Mosquera // Get values stored in **offload_baseptrs. 13558931add6SHamilton Tobon Mosquera V = getUnderlyingObject(PtrsArg); 13568931add6SHamilton Tobon Mosquera if (!isa<AllocaInst>(V)) 13578931add6SHamilton Tobon Mosquera return false; 13588931add6SHamilton Tobon Mosquera auto *PtrsArray = cast<AllocaInst>(V); 13598931add6SHamilton Tobon Mosquera if (!OAs[1].initialize(*PtrsArray, RuntimeCall)) 13608931add6SHamilton Tobon Mosquera return false; 13618931add6SHamilton Tobon Mosquera 13628931add6SHamilton Tobon Mosquera // Get values stored in **offload_sizes. 13638931add6SHamilton Tobon Mosquera V = getUnderlyingObject(SizesArg); 13648931add6SHamilton Tobon Mosquera // If it's a [constant] global array don't analyze it. 13658931add6SHamilton Tobon Mosquera if (isa<GlobalValue>(V)) 13668931add6SHamilton Tobon Mosquera return isa<Constant>(V); 13678931add6SHamilton Tobon Mosquera if (!isa<AllocaInst>(V)) 13688931add6SHamilton Tobon Mosquera return false; 13698931add6SHamilton Tobon Mosquera 13708931add6SHamilton Tobon Mosquera auto *SizesArray = cast<AllocaInst>(V); 13718931add6SHamilton Tobon Mosquera if (!OAs[2].initialize(*SizesArray, RuntimeCall)) 13728931add6SHamilton Tobon Mosquera return false; 13738931add6SHamilton Tobon Mosquera 13748931add6SHamilton Tobon Mosquera return true; 13758931add6SHamilton Tobon Mosquera } 13768931add6SHamilton Tobon Mosquera 13778931add6SHamilton Tobon Mosquera /// Prints the values in the OffloadArrays \p OAs using LLVM_DEBUG. 13788931add6SHamilton Tobon Mosquera /// For now this is a way to test that the function getValuesInOffloadArrays 13798931add6SHamilton Tobon Mosquera /// is working properly. 13808931add6SHamilton Tobon Mosquera /// TODO: Move this to a unittest when unittests are available for OpenMPOpt. 13818931add6SHamilton Tobon Mosquera void dumpValuesInOffloadArrays(ArrayRef<OffloadArray> OAs) { 13828931add6SHamilton Tobon Mosquera assert(OAs.size() == 3 && "There are three offload arrays to debug!"); 13838931add6SHamilton Tobon Mosquera 13848931add6SHamilton Tobon Mosquera LLVM_DEBUG(dbgs() << TAG << " Successfully got offload values:\n"); 13858931add6SHamilton Tobon Mosquera std::string ValuesStr; 13868931add6SHamilton Tobon Mosquera raw_string_ostream Printer(ValuesStr); 13878931add6SHamilton Tobon Mosquera std::string Separator = " --- "; 13888931add6SHamilton Tobon Mosquera 13898931add6SHamilton Tobon Mosquera for (auto *BP : OAs[0].StoredValues) { 13908931add6SHamilton Tobon Mosquera BP->print(Printer); 13918931add6SHamilton Tobon Mosquera Printer << Separator; 13928931add6SHamilton Tobon Mosquera } 13938931add6SHamilton Tobon Mosquera LLVM_DEBUG(dbgs() << "\t\toffload_baseptrs: " << Printer.str() << "\n"); 13948931add6SHamilton Tobon Mosquera ValuesStr.clear(); 13958931add6SHamilton Tobon Mosquera 13968931add6SHamilton Tobon Mosquera for (auto *P : OAs[1].StoredValues) { 13978931add6SHamilton Tobon Mosquera P->print(Printer); 13988931add6SHamilton Tobon Mosquera Printer << Separator; 13998931add6SHamilton Tobon Mosquera } 14008931add6SHamilton Tobon Mosquera LLVM_DEBUG(dbgs() << "\t\toffload_ptrs: " << Printer.str() << "\n"); 14018931add6SHamilton Tobon Mosquera ValuesStr.clear(); 14028931add6SHamilton Tobon Mosquera 14038931add6SHamilton Tobon Mosquera for (auto *S : OAs[2].StoredValues) { 14048931add6SHamilton Tobon Mosquera S->print(Printer); 14058931add6SHamilton Tobon Mosquera Printer << Separator; 14068931add6SHamilton Tobon Mosquera } 14078931add6SHamilton Tobon Mosquera LLVM_DEBUG(dbgs() << "\t\toffload_sizes: " << Printer.str() << "\n"); 14088931add6SHamilton Tobon Mosquera } 14098931add6SHamilton Tobon Mosquera 1410bd2fa181SHamilton Tobon Mosquera /// Returns the instruction where the "wait" counterpart \p RuntimeCall can be 1411bd2fa181SHamilton Tobon Mosquera /// moved. Returns nullptr if the movement is not possible, or not worth it. 1412bd2fa181SHamilton Tobon Mosquera Instruction *canBeMovedDownwards(CallInst &RuntimeCall) { 1413bd2fa181SHamilton Tobon Mosquera // FIXME: This traverses only the BasicBlock where RuntimeCall is. 1414bd2fa181SHamilton Tobon Mosquera // Make it traverse the CFG. 1415bd2fa181SHamilton Tobon Mosquera 1416bd2fa181SHamilton Tobon Mosquera Instruction *CurrentI = &RuntimeCall; 1417bd2fa181SHamilton Tobon Mosquera bool IsWorthIt = false; 1418bd2fa181SHamilton Tobon Mosquera while ((CurrentI = CurrentI->getNextNode())) { 1419bd2fa181SHamilton Tobon Mosquera 1420bd2fa181SHamilton Tobon Mosquera // TODO: Once we detect the regions to be offloaded we should use the 1421bd2fa181SHamilton Tobon Mosquera // alias analysis manager to check if CurrentI may modify one of 1422bd2fa181SHamilton Tobon Mosquera // the offloaded regions. 1423bd2fa181SHamilton Tobon Mosquera if (CurrentI->mayHaveSideEffects() || CurrentI->mayReadFromMemory()) { 1424bd2fa181SHamilton Tobon Mosquera if (IsWorthIt) 1425bd2fa181SHamilton Tobon Mosquera return CurrentI; 1426bd2fa181SHamilton Tobon Mosquera 1427bd2fa181SHamilton Tobon Mosquera return nullptr; 1428bd2fa181SHamilton Tobon Mosquera } 1429bd2fa181SHamilton Tobon Mosquera 1430bd2fa181SHamilton Tobon Mosquera // FIXME: For now if we move it over anything without side effect 1431bd2fa181SHamilton Tobon Mosquera // is worth it. 1432bd2fa181SHamilton Tobon Mosquera IsWorthIt = true; 1433bd2fa181SHamilton Tobon Mosquera } 1434bd2fa181SHamilton Tobon Mosquera 1435bd2fa181SHamilton Tobon Mosquera // Return end of BasicBlock. 1436bd2fa181SHamilton Tobon Mosquera return RuntimeCall.getParent()->getTerminator(); 1437bd2fa181SHamilton Tobon Mosquera } 1438bd2fa181SHamilton Tobon Mosquera 1439496f8e5bSHamilton Tobon Mosquera /// Splits \p RuntimeCall into its "issue" and "wait" counterparts. 1440bd2fa181SHamilton Tobon Mosquera bool splitTargetDataBeginRTC(CallInst &RuntimeCall, 1441bd2fa181SHamilton Tobon Mosquera Instruction &WaitMovementPoint) { 1442bd31abc1SHamilton Tobon Mosquera // Create stack allocated handle (__tgt_async_info) at the beginning of the 1443bd31abc1SHamilton Tobon Mosquera // function. Used for storing information of the async transfer, allowing to 1444bd31abc1SHamilton Tobon Mosquera // wait on it later. 1445496f8e5bSHamilton Tobon Mosquera auto &IRBuilder = OMPInfoCache.OMPBuilder; 1446bd31abc1SHamilton Tobon Mosquera auto *F = RuntimeCall.getCaller(); 1447bd31abc1SHamilton Tobon Mosquera Instruction *FirstInst = &(F->getEntryBlock().front()); 1448bd31abc1SHamilton Tobon Mosquera AllocaInst *Handle = new AllocaInst( 1449bd31abc1SHamilton Tobon Mosquera IRBuilder.AsyncInfo, F->getAddressSpace(), "handle", FirstInst); 1450bd31abc1SHamilton Tobon Mosquera 1451496f8e5bSHamilton Tobon Mosquera // Add "issue" runtime call declaration: 1452496f8e5bSHamilton Tobon Mosquera // declare %struct.tgt_async_info @__tgt_target_data_begin_issue(i64, i32, 1453496f8e5bSHamilton Tobon Mosquera // i8**, i8**, i64*, i64*) 1454496f8e5bSHamilton Tobon Mosquera FunctionCallee IssueDecl = IRBuilder.getOrCreateRuntimeFunction( 1455496f8e5bSHamilton Tobon Mosquera M, OMPRTL___tgt_target_data_begin_mapper_issue); 1456496f8e5bSHamilton Tobon Mosquera 1457496f8e5bSHamilton Tobon Mosquera // Change RuntimeCall call site for its asynchronous version. 145897e55cfeSJoseph Huber SmallVector<Value *, 16> Args; 1459bd2fa181SHamilton Tobon Mosquera for (auto &Arg : RuntimeCall.args()) 1460496f8e5bSHamilton Tobon Mosquera Args.push_back(Arg.get()); 1461bd31abc1SHamilton Tobon Mosquera Args.push_back(Handle); 1462496f8e5bSHamilton Tobon Mosquera 1463496f8e5bSHamilton Tobon Mosquera CallInst *IssueCallsite = 1464bd31abc1SHamilton Tobon Mosquera CallInst::Create(IssueDecl, Args, /*NameStr=*/"", &RuntimeCall); 1465bd2fa181SHamilton Tobon Mosquera RuntimeCall.eraseFromParent(); 1466496f8e5bSHamilton Tobon Mosquera 1467496f8e5bSHamilton Tobon Mosquera // Add "wait" runtime call declaration: 1468496f8e5bSHamilton Tobon Mosquera // declare void @__tgt_target_data_begin_wait(i64, %struct.__tgt_async_info) 1469496f8e5bSHamilton Tobon Mosquera FunctionCallee WaitDecl = IRBuilder.getOrCreateRuntimeFunction( 1470496f8e5bSHamilton Tobon Mosquera M, OMPRTL___tgt_target_data_begin_mapper_wait); 1471496f8e5bSHamilton Tobon Mosquera 1472496f8e5bSHamilton Tobon Mosquera Value *WaitParams[2] = { 1473da8bec47SJoseph Huber IssueCallsite->getArgOperand( 1474da8bec47SJoseph Huber OffloadArray::DeviceIDArgNum), // device_id. 1475bd31abc1SHamilton Tobon Mosquera Handle // handle to wait on. 1476496f8e5bSHamilton Tobon Mosquera }; 1477bd2fa181SHamilton Tobon Mosquera CallInst::Create(WaitDecl, WaitParams, /*NameStr=*/"", &WaitMovementPoint); 1478496f8e5bSHamilton Tobon Mosquera 1479496f8e5bSHamilton Tobon Mosquera return true; 1480496f8e5bSHamilton Tobon Mosquera } 1481496f8e5bSHamilton Tobon Mosquera 1482dc3b5b00SJohannes Doerfert static Value *combinedIdentStruct(Value *CurrentIdent, Value *NextIdent, 1483dc3b5b00SJohannes Doerfert bool GlobalOnly, bool &SingleChoice) { 1484dc3b5b00SJohannes Doerfert if (CurrentIdent == NextIdent) 1485dc3b5b00SJohannes Doerfert return CurrentIdent; 1486dc3b5b00SJohannes Doerfert 1487396b7253SJohannes Doerfert // TODO: Figure out how to actually combine multiple debug locations. For 1488dc3b5b00SJohannes Doerfert // now we just keep an existing one if there is a single choice. 1489dc3b5b00SJohannes Doerfert if (!GlobalOnly || isa<GlobalValue>(NextIdent)) { 1490dc3b5b00SJohannes Doerfert SingleChoice = !CurrentIdent; 1491dc3b5b00SJohannes Doerfert return NextIdent; 1492dc3b5b00SJohannes Doerfert } 1493396b7253SJohannes Doerfert return nullptr; 1494396b7253SJohannes Doerfert } 1495396b7253SJohannes Doerfert 1496396b7253SJohannes Doerfert /// Return an `struct ident_t*` value that represents the ones used in the 1497396b7253SJohannes Doerfert /// calls of \p RFI inside of \p F. If \p GlobalOnly is true, we will not 1498396b7253SJohannes Doerfert /// return a local `struct ident_t*`. For now, if we cannot find a suitable 1499396b7253SJohannes Doerfert /// return value we create one from scratch. We also do not yet combine 1500396b7253SJohannes Doerfert /// information, e.g., the source locations, see combinedIdentStruct. 15017cfd267cSsstefan1 Value * 15027cfd267cSsstefan1 getCombinedIdentFromCallUsesIn(OMPInformationCache::RuntimeFunctionInfo &RFI, 15037cfd267cSsstefan1 Function &F, bool GlobalOnly) { 1504dc3b5b00SJohannes Doerfert bool SingleChoice = true; 1505396b7253SJohannes Doerfert Value *Ident = nullptr; 1506396b7253SJohannes Doerfert auto CombineIdentStruct = [&](Use &U, Function &Caller) { 1507396b7253SJohannes Doerfert CallInst *CI = getCallIfRegularCall(U, &RFI); 1508396b7253SJohannes Doerfert if (!CI || &F != &Caller) 1509396b7253SJohannes Doerfert return false; 1510396b7253SJohannes Doerfert Ident = combinedIdentStruct(Ident, CI->getArgOperand(0), 1511dc3b5b00SJohannes Doerfert /* GlobalOnly */ true, SingleChoice); 1512396b7253SJohannes Doerfert return false; 1513396b7253SJohannes Doerfert }; 1514624d34afSJohannes Doerfert RFI.foreachUse(SCC, CombineIdentStruct); 1515396b7253SJohannes Doerfert 1516dc3b5b00SJohannes Doerfert if (!Ident || !SingleChoice) { 1517396b7253SJohannes Doerfert // The IRBuilder uses the insertion block to get to the module, this is 1518396b7253SJohannes Doerfert // unfortunate but we work around it for now. 15197cfd267cSsstefan1 if (!OMPInfoCache.OMPBuilder.getInsertionPoint().getBlock()) 15207cfd267cSsstefan1 OMPInfoCache.OMPBuilder.updateToLocation(OpenMPIRBuilder::InsertPointTy( 1521396b7253SJohannes Doerfert &F.getEntryBlock(), F.getEntryBlock().begin())); 1522396b7253SJohannes Doerfert // Create a fallback location if non was found. 1523396b7253SJohannes Doerfert // TODO: Use the debug locations of the calls instead. 15247cfd267cSsstefan1 Constant *Loc = OMPInfoCache.OMPBuilder.getOrCreateDefaultSrcLocStr(); 15257cfd267cSsstefan1 Ident = OMPInfoCache.OMPBuilder.getOrCreateIdent(Loc); 1526396b7253SJohannes Doerfert } 1527396b7253SJohannes Doerfert return Ident; 1528396b7253SJohannes Doerfert } 1529396b7253SJohannes Doerfert 1530b726c557SJohannes Doerfert /// Try to eliminate calls of \p RFI in \p F by reusing an existing one or 15319548b74aSJohannes Doerfert /// \p ReplVal if given. 15327cfd267cSsstefan1 bool deduplicateRuntimeCalls(Function &F, 15337cfd267cSsstefan1 OMPInformationCache::RuntimeFunctionInfo &RFI, 15349548b74aSJohannes Doerfert Value *ReplVal = nullptr) { 15358855fec3SJohannes Doerfert auto *UV = RFI.getUseVector(F); 15368855fec3SJohannes Doerfert if (!UV || UV->size() + (ReplVal != nullptr) < 2) 1537b1fbf438SRoman Lebedev return false; 1538b1fbf438SRoman Lebedev 15397cfd267cSsstefan1 LLVM_DEBUG( 15407cfd267cSsstefan1 dbgs() << TAG << "Deduplicate " << UV->size() << " uses of " << RFI.Name 15417cfd267cSsstefan1 << (ReplVal ? " with an existing value\n" : "\n") << "\n"); 15427cfd267cSsstefan1 1543ab3da5ddSMichael Liao assert((!ReplVal || (isa<Argument>(ReplVal) && 1544ab3da5ddSMichael Liao cast<Argument>(ReplVal)->getParent() == &F)) && 15459548b74aSJohannes Doerfert "Unexpected replacement value!"); 1546396b7253SJohannes Doerfert 1547396b7253SJohannes Doerfert // TODO: Use dominance to find a good position instead. 15486aab27baSsstefan1 auto CanBeMoved = [this](CallBase &CB) { 1549396b7253SJohannes Doerfert unsigned NumArgs = CB.getNumArgOperands(); 1550396b7253SJohannes Doerfert if (NumArgs == 0) 1551396b7253SJohannes Doerfert return true; 15526aab27baSsstefan1 if (CB.getArgOperand(0)->getType() != OMPInfoCache.OMPBuilder.IdentPtr) 1553396b7253SJohannes Doerfert return false; 1554396b7253SJohannes Doerfert for (unsigned u = 1; u < NumArgs; ++u) 1555396b7253SJohannes Doerfert if (isa<Instruction>(CB.getArgOperand(u))) 1556396b7253SJohannes Doerfert return false; 1557396b7253SJohannes Doerfert return true; 1558396b7253SJohannes Doerfert }; 1559396b7253SJohannes Doerfert 15609548b74aSJohannes Doerfert if (!ReplVal) { 15618855fec3SJohannes Doerfert for (Use *U : *UV) 15629548b74aSJohannes Doerfert if (CallInst *CI = getCallIfRegularCall(*U, &RFI)) { 1563396b7253SJohannes Doerfert if (!CanBeMoved(*CI)) 1564396b7253SJohannes Doerfert continue; 15654d4ea9acSHuber, Joseph 15669548b74aSJohannes Doerfert CI->moveBefore(&*F.getEntryBlock().getFirstInsertionPt()); 15679548b74aSJohannes Doerfert ReplVal = CI; 15689548b74aSJohannes Doerfert break; 15699548b74aSJohannes Doerfert } 15709548b74aSJohannes Doerfert if (!ReplVal) 15719548b74aSJohannes Doerfert return false; 15729548b74aSJohannes Doerfert } 15739548b74aSJohannes Doerfert 1574396b7253SJohannes Doerfert // If we use a call as a replacement value we need to make sure the ident is 1575396b7253SJohannes Doerfert // valid at the new location. For now we just pick a global one, either 1576396b7253SJohannes Doerfert // existing and used by one of the calls, or created from scratch. 1577396b7253SJohannes Doerfert if (CallBase *CI = dyn_cast<CallBase>(ReplVal)) { 1578396b7253SJohannes Doerfert if (CI->getNumArgOperands() > 0 && 15796aab27baSsstefan1 CI->getArgOperand(0)->getType() == OMPInfoCache.OMPBuilder.IdentPtr) { 1580396b7253SJohannes Doerfert Value *Ident = getCombinedIdentFromCallUsesIn(RFI, F, 1581396b7253SJohannes Doerfert /* GlobalOnly */ true); 1582396b7253SJohannes Doerfert CI->setArgOperand(0, Ident); 1583396b7253SJohannes Doerfert } 1584396b7253SJohannes Doerfert } 1585396b7253SJohannes Doerfert 15869548b74aSJohannes Doerfert bool Changed = false; 15879548b74aSJohannes Doerfert auto ReplaceAndDeleteCB = [&](Use &U, Function &Caller) { 15889548b74aSJohannes Doerfert CallInst *CI = getCallIfRegularCall(U, &RFI); 15899548b74aSJohannes Doerfert if (!CI || CI == ReplVal || &F != &Caller) 15909548b74aSJohannes Doerfert return false; 15919548b74aSJohannes Doerfert assert(CI->getCaller() == &F && "Unexpected call!"); 15924d4ea9acSHuber, Joseph 15934d4ea9acSHuber, Joseph auto Remark = [&](OptimizationRemark OR) { 15944d4ea9acSHuber, Joseph return OR << "OpenMP runtime call " 1595eef6601bSJoseph Huber << ore::NV("OpenMPOptRuntime", RFI.Name) << " deduplicated."; 15964d4ea9acSHuber, Joseph }; 1597eef6601bSJoseph Huber if (CI->getDebugLoc()) 15982c31d5ebSJoseph Huber emitRemark<OptimizationRemark>(CI, "OMP170", Remark); 1599eef6601bSJoseph Huber else 16002c31d5ebSJoseph Huber emitRemark<OptimizationRemark>(&F, "OMP170", Remark); 16014d4ea9acSHuber, Joseph 16029548b74aSJohannes Doerfert CGUpdater.removeCallSite(*CI); 16039548b74aSJohannes Doerfert CI->replaceAllUsesWith(ReplVal); 16049548b74aSJohannes Doerfert CI->eraseFromParent(); 16059548b74aSJohannes Doerfert ++NumOpenMPRuntimeCallsDeduplicated; 16069548b74aSJohannes Doerfert Changed = true; 16079548b74aSJohannes Doerfert return true; 16089548b74aSJohannes Doerfert }; 1609624d34afSJohannes Doerfert RFI.foreachUse(SCC, ReplaceAndDeleteCB); 16109548b74aSJohannes Doerfert 16119548b74aSJohannes Doerfert return Changed; 16129548b74aSJohannes Doerfert } 16139548b74aSJohannes Doerfert 16149548b74aSJohannes Doerfert /// Collect arguments that represent the global thread id in \p GTIdArgs. 16159548b74aSJohannes Doerfert void collectGlobalThreadIdArguments(SmallSetVector<Value *, 16> >IdArgs) { 16169548b74aSJohannes Doerfert // TODO: Below we basically perform a fixpoint iteration with a pessimistic 16179548b74aSJohannes Doerfert // initialization. We could define an AbstractAttribute instead and 16189548b74aSJohannes Doerfert // run the Attributor here once it can be run as an SCC pass. 16199548b74aSJohannes Doerfert 16209548b74aSJohannes Doerfert // Helper to check the argument \p ArgNo at all call sites of \p F for 16219548b74aSJohannes Doerfert // a GTId. 16229548b74aSJohannes Doerfert auto CallArgOpIsGTId = [&](Function &F, unsigned ArgNo, CallInst &RefCI) { 16239548b74aSJohannes Doerfert if (!F.hasLocalLinkage()) 16249548b74aSJohannes Doerfert return false; 16259548b74aSJohannes Doerfert for (Use &U : F.uses()) { 16269548b74aSJohannes Doerfert if (CallInst *CI = getCallIfRegularCall(U)) { 16279548b74aSJohannes Doerfert Value *ArgOp = CI->getArgOperand(ArgNo); 16289548b74aSJohannes Doerfert if (CI == &RefCI || GTIdArgs.count(ArgOp) || 16297cfd267cSsstefan1 getCallIfRegularCall( 16307cfd267cSsstefan1 *ArgOp, &OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num])) 16319548b74aSJohannes Doerfert continue; 16329548b74aSJohannes Doerfert } 16339548b74aSJohannes Doerfert return false; 16349548b74aSJohannes Doerfert } 16359548b74aSJohannes Doerfert return true; 16369548b74aSJohannes Doerfert }; 16379548b74aSJohannes Doerfert 16389548b74aSJohannes Doerfert // Helper to identify uses of a GTId as GTId arguments. 16399548b74aSJohannes Doerfert auto AddUserArgs = [&](Value >Id) { 16409548b74aSJohannes Doerfert for (Use &U : GTId.uses()) 16419548b74aSJohannes Doerfert if (CallInst *CI = dyn_cast<CallInst>(U.getUser())) 16429548b74aSJohannes Doerfert if (CI->isArgOperand(&U)) 16439548b74aSJohannes Doerfert if (Function *Callee = CI->getCalledFunction()) 16449548b74aSJohannes Doerfert if (CallArgOpIsGTId(*Callee, U.getOperandNo(), *CI)) 16459548b74aSJohannes Doerfert GTIdArgs.insert(Callee->getArg(U.getOperandNo())); 16469548b74aSJohannes Doerfert }; 16479548b74aSJohannes Doerfert 16489548b74aSJohannes Doerfert // The argument users of __kmpc_global_thread_num calls are GTIds. 16497cfd267cSsstefan1 OMPInformationCache::RuntimeFunctionInfo &GlobThreadNumRFI = 16507cfd267cSsstefan1 OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num]; 16517cfd267cSsstefan1 1652624d34afSJohannes Doerfert GlobThreadNumRFI.foreachUse(SCC, [&](Use &U, Function &F) { 16538855fec3SJohannes Doerfert if (CallInst *CI = getCallIfRegularCall(U, &GlobThreadNumRFI)) 16549548b74aSJohannes Doerfert AddUserArgs(*CI); 16558855fec3SJohannes Doerfert return false; 16568855fec3SJohannes Doerfert }); 16579548b74aSJohannes Doerfert 16589548b74aSJohannes Doerfert // Transitively search for more arguments by looking at the users of the 16599548b74aSJohannes Doerfert // ones we know already. During the search the GTIdArgs vector is extended 16609548b74aSJohannes Doerfert // so we cannot cache the size nor can we use a range based for. 16619548b74aSJohannes Doerfert for (unsigned u = 0; u < GTIdArgs.size(); ++u) 16629548b74aSJohannes Doerfert AddUserArgs(*GTIdArgs[u]); 16639548b74aSJohannes Doerfert } 16649548b74aSJohannes Doerfert 16655b0581aeSJohannes Doerfert /// Kernel (=GPU) optimizations and utility functions 16665b0581aeSJohannes Doerfert /// 16675b0581aeSJohannes Doerfert ///{{ 16685b0581aeSJohannes Doerfert 16695b0581aeSJohannes Doerfert /// Check if \p F is a kernel, hence entry point for target offloading. 16705b0581aeSJohannes Doerfert bool isKernel(Function &F) { return OMPInfoCache.Kernels.count(&F); } 16715b0581aeSJohannes Doerfert 16725b0581aeSJohannes Doerfert /// Cache to remember the unique kernel for a function. 16735b0581aeSJohannes Doerfert DenseMap<Function *, Optional<Kernel>> UniqueKernelMap; 16745b0581aeSJohannes Doerfert 16755b0581aeSJohannes Doerfert /// Find the unique kernel that will execute \p F, if any. 16765b0581aeSJohannes Doerfert Kernel getUniqueKernelFor(Function &F); 16775b0581aeSJohannes Doerfert 16785b0581aeSJohannes Doerfert /// Find the unique kernel that will execute \p I, if any. 16795b0581aeSJohannes Doerfert Kernel getUniqueKernelFor(Instruction &I) { 16805b0581aeSJohannes Doerfert return getUniqueKernelFor(*I.getFunction()); 16815b0581aeSJohannes Doerfert } 16825b0581aeSJohannes Doerfert 16835b0581aeSJohannes Doerfert /// Rewrite the device (=GPU) code state machine create in non-SPMD mode in 16845b0581aeSJohannes Doerfert /// the cases we can avoid taking the address of a function. 16855b0581aeSJohannes Doerfert bool rewriteDeviceCodeStateMachine(); 16865b0581aeSJohannes Doerfert 16875b0581aeSJohannes Doerfert /// 16885b0581aeSJohannes Doerfert ///}} 16895b0581aeSJohannes Doerfert 16904d4ea9acSHuber, Joseph /// Emit a remark generically 16914d4ea9acSHuber, Joseph /// 16924d4ea9acSHuber, Joseph /// This template function can be used to generically emit a remark. The 16934d4ea9acSHuber, Joseph /// RemarkKind should be one of the following: 16944d4ea9acSHuber, Joseph /// - OptimizationRemark to indicate a successful optimization attempt 16954d4ea9acSHuber, Joseph /// - OptimizationRemarkMissed to report a failed optimization attempt 16964d4ea9acSHuber, Joseph /// - OptimizationRemarkAnalysis to provide additional information about an 16974d4ea9acSHuber, Joseph /// optimization attempt 16984d4ea9acSHuber, Joseph /// 16994d4ea9acSHuber, Joseph /// The remark is built using a callback function provided by the caller that 17004d4ea9acSHuber, Joseph /// takes a RemarkKind as input and returns a RemarkKind. 17012db182ffSJoseph Huber template <typename RemarkKind, typename RemarkCallBack> 17022db182ffSJoseph Huber void emitRemark(Instruction *I, StringRef RemarkName, 1703e8039ad4SJohannes Doerfert RemarkCallBack &&RemarkCB) const { 17042db182ffSJoseph Huber Function *F = I->getParent()->getParent(); 17054d4ea9acSHuber, Joseph auto &ORE = OREGetter(F); 17064d4ea9acSHuber, Joseph 17072c31d5ebSJoseph Huber if (RemarkName.startswith("OMP")) 17082c31d5ebSJoseph Huber ORE.emit([&]() { 17092c31d5ebSJoseph Huber return RemarkCB(RemarkKind(DEBUG_TYPE, RemarkName, I)) 17102c31d5ebSJoseph Huber << " [" << RemarkName << "]"; 17112c31d5ebSJoseph Huber }); 17122c31d5ebSJoseph Huber else 17132c31d5ebSJoseph Huber ORE.emit( 17142c31d5ebSJoseph Huber [&]() { return RemarkCB(RemarkKind(DEBUG_TYPE, RemarkName, I)); }); 17154d4ea9acSHuber, Joseph } 17164d4ea9acSHuber, Joseph 17172db182ffSJoseph Huber /// Emit a remark on a function. 17182db182ffSJoseph Huber template <typename RemarkKind, typename RemarkCallBack> 17192db182ffSJoseph Huber void emitRemark(Function *F, StringRef RemarkName, 17202db182ffSJoseph Huber RemarkCallBack &&RemarkCB) const { 17210f426935Ssstefan1 auto &ORE = OREGetter(F); 17220f426935Ssstefan1 17232c31d5ebSJoseph Huber if (RemarkName.startswith("OMP")) 17242c31d5ebSJoseph Huber ORE.emit([&]() { 17252c31d5ebSJoseph Huber return RemarkCB(RemarkKind(DEBUG_TYPE, RemarkName, F)) 17262c31d5ebSJoseph Huber << " [" << RemarkName << "]"; 17272c31d5ebSJoseph Huber }); 17282c31d5ebSJoseph Huber else 17292c31d5ebSJoseph Huber ORE.emit( 17302c31d5ebSJoseph Huber [&]() { return RemarkCB(RemarkKind(DEBUG_TYPE, RemarkName, F)); }); 17310f426935Ssstefan1 } 17320f426935Ssstefan1 1733b726c557SJohannes Doerfert /// The underlying module. 17349548b74aSJohannes Doerfert Module &M; 17359548b74aSJohannes Doerfert 17369548b74aSJohannes Doerfert /// The SCC we are operating on. 1737ee17263aSJohannes Doerfert SmallVectorImpl<Function *> &SCC; 17389548b74aSJohannes Doerfert 17399548b74aSJohannes Doerfert /// Callback to update the call graph, the first argument is a removed call, 17409548b74aSJohannes Doerfert /// the second an optional replacement call. 17419548b74aSJohannes Doerfert CallGraphUpdater &CGUpdater; 17429548b74aSJohannes Doerfert 17434d4ea9acSHuber, Joseph /// Callback to get an OptimizationRemarkEmitter from a Function * 17444d4ea9acSHuber, Joseph OptimizationRemarkGetter OREGetter; 17454d4ea9acSHuber, Joseph 17467cfd267cSsstefan1 /// OpenMP-specific information cache. Also Used for Attributor runs. 17477cfd267cSsstefan1 OMPInformationCache &OMPInfoCache; 1748b8235d2bSsstefan1 1749b8235d2bSsstefan1 /// Attributor instance. 1750b8235d2bSsstefan1 Attributor &A; 1751b8235d2bSsstefan1 1752b8235d2bSsstefan1 /// Helper function to run Attributor on SCC. 1753d9659bf6SJohannes Doerfert bool runAttributor(bool IsModulePass) { 1754b8235d2bSsstefan1 if (SCC.empty()) 1755b8235d2bSsstefan1 return false; 1756b8235d2bSsstefan1 1757d9659bf6SJohannes Doerfert registerAAs(IsModulePass); 1758b8235d2bSsstefan1 1759b8235d2bSsstefan1 ChangeStatus Changed = A.run(); 1760b8235d2bSsstefan1 1761b8235d2bSsstefan1 LLVM_DEBUG(dbgs() << "[Attributor] Done with " << SCC.size() 1762b8235d2bSsstefan1 << " functions, result: " << Changed << ".\n"); 1763b8235d2bSsstefan1 1764b8235d2bSsstefan1 return Changed == ChangeStatus::CHANGED; 1765b8235d2bSsstefan1 } 1766b8235d2bSsstefan1 1767b8235d2bSsstefan1 /// Populate the Attributor with abstract attribute opportunities in the 1768b8235d2bSsstefan1 /// function. 1769d9659bf6SJohannes Doerfert void registerAAs(bool IsModulePass); 1770b8235d2bSsstefan1 }; 1771b8235d2bSsstefan1 17725b0581aeSJohannes Doerfert Kernel OpenMPOpt::getUniqueKernelFor(Function &F) { 17735b0581aeSJohannes Doerfert if (!OMPInfoCache.ModuleSlice.count(&F)) 17745b0581aeSJohannes Doerfert return nullptr; 17755b0581aeSJohannes Doerfert 17765b0581aeSJohannes Doerfert // Use a scope to keep the lifetime of the CachedKernel short. 17775b0581aeSJohannes Doerfert { 17785b0581aeSJohannes Doerfert Optional<Kernel> &CachedKernel = UniqueKernelMap[&F]; 17795b0581aeSJohannes Doerfert if (CachedKernel) 17805b0581aeSJohannes Doerfert return *CachedKernel; 17815b0581aeSJohannes Doerfert 17825b0581aeSJohannes Doerfert // TODO: We should use an AA to create an (optimistic and callback 17835b0581aeSJohannes Doerfert // call-aware) call graph. For now we stick to simple patterns that 17845b0581aeSJohannes Doerfert // are less powerful, basically the worst fixpoint. 17855b0581aeSJohannes Doerfert if (isKernel(F)) { 17865b0581aeSJohannes Doerfert CachedKernel = Kernel(&F); 17875b0581aeSJohannes Doerfert return *CachedKernel; 17885b0581aeSJohannes Doerfert } 17895b0581aeSJohannes Doerfert 17905b0581aeSJohannes Doerfert CachedKernel = nullptr; 1791994bb6ebSJohannes Doerfert if (!F.hasLocalLinkage()) { 1792994bb6ebSJohannes Doerfert 1793994bb6ebSJohannes Doerfert // See https://openmp.llvm.org/remarks/OptimizationRemarks.html 17942db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 1795eef6601bSJoseph Huber return ORA << "Potentially unknown OpenMP target region caller."; 1796994bb6ebSJohannes Doerfert }; 17972db182ffSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(&F, "OMP100", Remark); 1798994bb6ebSJohannes Doerfert 17995b0581aeSJohannes Doerfert return nullptr; 18005b0581aeSJohannes Doerfert } 1801994bb6ebSJohannes Doerfert } 18025b0581aeSJohannes Doerfert 18035b0581aeSJohannes Doerfert auto GetUniqueKernelForUse = [&](const Use &U) -> Kernel { 18045b0581aeSJohannes Doerfert if (auto *Cmp = dyn_cast<ICmpInst>(U.getUser())) { 18055b0581aeSJohannes Doerfert // Allow use in equality comparisons. 18065b0581aeSJohannes Doerfert if (Cmp->isEquality()) 18075b0581aeSJohannes Doerfert return getUniqueKernelFor(*Cmp); 18085b0581aeSJohannes Doerfert return nullptr; 18095b0581aeSJohannes Doerfert } 18105b0581aeSJohannes Doerfert if (auto *CB = dyn_cast<CallBase>(U.getUser())) { 18115b0581aeSJohannes Doerfert // Allow direct calls. 18125b0581aeSJohannes Doerfert if (CB->isCallee(&U)) 18135b0581aeSJohannes Doerfert return getUniqueKernelFor(*CB); 1814a2dbfb6bSGiorgis Georgakoudis 1815a2dbfb6bSGiorgis Georgakoudis OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI = 1816a2dbfb6bSGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_51]; 1817a2dbfb6bSGiorgis Georgakoudis // Allow the use in __kmpc_parallel_51 calls. 1818a2dbfb6bSGiorgis Georgakoudis if (OpenMPOpt::getCallIfRegularCall(*U.getUser(), &KernelParallelRFI)) 18195b0581aeSJohannes Doerfert return getUniqueKernelFor(*CB); 18205b0581aeSJohannes Doerfert return nullptr; 18215b0581aeSJohannes Doerfert } 18225b0581aeSJohannes Doerfert // Disallow every other use. 18235b0581aeSJohannes Doerfert return nullptr; 18245b0581aeSJohannes Doerfert }; 18255b0581aeSJohannes Doerfert 18265b0581aeSJohannes Doerfert // TODO: In the future we want to track more than just a unique kernel. 18275b0581aeSJohannes Doerfert SmallPtrSet<Kernel, 2> PotentialKernels; 18288d8ce85bSsstefan1 OMPInformationCache::foreachUse(F, [&](const Use &U) { 18295b0581aeSJohannes Doerfert PotentialKernels.insert(GetUniqueKernelForUse(U)); 18305b0581aeSJohannes Doerfert }); 18315b0581aeSJohannes Doerfert 18325b0581aeSJohannes Doerfert Kernel K = nullptr; 18335b0581aeSJohannes Doerfert if (PotentialKernels.size() == 1) 18345b0581aeSJohannes Doerfert K = *PotentialKernels.begin(); 18355b0581aeSJohannes Doerfert 18365b0581aeSJohannes Doerfert // Cache the result. 18375b0581aeSJohannes Doerfert UniqueKernelMap[&F] = K; 18385b0581aeSJohannes Doerfert 18395b0581aeSJohannes Doerfert return K; 18405b0581aeSJohannes Doerfert } 18415b0581aeSJohannes Doerfert 18425b0581aeSJohannes Doerfert bool OpenMPOpt::rewriteDeviceCodeStateMachine() { 1843a2dbfb6bSGiorgis Georgakoudis OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI = 1844a2dbfb6bSGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_51]; 18455b0581aeSJohannes Doerfert 18465b0581aeSJohannes Doerfert bool Changed = false; 1847a2dbfb6bSGiorgis Georgakoudis if (!KernelParallelRFI) 18485b0581aeSJohannes Doerfert return Changed; 18495b0581aeSJohannes Doerfert 18505b0581aeSJohannes Doerfert for (Function *F : SCC) { 18515b0581aeSJohannes Doerfert 1852a2dbfb6bSGiorgis Georgakoudis // Check if the function is a use in a __kmpc_parallel_51 call at 18535b0581aeSJohannes Doerfert // all. 18545b0581aeSJohannes Doerfert bool UnknownUse = false; 1855a2dbfb6bSGiorgis Georgakoudis bool KernelParallelUse = false; 18565b0581aeSJohannes Doerfert unsigned NumDirectCalls = 0; 18575b0581aeSJohannes Doerfert 18585b0581aeSJohannes Doerfert SmallVector<Use *, 2> ToBeReplacedStateMachineUses; 18598d8ce85bSsstefan1 OMPInformationCache::foreachUse(*F, [&](Use &U) { 18605b0581aeSJohannes Doerfert if (auto *CB = dyn_cast<CallBase>(U.getUser())) 18615b0581aeSJohannes Doerfert if (CB->isCallee(&U)) { 18625b0581aeSJohannes Doerfert ++NumDirectCalls; 18635b0581aeSJohannes Doerfert return; 18645b0581aeSJohannes Doerfert } 18655b0581aeSJohannes Doerfert 186681db6144SMichael Liao if (isa<ICmpInst>(U.getUser())) { 18675b0581aeSJohannes Doerfert ToBeReplacedStateMachineUses.push_back(&U); 18685b0581aeSJohannes Doerfert return; 18695b0581aeSJohannes Doerfert } 1870a2dbfb6bSGiorgis Georgakoudis 1871a2dbfb6bSGiorgis Georgakoudis // Find wrapper functions that represent parallel kernels. 1872a2dbfb6bSGiorgis Georgakoudis CallInst *CI = 1873a2dbfb6bSGiorgis Georgakoudis OpenMPOpt::getCallIfRegularCall(*U.getUser(), &KernelParallelRFI); 1874a2dbfb6bSGiorgis Georgakoudis const unsigned int WrapperFunctionArgNo = 6; 1875a2dbfb6bSGiorgis Georgakoudis if (!KernelParallelUse && CI && 1876a2dbfb6bSGiorgis Georgakoudis CI->getArgOperandNo(&U) == WrapperFunctionArgNo) { 1877a2dbfb6bSGiorgis Georgakoudis KernelParallelUse = true; 18785b0581aeSJohannes Doerfert ToBeReplacedStateMachineUses.push_back(&U); 18795b0581aeSJohannes Doerfert return; 18805b0581aeSJohannes Doerfert } 18815b0581aeSJohannes Doerfert UnknownUse = true; 18825b0581aeSJohannes Doerfert }); 18835b0581aeSJohannes Doerfert 1884a2dbfb6bSGiorgis Georgakoudis // Do not emit a remark if we haven't seen a __kmpc_parallel_51 1885fec1f210SJohannes Doerfert // use. 1886a2dbfb6bSGiorgis Georgakoudis if (!KernelParallelUse) 18875b0581aeSJohannes Doerfert continue; 18885b0581aeSJohannes Doerfert 1889fec1f210SJohannes Doerfert // If this ever hits, we should investigate. 1890fec1f210SJohannes Doerfert // TODO: Checking the number of uses is not a necessary restriction and 1891fec1f210SJohannes Doerfert // should be lifted. 1892fec1f210SJohannes Doerfert if (UnknownUse || NumDirectCalls != 1 || 1893d9659bf6SJohannes Doerfert ToBeReplacedStateMachineUses.size() > 2) { 18942db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 18952db182ffSJoseph Huber return ORA << "Parallel region is used in " 1896fec1f210SJohannes Doerfert << (UnknownUse ? "unknown" : "unexpected") 1897eef6601bSJoseph Huber << " ways. Will not attempt to rewrite the state machine."; 1898fec1f210SJohannes Doerfert }; 18992c31d5ebSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(F, "OMP101", Remark); 19005b0581aeSJohannes Doerfert continue; 1901fec1f210SJohannes Doerfert } 19025b0581aeSJohannes Doerfert 1903a2dbfb6bSGiorgis Georgakoudis // Even if we have __kmpc_parallel_51 calls, we (for now) give 19045b0581aeSJohannes Doerfert // up if the function is not called from a unique kernel. 19055b0581aeSJohannes Doerfert Kernel K = getUniqueKernelFor(*F); 1906fec1f210SJohannes Doerfert if (!K) { 19072db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 1908eef6601bSJoseph Huber return ORA << "Parallel region is not called from a unique kernel. " 1909eef6601bSJoseph Huber "Will not attempt to rewrite the state machine."; 1910fec1f210SJohannes Doerfert }; 19112c31d5ebSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(F, "OMP102", Remark); 19125b0581aeSJohannes Doerfert continue; 1913fec1f210SJohannes Doerfert } 19145b0581aeSJohannes Doerfert 19155b0581aeSJohannes Doerfert // We now know F is a parallel body function called only from the kernel K. 19165b0581aeSJohannes Doerfert // We also identified the state machine uses in which we replace the 19175b0581aeSJohannes Doerfert // function pointer by a new global symbol for identification purposes. This 19185b0581aeSJohannes Doerfert // ensures only direct calls to the function are left. 19195b0581aeSJohannes Doerfert 19205b0581aeSJohannes Doerfert Module &M = *F->getParent(); 19215b0581aeSJohannes Doerfert Type *Int8Ty = Type::getInt8Ty(M.getContext()); 19225b0581aeSJohannes Doerfert 19235b0581aeSJohannes Doerfert auto *ID = new GlobalVariable( 19245b0581aeSJohannes Doerfert M, Int8Ty, /* isConstant */ true, GlobalValue::PrivateLinkage, 19255b0581aeSJohannes Doerfert UndefValue::get(Int8Ty), F->getName() + ".ID"); 19265b0581aeSJohannes Doerfert 19275b0581aeSJohannes Doerfert for (Use *U : ToBeReplacedStateMachineUses) 19285b0581aeSJohannes Doerfert U->set(ConstantExpr::getBitCast(ID, U->get()->getType())); 19295b0581aeSJohannes Doerfert 19305b0581aeSJohannes Doerfert ++NumOpenMPParallelRegionsReplacedInGPUStateMachine; 19315b0581aeSJohannes Doerfert 19325b0581aeSJohannes Doerfert Changed = true; 19335b0581aeSJohannes Doerfert } 19345b0581aeSJohannes Doerfert 19355b0581aeSJohannes Doerfert return Changed; 19365b0581aeSJohannes Doerfert } 19375b0581aeSJohannes Doerfert 1938b8235d2bSsstefan1 /// Abstract Attribute for tracking ICV values. 1939b8235d2bSsstefan1 struct AAICVTracker : public StateWrapper<BooleanState, AbstractAttribute> { 1940b8235d2bSsstefan1 using Base = StateWrapper<BooleanState, AbstractAttribute>; 1941b8235d2bSsstefan1 AAICVTracker(const IRPosition &IRP, Attributor &A) : Base(IRP) {} 1942b8235d2bSsstefan1 19435dfd7cc4Ssstefan1 void initialize(Attributor &A) override { 19445dfd7cc4Ssstefan1 Function *F = getAnchorScope(); 19455dfd7cc4Ssstefan1 if (!F || !A.isFunctionIPOAmendable(*F)) 19465dfd7cc4Ssstefan1 indicatePessimisticFixpoint(); 19475dfd7cc4Ssstefan1 } 19485dfd7cc4Ssstefan1 1949b8235d2bSsstefan1 /// Returns true if value is assumed to be tracked. 1950b8235d2bSsstefan1 bool isAssumedTracked() const { return getAssumed(); } 1951b8235d2bSsstefan1 1952b8235d2bSsstefan1 /// Returns true if value is known to be tracked. 1953b8235d2bSsstefan1 bool isKnownTracked() const { return getAssumed(); } 1954b8235d2bSsstefan1 1955b8235d2bSsstefan1 /// Create an abstract attribute biew for the position \p IRP. 1956b8235d2bSsstefan1 static AAICVTracker &createForPosition(const IRPosition &IRP, Attributor &A); 1957b8235d2bSsstefan1 1958b8235d2bSsstefan1 /// Return the value with which \p I can be replaced for specific \p ICV. 19595dfd7cc4Ssstefan1 virtual Optional<Value *> getReplacementValue(InternalControlVar ICV, 19605dfd7cc4Ssstefan1 const Instruction *I, 19615dfd7cc4Ssstefan1 Attributor &A) const { 19625dfd7cc4Ssstefan1 return None; 19635dfd7cc4Ssstefan1 } 19645dfd7cc4Ssstefan1 19655dfd7cc4Ssstefan1 /// Return an assumed unique ICV value if a single candidate is found. If 19665dfd7cc4Ssstefan1 /// there cannot be one, return a nullptr. If it is not clear yet, return the 19675dfd7cc4Ssstefan1 /// Optional::NoneType. 19685dfd7cc4Ssstefan1 virtual Optional<Value *> 19695dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const = 0; 19705dfd7cc4Ssstefan1 19715dfd7cc4Ssstefan1 // Currently only nthreads is being tracked. 19725dfd7cc4Ssstefan1 // this array will only grow with time. 19735dfd7cc4Ssstefan1 InternalControlVar TrackableICVs[1] = {ICV_nthreads}; 1974b8235d2bSsstefan1 1975b8235d2bSsstefan1 /// See AbstractAttribute::getName() 1976b8235d2bSsstefan1 const std::string getName() const override { return "AAICVTracker"; } 1977b8235d2bSsstefan1 1978233af895SLuofan Chen /// See AbstractAttribute::getIdAddr() 1979233af895SLuofan Chen const char *getIdAddr() const override { return &ID; } 1980233af895SLuofan Chen 1981233af895SLuofan Chen /// This function should return true if the type of the \p AA is AAICVTracker 1982233af895SLuofan Chen static bool classof(const AbstractAttribute *AA) { 1983233af895SLuofan Chen return (AA->getIdAddr() == &ID); 1984233af895SLuofan Chen } 1985233af895SLuofan Chen 1986b8235d2bSsstefan1 static const char ID; 1987b8235d2bSsstefan1 }; 1988b8235d2bSsstefan1 1989b8235d2bSsstefan1 struct AAICVTrackerFunction : public AAICVTracker { 1990b8235d2bSsstefan1 AAICVTrackerFunction(const IRPosition &IRP, Attributor &A) 1991b8235d2bSsstefan1 : AAICVTracker(IRP, A) {} 1992b8235d2bSsstefan1 1993b8235d2bSsstefan1 // FIXME: come up with better string. 19945dfd7cc4Ssstefan1 const std::string getAsStr() const override { return "ICVTrackerFunction"; } 1995b8235d2bSsstefan1 1996b8235d2bSsstefan1 // FIXME: come up with some stats. 1997b8235d2bSsstefan1 void trackStatistics() const override {} 1998b8235d2bSsstefan1 19995dfd7cc4Ssstefan1 /// We don't manifest anything for this AA. 2000b8235d2bSsstefan1 ChangeStatus manifest(Attributor &A) override { 20015dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 2002b8235d2bSsstefan1 } 2003b8235d2bSsstefan1 2004b8235d2bSsstefan1 // Map of ICV to their values at specific program point. 20055dfd7cc4Ssstefan1 EnumeratedArray<DenseMap<Instruction *, Value *>, InternalControlVar, 2006b8235d2bSsstefan1 InternalControlVar::ICV___last> 20075dfd7cc4Ssstefan1 ICVReplacementValuesMap; 2008b8235d2bSsstefan1 2009b8235d2bSsstefan1 ChangeStatus updateImpl(Attributor &A) override { 2010b8235d2bSsstefan1 ChangeStatus HasChanged = ChangeStatus::UNCHANGED; 2011b8235d2bSsstefan1 2012b8235d2bSsstefan1 Function *F = getAnchorScope(); 2013b8235d2bSsstefan1 2014b8235d2bSsstefan1 auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2015b8235d2bSsstefan1 2016b8235d2bSsstefan1 for (InternalControlVar ICV : TrackableICVs) { 2017b8235d2bSsstefan1 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter]; 2018b8235d2bSsstefan1 20195dfd7cc4Ssstefan1 auto &ValuesMap = ICVReplacementValuesMap[ICV]; 2020b8235d2bSsstefan1 auto TrackValues = [&](Use &U, Function &) { 2021b8235d2bSsstefan1 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U); 2022b8235d2bSsstefan1 if (!CI) 2023b8235d2bSsstefan1 return false; 2024b8235d2bSsstefan1 2025b8235d2bSsstefan1 // FIXME: handle setters with more that 1 arguments. 2026b8235d2bSsstefan1 /// Track new value. 20275dfd7cc4Ssstefan1 if (ValuesMap.insert(std::make_pair(CI, CI->getArgOperand(0))).second) 2028b8235d2bSsstefan1 HasChanged = ChangeStatus::CHANGED; 2029b8235d2bSsstefan1 2030b8235d2bSsstefan1 return false; 2031b8235d2bSsstefan1 }; 2032b8235d2bSsstefan1 20335dfd7cc4Ssstefan1 auto CallCheck = [&](Instruction &I) { 20345dfd7cc4Ssstefan1 Optional<Value *> ReplVal = getValueForCall(A, &I, ICV); 20355dfd7cc4Ssstefan1 if (ReplVal.hasValue() && 20365dfd7cc4Ssstefan1 ValuesMap.insert(std::make_pair(&I, *ReplVal)).second) 20375dfd7cc4Ssstefan1 HasChanged = ChangeStatus::CHANGED; 20385dfd7cc4Ssstefan1 20395dfd7cc4Ssstefan1 return true; 20405dfd7cc4Ssstefan1 }; 20415dfd7cc4Ssstefan1 20425dfd7cc4Ssstefan1 // Track all changes of an ICV. 2043b8235d2bSsstefan1 SetterRFI.foreachUse(TrackValues, F); 20445dfd7cc4Ssstefan1 2045792aac98SJohannes Doerfert bool UsedAssumedInformation = false; 20465dfd7cc4Ssstefan1 A.checkForAllInstructions(CallCheck, *this, {Instruction::Call}, 2047792aac98SJohannes Doerfert UsedAssumedInformation, 20485dfd7cc4Ssstefan1 /* CheckBBLivenessOnly */ true); 20495dfd7cc4Ssstefan1 20505dfd7cc4Ssstefan1 /// TODO: Figure out a way to avoid adding entry in 20515dfd7cc4Ssstefan1 /// ICVReplacementValuesMap 20525dfd7cc4Ssstefan1 Instruction *Entry = &F->getEntryBlock().front(); 20535dfd7cc4Ssstefan1 if (HasChanged == ChangeStatus::CHANGED && !ValuesMap.count(Entry)) 20545dfd7cc4Ssstefan1 ValuesMap.insert(std::make_pair(Entry, nullptr)); 2055b8235d2bSsstefan1 } 2056b8235d2bSsstefan1 2057b8235d2bSsstefan1 return HasChanged; 2058b8235d2bSsstefan1 } 2059b8235d2bSsstefan1 20605dfd7cc4Ssstefan1 /// Hepler to check if \p I is a call and get the value for it if it is 20615dfd7cc4Ssstefan1 /// unique. 20625dfd7cc4Ssstefan1 Optional<Value *> getValueForCall(Attributor &A, const Instruction *I, 20635dfd7cc4Ssstefan1 InternalControlVar &ICV) const { 2064b8235d2bSsstefan1 20655dfd7cc4Ssstefan1 const auto *CB = dyn_cast<CallBase>(I); 2066dcaec812SJohannes Doerfert if (!CB || CB->hasFnAttr("no_openmp") || 2067dcaec812SJohannes Doerfert CB->hasFnAttr("no_openmp_routines")) 20685dfd7cc4Ssstefan1 return None; 20695dfd7cc4Ssstefan1 2070b8235d2bSsstefan1 auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2071b8235d2bSsstefan1 auto &GetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Getter]; 20725dfd7cc4Ssstefan1 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter]; 20735dfd7cc4Ssstefan1 Function *CalledFunction = CB->getCalledFunction(); 2074b8235d2bSsstefan1 20754eef14f9SWei Wang // Indirect call, assume ICV changes. 20764eef14f9SWei Wang if (CalledFunction == nullptr) 20774eef14f9SWei Wang return nullptr; 20785dfd7cc4Ssstefan1 if (CalledFunction == GetterRFI.Declaration) 20795dfd7cc4Ssstefan1 return None; 20805dfd7cc4Ssstefan1 if (CalledFunction == SetterRFI.Declaration) { 20815dfd7cc4Ssstefan1 if (ICVReplacementValuesMap[ICV].count(I)) 20825dfd7cc4Ssstefan1 return ICVReplacementValuesMap[ICV].lookup(I); 20835dfd7cc4Ssstefan1 20845dfd7cc4Ssstefan1 return nullptr; 20855dfd7cc4Ssstefan1 } 20865dfd7cc4Ssstefan1 20875dfd7cc4Ssstefan1 // Since we don't know, assume it changes the ICV. 20885dfd7cc4Ssstefan1 if (CalledFunction->isDeclaration()) 20895dfd7cc4Ssstefan1 return nullptr; 20905dfd7cc4Ssstefan1 20915b70c12fSJohannes Doerfert const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 20925b70c12fSJohannes Doerfert *this, IRPosition::callsite_returned(*CB), DepClassTy::REQUIRED); 20935dfd7cc4Ssstefan1 20945dfd7cc4Ssstefan1 if (ICVTrackingAA.isAssumedTracked()) 20955dfd7cc4Ssstefan1 return ICVTrackingAA.getUniqueReplacementValue(ICV); 20965dfd7cc4Ssstefan1 20975dfd7cc4Ssstefan1 // If we don't know, assume it changes. 20985dfd7cc4Ssstefan1 return nullptr; 20995dfd7cc4Ssstefan1 } 21005dfd7cc4Ssstefan1 21015dfd7cc4Ssstefan1 // We don't check unique value for a function, so return None. 21025dfd7cc4Ssstefan1 Optional<Value *> 21035dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 21045dfd7cc4Ssstefan1 return None; 21055dfd7cc4Ssstefan1 } 21065dfd7cc4Ssstefan1 21075dfd7cc4Ssstefan1 /// Return the value with which \p I can be replaced for specific \p ICV. 21085dfd7cc4Ssstefan1 Optional<Value *> getReplacementValue(InternalControlVar ICV, 21095dfd7cc4Ssstefan1 const Instruction *I, 21105dfd7cc4Ssstefan1 Attributor &A) const override { 21115dfd7cc4Ssstefan1 const auto &ValuesMap = ICVReplacementValuesMap[ICV]; 21125dfd7cc4Ssstefan1 if (ValuesMap.count(I)) 21135dfd7cc4Ssstefan1 return ValuesMap.lookup(I); 21145dfd7cc4Ssstefan1 21155dfd7cc4Ssstefan1 SmallVector<const Instruction *, 16> Worklist; 21165dfd7cc4Ssstefan1 SmallPtrSet<const Instruction *, 16> Visited; 21175dfd7cc4Ssstefan1 Worklist.push_back(I); 21185dfd7cc4Ssstefan1 21195dfd7cc4Ssstefan1 Optional<Value *> ReplVal; 21205dfd7cc4Ssstefan1 21215dfd7cc4Ssstefan1 while (!Worklist.empty()) { 21225dfd7cc4Ssstefan1 const Instruction *CurrInst = Worklist.pop_back_val(); 21235dfd7cc4Ssstefan1 if (!Visited.insert(CurrInst).second) 2124b8235d2bSsstefan1 continue; 2125b8235d2bSsstefan1 21265dfd7cc4Ssstefan1 const BasicBlock *CurrBB = CurrInst->getParent(); 21275dfd7cc4Ssstefan1 21285dfd7cc4Ssstefan1 // Go up and look for all potential setters/calls that might change the 21295dfd7cc4Ssstefan1 // ICV. 21305dfd7cc4Ssstefan1 while ((CurrInst = CurrInst->getPrevNode())) { 21315dfd7cc4Ssstefan1 if (ValuesMap.count(CurrInst)) { 21325dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = ValuesMap.lookup(CurrInst); 21335dfd7cc4Ssstefan1 // Unknown value, track new. 21345dfd7cc4Ssstefan1 if (!ReplVal.hasValue()) { 21355dfd7cc4Ssstefan1 ReplVal = NewReplVal; 21365dfd7cc4Ssstefan1 break; 21375dfd7cc4Ssstefan1 } 21385dfd7cc4Ssstefan1 21395dfd7cc4Ssstefan1 // If we found a new value, we can't know the icv value anymore. 21405dfd7cc4Ssstefan1 if (NewReplVal.hasValue()) 21415dfd7cc4Ssstefan1 if (ReplVal != NewReplVal) 2142b8235d2bSsstefan1 return nullptr; 2143b8235d2bSsstefan1 21445dfd7cc4Ssstefan1 break; 2145b8235d2bSsstefan1 } 2146b8235d2bSsstefan1 21475dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = getValueForCall(A, CurrInst, ICV); 21485dfd7cc4Ssstefan1 if (!NewReplVal.hasValue()) 21495dfd7cc4Ssstefan1 continue; 21505dfd7cc4Ssstefan1 21515dfd7cc4Ssstefan1 // Unknown value, track new. 21525dfd7cc4Ssstefan1 if (!ReplVal.hasValue()) { 21535dfd7cc4Ssstefan1 ReplVal = NewReplVal; 21545dfd7cc4Ssstefan1 break; 2155b8235d2bSsstefan1 } 2156b8235d2bSsstefan1 21575dfd7cc4Ssstefan1 // if (NewReplVal.hasValue()) 21585dfd7cc4Ssstefan1 // We found a new value, we can't know the icv value anymore. 21595dfd7cc4Ssstefan1 if (ReplVal != NewReplVal) 2160b8235d2bSsstefan1 return nullptr; 2161b8235d2bSsstefan1 } 21625dfd7cc4Ssstefan1 21635dfd7cc4Ssstefan1 // If we are in the same BB and we have a value, we are done. 21645dfd7cc4Ssstefan1 if (CurrBB == I->getParent() && ReplVal.hasValue()) 21655dfd7cc4Ssstefan1 return ReplVal; 21665dfd7cc4Ssstefan1 21675dfd7cc4Ssstefan1 // Go through all predecessors and add terminators for analysis. 21685dfd7cc4Ssstefan1 for (const BasicBlock *Pred : predecessors(CurrBB)) 21695dfd7cc4Ssstefan1 if (const Instruction *Terminator = Pred->getTerminator()) 21705dfd7cc4Ssstefan1 Worklist.push_back(Terminator); 21715dfd7cc4Ssstefan1 } 21725dfd7cc4Ssstefan1 21735dfd7cc4Ssstefan1 return ReplVal; 21745dfd7cc4Ssstefan1 } 21755dfd7cc4Ssstefan1 }; 21765dfd7cc4Ssstefan1 21775dfd7cc4Ssstefan1 struct AAICVTrackerFunctionReturned : AAICVTracker { 21785dfd7cc4Ssstefan1 AAICVTrackerFunctionReturned(const IRPosition &IRP, Attributor &A) 21795dfd7cc4Ssstefan1 : AAICVTracker(IRP, A) {} 21805dfd7cc4Ssstefan1 21815dfd7cc4Ssstefan1 // FIXME: come up with better string. 21825dfd7cc4Ssstefan1 const std::string getAsStr() const override { 21835dfd7cc4Ssstefan1 return "ICVTrackerFunctionReturned"; 21845dfd7cc4Ssstefan1 } 21855dfd7cc4Ssstefan1 21865dfd7cc4Ssstefan1 // FIXME: come up with some stats. 21875dfd7cc4Ssstefan1 void trackStatistics() const override {} 21885dfd7cc4Ssstefan1 21895dfd7cc4Ssstefan1 /// We don't manifest anything for this AA. 21905dfd7cc4Ssstefan1 ChangeStatus manifest(Attributor &A) override { 21915dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 21925dfd7cc4Ssstefan1 } 21935dfd7cc4Ssstefan1 21945dfd7cc4Ssstefan1 // Map of ICV to their values at specific program point. 21955dfd7cc4Ssstefan1 EnumeratedArray<Optional<Value *>, InternalControlVar, 21965dfd7cc4Ssstefan1 InternalControlVar::ICV___last> 21975dfd7cc4Ssstefan1 ICVReplacementValuesMap; 21985dfd7cc4Ssstefan1 21995dfd7cc4Ssstefan1 /// Return the value with which \p I can be replaced for specific \p ICV. 22005dfd7cc4Ssstefan1 Optional<Value *> 22015dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 22025dfd7cc4Ssstefan1 return ICVReplacementValuesMap[ICV]; 22035dfd7cc4Ssstefan1 } 22045dfd7cc4Ssstefan1 22055dfd7cc4Ssstefan1 ChangeStatus updateImpl(Attributor &A) override { 22065dfd7cc4Ssstefan1 ChangeStatus Changed = ChangeStatus::UNCHANGED; 22075dfd7cc4Ssstefan1 const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 22085b70c12fSJohannes Doerfert *this, IRPosition::function(*getAnchorScope()), DepClassTy::REQUIRED); 22095dfd7cc4Ssstefan1 22105dfd7cc4Ssstefan1 if (!ICVTrackingAA.isAssumedTracked()) 22115dfd7cc4Ssstefan1 return indicatePessimisticFixpoint(); 22125dfd7cc4Ssstefan1 22135dfd7cc4Ssstefan1 for (InternalControlVar ICV : TrackableICVs) { 22145dfd7cc4Ssstefan1 Optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV]; 22155dfd7cc4Ssstefan1 Optional<Value *> UniqueICVValue; 22165dfd7cc4Ssstefan1 22175dfd7cc4Ssstefan1 auto CheckReturnInst = [&](Instruction &I) { 22185dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = 22195dfd7cc4Ssstefan1 ICVTrackingAA.getReplacementValue(ICV, &I, A); 22205dfd7cc4Ssstefan1 22215dfd7cc4Ssstefan1 // If we found a second ICV value there is no unique returned value. 22225dfd7cc4Ssstefan1 if (UniqueICVValue.hasValue() && UniqueICVValue != NewReplVal) 22235dfd7cc4Ssstefan1 return false; 22245dfd7cc4Ssstefan1 22255dfd7cc4Ssstefan1 UniqueICVValue = NewReplVal; 22265dfd7cc4Ssstefan1 22275dfd7cc4Ssstefan1 return true; 22285dfd7cc4Ssstefan1 }; 22295dfd7cc4Ssstefan1 2230792aac98SJohannes Doerfert bool UsedAssumedInformation = false; 22315dfd7cc4Ssstefan1 if (!A.checkForAllInstructions(CheckReturnInst, *this, {Instruction::Ret}, 2232792aac98SJohannes Doerfert UsedAssumedInformation, 22335dfd7cc4Ssstefan1 /* CheckBBLivenessOnly */ true)) 22345dfd7cc4Ssstefan1 UniqueICVValue = nullptr; 22355dfd7cc4Ssstefan1 22365dfd7cc4Ssstefan1 if (UniqueICVValue == ReplVal) 22375dfd7cc4Ssstefan1 continue; 22385dfd7cc4Ssstefan1 22395dfd7cc4Ssstefan1 ReplVal = UniqueICVValue; 22405dfd7cc4Ssstefan1 Changed = ChangeStatus::CHANGED; 22415dfd7cc4Ssstefan1 } 22425dfd7cc4Ssstefan1 22435dfd7cc4Ssstefan1 return Changed; 22445dfd7cc4Ssstefan1 } 22455dfd7cc4Ssstefan1 }; 22465dfd7cc4Ssstefan1 22475dfd7cc4Ssstefan1 struct AAICVTrackerCallSite : AAICVTracker { 22485dfd7cc4Ssstefan1 AAICVTrackerCallSite(const IRPosition &IRP, Attributor &A) 22495dfd7cc4Ssstefan1 : AAICVTracker(IRP, A) {} 22505dfd7cc4Ssstefan1 22515dfd7cc4Ssstefan1 void initialize(Attributor &A) override { 22525dfd7cc4Ssstefan1 Function *F = getAnchorScope(); 22535dfd7cc4Ssstefan1 if (!F || !A.isFunctionIPOAmendable(*F)) 22545dfd7cc4Ssstefan1 indicatePessimisticFixpoint(); 22555dfd7cc4Ssstefan1 22565dfd7cc4Ssstefan1 // We only initialize this AA for getters, so we need to know which ICV it 22575dfd7cc4Ssstefan1 // gets. 22585dfd7cc4Ssstefan1 auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 22595dfd7cc4Ssstefan1 for (InternalControlVar ICV : TrackableICVs) { 22605dfd7cc4Ssstefan1 auto ICVInfo = OMPInfoCache.ICVs[ICV]; 22615dfd7cc4Ssstefan1 auto &Getter = OMPInfoCache.RFIs[ICVInfo.Getter]; 22625dfd7cc4Ssstefan1 if (Getter.Declaration == getAssociatedFunction()) { 22635dfd7cc4Ssstefan1 AssociatedICV = ICVInfo.Kind; 22645dfd7cc4Ssstefan1 return; 22655dfd7cc4Ssstefan1 } 22665dfd7cc4Ssstefan1 } 22675dfd7cc4Ssstefan1 22685dfd7cc4Ssstefan1 /// Unknown ICV. 22695dfd7cc4Ssstefan1 indicatePessimisticFixpoint(); 22705dfd7cc4Ssstefan1 } 22715dfd7cc4Ssstefan1 22725dfd7cc4Ssstefan1 ChangeStatus manifest(Attributor &A) override { 22735dfd7cc4Ssstefan1 if (!ReplVal.hasValue() || !ReplVal.getValue()) 22745dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 22755dfd7cc4Ssstefan1 22765dfd7cc4Ssstefan1 A.changeValueAfterManifest(*getCtxI(), **ReplVal); 22775dfd7cc4Ssstefan1 A.deleteAfterManifest(*getCtxI()); 22785dfd7cc4Ssstefan1 22795dfd7cc4Ssstefan1 return ChangeStatus::CHANGED; 22805dfd7cc4Ssstefan1 } 22815dfd7cc4Ssstefan1 22825dfd7cc4Ssstefan1 // FIXME: come up with better string. 22835dfd7cc4Ssstefan1 const std::string getAsStr() const override { return "ICVTrackerCallSite"; } 22845dfd7cc4Ssstefan1 22855dfd7cc4Ssstefan1 // FIXME: come up with some stats. 22865dfd7cc4Ssstefan1 void trackStatistics() const override {} 22875dfd7cc4Ssstefan1 22885dfd7cc4Ssstefan1 InternalControlVar AssociatedICV; 22895dfd7cc4Ssstefan1 Optional<Value *> ReplVal; 22905dfd7cc4Ssstefan1 22915dfd7cc4Ssstefan1 ChangeStatus updateImpl(Attributor &A) override { 22925dfd7cc4Ssstefan1 const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 22935b70c12fSJohannes Doerfert *this, IRPosition::function(*getAnchorScope()), DepClassTy::REQUIRED); 22945dfd7cc4Ssstefan1 22955dfd7cc4Ssstefan1 // We don't have any information, so we assume it changes the ICV. 22965dfd7cc4Ssstefan1 if (!ICVTrackingAA.isAssumedTracked()) 22975dfd7cc4Ssstefan1 return indicatePessimisticFixpoint(); 22985dfd7cc4Ssstefan1 22995dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = 23005dfd7cc4Ssstefan1 ICVTrackingAA.getReplacementValue(AssociatedICV, getCtxI(), A); 23015dfd7cc4Ssstefan1 23025dfd7cc4Ssstefan1 if (ReplVal == NewReplVal) 23035dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 23045dfd7cc4Ssstefan1 23055dfd7cc4Ssstefan1 ReplVal = NewReplVal; 23065dfd7cc4Ssstefan1 return ChangeStatus::CHANGED; 23075dfd7cc4Ssstefan1 } 23085dfd7cc4Ssstefan1 23095dfd7cc4Ssstefan1 // Return the value with which associated value can be replaced for specific 23105dfd7cc4Ssstefan1 // \p ICV. 23115dfd7cc4Ssstefan1 Optional<Value *> 23125dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 23135dfd7cc4Ssstefan1 return ReplVal; 23145dfd7cc4Ssstefan1 } 23155dfd7cc4Ssstefan1 }; 23165dfd7cc4Ssstefan1 23175dfd7cc4Ssstefan1 struct AAICVTrackerCallSiteReturned : AAICVTracker { 23185dfd7cc4Ssstefan1 AAICVTrackerCallSiteReturned(const IRPosition &IRP, Attributor &A) 23195dfd7cc4Ssstefan1 : AAICVTracker(IRP, A) {} 23205dfd7cc4Ssstefan1 23215dfd7cc4Ssstefan1 // FIXME: come up with better string. 23225dfd7cc4Ssstefan1 const std::string getAsStr() const override { 23235dfd7cc4Ssstefan1 return "ICVTrackerCallSiteReturned"; 23245dfd7cc4Ssstefan1 } 23255dfd7cc4Ssstefan1 23265dfd7cc4Ssstefan1 // FIXME: come up with some stats. 23275dfd7cc4Ssstefan1 void trackStatistics() const override {} 23285dfd7cc4Ssstefan1 23295dfd7cc4Ssstefan1 /// We don't manifest anything for this AA. 23305dfd7cc4Ssstefan1 ChangeStatus manifest(Attributor &A) override { 23315dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 23325dfd7cc4Ssstefan1 } 23335dfd7cc4Ssstefan1 23345dfd7cc4Ssstefan1 // Map of ICV to their values at specific program point. 23355dfd7cc4Ssstefan1 EnumeratedArray<Optional<Value *>, InternalControlVar, 23365dfd7cc4Ssstefan1 InternalControlVar::ICV___last> 23375dfd7cc4Ssstefan1 ICVReplacementValuesMap; 23385dfd7cc4Ssstefan1 23395dfd7cc4Ssstefan1 /// Return the value with which associated value can be replaced for specific 23405dfd7cc4Ssstefan1 /// \p ICV. 23415dfd7cc4Ssstefan1 Optional<Value *> 23425dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 23435dfd7cc4Ssstefan1 return ICVReplacementValuesMap[ICV]; 23445dfd7cc4Ssstefan1 } 23455dfd7cc4Ssstefan1 23465dfd7cc4Ssstefan1 ChangeStatus updateImpl(Attributor &A) override { 23475dfd7cc4Ssstefan1 ChangeStatus Changed = ChangeStatus::UNCHANGED; 23485dfd7cc4Ssstefan1 const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 23495b70c12fSJohannes Doerfert *this, IRPosition::returned(*getAssociatedFunction()), 23505b70c12fSJohannes Doerfert DepClassTy::REQUIRED); 23515dfd7cc4Ssstefan1 23525dfd7cc4Ssstefan1 // We don't have any information, so we assume it changes the ICV. 23535dfd7cc4Ssstefan1 if (!ICVTrackingAA.isAssumedTracked()) 23545dfd7cc4Ssstefan1 return indicatePessimisticFixpoint(); 23555dfd7cc4Ssstefan1 23565dfd7cc4Ssstefan1 for (InternalControlVar ICV : TrackableICVs) { 23575dfd7cc4Ssstefan1 Optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV]; 23585dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = 23595dfd7cc4Ssstefan1 ICVTrackingAA.getUniqueReplacementValue(ICV); 23605dfd7cc4Ssstefan1 23615dfd7cc4Ssstefan1 if (ReplVal == NewReplVal) 23625dfd7cc4Ssstefan1 continue; 23635dfd7cc4Ssstefan1 23645dfd7cc4Ssstefan1 ReplVal = NewReplVal; 23655dfd7cc4Ssstefan1 Changed = ChangeStatus::CHANGED; 23665dfd7cc4Ssstefan1 } 23675dfd7cc4Ssstefan1 return Changed; 23685dfd7cc4Ssstefan1 } 23699548b74aSJohannes Doerfert }; 237018283125SJoseph Huber 237118283125SJoseph Huber struct AAExecutionDomainFunction : public AAExecutionDomain { 237218283125SJoseph Huber AAExecutionDomainFunction(const IRPosition &IRP, Attributor &A) 237318283125SJoseph Huber : AAExecutionDomain(IRP, A) {} 237418283125SJoseph Huber 237518283125SJoseph Huber const std::string getAsStr() const override { 237618283125SJoseph Huber return "[AAExecutionDomain] " + std::to_string(SingleThreadedBBs.size()) + 237718283125SJoseph Huber "/" + std::to_string(NumBBs) + " BBs thread 0 only."; 237818283125SJoseph Huber } 237918283125SJoseph Huber 238018283125SJoseph Huber /// See AbstractAttribute::trackStatistics(). 238118283125SJoseph Huber void trackStatistics() const override {} 238218283125SJoseph Huber 238318283125SJoseph Huber void initialize(Attributor &A) override { 238418283125SJoseph Huber Function *F = getAnchorScope(); 238518283125SJoseph Huber for (const auto &BB : *F) 238618283125SJoseph Huber SingleThreadedBBs.insert(&BB); 238718283125SJoseph Huber NumBBs = SingleThreadedBBs.size(); 238818283125SJoseph Huber } 238918283125SJoseph Huber 239018283125SJoseph Huber ChangeStatus manifest(Attributor &A) override { 239118283125SJoseph Huber LLVM_DEBUG({ 239218283125SJoseph Huber for (const BasicBlock *BB : SingleThreadedBBs) 239318283125SJoseph Huber dbgs() << TAG << " Basic block @" << getAnchorScope()->getName() << " " 239418283125SJoseph Huber << BB->getName() << " is executed by a single thread.\n"; 239518283125SJoseph Huber }); 239618283125SJoseph Huber return ChangeStatus::UNCHANGED; 239718283125SJoseph Huber } 239818283125SJoseph Huber 239918283125SJoseph Huber ChangeStatus updateImpl(Attributor &A) override; 240018283125SJoseph Huber 240118283125SJoseph Huber /// Check if an instruction is executed by a single thread. 24029a23e673SJohannes Doerfert bool isExecutedByInitialThreadOnly(const Instruction &I) const override { 24039a23e673SJohannes Doerfert return isExecutedByInitialThreadOnly(*I.getParent()); 240418283125SJoseph Huber } 240518283125SJoseph Huber 24069a23e673SJohannes Doerfert bool isExecutedByInitialThreadOnly(const BasicBlock &BB) const override { 24071cfdcae6SJoseph Huber return isValidState() && SingleThreadedBBs.contains(&BB); 240818283125SJoseph Huber } 240918283125SJoseph Huber 241018283125SJoseph Huber /// Set of basic blocks that are executed by a single thread. 241118283125SJoseph Huber DenseSet<const BasicBlock *> SingleThreadedBBs; 241218283125SJoseph Huber 241318283125SJoseph Huber /// Total number of basic blocks in this function. 241418283125SJoseph Huber long unsigned NumBBs; 241518283125SJoseph Huber }; 241618283125SJoseph Huber 241718283125SJoseph Huber ChangeStatus AAExecutionDomainFunction::updateImpl(Attributor &A) { 241818283125SJoseph Huber Function *F = getAnchorScope(); 241918283125SJoseph Huber ReversePostOrderTraversal<Function *> RPOT(F); 242018283125SJoseph Huber auto NumSingleThreadedBBs = SingleThreadedBBs.size(); 242118283125SJoseph Huber 242218283125SJoseph Huber bool AllCallSitesKnown; 242318283125SJoseph Huber auto PredForCallSite = [&](AbstractCallSite ACS) { 242418283125SJoseph Huber const auto &ExecutionDomainAA = A.getAAFor<AAExecutionDomain>( 242518283125SJoseph Huber *this, IRPosition::function(*ACS.getInstruction()->getFunction()), 242618283125SJoseph Huber DepClassTy::REQUIRED); 24271cfdcae6SJoseph Huber return ACS.isDirectCall() && 24281cfdcae6SJoseph Huber ExecutionDomainAA.isExecutedByInitialThreadOnly( 24299a23e673SJohannes Doerfert *ACS.getInstruction()); 243018283125SJoseph Huber }; 243118283125SJoseph Huber 243218283125SJoseph Huber if (!A.checkForAllCallSites(PredForCallSite, *this, 243318283125SJoseph Huber /* RequiresAllCallSites */ true, 243418283125SJoseph Huber AllCallSitesKnown)) 243518283125SJoseph Huber SingleThreadedBBs.erase(&F->getEntryBlock()); 243618283125SJoseph Huber 2437e2cfbfccSJohannes Doerfert auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2438e2cfbfccSJohannes Doerfert auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_target_init]; 2439e2cfbfccSJohannes Doerfert 2440e2cfbfccSJohannes Doerfert // Check if the edge into the successor block compares the __kmpc_target_init 2441e2cfbfccSJohannes Doerfert // result with -1. If we are in non-SPMD-mode that signals only the main 2442e2cfbfccSJohannes Doerfert // thread will execute the edge. 24436fc51c9fSJoseph Huber auto IsInitialThreadOnly = [&](BranchInst *Edge, BasicBlock *SuccessorBB) { 244418283125SJoseph Huber if (!Edge || !Edge->isConditional()) 244518283125SJoseph Huber return false; 244618283125SJoseph Huber if (Edge->getSuccessor(0) != SuccessorBB) 244718283125SJoseph Huber return false; 244818283125SJoseph Huber 244918283125SJoseph Huber auto *Cmp = dyn_cast<CmpInst>(Edge->getCondition()); 245018283125SJoseph Huber if (!Cmp || !Cmp->isTrueWhenEqual() || !Cmp->isEquality()) 245118283125SJoseph Huber return false; 245218283125SJoseph Huber 245318283125SJoseph Huber ConstantInt *C = dyn_cast<ConstantInt>(Cmp->getOperand(1)); 2454e2cfbfccSJohannes Doerfert if (!C) 245518283125SJoseph Huber return false; 245618283125SJoseph Huber 2457e2cfbfccSJohannes Doerfert // Match: -1 == __kmpc_target_init (for non-SPMD kernels only!) 2458e2cfbfccSJohannes Doerfert if (C->isAllOnesValue()) { 2459e2cfbfccSJohannes Doerfert auto *CB = dyn_cast<CallBase>(Cmp->getOperand(0)); 2460e2cfbfccSJohannes Doerfert if (!CB || CB->getCalledFunction() != RFI.Declaration) 2461e2cfbfccSJohannes Doerfert return false; 2462e2cfbfccSJohannes Doerfert const int InitIsSPMDArgNo = 1; 2463e2cfbfccSJohannes Doerfert auto *IsSPMDModeCI = 2464e2cfbfccSJohannes Doerfert dyn_cast<ConstantInt>(CB->getOperand(InitIsSPMDArgNo)); 2465e2cfbfccSJohannes Doerfert return IsSPMDModeCI && IsSPMDModeCI->isZero(); 2466e2cfbfccSJohannes Doerfert } 246718283125SJoseph Huber 246818283125SJoseph Huber return false; 246918283125SJoseph Huber }; 247018283125SJoseph Huber 247118283125SJoseph Huber // Merge all the predecessor states into the current basic block. A basic 247218283125SJoseph Huber // block is executed by a single thread if all of its predecessors are. 247318283125SJoseph Huber auto MergePredecessorStates = [&](BasicBlock *BB) { 247418283125SJoseph Huber if (pred_begin(BB) == pred_end(BB)) 247518283125SJoseph Huber return SingleThreadedBBs.contains(BB); 247618283125SJoseph Huber 24776fc51c9fSJoseph Huber bool IsInitialThread = true; 247818283125SJoseph Huber for (auto PredBB = pred_begin(BB), PredEndBB = pred_end(BB); 247918283125SJoseph Huber PredBB != PredEndBB; ++PredBB) { 24806fc51c9fSJoseph Huber if (!IsInitialThreadOnly(dyn_cast<BranchInst>((*PredBB)->getTerminator()), 248118283125SJoseph Huber BB)) 24826fc51c9fSJoseph Huber IsInitialThread &= SingleThreadedBBs.contains(*PredBB); 248318283125SJoseph Huber } 248418283125SJoseph Huber 24856fc51c9fSJoseph Huber return IsInitialThread; 248618283125SJoseph Huber }; 248718283125SJoseph Huber 248818283125SJoseph Huber for (auto *BB : RPOT) { 248918283125SJoseph Huber if (!MergePredecessorStates(BB)) 249018283125SJoseph Huber SingleThreadedBBs.erase(BB); 249118283125SJoseph Huber } 249218283125SJoseph Huber 249318283125SJoseph Huber return (NumSingleThreadedBBs == SingleThreadedBBs.size()) 249418283125SJoseph Huber ? ChangeStatus::UNCHANGED 249518283125SJoseph Huber : ChangeStatus::CHANGED; 249618283125SJoseph Huber } 249718283125SJoseph Huber 24986fc51c9fSJoseph Huber /// Try to replace memory allocation calls called by a single thread with a 24996fc51c9fSJoseph Huber /// static buffer of shared memory. 25006fc51c9fSJoseph Huber struct AAHeapToShared : public StateWrapper<BooleanState, AbstractAttribute> { 25016fc51c9fSJoseph Huber using Base = StateWrapper<BooleanState, AbstractAttribute>; 25026fc51c9fSJoseph Huber AAHeapToShared(const IRPosition &IRP, Attributor &A) : Base(IRP) {} 25036fc51c9fSJoseph Huber 25046fc51c9fSJoseph Huber /// Create an abstract attribute view for the position \p IRP. 25056fc51c9fSJoseph Huber static AAHeapToShared &createForPosition(const IRPosition &IRP, 25066fc51c9fSJoseph Huber Attributor &A); 25076fc51c9fSJoseph Huber 25086fc51c9fSJoseph Huber /// See AbstractAttribute::getName(). 25096fc51c9fSJoseph Huber const std::string getName() const override { return "AAHeapToShared"; } 25106fc51c9fSJoseph Huber 25116fc51c9fSJoseph Huber /// See AbstractAttribute::getIdAddr(). 25126fc51c9fSJoseph Huber const char *getIdAddr() const override { return &ID; } 25136fc51c9fSJoseph Huber 25146fc51c9fSJoseph Huber /// This function should return true if the type of the \p AA is 25156fc51c9fSJoseph Huber /// AAHeapToShared. 25166fc51c9fSJoseph Huber static bool classof(const AbstractAttribute *AA) { 25176fc51c9fSJoseph Huber return (AA->getIdAddr() == &ID); 25186fc51c9fSJoseph Huber } 25196fc51c9fSJoseph Huber 25206fc51c9fSJoseph Huber /// Unique ID (due to the unique address) 25216fc51c9fSJoseph Huber static const char ID; 25226fc51c9fSJoseph Huber }; 25236fc51c9fSJoseph Huber 25246fc51c9fSJoseph Huber struct AAHeapToSharedFunction : public AAHeapToShared { 25256fc51c9fSJoseph Huber AAHeapToSharedFunction(const IRPosition &IRP, Attributor &A) 25266fc51c9fSJoseph Huber : AAHeapToShared(IRP, A) {} 25276fc51c9fSJoseph Huber 25286fc51c9fSJoseph Huber const std::string getAsStr() const override { 25296fc51c9fSJoseph Huber return "[AAHeapToShared] " + std::to_string(MallocCalls.size()) + 25306fc51c9fSJoseph Huber " malloc calls eligible."; 25316fc51c9fSJoseph Huber } 25326fc51c9fSJoseph Huber 25336fc51c9fSJoseph Huber /// See AbstractAttribute::trackStatistics(). 25346fc51c9fSJoseph Huber void trackStatistics() const override {} 25356fc51c9fSJoseph Huber 25366fc51c9fSJoseph Huber void initialize(Attributor &A) override { 25376fc51c9fSJoseph Huber auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 25386fc51c9fSJoseph Huber auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared]; 25396fc51c9fSJoseph Huber 25406fc51c9fSJoseph Huber for (User *U : RFI.Declaration->users()) 25416fc51c9fSJoseph Huber if (CallBase *CB = dyn_cast<CallBase>(U)) 25426fc51c9fSJoseph Huber MallocCalls.insert(CB); 25436fc51c9fSJoseph Huber } 25446fc51c9fSJoseph Huber 25456fc51c9fSJoseph Huber ChangeStatus manifest(Attributor &A) override { 25466fc51c9fSJoseph Huber if (MallocCalls.empty()) 25476fc51c9fSJoseph Huber return ChangeStatus::UNCHANGED; 25486fc51c9fSJoseph Huber 25496fc51c9fSJoseph Huber auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 25506fc51c9fSJoseph Huber auto &FreeCall = OMPInfoCache.RFIs[OMPRTL___kmpc_free_shared]; 25516fc51c9fSJoseph Huber 25526fc51c9fSJoseph Huber Function *F = getAnchorScope(); 25536fc51c9fSJoseph Huber auto *HS = A.lookupAAFor<AAHeapToStack>(IRPosition::function(*F), this, 25546fc51c9fSJoseph Huber DepClassTy::OPTIONAL); 25556fc51c9fSJoseph Huber 25566fc51c9fSJoseph Huber ChangeStatus Changed = ChangeStatus::UNCHANGED; 25576fc51c9fSJoseph Huber for (CallBase *CB : MallocCalls) { 25586fc51c9fSJoseph Huber // Skip replacing this if HeapToStack has already claimed it. 2559c1c1fe93SJohannes Doerfert if (HS && HS->isAssumedHeapToStack(*CB)) 25606fc51c9fSJoseph Huber continue; 25616fc51c9fSJoseph Huber 25626fc51c9fSJoseph Huber // Find the unique free call to remove it. 25636fc51c9fSJoseph Huber SmallVector<CallBase *, 4> FreeCalls; 25646fc51c9fSJoseph Huber for (auto *U : CB->users()) { 25656fc51c9fSJoseph Huber CallBase *C = dyn_cast<CallBase>(U); 25666fc51c9fSJoseph Huber if (C && C->getCalledFunction() == FreeCall.Declaration) 25676fc51c9fSJoseph Huber FreeCalls.push_back(C); 25686fc51c9fSJoseph Huber } 25696fc51c9fSJoseph Huber if (FreeCalls.size() != 1) 25706fc51c9fSJoseph Huber continue; 25716fc51c9fSJoseph Huber 25726fc51c9fSJoseph Huber ConstantInt *AllocSize = dyn_cast<ConstantInt>(CB->getArgOperand(0)); 25736fc51c9fSJoseph Huber 25746fc51c9fSJoseph Huber LLVM_DEBUG(dbgs() << TAG << "Replace globalization call in " 25756fc51c9fSJoseph Huber << CB->getCaller()->getName() << " with " 25766fc51c9fSJoseph Huber << AllocSize->getZExtValue() 25776fc51c9fSJoseph Huber << " bytes of shared memory\n"); 25786fc51c9fSJoseph Huber 25796fc51c9fSJoseph Huber // Create a new shared memory buffer of the same size as the allocation 25806fc51c9fSJoseph Huber // and replace all the uses of the original allocation with it. 25816fc51c9fSJoseph Huber Module *M = CB->getModule(); 25826fc51c9fSJoseph Huber Type *Int8Ty = Type::getInt8Ty(M->getContext()); 25836fc51c9fSJoseph Huber Type *Int8ArrTy = ArrayType::get(Int8Ty, AllocSize->getZExtValue()); 25846fc51c9fSJoseph Huber auto *SharedMem = new GlobalVariable( 25856fc51c9fSJoseph Huber *M, Int8ArrTy, /* IsConstant */ false, GlobalValue::InternalLinkage, 25866fc51c9fSJoseph Huber UndefValue::get(Int8ArrTy), CB->getName(), nullptr, 25876fc51c9fSJoseph Huber GlobalValue::NotThreadLocal, 25886fc51c9fSJoseph Huber static_cast<unsigned>(AddressSpace::Shared)); 25896fc51c9fSJoseph Huber auto *NewBuffer = 25906fc51c9fSJoseph Huber ConstantExpr::getPointerCast(SharedMem, Int8Ty->getPointerTo()); 25916fc51c9fSJoseph Huber 259230e36c9bSJoseph Huber auto Remark = [&](OptimizationRemark OR) { 259330e36c9bSJoseph Huber return OR << "Replaced globalized variable with " 259430e36c9bSJoseph Huber << ore::NV("SharedMemory", AllocSize->getZExtValue()) 259530e36c9bSJoseph Huber << ((AllocSize->getZExtValue() != 1) ? " bytes " : " byte ") 2596eef6601bSJoseph Huber << "of shared memory."; 259730e36c9bSJoseph Huber }; 25982c31d5ebSJoseph Huber A.emitRemark<OptimizationRemark>(CB, "OMP111", Remark); 259930e36c9bSJoseph Huber 26006fc51c9fSJoseph Huber SharedMem->setAlignment(MaybeAlign(32)); 26016fc51c9fSJoseph Huber 26026fc51c9fSJoseph Huber A.changeValueAfterManifest(*CB, *NewBuffer); 26036fc51c9fSJoseph Huber A.deleteAfterManifest(*CB); 26046fc51c9fSJoseph Huber A.deleteAfterManifest(*FreeCalls.front()); 26056fc51c9fSJoseph Huber 26066fc51c9fSJoseph Huber NumBytesMovedToSharedMemory += AllocSize->getZExtValue(); 26076fc51c9fSJoseph Huber Changed = ChangeStatus::CHANGED; 26086fc51c9fSJoseph Huber } 26096fc51c9fSJoseph Huber 26106fc51c9fSJoseph Huber return Changed; 26116fc51c9fSJoseph Huber } 26126fc51c9fSJoseph Huber 26136fc51c9fSJoseph Huber ChangeStatus updateImpl(Attributor &A) override { 26146fc51c9fSJoseph Huber auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 26156fc51c9fSJoseph Huber auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared]; 26166fc51c9fSJoseph Huber Function *F = getAnchorScope(); 26176fc51c9fSJoseph Huber 26186fc51c9fSJoseph Huber auto NumMallocCalls = MallocCalls.size(); 26196fc51c9fSJoseph Huber 26206fc51c9fSJoseph Huber // Only consider malloc calls executed by a single thread with a constant. 26216fc51c9fSJoseph Huber for (User *U : RFI.Declaration->users()) { 26226fc51c9fSJoseph Huber const auto &ED = A.getAAFor<AAExecutionDomain>( 26236fc51c9fSJoseph Huber *this, IRPosition::function(*F), DepClassTy::REQUIRED); 26246fc51c9fSJoseph Huber if (CallBase *CB = dyn_cast<CallBase>(U)) 26256fc51c9fSJoseph Huber if (!dyn_cast<ConstantInt>(CB->getArgOperand(0)) || 26266fc51c9fSJoseph Huber !ED.isExecutedByInitialThreadOnly(*CB)) 26276fc51c9fSJoseph Huber MallocCalls.erase(CB); 26286fc51c9fSJoseph Huber } 26296fc51c9fSJoseph Huber 26306fc51c9fSJoseph Huber if (NumMallocCalls != MallocCalls.size()) 26316fc51c9fSJoseph Huber return ChangeStatus::CHANGED; 26326fc51c9fSJoseph Huber 26336fc51c9fSJoseph Huber return ChangeStatus::UNCHANGED; 26346fc51c9fSJoseph Huber } 26356fc51c9fSJoseph Huber 26366fc51c9fSJoseph Huber /// Collection of all malloc calls in a function. 26376fc51c9fSJoseph Huber SmallPtrSet<CallBase *, 4> MallocCalls; 26386fc51c9fSJoseph Huber }; 26396fc51c9fSJoseph Huber 2640d9659bf6SJohannes Doerfert struct AAKernelInfo : public StateWrapper<KernelInfoState, AbstractAttribute> { 2641d9659bf6SJohannes Doerfert using Base = StateWrapper<KernelInfoState, AbstractAttribute>; 2642d9659bf6SJohannes Doerfert AAKernelInfo(const IRPosition &IRP, Attributor &A) : Base(IRP) {} 2643d9659bf6SJohannes Doerfert 2644d9659bf6SJohannes Doerfert /// Statistics are tracked as part of manifest for now. 2645d9659bf6SJohannes Doerfert void trackStatistics() const override {} 2646d9659bf6SJohannes Doerfert 2647d9659bf6SJohannes Doerfert /// See AbstractAttribute::getAsStr() 2648d9659bf6SJohannes Doerfert const std::string getAsStr() const override { 2649d9659bf6SJohannes Doerfert if (!isValidState()) 2650d9659bf6SJohannes Doerfert return "<invalid>"; 2651514c033dSJohannes Doerfert return std::string(SPMDCompatibilityTracker.isAssumed() ? "SPMD" 2652514c033dSJohannes Doerfert : "generic") + 2653514c033dSJohannes Doerfert std::string(SPMDCompatibilityTracker.isAtFixpoint() ? " [FIX]" 2654514c033dSJohannes Doerfert : "") + 2655d9659bf6SJohannes Doerfert std::string(" #PRs: ") + 2656d9659bf6SJohannes Doerfert std::to_string(ReachedKnownParallelRegions.size()) + 2657d9659bf6SJohannes Doerfert ", #Unknown PRs: " + 2658d9659bf6SJohannes Doerfert std::to_string(ReachedUnknownParallelRegions.size()); 2659d9659bf6SJohannes Doerfert } 2660d9659bf6SJohannes Doerfert 2661d9659bf6SJohannes Doerfert /// Create an abstract attribute biew for the position \p IRP. 2662d9659bf6SJohannes Doerfert static AAKernelInfo &createForPosition(const IRPosition &IRP, Attributor &A); 2663d9659bf6SJohannes Doerfert 2664d9659bf6SJohannes Doerfert /// See AbstractAttribute::getName() 2665d9659bf6SJohannes Doerfert const std::string getName() const override { return "AAKernelInfo"; } 2666d9659bf6SJohannes Doerfert 2667d9659bf6SJohannes Doerfert /// See AbstractAttribute::getIdAddr() 2668d9659bf6SJohannes Doerfert const char *getIdAddr() const override { return &ID; } 2669d9659bf6SJohannes Doerfert 2670d9659bf6SJohannes Doerfert /// This function should return true if the type of the \p AA is AAKernelInfo 2671d9659bf6SJohannes Doerfert static bool classof(const AbstractAttribute *AA) { 2672d9659bf6SJohannes Doerfert return (AA->getIdAddr() == &ID); 2673d9659bf6SJohannes Doerfert } 2674d9659bf6SJohannes Doerfert 2675d9659bf6SJohannes Doerfert static const char ID; 2676d9659bf6SJohannes Doerfert }; 2677d9659bf6SJohannes Doerfert 2678d9659bf6SJohannes Doerfert /// The function kernel info abstract attribute, basically, what can we say 2679d9659bf6SJohannes Doerfert /// about a function with regards to the KernelInfoState. 2680d9659bf6SJohannes Doerfert struct AAKernelInfoFunction : AAKernelInfo { 2681d9659bf6SJohannes Doerfert AAKernelInfoFunction(const IRPosition &IRP, Attributor &A) 2682d9659bf6SJohannes Doerfert : AAKernelInfo(IRP, A) {} 2683d9659bf6SJohannes Doerfert 2684d9659bf6SJohannes Doerfert /// See AbstractAttribute::initialize(...). 2685d9659bf6SJohannes Doerfert void initialize(Attributor &A) override { 2686d9659bf6SJohannes Doerfert // This is a high-level transform that might change the constant arguments 2687d9659bf6SJohannes Doerfert // of the init and dinit calls. We need to tell the Attributor about this 2688d9659bf6SJohannes Doerfert // to avoid other parts using the current constant value for simpliication. 2689d9659bf6SJohannes Doerfert auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2690d9659bf6SJohannes Doerfert 2691d9659bf6SJohannes Doerfert Function *Fn = getAnchorScope(); 2692d9659bf6SJohannes Doerfert if (!OMPInfoCache.Kernels.count(Fn)) 2693d9659bf6SJohannes Doerfert return; 2694d9659bf6SJohannes Doerfert 2695ca662297SShilei Tian // Add itself to the reaching kernel and set IsKernelEntry. 2696ca662297SShilei Tian ReachingKernelEntries.insert(Fn); 2697ca662297SShilei Tian IsKernelEntry = true; 2698ca662297SShilei Tian 2699d9659bf6SJohannes Doerfert OMPInformationCache::RuntimeFunctionInfo &InitRFI = 2700d9659bf6SJohannes Doerfert OMPInfoCache.RFIs[OMPRTL___kmpc_target_init]; 2701d9659bf6SJohannes Doerfert OMPInformationCache::RuntimeFunctionInfo &DeinitRFI = 2702d9659bf6SJohannes Doerfert OMPInfoCache.RFIs[OMPRTL___kmpc_target_deinit]; 2703d9659bf6SJohannes Doerfert 2704d9659bf6SJohannes Doerfert // For kernels we perform more initialization work, first we find the init 2705d9659bf6SJohannes Doerfert // and deinit calls. 2706d9659bf6SJohannes Doerfert auto StoreCallBase = [](Use &U, 2707d9659bf6SJohannes Doerfert OMPInformationCache::RuntimeFunctionInfo &RFI, 2708d9659bf6SJohannes Doerfert CallBase *&Storage) { 2709d9659bf6SJohannes Doerfert CallBase *CB = OpenMPOpt::getCallIfRegularCall(U, &RFI); 2710d9659bf6SJohannes Doerfert assert(CB && 2711d9659bf6SJohannes Doerfert "Unexpected use of __kmpc_target_init or __kmpc_target_deinit!"); 2712d9659bf6SJohannes Doerfert assert(!Storage && 2713d9659bf6SJohannes Doerfert "Multiple uses of __kmpc_target_init or __kmpc_target_deinit!"); 2714d9659bf6SJohannes Doerfert Storage = CB; 2715d9659bf6SJohannes Doerfert return false; 2716d9659bf6SJohannes Doerfert }; 2717d9659bf6SJohannes Doerfert InitRFI.foreachUse( 2718d9659bf6SJohannes Doerfert [&](Use &U, Function &) { 2719d9659bf6SJohannes Doerfert StoreCallBase(U, InitRFI, KernelInitCB); 2720d9659bf6SJohannes Doerfert return false; 2721d9659bf6SJohannes Doerfert }, 2722d9659bf6SJohannes Doerfert Fn); 2723d9659bf6SJohannes Doerfert DeinitRFI.foreachUse( 2724d9659bf6SJohannes Doerfert [&](Use &U, Function &) { 2725d9659bf6SJohannes Doerfert StoreCallBase(U, DeinitRFI, KernelDeinitCB); 2726d9659bf6SJohannes Doerfert return false; 2727d9659bf6SJohannes Doerfert }, 2728d9659bf6SJohannes Doerfert Fn); 2729d9659bf6SJohannes Doerfert 2730d9659bf6SJohannes Doerfert assert((KernelInitCB && KernelDeinitCB) && 2731d9659bf6SJohannes Doerfert "Kernel without __kmpc_target_init or __kmpc_target_deinit!"); 2732d9659bf6SJohannes Doerfert 2733514c033dSJohannes Doerfert // For kernels we might need to initialize/finalize the IsSPMD state and 2734514c033dSJohannes Doerfert // we need to register a simplification callback so that the Attributor 2735514c033dSJohannes Doerfert // knows the constant arguments to __kmpc_target_init and 2736d9659bf6SJohannes Doerfert // __kmpc_target_deinit might actually change. 2737d9659bf6SJohannes Doerfert 2738d9659bf6SJohannes Doerfert Attributor::SimplifictionCallbackTy StateMachineSimplifyCB = 2739d9659bf6SJohannes Doerfert [&](const IRPosition &IRP, const AbstractAttribute *AA, 2740d9659bf6SJohannes Doerfert bool &UsedAssumedInformation) -> Optional<Value *> { 2741d9659bf6SJohannes Doerfert // IRP represents the "use generic state machine" argument of an 2742d9659bf6SJohannes Doerfert // __kmpc_target_init call. We will answer this one with the internal 2743d9659bf6SJohannes Doerfert // state. As long as we are not in an invalid state, we will create a 2744d9659bf6SJohannes Doerfert // custom state machine so the value should be a `i1 false`. If we are 2745d9659bf6SJohannes Doerfert // in an invalid state, we won't change the value that is in the IR. 2746d9659bf6SJohannes Doerfert if (!isValidState()) 2747d9659bf6SJohannes Doerfert return nullptr; 2748d9659bf6SJohannes Doerfert if (AA) 2749d9659bf6SJohannes Doerfert A.recordDependence(*this, *AA, DepClassTy::OPTIONAL); 2750d9659bf6SJohannes Doerfert UsedAssumedInformation = !isAtFixpoint(); 2751d9659bf6SJohannes Doerfert auto *FalseVal = 2752d9659bf6SJohannes Doerfert ConstantInt::getBool(IRP.getAnchorValue().getContext(), 0); 2753d9659bf6SJohannes Doerfert return FalseVal; 2754d9659bf6SJohannes Doerfert }; 2755d9659bf6SJohannes Doerfert 2756514c033dSJohannes Doerfert Attributor::SimplifictionCallbackTy IsSPMDModeSimplifyCB = 2757514c033dSJohannes Doerfert [&](const IRPosition &IRP, const AbstractAttribute *AA, 2758514c033dSJohannes Doerfert bool &UsedAssumedInformation) -> Optional<Value *> { 2759514c033dSJohannes Doerfert // IRP represents the "SPMDCompatibilityTracker" argument of an 2760514c033dSJohannes Doerfert // __kmpc_target_init or 2761514c033dSJohannes Doerfert // __kmpc_target_deinit call. We will answer this one with the internal 2762514c033dSJohannes Doerfert // state. 276397387fdfSJohannes Doerfert if (!SPMDCompatibilityTracker.isValidState()) 2764514c033dSJohannes Doerfert return nullptr; 2765514c033dSJohannes Doerfert if (!SPMDCompatibilityTracker.isAtFixpoint()) { 2766514c033dSJohannes Doerfert if (AA) 2767514c033dSJohannes Doerfert A.recordDependence(*this, *AA, DepClassTy::OPTIONAL); 2768514c033dSJohannes Doerfert UsedAssumedInformation = true; 2769514c033dSJohannes Doerfert } else { 2770514c033dSJohannes Doerfert UsedAssumedInformation = false; 2771514c033dSJohannes Doerfert } 2772514c033dSJohannes Doerfert auto *Val = ConstantInt::getBool(IRP.getAnchorValue().getContext(), 2773514c033dSJohannes Doerfert SPMDCompatibilityTracker.isAssumed()); 2774514c033dSJohannes Doerfert return Val; 2775514c033dSJohannes Doerfert }; 2776514c033dSJohannes Doerfert 2777e8439ec8SGiorgis Georgakoudis Attributor::SimplifictionCallbackTy IsGenericModeSimplifyCB = 2778e8439ec8SGiorgis Georgakoudis [&](const IRPosition &IRP, const AbstractAttribute *AA, 2779e8439ec8SGiorgis Georgakoudis bool &UsedAssumedInformation) -> Optional<Value *> { 2780e8439ec8SGiorgis Georgakoudis // IRP represents the "RequiresFullRuntime" argument of an 2781e8439ec8SGiorgis Georgakoudis // __kmpc_target_init or __kmpc_target_deinit call. We will answer this 2782e8439ec8SGiorgis Georgakoudis // one with the internal state of the SPMDCompatibilityTracker, so if 2783e8439ec8SGiorgis Georgakoudis // generic then true, if SPMD then false. 2784e8439ec8SGiorgis Georgakoudis if (!SPMDCompatibilityTracker.isValidState()) 2785e8439ec8SGiorgis Georgakoudis return nullptr; 2786e8439ec8SGiorgis Georgakoudis if (!SPMDCompatibilityTracker.isAtFixpoint()) { 2787e8439ec8SGiorgis Georgakoudis if (AA) 2788e8439ec8SGiorgis Georgakoudis A.recordDependence(*this, *AA, DepClassTy::OPTIONAL); 2789e8439ec8SGiorgis Georgakoudis UsedAssumedInformation = true; 2790e8439ec8SGiorgis Georgakoudis } else { 2791e8439ec8SGiorgis Georgakoudis UsedAssumedInformation = false; 2792e8439ec8SGiorgis Georgakoudis } 2793e8439ec8SGiorgis Georgakoudis auto *Val = ConstantInt::getBool(IRP.getAnchorValue().getContext(), 2794e8439ec8SGiorgis Georgakoudis !SPMDCompatibilityTracker.isAssumed()); 2795e8439ec8SGiorgis Georgakoudis return Val; 2796e8439ec8SGiorgis Georgakoudis }; 2797e8439ec8SGiorgis Georgakoudis 2798514c033dSJohannes Doerfert constexpr const int InitIsSPMDArgNo = 1; 2799514c033dSJohannes Doerfert constexpr const int DeinitIsSPMDArgNo = 1; 2800d9659bf6SJohannes Doerfert constexpr const int InitUseStateMachineArgNo = 2; 2801e8439ec8SGiorgis Georgakoudis constexpr const int InitRequiresFullRuntimeArgNo = 3; 2802e8439ec8SGiorgis Georgakoudis constexpr const int DeinitRequiresFullRuntimeArgNo = 2; 2803d9659bf6SJohannes Doerfert A.registerSimplificationCallback( 2804d9659bf6SJohannes Doerfert IRPosition::callsite_argument(*KernelInitCB, InitUseStateMachineArgNo), 2805d9659bf6SJohannes Doerfert StateMachineSimplifyCB); 2806514c033dSJohannes Doerfert A.registerSimplificationCallback( 2807514c033dSJohannes Doerfert IRPosition::callsite_argument(*KernelInitCB, InitIsSPMDArgNo), 2808514c033dSJohannes Doerfert IsSPMDModeSimplifyCB); 2809514c033dSJohannes Doerfert A.registerSimplificationCallback( 2810514c033dSJohannes Doerfert IRPosition::callsite_argument(*KernelDeinitCB, DeinitIsSPMDArgNo), 2811514c033dSJohannes Doerfert IsSPMDModeSimplifyCB); 2812e8439ec8SGiorgis Georgakoudis A.registerSimplificationCallback( 2813e8439ec8SGiorgis Georgakoudis IRPosition::callsite_argument(*KernelInitCB, 2814e8439ec8SGiorgis Georgakoudis InitRequiresFullRuntimeArgNo), 2815e8439ec8SGiorgis Georgakoudis IsGenericModeSimplifyCB); 2816e8439ec8SGiorgis Georgakoudis A.registerSimplificationCallback( 2817e8439ec8SGiorgis Georgakoudis IRPosition::callsite_argument(*KernelDeinitCB, 2818e8439ec8SGiorgis Georgakoudis DeinitRequiresFullRuntimeArgNo), 2819e8439ec8SGiorgis Georgakoudis IsGenericModeSimplifyCB); 2820514c033dSJohannes Doerfert 2821514c033dSJohannes Doerfert // Check if we know we are in SPMD-mode already. 2822514c033dSJohannes Doerfert ConstantInt *IsSPMDArg = 2823514c033dSJohannes Doerfert dyn_cast<ConstantInt>(KernelInitCB->getArgOperand(InitIsSPMDArgNo)); 2824514c033dSJohannes Doerfert if (IsSPMDArg && !IsSPMDArg->isZero()) 2825514c033dSJohannes Doerfert SPMDCompatibilityTracker.indicateOptimisticFixpoint(); 2826d9659bf6SJohannes Doerfert } 2827d9659bf6SJohannes Doerfert 2828d9659bf6SJohannes Doerfert /// Modify the IR based on the KernelInfoState as the fixpoint iteration is 2829d9659bf6SJohannes Doerfert /// finished now. 2830d9659bf6SJohannes Doerfert ChangeStatus manifest(Attributor &A) override { 2831d9659bf6SJohannes Doerfert // If we are not looking at a kernel with __kmpc_target_init and 2832d9659bf6SJohannes Doerfert // __kmpc_target_deinit call we cannot actually manifest the information. 2833d9659bf6SJohannes Doerfert if (!KernelInitCB || !KernelDeinitCB) 2834d9659bf6SJohannes Doerfert return ChangeStatus::UNCHANGED; 2835d9659bf6SJohannes Doerfert 2836514c033dSJohannes Doerfert // Known SPMD-mode kernels need no manifest changes. 2837514c033dSJohannes Doerfert if (SPMDCompatibilityTracker.isKnown()) 2838514c033dSJohannes Doerfert return ChangeStatus::UNCHANGED; 2839514c033dSJohannes Doerfert 2840514c033dSJohannes Doerfert // If we can we change the execution mode to SPMD-mode otherwise we build a 2841514c033dSJohannes Doerfert // custom state machine. 2842514c033dSJohannes Doerfert if (!changeToSPMDMode(A)) 2843d9659bf6SJohannes Doerfert buildCustomStateMachine(A); 2844d9659bf6SJohannes Doerfert 2845d9659bf6SJohannes Doerfert return ChangeStatus::CHANGED; 2846d9659bf6SJohannes Doerfert } 2847d9659bf6SJohannes Doerfert 2848514c033dSJohannes Doerfert bool changeToSPMDMode(Attributor &A) { 2849eef6601bSJoseph Huber auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2850eef6601bSJoseph Huber 2851514c033dSJohannes Doerfert if (!SPMDCompatibilityTracker.isAssumed()) { 2852514c033dSJohannes Doerfert for (Instruction *NonCompatibleI : SPMDCompatibilityTracker) { 2853514c033dSJohannes Doerfert if (!NonCompatibleI) 2854514c033dSJohannes Doerfert continue; 2855eef6601bSJoseph Huber 2856eef6601bSJoseph Huber // Skip diagnostics on calls to known OpenMP runtime functions for now. 2857eef6601bSJoseph Huber if (auto *CB = dyn_cast<CallBase>(NonCompatibleI)) 2858eef6601bSJoseph Huber if (OMPInfoCache.RTLFunctions.contains(CB->getCalledFunction())) 2859eef6601bSJoseph Huber continue; 2860eef6601bSJoseph Huber 2861514c033dSJohannes Doerfert auto Remark = [&](OptimizationRemarkAnalysis ORA) { 2862eef6601bSJoseph Huber ORA << "Value has potential side effects preventing SPMD-mode " 2863eef6601bSJoseph Huber "execution"; 2864eef6601bSJoseph Huber if (isa<CallBase>(NonCompatibleI)) { 2865eef6601bSJoseph Huber ORA << ". Add `__attribute__((assume(\"ompx_spmd_amenable\")))` to " 2866eef6601bSJoseph Huber "the called function to override"; 2867514c033dSJohannes Doerfert } 2868514c033dSJohannes Doerfert return ORA << "."; 2869514c033dSJohannes Doerfert }; 28702c31d5ebSJoseph Huber A.emitRemark<OptimizationRemarkAnalysis>(NonCompatibleI, "OMP121", 28712c31d5ebSJoseph Huber Remark); 2872514c033dSJohannes Doerfert 2873514c033dSJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "SPMD-incompatible side-effect: " 2874514c033dSJohannes Doerfert << *NonCompatibleI << "\n"); 2875514c033dSJohannes Doerfert } 2876514c033dSJohannes Doerfert 2877514c033dSJohannes Doerfert return false; 2878514c033dSJohannes Doerfert } 2879514c033dSJohannes Doerfert 2880514c033dSJohannes Doerfert // Adjust the global exec mode flag that tells the runtime what mode this 2881514c033dSJohannes Doerfert // kernel is executed in. 2882514c033dSJohannes Doerfert Function *Kernel = getAnchorScope(); 2883514c033dSJohannes Doerfert GlobalVariable *ExecMode = Kernel->getParent()->getGlobalVariable( 2884514c033dSJohannes Doerfert (Kernel->getName() + "_exec_mode").str()); 2885514c033dSJohannes Doerfert assert(ExecMode && "Kernel without exec mode?"); 2886514c033dSJohannes Doerfert assert(ExecMode->getInitializer() && 2887514c033dSJohannes Doerfert ExecMode->getInitializer()->isOneValue() && 2888514c033dSJohannes Doerfert "Initially non-SPMD kernel has SPMD exec mode!"); 2889*7d576392SJoseph Huber 2890*7d576392SJoseph Huber // Set the global exec mode flag to indicate SPMD-Generic mode. 2891*7d576392SJoseph Huber constexpr int SPMDGeneric = 2; 2892*7d576392SJoseph Huber if (!ExecMode->getInitializer()->isZeroValue()) 2893514c033dSJohannes Doerfert ExecMode->setInitializer( 2894*7d576392SJoseph Huber ConstantInt::get(ExecMode->getInitializer()->getType(), SPMDGeneric)); 2895514c033dSJohannes Doerfert 2896514c033dSJohannes Doerfert // Next rewrite the init and deinit calls to indicate we use SPMD-mode now. 2897514c033dSJohannes Doerfert const int InitIsSPMDArgNo = 1; 2898514c033dSJohannes Doerfert const int DeinitIsSPMDArgNo = 1; 2899514c033dSJohannes Doerfert const int InitUseStateMachineArgNo = 2; 2900e8439ec8SGiorgis Georgakoudis const int InitRequiresFullRuntimeArgNo = 3; 2901e8439ec8SGiorgis Georgakoudis const int DeinitRequiresFullRuntimeArgNo = 2; 2902514c033dSJohannes Doerfert 2903514c033dSJohannes Doerfert auto &Ctx = getAnchorValue().getContext(); 2904514c033dSJohannes Doerfert A.changeUseAfterManifest(KernelInitCB->getArgOperandUse(InitIsSPMDArgNo), 2905514c033dSJohannes Doerfert *ConstantInt::getBool(Ctx, 1)); 2906514c033dSJohannes Doerfert A.changeUseAfterManifest( 2907514c033dSJohannes Doerfert KernelInitCB->getArgOperandUse(InitUseStateMachineArgNo), 2908514c033dSJohannes Doerfert *ConstantInt::getBool(Ctx, 0)); 2909514c033dSJohannes Doerfert A.changeUseAfterManifest( 2910514c033dSJohannes Doerfert KernelDeinitCB->getArgOperandUse(DeinitIsSPMDArgNo), 2911514c033dSJohannes Doerfert *ConstantInt::getBool(Ctx, 1)); 2912e8439ec8SGiorgis Georgakoudis A.changeUseAfterManifest( 2913e8439ec8SGiorgis Georgakoudis KernelInitCB->getArgOperandUse(InitRequiresFullRuntimeArgNo), 2914e8439ec8SGiorgis Georgakoudis *ConstantInt::getBool(Ctx, 0)); 2915e8439ec8SGiorgis Georgakoudis A.changeUseAfterManifest( 2916e8439ec8SGiorgis Georgakoudis KernelDeinitCB->getArgOperandUse(DeinitRequiresFullRuntimeArgNo), 2917e8439ec8SGiorgis Georgakoudis *ConstantInt::getBool(Ctx, 0)); 2918e8439ec8SGiorgis Georgakoudis 2919514c033dSJohannes Doerfert ++NumOpenMPTargetRegionKernelsSPMD; 2920514c033dSJohannes Doerfert 2921514c033dSJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 2922eef6601bSJoseph Huber return OR << "Transformed generic-mode kernel to SPMD-mode."; 2923514c033dSJohannes Doerfert }; 29242c31d5ebSJoseph Huber A.emitRemark<OptimizationRemark>(KernelInitCB, "OMP120", Remark); 2925514c033dSJohannes Doerfert return true; 2926514c033dSJohannes Doerfert }; 2927514c033dSJohannes Doerfert 2928d9659bf6SJohannes Doerfert ChangeStatus buildCustomStateMachine(Attributor &A) { 2929d9659bf6SJohannes Doerfert assert(ReachedKnownParallelRegions.isValidState() && 2930d9659bf6SJohannes Doerfert "Custom state machine with invalid parallel region states?"); 2931d9659bf6SJohannes Doerfert 2932d9659bf6SJohannes Doerfert const int InitIsSPMDArgNo = 1; 2933d9659bf6SJohannes Doerfert const int InitUseStateMachineArgNo = 2; 2934d9659bf6SJohannes Doerfert 2935d9659bf6SJohannes Doerfert // Check if the current configuration is non-SPMD and generic state machine. 2936d9659bf6SJohannes Doerfert // If we already have SPMD mode or a custom state machine we do not need to 2937d9659bf6SJohannes Doerfert // go any further. If it is anything but a constant something is weird and 2938d9659bf6SJohannes Doerfert // we give up. 2939d9659bf6SJohannes Doerfert ConstantInt *UseStateMachine = dyn_cast<ConstantInt>( 2940d9659bf6SJohannes Doerfert KernelInitCB->getArgOperand(InitUseStateMachineArgNo)); 2941d9659bf6SJohannes Doerfert ConstantInt *IsSPMD = 2942d9659bf6SJohannes Doerfert dyn_cast<ConstantInt>(KernelInitCB->getArgOperand(InitIsSPMDArgNo)); 2943d9659bf6SJohannes Doerfert 2944d9659bf6SJohannes Doerfert // If we are stuck with generic mode, try to create a custom device (=GPU) 2945d9659bf6SJohannes Doerfert // state machine which is specialized for the parallel regions that are 2946d9659bf6SJohannes Doerfert // reachable by the kernel. 2947d9659bf6SJohannes Doerfert if (!UseStateMachine || UseStateMachine->isZero() || !IsSPMD || 2948d9659bf6SJohannes Doerfert !IsSPMD->isZero()) 2949d9659bf6SJohannes Doerfert return ChangeStatus::UNCHANGED; 2950d9659bf6SJohannes Doerfert 2951514c033dSJohannes Doerfert // If not SPMD mode, indicate we use a custom state machine now. 2952d9659bf6SJohannes Doerfert auto &Ctx = getAnchorValue().getContext(); 2953d9659bf6SJohannes Doerfert auto *FalseVal = ConstantInt::getBool(Ctx, 0); 2954d9659bf6SJohannes Doerfert A.changeUseAfterManifest( 2955d9659bf6SJohannes Doerfert KernelInitCB->getArgOperandUse(InitUseStateMachineArgNo), *FalseVal); 2956d9659bf6SJohannes Doerfert 2957d9659bf6SJohannes Doerfert // If we don't actually need a state machine we are done here. This can 2958d9659bf6SJohannes Doerfert // happen if there simply are no parallel regions. In the resulting kernel 2959d9659bf6SJohannes Doerfert // all worker threads will simply exit right away, leaving the main thread 2960d9659bf6SJohannes Doerfert // to do the work alone. 2961d9659bf6SJohannes Doerfert if (ReachedKnownParallelRegions.empty() && 2962d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions.empty()) { 2963d9659bf6SJohannes Doerfert ++NumOpenMPTargetRegionKernelsWithoutStateMachine; 2964d9659bf6SJohannes Doerfert 2965d9659bf6SJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 2966eef6601bSJoseph Huber return OR << "Removing unused state machine from generic-mode kernel."; 2967d9659bf6SJohannes Doerfert }; 29682c31d5ebSJoseph Huber A.emitRemark<OptimizationRemark>(KernelInitCB, "OMP130", Remark); 2969d9659bf6SJohannes Doerfert 2970d9659bf6SJohannes Doerfert return ChangeStatus::CHANGED; 2971d9659bf6SJohannes Doerfert } 2972d9659bf6SJohannes Doerfert 2973d9659bf6SJohannes Doerfert // Keep track in the statistics of our new shiny custom state machine. 2974d9659bf6SJohannes Doerfert if (ReachedUnknownParallelRegions.empty()) { 2975d9659bf6SJohannes Doerfert ++NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback; 2976d9659bf6SJohannes Doerfert 2977d9659bf6SJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 2978eef6601bSJoseph Huber return OR << "Rewriting generic-mode kernel with a customized state " 2979eef6601bSJoseph Huber "machine."; 2980d9659bf6SJohannes Doerfert }; 29812c31d5ebSJoseph Huber A.emitRemark<OptimizationRemark>(KernelInitCB, "OMP131", Remark); 2982d9659bf6SJohannes Doerfert } else { 2983d9659bf6SJohannes Doerfert ++NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback; 2984d9659bf6SJohannes Doerfert 2985eef6601bSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis OR) { 2986d9659bf6SJohannes Doerfert return OR << "Generic-mode kernel is executed with a customized state " 2987eef6601bSJoseph Huber "machine that requires a fallback."; 2988d9659bf6SJohannes Doerfert }; 29892c31d5ebSJoseph Huber A.emitRemark<OptimizationRemarkAnalysis>(KernelInitCB, "OMP132", Remark); 2990d9659bf6SJohannes Doerfert 2991d9659bf6SJohannes Doerfert // Tell the user why we ended up with a fallback. 2992d9659bf6SJohannes Doerfert for (CallBase *UnknownParallelRegionCB : ReachedUnknownParallelRegions) { 2993d9659bf6SJohannes Doerfert if (!UnknownParallelRegionCB) 2994d9659bf6SJohannes Doerfert continue; 2995d9659bf6SJohannes Doerfert auto Remark = [&](OptimizationRemarkAnalysis ORA) { 2996eef6601bSJoseph Huber return ORA << "Call may contain unknown parallel regions. Use " 2997eef6601bSJoseph Huber << "`__attribute__((assume(\"omp_no_parallelism\")))` to " 2998eef6601bSJoseph Huber "override."; 2999d9659bf6SJohannes Doerfert }; 30002c31d5ebSJoseph Huber A.emitRemark<OptimizationRemarkAnalysis>(UnknownParallelRegionCB, 30012c31d5ebSJoseph Huber "OMP133", Remark); 3002d9659bf6SJohannes Doerfert } 3003d9659bf6SJohannes Doerfert } 3004d9659bf6SJohannes Doerfert 3005d9659bf6SJohannes Doerfert // Create all the blocks: 3006d9659bf6SJohannes Doerfert // 3007d9659bf6SJohannes Doerfert // InitCB = __kmpc_target_init(...) 3008d9659bf6SJohannes Doerfert // bool IsWorker = InitCB >= 0; 3009d9659bf6SJohannes Doerfert // if (IsWorker) { 3010d9659bf6SJohannes Doerfert // SMBeginBB: __kmpc_barrier_simple_spmd(...); 3011d9659bf6SJohannes Doerfert // void *WorkFn; 3012d9659bf6SJohannes Doerfert // bool Active = __kmpc_kernel_parallel(&WorkFn); 3013d9659bf6SJohannes Doerfert // if (!WorkFn) return; 3014d9659bf6SJohannes Doerfert // SMIsActiveCheckBB: if (Active) { 3015d9659bf6SJohannes Doerfert // SMIfCascadeCurrentBB: if (WorkFn == <ParFn0>) 3016d9659bf6SJohannes Doerfert // ParFn0(...); 3017d9659bf6SJohannes Doerfert // SMIfCascadeCurrentBB: else if (WorkFn == <ParFn1>) 3018d9659bf6SJohannes Doerfert // ParFn1(...); 3019d9659bf6SJohannes Doerfert // ... 3020d9659bf6SJohannes Doerfert // SMIfCascadeCurrentBB: else 3021d9659bf6SJohannes Doerfert // ((WorkFnTy*)WorkFn)(...); 3022d9659bf6SJohannes Doerfert // SMEndParallelBB: __kmpc_kernel_end_parallel(...); 3023d9659bf6SJohannes Doerfert // } 3024d9659bf6SJohannes Doerfert // SMDoneBB: __kmpc_barrier_simple_spmd(...); 3025d9659bf6SJohannes Doerfert // goto SMBeginBB; 3026d9659bf6SJohannes Doerfert // } 3027d9659bf6SJohannes Doerfert // UserCodeEntryBB: // user code 3028d9659bf6SJohannes Doerfert // __kmpc_target_deinit(...) 3029d9659bf6SJohannes Doerfert // 3030d9659bf6SJohannes Doerfert Function *Kernel = getAssociatedFunction(); 3031d9659bf6SJohannes Doerfert assert(Kernel && "Expected an associated function!"); 3032d9659bf6SJohannes Doerfert 3033d9659bf6SJohannes Doerfert BasicBlock *InitBB = KernelInitCB->getParent(); 3034d9659bf6SJohannes Doerfert BasicBlock *UserCodeEntryBB = InitBB->splitBasicBlock( 3035d9659bf6SJohannes Doerfert KernelInitCB->getNextNode(), "thread.user_code.check"); 3036d9659bf6SJohannes Doerfert BasicBlock *StateMachineBeginBB = BasicBlock::Create( 3037d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.begin", Kernel, UserCodeEntryBB); 3038d9659bf6SJohannes Doerfert BasicBlock *StateMachineFinishedBB = BasicBlock::Create( 3039d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.finished", Kernel, UserCodeEntryBB); 3040d9659bf6SJohannes Doerfert BasicBlock *StateMachineIsActiveCheckBB = BasicBlock::Create( 3041d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.is_active.check", Kernel, UserCodeEntryBB); 3042d9659bf6SJohannes Doerfert BasicBlock *StateMachineIfCascadeCurrentBB = 3043d9659bf6SJohannes Doerfert BasicBlock::Create(Ctx, "worker_state_machine.parallel_region.check", 3044d9659bf6SJohannes Doerfert Kernel, UserCodeEntryBB); 3045d9659bf6SJohannes Doerfert BasicBlock *StateMachineEndParallelBB = 3046d9659bf6SJohannes Doerfert BasicBlock::Create(Ctx, "worker_state_machine.parallel_region.end", 3047d9659bf6SJohannes Doerfert Kernel, UserCodeEntryBB); 3048d9659bf6SJohannes Doerfert BasicBlock *StateMachineDoneBarrierBB = BasicBlock::Create( 3049d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.done.barrier", Kernel, UserCodeEntryBB); 30503f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*InitBB); 30513f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*UserCodeEntryBB); 30523f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineBeginBB); 30533f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineFinishedBB); 30543f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineIsActiveCheckBB); 30553f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineIfCascadeCurrentBB); 30563f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineEndParallelBB); 30573f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineDoneBarrierBB); 3058d9659bf6SJohannes Doerfert 3059d9659bf6SJohannes Doerfert const DebugLoc &DLoc = KernelInitCB->getDebugLoc(); 3060d9659bf6SJohannes Doerfert ReturnInst::Create(Ctx, StateMachineFinishedBB)->setDebugLoc(DLoc); 3061d9659bf6SJohannes Doerfert 3062d9659bf6SJohannes Doerfert InitBB->getTerminator()->eraseFromParent(); 3063d9659bf6SJohannes Doerfert Instruction *IsWorker = 3064d9659bf6SJohannes Doerfert ICmpInst::Create(ICmpInst::ICmp, llvm::CmpInst::ICMP_NE, KernelInitCB, 3065d9659bf6SJohannes Doerfert ConstantInt::get(KernelInitCB->getType(), -1), 3066d9659bf6SJohannes Doerfert "thread.is_worker", InitBB); 3067d9659bf6SJohannes Doerfert IsWorker->setDebugLoc(DLoc); 3068d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineBeginBB, UserCodeEntryBB, IsWorker, InitBB); 3069d9659bf6SJohannes Doerfert 3070d9659bf6SJohannes Doerfert // Create local storage for the work function pointer. 3071d9659bf6SJohannes Doerfert Type *VoidPtrTy = Type::getInt8PtrTy(Ctx); 3072d9659bf6SJohannes Doerfert AllocaInst *WorkFnAI = new AllocaInst(VoidPtrTy, 0, "worker.work_fn.addr", 3073d9659bf6SJohannes Doerfert &Kernel->getEntryBlock().front()); 3074d9659bf6SJohannes Doerfert WorkFnAI->setDebugLoc(DLoc); 3075d9659bf6SJohannes Doerfert 3076d9659bf6SJohannes Doerfert auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 3077d9659bf6SJohannes Doerfert OMPInfoCache.OMPBuilder.updateToLocation( 3078d9659bf6SJohannes Doerfert OpenMPIRBuilder::LocationDescription( 3079d9659bf6SJohannes Doerfert IRBuilder<>::InsertPoint(StateMachineBeginBB, 3080d9659bf6SJohannes Doerfert StateMachineBeginBB->end()), 3081d9659bf6SJohannes Doerfert DLoc)); 3082d9659bf6SJohannes Doerfert 3083d9659bf6SJohannes Doerfert Value *Ident = KernelInitCB->getArgOperand(0); 3084d9659bf6SJohannes Doerfert Value *GTid = KernelInitCB; 3085d9659bf6SJohannes Doerfert 3086d9659bf6SJohannes Doerfert Module &M = *Kernel->getParent(); 3087d9659bf6SJohannes Doerfert FunctionCallee BarrierFn = 3088d9659bf6SJohannes Doerfert OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 3089d9659bf6SJohannes Doerfert M, OMPRTL___kmpc_barrier_simple_spmd); 3090d9659bf6SJohannes Doerfert CallInst::Create(BarrierFn, {Ident, GTid}, "", StateMachineBeginBB) 3091d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3092d9659bf6SJohannes Doerfert 3093d9659bf6SJohannes Doerfert FunctionCallee KernelParallelFn = 3094d9659bf6SJohannes Doerfert OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 3095d9659bf6SJohannes Doerfert M, OMPRTL___kmpc_kernel_parallel); 3096d9659bf6SJohannes Doerfert Instruction *IsActiveWorker = CallInst::Create( 3097d9659bf6SJohannes Doerfert KernelParallelFn, {WorkFnAI}, "worker.is_active", StateMachineBeginBB); 3098d9659bf6SJohannes Doerfert IsActiveWorker->setDebugLoc(DLoc); 3099d9659bf6SJohannes Doerfert Instruction *WorkFn = new LoadInst(VoidPtrTy, WorkFnAI, "worker.work_fn", 3100d9659bf6SJohannes Doerfert StateMachineBeginBB); 3101d9659bf6SJohannes Doerfert WorkFn->setDebugLoc(DLoc); 3102d9659bf6SJohannes Doerfert 3103d9659bf6SJohannes Doerfert FunctionType *ParallelRegionFnTy = FunctionType::get( 3104d9659bf6SJohannes Doerfert Type::getVoidTy(Ctx), {Type::getInt16Ty(Ctx), Type::getInt32Ty(Ctx)}, 3105d9659bf6SJohannes Doerfert false); 3106d9659bf6SJohannes Doerfert Value *WorkFnCast = BitCastInst::CreatePointerBitCastOrAddrSpaceCast( 3107d9659bf6SJohannes Doerfert WorkFn, ParallelRegionFnTy->getPointerTo(), "worker.work_fn.addr_cast", 3108d9659bf6SJohannes Doerfert StateMachineBeginBB); 3109d9659bf6SJohannes Doerfert 3110d9659bf6SJohannes Doerfert Instruction *IsDone = 3111d9659bf6SJohannes Doerfert ICmpInst::Create(ICmpInst::ICmp, llvm::CmpInst::ICMP_EQ, WorkFn, 3112d9659bf6SJohannes Doerfert Constant::getNullValue(VoidPtrTy), "worker.is_done", 3113d9659bf6SJohannes Doerfert StateMachineBeginBB); 3114d9659bf6SJohannes Doerfert IsDone->setDebugLoc(DLoc); 3115d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineFinishedBB, StateMachineIsActiveCheckBB, 3116d9659bf6SJohannes Doerfert IsDone, StateMachineBeginBB) 3117d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3118d9659bf6SJohannes Doerfert 3119d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineIfCascadeCurrentBB, 3120d9659bf6SJohannes Doerfert StateMachineDoneBarrierBB, IsActiveWorker, 3121d9659bf6SJohannes Doerfert StateMachineIsActiveCheckBB) 3122d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3123d9659bf6SJohannes Doerfert 3124d9659bf6SJohannes Doerfert Value *ZeroArg = 3125d9659bf6SJohannes Doerfert Constant::getNullValue(ParallelRegionFnTy->getParamType(0)); 3126d9659bf6SJohannes Doerfert 3127d9659bf6SJohannes Doerfert // Now that we have most of the CFG skeleton it is time for the if-cascade 3128d9659bf6SJohannes Doerfert // that checks the function pointer we got from the runtime against the 3129d9659bf6SJohannes Doerfert // parallel regions we expect, if there are any. 3130d9659bf6SJohannes Doerfert for (int i = 0, e = ReachedKnownParallelRegions.size(); i < e; ++i) { 3131d9659bf6SJohannes Doerfert auto *ParallelRegion = ReachedKnownParallelRegions[i]; 3132d9659bf6SJohannes Doerfert BasicBlock *PRExecuteBB = BasicBlock::Create( 3133d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.parallel_region.execute", Kernel, 3134d9659bf6SJohannes Doerfert StateMachineEndParallelBB); 3135d9659bf6SJohannes Doerfert CallInst::Create(ParallelRegion, {ZeroArg, GTid}, "", PRExecuteBB) 3136d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3137d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineEndParallelBB, PRExecuteBB) 3138d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3139d9659bf6SJohannes Doerfert 3140d9659bf6SJohannes Doerfert BasicBlock *PRNextBB = 3141d9659bf6SJohannes Doerfert BasicBlock::Create(Ctx, "worker_state_machine.parallel_region.check", 3142d9659bf6SJohannes Doerfert Kernel, StateMachineEndParallelBB); 3143d9659bf6SJohannes Doerfert 3144d9659bf6SJohannes Doerfert // Check if we need to compare the pointer at all or if we can just 3145d9659bf6SJohannes Doerfert // call the parallel region function. 3146d9659bf6SJohannes Doerfert Value *IsPR; 3147d9659bf6SJohannes Doerfert if (i + 1 < e || !ReachedUnknownParallelRegions.empty()) { 3148d9659bf6SJohannes Doerfert Instruction *CmpI = ICmpInst::Create( 3149d9659bf6SJohannes Doerfert ICmpInst::ICmp, llvm::CmpInst::ICMP_EQ, WorkFnCast, ParallelRegion, 3150d9659bf6SJohannes Doerfert "worker.check_parallel_region", StateMachineIfCascadeCurrentBB); 3151d9659bf6SJohannes Doerfert CmpI->setDebugLoc(DLoc); 3152d9659bf6SJohannes Doerfert IsPR = CmpI; 3153d9659bf6SJohannes Doerfert } else { 3154d9659bf6SJohannes Doerfert IsPR = ConstantInt::getTrue(Ctx); 3155d9659bf6SJohannes Doerfert } 3156d9659bf6SJohannes Doerfert 3157d9659bf6SJohannes Doerfert BranchInst::Create(PRExecuteBB, PRNextBB, IsPR, 3158d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB) 3159d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3160d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB = PRNextBB; 3161d9659bf6SJohannes Doerfert } 3162d9659bf6SJohannes Doerfert 3163d9659bf6SJohannes Doerfert // At the end of the if-cascade we place the indirect function pointer call 3164d9659bf6SJohannes Doerfert // in case we might need it, that is if there can be parallel regions we 3165d9659bf6SJohannes Doerfert // have not handled in the if-cascade above. 3166d9659bf6SJohannes Doerfert if (!ReachedUnknownParallelRegions.empty()) { 3167d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB->setName( 3168d9659bf6SJohannes Doerfert "worker_state_machine.parallel_region.fallback.execute"); 3169d9659bf6SJohannes Doerfert CallInst::Create(ParallelRegionFnTy, WorkFnCast, {ZeroArg, GTid}, "", 3170d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB) 3171d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3172d9659bf6SJohannes Doerfert } 3173d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineEndParallelBB, 3174d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB) 3175d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3176d9659bf6SJohannes Doerfert 3177d9659bf6SJohannes Doerfert CallInst::Create(OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 3178d9659bf6SJohannes Doerfert M, OMPRTL___kmpc_kernel_end_parallel), 3179d9659bf6SJohannes Doerfert {}, "", StateMachineEndParallelBB) 3180d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3181d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineDoneBarrierBB, StateMachineEndParallelBB) 3182d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3183d9659bf6SJohannes Doerfert 3184d9659bf6SJohannes Doerfert CallInst::Create(BarrierFn, {Ident, GTid}, "", StateMachineDoneBarrierBB) 3185d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3186d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineBeginBB, StateMachineDoneBarrierBB) 3187d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3188d9659bf6SJohannes Doerfert 3189d9659bf6SJohannes Doerfert return ChangeStatus::CHANGED; 3190d9659bf6SJohannes Doerfert } 3191d9659bf6SJohannes Doerfert 3192d9659bf6SJohannes Doerfert /// Fixpoint iteration update function. Will be called every time a dependence 3193d9659bf6SJohannes Doerfert /// changed its state (and in the beginning). 3194d9659bf6SJohannes Doerfert ChangeStatus updateImpl(Attributor &A) override { 3195d9659bf6SJohannes Doerfert KernelInfoState StateBefore = getState(); 3196d9659bf6SJohannes Doerfert 3197514c033dSJohannes Doerfert // Callback to check a read/write instruction. 3198514c033dSJohannes Doerfert auto CheckRWInst = [&](Instruction &I) { 3199514c033dSJohannes Doerfert // We handle calls later. 3200514c033dSJohannes Doerfert if (isa<CallBase>(I)) 3201514c033dSJohannes Doerfert return true; 3202514c033dSJohannes Doerfert // We only care about write effects. 3203514c033dSJohannes Doerfert if (!I.mayWriteToMemory()) 3204514c033dSJohannes Doerfert return true; 3205514c033dSJohannes Doerfert if (auto *SI = dyn_cast<StoreInst>(&I)) { 3206514c033dSJohannes Doerfert SmallVector<const Value *> Objects; 3207514c033dSJohannes Doerfert getUnderlyingObjects(SI->getPointerOperand(), Objects); 3208514c033dSJohannes Doerfert if (llvm::all_of(Objects, 3209514c033dSJohannes Doerfert [](const Value *Obj) { return isa<AllocaInst>(Obj); })) 3210514c033dSJohannes Doerfert return true; 3211514c033dSJohannes Doerfert } 3212514c033dSJohannes Doerfert // For now we give up on everything but stores. 3213514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&I); 3214514c033dSJohannes Doerfert return true; 3215514c033dSJohannes Doerfert }; 3216792aac98SJohannes Doerfert 3217792aac98SJohannes Doerfert bool UsedAssumedInformationInCheckRWInst = false; 321897387fdfSJohannes Doerfert if (!SPMDCompatibilityTracker.isAtFixpoint()) 3219792aac98SJohannes Doerfert if (!A.checkForAllReadWriteInstructions( 3220792aac98SJohannes Doerfert CheckRWInst, *this, UsedAssumedInformationInCheckRWInst)) 3221514c033dSJohannes Doerfert SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 3222514c033dSJohannes Doerfert 3223ca662297SShilei Tian if (!IsKernelEntry) 3224ca662297SShilei Tian updateReachingKernelEntries(A); 3225ca662297SShilei Tian 3226d9659bf6SJohannes Doerfert // Callback to check a call instruction. 322797387fdfSJohannes Doerfert bool AllSPMDStatesWereFixed = true; 3228d9659bf6SJohannes Doerfert auto CheckCallInst = [&](Instruction &I) { 3229d9659bf6SJohannes Doerfert auto &CB = cast<CallBase>(I); 3230d9659bf6SJohannes Doerfert auto &CBAA = A.getAAFor<AAKernelInfo>( 3231d9659bf6SJohannes Doerfert *this, IRPosition::callsite_function(CB), DepClassTy::OPTIONAL); 3232d9659bf6SJohannes Doerfert getState() ^= CBAA.getState(); 323397387fdfSJohannes Doerfert AllSPMDStatesWereFixed &= CBAA.SPMDCompatibilityTracker.isAtFixpoint(); 3234d9659bf6SJohannes Doerfert return true; 3235d9659bf6SJohannes Doerfert }; 3236d9659bf6SJohannes Doerfert 3237792aac98SJohannes Doerfert bool UsedAssumedInformationInCheckCallInst = false; 3238792aac98SJohannes Doerfert if (!A.checkForAllCallLikeInstructions( 3239792aac98SJohannes Doerfert CheckCallInst, *this, UsedAssumedInformationInCheckCallInst)) 3240d9659bf6SJohannes Doerfert return indicatePessimisticFixpoint(); 3241d9659bf6SJohannes Doerfert 324297387fdfSJohannes Doerfert // If we haven't used any assumed information for the SPMD state we can fix 324397387fdfSJohannes Doerfert // it. 324497387fdfSJohannes Doerfert if (!UsedAssumedInformationInCheckRWInst && 324597387fdfSJohannes Doerfert !UsedAssumedInformationInCheckCallInst && AllSPMDStatesWereFixed) 324697387fdfSJohannes Doerfert SPMDCompatibilityTracker.indicateOptimisticFixpoint(); 324797387fdfSJohannes Doerfert 3248d9659bf6SJohannes Doerfert return StateBefore == getState() ? ChangeStatus::UNCHANGED 3249d9659bf6SJohannes Doerfert : ChangeStatus::CHANGED; 3250d9659bf6SJohannes Doerfert } 3251ca662297SShilei Tian 3252ca662297SShilei Tian private: 3253ca662297SShilei Tian /// Update info regarding reaching kernels. 3254ca662297SShilei Tian void updateReachingKernelEntries(Attributor &A) { 3255ca662297SShilei Tian auto PredCallSite = [&](AbstractCallSite ACS) { 3256ca662297SShilei Tian Function *Caller = ACS.getInstruction()->getFunction(); 3257ca662297SShilei Tian 3258ca662297SShilei Tian assert(Caller && "Caller is nullptr"); 3259ca662297SShilei Tian 3260d3454ee8SShilei Tian auto &CAA = A.getOrCreateAAFor<AAKernelInfo>( 3261d3454ee8SShilei Tian IRPosition::function(*Caller), this, DepClassTy::REQUIRED); 3262ca662297SShilei Tian if (CAA.ReachingKernelEntries.isValidState()) { 3263ca662297SShilei Tian ReachingKernelEntries ^= CAA.ReachingKernelEntries; 3264ca662297SShilei Tian return true; 3265ca662297SShilei Tian } 3266ca662297SShilei Tian 3267ca662297SShilei Tian // We lost track of the caller of the associated function, any kernel 3268ca662297SShilei Tian // could reach now. 3269ca662297SShilei Tian ReachingKernelEntries.indicatePessimisticFixpoint(); 3270ca662297SShilei Tian 3271ca662297SShilei Tian return true; 3272ca662297SShilei Tian }; 3273ca662297SShilei Tian 3274ca662297SShilei Tian bool AllCallSitesKnown; 3275ca662297SShilei Tian if (!A.checkForAllCallSites(PredCallSite, *this, 3276ca662297SShilei Tian true /* RequireAllCallSites */, 3277ca662297SShilei Tian AllCallSitesKnown)) 3278ca662297SShilei Tian ReachingKernelEntries.indicatePessimisticFixpoint(); 3279ca662297SShilei Tian } 3280d9659bf6SJohannes Doerfert }; 3281d9659bf6SJohannes Doerfert 3282d9659bf6SJohannes Doerfert /// The call site kernel info abstract attribute, basically, what can we say 3283d9659bf6SJohannes Doerfert /// about a call site with regards to the KernelInfoState. For now this simply 3284d9659bf6SJohannes Doerfert /// forwards the information from the callee. 3285d9659bf6SJohannes Doerfert struct AAKernelInfoCallSite : AAKernelInfo { 3286d9659bf6SJohannes Doerfert AAKernelInfoCallSite(const IRPosition &IRP, Attributor &A) 3287d9659bf6SJohannes Doerfert : AAKernelInfo(IRP, A) {} 3288d9659bf6SJohannes Doerfert 3289d9659bf6SJohannes Doerfert /// See AbstractAttribute::initialize(...). 3290d9659bf6SJohannes Doerfert void initialize(Attributor &A) override { 3291d9659bf6SJohannes Doerfert AAKernelInfo::initialize(A); 3292d9659bf6SJohannes Doerfert 3293d9659bf6SJohannes Doerfert CallBase &CB = cast<CallBase>(getAssociatedValue()); 3294d9659bf6SJohannes Doerfert Function *Callee = getAssociatedFunction(); 3295d9659bf6SJohannes Doerfert 3296d9659bf6SJohannes Doerfert // Helper to lookup an assumption string. 3297d9659bf6SJohannes Doerfert auto HasAssumption = [](Function *Fn, StringRef AssumptionStr) { 3298d9659bf6SJohannes Doerfert return Fn && hasAssumption(*Fn, AssumptionStr); 3299d9659bf6SJohannes Doerfert }; 3300d9659bf6SJohannes Doerfert 3301514c033dSJohannes Doerfert // Check for SPMD-mode assumptions. 3302514c033dSJohannes Doerfert if (HasAssumption(Callee, "ompx_spmd_amenable")) 3303514c033dSJohannes Doerfert SPMDCompatibilityTracker.indicateOptimisticFixpoint(); 3304514c033dSJohannes Doerfert 3305d9659bf6SJohannes Doerfert // First weed out calls we do not care about, that is readonly/readnone 3306d9659bf6SJohannes Doerfert // calls, intrinsics, and "no_openmp" calls. Neither of these can reach a 3307d9659bf6SJohannes Doerfert // parallel region or anything else we are looking for. 3308d9659bf6SJohannes Doerfert if (!CB.mayWriteToMemory() || isa<IntrinsicInst>(CB)) { 3309d9659bf6SJohannes Doerfert indicateOptimisticFixpoint(); 3310d9659bf6SJohannes Doerfert return; 3311d9659bf6SJohannes Doerfert } 3312d9659bf6SJohannes Doerfert 3313d9659bf6SJohannes Doerfert // Next we check if we know the callee. If it is a known OpenMP function 3314d9659bf6SJohannes Doerfert // we will handle them explicitly in the switch below. If it is not, we 3315d9659bf6SJohannes Doerfert // will use an AAKernelInfo object on the callee to gather information and 3316d9659bf6SJohannes Doerfert // merge that into the current state. The latter happens in the updateImpl. 3317d9659bf6SJohannes Doerfert auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 3318d9659bf6SJohannes Doerfert const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee); 3319d9659bf6SJohannes Doerfert if (It == OMPInfoCache.RuntimeFunctionIDMap.end()) { 3320d9659bf6SJohannes Doerfert // Unknown caller or declarations are not analyzable, we give up. 3321d9659bf6SJohannes Doerfert if (!Callee || !A.isFunctionIPOAmendable(*Callee)) { 3322d9659bf6SJohannes Doerfert 3323d9659bf6SJohannes Doerfert // Unknown callees might contain parallel regions, except if they have 3324d9659bf6SJohannes Doerfert // an appropriate assumption attached. 3325d9659bf6SJohannes Doerfert if (!(HasAssumption(Callee, "omp_no_openmp") || 3326d9659bf6SJohannes Doerfert HasAssumption(Callee, "omp_no_parallelism"))) 3327d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions.insert(&CB); 3328d9659bf6SJohannes Doerfert 3329514c033dSJohannes Doerfert // If SPMDCompatibilityTracker is not fixed, we need to give up on the 3330514c033dSJohannes Doerfert // idea we can run something unknown in SPMD-mode. 3331514c033dSJohannes Doerfert if (!SPMDCompatibilityTracker.isAtFixpoint()) 3332514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&CB); 3333514c033dSJohannes Doerfert 3334d9659bf6SJohannes Doerfert // We have updated the state for this unknown call properly, there won't 3335d9659bf6SJohannes Doerfert // be any change so we indicate a fixpoint. 3336d9659bf6SJohannes Doerfert indicateOptimisticFixpoint(); 3337d9659bf6SJohannes Doerfert } 3338d9659bf6SJohannes Doerfert // If the callee is known and can be used in IPO, we will update the state 3339d9659bf6SJohannes Doerfert // based on the callee state in updateImpl. 3340d9659bf6SJohannes Doerfert return; 3341d9659bf6SJohannes Doerfert } 3342d9659bf6SJohannes Doerfert 3343d9659bf6SJohannes Doerfert const unsigned int WrapperFunctionArgNo = 6; 3344d9659bf6SJohannes Doerfert RuntimeFunction RF = It->getSecond(); 3345d9659bf6SJohannes Doerfert switch (RF) { 3346514c033dSJohannes Doerfert // All the functions we know are compatible with SPMD mode. 3347514c033dSJohannes Doerfert case OMPRTL___kmpc_is_spmd_exec_mode: 3348514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_fini: 3349514c033dSJohannes Doerfert case OMPRTL___kmpc_global_thread_num: 3350514c033dSJohannes Doerfert case OMPRTL___kmpc_single: 3351514c033dSJohannes Doerfert case OMPRTL___kmpc_end_single: 3352514c033dSJohannes Doerfert case OMPRTL___kmpc_master: 3353514c033dSJohannes Doerfert case OMPRTL___kmpc_end_master: 3354514c033dSJohannes Doerfert case OMPRTL___kmpc_barrier: 3355514c033dSJohannes Doerfert break; 3356514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_init_4: 3357514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_init_4u: 3358514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_init_8: 3359514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_init_8u: { 3360514c033dSJohannes Doerfert // Check the schedule and allow static schedule in SPMD mode. 3361514c033dSJohannes Doerfert unsigned ScheduleArgOpNo = 2; 3362514c033dSJohannes Doerfert auto *ScheduleTypeCI = 3363514c033dSJohannes Doerfert dyn_cast<ConstantInt>(CB.getArgOperand(ScheduleArgOpNo)); 3364514c033dSJohannes Doerfert unsigned ScheduleTypeVal = 3365514c033dSJohannes Doerfert ScheduleTypeCI ? ScheduleTypeCI->getZExtValue() : 0; 3366514c033dSJohannes Doerfert switch (OMPScheduleType(ScheduleTypeVal)) { 3367514c033dSJohannes Doerfert case OMPScheduleType::Static: 3368514c033dSJohannes Doerfert case OMPScheduleType::StaticChunked: 3369514c033dSJohannes Doerfert case OMPScheduleType::Distribute: 3370514c033dSJohannes Doerfert case OMPScheduleType::DistributeChunked: 3371514c033dSJohannes Doerfert break; 3372514c033dSJohannes Doerfert default: 3373514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&CB); 3374514c033dSJohannes Doerfert break; 3375514c033dSJohannes Doerfert }; 3376514c033dSJohannes Doerfert } break; 3377d9659bf6SJohannes Doerfert case OMPRTL___kmpc_target_init: 3378d9659bf6SJohannes Doerfert KernelInitCB = &CB; 3379d9659bf6SJohannes Doerfert break; 3380d9659bf6SJohannes Doerfert case OMPRTL___kmpc_target_deinit: 3381d9659bf6SJohannes Doerfert KernelDeinitCB = &CB; 3382d9659bf6SJohannes Doerfert break; 3383d9659bf6SJohannes Doerfert case OMPRTL___kmpc_parallel_51: 3384d9659bf6SJohannes Doerfert if (auto *ParallelRegion = dyn_cast<Function>( 3385d9659bf6SJohannes Doerfert CB.getArgOperand(WrapperFunctionArgNo)->stripPointerCasts())) { 3386d9659bf6SJohannes Doerfert ReachedKnownParallelRegions.insert(ParallelRegion); 3387d9659bf6SJohannes Doerfert break; 3388d9659bf6SJohannes Doerfert } 3389d9659bf6SJohannes Doerfert // The condition above should usually get the parallel region function 3390d9659bf6SJohannes Doerfert // pointer and record it. In the off chance it doesn't we assume the 3391d9659bf6SJohannes Doerfert // worst. 3392d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions.insert(&CB); 3393d9659bf6SJohannes Doerfert break; 3394d9659bf6SJohannes Doerfert case OMPRTL___kmpc_omp_task: 3395d9659bf6SJohannes Doerfert // We do not look into tasks right now, just give up. 3396514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&CB); 3397d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions.insert(&CB); 3398d9659bf6SJohannes Doerfert break; 3399d9659bf6SJohannes Doerfert default: 3400514c033dSJohannes Doerfert // Unknown OpenMP runtime calls cannot be executed in SPMD-mode, 3401514c033dSJohannes Doerfert // generally. 3402514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&CB); 3403d9659bf6SJohannes Doerfert break; 3404d9659bf6SJohannes Doerfert } 3405d9659bf6SJohannes Doerfert // All other OpenMP runtime calls will not reach parallel regions so they 3406d9659bf6SJohannes Doerfert // can be safely ignored for now. Since it is a known OpenMP runtime call we 3407d9659bf6SJohannes Doerfert // have now modeled all effects and there is no need for any update. 3408d9659bf6SJohannes Doerfert indicateOptimisticFixpoint(); 3409d9659bf6SJohannes Doerfert } 3410d9659bf6SJohannes Doerfert 3411d9659bf6SJohannes Doerfert ChangeStatus updateImpl(Attributor &A) override { 3412d9659bf6SJohannes Doerfert // TODO: Once we have call site specific value information we can provide 3413d9659bf6SJohannes Doerfert // call site specific liveness information and then it makes 3414d9659bf6SJohannes Doerfert // sense to specialize attributes for call sites arguments instead of 3415d9659bf6SJohannes Doerfert // redirecting requests to the callee argument. 3416d9659bf6SJohannes Doerfert Function *F = getAssociatedFunction(); 3417d9659bf6SJohannes Doerfert const IRPosition &FnPos = IRPosition::function(*F); 3418d9659bf6SJohannes Doerfert auto &FnAA = A.getAAFor<AAKernelInfo>(*this, FnPos, DepClassTy::REQUIRED); 3419d9659bf6SJohannes Doerfert if (getState() == FnAA.getState()) 3420d9659bf6SJohannes Doerfert return ChangeStatus::UNCHANGED; 3421d9659bf6SJohannes Doerfert getState() = FnAA.getState(); 3422d9659bf6SJohannes Doerfert return ChangeStatus::CHANGED; 3423d9659bf6SJohannes Doerfert } 3424d9659bf6SJohannes Doerfert }; 3425d9659bf6SJohannes Doerfert 3426ca662297SShilei Tian struct AAFoldRuntimeCall 3427ca662297SShilei Tian : public StateWrapper<BooleanState, AbstractAttribute> { 3428ca662297SShilei Tian using Base = StateWrapper<BooleanState, AbstractAttribute>; 3429ca662297SShilei Tian 3430ca662297SShilei Tian AAFoldRuntimeCall(const IRPosition &IRP, Attributor &A) : Base(IRP) {} 3431ca662297SShilei Tian 3432ca662297SShilei Tian /// Statistics are tracked as part of manifest for now. 3433ca662297SShilei Tian void trackStatistics() const override {} 3434ca662297SShilei Tian 3435ca662297SShilei Tian /// Create an abstract attribute biew for the position \p IRP. 3436ca662297SShilei Tian static AAFoldRuntimeCall &createForPosition(const IRPosition &IRP, 3437ca662297SShilei Tian Attributor &A); 3438ca662297SShilei Tian 3439ca662297SShilei Tian /// See AbstractAttribute::getName() 3440ca662297SShilei Tian const std::string getName() const override { return "AAFoldRuntimeCall"; } 3441ca662297SShilei Tian 3442ca662297SShilei Tian /// See AbstractAttribute::getIdAddr() 3443ca662297SShilei Tian const char *getIdAddr() const override { return &ID; } 3444ca662297SShilei Tian 3445ca662297SShilei Tian /// This function should return true if the type of the \p AA is 3446ca662297SShilei Tian /// AAFoldRuntimeCall 3447ca662297SShilei Tian static bool classof(const AbstractAttribute *AA) { 3448ca662297SShilei Tian return (AA->getIdAddr() == &ID); 3449ca662297SShilei Tian } 3450ca662297SShilei Tian 3451ca662297SShilei Tian static const char ID; 3452ca662297SShilei Tian }; 3453ca662297SShilei Tian 3454ca662297SShilei Tian struct AAFoldRuntimeCallCallSiteReturned : AAFoldRuntimeCall { 3455ca662297SShilei Tian AAFoldRuntimeCallCallSiteReturned(const IRPosition &IRP, Attributor &A) 3456ca662297SShilei Tian : AAFoldRuntimeCall(IRP, A) {} 3457ca662297SShilei Tian 3458ca662297SShilei Tian /// See AbstractAttribute::getAsStr() 3459ca662297SShilei Tian const std::string getAsStr() const override { 3460ca662297SShilei Tian if (!isValidState()) 3461ca662297SShilei Tian return "<invalid>"; 3462ca662297SShilei Tian 3463ca662297SShilei Tian std::string Str("simplified value: "); 3464ca662297SShilei Tian 3465ca662297SShilei Tian if (!SimplifiedValue.hasValue()) 3466ca662297SShilei Tian return Str + std::string("none"); 3467ca662297SShilei Tian 3468ca662297SShilei Tian if (!SimplifiedValue.getValue()) 3469ca662297SShilei Tian return Str + std::string("nullptr"); 3470ca662297SShilei Tian 3471ca662297SShilei Tian if (ConstantInt *CI = dyn_cast<ConstantInt>(SimplifiedValue.getValue())) 3472ca662297SShilei Tian return Str + std::to_string(CI->getSExtValue()); 3473ca662297SShilei Tian 3474ca662297SShilei Tian return Str + std::string("unknown"); 3475ca662297SShilei Tian } 3476ca662297SShilei Tian 3477ca662297SShilei Tian void initialize(Attributor &A) override { 3478ca662297SShilei Tian Function *Callee = getAssociatedFunction(); 3479ca662297SShilei Tian 3480ca662297SShilei Tian auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 3481ca662297SShilei Tian const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee); 3482ca662297SShilei Tian assert(It != OMPInfoCache.RuntimeFunctionIDMap.end() && 3483ca662297SShilei Tian "Expected a known OpenMP runtime function"); 3484ca662297SShilei Tian 3485ca662297SShilei Tian RFKind = It->getSecond(); 3486ca662297SShilei Tian 3487ca662297SShilei Tian CallBase &CB = cast<CallBase>(getAssociatedValue()); 3488ca662297SShilei Tian A.registerSimplificationCallback( 3489ca662297SShilei Tian IRPosition::callsite_returned(CB), 3490ca662297SShilei Tian [&](const IRPosition &IRP, const AbstractAttribute *AA, 3491ca662297SShilei Tian bool &UsedAssumedInformation) -> Optional<Value *> { 3492ca662297SShilei Tian assert((isValidState() || (SimplifiedValue.hasValue() && 3493ca662297SShilei Tian SimplifiedValue.getValue() == nullptr)) && 3494ca662297SShilei Tian "Unexpected invalid state!"); 3495ca662297SShilei Tian 3496ca662297SShilei Tian if (!isAtFixpoint()) { 3497ca662297SShilei Tian UsedAssumedInformation = true; 3498ca662297SShilei Tian if (AA) 3499ca662297SShilei Tian A.recordDependence(*this, *AA, DepClassTy::OPTIONAL); 3500ca662297SShilei Tian } 3501ca662297SShilei Tian return SimplifiedValue; 3502ca662297SShilei Tian }); 3503ca662297SShilei Tian } 3504ca662297SShilei Tian 3505ca662297SShilei Tian ChangeStatus updateImpl(Attributor &A) override { 3506ca662297SShilei Tian ChangeStatus Changed = ChangeStatus::UNCHANGED; 3507ca662297SShilei Tian 3508ca662297SShilei Tian switch (RFKind) { 3509ca662297SShilei Tian case OMPRTL___kmpc_is_spmd_exec_mode: 3510c23da666SShilei Tian Changed |= foldIsSPMDExecMode(A); 3511ca662297SShilei Tian break; 3512ca662297SShilei Tian default: 3513ca662297SShilei Tian llvm_unreachable("Unhandled OpenMP runtime function!"); 3514ca662297SShilei Tian } 3515ca662297SShilei Tian 3516ca662297SShilei Tian return Changed; 3517ca662297SShilei Tian } 3518ca662297SShilei Tian 3519ca662297SShilei Tian ChangeStatus manifest(Attributor &A) override { 3520ca662297SShilei Tian ChangeStatus Changed = ChangeStatus::UNCHANGED; 3521ca662297SShilei Tian 3522ca662297SShilei Tian if (SimplifiedValue.hasValue() && SimplifiedValue.getValue()) { 3523ca662297SShilei Tian Instruction &CB = *getCtxI(); 3524ca662297SShilei Tian A.changeValueAfterManifest(CB, **SimplifiedValue); 3525ca662297SShilei Tian A.deleteAfterManifest(CB); 3526ca662297SShilei Tian Changed = ChangeStatus::CHANGED; 3527ca662297SShilei Tian } 3528ca662297SShilei Tian 3529ca662297SShilei Tian return Changed; 3530ca662297SShilei Tian } 3531ca662297SShilei Tian 3532ca662297SShilei Tian ChangeStatus indicatePessimisticFixpoint() override { 3533ca662297SShilei Tian SimplifiedValue = nullptr; 3534ca662297SShilei Tian return AAFoldRuntimeCall::indicatePessimisticFixpoint(); 3535ca662297SShilei Tian } 3536ca662297SShilei Tian 3537ca662297SShilei Tian private: 3538ca662297SShilei Tian /// Fold __kmpc_is_spmd_exec_mode into a constant if possible. 3539ca662297SShilei Tian ChangeStatus foldIsSPMDExecMode(Attributor &A) { 3540ca662297SShilei Tian Optional<Value *> SimplifiedValueBefore = SimplifiedValue; 3541ca662297SShilei Tian 3542ca662297SShilei Tian unsigned AssumedSPMDCount = 0, KnownSPMDCount = 0; 3543ca662297SShilei Tian unsigned AssumedNonSPMDCount = 0, KnownNonSPMDCount = 0; 3544ca662297SShilei Tian auto &CallerKernelInfoAA = A.getAAFor<AAKernelInfo>( 3545ca662297SShilei Tian *this, IRPosition::function(*getAnchorScope()), DepClassTy::REQUIRED); 3546ca662297SShilei Tian 3547ca662297SShilei Tian if (!CallerKernelInfoAA.ReachingKernelEntries.isValidState()) 3548ca662297SShilei Tian return indicatePessimisticFixpoint(); 3549ca662297SShilei Tian 3550ca662297SShilei Tian for (Kernel K : CallerKernelInfoAA.ReachingKernelEntries) { 3551ca662297SShilei Tian auto &AA = A.getAAFor<AAKernelInfo>(*this, IRPosition::function(*K), 3552ca662297SShilei Tian DepClassTy::REQUIRED); 3553ca662297SShilei Tian 3554ca662297SShilei Tian if (!AA.isValidState()) { 3555ca662297SShilei Tian SimplifiedValue = nullptr; 3556ca662297SShilei Tian return indicatePessimisticFixpoint(); 3557ca662297SShilei Tian } 3558ca662297SShilei Tian 3559ca662297SShilei Tian if (AA.SPMDCompatibilityTracker.isAssumed()) { 3560ca662297SShilei Tian if (AA.SPMDCompatibilityTracker.isAtFixpoint()) 3561ca662297SShilei Tian ++KnownSPMDCount; 3562ca662297SShilei Tian else 3563ca662297SShilei Tian ++AssumedSPMDCount; 3564ca662297SShilei Tian } else { 3565ca662297SShilei Tian if (AA.SPMDCompatibilityTracker.isAtFixpoint()) 3566ca662297SShilei Tian ++KnownNonSPMDCount; 3567ca662297SShilei Tian else 3568ca662297SShilei Tian ++AssumedNonSPMDCount; 3569ca662297SShilei Tian } 3570ca662297SShilei Tian } 3571ca662297SShilei Tian 3572ca662297SShilei Tian if (KnownSPMDCount && KnownNonSPMDCount) 3573ca662297SShilei Tian return indicatePessimisticFixpoint(); 3574ca662297SShilei Tian 3575ca662297SShilei Tian if (AssumedSPMDCount && AssumedNonSPMDCount) 3576ca662297SShilei Tian return indicatePessimisticFixpoint(); 3577ca662297SShilei Tian 3578ca662297SShilei Tian auto &Ctx = getAnchorValue().getContext(); 3579ca662297SShilei Tian if (KnownSPMDCount || AssumedSPMDCount) { 3580ca662297SShilei Tian assert(KnownNonSPMDCount == 0 && AssumedNonSPMDCount == 0 && 3581ca662297SShilei Tian "Expected only SPMD kernels!"); 3582ca662297SShilei Tian // All reaching kernels are in SPMD mode. Update all function calls to 3583ca662297SShilei Tian // __kmpc_is_spmd_exec_mode to 1. 3584ca662297SShilei Tian SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), true); 3585d3454ee8SShilei Tian } else if (KnownNonSPMDCount || AssumedNonSPMDCount) { 3586ca662297SShilei Tian assert(KnownSPMDCount == 0 && AssumedSPMDCount == 0 && 3587ca662297SShilei Tian "Expected only non-SPMD kernels!"); 3588ca662297SShilei Tian // All reaching kernels are in non-SPMD mode. Update all function 3589ca662297SShilei Tian // calls to __kmpc_is_spmd_exec_mode to 0. 3590ca662297SShilei Tian SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), false); 3591d3454ee8SShilei Tian } else { 3592d3454ee8SShilei Tian // We have empty reaching kernels, therefore we cannot tell if the 3593d3454ee8SShilei Tian // associated call site can be folded. At this moment, SimplifiedValue 3594d3454ee8SShilei Tian // must be none. 3595d3454ee8SShilei Tian assert(!SimplifiedValue.hasValue() && "SimplifiedValue should be none"); 3596ca662297SShilei Tian } 3597ca662297SShilei Tian 3598ca662297SShilei Tian return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED 3599ca662297SShilei Tian : ChangeStatus::CHANGED; 3600ca662297SShilei Tian } 3601ca662297SShilei Tian 3602ca662297SShilei Tian /// An optional value the associated value is assumed to fold to. That is, we 3603ca662297SShilei Tian /// assume the associated value (which is a call) can be replaced by this 3604ca662297SShilei Tian /// simplified value. 3605ca662297SShilei Tian Optional<Value *> SimplifiedValue; 3606ca662297SShilei Tian 3607ca662297SShilei Tian /// The runtime function kind of the callee of the associated call site. 3608ca662297SShilei Tian RuntimeFunction RFKind; 3609ca662297SShilei Tian }; 3610ca662297SShilei Tian 36119548b74aSJohannes Doerfert } // namespace 36129548b74aSJohannes Doerfert 3613d9659bf6SJohannes Doerfert void OpenMPOpt::registerAAs(bool IsModulePass) { 3614d9659bf6SJohannes Doerfert if (SCC.empty()) 3615d9659bf6SJohannes Doerfert 3616d9659bf6SJohannes Doerfert return; 3617d9659bf6SJohannes Doerfert if (IsModulePass) { 3618d9659bf6SJohannes Doerfert // Ensure we create the AAKernelInfo AAs first and without triggering an 3619d9659bf6SJohannes Doerfert // update. This will make sure we register all value simplification 3620d9659bf6SJohannes Doerfert // callbacks before any other AA has the chance to create an AAValueSimplify 3621d9659bf6SJohannes Doerfert // or similar. 3622d9659bf6SJohannes Doerfert for (Function *Kernel : OMPInfoCache.Kernels) 3623d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAKernelInfo>( 3624d9659bf6SJohannes Doerfert IRPosition::function(*Kernel), /* QueryingAA */ nullptr, 3625d9659bf6SJohannes Doerfert DepClassTy::NONE, /* ForceUpdate */ false, 3626d9659bf6SJohannes Doerfert /* UpdateAfterInit */ false); 3627ca662297SShilei Tian 3628ca662297SShilei Tian auto &IsSPMDRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_is_spmd_exec_mode]; 3629ca662297SShilei Tian IsSPMDRFI.foreachUse(SCC, [&](Use &U, Function &) { 3630ca662297SShilei Tian CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &IsSPMDRFI); 3631ca662297SShilei Tian if (!CI) 3632ca662297SShilei Tian return false; 3633ca662297SShilei Tian A.getOrCreateAAFor<AAFoldRuntimeCall>( 3634ca662297SShilei Tian IRPosition::callsite_returned(*CI), /* QueryingAA */ nullptr, 3635ca662297SShilei Tian DepClassTy::NONE, /* ForceUpdate */ false, 3636ca662297SShilei Tian /* UpdateAfterInit */ false); 3637ca662297SShilei Tian return false; 3638ca662297SShilei Tian }); 3639d9659bf6SJohannes Doerfert } 3640d9659bf6SJohannes Doerfert 3641d9659bf6SJohannes Doerfert // Create CallSite AA for all Getters. 3642d9659bf6SJohannes Doerfert for (int Idx = 0; Idx < OMPInfoCache.ICVs.size() - 1; ++Idx) { 3643d9659bf6SJohannes Doerfert auto ICVInfo = OMPInfoCache.ICVs[static_cast<InternalControlVar>(Idx)]; 3644d9659bf6SJohannes Doerfert 3645d9659bf6SJohannes Doerfert auto &GetterRFI = OMPInfoCache.RFIs[ICVInfo.Getter]; 3646d9659bf6SJohannes Doerfert 3647d9659bf6SJohannes Doerfert auto CreateAA = [&](Use &U, Function &Caller) { 3648d9659bf6SJohannes Doerfert CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &GetterRFI); 3649d9659bf6SJohannes Doerfert if (!CI) 3650d9659bf6SJohannes Doerfert return false; 3651d9659bf6SJohannes Doerfert 3652d9659bf6SJohannes Doerfert auto &CB = cast<CallBase>(*CI); 3653d9659bf6SJohannes Doerfert 3654d9659bf6SJohannes Doerfert IRPosition CBPos = IRPosition::callsite_function(CB); 3655d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAICVTracker>(CBPos); 3656d9659bf6SJohannes Doerfert return false; 3657d9659bf6SJohannes Doerfert }; 3658d9659bf6SJohannes Doerfert 3659d9659bf6SJohannes Doerfert GetterRFI.foreachUse(SCC, CreateAA); 3660d9659bf6SJohannes Doerfert } 3661d9659bf6SJohannes Doerfert auto &GlobalizationRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared]; 3662d9659bf6SJohannes Doerfert auto CreateAA = [&](Use &U, Function &F) { 3663d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAHeapToShared>(IRPosition::function(F)); 3664d9659bf6SJohannes Doerfert return false; 3665d9659bf6SJohannes Doerfert }; 3666d9659bf6SJohannes Doerfert GlobalizationRFI.foreachUse(SCC, CreateAA); 3667d9659bf6SJohannes Doerfert 3668d9659bf6SJohannes Doerfert // Create an ExecutionDomain AA for every function and a HeapToStack AA for 3669d9659bf6SJohannes Doerfert // every function if there is a device kernel. 3670d9659bf6SJohannes Doerfert for (auto *F : SCC) { 3671d9659bf6SJohannes Doerfert if (!F->isDeclaration()) 3672d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAExecutionDomain>(IRPosition::function(*F)); 3673d9659bf6SJohannes Doerfert if (isOpenMPDevice(M)) 3674d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAHeapToStack>(IRPosition::function(*F)); 3675d9659bf6SJohannes Doerfert } 3676d9659bf6SJohannes Doerfert } 3677d9659bf6SJohannes Doerfert 3678b8235d2bSsstefan1 const char AAICVTracker::ID = 0; 3679d9659bf6SJohannes Doerfert const char AAKernelInfo::ID = 0; 368018283125SJoseph Huber const char AAExecutionDomain::ID = 0; 36816fc51c9fSJoseph Huber const char AAHeapToShared::ID = 0; 3682ca662297SShilei Tian const char AAFoldRuntimeCall::ID = 0; 3683b8235d2bSsstefan1 3684b8235d2bSsstefan1 AAICVTracker &AAICVTracker::createForPosition(const IRPosition &IRP, 3685b8235d2bSsstefan1 Attributor &A) { 3686b8235d2bSsstefan1 AAICVTracker *AA = nullptr; 3687b8235d2bSsstefan1 switch (IRP.getPositionKind()) { 3688b8235d2bSsstefan1 case IRPosition::IRP_INVALID: 3689b8235d2bSsstefan1 case IRPosition::IRP_FLOAT: 3690b8235d2bSsstefan1 case IRPosition::IRP_ARGUMENT: 3691b8235d2bSsstefan1 case IRPosition::IRP_CALL_SITE_ARGUMENT: 36921de70a72SJohannes Doerfert llvm_unreachable("ICVTracker can only be created for function position!"); 36935dfd7cc4Ssstefan1 case IRPosition::IRP_RETURNED: 36945dfd7cc4Ssstefan1 AA = new (A.Allocator) AAICVTrackerFunctionReturned(IRP, A); 36955dfd7cc4Ssstefan1 break; 36965dfd7cc4Ssstefan1 case IRPosition::IRP_CALL_SITE_RETURNED: 36975dfd7cc4Ssstefan1 AA = new (A.Allocator) AAICVTrackerCallSiteReturned(IRP, A); 36985dfd7cc4Ssstefan1 break; 36995dfd7cc4Ssstefan1 case IRPosition::IRP_CALL_SITE: 37005dfd7cc4Ssstefan1 AA = new (A.Allocator) AAICVTrackerCallSite(IRP, A); 37015dfd7cc4Ssstefan1 break; 3702b8235d2bSsstefan1 case IRPosition::IRP_FUNCTION: 3703b8235d2bSsstefan1 AA = new (A.Allocator) AAICVTrackerFunction(IRP, A); 3704b8235d2bSsstefan1 break; 3705b8235d2bSsstefan1 } 3706b8235d2bSsstefan1 3707b8235d2bSsstefan1 return *AA; 3708b8235d2bSsstefan1 } 3709b8235d2bSsstefan1 371018283125SJoseph Huber AAExecutionDomain &AAExecutionDomain::createForPosition(const IRPosition &IRP, 371118283125SJoseph Huber Attributor &A) { 371218283125SJoseph Huber AAExecutionDomainFunction *AA = nullptr; 371318283125SJoseph Huber switch (IRP.getPositionKind()) { 371418283125SJoseph Huber case IRPosition::IRP_INVALID: 371518283125SJoseph Huber case IRPosition::IRP_FLOAT: 371618283125SJoseph Huber case IRPosition::IRP_ARGUMENT: 371718283125SJoseph Huber case IRPosition::IRP_CALL_SITE_ARGUMENT: 371818283125SJoseph Huber case IRPosition::IRP_RETURNED: 371918283125SJoseph Huber case IRPosition::IRP_CALL_SITE_RETURNED: 372018283125SJoseph Huber case IRPosition::IRP_CALL_SITE: 372118283125SJoseph Huber llvm_unreachable( 372218283125SJoseph Huber "AAExecutionDomain can only be created for function position!"); 372318283125SJoseph Huber case IRPosition::IRP_FUNCTION: 372418283125SJoseph Huber AA = new (A.Allocator) AAExecutionDomainFunction(IRP, A); 372518283125SJoseph Huber break; 372618283125SJoseph Huber } 372718283125SJoseph Huber 372818283125SJoseph Huber return *AA; 372918283125SJoseph Huber } 373018283125SJoseph Huber 37316fc51c9fSJoseph Huber AAHeapToShared &AAHeapToShared::createForPosition(const IRPosition &IRP, 37326fc51c9fSJoseph Huber Attributor &A) { 37336fc51c9fSJoseph Huber AAHeapToSharedFunction *AA = nullptr; 37346fc51c9fSJoseph Huber switch (IRP.getPositionKind()) { 37356fc51c9fSJoseph Huber case IRPosition::IRP_INVALID: 37366fc51c9fSJoseph Huber case IRPosition::IRP_FLOAT: 37376fc51c9fSJoseph Huber case IRPosition::IRP_ARGUMENT: 37386fc51c9fSJoseph Huber case IRPosition::IRP_CALL_SITE_ARGUMENT: 37396fc51c9fSJoseph Huber case IRPosition::IRP_RETURNED: 37406fc51c9fSJoseph Huber case IRPosition::IRP_CALL_SITE_RETURNED: 37416fc51c9fSJoseph Huber case IRPosition::IRP_CALL_SITE: 37426fc51c9fSJoseph Huber llvm_unreachable( 37436fc51c9fSJoseph Huber "AAHeapToShared can only be created for function position!"); 37446fc51c9fSJoseph Huber case IRPosition::IRP_FUNCTION: 37456fc51c9fSJoseph Huber AA = new (A.Allocator) AAHeapToSharedFunction(IRP, A); 37466fc51c9fSJoseph Huber break; 37476fc51c9fSJoseph Huber } 37486fc51c9fSJoseph Huber 37496fc51c9fSJoseph Huber return *AA; 37506fc51c9fSJoseph Huber } 37516fc51c9fSJoseph Huber 3752d9659bf6SJohannes Doerfert AAKernelInfo &AAKernelInfo::createForPosition(const IRPosition &IRP, 3753d9659bf6SJohannes Doerfert Attributor &A) { 3754d9659bf6SJohannes Doerfert AAKernelInfo *AA = nullptr; 3755d9659bf6SJohannes Doerfert switch (IRP.getPositionKind()) { 3756d9659bf6SJohannes Doerfert case IRPosition::IRP_INVALID: 3757d9659bf6SJohannes Doerfert case IRPosition::IRP_FLOAT: 3758d9659bf6SJohannes Doerfert case IRPosition::IRP_ARGUMENT: 3759d9659bf6SJohannes Doerfert case IRPosition::IRP_RETURNED: 3760d9659bf6SJohannes Doerfert case IRPosition::IRP_CALL_SITE_RETURNED: 3761d9659bf6SJohannes Doerfert case IRPosition::IRP_CALL_SITE_ARGUMENT: 3762d9659bf6SJohannes Doerfert llvm_unreachable("KernelInfo can only be created for function position!"); 3763d9659bf6SJohannes Doerfert case IRPosition::IRP_CALL_SITE: 3764d9659bf6SJohannes Doerfert AA = new (A.Allocator) AAKernelInfoCallSite(IRP, A); 3765d9659bf6SJohannes Doerfert break; 3766d9659bf6SJohannes Doerfert case IRPosition::IRP_FUNCTION: 3767d9659bf6SJohannes Doerfert AA = new (A.Allocator) AAKernelInfoFunction(IRP, A); 3768d9659bf6SJohannes Doerfert break; 3769d9659bf6SJohannes Doerfert } 3770d9659bf6SJohannes Doerfert 3771d9659bf6SJohannes Doerfert return *AA; 3772d9659bf6SJohannes Doerfert } 3773d9659bf6SJohannes Doerfert 3774ca662297SShilei Tian AAFoldRuntimeCall &AAFoldRuntimeCall::createForPosition(const IRPosition &IRP, 3775ca662297SShilei Tian Attributor &A) { 3776ca662297SShilei Tian AAFoldRuntimeCall *AA = nullptr; 3777ca662297SShilei Tian switch (IRP.getPositionKind()) { 3778ca662297SShilei Tian case IRPosition::IRP_INVALID: 3779ca662297SShilei Tian case IRPosition::IRP_FLOAT: 3780ca662297SShilei Tian case IRPosition::IRP_ARGUMENT: 3781ca662297SShilei Tian case IRPosition::IRP_RETURNED: 3782ca662297SShilei Tian case IRPosition::IRP_FUNCTION: 3783ca662297SShilei Tian case IRPosition::IRP_CALL_SITE: 3784ca662297SShilei Tian case IRPosition::IRP_CALL_SITE_ARGUMENT: 3785ca662297SShilei Tian llvm_unreachable("KernelInfo can only be created for call site position!"); 3786ca662297SShilei Tian case IRPosition::IRP_CALL_SITE_RETURNED: 3787ca662297SShilei Tian AA = new (A.Allocator) AAFoldRuntimeCallCallSiteReturned(IRP, A); 3788ca662297SShilei Tian break; 3789ca662297SShilei Tian } 3790ca662297SShilei Tian 3791ca662297SShilei Tian return *AA; 3792ca662297SShilei Tian } 3793ca662297SShilei Tian 3794b2ad63d3SJoseph Huber PreservedAnalyses OpenMPOptPass::run(Module &M, ModuleAnalysisManager &AM) { 37955ccb7424SJoseph Huber if (!containsOpenMP(M)) 3796b2ad63d3SJoseph Huber return PreservedAnalyses::all(); 3797b2ad63d3SJoseph Huber if (DisableOpenMPOptimizations) 3798b2ad63d3SJoseph Huber return PreservedAnalyses::all(); 3799b2ad63d3SJoseph Huber 38000edb8777SJoseph Huber FunctionAnalysisManager &FAM = 38010edb8777SJoseph Huber AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); 38025ccb7424SJoseph Huber KernelSet Kernels = getDeviceKernels(M); 38035ccb7424SJoseph Huber 380457ad2e10SJoseph Huber auto IsCalled = [&](Function &F) { 380557ad2e10SJoseph Huber if (Kernels.contains(&F)) 380657ad2e10SJoseph Huber return true; 380757ad2e10SJoseph Huber for (const User *U : F.users()) 380857ad2e10SJoseph Huber if (!isa<BlockAddress>(U)) 380957ad2e10SJoseph Huber return true; 381057ad2e10SJoseph Huber return false; 381157ad2e10SJoseph Huber }; 381257ad2e10SJoseph Huber 38130edb8777SJoseph Huber auto EmitRemark = [&](Function &F) { 38140edb8777SJoseph Huber auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F); 38150edb8777SJoseph Huber ORE.emit([&]() { 38162c31d5ebSJoseph Huber OptimizationRemarkAnalysis ORA(DEBUG_TYPE, "OMP140", &F); 3817ecabc668SJoseph Huber return ORA << "Could not internalize function. " 38180edb8777SJoseph Huber << "Some optimizations may not be possible."; 38190edb8777SJoseph Huber }); 38200edb8777SJoseph Huber }; 38210edb8777SJoseph Huber 382257ad2e10SJoseph Huber // Create internal copies of each function if this is a kernel Module. This 382357ad2e10SJoseph Huber // allows iterprocedural passes to see every call edge. 382403d7e61cSJoseph Huber DenseSet<const Function *> InternalizedFuncs; 38255ccb7424SJoseph Huber if (isOpenMPDevice(M)) 382603d7e61cSJoseph Huber for (Function &F : M) 38270edb8777SJoseph Huber if (!F.isDeclaration() && !Kernels.contains(&F) && IsCalled(F)) { 38280edb8777SJoseph Huber if (Attributor::internalizeFunction(F, /* Force */ true)) { 382903d7e61cSJoseph Huber InternalizedFuncs.insert(&F); 3830ecabc668SJoseph Huber } else if (!F.hasLocalLinkage() && !F.hasFnAttribute(Attribute::Cold)) { 38310edb8777SJoseph Huber EmitRemark(F); 38320edb8777SJoseph Huber } 38330edb8777SJoseph Huber } 383403d7e61cSJoseph Huber 383557ad2e10SJoseph Huber // Look at every function in the Module unless it was internalized. 3836b2ad63d3SJoseph Huber SmallVector<Function *, 16> SCC; 383703d7e61cSJoseph Huber for (Function &F : M) 383803d7e61cSJoseph Huber if (!F.isDeclaration() && !InternalizedFuncs.contains(&F)) 383903d7e61cSJoseph Huber SCC.push_back(&F); 3840b2ad63d3SJoseph Huber 3841b2ad63d3SJoseph Huber if (SCC.empty()) 3842b2ad63d3SJoseph Huber return PreservedAnalyses::all(); 3843b2ad63d3SJoseph Huber 3844b2ad63d3SJoseph Huber AnalysisGetter AG(FAM); 3845b2ad63d3SJoseph Huber 3846b2ad63d3SJoseph Huber auto OREGetter = [&FAM](Function *F) -> OptimizationRemarkEmitter & { 3847b2ad63d3SJoseph Huber return FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F); 3848b2ad63d3SJoseph Huber }; 3849b2ad63d3SJoseph Huber 3850b2ad63d3SJoseph Huber BumpPtrAllocator Allocator; 3851b2ad63d3SJoseph Huber CallGraphUpdater CGUpdater; 3852b2ad63d3SJoseph Huber 3853b2ad63d3SJoseph Huber SetVector<Function *> Functions(SCC.begin(), SCC.end()); 38545ccb7424SJoseph Huber OMPInformationCache InfoCache(M, AG, Allocator, /*CGSCC*/ Functions, Kernels); 3855b2ad63d3SJoseph Huber 385613b2fba2SJoseph Huber unsigned MaxFixpointIterations = (isOpenMPDevice(M)) ? 128 : 32; 38574a6bd8e3SJoseph Huber Attributor A(Functions, InfoCache, CGUpdater, nullptr, true, false, 385813b2fba2SJoseph Huber MaxFixpointIterations, OREGetter, DEBUG_TYPE); 3859b2ad63d3SJoseph Huber 3860b2ad63d3SJoseph Huber OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A); 3861b2ad63d3SJoseph Huber bool Changed = OMPOpt.run(true); 3862b2ad63d3SJoseph Huber if (Changed) 3863b2ad63d3SJoseph Huber return PreservedAnalyses::none(); 3864b2ad63d3SJoseph Huber 3865b2ad63d3SJoseph Huber return PreservedAnalyses::all(); 3866b2ad63d3SJoseph Huber } 3867b2ad63d3SJoseph Huber 3868b2ad63d3SJoseph Huber PreservedAnalyses OpenMPOptCGSCCPass::run(LazyCallGraph::SCC &C, 38699548b74aSJohannes Doerfert CGSCCAnalysisManager &AM, 3870b2ad63d3SJoseph Huber LazyCallGraph &CG, 3871b2ad63d3SJoseph Huber CGSCCUpdateResult &UR) { 38725ccb7424SJoseph Huber if (!containsOpenMP(*C.begin()->getFunction().getParent())) 38739548b74aSJohannes Doerfert return PreservedAnalyses::all(); 38749548b74aSJohannes Doerfert if (DisableOpenMPOptimizations) 38759548b74aSJohannes Doerfert return PreservedAnalyses::all(); 38769548b74aSJohannes Doerfert 3877ee17263aSJohannes Doerfert SmallVector<Function *, 16> SCC; 3878351d234dSRoman Lebedev // If there are kernels in the module, we have to run on all SCC's. 3879351d234dSRoman Lebedev for (LazyCallGraph::Node &N : C) { 3880351d234dSRoman Lebedev Function *Fn = &N.getFunction(); 3881351d234dSRoman Lebedev SCC.push_back(Fn); 3882351d234dSRoman Lebedev } 3883351d234dSRoman Lebedev 38845ccb7424SJoseph Huber if (SCC.empty()) 38859548b74aSJohannes Doerfert return PreservedAnalyses::all(); 38869548b74aSJohannes Doerfert 38875ccb7424SJoseph Huber Module &M = *C.begin()->getFunction().getParent(); 38885ccb7424SJoseph Huber 38895ccb7424SJoseph Huber KernelSet Kernels = getDeviceKernels(M); 38905ccb7424SJoseph Huber 38914d4ea9acSHuber, Joseph FunctionAnalysisManager &FAM = 38924d4ea9acSHuber, Joseph AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); 38937cfd267cSsstefan1 38947cfd267cSsstefan1 AnalysisGetter AG(FAM); 38957cfd267cSsstefan1 38967cfd267cSsstefan1 auto OREGetter = [&FAM](Function *F) -> OptimizationRemarkEmitter & { 38974d4ea9acSHuber, Joseph return FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F); 38984d4ea9acSHuber, Joseph }; 38994d4ea9acSHuber, Joseph 3900b2ad63d3SJoseph Huber BumpPtrAllocator Allocator; 39019548b74aSJohannes Doerfert CallGraphUpdater CGUpdater; 39029548b74aSJohannes Doerfert CGUpdater.initialize(CG, C, AM, UR); 39037cfd267cSsstefan1 39047cfd267cSsstefan1 SetVector<Function *> Functions(SCC.begin(), SCC.end()); 39057cfd267cSsstefan1 OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, Allocator, 39065ccb7424SJoseph Huber /*CGSCC*/ Functions, Kernels); 39077cfd267cSsstefan1 390813b2fba2SJoseph Huber unsigned MaxFixpointIterations = (isOpenMPDevice(M)) ? 128 : 32; 39094a6bd8e3SJoseph Huber Attributor A(Functions, InfoCache, CGUpdater, nullptr, false, true, 391013b2fba2SJoseph Huber MaxFixpointIterations, OREGetter, DEBUG_TYPE); 3911b8235d2bSsstefan1 3912b8235d2bSsstefan1 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A); 3913b2ad63d3SJoseph Huber bool Changed = OMPOpt.run(false); 3914694ded37SGiorgis Georgakoudis if (Changed) 3915694ded37SGiorgis Georgakoudis return PreservedAnalyses::none(); 3916694ded37SGiorgis Georgakoudis 39179548b74aSJohannes Doerfert return PreservedAnalyses::all(); 39189548b74aSJohannes Doerfert } 39198b57ed09SJoseph Huber 39209548b74aSJohannes Doerfert namespace { 39219548b74aSJohannes Doerfert 3922b2ad63d3SJoseph Huber struct OpenMPOptCGSCCLegacyPass : public CallGraphSCCPass { 39239548b74aSJohannes Doerfert CallGraphUpdater CGUpdater; 39249548b74aSJohannes Doerfert static char ID; 39259548b74aSJohannes Doerfert 3926b2ad63d3SJoseph Huber OpenMPOptCGSCCLegacyPass() : CallGraphSCCPass(ID) { 3927b2ad63d3SJoseph Huber initializeOpenMPOptCGSCCLegacyPassPass(*PassRegistry::getPassRegistry()); 39289548b74aSJohannes Doerfert } 39299548b74aSJohannes Doerfert 39309548b74aSJohannes Doerfert void getAnalysisUsage(AnalysisUsage &AU) const override { 39319548b74aSJohannes Doerfert CallGraphSCCPass::getAnalysisUsage(AU); 39329548b74aSJohannes Doerfert } 39339548b74aSJohannes Doerfert 39349548b74aSJohannes Doerfert bool runOnSCC(CallGraphSCC &CGSCC) override { 39355ccb7424SJoseph Huber if (!containsOpenMP(CGSCC.getCallGraph().getModule())) 39369548b74aSJohannes Doerfert return false; 39379548b74aSJohannes Doerfert if (DisableOpenMPOptimizations || skipSCC(CGSCC)) 39389548b74aSJohannes Doerfert return false; 39399548b74aSJohannes Doerfert 3940ee17263aSJohannes Doerfert SmallVector<Function *, 16> SCC; 3941351d234dSRoman Lebedev // If there are kernels in the module, we have to run on all SCC's. 3942351d234dSRoman Lebedev for (CallGraphNode *CGN : CGSCC) { 3943351d234dSRoman Lebedev Function *Fn = CGN->getFunction(); 3944351d234dSRoman Lebedev if (!Fn || Fn->isDeclaration()) 3945351d234dSRoman Lebedev continue; 3946ee17263aSJohannes Doerfert SCC.push_back(Fn); 3947351d234dSRoman Lebedev } 3948351d234dSRoman Lebedev 39495ccb7424SJoseph Huber if (SCC.empty()) 39509548b74aSJohannes Doerfert return false; 39519548b74aSJohannes Doerfert 39525ccb7424SJoseph Huber Module &M = CGSCC.getCallGraph().getModule(); 39535ccb7424SJoseph Huber KernelSet Kernels = getDeviceKernels(M); 39545ccb7424SJoseph Huber 39559548b74aSJohannes Doerfert CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph(); 39569548b74aSJohannes Doerfert CGUpdater.initialize(CG, CGSCC); 39579548b74aSJohannes Doerfert 39584d4ea9acSHuber, Joseph // Maintain a map of functions to avoid rebuilding the ORE 39594d4ea9acSHuber, Joseph DenseMap<Function *, std::unique_ptr<OptimizationRemarkEmitter>> OREMap; 39604d4ea9acSHuber, Joseph auto OREGetter = [&OREMap](Function *F) -> OptimizationRemarkEmitter & { 39614d4ea9acSHuber, Joseph std::unique_ptr<OptimizationRemarkEmitter> &ORE = OREMap[F]; 39624d4ea9acSHuber, Joseph if (!ORE) 39634d4ea9acSHuber, Joseph ORE = std::make_unique<OptimizationRemarkEmitter>(F); 39644d4ea9acSHuber, Joseph return *ORE; 39654d4ea9acSHuber, Joseph }; 39664d4ea9acSHuber, Joseph 39677cfd267cSsstefan1 AnalysisGetter AG; 39687cfd267cSsstefan1 SetVector<Function *> Functions(SCC.begin(), SCC.end()); 39697cfd267cSsstefan1 BumpPtrAllocator Allocator; 39705ccb7424SJoseph Huber OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, 39715ccb7424SJoseph Huber Allocator, 39725ccb7424SJoseph Huber /*CGSCC*/ Functions, Kernels); 39737cfd267cSsstefan1 397413b2fba2SJoseph Huber unsigned MaxFixpointIterations = (isOpenMPDevice(M)) ? 128 : 32; 397530e36c9bSJoseph Huber Attributor A(Functions, InfoCache, CGUpdater, nullptr, false, true, 397613b2fba2SJoseph Huber MaxFixpointIterations, OREGetter, DEBUG_TYPE); 3977b8235d2bSsstefan1 3978b8235d2bSsstefan1 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A); 3979b2ad63d3SJoseph Huber return OMPOpt.run(false); 39809548b74aSJohannes Doerfert } 39819548b74aSJohannes Doerfert 39829548b74aSJohannes Doerfert bool doFinalization(CallGraph &CG) override { return CGUpdater.finalize(); } 39839548b74aSJohannes Doerfert }; 39849548b74aSJohannes Doerfert 39859548b74aSJohannes Doerfert } // end anonymous namespace 39869548b74aSJohannes Doerfert 39875ccb7424SJoseph Huber KernelSet llvm::omp::getDeviceKernels(Module &M) { 39885ccb7424SJoseph Huber // TODO: Create a more cross-platform way of determining device kernels. 3989e8039ad4SJohannes Doerfert NamedMDNode *MD = M.getOrInsertNamedMetadata("nvvm.annotations"); 39905ccb7424SJoseph Huber KernelSet Kernels; 39915ccb7424SJoseph Huber 3992e8039ad4SJohannes Doerfert if (!MD) 39935ccb7424SJoseph Huber return Kernels; 3994e8039ad4SJohannes Doerfert 3995e8039ad4SJohannes Doerfert for (auto *Op : MD->operands()) { 3996e8039ad4SJohannes Doerfert if (Op->getNumOperands() < 2) 3997e8039ad4SJohannes Doerfert continue; 3998e8039ad4SJohannes Doerfert MDString *KindID = dyn_cast<MDString>(Op->getOperand(1)); 3999e8039ad4SJohannes Doerfert if (!KindID || KindID->getString() != "kernel") 4000e8039ad4SJohannes Doerfert continue; 4001e8039ad4SJohannes Doerfert 4002e8039ad4SJohannes Doerfert Function *KernelFn = 4003e8039ad4SJohannes Doerfert mdconst::dyn_extract_or_null<Function>(Op->getOperand(0)); 4004e8039ad4SJohannes Doerfert if (!KernelFn) 4005e8039ad4SJohannes Doerfert continue; 4006e8039ad4SJohannes Doerfert 4007e8039ad4SJohannes Doerfert ++NumOpenMPTargetRegionKernels; 4008e8039ad4SJohannes Doerfert 4009e8039ad4SJohannes Doerfert Kernels.insert(KernelFn); 4010e8039ad4SJohannes Doerfert } 40115ccb7424SJoseph Huber 40125ccb7424SJoseph Huber return Kernels; 4013e8039ad4SJohannes Doerfert } 4014e8039ad4SJohannes Doerfert 40155ccb7424SJoseph Huber bool llvm::omp::containsOpenMP(Module &M) { 40165ccb7424SJoseph Huber Metadata *MD = M.getModuleFlag("openmp"); 40175ccb7424SJoseph Huber if (!MD) 40185ccb7424SJoseph Huber return false; 4019dce6bc18SJohannes Doerfert 4020e8039ad4SJohannes Doerfert return true; 4021e8039ad4SJohannes Doerfert } 4022e8039ad4SJohannes Doerfert 40235ccb7424SJoseph Huber bool llvm::omp::isOpenMPDevice(Module &M) { 40245ccb7424SJoseph Huber Metadata *MD = M.getModuleFlag("openmp-device"); 40255ccb7424SJoseph Huber if (!MD) 40265ccb7424SJoseph Huber return false; 40275ccb7424SJoseph Huber 40285ccb7424SJoseph Huber return true; 40299548b74aSJohannes Doerfert } 40309548b74aSJohannes Doerfert 4031b2ad63d3SJoseph Huber char OpenMPOptCGSCCLegacyPass::ID = 0; 40329548b74aSJohannes Doerfert 4033b2ad63d3SJoseph Huber INITIALIZE_PASS_BEGIN(OpenMPOptCGSCCLegacyPass, "openmp-opt-cgscc", 40349548b74aSJohannes Doerfert "OpenMP specific optimizations", false, false) 40359548b74aSJohannes Doerfert INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) 4036b2ad63d3SJoseph Huber INITIALIZE_PASS_END(OpenMPOptCGSCCLegacyPass, "openmp-opt-cgscc", 40379548b74aSJohannes Doerfert "OpenMP specific optimizations", false, false) 40389548b74aSJohannes Doerfert 4039b2ad63d3SJoseph Huber Pass *llvm::createOpenMPOptCGSCCLegacyPass() { 4040b2ad63d3SJoseph Huber return new OpenMPOptCGSCCLegacyPass(); 4041b2ad63d3SJoseph Huber } 4042