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" 249f04a0eaSJohannes Doerfert #include "llvm/ADT/SetVector.h" 259548b74aSJohannes Doerfert #include "llvm/ADT/Statistic.h" 26e6e440aeSJohannes Doerfert #include "llvm/ADT/StringRef.h" 279548b74aSJohannes Doerfert #include "llvm/Analysis/CallGraph.h" 289548b74aSJohannes Doerfert #include "llvm/Analysis/CallGraphSCCPass.h" 294d4ea9acSHuber, Joseph #include "llvm/Analysis/OptimizationRemarkEmitter.h" 303a6bfcf2SGiorgis Georgakoudis #include "llvm/Analysis/ValueTracking.h" 319548b74aSJohannes Doerfert #include "llvm/Frontend/OpenMP/OMPConstants.h" 32e28936f6SJohannes Doerfert #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" 33d9659bf6SJohannes Doerfert #include "llvm/IR/Assumptions.h" 34d9659bf6SJohannes Doerfert #include "llvm/IR/DiagnosticInfo.h" 35514c033dSJohannes Doerfert #include "llvm/IR/GlobalValue.h" 36d9659bf6SJohannes Doerfert #include "llvm/IR/Instruction.h" 3768abc3d2SJoseph Huber #include "llvm/IR/IntrinsicInst.h" 3827905eebSJoseph Huber #include "llvm/IR/IntrinsicsAMDGPU.h" 3927905eebSJoseph Huber #include "llvm/IR/IntrinsicsNVPTX.h" 409548b74aSJohannes Doerfert #include "llvm/InitializePasses.h" 419548b74aSJohannes Doerfert #include "llvm/Support/CommandLine.h" 429548b74aSJohannes Doerfert #include "llvm/Transforms/IPO.h" 437cfd267cSsstefan1 #include "llvm/Transforms/IPO/Attributor.h" 443a6bfcf2SGiorgis Georgakoudis #include "llvm/Transforms/Utils/BasicBlockUtils.h" 459548b74aSJohannes Doerfert #include "llvm/Transforms/Utils/CallGraphUpdater.h" 4697517055SGiorgis Georgakoudis #include "llvm/Transforms/Utils/CodeExtractor.h" 479548b74aSJohannes Doerfert 48e6e440aeSJohannes Doerfert #include <algorithm> 49e6e440aeSJohannes Doerfert 509548b74aSJohannes Doerfert using namespace llvm; 519548b74aSJohannes Doerfert using namespace omp; 529548b74aSJohannes Doerfert 539548b74aSJohannes Doerfert #define DEBUG_TYPE "openmp-opt" 549548b74aSJohannes Doerfert 559548b74aSJohannes Doerfert static cl::opt<bool> DisableOpenMPOptimizations( 569548b74aSJohannes Doerfert "openmp-opt-disable", cl::ZeroOrMore, 579548b74aSJohannes Doerfert cl::desc("Disable OpenMP specific optimizations."), cl::Hidden, 589548b74aSJohannes Doerfert cl::init(false)); 599548b74aSJohannes Doerfert 603a6bfcf2SGiorgis Georgakoudis static cl::opt<bool> EnableParallelRegionMerging( 613a6bfcf2SGiorgis Georgakoudis "openmp-opt-enable-merging", cl::ZeroOrMore, 623a6bfcf2SGiorgis Georgakoudis cl::desc("Enable the OpenMP region merging optimization."), cl::Hidden, 633a6bfcf2SGiorgis Georgakoudis cl::init(false)); 643a6bfcf2SGiorgis Georgakoudis 654a668604SJoseph Huber static cl::opt<bool> 664a668604SJoseph Huber DisableInternalization("openmp-opt-disable-internalization", cl::ZeroOrMore, 674a668604SJoseph Huber cl::desc("Disable function internalization."), 684a668604SJoseph Huber cl::Hidden, cl::init(false)); 694a668604SJoseph Huber 700f426935Ssstefan1 static cl::opt<bool> PrintICVValues("openmp-print-icv-values", cl::init(false), 710f426935Ssstefan1 cl::Hidden); 72e8039ad4SJohannes Doerfert static cl::opt<bool> PrintOpenMPKernels("openmp-print-gpu-kernels", 73e8039ad4SJohannes Doerfert cl::init(false), cl::Hidden); 740f426935Ssstefan1 75496f8e5bSHamilton Tobon Mosquera static cl::opt<bool> HideMemoryTransferLatency( 76496f8e5bSHamilton Tobon Mosquera "openmp-hide-memory-transfer-latency", 77496f8e5bSHamilton Tobon Mosquera cl::desc("[WIP] Tries to hide the latency of host to device memory" 78496f8e5bSHamilton Tobon Mosquera " transfers"), 79496f8e5bSHamilton Tobon Mosquera cl::Hidden, cl::init(false)); 80496f8e5bSHamilton Tobon Mosquera 81cd0dd8ecSJoseph Huber static cl::opt<bool> DisableOpenMPOptDeglobalization( 82cd0dd8ecSJoseph Huber "openmp-opt-disable-deglobalization", cl::ZeroOrMore, 83cd0dd8ecSJoseph Huber cl::desc("Disable OpenMP optimizations involving deglobalization."), 84cd0dd8ecSJoseph Huber cl::Hidden, cl::init(false)); 85cd0dd8ecSJoseph Huber 86cd0dd8ecSJoseph Huber static cl::opt<bool> DisableOpenMPOptSPMDization( 87cd0dd8ecSJoseph Huber "openmp-opt-disable-spmdization", cl::ZeroOrMore, 88cd0dd8ecSJoseph Huber cl::desc("Disable OpenMP optimizations involving SPMD-ization."), 89cd0dd8ecSJoseph Huber cl::Hidden, cl::init(false)); 90cd0dd8ecSJoseph Huber 91cd0dd8ecSJoseph Huber static cl::opt<bool> DisableOpenMPOptFolding( 92cd0dd8ecSJoseph Huber "openmp-opt-disable-folding", cl::ZeroOrMore, 93cd0dd8ecSJoseph Huber cl::desc("Disable OpenMP optimizations involving folding."), cl::Hidden, 94cd0dd8ecSJoseph Huber cl::init(false)); 95cd0dd8ecSJoseph Huber 96cd0dd8ecSJoseph Huber static cl::opt<bool> DisableOpenMPOptStateMachineRewrite( 97cd0dd8ecSJoseph Huber "openmp-opt-disable-state-machine-rewrite", cl::ZeroOrMore, 98cd0dd8ecSJoseph Huber cl::desc("Disable OpenMP optimizations that replace the state machine."), 99cd0dd8ecSJoseph Huber cl::Hidden, cl::init(false)); 100cd0dd8ecSJoseph Huber 101339aa765SJoseph Huber static cl::opt<bool> PrintModuleAfterOptimizations( 102339aa765SJoseph Huber "openmp-opt-print-module", cl::ZeroOrMore, 103339aa765SJoseph Huber cl::desc("Print the current module after OpenMP optimizations."), 104339aa765SJoseph Huber cl::Hidden, cl::init(false)); 105339aa765SJoseph Huber 10629a74a39SJoseph Huber static cl::opt<bool> AlwaysInlineDeviceFunctions( 10729a74a39SJoseph Huber "openmp-opt-inline-device", cl::ZeroOrMore, 10829a74a39SJoseph Huber cl::desc("Inline all applicible functions on the device."), cl::Hidden, 10929a74a39SJoseph Huber cl::init(false)); 11029a74a39SJoseph Huber 1117eb899cbSJoseph Huber static cl::opt<bool> 1127eb899cbSJoseph Huber EnableVerboseRemarks("openmp-opt-verbose-remarks", cl::ZeroOrMore, 1137eb899cbSJoseph Huber cl::desc("Enables more verbose remarks."), cl::Hidden, 1147eb899cbSJoseph Huber cl::init(false)); 1157eb899cbSJoseph Huber 116f074a6a0SJoseph Huber static cl::opt<unsigned> 117f074a6a0SJoseph Huber SetFixpointIterations("openmp-opt-max-iterations", cl::Hidden, 118f074a6a0SJoseph Huber cl::desc("Maximal number of attributor iterations."), 119f074a6a0SJoseph Huber cl::init(256)); 120f074a6a0SJoseph Huber 1219548b74aSJohannes Doerfert STATISTIC(NumOpenMPRuntimeCallsDeduplicated, 1229548b74aSJohannes Doerfert "Number of OpenMP runtime calls deduplicated"); 12355eb714aSRoman Lebedev STATISTIC(NumOpenMPParallelRegionsDeleted, 12455eb714aSRoman Lebedev "Number of OpenMP parallel regions deleted"); 1259548b74aSJohannes Doerfert STATISTIC(NumOpenMPRuntimeFunctionsIdentified, 1269548b74aSJohannes Doerfert "Number of OpenMP runtime functions identified"); 1279548b74aSJohannes Doerfert STATISTIC(NumOpenMPRuntimeFunctionUsesIdentified, 1289548b74aSJohannes Doerfert "Number of OpenMP runtime function uses identified"); 129e8039ad4SJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernels, 130e8039ad4SJohannes Doerfert "Number of OpenMP target region entry points (=kernels) identified"); 131514c033dSJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernelsSPMD, 132514c033dSJohannes Doerfert "Number of OpenMP target region entry points (=kernels) executed in " 133514c033dSJohannes Doerfert "SPMD-mode instead of generic-mode"); 134d9659bf6SJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernelsWithoutStateMachine, 135d9659bf6SJohannes Doerfert "Number of OpenMP target region entry points (=kernels) executed in " 136d9659bf6SJohannes Doerfert "generic-mode without a state machines"); 137d9659bf6SJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback, 138d9659bf6SJohannes Doerfert "Number of OpenMP target region entry points (=kernels) executed in " 139d9659bf6SJohannes Doerfert "generic-mode with customized state machines with fallback"); 140d9659bf6SJohannes Doerfert STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback, 141d9659bf6SJohannes Doerfert "Number of OpenMP target region entry points (=kernels) executed in " 142d9659bf6SJohannes Doerfert "generic-mode with customized state machines without fallback"); 1435b0581aeSJohannes Doerfert STATISTIC( 1445b0581aeSJohannes Doerfert NumOpenMPParallelRegionsReplacedInGPUStateMachine, 1455b0581aeSJohannes Doerfert "Number of OpenMP parallel regions replaced with ID in GPU state machines"); 1463a6bfcf2SGiorgis Georgakoudis STATISTIC(NumOpenMPParallelRegionsMerged, 1473a6bfcf2SGiorgis Georgakoudis "Number of OpenMP parallel regions merged"); 1486fc51c9fSJoseph Huber STATISTIC(NumBytesMovedToSharedMemory, 1496fc51c9fSJoseph Huber "Amount of memory pushed to shared memory"); 1509548b74aSJohannes Doerfert 151263c4a3cSrathod-sahaab #if !defined(NDEBUG) 1529548b74aSJohannes Doerfert static constexpr auto TAG = "[" DEBUG_TYPE "]"; 153a50c0b0dSMikael Holmen #endif 1549548b74aSJohannes Doerfert 1559548b74aSJohannes Doerfert namespace { 1569548b74aSJohannes Doerfert 1576fc51c9fSJoseph Huber struct AAHeapToShared; 1586fc51c9fSJoseph Huber 159b8235d2bSsstefan1 struct AAICVTracker; 160b8235d2bSsstefan1 1617cfd267cSsstefan1 /// OpenMP specific information. For now, stores RFIs and ICVs also needed for 1627cfd267cSsstefan1 /// Attributor runs. 1637cfd267cSsstefan1 struct OMPInformationCache : public InformationCache { 1647cfd267cSsstefan1 OMPInformationCache(Module &M, AnalysisGetter &AG, 165624d34afSJohannes Doerfert BumpPtrAllocator &Allocator, SetVector<Function *> &CGSCC, 166*86cdff0eSEli Friedman KernelSet &Kernels) 167624d34afSJohannes Doerfert : InformationCache(M, AG, Allocator, &CGSCC), OMPBuilder(M), 168624d34afSJohannes Doerfert Kernels(Kernels) { 169624d34afSJohannes Doerfert 17061238d26Ssstefan1 OMPBuilder.initialize(); 1719548b74aSJohannes Doerfert initializeRuntimeFunctions(); 1720f426935Ssstefan1 initializeInternalControlVars(); 1739548b74aSJohannes Doerfert } 1749548b74aSJohannes Doerfert 1750f426935Ssstefan1 /// Generic information that describes an internal control variable. 1760f426935Ssstefan1 struct InternalControlVarInfo { 1770f426935Ssstefan1 /// The kind, as described by InternalControlVar enum. 1780f426935Ssstefan1 InternalControlVar Kind; 1790f426935Ssstefan1 1800f426935Ssstefan1 /// The name of the ICV. 1810f426935Ssstefan1 StringRef Name; 1820f426935Ssstefan1 1830f426935Ssstefan1 /// Environment variable associated with this ICV. 1840f426935Ssstefan1 StringRef EnvVarName; 1850f426935Ssstefan1 1860f426935Ssstefan1 /// Initial value kind. 1870f426935Ssstefan1 ICVInitValue InitKind; 1880f426935Ssstefan1 1890f426935Ssstefan1 /// Initial value. 1900f426935Ssstefan1 ConstantInt *InitValue; 1910f426935Ssstefan1 1920f426935Ssstefan1 /// Setter RTL function associated with this ICV. 1930f426935Ssstefan1 RuntimeFunction Setter; 1940f426935Ssstefan1 1950f426935Ssstefan1 /// Getter RTL function associated with this ICV. 1960f426935Ssstefan1 RuntimeFunction Getter; 1970f426935Ssstefan1 1980f426935Ssstefan1 /// RTL Function corresponding to the override clause of this ICV 1990f426935Ssstefan1 RuntimeFunction Clause; 2000f426935Ssstefan1 }; 2010f426935Ssstefan1 2029548b74aSJohannes Doerfert /// Generic information that describes a runtime function 2039548b74aSJohannes Doerfert struct RuntimeFunctionInfo { 2048855fec3SJohannes Doerfert 2059548b74aSJohannes Doerfert /// The kind, as described by the RuntimeFunction enum. 2069548b74aSJohannes Doerfert RuntimeFunction Kind; 2079548b74aSJohannes Doerfert 2089548b74aSJohannes Doerfert /// The name of the function. 2099548b74aSJohannes Doerfert StringRef Name; 2109548b74aSJohannes Doerfert 2119548b74aSJohannes Doerfert /// Flag to indicate a variadic function. 2129548b74aSJohannes Doerfert bool IsVarArg; 2139548b74aSJohannes Doerfert 2149548b74aSJohannes Doerfert /// The return type of the function. 2159548b74aSJohannes Doerfert Type *ReturnType; 2169548b74aSJohannes Doerfert 2179548b74aSJohannes Doerfert /// The argument types of the function. 2189548b74aSJohannes Doerfert SmallVector<Type *, 8> ArgumentTypes; 2199548b74aSJohannes Doerfert 2209548b74aSJohannes Doerfert /// The declaration if available. 221f09f4b26SJohannes Doerfert Function *Declaration = nullptr; 2229548b74aSJohannes Doerfert 2239548b74aSJohannes Doerfert /// Uses of this runtime function per function containing the use. 2248855fec3SJohannes Doerfert using UseVector = SmallVector<Use *, 16>; 2258855fec3SJohannes Doerfert 226b8235d2bSsstefan1 /// Clear UsesMap for runtime function. 227b8235d2bSsstefan1 void clearUsesMap() { UsesMap.clear(); } 228b8235d2bSsstefan1 22954bd3751SJohannes Doerfert /// Boolean conversion that is true if the runtime function was found. 23054bd3751SJohannes Doerfert operator bool() const { return Declaration; } 23154bd3751SJohannes Doerfert 2328855fec3SJohannes Doerfert /// Return the vector of uses in function \p F. 2338855fec3SJohannes Doerfert UseVector &getOrCreateUseVector(Function *F) { 234b8235d2bSsstefan1 std::shared_ptr<UseVector> &UV = UsesMap[F]; 2358855fec3SJohannes Doerfert if (!UV) 236b8235d2bSsstefan1 UV = std::make_shared<UseVector>(); 2378855fec3SJohannes Doerfert return *UV; 2388855fec3SJohannes Doerfert } 2398855fec3SJohannes Doerfert 2408855fec3SJohannes Doerfert /// Return the vector of uses in function \p F or `nullptr` if there are 2418855fec3SJohannes Doerfert /// none. 2428855fec3SJohannes Doerfert const UseVector *getUseVector(Function &F) const { 24395e57072SDavid Blaikie auto I = UsesMap.find(&F); 24495e57072SDavid Blaikie if (I != UsesMap.end()) 24595e57072SDavid Blaikie return I->second.get(); 24695e57072SDavid Blaikie return nullptr; 2478855fec3SJohannes Doerfert } 2488855fec3SJohannes Doerfert 2498855fec3SJohannes Doerfert /// Return how many functions contain uses of this runtime function. 2508855fec3SJohannes Doerfert size_t getNumFunctionsWithUses() const { return UsesMap.size(); } 2519548b74aSJohannes Doerfert 2529548b74aSJohannes Doerfert /// Return the number of arguments (or the minimal number for variadic 2539548b74aSJohannes Doerfert /// functions). 2549548b74aSJohannes Doerfert size_t getNumArgs() const { return ArgumentTypes.size(); } 2559548b74aSJohannes Doerfert 2569548b74aSJohannes Doerfert /// Run the callback \p CB on each use and forget the use if the result is 2579548b74aSJohannes Doerfert /// true. The callback will be fed the function in which the use was 2589548b74aSJohannes Doerfert /// encountered as second argument. 259624d34afSJohannes Doerfert void foreachUse(SmallVectorImpl<Function *> &SCC, 260624d34afSJohannes Doerfert function_ref<bool(Use &, Function &)> CB) { 261624d34afSJohannes Doerfert for (Function *F : SCC) 262624d34afSJohannes Doerfert foreachUse(CB, F); 263e099c7b6Ssstefan1 } 264e099c7b6Ssstefan1 265e099c7b6Ssstefan1 /// Run the callback \p CB on each use within the function \p F and forget 266e099c7b6Ssstefan1 /// the use if the result is true. 267624d34afSJohannes Doerfert void foreachUse(function_ref<bool(Use &, Function &)> CB, Function *F) { 2688855fec3SJohannes Doerfert SmallVector<unsigned, 8> ToBeDeleted; 2699548b74aSJohannes Doerfert ToBeDeleted.clear(); 270e099c7b6Ssstefan1 2718855fec3SJohannes Doerfert unsigned Idx = 0; 272624d34afSJohannes Doerfert UseVector &UV = getOrCreateUseVector(F); 273e099c7b6Ssstefan1 2748855fec3SJohannes Doerfert for (Use *U : UV) { 275e099c7b6Ssstefan1 if (CB(*U, *F)) 2768855fec3SJohannes Doerfert ToBeDeleted.push_back(Idx); 2778855fec3SJohannes Doerfert ++Idx; 2788855fec3SJohannes Doerfert } 2798855fec3SJohannes Doerfert 2808855fec3SJohannes Doerfert // Remove the to-be-deleted indices in reverse order as prior 281b726c557SJohannes Doerfert // modifications will not modify the smaller indices. 2828855fec3SJohannes Doerfert while (!ToBeDeleted.empty()) { 2838855fec3SJohannes Doerfert unsigned Idx = ToBeDeleted.pop_back_val(); 2848855fec3SJohannes Doerfert UV[Idx] = UV.back(); 2858855fec3SJohannes Doerfert UV.pop_back(); 2869548b74aSJohannes Doerfert } 2879548b74aSJohannes Doerfert } 2888855fec3SJohannes Doerfert 2898855fec3SJohannes Doerfert private: 2908855fec3SJohannes Doerfert /// Map from functions to all uses of this runtime function contained in 2918855fec3SJohannes Doerfert /// them. 292b8235d2bSsstefan1 DenseMap<Function *, std::shared_ptr<UseVector>> UsesMap; 293d9659bf6SJohannes Doerfert 294d9659bf6SJohannes Doerfert public: 295d9659bf6SJohannes Doerfert /// Iterators for the uses of this runtime function. 296d9659bf6SJohannes Doerfert decltype(UsesMap)::iterator begin() { return UsesMap.begin(); } 297d9659bf6SJohannes Doerfert decltype(UsesMap)::iterator end() { return UsesMap.end(); } 2989548b74aSJohannes Doerfert }; 2999548b74aSJohannes Doerfert 3007cfd267cSsstefan1 /// An OpenMP-IR-Builder instance 3017cfd267cSsstefan1 OpenMPIRBuilder OMPBuilder; 3027cfd267cSsstefan1 3037cfd267cSsstefan1 /// Map from runtime function kind to the runtime function description. 3047cfd267cSsstefan1 EnumeratedArray<RuntimeFunctionInfo, RuntimeFunction, 3057cfd267cSsstefan1 RuntimeFunction::OMPRTL___last> 3067cfd267cSsstefan1 RFIs; 3077cfd267cSsstefan1 308d9659bf6SJohannes Doerfert /// Map from function declarations/definitions to their runtime enum type. 309d9659bf6SJohannes Doerfert DenseMap<Function *, RuntimeFunction> RuntimeFunctionIDMap; 310d9659bf6SJohannes Doerfert 3110f426935Ssstefan1 /// Map from ICV kind to the ICV description. 3120f426935Ssstefan1 EnumeratedArray<InternalControlVarInfo, InternalControlVar, 3130f426935Ssstefan1 InternalControlVar::ICV___last> 3140f426935Ssstefan1 ICVs; 3150f426935Ssstefan1 3160f426935Ssstefan1 /// Helper to initialize all internal control variable information for those 3170f426935Ssstefan1 /// defined in OMPKinds.def. 3180f426935Ssstefan1 void initializeInternalControlVars() { 3190f426935Ssstefan1 #define ICV_RT_SET(_Name, RTL) \ 3200f426935Ssstefan1 { \ 3210f426935Ssstefan1 auto &ICV = ICVs[_Name]; \ 3220f426935Ssstefan1 ICV.Setter = RTL; \ 3230f426935Ssstefan1 } 3240f426935Ssstefan1 #define ICV_RT_GET(Name, RTL) \ 3250f426935Ssstefan1 { \ 3260f426935Ssstefan1 auto &ICV = ICVs[Name]; \ 3270f426935Ssstefan1 ICV.Getter = RTL; \ 3280f426935Ssstefan1 } 3290f426935Ssstefan1 #define ICV_DATA_ENV(Enum, _Name, _EnvVarName, Init) \ 3300f426935Ssstefan1 { \ 3310f426935Ssstefan1 auto &ICV = ICVs[Enum]; \ 3320f426935Ssstefan1 ICV.Name = _Name; \ 3330f426935Ssstefan1 ICV.Kind = Enum; \ 3340f426935Ssstefan1 ICV.InitKind = Init; \ 3350f426935Ssstefan1 ICV.EnvVarName = _EnvVarName; \ 3360f426935Ssstefan1 switch (ICV.InitKind) { \ 337951e43f3Ssstefan1 case ICV_IMPLEMENTATION_DEFINED: \ 3380f426935Ssstefan1 ICV.InitValue = nullptr; \ 3390f426935Ssstefan1 break; \ 340951e43f3Ssstefan1 case ICV_ZERO: \ 3416aab27baSsstefan1 ICV.InitValue = ConstantInt::get( \ 3426aab27baSsstefan1 Type::getInt32Ty(OMPBuilder.Int32->getContext()), 0); \ 3430f426935Ssstefan1 break; \ 344951e43f3Ssstefan1 case ICV_FALSE: \ 3456aab27baSsstefan1 ICV.InitValue = ConstantInt::getFalse(OMPBuilder.Int1->getContext()); \ 3460f426935Ssstefan1 break; \ 347951e43f3Ssstefan1 case ICV_LAST: \ 3480f426935Ssstefan1 break; \ 3490f426935Ssstefan1 } \ 3500f426935Ssstefan1 } 3510f426935Ssstefan1 #include "llvm/Frontend/OpenMP/OMPKinds.def" 3520f426935Ssstefan1 } 3530f426935Ssstefan1 3547cfd267cSsstefan1 /// Returns true if the function declaration \p F matches the runtime 3557cfd267cSsstefan1 /// function types, that is, return type \p RTFRetType, and argument types 3567cfd267cSsstefan1 /// \p RTFArgTypes. 3577cfd267cSsstefan1 static bool declMatchesRTFTypes(Function *F, Type *RTFRetType, 3587cfd267cSsstefan1 SmallVector<Type *, 8> &RTFArgTypes) { 3597cfd267cSsstefan1 // TODO: We should output information to the user (under debug output 3607cfd267cSsstefan1 // and via remarks). 3617cfd267cSsstefan1 3627cfd267cSsstefan1 if (!F) 3637cfd267cSsstefan1 return false; 3647cfd267cSsstefan1 if (F->getReturnType() != RTFRetType) 3657cfd267cSsstefan1 return false; 3667cfd267cSsstefan1 if (F->arg_size() != RTFArgTypes.size()) 3677cfd267cSsstefan1 return false; 3687cfd267cSsstefan1 369c11ebfeaSJoseph Huber auto *RTFTyIt = RTFArgTypes.begin(); 3707cfd267cSsstefan1 for (Argument &Arg : F->args()) { 3717cfd267cSsstefan1 if (Arg.getType() != *RTFTyIt) 3727cfd267cSsstefan1 return false; 3737cfd267cSsstefan1 3747cfd267cSsstefan1 ++RTFTyIt; 3757cfd267cSsstefan1 } 3767cfd267cSsstefan1 3777cfd267cSsstefan1 return true; 3787cfd267cSsstefan1 } 3797cfd267cSsstefan1 380b726c557SJohannes Doerfert // Helper to collect all uses of the declaration in the UsesMap. 381b8235d2bSsstefan1 unsigned collectUses(RuntimeFunctionInfo &RFI, bool CollectStats = true) { 3827cfd267cSsstefan1 unsigned NumUses = 0; 3837cfd267cSsstefan1 if (!RFI.Declaration) 3847cfd267cSsstefan1 return NumUses; 3857cfd267cSsstefan1 OMPBuilder.addAttributes(RFI.Kind, *RFI.Declaration); 3867cfd267cSsstefan1 387b8235d2bSsstefan1 if (CollectStats) { 3887cfd267cSsstefan1 NumOpenMPRuntimeFunctionsIdentified += 1; 3897cfd267cSsstefan1 NumOpenMPRuntimeFunctionUsesIdentified += RFI.Declaration->getNumUses(); 390b8235d2bSsstefan1 } 3917cfd267cSsstefan1 3927cfd267cSsstefan1 // TODO: We directly convert uses into proper calls and unknown uses. 3937cfd267cSsstefan1 for (Use &U : RFI.Declaration->uses()) { 3947cfd267cSsstefan1 if (Instruction *UserI = dyn_cast<Instruction>(U.getUser())) { 3957cfd267cSsstefan1 if (ModuleSlice.count(UserI->getFunction())) { 3967cfd267cSsstefan1 RFI.getOrCreateUseVector(UserI->getFunction()).push_back(&U); 3977cfd267cSsstefan1 ++NumUses; 3987cfd267cSsstefan1 } 3997cfd267cSsstefan1 } else { 4007cfd267cSsstefan1 RFI.getOrCreateUseVector(nullptr).push_back(&U); 4017cfd267cSsstefan1 ++NumUses; 4027cfd267cSsstefan1 } 4037cfd267cSsstefan1 } 4047cfd267cSsstefan1 return NumUses; 405b8235d2bSsstefan1 } 4067cfd267cSsstefan1 40797517055SGiorgis Georgakoudis // Helper function to recollect uses of a runtime function. 40897517055SGiorgis Georgakoudis void recollectUsesForFunction(RuntimeFunction RTF) { 40997517055SGiorgis Georgakoudis auto &RFI = RFIs[RTF]; 410b8235d2bSsstefan1 RFI.clearUsesMap(); 411b8235d2bSsstefan1 collectUses(RFI, /*CollectStats*/ false); 412b8235d2bSsstefan1 } 41397517055SGiorgis Georgakoudis 41497517055SGiorgis Georgakoudis // Helper function to recollect uses of all runtime functions. 41597517055SGiorgis Georgakoudis void recollectUses() { 41697517055SGiorgis Georgakoudis for (int Idx = 0; Idx < RFIs.size(); ++Idx) 41797517055SGiorgis Georgakoudis recollectUsesForFunction(static_cast<RuntimeFunction>(Idx)); 418b8235d2bSsstefan1 } 419b8235d2bSsstefan1 420b8235d2bSsstefan1 /// Helper to initialize all runtime function information for those defined 421b8235d2bSsstefan1 /// in OpenMPKinds.def. 422b8235d2bSsstefan1 void initializeRuntimeFunctions() { 4237cfd267cSsstefan1 Module &M = *((*ModuleSlice.begin())->getParent()); 4247cfd267cSsstefan1 4256aab27baSsstefan1 // Helper macros for handling __VA_ARGS__ in OMP_RTL 4266aab27baSsstefan1 #define OMP_TYPE(VarName, ...) \ 4276aab27baSsstefan1 Type *VarName = OMPBuilder.VarName; \ 4286aab27baSsstefan1 (void)VarName; 4296aab27baSsstefan1 4306aab27baSsstefan1 #define OMP_ARRAY_TYPE(VarName, ...) \ 4316aab27baSsstefan1 ArrayType *VarName##Ty = OMPBuilder.VarName##Ty; \ 4326aab27baSsstefan1 (void)VarName##Ty; \ 4336aab27baSsstefan1 PointerType *VarName##PtrTy = OMPBuilder.VarName##PtrTy; \ 4346aab27baSsstefan1 (void)VarName##PtrTy; 4356aab27baSsstefan1 4366aab27baSsstefan1 #define OMP_FUNCTION_TYPE(VarName, ...) \ 4376aab27baSsstefan1 FunctionType *VarName = OMPBuilder.VarName; \ 4386aab27baSsstefan1 (void)VarName; \ 4396aab27baSsstefan1 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \ 4406aab27baSsstefan1 (void)VarName##Ptr; 4416aab27baSsstefan1 4426aab27baSsstefan1 #define OMP_STRUCT_TYPE(VarName, ...) \ 4436aab27baSsstefan1 StructType *VarName = OMPBuilder.VarName; \ 4446aab27baSsstefan1 (void)VarName; \ 4456aab27baSsstefan1 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \ 4466aab27baSsstefan1 (void)VarName##Ptr; 4476aab27baSsstefan1 4487cfd267cSsstefan1 #define OMP_RTL(_Enum, _Name, _IsVarArg, _ReturnType, ...) \ 4497cfd267cSsstefan1 { \ 4507cfd267cSsstefan1 SmallVector<Type *, 8> ArgsTypes({__VA_ARGS__}); \ 4517cfd267cSsstefan1 Function *F = M.getFunction(_Name); \ 452eef6601bSJoseph Huber RTLFunctions.insert(F); \ 4536aab27baSsstefan1 if (declMatchesRTFTypes(F, OMPBuilder._ReturnType, ArgsTypes)) { \ 454d9659bf6SJohannes Doerfert RuntimeFunctionIDMap[F] = _Enum; \ 45516206d17SJoseph Huber F->removeFnAttr(Attribute::NoInline); \ 4567cfd267cSsstefan1 auto &RFI = RFIs[_Enum]; \ 4577cfd267cSsstefan1 RFI.Kind = _Enum; \ 4587cfd267cSsstefan1 RFI.Name = _Name; \ 4597cfd267cSsstefan1 RFI.IsVarArg = _IsVarArg; \ 4606aab27baSsstefan1 RFI.ReturnType = OMPBuilder._ReturnType; \ 4617cfd267cSsstefan1 RFI.ArgumentTypes = std::move(ArgsTypes); \ 4627cfd267cSsstefan1 RFI.Declaration = F; \ 463b8235d2bSsstefan1 unsigned NumUses = collectUses(RFI); \ 4647cfd267cSsstefan1 (void)NumUses; \ 4657cfd267cSsstefan1 LLVM_DEBUG({ \ 4667cfd267cSsstefan1 dbgs() << TAG << RFI.Name << (RFI.Declaration ? "" : " not") \ 4677cfd267cSsstefan1 << " found\n"; \ 4687cfd267cSsstefan1 if (RFI.Declaration) \ 4697cfd267cSsstefan1 dbgs() << TAG << "-> got " << NumUses << " uses in " \ 4707cfd267cSsstefan1 << RFI.getNumFunctionsWithUses() \ 4717cfd267cSsstefan1 << " different functions.\n"; \ 4727cfd267cSsstefan1 }); \ 4737cfd267cSsstefan1 } \ 4747cfd267cSsstefan1 } 4757cfd267cSsstefan1 #include "llvm/Frontend/OpenMP/OMPKinds.def" 4767cfd267cSsstefan1 4777cfd267cSsstefan1 // TODO: We should attach the attributes defined in OMPKinds.def. 4787cfd267cSsstefan1 } 479e8039ad4SJohannes Doerfert 480e8039ad4SJohannes Doerfert /// Collection of known kernels (\see Kernel) in the module. 481*86cdff0eSEli Friedman KernelSet &Kernels; 482eef6601bSJoseph Huber 483eef6601bSJoseph Huber /// Collection of known OpenMP runtime functions.. 484eef6601bSJoseph Huber DenseSet<const Function *> RTLFunctions; 4857cfd267cSsstefan1 }; 4867cfd267cSsstefan1 487d9659bf6SJohannes Doerfert template <typename Ty, bool InsertInvalidates = true> 4881a7f7790SShilei Tian struct BooleanStateWithSetVector : public BooleanState { 4891a7f7790SShilei Tian bool contains(const Ty &Elem) const { return Set.contains(Elem); } 4901a7f7790SShilei Tian bool insert(const Ty &Elem) { 491d9659bf6SJohannes Doerfert if (InsertInvalidates) 492d9659bf6SJohannes Doerfert BooleanState::indicatePessimisticFixpoint(); 493d9659bf6SJohannes Doerfert return Set.insert(Elem); 494d9659bf6SJohannes Doerfert } 495d9659bf6SJohannes Doerfert 4961a7f7790SShilei Tian const Ty &operator[](int Idx) const { return Set[Idx]; } 4971a7f7790SShilei Tian bool operator==(const BooleanStateWithSetVector &RHS) const { 498d9659bf6SJohannes Doerfert return BooleanState::operator==(RHS) && Set == RHS.Set; 499d9659bf6SJohannes Doerfert } 5001a7f7790SShilei Tian bool operator!=(const BooleanStateWithSetVector &RHS) const { 501d9659bf6SJohannes Doerfert return !(*this == RHS); 502d9659bf6SJohannes Doerfert } 503d9659bf6SJohannes Doerfert 504d9659bf6SJohannes Doerfert bool empty() const { return Set.empty(); } 505d9659bf6SJohannes Doerfert size_t size() const { return Set.size(); } 506d9659bf6SJohannes Doerfert 507d9659bf6SJohannes Doerfert /// "Clamp" this state with \p RHS. 5081a7f7790SShilei Tian BooleanStateWithSetVector &operator^=(const BooleanStateWithSetVector &RHS) { 509d9659bf6SJohannes Doerfert BooleanState::operator^=(RHS); 510d9659bf6SJohannes Doerfert Set.insert(RHS.Set.begin(), RHS.Set.end()); 511d9659bf6SJohannes Doerfert return *this; 512d9659bf6SJohannes Doerfert } 513d9659bf6SJohannes Doerfert 514d9659bf6SJohannes Doerfert private: 515d9659bf6SJohannes Doerfert /// A set to keep track of elements. 5161a7f7790SShilei Tian SetVector<Ty> Set; 517d9659bf6SJohannes Doerfert 518d9659bf6SJohannes Doerfert public: 519d9659bf6SJohannes Doerfert typename decltype(Set)::iterator begin() { return Set.begin(); } 520d9659bf6SJohannes Doerfert typename decltype(Set)::iterator end() { return Set.end(); } 521d9659bf6SJohannes Doerfert typename decltype(Set)::const_iterator begin() const { return Set.begin(); } 522d9659bf6SJohannes Doerfert typename decltype(Set)::const_iterator end() const { return Set.end(); } 523d9659bf6SJohannes Doerfert }; 524d9659bf6SJohannes Doerfert 5251a7f7790SShilei Tian template <typename Ty, bool InsertInvalidates = true> 5261a7f7790SShilei Tian using BooleanStateWithPtrSetVector = 5271a7f7790SShilei Tian BooleanStateWithSetVector<Ty *, InsertInvalidates>; 5281a7f7790SShilei Tian 529d9659bf6SJohannes Doerfert struct KernelInfoState : AbstractState { 530d9659bf6SJohannes Doerfert /// Flag to track if we reached a fixpoint. 531d9659bf6SJohannes Doerfert bool IsAtFixpoint = false; 532d9659bf6SJohannes Doerfert 533d9659bf6SJohannes Doerfert /// The parallel regions (identified by the outlined parallel functions) that 534d9659bf6SJohannes Doerfert /// can be reached from the associated function. 535d9659bf6SJohannes Doerfert BooleanStateWithPtrSetVector<Function, /* InsertInvalidates */ false> 536d9659bf6SJohannes Doerfert ReachedKnownParallelRegions; 537d9659bf6SJohannes Doerfert 538d9659bf6SJohannes Doerfert /// State to track what parallel region we might reach. 539d9659bf6SJohannes Doerfert BooleanStateWithPtrSetVector<CallBase> ReachedUnknownParallelRegions; 540d9659bf6SJohannes Doerfert 541514c033dSJohannes Doerfert /// State to track if we are in SPMD-mode, assumed or know, and why we decided 542e8439ec8SGiorgis Georgakoudis /// we cannot be. If it is assumed, then RequiresFullRuntime should also be 543e8439ec8SGiorgis Georgakoudis /// false. 54429a3e3ddSGiorgis Georgakoudis BooleanStateWithPtrSetVector<Instruction, false> SPMDCompatibilityTracker; 545514c033dSJohannes Doerfert 546d9659bf6SJohannes Doerfert /// The __kmpc_target_init call in this kernel, if any. If we find more than 547d9659bf6SJohannes Doerfert /// one we abort as the kernel is malformed. 548d9659bf6SJohannes Doerfert CallBase *KernelInitCB = nullptr; 549d9659bf6SJohannes Doerfert 550d9659bf6SJohannes Doerfert /// The __kmpc_target_deinit call in this kernel, if any. If we find more than 551d9659bf6SJohannes Doerfert /// one we abort as the kernel is malformed. 552d9659bf6SJohannes Doerfert CallBase *KernelDeinitCB = nullptr; 553d9659bf6SJohannes Doerfert 554ca662297SShilei Tian /// Flag to indicate if the associated function is a kernel entry. 555ca662297SShilei Tian bool IsKernelEntry = false; 556ca662297SShilei Tian 557ca662297SShilei Tian /// State to track what kernel entries can reach the associated function. 558ca662297SShilei Tian BooleanStateWithPtrSetVector<Function, false> ReachingKernelEntries; 559ca662297SShilei Tian 560e97e0a4fSShilei Tian /// State to indicate if we can track parallel level of the associated 561e97e0a4fSShilei Tian /// function. We will give up tracking if we encounter unknown caller or the 562e97e0a4fSShilei Tian /// caller is __kmpc_parallel_51. 563e97e0a4fSShilei Tian BooleanStateWithSetVector<uint8_t> ParallelLevels; 564e97e0a4fSShilei Tian 565d9659bf6SJohannes Doerfert /// Abstract State interface 566d9659bf6SJohannes Doerfert ///{ 567d9659bf6SJohannes Doerfert 568d9659bf6SJohannes Doerfert KernelInfoState() {} 569d9659bf6SJohannes Doerfert KernelInfoState(bool BestState) { 570d9659bf6SJohannes Doerfert if (!BestState) 571d9659bf6SJohannes Doerfert indicatePessimisticFixpoint(); 572d9659bf6SJohannes Doerfert } 573d9659bf6SJohannes Doerfert 574d9659bf6SJohannes Doerfert /// See AbstractState::isValidState(...) 575d9659bf6SJohannes Doerfert bool isValidState() const override { return true; } 576d9659bf6SJohannes Doerfert 577d9659bf6SJohannes Doerfert /// See AbstractState::isAtFixpoint(...) 578d9659bf6SJohannes Doerfert bool isAtFixpoint() const override { return IsAtFixpoint; } 579d9659bf6SJohannes Doerfert 580d9659bf6SJohannes Doerfert /// See AbstractState::indicatePessimisticFixpoint(...) 581d9659bf6SJohannes Doerfert ChangeStatus indicatePessimisticFixpoint() override { 582d9659bf6SJohannes Doerfert IsAtFixpoint = true; 583c6457dcaSJohannes Doerfert ReachingKernelEntries.indicatePessimisticFixpoint(); 584514c033dSJohannes Doerfert SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 585c6457dcaSJohannes Doerfert ReachedKnownParallelRegions.indicatePessimisticFixpoint(); 586d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions.indicatePessimisticFixpoint(); 587d9659bf6SJohannes Doerfert return ChangeStatus::CHANGED; 588d9659bf6SJohannes Doerfert } 589d9659bf6SJohannes Doerfert 590d9659bf6SJohannes Doerfert /// See AbstractState::indicateOptimisticFixpoint(...) 591d9659bf6SJohannes Doerfert ChangeStatus indicateOptimisticFixpoint() override { 592d9659bf6SJohannes Doerfert IsAtFixpoint = true; 593d61aac76SJohannes Doerfert ReachingKernelEntries.indicateOptimisticFixpoint(); 594d61aac76SJohannes Doerfert SPMDCompatibilityTracker.indicateOptimisticFixpoint(); 595d61aac76SJohannes Doerfert ReachedKnownParallelRegions.indicateOptimisticFixpoint(); 596d61aac76SJohannes Doerfert ReachedUnknownParallelRegions.indicateOptimisticFixpoint(); 597d9659bf6SJohannes Doerfert return ChangeStatus::UNCHANGED; 598d9659bf6SJohannes Doerfert } 599d9659bf6SJohannes Doerfert 600d9659bf6SJohannes Doerfert /// Return the assumed state 601d9659bf6SJohannes Doerfert KernelInfoState &getAssumed() { return *this; } 602d9659bf6SJohannes Doerfert const KernelInfoState &getAssumed() const { return *this; } 603d9659bf6SJohannes Doerfert 604d9659bf6SJohannes Doerfert bool operator==(const KernelInfoState &RHS) const { 605514c033dSJohannes Doerfert if (SPMDCompatibilityTracker != RHS.SPMDCompatibilityTracker) 606514c033dSJohannes Doerfert return false; 607d9659bf6SJohannes Doerfert if (ReachedKnownParallelRegions != RHS.ReachedKnownParallelRegions) 608d9659bf6SJohannes Doerfert return false; 609d9659bf6SJohannes Doerfert if (ReachedUnknownParallelRegions != RHS.ReachedUnknownParallelRegions) 610d9659bf6SJohannes Doerfert return false; 611ca662297SShilei Tian if (ReachingKernelEntries != RHS.ReachingKernelEntries) 612ca662297SShilei Tian return false; 613d9659bf6SJohannes Doerfert return true; 614d9659bf6SJohannes Doerfert } 615d9659bf6SJohannes Doerfert 6166b9a3ec3SJoseph Huber /// Returns true if this kernel contains any OpenMP parallel regions. 6176b9a3ec3SJoseph Huber bool mayContainParallelRegion() { 6186b9a3ec3SJoseph Huber return !ReachedKnownParallelRegions.empty() || 6196b9a3ec3SJoseph Huber !ReachedUnknownParallelRegions.empty(); 6206b9a3ec3SJoseph Huber } 6216b9a3ec3SJoseph Huber 622d9659bf6SJohannes Doerfert /// Return empty set as the best state of potential values. 623d9659bf6SJohannes Doerfert static KernelInfoState getBestState() { return KernelInfoState(true); } 624d9659bf6SJohannes Doerfert 625d9659bf6SJohannes Doerfert static KernelInfoState getBestState(KernelInfoState &KIS) { 626d9659bf6SJohannes Doerfert return getBestState(); 627d9659bf6SJohannes Doerfert } 628d9659bf6SJohannes Doerfert 629d9659bf6SJohannes Doerfert /// Return full set as the worst state of potential values. 630d9659bf6SJohannes Doerfert static KernelInfoState getWorstState() { return KernelInfoState(false); } 631d9659bf6SJohannes Doerfert 632d9659bf6SJohannes Doerfert /// "Clamp" this state with \p KIS. 633d9659bf6SJohannes Doerfert KernelInfoState operator^=(const KernelInfoState &KIS) { 634d9659bf6SJohannes Doerfert // Do not merge two different _init and _deinit call sites. 635d9659bf6SJohannes Doerfert if (KIS.KernelInitCB) { 636d9659bf6SJohannes Doerfert if (KernelInitCB && KernelInitCB != KIS.KernelInitCB) 637e6e440aeSJohannes Doerfert llvm_unreachable("Kernel that calls another kernel violates OpenMP-Opt " 638e6e440aeSJohannes Doerfert "assumptions."); 639d9659bf6SJohannes Doerfert KernelInitCB = KIS.KernelInitCB; 640d9659bf6SJohannes Doerfert } 641d9659bf6SJohannes Doerfert if (KIS.KernelDeinitCB) { 642d9659bf6SJohannes Doerfert if (KernelDeinitCB && KernelDeinitCB != KIS.KernelDeinitCB) 643e6e440aeSJohannes Doerfert llvm_unreachable("Kernel that calls another kernel violates OpenMP-Opt " 644e6e440aeSJohannes Doerfert "assumptions."); 645d9659bf6SJohannes Doerfert KernelDeinitCB = KIS.KernelDeinitCB; 646d9659bf6SJohannes Doerfert } 647514c033dSJohannes Doerfert SPMDCompatibilityTracker ^= KIS.SPMDCompatibilityTracker; 648d9659bf6SJohannes Doerfert ReachedKnownParallelRegions ^= KIS.ReachedKnownParallelRegions; 649d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions ^= KIS.ReachedUnknownParallelRegions; 650d9659bf6SJohannes Doerfert return *this; 651d9659bf6SJohannes Doerfert } 652d9659bf6SJohannes Doerfert 653d9659bf6SJohannes Doerfert KernelInfoState operator&=(const KernelInfoState &KIS) { 654d9659bf6SJohannes Doerfert return (*this ^= KIS); 655d9659bf6SJohannes Doerfert } 656d9659bf6SJohannes Doerfert 657d9659bf6SJohannes Doerfert ///} 658d9659bf6SJohannes Doerfert }; 659d9659bf6SJohannes Doerfert 6608931add6SHamilton Tobon Mosquera /// Used to map the values physically (in the IR) stored in an offload 6618931add6SHamilton Tobon Mosquera /// array, to a vector in memory. 6628931add6SHamilton Tobon Mosquera struct OffloadArray { 6638931add6SHamilton Tobon Mosquera /// Physical array (in the IR). 6648931add6SHamilton Tobon Mosquera AllocaInst *Array = nullptr; 6658931add6SHamilton Tobon Mosquera /// Mapped values. 6668931add6SHamilton Tobon Mosquera SmallVector<Value *, 8> StoredValues; 6678931add6SHamilton Tobon Mosquera /// Last stores made in the offload array. 6688931add6SHamilton Tobon Mosquera SmallVector<StoreInst *, 8> LastAccesses; 6698931add6SHamilton Tobon Mosquera 6708931add6SHamilton Tobon Mosquera OffloadArray() = default; 6718931add6SHamilton Tobon Mosquera 6728931add6SHamilton Tobon Mosquera /// Initializes the OffloadArray with the values stored in \p Array before 6738931add6SHamilton Tobon Mosquera /// instruction \p Before is reached. Returns false if the initialization 6748931add6SHamilton Tobon Mosquera /// fails. 6758931add6SHamilton Tobon Mosquera /// This MUST be used immediately after the construction of the object. 6768931add6SHamilton Tobon Mosquera bool initialize(AllocaInst &Array, Instruction &Before) { 6778931add6SHamilton Tobon Mosquera if (!Array.getAllocatedType()->isArrayTy()) 6788931add6SHamilton Tobon Mosquera return false; 6798931add6SHamilton Tobon Mosquera 6808931add6SHamilton Tobon Mosquera if (!getValues(Array, Before)) 6818931add6SHamilton Tobon Mosquera return false; 6828931add6SHamilton Tobon Mosquera 6838931add6SHamilton Tobon Mosquera this->Array = &Array; 6848931add6SHamilton Tobon Mosquera return true; 6858931add6SHamilton Tobon Mosquera } 6868931add6SHamilton Tobon Mosquera 687da8bec47SJoseph Huber static const unsigned DeviceIDArgNum = 1; 688da8bec47SJoseph Huber static const unsigned BasePtrsArgNum = 3; 689da8bec47SJoseph Huber static const unsigned PtrsArgNum = 4; 690da8bec47SJoseph Huber static const unsigned SizesArgNum = 5; 6911d3d9b9cSHamilton Tobon Mosquera 6928931add6SHamilton Tobon Mosquera private: 6938931add6SHamilton Tobon Mosquera /// Traverses the BasicBlock where \p Array is, collecting the stores made to 6948931add6SHamilton Tobon Mosquera /// \p Array, leaving StoredValues with the values stored before the 6958931add6SHamilton Tobon Mosquera /// instruction \p Before is reached. 6968931add6SHamilton Tobon Mosquera bool getValues(AllocaInst &Array, Instruction &Before) { 6978931add6SHamilton Tobon Mosquera // Initialize container. 698d08d490aSJohannes Doerfert const uint64_t NumValues = Array.getAllocatedType()->getArrayNumElements(); 6998931add6SHamilton Tobon Mosquera StoredValues.assign(NumValues, nullptr); 7008931add6SHamilton Tobon Mosquera LastAccesses.assign(NumValues, nullptr); 7018931add6SHamilton Tobon Mosquera 7028931add6SHamilton Tobon Mosquera // TODO: This assumes the instruction \p Before is in the same 7038931add6SHamilton Tobon Mosquera // BasicBlock as Array. Make it general, for any control flow graph. 7048931add6SHamilton Tobon Mosquera BasicBlock *BB = Array.getParent(); 7058931add6SHamilton Tobon Mosquera if (BB != Before.getParent()) 7068931add6SHamilton Tobon Mosquera return false; 7078931add6SHamilton Tobon Mosquera 7088931add6SHamilton Tobon Mosquera const DataLayout &DL = Array.getModule()->getDataLayout(); 7098931add6SHamilton Tobon Mosquera const unsigned int PointerSize = DL.getPointerSize(); 7108931add6SHamilton Tobon Mosquera 7118931add6SHamilton Tobon Mosquera for (Instruction &I : *BB) { 7128931add6SHamilton Tobon Mosquera if (&I == &Before) 7138931add6SHamilton Tobon Mosquera break; 7148931add6SHamilton Tobon Mosquera 7158931add6SHamilton Tobon Mosquera if (!isa<StoreInst>(&I)) 7168931add6SHamilton Tobon Mosquera continue; 7178931add6SHamilton Tobon Mosquera 7188931add6SHamilton Tobon Mosquera auto *S = cast<StoreInst>(&I); 7198931add6SHamilton Tobon Mosquera int64_t Offset = -1; 720d08d490aSJohannes Doerfert auto *Dst = 721d08d490aSJohannes Doerfert GetPointerBaseWithConstantOffset(S->getPointerOperand(), Offset, DL); 7228931add6SHamilton Tobon Mosquera if (Dst == &Array) { 7238931add6SHamilton Tobon Mosquera int64_t Idx = Offset / PointerSize; 7248931add6SHamilton Tobon Mosquera StoredValues[Idx] = getUnderlyingObject(S->getValueOperand()); 7258931add6SHamilton Tobon Mosquera LastAccesses[Idx] = S; 7268931add6SHamilton Tobon Mosquera } 7278931add6SHamilton Tobon Mosquera } 7288931add6SHamilton Tobon Mosquera 7298931add6SHamilton Tobon Mosquera return isFilled(); 7308931add6SHamilton Tobon Mosquera } 7318931add6SHamilton Tobon Mosquera 7328931add6SHamilton Tobon Mosquera /// Returns true if all values in StoredValues and 7338931add6SHamilton Tobon Mosquera /// LastAccesses are not nullptrs. 7348931add6SHamilton Tobon Mosquera bool isFilled() { 7358931add6SHamilton Tobon Mosquera const unsigned NumValues = StoredValues.size(); 7368931add6SHamilton Tobon Mosquera for (unsigned I = 0; I < NumValues; ++I) { 7378931add6SHamilton Tobon Mosquera if (!StoredValues[I] || !LastAccesses[I]) 7388931add6SHamilton Tobon Mosquera return false; 7398931add6SHamilton Tobon Mosquera } 7408931add6SHamilton Tobon Mosquera 7418931add6SHamilton Tobon Mosquera return true; 7428931add6SHamilton Tobon Mosquera } 7438931add6SHamilton Tobon Mosquera }; 7448931add6SHamilton Tobon Mosquera 7457cfd267cSsstefan1 struct OpenMPOpt { 7467cfd267cSsstefan1 7477cfd267cSsstefan1 using OptimizationRemarkGetter = 7487cfd267cSsstefan1 function_ref<OptimizationRemarkEmitter &(Function *)>; 7497cfd267cSsstefan1 7507cfd267cSsstefan1 OpenMPOpt(SmallVectorImpl<Function *> &SCC, CallGraphUpdater &CGUpdater, 7517cfd267cSsstefan1 OptimizationRemarkGetter OREGetter, 752b8235d2bSsstefan1 OMPInformationCache &OMPInfoCache, Attributor &A) 75377b79d79SMehdi Amini : M(*(*SCC.begin())->getParent()), SCC(SCC), CGUpdater(CGUpdater), 754b8235d2bSsstefan1 OREGetter(OREGetter), OMPInfoCache(OMPInfoCache), A(A) {} 7557cfd267cSsstefan1 756a2281419SJoseph Huber /// Check if any remarks are enabled for openmp-opt 757a2281419SJoseph Huber bool remarksEnabled() { 758a2281419SJoseph Huber auto &Ctx = M.getContext(); 759a2281419SJoseph Huber return Ctx.getDiagHandlerPtr()->isAnyRemarkEnabled(DEBUG_TYPE); 760a2281419SJoseph Huber } 761a2281419SJoseph Huber 7629548b74aSJohannes Doerfert /// Run all OpenMP optimizations on the underlying SCC/ModuleSlice. 763b2ad63d3SJoseph Huber bool run(bool IsModulePass) { 76454bd3751SJohannes Doerfert if (SCC.empty()) 76554bd3751SJohannes Doerfert return false; 76654bd3751SJohannes Doerfert 7679548b74aSJohannes Doerfert bool Changed = false; 7689548b74aSJohannes Doerfert 7699548b74aSJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "Run on SCC with " << SCC.size() 77077b79d79SMehdi Amini << " functions in a slice with " 77177b79d79SMehdi Amini << OMPInfoCache.ModuleSlice.size() << " functions\n"); 7729548b74aSJohannes Doerfert 773b2ad63d3SJoseph Huber if (IsModulePass) { 774d9659bf6SJohannes Doerfert Changed |= runAttributor(IsModulePass); 77518283125SJoseph Huber 7766fc51c9fSJoseph Huber // Recollect uses, in case Attributor deleted any. 7776fc51c9fSJoseph Huber OMPInfoCache.recollectUses(); 7786fc51c9fSJoseph Huber 779be2b5696SJohannes Doerfert // TODO: This should be folded into buildCustomStateMachine. 780be2b5696SJohannes Doerfert Changed |= rewriteDeviceCodeStateMachine(); 781be2b5696SJohannes Doerfert 782b2ad63d3SJoseph Huber if (remarksEnabled()) 783b2ad63d3SJoseph Huber analysisGlobalization(); 784b2ad63d3SJoseph Huber } else { 785e8039ad4SJohannes Doerfert if (PrintICVValues) 786e8039ad4SJohannes Doerfert printICVs(); 787e8039ad4SJohannes Doerfert if (PrintOpenMPKernels) 788e8039ad4SJohannes Doerfert printKernels(); 789e8039ad4SJohannes Doerfert 790d9659bf6SJohannes Doerfert Changed |= runAttributor(IsModulePass); 791e8039ad4SJohannes Doerfert 792e8039ad4SJohannes Doerfert // Recollect uses, in case Attributor deleted any. 793e8039ad4SJohannes Doerfert OMPInfoCache.recollectUses(); 794e8039ad4SJohannes Doerfert 795e8039ad4SJohannes Doerfert Changed |= deleteParallelRegions(); 796d9659bf6SJohannes Doerfert 797496f8e5bSHamilton Tobon Mosquera if (HideMemoryTransferLatency) 798496f8e5bSHamilton Tobon Mosquera Changed |= hideMemTransfersLatency(); 7993a6bfcf2SGiorgis Georgakoudis Changed |= deduplicateRuntimeCalls(); 8003a6bfcf2SGiorgis Georgakoudis if (EnableParallelRegionMerging) { 8013a6bfcf2SGiorgis Georgakoudis if (mergeParallelRegions()) { 8023a6bfcf2SGiorgis Georgakoudis deduplicateRuntimeCalls(); 8033a6bfcf2SGiorgis Georgakoudis Changed = true; 8043a6bfcf2SGiorgis Georgakoudis } 8053a6bfcf2SGiorgis Georgakoudis } 806b2ad63d3SJoseph Huber } 807e8039ad4SJohannes Doerfert 808e8039ad4SJohannes Doerfert return Changed; 809e8039ad4SJohannes Doerfert } 810e8039ad4SJohannes Doerfert 8110f426935Ssstefan1 /// Print initial ICV values for testing. 8120f426935Ssstefan1 /// FIXME: This should be done from the Attributor once it is added. 813e8039ad4SJohannes Doerfert void printICVs() const { 814cb9cfa0dSsstefan1 InternalControlVar ICVs[] = {ICV_nthreads, ICV_active_levels, ICV_cancel, 815cb9cfa0dSsstefan1 ICV_proc_bind}; 8160f426935Ssstefan1 8170f426935Ssstefan1 for (Function *F : OMPInfoCache.ModuleSlice) { 8180f426935Ssstefan1 for (auto ICV : ICVs) { 8190f426935Ssstefan1 auto ICVInfo = OMPInfoCache.ICVs[ICV]; 8202db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 8212db182ffSJoseph Huber return ORA << "OpenMP ICV " << ore::NV("OpenMPICV", ICVInfo.Name) 8220f426935Ssstefan1 << " Value: " 8230f426935Ssstefan1 << (ICVInfo.InitValue 82461cdaf66SSimon Pilgrim ? toString(ICVInfo.InitValue->getValue(), 10, true) 8250f426935Ssstefan1 : "IMPLEMENTATION_DEFINED"); 8260f426935Ssstefan1 }; 8270f426935Ssstefan1 8282db182ffSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(F, "OpenMPICVTracker", Remark); 8290f426935Ssstefan1 } 8300f426935Ssstefan1 } 8310f426935Ssstefan1 } 8320f426935Ssstefan1 833e8039ad4SJohannes Doerfert /// Print OpenMP GPU kernels for testing. 834e8039ad4SJohannes Doerfert void printKernels() const { 835e8039ad4SJohannes Doerfert for (Function *F : SCC) { 836e8039ad4SJohannes Doerfert if (!OMPInfoCache.Kernels.count(F)) 837e8039ad4SJohannes Doerfert continue; 838b8235d2bSsstefan1 8392db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 8402db182ffSJoseph Huber return ORA << "OpenMP GPU kernel " 841e8039ad4SJohannes Doerfert << ore::NV("OpenMPGPUKernel", F->getName()) << "\n"; 842e8039ad4SJohannes Doerfert }; 843b8235d2bSsstefan1 8442db182ffSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(F, "OpenMPGPU", Remark); 845e8039ad4SJohannes Doerfert } 8469548b74aSJohannes Doerfert } 8479548b74aSJohannes Doerfert 8487cfd267cSsstefan1 /// Return the call if \p U is a callee use in a regular call. If \p RFI is 8497cfd267cSsstefan1 /// given it has to be the callee or a nullptr is returned. 8507cfd267cSsstefan1 static CallInst *getCallIfRegularCall( 8517cfd267cSsstefan1 Use &U, OMPInformationCache::RuntimeFunctionInfo *RFI = nullptr) { 8527cfd267cSsstefan1 CallInst *CI = dyn_cast<CallInst>(U.getUser()); 8537cfd267cSsstefan1 if (CI && CI->isCallee(&U) && !CI->hasOperandBundles() && 854c4b1fe05SJohannes Doerfert (!RFI || 855c4b1fe05SJohannes Doerfert (RFI->Declaration && CI->getCalledFunction() == RFI->Declaration))) 8567cfd267cSsstefan1 return CI; 8577cfd267cSsstefan1 return nullptr; 8587cfd267cSsstefan1 } 8597cfd267cSsstefan1 8607cfd267cSsstefan1 /// Return the call if \p V is a regular call. If \p RFI is given it has to be 8617cfd267cSsstefan1 /// the callee or a nullptr is returned. 8627cfd267cSsstefan1 static CallInst *getCallIfRegularCall( 8637cfd267cSsstefan1 Value &V, OMPInformationCache::RuntimeFunctionInfo *RFI = nullptr) { 8647cfd267cSsstefan1 CallInst *CI = dyn_cast<CallInst>(&V); 8657cfd267cSsstefan1 if (CI && !CI->hasOperandBundles() && 866c4b1fe05SJohannes Doerfert (!RFI || 867c4b1fe05SJohannes Doerfert (RFI->Declaration && CI->getCalledFunction() == RFI->Declaration))) 8687cfd267cSsstefan1 return CI; 8697cfd267cSsstefan1 return nullptr; 8707cfd267cSsstefan1 } 8717cfd267cSsstefan1 8729548b74aSJohannes Doerfert private: 8733a6bfcf2SGiorgis Georgakoudis /// Merge parallel regions when it is safe. 8743a6bfcf2SGiorgis Georgakoudis bool mergeParallelRegions() { 8753a6bfcf2SGiorgis Georgakoudis const unsigned CallbackCalleeOperand = 2; 8763a6bfcf2SGiorgis Georgakoudis const unsigned CallbackFirstArgOperand = 3; 8773a6bfcf2SGiorgis Georgakoudis using InsertPointTy = OpenMPIRBuilder::InsertPointTy; 8783a6bfcf2SGiorgis Georgakoudis 8793a6bfcf2SGiorgis Georgakoudis // Check if there are any __kmpc_fork_call calls to merge. 8803a6bfcf2SGiorgis Georgakoudis OMPInformationCache::RuntimeFunctionInfo &RFI = 8813a6bfcf2SGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call]; 8823a6bfcf2SGiorgis Georgakoudis 8833a6bfcf2SGiorgis Georgakoudis if (!RFI.Declaration) 8843a6bfcf2SGiorgis Georgakoudis return false; 8853a6bfcf2SGiorgis Georgakoudis 88697517055SGiorgis Georgakoudis // Unmergable calls that prevent merging a parallel region. 88797517055SGiorgis Georgakoudis OMPInformationCache::RuntimeFunctionInfo UnmergableCallsInfo[] = { 88897517055SGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_push_proc_bind], 88997517055SGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_push_num_threads], 89097517055SGiorgis Georgakoudis }; 8913a6bfcf2SGiorgis Georgakoudis 8923a6bfcf2SGiorgis Georgakoudis bool Changed = false; 8933a6bfcf2SGiorgis Georgakoudis LoopInfo *LI = nullptr; 8943a6bfcf2SGiorgis Georgakoudis DominatorTree *DT = nullptr; 8953a6bfcf2SGiorgis Georgakoudis 8963a6bfcf2SGiorgis Georgakoudis SmallDenseMap<BasicBlock *, SmallPtrSet<Instruction *, 4>> BB2PRMap; 8973a6bfcf2SGiorgis Georgakoudis 8983a6bfcf2SGiorgis Georgakoudis BasicBlock *StartBB = nullptr, *EndBB = nullptr; 8993a6bfcf2SGiorgis Georgakoudis auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, 9003a6bfcf2SGiorgis Georgakoudis BasicBlock &ContinuationIP) { 9013a6bfcf2SGiorgis Georgakoudis BasicBlock *CGStartBB = CodeGenIP.getBlock(); 9023a6bfcf2SGiorgis Georgakoudis BasicBlock *CGEndBB = 9033a6bfcf2SGiorgis Georgakoudis SplitBlock(CGStartBB, &*CodeGenIP.getPoint(), DT, LI); 9043a6bfcf2SGiorgis Georgakoudis assert(StartBB != nullptr && "StartBB should not be null"); 9053a6bfcf2SGiorgis Georgakoudis CGStartBB->getTerminator()->setSuccessor(0, StartBB); 9063a6bfcf2SGiorgis Georgakoudis assert(EndBB != nullptr && "EndBB should not be null"); 9073a6bfcf2SGiorgis Georgakoudis EndBB->getTerminator()->setSuccessor(0, CGEndBB); 9083a6bfcf2SGiorgis Georgakoudis }; 9093a6bfcf2SGiorgis Georgakoudis 910240dd924SAlex Zinenko auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &, 911240dd924SAlex Zinenko Value &Inner, Value *&ReplacementValue) -> InsertPointTy { 912240dd924SAlex Zinenko ReplacementValue = &Inner; 9133a6bfcf2SGiorgis Georgakoudis return CodeGenIP; 9143a6bfcf2SGiorgis Georgakoudis }; 9153a6bfcf2SGiorgis Georgakoudis 9163a6bfcf2SGiorgis Georgakoudis auto FiniCB = [&](InsertPointTy CodeGenIP) {}; 9173a6bfcf2SGiorgis Georgakoudis 91897517055SGiorgis Georgakoudis /// Create a sequential execution region within a merged parallel region, 91997517055SGiorgis Georgakoudis /// encapsulated in a master construct with a barrier for synchronization. 92097517055SGiorgis Georgakoudis auto CreateSequentialRegion = [&](Function *OuterFn, 92197517055SGiorgis Georgakoudis BasicBlock *OuterPredBB, 92297517055SGiorgis Georgakoudis Instruction *SeqStartI, 92397517055SGiorgis Georgakoudis Instruction *SeqEndI) { 92497517055SGiorgis Georgakoudis // Isolate the instructions of the sequential region to a separate 92597517055SGiorgis Georgakoudis // block. 92697517055SGiorgis Georgakoudis BasicBlock *ParentBB = SeqStartI->getParent(); 92797517055SGiorgis Georgakoudis BasicBlock *SeqEndBB = 92897517055SGiorgis Georgakoudis SplitBlock(ParentBB, SeqEndI->getNextNode(), DT, LI); 92997517055SGiorgis Georgakoudis BasicBlock *SeqAfterBB = 93097517055SGiorgis Georgakoudis SplitBlock(SeqEndBB, &*SeqEndBB->getFirstInsertionPt(), DT, LI); 93197517055SGiorgis Georgakoudis BasicBlock *SeqStartBB = 93297517055SGiorgis Georgakoudis SplitBlock(ParentBB, SeqStartI, DT, LI, nullptr, "seq.par.merged"); 93397517055SGiorgis Georgakoudis 93497517055SGiorgis Georgakoudis assert(ParentBB->getUniqueSuccessor() == SeqStartBB && 93597517055SGiorgis Georgakoudis "Expected a different CFG"); 93697517055SGiorgis Georgakoudis const DebugLoc DL = ParentBB->getTerminator()->getDebugLoc(); 93797517055SGiorgis Georgakoudis ParentBB->getTerminator()->eraseFromParent(); 93897517055SGiorgis Georgakoudis 93997517055SGiorgis Georgakoudis auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, 94097517055SGiorgis Georgakoudis BasicBlock &ContinuationIP) { 94197517055SGiorgis Georgakoudis BasicBlock *CGStartBB = CodeGenIP.getBlock(); 94297517055SGiorgis Georgakoudis BasicBlock *CGEndBB = 94397517055SGiorgis Georgakoudis SplitBlock(CGStartBB, &*CodeGenIP.getPoint(), DT, LI); 94497517055SGiorgis Georgakoudis assert(SeqStartBB != nullptr && "SeqStartBB should not be null"); 94597517055SGiorgis Georgakoudis CGStartBB->getTerminator()->setSuccessor(0, SeqStartBB); 94697517055SGiorgis Georgakoudis assert(SeqEndBB != nullptr && "SeqEndBB should not be null"); 94797517055SGiorgis Georgakoudis SeqEndBB->getTerminator()->setSuccessor(0, CGEndBB); 94897517055SGiorgis Georgakoudis }; 94997517055SGiorgis Georgakoudis auto FiniCB = [&](InsertPointTy CodeGenIP) {}; 95097517055SGiorgis Georgakoudis 95197517055SGiorgis Georgakoudis // Find outputs from the sequential region to outside users and 95297517055SGiorgis Georgakoudis // broadcast their values to them. 95397517055SGiorgis Georgakoudis for (Instruction &I : *SeqStartBB) { 95497517055SGiorgis Georgakoudis SmallPtrSet<Instruction *, 4> OutsideUsers; 95597517055SGiorgis Georgakoudis for (User *Usr : I.users()) { 95697517055SGiorgis Georgakoudis Instruction &UsrI = *cast<Instruction>(Usr); 95797517055SGiorgis Georgakoudis // Ignore outputs to LT intrinsics, code extraction for the merged 95897517055SGiorgis Georgakoudis // parallel region will fix them. 95997517055SGiorgis Georgakoudis if (UsrI.isLifetimeStartOrEnd()) 96097517055SGiorgis Georgakoudis continue; 96197517055SGiorgis Georgakoudis 96297517055SGiorgis Georgakoudis if (UsrI.getParent() != SeqStartBB) 96397517055SGiorgis Georgakoudis OutsideUsers.insert(&UsrI); 96497517055SGiorgis Georgakoudis } 96597517055SGiorgis Georgakoudis 96697517055SGiorgis Georgakoudis if (OutsideUsers.empty()) 96797517055SGiorgis Georgakoudis continue; 96897517055SGiorgis Georgakoudis 96997517055SGiorgis Georgakoudis // Emit an alloca in the outer region to store the broadcasted 97097517055SGiorgis Georgakoudis // value. 97197517055SGiorgis Georgakoudis const DataLayout &DL = M.getDataLayout(); 97297517055SGiorgis Georgakoudis AllocaInst *AllocaI = new AllocaInst( 97397517055SGiorgis Georgakoudis I.getType(), DL.getAllocaAddrSpace(), nullptr, 97497517055SGiorgis Georgakoudis I.getName() + ".seq.output.alloc", &OuterFn->front().front()); 97597517055SGiorgis Georgakoudis 97697517055SGiorgis Georgakoudis // Emit a store instruction in the sequential BB to update the 97797517055SGiorgis Georgakoudis // value. 97897517055SGiorgis Georgakoudis new StoreInst(&I, AllocaI, SeqStartBB->getTerminator()); 97997517055SGiorgis Georgakoudis 98097517055SGiorgis Georgakoudis // Emit a load instruction and replace the use of the output value 98197517055SGiorgis Georgakoudis // with it. 98297517055SGiorgis Georgakoudis for (Instruction *UsrI : OutsideUsers) { 9835b70c12fSJohannes Doerfert LoadInst *LoadI = new LoadInst( 9845b70c12fSJohannes Doerfert I.getType(), AllocaI, I.getName() + ".seq.output.load", UsrI); 98597517055SGiorgis Georgakoudis UsrI->replaceUsesOfWith(&I, LoadI); 98697517055SGiorgis Georgakoudis } 98797517055SGiorgis Georgakoudis } 98897517055SGiorgis Georgakoudis 98997517055SGiorgis Georgakoudis OpenMPIRBuilder::LocationDescription Loc( 99097517055SGiorgis Georgakoudis InsertPointTy(ParentBB, ParentBB->end()), DL); 99197517055SGiorgis Georgakoudis InsertPointTy SeqAfterIP = 99297517055SGiorgis Georgakoudis OMPInfoCache.OMPBuilder.createMaster(Loc, BodyGenCB, FiniCB); 99397517055SGiorgis Georgakoudis 99497517055SGiorgis Georgakoudis OMPInfoCache.OMPBuilder.createBarrier(SeqAfterIP, OMPD_parallel); 99597517055SGiorgis Georgakoudis 99697517055SGiorgis Georgakoudis BranchInst::Create(SeqAfterBB, SeqAfterIP.getBlock()); 99797517055SGiorgis Georgakoudis 99897517055SGiorgis Georgakoudis LLVM_DEBUG(dbgs() << TAG << "After sequential inlining " << *OuterFn 99997517055SGiorgis Georgakoudis << "\n"); 100097517055SGiorgis Georgakoudis }; 100197517055SGiorgis Georgakoudis 10023a6bfcf2SGiorgis Georgakoudis // Helper to merge the __kmpc_fork_call calls in MergableCIs. They are all 10033a6bfcf2SGiorgis Georgakoudis // contained in BB and only separated by instructions that can be 10043a6bfcf2SGiorgis Georgakoudis // redundantly executed in parallel. The block BB is split before the first 10053a6bfcf2SGiorgis Georgakoudis // call (in MergableCIs) and after the last so the entire region we merge 10063a6bfcf2SGiorgis Georgakoudis // into a single parallel region is contained in a single basic block 10073a6bfcf2SGiorgis Georgakoudis // without any other instructions. We use the OpenMPIRBuilder to outline 10083a6bfcf2SGiorgis Georgakoudis // that block and call the resulting function via __kmpc_fork_call. 10093a6bfcf2SGiorgis Georgakoudis auto Merge = [&](SmallVectorImpl<CallInst *> &MergableCIs, BasicBlock *BB) { 10103a6bfcf2SGiorgis Georgakoudis // TODO: Change the interface to allow single CIs expanded, e.g, to 10113a6bfcf2SGiorgis Georgakoudis // include an outer loop. 10123a6bfcf2SGiorgis Georgakoudis assert(MergableCIs.size() > 1 && "Assumed multiple mergable CIs"); 10133a6bfcf2SGiorgis Georgakoudis 10143a6bfcf2SGiorgis Georgakoudis auto Remark = [&](OptimizationRemark OR) { 1015eef6601bSJoseph Huber OR << "Parallel region merged with parallel region" 1016eef6601bSJoseph Huber << (MergableCIs.size() > 2 ? "s" : "") << " at "; 101723b0ab2aSKazu Hirata for (auto *CI : llvm::drop_begin(MergableCIs)) { 10183a6bfcf2SGiorgis Georgakoudis OR << ore::NV("OpenMPParallelMerge", CI->getDebugLoc()); 10193a6bfcf2SGiorgis Georgakoudis if (CI != MergableCIs.back()) 10203a6bfcf2SGiorgis Georgakoudis OR << ", "; 10213a6bfcf2SGiorgis Georgakoudis } 1022eef6601bSJoseph Huber return OR << "."; 10233a6bfcf2SGiorgis Georgakoudis }; 10243a6bfcf2SGiorgis Georgakoudis 10252c31d5ebSJoseph Huber emitRemark<OptimizationRemark>(MergableCIs.front(), "OMP150", Remark); 10263a6bfcf2SGiorgis Georgakoudis 10273a6bfcf2SGiorgis Georgakoudis Function *OriginalFn = BB->getParent(); 10283a6bfcf2SGiorgis Georgakoudis LLVM_DEBUG(dbgs() << TAG << "Merge " << MergableCIs.size() 10293a6bfcf2SGiorgis Georgakoudis << " parallel regions in " << OriginalFn->getName() 10303a6bfcf2SGiorgis Georgakoudis << "\n"); 10313a6bfcf2SGiorgis Georgakoudis 10323a6bfcf2SGiorgis Georgakoudis // Isolate the calls to merge in a separate block. 10333a6bfcf2SGiorgis Georgakoudis EndBB = SplitBlock(BB, MergableCIs.back()->getNextNode(), DT, LI); 10343a6bfcf2SGiorgis Georgakoudis BasicBlock *AfterBB = 10353a6bfcf2SGiorgis Georgakoudis SplitBlock(EndBB, &*EndBB->getFirstInsertionPt(), DT, LI); 10363a6bfcf2SGiorgis Georgakoudis StartBB = SplitBlock(BB, MergableCIs.front(), DT, LI, nullptr, 10373a6bfcf2SGiorgis Georgakoudis "omp.par.merged"); 10383a6bfcf2SGiorgis Georgakoudis 10393a6bfcf2SGiorgis Georgakoudis assert(BB->getUniqueSuccessor() == StartBB && "Expected a different CFG"); 10403a6bfcf2SGiorgis Georgakoudis const DebugLoc DL = BB->getTerminator()->getDebugLoc(); 10413a6bfcf2SGiorgis Georgakoudis BB->getTerminator()->eraseFromParent(); 10423a6bfcf2SGiorgis Georgakoudis 104397517055SGiorgis Georgakoudis // Create sequential regions for sequential instructions that are 104497517055SGiorgis Georgakoudis // in-between mergable parallel regions. 104597517055SGiorgis Georgakoudis for (auto *It = MergableCIs.begin(), *End = MergableCIs.end() - 1; 104697517055SGiorgis Georgakoudis It != End; ++It) { 104797517055SGiorgis Georgakoudis Instruction *ForkCI = *It; 104897517055SGiorgis Georgakoudis Instruction *NextForkCI = *(It + 1); 104997517055SGiorgis Georgakoudis 105097517055SGiorgis Georgakoudis // Continue if there are not in-between instructions. 105197517055SGiorgis Georgakoudis if (ForkCI->getNextNode() == NextForkCI) 105297517055SGiorgis Georgakoudis continue; 105397517055SGiorgis Georgakoudis 105497517055SGiorgis Georgakoudis CreateSequentialRegion(OriginalFn, BB, ForkCI->getNextNode(), 105597517055SGiorgis Georgakoudis NextForkCI->getPrevNode()); 105697517055SGiorgis Georgakoudis } 105797517055SGiorgis Georgakoudis 10583a6bfcf2SGiorgis Georgakoudis OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BB, BB->end()), 10593a6bfcf2SGiorgis Georgakoudis DL); 10603a6bfcf2SGiorgis Georgakoudis IRBuilder<>::InsertPoint AllocaIP( 10613a6bfcf2SGiorgis Georgakoudis &OriginalFn->getEntryBlock(), 10623a6bfcf2SGiorgis Georgakoudis OriginalFn->getEntryBlock().getFirstInsertionPt()); 10633a6bfcf2SGiorgis Georgakoudis // Create the merged parallel region with default proc binding, to 10643a6bfcf2SGiorgis Georgakoudis // avoid overriding binding settings, and without explicit cancellation. 1065e5dba2d7SMichael Kruse InsertPointTy AfterIP = OMPInfoCache.OMPBuilder.createParallel( 10663a6bfcf2SGiorgis Georgakoudis Loc, AllocaIP, BodyGenCB, PrivCB, FiniCB, nullptr, nullptr, 10673a6bfcf2SGiorgis Georgakoudis OMP_PROC_BIND_default, /* IsCancellable */ false); 10683a6bfcf2SGiorgis Georgakoudis BranchInst::Create(AfterBB, AfterIP.getBlock()); 10693a6bfcf2SGiorgis Georgakoudis 10703a6bfcf2SGiorgis Georgakoudis // Perform the actual outlining. 1071b1191206SMichael Kruse OMPInfoCache.OMPBuilder.finalize(OriginalFn, 1072b1191206SMichael Kruse /* AllowExtractorSinking */ true); 10733a6bfcf2SGiorgis Georgakoudis 10743a6bfcf2SGiorgis Georgakoudis Function *OutlinedFn = MergableCIs.front()->getCaller(); 10753a6bfcf2SGiorgis Georgakoudis 10763a6bfcf2SGiorgis Georgakoudis // Replace the __kmpc_fork_call calls with direct calls to the outlined 10773a6bfcf2SGiorgis Georgakoudis // callbacks. 10783a6bfcf2SGiorgis Georgakoudis SmallVector<Value *, 8> Args; 10793a6bfcf2SGiorgis Georgakoudis for (auto *CI : MergableCIs) { 10803a6bfcf2SGiorgis Georgakoudis Value *Callee = 10813a6bfcf2SGiorgis Georgakoudis CI->getArgOperand(CallbackCalleeOperand)->stripPointerCasts(); 10823a6bfcf2SGiorgis Georgakoudis FunctionType *FT = 10833a6bfcf2SGiorgis Georgakoudis cast<FunctionType>(Callee->getType()->getPointerElementType()); 10843a6bfcf2SGiorgis Georgakoudis Args.clear(); 10853a6bfcf2SGiorgis Georgakoudis Args.push_back(OutlinedFn->getArg(0)); 10863a6bfcf2SGiorgis Georgakoudis Args.push_back(OutlinedFn->getArg(1)); 10874f0225f6SKazu Hirata for (unsigned U = CallbackFirstArgOperand, E = CI->arg_size(); U < E; 10884f0225f6SKazu Hirata ++U) 10893a6bfcf2SGiorgis Georgakoudis Args.push_back(CI->getArgOperand(U)); 10903a6bfcf2SGiorgis Georgakoudis 10913a6bfcf2SGiorgis Georgakoudis CallInst *NewCI = CallInst::Create(FT, Callee, Args, "", CI); 10923a6bfcf2SGiorgis Georgakoudis if (CI->getDebugLoc()) 10933a6bfcf2SGiorgis Georgakoudis NewCI->setDebugLoc(CI->getDebugLoc()); 10943a6bfcf2SGiorgis Georgakoudis 10953a6bfcf2SGiorgis Georgakoudis // Forward parameter attributes from the callback to the callee. 10964f0225f6SKazu Hirata for (unsigned U = CallbackFirstArgOperand, E = CI->arg_size(); U < E; 10974f0225f6SKazu Hirata ++U) 109880ea2bb5SArthur Eubanks for (const Attribute &A : CI->getAttributes().getParamAttrs(U)) 10993a6bfcf2SGiorgis Georgakoudis NewCI->addParamAttr( 11003a6bfcf2SGiorgis Georgakoudis U - (CallbackFirstArgOperand - CallbackCalleeOperand), A); 11013a6bfcf2SGiorgis Georgakoudis 11023a6bfcf2SGiorgis Georgakoudis // Emit an explicit barrier to replace the implicit fork-join barrier. 11033a6bfcf2SGiorgis Georgakoudis if (CI != MergableCIs.back()) { 11043a6bfcf2SGiorgis Georgakoudis // TODO: Remove barrier if the merged parallel region includes the 11053a6bfcf2SGiorgis Georgakoudis // 'nowait' clause. 1106e5dba2d7SMichael Kruse OMPInfoCache.OMPBuilder.createBarrier( 11073a6bfcf2SGiorgis Georgakoudis InsertPointTy(NewCI->getParent(), 11083a6bfcf2SGiorgis Georgakoudis NewCI->getNextNode()->getIterator()), 11093a6bfcf2SGiorgis Georgakoudis OMPD_parallel); 11103a6bfcf2SGiorgis Georgakoudis } 11113a6bfcf2SGiorgis Georgakoudis 11123a6bfcf2SGiorgis Georgakoudis CI->eraseFromParent(); 11133a6bfcf2SGiorgis Georgakoudis } 11143a6bfcf2SGiorgis Georgakoudis 11153a6bfcf2SGiorgis Georgakoudis assert(OutlinedFn != OriginalFn && "Outlining failed"); 11167fea561eSArthur Eubanks CGUpdater.registerOutlinedFunction(*OriginalFn, *OutlinedFn); 11173a6bfcf2SGiorgis Georgakoudis CGUpdater.reanalyzeFunction(*OriginalFn); 11183a6bfcf2SGiorgis Georgakoudis 11193a6bfcf2SGiorgis Georgakoudis NumOpenMPParallelRegionsMerged += MergableCIs.size(); 11203a6bfcf2SGiorgis Georgakoudis 11213a6bfcf2SGiorgis Georgakoudis return true; 11223a6bfcf2SGiorgis Georgakoudis }; 11233a6bfcf2SGiorgis Georgakoudis 11243a6bfcf2SGiorgis Georgakoudis // Helper function that identifes sequences of 11253a6bfcf2SGiorgis Georgakoudis // __kmpc_fork_call uses in a basic block. 11263a6bfcf2SGiorgis Georgakoudis auto DetectPRsCB = [&](Use &U, Function &F) { 11273a6bfcf2SGiorgis Georgakoudis CallInst *CI = getCallIfRegularCall(U, &RFI); 11283a6bfcf2SGiorgis Georgakoudis BB2PRMap[CI->getParent()].insert(CI); 11293a6bfcf2SGiorgis Georgakoudis 11303a6bfcf2SGiorgis Georgakoudis return false; 11313a6bfcf2SGiorgis Georgakoudis }; 11323a6bfcf2SGiorgis Georgakoudis 11333a6bfcf2SGiorgis Georgakoudis BB2PRMap.clear(); 11343a6bfcf2SGiorgis Georgakoudis RFI.foreachUse(SCC, DetectPRsCB); 11353a6bfcf2SGiorgis Georgakoudis SmallVector<SmallVector<CallInst *, 4>, 4> MergableCIsVector; 11363a6bfcf2SGiorgis Georgakoudis // Find mergable parallel regions within a basic block that are 11373a6bfcf2SGiorgis Georgakoudis // safe to merge, that is any in-between instructions can safely 11383a6bfcf2SGiorgis Georgakoudis // execute in parallel after merging. 11393a6bfcf2SGiorgis Georgakoudis // TODO: support merging across basic-blocks. 11403a6bfcf2SGiorgis Georgakoudis for (auto &It : BB2PRMap) { 11413a6bfcf2SGiorgis Georgakoudis auto &CIs = It.getSecond(); 11423a6bfcf2SGiorgis Georgakoudis if (CIs.size() < 2) 11433a6bfcf2SGiorgis Georgakoudis continue; 11443a6bfcf2SGiorgis Georgakoudis 11453a6bfcf2SGiorgis Georgakoudis BasicBlock *BB = It.getFirst(); 11463a6bfcf2SGiorgis Georgakoudis SmallVector<CallInst *, 4> MergableCIs; 11473a6bfcf2SGiorgis Georgakoudis 114897517055SGiorgis Georgakoudis /// Returns true if the instruction is mergable, false otherwise. 114997517055SGiorgis Georgakoudis /// A terminator instruction is unmergable by definition since merging 115097517055SGiorgis Georgakoudis /// works within a BB. Instructions before the mergable region are 115197517055SGiorgis Georgakoudis /// mergable if they are not calls to OpenMP runtime functions that may 115297517055SGiorgis Georgakoudis /// set different execution parameters for subsequent parallel regions. 115397517055SGiorgis Georgakoudis /// Instructions in-between parallel regions are mergable if they are not 115497517055SGiorgis Georgakoudis /// calls to any non-intrinsic function since that may call a non-mergable 115597517055SGiorgis Georgakoudis /// OpenMP runtime function. 115697517055SGiorgis Georgakoudis auto IsMergable = [&](Instruction &I, bool IsBeforeMergableRegion) { 115797517055SGiorgis Georgakoudis // We do not merge across BBs, hence return false (unmergable) if the 115897517055SGiorgis Georgakoudis // instruction is a terminator. 115997517055SGiorgis Georgakoudis if (I.isTerminator()) 116097517055SGiorgis Georgakoudis return false; 116197517055SGiorgis Georgakoudis 116297517055SGiorgis Georgakoudis if (!isa<CallInst>(&I)) 116397517055SGiorgis Georgakoudis return true; 116497517055SGiorgis Georgakoudis 116597517055SGiorgis Georgakoudis CallInst *CI = cast<CallInst>(&I); 116697517055SGiorgis Georgakoudis if (IsBeforeMergableRegion) { 116797517055SGiorgis Georgakoudis Function *CalledFunction = CI->getCalledFunction(); 116897517055SGiorgis Georgakoudis if (!CalledFunction) 116997517055SGiorgis Georgakoudis return false; 117097517055SGiorgis Georgakoudis // Return false (unmergable) if the call before the parallel 117197517055SGiorgis Georgakoudis // region calls an explicit affinity (proc_bind) or number of 117297517055SGiorgis Georgakoudis // threads (num_threads) compiler-generated function. Those settings 117397517055SGiorgis Georgakoudis // may be incompatible with following parallel regions. 117497517055SGiorgis Georgakoudis // TODO: ICV tracking to detect compatibility. 117597517055SGiorgis Georgakoudis for (const auto &RFI : UnmergableCallsInfo) { 117697517055SGiorgis Georgakoudis if (CalledFunction == RFI.Declaration) 117797517055SGiorgis Georgakoudis return false; 117897517055SGiorgis Georgakoudis } 117997517055SGiorgis Georgakoudis } else { 118097517055SGiorgis Georgakoudis // Return false (unmergable) if there is a call instruction 118197517055SGiorgis Georgakoudis // in-between parallel regions when it is not an intrinsic. It 118297517055SGiorgis Georgakoudis // may call an unmergable OpenMP runtime function in its callpath. 118397517055SGiorgis Georgakoudis // TODO: Keep track of possible OpenMP calls in the callpath. 118497517055SGiorgis Georgakoudis if (!isa<IntrinsicInst>(CI)) 118597517055SGiorgis Georgakoudis return false; 118697517055SGiorgis Georgakoudis } 118797517055SGiorgis Georgakoudis 118897517055SGiorgis Georgakoudis return true; 118997517055SGiorgis Georgakoudis }; 11903a6bfcf2SGiorgis Georgakoudis // Find maximal number of parallel region CIs that are safe to merge. 119197517055SGiorgis Georgakoudis for (auto It = BB->begin(), End = BB->end(); It != End;) { 119297517055SGiorgis Georgakoudis Instruction &I = *It; 119397517055SGiorgis Georgakoudis ++It; 119497517055SGiorgis Georgakoudis 11953a6bfcf2SGiorgis Georgakoudis if (CIs.count(&I)) { 11963a6bfcf2SGiorgis Georgakoudis MergableCIs.push_back(cast<CallInst>(&I)); 11973a6bfcf2SGiorgis Georgakoudis continue; 11983a6bfcf2SGiorgis Georgakoudis } 11993a6bfcf2SGiorgis Georgakoudis 120097517055SGiorgis Georgakoudis // Continue expanding if the instruction is mergable. 120197517055SGiorgis Georgakoudis if (IsMergable(I, MergableCIs.empty())) 12023a6bfcf2SGiorgis Georgakoudis continue; 12033a6bfcf2SGiorgis Georgakoudis 120497517055SGiorgis Georgakoudis // Forward the instruction iterator to skip the next parallel region 120597517055SGiorgis Georgakoudis // since there is an unmergable instruction which can affect it. 120697517055SGiorgis Georgakoudis for (; It != End; ++It) { 120797517055SGiorgis Georgakoudis Instruction &SkipI = *It; 120897517055SGiorgis Georgakoudis if (CIs.count(&SkipI)) { 120997517055SGiorgis Georgakoudis LLVM_DEBUG(dbgs() << TAG << "Skip parallel region " << SkipI 121097517055SGiorgis Georgakoudis << " due to " << I << "\n"); 121197517055SGiorgis Georgakoudis ++It; 121297517055SGiorgis Georgakoudis break; 121397517055SGiorgis Georgakoudis } 121497517055SGiorgis Georgakoudis } 121597517055SGiorgis Georgakoudis 121697517055SGiorgis Georgakoudis // Store mergable regions found. 12173a6bfcf2SGiorgis Georgakoudis if (MergableCIs.size() > 1) { 12183a6bfcf2SGiorgis Georgakoudis MergableCIsVector.push_back(MergableCIs); 12193a6bfcf2SGiorgis Georgakoudis LLVM_DEBUG(dbgs() << TAG << "Found " << MergableCIs.size() 12203a6bfcf2SGiorgis Georgakoudis << " parallel regions in block " << BB->getName() 12213a6bfcf2SGiorgis Georgakoudis << " of function " << BB->getParent()->getName() 12223a6bfcf2SGiorgis Georgakoudis << "\n";); 12233a6bfcf2SGiorgis Georgakoudis } 12243a6bfcf2SGiorgis Georgakoudis 12253a6bfcf2SGiorgis Georgakoudis MergableCIs.clear(); 12263a6bfcf2SGiorgis Georgakoudis } 12273a6bfcf2SGiorgis Georgakoudis 12283a6bfcf2SGiorgis Georgakoudis if (!MergableCIsVector.empty()) { 12293a6bfcf2SGiorgis Georgakoudis Changed = true; 12303a6bfcf2SGiorgis Georgakoudis 12313a6bfcf2SGiorgis Georgakoudis for (auto &MergableCIs : MergableCIsVector) 12323a6bfcf2SGiorgis Georgakoudis Merge(MergableCIs, BB); 1233b2ad63d3SJoseph Huber MergableCIsVector.clear(); 12343a6bfcf2SGiorgis Georgakoudis } 12353a6bfcf2SGiorgis Georgakoudis } 12363a6bfcf2SGiorgis Georgakoudis 12373a6bfcf2SGiorgis Georgakoudis if (Changed) { 123897517055SGiorgis Georgakoudis /// Re-collect use for fork calls, emitted barrier calls, and 123997517055SGiorgis Georgakoudis /// any emitted master/end_master calls. 124097517055SGiorgis Georgakoudis OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_fork_call); 124197517055SGiorgis Georgakoudis OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_barrier); 124297517055SGiorgis Georgakoudis OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_master); 124397517055SGiorgis Georgakoudis OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_end_master); 12443a6bfcf2SGiorgis Georgakoudis } 12453a6bfcf2SGiorgis Georgakoudis 12463a6bfcf2SGiorgis Georgakoudis return Changed; 12473a6bfcf2SGiorgis Georgakoudis } 12483a6bfcf2SGiorgis Georgakoudis 12499d38f98dSJohannes Doerfert /// Try to delete parallel regions if possible. 1250e565db49SJohannes Doerfert bool deleteParallelRegions() { 1251e565db49SJohannes Doerfert const unsigned CallbackCalleeOperand = 2; 1252e565db49SJohannes Doerfert 12537cfd267cSsstefan1 OMPInformationCache::RuntimeFunctionInfo &RFI = 12547cfd267cSsstefan1 OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call]; 12557cfd267cSsstefan1 1256e565db49SJohannes Doerfert if (!RFI.Declaration) 1257e565db49SJohannes Doerfert return false; 1258e565db49SJohannes Doerfert 1259e565db49SJohannes Doerfert bool Changed = false; 1260e565db49SJohannes Doerfert auto DeleteCallCB = [&](Use &U, Function &) { 1261e565db49SJohannes Doerfert CallInst *CI = getCallIfRegularCall(U); 1262e565db49SJohannes Doerfert if (!CI) 1263e565db49SJohannes Doerfert return false; 1264e565db49SJohannes Doerfert auto *Fn = dyn_cast<Function>( 1265e565db49SJohannes Doerfert CI->getArgOperand(CallbackCalleeOperand)->stripPointerCasts()); 1266e565db49SJohannes Doerfert if (!Fn) 1267e565db49SJohannes Doerfert return false; 1268e565db49SJohannes Doerfert if (!Fn->onlyReadsMemory()) 1269e565db49SJohannes Doerfert return false; 1270e565db49SJohannes Doerfert if (!Fn->hasFnAttribute(Attribute::WillReturn)) 1271e565db49SJohannes Doerfert return false; 1272e565db49SJohannes Doerfert 1273e565db49SJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "Delete read-only parallel region in " 1274e565db49SJohannes Doerfert << CI->getCaller()->getName() << "\n"); 12754d4ea9acSHuber, Joseph 12764d4ea9acSHuber, Joseph auto Remark = [&](OptimizationRemark OR) { 1277eef6601bSJoseph Huber return OR << "Removing parallel region with no side-effects."; 12784d4ea9acSHuber, Joseph }; 12792c31d5ebSJoseph Huber emitRemark<OptimizationRemark>(CI, "OMP160", Remark); 12804d4ea9acSHuber, Joseph 1281e565db49SJohannes Doerfert CGUpdater.removeCallSite(*CI); 1282e565db49SJohannes Doerfert CI->eraseFromParent(); 1283e565db49SJohannes Doerfert Changed = true; 128455eb714aSRoman Lebedev ++NumOpenMPParallelRegionsDeleted; 1285e565db49SJohannes Doerfert return true; 1286e565db49SJohannes Doerfert }; 1287e565db49SJohannes Doerfert 1288624d34afSJohannes Doerfert RFI.foreachUse(SCC, DeleteCallCB); 1289e565db49SJohannes Doerfert 1290e565db49SJohannes Doerfert return Changed; 1291e565db49SJohannes Doerfert } 1292e565db49SJohannes Doerfert 1293b726c557SJohannes Doerfert /// Try to eliminate runtime calls by reusing existing ones. 12949548b74aSJohannes Doerfert bool deduplicateRuntimeCalls() { 12959548b74aSJohannes Doerfert bool Changed = false; 12969548b74aSJohannes Doerfert 1297e28936f6SJohannes Doerfert RuntimeFunction DeduplicableRuntimeCallIDs[] = { 1298e28936f6SJohannes Doerfert OMPRTL_omp_get_num_threads, 1299e28936f6SJohannes Doerfert OMPRTL_omp_in_parallel, 1300e28936f6SJohannes Doerfert OMPRTL_omp_get_cancellation, 1301e28936f6SJohannes Doerfert OMPRTL_omp_get_thread_limit, 1302e28936f6SJohannes Doerfert OMPRTL_omp_get_supported_active_levels, 1303e28936f6SJohannes Doerfert OMPRTL_omp_get_level, 1304e28936f6SJohannes Doerfert OMPRTL_omp_get_ancestor_thread_num, 1305e28936f6SJohannes Doerfert OMPRTL_omp_get_team_size, 1306e28936f6SJohannes Doerfert OMPRTL_omp_get_active_level, 1307e28936f6SJohannes Doerfert OMPRTL_omp_in_final, 1308e28936f6SJohannes Doerfert OMPRTL_omp_get_proc_bind, 1309e28936f6SJohannes Doerfert OMPRTL_omp_get_num_places, 1310e28936f6SJohannes Doerfert OMPRTL_omp_get_num_procs, 1311e28936f6SJohannes Doerfert OMPRTL_omp_get_place_num, 1312e28936f6SJohannes Doerfert OMPRTL_omp_get_partition_num_places, 1313e28936f6SJohannes Doerfert OMPRTL_omp_get_partition_place_nums}; 1314e28936f6SJohannes Doerfert 1315bc93c2d7SMarek Kurdej // Global-tid is handled separately. 13169548b74aSJohannes Doerfert SmallSetVector<Value *, 16> GTIdArgs; 13179548b74aSJohannes Doerfert collectGlobalThreadIdArguments(GTIdArgs); 13189548b74aSJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "Found " << GTIdArgs.size() 13199548b74aSJohannes Doerfert << " global thread ID arguments\n"); 13209548b74aSJohannes Doerfert 13219548b74aSJohannes Doerfert for (Function *F : SCC) { 1322e28936f6SJohannes Doerfert for (auto DeduplicableRuntimeCallID : DeduplicableRuntimeCallIDs) 13234e29d256Sserge-sans-paille Changed |= deduplicateRuntimeCalls( 13244e29d256Sserge-sans-paille *F, OMPInfoCache.RFIs[DeduplicableRuntimeCallID]); 1325e28936f6SJohannes Doerfert 1326e28936f6SJohannes Doerfert // __kmpc_global_thread_num is special as we can replace it with an 1327e28936f6SJohannes Doerfert // argument in enough cases to make it worth trying. 13289548b74aSJohannes Doerfert Value *GTIdArg = nullptr; 13299548b74aSJohannes Doerfert for (Argument &Arg : F->args()) 13309548b74aSJohannes Doerfert if (GTIdArgs.count(&Arg)) { 13319548b74aSJohannes Doerfert GTIdArg = &Arg; 13329548b74aSJohannes Doerfert break; 13339548b74aSJohannes Doerfert } 13349548b74aSJohannes Doerfert Changed |= deduplicateRuntimeCalls( 13357cfd267cSsstefan1 *F, OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num], GTIdArg); 13369548b74aSJohannes Doerfert } 13379548b74aSJohannes Doerfert 13389548b74aSJohannes Doerfert return Changed; 13399548b74aSJohannes Doerfert } 13409548b74aSJohannes Doerfert 1341496f8e5bSHamilton Tobon Mosquera /// Tries to hide the latency of runtime calls that involve host to 1342496f8e5bSHamilton Tobon Mosquera /// device memory transfers by splitting them into their "issue" and "wait" 1343496f8e5bSHamilton Tobon Mosquera /// versions. The "issue" is moved upwards as much as possible. The "wait" is 1344496f8e5bSHamilton Tobon Mosquera /// moved downards as much as possible. The "issue" issues the memory transfer 1345496f8e5bSHamilton Tobon Mosquera /// asynchronously, returning a handle. The "wait" waits in the returned 1346496f8e5bSHamilton Tobon Mosquera /// handle for the memory transfer to finish. 1347496f8e5bSHamilton Tobon Mosquera bool hideMemTransfersLatency() { 1348496f8e5bSHamilton Tobon Mosquera auto &RFI = OMPInfoCache.RFIs[OMPRTL___tgt_target_data_begin_mapper]; 1349496f8e5bSHamilton Tobon Mosquera bool Changed = false; 1350496f8e5bSHamilton Tobon Mosquera auto SplitMemTransfers = [&](Use &U, Function &Decl) { 1351496f8e5bSHamilton Tobon Mosquera auto *RTCall = getCallIfRegularCall(U, &RFI); 1352496f8e5bSHamilton Tobon Mosquera if (!RTCall) 1353496f8e5bSHamilton Tobon Mosquera return false; 1354496f8e5bSHamilton Tobon Mosquera 13558931add6SHamilton Tobon Mosquera OffloadArray OffloadArrays[3]; 13568931add6SHamilton Tobon Mosquera if (!getValuesInOffloadArrays(*RTCall, OffloadArrays)) 13578931add6SHamilton Tobon Mosquera return false; 13588931add6SHamilton Tobon Mosquera 13598931add6SHamilton Tobon Mosquera LLVM_DEBUG(dumpValuesInOffloadArrays(OffloadArrays)); 13608931add6SHamilton Tobon Mosquera 1361bd2fa181SHamilton Tobon Mosquera // TODO: Check if can be moved upwards. 1362bd2fa181SHamilton Tobon Mosquera bool WasSplit = false; 1363bd2fa181SHamilton Tobon Mosquera Instruction *WaitMovementPoint = canBeMovedDownwards(*RTCall); 1364bd2fa181SHamilton Tobon Mosquera if (WaitMovementPoint) 1365bd2fa181SHamilton Tobon Mosquera WasSplit = splitTargetDataBeginRTC(*RTCall, *WaitMovementPoint); 1366bd2fa181SHamilton Tobon Mosquera 1367496f8e5bSHamilton Tobon Mosquera Changed |= WasSplit; 1368496f8e5bSHamilton Tobon Mosquera return WasSplit; 1369496f8e5bSHamilton Tobon Mosquera }; 1370496f8e5bSHamilton Tobon Mosquera RFI.foreachUse(SCC, SplitMemTransfers); 1371496f8e5bSHamilton Tobon Mosquera 1372496f8e5bSHamilton Tobon Mosquera return Changed; 1373496f8e5bSHamilton Tobon Mosquera } 1374496f8e5bSHamilton Tobon Mosquera 1375a2281419SJoseph Huber void analysisGlobalization() { 13766fc51c9fSJoseph Huber auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared]; 137782453e75SJoseph Huber 137882453e75SJoseph Huber auto CheckGlobalization = [&](Use &U, Function &Decl) { 1379a2281419SJoseph Huber if (CallInst *CI = getCallIfRegularCall(U, &RFI)) { 138044feacc7SJoseph Huber auto Remark = [&](OptimizationRemarkMissed ORM) { 138144feacc7SJoseph Huber return ORM 1382a2281419SJoseph Huber << "Found thread data sharing on the GPU. " 1383a2281419SJoseph Huber << "Expect degraded performance due to data globalization."; 1384a2281419SJoseph Huber }; 13852c31d5ebSJoseph Huber emitRemark<OptimizationRemarkMissed>(CI, "OMP112", Remark); 1386a2281419SJoseph Huber } 1387a2281419SJoseph Huber 1388a2281419SJoseph Huber return false; 1389a2281419SJoseph Huber }; 1390a2281419SJoseph Huber 139182453e75SJoseph Huber RFI.foreachUse(SCC, CheckGlobalization); 139282453e75SJoseph Huber } 1393a2281419SJoseph Huber 13948931add6SHamilton Tobon Mosquera /// Maps the values stored in the offload arrays passed as arguments to 13958931add6SHamilton Tobon Mosquera /// \p RuntimeCall into the offload arrays in \p OAs. 13968931add6SHamilton Tobon Mosquera bool getValuesInOffloadArrays(CallInst &RuntimeCall, 13978931add6SHamilton Tobon Mosquera MutableArrayRef<OffloadArray> OAs) { 13988931add6SHamilton Tobon Mosquera assert(OAs.size() == 3 && "Need space for three offload arrays!"); 13998931add6SHamilton Tobon Mosquera 14008931add6SHamilton Tobon Mosquera // A runtime call that involves memory offloading looks something like: 14018931add6SHamilton Tobon Mosquera // call void @__tgt_target_data_begin_mapper(arg0, arg1, 14028931add6SHamilton Tobon Mosquera // i8** %offload_baseptrs, i8** %offload_ptrs, i64* %offload_sizes, 14038931add6SHamilton Tobon Mosquera // ...) 14048931add6SHamilton Tobon Mosquera // So, the idea is to access the allocas that allocate space for these 14058931add6SHamilton Tobon Mosquera // offload arrays, offload_baseptrs, offload_ptrs, offload_sizes. 14068931add6SHamilton Tobon Mosquera // Therefore: 14078931add6SHamilton Tobon Mosquera // i8** %offload_baseptrs. 14081d3d9b9cSHamilton Tobon Mosquera Value *BasePtrsArg = 14091d3d9b9cSHamilton Tobon Mosquera RuntimeCall.getArgOperand(OffloadArray::BasePtrsArgNum); 14108931add6SHamilton Tobon Mosquera // i8** %offload_ptrs. 14111d3d9b9cSHamilton Tobon Mosquera Value *PtrsArg = RuntimeCall.getArgOperand(OffloadArray::PtrsArgNum); 14128931add6SHamilton Tobon Mosquera // i8** %offload_sizes. 14131d3d9b9cSHamilton Tobon Mosquera Value *SizesArg = RuntimeCall.getArgOperand(OffloadArray::SizesArgNum); 14148931add6SHamilton Tobon Mosquera 14158931add6SHamilton Tobon Mosquera // Get values stored in **offload_baseptrs. 14168931add6SHamilton Tobon Mosquera auto *V = getUnderlyingObject(BasePtrsArg); 14178931add6SHamilton Tobon Mosquera if (!isa<AllocaInst>(V)) 14188931add6SHamilton Tobon Mosquera return false; 14198931add6SHamilton Tobon Mosquera auto *BasePtrsArray = cast<AllocaInst>(V); 14208931add6SHamilton Tobon Mosquera if (!OAs[0].initialize(*BasePtrsArray, RuntimeCall)) 14218931add6SHamilton Tobon Mosquera return false; 14228931add6SHamilton Tobon Mosquera 14238931add6SHamilton Tobon Mosquera // Get values stored in **offload_baseptrs. 14248931add6SHamilton Tobon Mosquera V = getUnderlyingObject(PtrsArg); 14258931add6SHamilton Tobon Mosquera if (!isa<AllocaInst>(V)) 14268931add6SHamilton Tobon Mosquera return false; 14278931add6SHamilton Tobon Mosquera auto *PtrsArray = cast<AllocaInst>(V); 14288931add6SHamilton Tobon Mosquera if (!OAs[1].initialize(*PtrsArray, RuntimeCall)) 14298931add6SHamilton Tobon Mosquera return false; 14308931add6SHamilton Tobon Mosquera 14318931add6SHamilton Tobon Mosquera // Get values stored in **offload_sizes. 14328931add6SHamilton Tobon Mosquera V = getUnderlyingObject(SizesArg); 14338931add6SHamilton Tobon Mosquera // If it's a [constant] global array don't analyze it. 14348931add6SHamilton Tobon Mosquera if (isa<GlobalValue>(V)) 14358931add6SHamilton Tobon Mosquera return isa<Constant>(V); 14368931add6SHamilton Tobon Mosquera if (!isa<AllocaInst>(V)) 14378931add6SHamilton Tobon Mosquera return false; 14388931add6SHamilton Tobon Mosquera 14398931add6SHamilton Tobon Mosquera auto *SizesArray = cast<AllocaInst>(V); 14408931add6SHamilton Tobon Mosquera if (!OAs[2].initialize(*SizesArray, RuntimeCall)) 14418931add6SHamilton Tobon Mosquera return false; 14428931add6SHamilton Tobon Mosquera 14438931add6SHamilton Tobon Mosquera return true; 14448931add6SHamilton Tobon Mosquera } 14458931add6SHamilton Tobon Mosquera 14468931add6SHamilton Tobon Mosquera /// Prints the values in the OffloadArrays \p OAs using LLVM_DEBUG. 14478931add6SHamilton Tobon Mosquera /// For now this is a way to test that the function getValuesInOffloadArrays 14488931add6SHamilton Tobon Mosquera /// is working properly. 14498931add6SHamilton Tobon Mosquera /// TODO: Move this to a unittest when unittests are available for OpenMPOpt. 14508931add6SHamilton Tobon Mosquera void dumpValuesInOffloadArrays(ArrayRef<OffloadArray> OAs) { 14518931add6SHamilton Tobon Mosquera assert(OAs.size() == 3 && "There are three offload arrays to debug!"); 14528931add6SHamilton Tobon Mosquera 14538931add6SHamilton Tobon Mosquera LLVM_DEBUG(dbgs() << TAG << " Successfully got offload values:\n"); 14548931add6SHamilton Tobon Mosquera std::string ValuesStr; 14558931add6SHamilton Tobon Mosquera raw_string_ostream Printer(ValuesStr); 14568931add6SHamilton Tobon Mosquera std::string Separator = " --- "; 14578931add6SHamilton Tobon Mosquera 14588931add6SHamilton Tobon Mosquera for (auto *BP : OAs[0].StoredValues) { 14598931add6SHamilton Tobon Mosquera BP->print(Printer); 14608931add6SHamilton Tobon Mosquera Printer << Separator; 14618931add6SHamilton Tobon Mosquera } 14628931add6SHamilton Tobon Mosquera LLVM_DEBUG(dbgs() << "\t\toffload_baseptrs: " << Printer.str() << "\n"); 14638931add6SHamilton Tobon Mosquera ValuesStr.clear(); 14648931add6SHamilton Tobon Mosquera 14658931add6SHamilton Tobon Mosquera for (auto *P : OAs[1].StoredValues) { 14668931add6SHamilton Tobon Mosquera P->print(Printer); 14678931add6SHamilton Tobon Mosquera Printer << Separator; 14688931add6SHamilton Tobon Mosquera } 14698931add6SHamilton Tobon Mosquera LLVM_DEBUG(dbgs() << "\t\toffload_ptrs: " << Printer.str() << "\n"); 14708931add6SHamilton Tobon Mosquera ValuesStr.clear(); 14718931add6SHamilton Tobon Mosquera 14728931add6SHamilton Tobon Mosquera for (auto *S : OAs[2].StoredValues) { 14738931add6SHamilton Tobon Mosquera S->print(Printer); 14748931add6SHamilton Tobon Mosquera Printer << Separator; 14758931add6SHamilton Tobon Mosquera } 14768931add6SHamilton Tobon Mosquera LLVM_DEBUG(dbgs() << "\t\toffload_sizes: " << Printer.str() << "\n"); 14778931add6SHamilton Tobon Mosquera } 14788931add6SHamilton Tobon Mosquera 1479bd2fa181SHamilton Tobon Mosquera /// Returns the instruction where the "wait" counterpart \p RuntimeCall can be 1480bd2fa181SHamilton Tobon Mosquera /// moved. Returns nullptr if the movement is not possible, or not worth it. 1481bd2fa181SHamilton Tobon Mosquera Instruction *canBeMovedDownwards(CallInst &RuntimeCall) { 1482bd2fa181SHamilton Tobon Mosquera // FIXME: This traverses only the BasicBlock where RuntimeCall is. 1483bd2fa181SHamilton Tobon Mosquera // Make it traverse the CFG. 1484bd2fa181SHamilton Tobon Mosquera 1485bd2fa181SHamilton Tobon Mosquera Instruction *CurrentI = &RuntimeCall; 1486bd2fa181SHamilton Tobon Mosquera bool IsWorthIt = false; 1487bd2fa181SHamilton Tobon Mosquera while ((CurrentI = CurrentI->getNextNode())) { 1488bd2fa181SHamilton Tobon Mosquera 1489bd2fa181SHamilton Tobon Mosquera // TODO: Once we detect the regions to be offloaded we should use the 1490bd2fa181SHamilton Tobon Mosquera // alias analysis manager to check if CurrentI may modify one of 1491bd2fa181SHamilton Tobon Mosquera // the offloaded regions. 1492bd2fa181SHamilton Tobon Mosquera if (CurrentI->mayHaveSideEffects() || CurrentI->mayReadFromMemory()) { 1493bd2fa181SHamilton Tobon Mosquera if (IsWorthIt) 1494bd2fa181SHamilton Tobon Mosquera return CurrentI; 1495bd2fa181SHamilton Tobon Mosquera 1496bd2fa181SHamilton Tobon Mosquera return nullptr; 1497bd2fa181SHamilton Tobon Mosquera } 1498bd2fa181SHamilton Tobon Mosquera 1499bd2fa181SHamilton Tobon Mosquera // FIXME: For now if we move it over anything without side effect 1500bd2fa181SHamilton Tobon Mosquera // is worth it. 1501bd2fa181SHamilton Tobon Mosquera IsWorthIt = true; 1502bd2fa181SHamilton Tobon Mosquera } 1503bd2fa181SHamilton Tobon Mosquera 1504bd2fa181SHamilton Tobon Mosquera // Return end of BasicBlock. 1505bd2fa181SHamilton Tobon Mosquera return RuntimeCall.getParent()->getTerminator(); 1506bd2fa181SHamilton Tobon Mosquera } 1507bd2fa181SHamilton Tobon Mosquera 1508496f8e5bSHamilton Tobon Mosquera /// Splits \p RuntimeCall into its "issue" and "wait" counterparts. 1509bd2fa181SHamilton Tobon Mosquera bool splitTargetDataBeginRTC(CallInst &RuntimeCall, 1510bd2fa181SHamilton Tobon Mosquera Instruction &WaitMovementPoint) { 1511bd31abc1SHamilton Tobon Mosquera // Create stack allocated handle (__tgt_async_info) at the beginning of the 1512bd31abc1SHamilton Tobon Mosquera // function. Used for storing information of the async transfer, allowing to 1513bd31abc1SHamilton Tobon Mosquera // wait on it later. 1514496f8e5bSHamilton Tobon Mosquera auto &IRBuilder = OMPInfoCache.OMPBuilder; 1515bd31abc1SHamilton Tobon Mosquera auto *F = RuntimeCall.getCaller(); 1516bd31abc1SHamilton Tobon Mosquera Instruction *FirstInst = &(F->getEntryBlock().front()); 1517bd31abc1SHamilton Tobon Mosquera AllocaInst *Handle = new AllocaInst( 1518bd31abc1SHamilton Tobon Mosquera IRBuilder.AsyncInfo, F->getAddressSpace(), "handle", FirstInst); 1519bd31abc1SHamilton Tobon Mosquera 1520496f8e5bSHamilton Tobon Mosquera // Add "issue" runtime call declaration: 1521496f8e5bSHamilton Tobon Mosquera // declare %struct.tgt_async_info @__tgt_target_data_begin_issue(i64, i32, 1522496f8e5bSHamilton Tobon Mosquera // i8**, i8**, i64*, i64*) 1523496f8e5bSHamilton Tobon Mosquera FunctionCallee IssueDecl = IRBuilder.getOrCreateRuntimeFunction( 1524496f8e5bSHamilton Tobon Mosquera M, OMPRTL___tgt_target_data_begin_mapper_issue); 1525496f8e5bSHamilton Tobon Mosquera 1526496f8e5bSHamilton Tobon Mosquera // Change RuntimeCall call site for its asynchronous version. 152797e55cfeSJoseph Huber SmallVector<Value *, 16> Args; 1528bd2fa181SHamilton Tobon Mosquera for (auto &Arg : RuntimeCall.args()) 1529496f8e5bSHamilton Tobon Mosquera Args.push_back(Arg.get()); 1530bd31abc1SHamilton Tobon Mosquera Args.push_back(Handle); 1531496f8e5bSHamilton Tobon Mosquera 1532496f8e5bSHamilton Tobon Mosquera CallInst *IssueCallsite = 1533bd31abc1SHamilton Tobon Mosquera CallInst::Create(IssueDecl, Args, /*NameStr=*/"", &RuntimeCall); 1534bd2fa181SHamilton Tobon Mosquera RuntimeCall.eraseFromParent(); 1535496f8e5bSHamilton Tobon Mosquera 1536496f8e5bSHamilton Tobon Mosquera // Add "wait" runtime call declaration: 1537496f8e5bSHamilton Tobon Mosquera // declare void @__tgt_target_data_begin_wait(i64, %struct.__tgt_async_info) 1538496f8e5bSHamilton Tobon Mosquera FunctionCallee WaitDecl = IRBuilder.getOrCreateRuntimeFunction( 1539496f8e5bSHamilton Tobon Mosquera M, OMPRTL___tgt_target_data_begin_mapper_wait); 1540496f8e5bSHamilton Tobon Mosquera 1541496f8e5bSHamilton Tobon Mosquera Value *WaitParams[2] = { 1542da8bec47SJoseph Huber IssueCallsite->getArgOperand( 1543da8bec47SJoseph Huber OffloadArray::DeviceIDArgNum), // device_id. 1544bd31abc1SHamilton Tobon Mosquera Handle // handle to wait on. 1545496f8e5bSHamilton Tobon Mosquera }; 1546bd2fa181SHamilton Tobon Mosquera CallInst::Create(WaitDecl, WaitParams, /*NameStr=*/"", &WaitMovementPoint); 1547496f8e5bSHamilton Tobon Mosquera 1548496f8e5bSHamilton Tobon Mosquera return true; 1549496f8e5bSHamilton Tobon Mosquera } 1550496f8e5bSHamilton Tobon Mosquera 1551dc3b5b00SJohannes Doerfert static Value *combinedIdentStruct(Value *CurrentIdent, Value *NextIdent, 1552dc3b5b00SJohannes Doerfert bool GlobalOnly, bool &SingleChoice) { 1553dc3b5b00SJohannes Doerfert if (CurrentIdent == NextIdent) 1554dc3b5b00SJohannes Doerfert return CurrentIdent; 1555dc3b5b00SJohannes Doerfert 1556396b7253SJohannes Doerfert // TODO: Figure out how to actually combine multiple debug locations. For 1557dc3b5b00SJohannes Doerfert // now we just keep an existing one if there is a single choice. 1558dc3b5b00SJohannes Doerfert if (!GlobalOnly || isa<GlobalValue>(NextIdent)) { 1559dc3b5b00SJohannes Doerfert SingleChoice = !CurrentIdent; 1560dc3b5b00SJohannes Doerfert return NextIdent; 1561dc3b5b00SJohannes Doerfert } 1562396b7253SJohannes Doerfert return nullptr; 1563396b7253SJohannes Doerfert } 1564396b7253SJohannes Doerfert 1565396b7253SJohannes Doerfert /// Return an `struct ident_t*` value that represents the ones used in the 1566396b7253SJohannes Doerfert /// calls of \p RFI inside of \p F. If \p GlobalOnly is true, we will not 1567396b7253SJohannes Doerfert /// return a local `struct ident_t*`. For now, if we cannot find a suitable 1568396b7253SJohannes Doerfert /// return value we create one from scratch. We also do not yet combine 1569396b7253SJohannes Doerfert /// information, e.g., the source locations, see combinedIdentStruct. 15707cfd267cSsstefan1 Value * 15717cfd267cSsstefan1 getCombinedIdentFromCallUsesIn(OMPInformationCache::RuntimeFunctionInfo &RFI, 15727cfd267cSsstefan1 Function &F, bool GlobalOnly) { 1573dc3b5b00SJohannes Doerfert bool SingleChoice = true; 1574396b7253SJohannes Doerfert Value *Ident = nullptr; 1575396b7253SJohannes Doerfert auto CombineIdentStruct = [&](Use &U, Function &Caller) { 1576396b7253SJohannes Doerfert CallInst *CI = getCallIfRegularCall(U, &RFI); 1577396b7253SJohannes Doerfert if (!CI || &F != &Caller) 1578396b7253SJohannes Doerfert return false; 1579396b7253SJohannes Doerfert Ident = combinedIdentStruct(Ident, CI->getArgOperand(0), 1580dc3b5b00SJohannes Doerfert /* GlobalOnly */ true, SingleChoice); 1581396b7253SJohannes Doerfert return false; 1582396b7253SJohannes Doerfert }; 1583624d34afSJohannes Doerfert RFI.foreachUse(SCC, CombineIdentStruct); 1584396b7253SJohannes Doerfert 1585dc3b5b00SJohannes Doerfert if (!Ident || !SingleChoice) { 1586396b7253SJohannes Doerfert // The IRBuilder uses the insertion block to get to the module, this is 1587396b7253SJohannes Doerfert // unfortunate but we work around it for now. 15887cfd267cSsstefan1 if (!OMPInfoCache.OMPBuilder.getInsertionPoint().getBlock()) 15897cfd267cSsstefan1 OMPInfoCache.OMPBuilder.updateToLocation(OpenMPIRBuilder::InsertPointTy( 1590396b7253SJohannes Doerfert &F.getEntryBlock(), F.getEntryBlock().begin())); 1591396b7253SJohannes Doerfert // Create a fallback location if non was found. 1592396b7253SJohannes Doerfert // TODO: Use the debug locations of the calls instead. 1593944aa042SJohannes Doerfert uint32_t SrcLocStrSize; 1594944aa042SJohannes Doerfert Constant *Loc = 1595944aa042SJohannes Doerfert OMPInfoCache.OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize); 1596944aa042SJohannes Doerfert Ident = OMPInfoCache.OMPBuilder.getOrCreateIdent(Loc, SrcLocStrSize); 1597396b7253SJohannes Doerfert } 1598396b7253SJohannes Doerfert return Ident; 1599396b7253SJohannes Doerfert } 1600396b7253SJohannes Doerfert 1601b726c557SJohannes Doerfert /// Try to eliminate calls of \p RFI in \p F by reusing an existing one or 16029548b74aSJohannes Doerfert /// \p ReplVal if given. 16037cfd267cSsstefan1 bool deduplicateRuntimeCalls(Function &F, 16047cfd267cSsstefan1 OMPInformationCache::RuntimeFunctionInfo &RFI, 16059548b74aSJohannes Doerfert Value *ReplVal = nullptr) { 16068855fec3SJohannes Doerfert auto *UV = RFI.getUseVector(F); 16078855fec3SJohannes Doerfert if (!UV || UV->size() + (ReplVal != nullptr) < 2) 1608b1fbf438SRoman Lebedev return false; 1609b1fbf438SRoman Lebedev 16107cfd267cSsstefan1 LLVM_DEBUG( 16117cfd267cSsstefan1 dbgs() << TAG << "Deduplicate " << UV->size() << " uses of " << RFI.Name 16127cfd267cSsstefan1 << (ReplVal ? " with an existing value\n" : "\n") << "\n"); 16137cfd267cSsstefan1 1614ab3da5ddSMichael Liao assert((!ReplVal || (isa<Argument>(ReplVal) && 1615ab3da5ddSMichael Liao cast<Argument>(ReplVal)->getParent() == &F)) && 16169548b74aSJohannes Doerfert "Unexpected replacement value!"); 1617396b7253SJohannes Doerfert 1618396b7253SJohannes Doerfert // TODO: Use dominance to find a good position instead. 16196aab27baSsstefan1 auto CanBeMoved = [this](CallBase &CB) { 16204f0225f6SKazu Hirata unsigned NumArgs = CB.arg_size(); 1621396b7253SJohannes Doerfert if (NumArgs == 0) 1622396b7253SJohannes Doerfert return true; 16236aab27baSsstefan1 if (CB.getArgOperand(0)->getType() != OMPInfoCache.OMPBuilder.IdentPtr) 1624396b7253SJohannes Doerfert return false; 1625c11ebfeaSJoseph Huber for (unsigned U = 1; U < NumArgs; ++U) 1626c11ebfeaSJoseph Huber if (isa<Instruction>(CB.getArgOperand(U))) 1627396b7253SJohannes Doerfert return false; 1628396b7253SJohannes Doerfert return true; 1629396b7253SJohannes Doerfert }; 1630396b7253SJohannes Doerfert 16319548b74aSJohannes Doerfert if (!ReplVal) { 16328855fec3SJohannes Doerfert for (Use *U : *UV) 16339548b74aSJohannes Doerfert if (CallInst *CI = getCallIfRegularCall(*U, &RFI)) { 1634396b7253SJohannes Doerfert if (!CanBeMoved(*CI)) 1635396b7253SJohannes Doerfert continue; 16364d4ea9acSHuber, Joseph 1637f97de4cbSGiorgis Georgakoudis // If the function is a kernel, dedup will move 1638f97de4cbSGiorgis Georgakoudis // the runtime call right after the kernel init callsite. Otherwise, 1639f97de4cbSGiorgis Georgakoudis // it will move it to the beginning of the caller function. 1640f97de4cbSGiorgis Georgakoudis if (isKernel(F)) { 1641f97de4cbSGiorgis Georgakoudis auto &KernelInitRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_target_init]; 1642f97de4cbSGiorgis Georgakoudis auto *KernelInitUV = KernelInitRFI.getUseVector(F); 1643f97de4cbSGiorgis Georgakoudis 1644f97de4cbSGiorgis Georgakoudis if (KernelInitUV->empty()) 1645f97de4cbSGiorgis Georgakoudis continue; 1646f97de4cbSGiorgis Georgakoudis 1647f97de4cbSGiorgis Georgakoudis assert(KernelInitUV->size() == 1 && 1648f97de4cbSGiorgis Georgakoudis "Expected a single __kmpc_target_init in kernel\n"); 1649f97de4cbSGiorgis Georgakoudis 1650f97de4cbSGiorgis Georgakoudis CallInst *KernelInitCI = 1651f97de4cbSGiorgis Georgakoudis getCallIfRegularCall(*KernelInitUV->front(), &KernelInitRFI); 1652f97de4cbSGiorgis Georgakoudis assert(KernelInitCI && 1653f97de4cbSGiorgis Georgakoudis "Expected a call to __kmpc_target_init in kernel\n"); 1654f97de4cbSGiorgis Georgakoudis 1655f97de4cbSGiorgis Georgakoudis CI->moveAfter(KernelInitCI); 1656f97de4cbSGiorgis Georgakoudis } else 16579548b74aSJohannes Doerfert CI->moveBefore(&*F.getEntryBlock().getFirstInsertionPt()); 16589548b74aSJohannes Doerfert ReplVal = CI; 16599548b74aSJohannes Doerfert break; 16609548b74aSJohannes Doerfert } 16619548b74aSJohannes Doerfert if (!ReplVal) 16629548b74aSJohannes Doerfert return false; 16639548b74aSJohannes Doerfert } 16649548b74aSJohannes Doerfert 1665396b7253SJohannes Doerfert // If we use a call as a replacement value we need to make sure the ident is 1666396b7253SJohannes Doerfert // valid at the new location. For now we just pick a global one, either 1667396b7253SJohannes Doerfert // existing and used by one of the calls, or created from scratch. 1668396b7253SJohannes Doerfert if (CallBase *CI = dyn_cast<CallBase>(ReplVal)) { 166992c9ff6dSKazu Hirata if (!CI->arg_empty() && 16706aab27baSsstefan1 CI->getArgOperand(0)->getType() == OMPInfoCache.OMPBuilder.IdentPtr) { 1671396b7253SJohannes Doerfert Value *Ident = getCombinedIdentFromCallUsesIn(RFI, F, 1672396b7253SJohannes Doerfert /* GlobalOnly */ true); 1673396b7253SJohannes Doerfert CI->setArgOperand(0, Ident); 1674396b7253SJohannes Doerfert } 1675396b7253SJohannes Doerfert } 1676396b7253SJohannes Doerfert 16779548b74aSJohannes Doerfert bool Changed = false; 16789548b74aSJohannes Doerfert auto ReplaceAndDeleteCB = [&](Use &U, Function &Caller) { 16799548b74aSJohannes Doerfert CallInst *CI = getCallIfRegularCall(U, &RFI); 16809548b74aSJohannes Doerfert if (!CI || CI == ReplVal || &F != &Caller) 16819548b74aSJohannes Doerfert return false; 16829548b74aSJohannes Doerfert assert(CI->getCaller() == &F && "Unexpected call!"); 16834d4ea9acSHuber, Joseph 16844d4ea9acSHuber, Joseph auto Remark = [&](OptimizationRemark OR) { 16854d4ea9acSHuber, Joseph return OR << "OpenMP runtime call " 1686eef6601bSJoseph Huber << ore::NV("OpenMPOptRuntime", RFI.Name) << " deduplicated."; 16874d4ea9acSHuber, Joseph }; 1688eef6601bSJoseph Huber if (CI->getDebugLoc()) 16892c31d5ebSJoseph Huber emitRemark<OptimizationRemark>(CI, "OMP170", Remark); 1690eef6601bSJoseph Huber else 16912c31d5ebSJoseph Huber emitRemark<OptimizationRemark>(&F, "OMP170", Remark); 16924d4ea9acSHuber, Joseph 16939548b74aSJohannes Doerfert CGUpdater.removeCallSite(*CI); 16949548b74aSJohannes Doerfert CI->replaceAllUsesWith(ReplVal); 16959548b74aSJohannes Doerfert CI->eraseFromParent(); 16969548b74aSJohannes Doerfert ++NumOpenMPRuntimeCallsDeduplicated; 16979548b74aSJohannes Doerfert Changed = true; 16989548b74aSJohannes Doerfert return true; 16999548b74aSJohannes Doerfert }; 1700624d34afSJohannes Doerfert RFI.foreachUse(SCC, ReplaceAndDeleteCB); 17019548b74aSJohannes Doerfert 17029548b74aSJohannes Doerfert return Changed; 17039548b74aSJohannes Doerfert } 17049548b74aSJohannes Doerfert 17059548b74aSJohannes Doerfert /// Collect arguments that represent the global thread id in \p GTIdArgs. 17069548b74aSJohannes Doerfert void collectGlobalThreadIdArguments(SmallSetVector<Value *, 16> >IdArgs) { 17079548b74aSJohannes Doerfert // TODO: Below we basically perform a fixpoint iteration with a pessimistic 17089548b74aSJohannes Doerfert // initialization. We could define an AbstractAttribute instead and 17099548b74aSJohannes Doerfert // run the Attributor here once it can be run as an SCC pass. 17109548b74aSJohannes Doerfert 17119548b74aSJohannes Doerfert // Helper to check the argument \p ArgNo at all call sites of \p F for 17129548b74aSJohannes Doerfert // a GTId. 17139548b74aSJohannes Doerfert auto CallArgOpIsGTId = [&](Function &F, unsigned ArgNo, CallInst &RefCI) { 17149548b74aSJohannes Doerfert if (!F.hasLocalLinkage()) 17159548b74aSJohannes Doerfert return false; 17169548b74aSJohannes Doerfert for (Use &U : F.uses()) { 17179548b74aSJohannes Doerfert if (CallInst *CI = getCallIfRegularCall(U)) { 17189548b74aSJohannes Doerfert Value *ArgOp = CI->getArgOperand(ArgNo); 17199548b74aSJohannes Doerfert if (CI == &RefCI || GTIdArgs.count(ArgOp) || 17207cfd267cSsstefan1 getCallIfRegularCall( 17217cfd267cSsstefan1 *ArgOp, &OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num])) 17229548b74aSJohannes Doerfert continue; 17239548b74aSJohannes Doerfert } 17249548b74aSJohannes Doerfert return false; 17259548b74aSJohannes Doerfert } 17269548b74aSJohannes Doerfert return true; 17279548b74aSJohannes Doerfert }; 17289548b74aSJohannes Doerfert 17299548b74aSJohannes Doerfert // Helper to identify uses of a GTId as GTId arguments. 17309548b74aSJohannes Doerfert auto AddUserArgs = [&](Value >Id) { 17319548b74aSJohannes Doerfert for (Use &U : GTId.uses()) 17329548b74aSJohannes Doerfert if (CallInst *CI = dyn_cast<CallInst>(U.getUser())) 17339548b74aSJohannes Doerfert if (CI->isArgOperand(&U)) 17349548b74aSJohannes Doerfert if (Function *Callee = CI->getCalledFunction()) 17359548b74aSJohannes Doerfert if (CallArgOpIsGTId(*Callee, U.getOperandNo(), *CI)) 17369548b74aSJohannes Doerfert GTIdArgs.insert(Callee->getArg(U.getOperandNo())); 17379548b74aSJohannes Doerfert }; 17389548b74aSJohannes Doerfert 17399548b74aSJohannes Doerfert // The argument users of __kmpc_global_thread_num calls are GTIds. 17407cfd267cSsstefan1 OMPInformationCache::RuntimeFunctionInfo &GlobThreadNumRFI = 17417cfd267cSsstefan1 OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num]; 17427cfd267cSsstefan1 1743624d34afSJohannes Doerfert GlobThreadNumRFI.foreachUse(SCC, [&](Use &U, Function &F) { 17448855fec3SJohannes Doerfert if (CallInst *CI = getCallIfRegularCall(U, &GlobThreadNumRFI)) 17459548b74aSJohannes Doerfert AddUserArgs(*CI); 17468855fec3SJohannes Doerfert return false; 17478855fec3SJohannes Doerfert }); 17489548b74aSJohannes Doerfert 17499548b74aSJohannes Doerfert // Transitively search for more arguments by looking at the users of the 17509548b74aSJohannes Doerfert // ones we know already. During the search the GTIdArgs vector is extended 17519548b74aSJohannes Doerfert // so we cannot cache the size nor can we use a range based for. 1752c11ebfeaSJoseph Huber for (unsigned U = 0; U < GTIdArgs.size(); ++U) 1753c11ebfeaSJoseph Huber AddUserArgs(*GTIdArgs[U]); 17549548b74aSJohannes Doerfert } 17559548b74aSJohannes Doerfert 17565b0581aeSJohannes Doerfert /// Kernel (=GPU) optimizations and utility functions 17575b0581aeSJohannes Doerfert /// 17585b0581aeSJohannes Doerfert ///{{ 17595b0581aeSJohannes Doerfert 17605b0581aeSJohannes Doerfert /// Check if \p F is a kernel, hence entry point for target offloading. 17615b0581aeSJohannes Doerfert bool isKernel(Function &F) { return OMPInfoCache.Kernels.count(&F); } 17625b0581aeSJohannes Doerfert 17635b0581aeSJohannes Doerfert /// Cache to remember the unique kernel for a function. 17645b0581aeSJohannes Doerfert DenseMap<Function *, Optional<Kernel>> UniqueKernelMap; 17655b0581aeSJohannes Doerfert 17665b0581aeSJohannes Doerfert /// Find the unique kernel that will execute \p F, if any. 17675b0581aeSJohannes Doerfert Kernel getUniqueKernelFor(Function &F); 17685b0581aeSJohannes Doerfert 17695b0581aeSJohannes Doerfert /// Find the unique kernel that will execute \p I, if any. 17705b0581aeSJohannes Doerfert Kernel getUniqueKernelFor(Instruction &I) { 17715b0581aeSJohannes Doerfert return getUniqueKernelFor(*I.getFunction()); 17725b0581aeSJohannes Doerfert } 17735b0581aeSJohannes Doerfert 17745b0581aeSJohannes Doerfert /// Rewrite the device (=GPU) code state machine create in non-SPMD mode in 17755b0581aeSJohannes Doerfert /// the cases we can avoid taking the address of a function. 17765b0581aeSJohannes Doerfert bool rewriteDeviceCodeStateMachine(); 17775b0581aeSJohannes Doerfert 17785b0581aeSJohannes Doerfert /// 17795b0581aeSJohannes Doerfert ///}} 17805b0581aeSJohannes Doerfert 17814d4ea9acSHuber, Joseph /// Emit a remark generically 17824d4ea9acSHuber, Joseph /// 17834d4ea9acSHuber, Joseph /// This template function can be used to generically emit a remark. The 17844d4ea9acSHuber, Joseph /// RemarkKind should be one of the following: 17854d4ea9acSHuber, Joseph /// - OptimizationRemark to indicate a successful optimization attempt 17864d4ea9acSHuber, Joseph /// - OptimizationRemarkMissed to report a failed optimization attempt 17874d4ea9acSHuber, Joseph /// - OptimizationRemarkAnalysis to provide additional information about an 17884d4ea9acSHuber, Joseph /// optimization attempt 17894d4ea9acSHuber, Joseph /// 17904d4ea9acSHuber, Joseph /// The remark is built using a callback function provided by the caller that 17914d4ea9acSHuber, Joseph /// takes a RemarkKind as input and returns a RemarkKind. 17922db182ffSJoseph Huber template <typename RemarkKind, typename RemarkCallBack> 17932db182ffSJoseph Huber void emitRemark(Instruction *I, StringRef RemarkName, 1794e8039ad4SJohannes Doerfert RemarkCallBack &&RemarkCB) const { 17952db182ffSJoseph Huber Function *F = I->getParent()->getParent(); 17964d4ea9acSHuber, Joseph auto &ORE = OREGetter(F); 17974d4ea9acSHuber, Joseph 17982c31d5ebSJoseph Huber if (RemarkName.startswith("OMP")) 17992c31d5ebSJoseph Huber ORE.emit([&]() { 18002c31d5ebSJoseph Huber return RemarkCB(RemarkKind(DEBUG_TYPE, RemarkName, I)) 18012c31d5ebSJoseph Huber << " [" << RemarkName << "]"; 18022c31d5ebSJoseph Huber }); 18032c31d5ebSJoseph Huber else 18042c31d5ebSJoseph Huber ORE.emit( 18052c31d5ebSJoseph Huber [&]() { return RemarkCB(RemarkKind(DEBUG_TYPE, RemarkName, I)); }); 18064d4ea9acSHuber, Joseph } 18074d4ea9acSHuber, Joseph 18082db182ffSJoseph Huber /// Emit a remark on a function. 18092db182ffSJoseph Huber template <typename RemarkKind, typename RemarkCallBack> 18102db182ffSJoseph Huber void emitRemark(Function *F, StringRef RemarkName, 18112db182ffSJoseph Huber RemarkCallBack &&RemarkCB) const { 18120f426935Ssstefan1 auto &ORE = OREGetter(F); 18130f426935Ssstefan1 18142c31d5ebSJoseph Huber if (RemarkName.startswith("OMP")) 18152c31d5ebSJoseph Huber ORE.emit([&]() { 18162c31d5ebSJoseph Huber return RemarkCB(RemarkKind(DEBUG_TYPE, RemarkName, F)) 18172c31d5ebSJoseph Huber << " [" << RemarkName << "]"; 18182c31d5ebSJoseph Huber }); 18192c31d5ebSJoseph Huber else 18202c31d5ebSJoseph Huber ORE.emit( 18212c31d5ebSJoseph Huber [&]() { return RemarkCB(RemarkKind(DEBUG_TYPE, RemarkName, F)); }); 18220f426935Ssstefan1 } 18230f426935Ssstefan1 182458725c12SJoseph Huber /// RAII struct to temporarily change an RTL function's linkage to external. 182558725c12SJoseph Huber /// This prevents it from being mistakenly removed by other optimizations. 182658725c12SJoseph Huber struct ExternalizationRAII { 182758725c12SJoseph Huber ExternalizationRAII(OMPInformationCache &OMPInfoCache, 182858725c12SJoseph Huber RuntimeFunction RFKind) 1829e757a3b0SJoseph Huber : Declaration(OMPInfoCache.RFIs[RFKind].Declaration) { 183058725c12SJoseph Huber if (!Declaration) 183158725c12SJoseph Huber return; 183258725c12SJoseph Huber 183358725c12SJoseph Huber LinkageType = Declaration->getLinkage(); 183458725c12SJoseph Huber Declaration->setLinkage(GlobalValue::ExternalLinkage); 183558725c12SJoseph Huber } 183658725c12SJoseph Huber 183758725c12SJoseph Huber ~ExternalizationRAII() { 183858725c12SJoseph Huber if (!Declaration) 183958725c12SJoseph Huber return; 184058725c12SJoseph Huber 184158725c12SJoseph Huber Declaration->setLinkage(LinkageType); 184258725c12SJoseph Huber } 184358725c12SJoseph Huber 184458725c12SJoseph Huber Function *Declaration; 184558725c12SJoseph Huber GlobalValue::LinkageTypes LinkageType; 184658725c12SJoseph Huber }; 184758725c12SJoseph Huber 1848b726c557SJohannes Doerfert /// The underlying module. 18499548b74aSJohannes Doerfert Module &M; 18509548b74aSJohannes Doerfert 18519548b74aSJohannes Doerfert /// The SCC we are operating on. 1852ee17263aSJohannes Doerfert SmallVectorImpl<Function *> &SCC; 18539548b74aSJohannes Doerfert 18549548b74aSJohannes Doerfert /// Callback to update the call graph, the first argument is a removed call, 18559548b74aSJohannes Doerfert /// the second an optional replacement call. 18569548b74aSJohannes Doerfert CallGraphUpdater &CGUpdater; 18579548b74aSJohannes Doerfert 18584d4ea9acSHuber, Joseph /// Callback to get an OptimizationRemarkEmitter from a Function * 18594d4ea9acSHuber, Joseph OptimizationRemarkGetter OREGetter; 18604d4ea9acSHuber, Joseph 18617cfd267cSsstefan1 /// OpenMP-specific information cache. Also Used for Attributor runs. 18627cfd267cSsstefan1 OMPInformationCache &OMPInfoCache; 1863b8235d2bSsstefan1 1864b8235d2bSsstefan1 /// Attributor instance. 1865b8235d2bSsstefan1 Attributor &A; 1866b8235d2bSsstefan1 1867b8235d2bSsstefan1 /// Helper function to run Attributor on SCC. 1868d9659bf6SJohannes Doerfert bool runAttributor(bool IsModulePass) { 1869b8235d2bSsstefan1 if (SCC.empty()) 1870b8235d2bSsstefan1 return false; 1871b8235d2bSsstefan1 187258725c12SJoseph Huber // Temporarily make these function have external linkage so the Attributor 187358725c12SJoseph Huber // doesn't remove them when we try to look them up later. 187458725c12SJoseph Huber ExternalizationRAII Parallel(OMPInfoCache, OMPRTL___kmpc_kernel_parallel); 187558725c12SJoseph Huber ExternalizationRAII EndParallel(OMPInfoCache, 187658725c12SJoseph Huber OMPRTL___kmpc_kernel_end_parallel); 187758725c12SJoseph Huber ExternalizationRAII BarrierSPMD(OMPInfoCache, 187858725c12SJoseph Huber OMPRTL___kmpc_barrier_simple_spmd); 187973720c80SJohannes Doerfert ExternalizationRAII BarrierGeneric(OMPInfoCache, 188073720c80SJohannes Doerfert OMPRTL___kmpc_barrier_simple_generic); 18811cf86df8SJoseph Huber ExternalizationRAII ThreadId(OMPInfoCache, 18821cf86df8SJoseph Huber OMPRTL___kmpc_get_hardware_thread_id_in_block); 18837986a5f2SJoseph Huber ExternalizationRAII WarpSize(OMPInfoCache, OMPRTL___kmpc_get_warp_size); 188458725c12SJoseph Huber 1885d9659bf6SJohannes Doerfert registerAAs(IsModulePass); 1886b8235d2bSsstefan1 1887b8235d2bSsstefan1 ChangeStatus Changed = A.run(); 1888b8235d2bSsstefan1 1889b8235d2bSsstefan1 LLVM_DEBUG(dbgs() << "[Attributor] Done with " << SCC.size() 1890b8235d2bSsstefan1 << " functions, result: " << Changed << ".\n"); 1891b8235d2bSsstefan1 1892b8235d2bSsstefan1 return Changed == ChangeStatus::CHANGED; 1893b8235d2bSsstefan1 } 1894b8235d2bSsstefan1 18955ab6aeddSJose M Monsalve Diaz void registerFoldRuntimeCall(RuntimeFunction RF); 18965ab6aeddSJose M Monsalve Diaz 1897b8235d2bSsstefan1 /// Populate the Attributor with abstract attribute opportunities in the 1898b8235d2bSsstefan1 /// function. 1899d9659bf6SJohannes Doerfert void registerAAs(bool IsModulePass); 1900b8235d2bSsstefan1 }; 1901b8235d2bSsstefan1 19025b0581aeSJohannes Doerfert Kernel OpenMPOpt::getUniqueKernelFor(Function &F) { 19035b0581aeSJohannes Doerfert if (!OMPInfoCache.ModuleSlice.count(&F)) 19045b0581aeSJohannes Doerfert return nullptr; 19055b0581aeSJohannes Doerfert 19065b0581aeSJohannes Doerfert // Use a scope to keep the lifetime of the CachedKernel short. 19075b0581aeSJohannes Doerfert { 19085b0581aeSJohannes Doerfert Optional<Kernel> &CachedKernel = UniqueKernelMap[&F]; 19095b0581aeSJohannes Doerfert if (CachedKernel) 19105b0581aeSJohannes Doerfert return *CachedKernel; 19115b0581aeSJohannes Doerfert 19125b0581aeSJohannes Doerfert // TODO: We should use an AA to create an (optimistic and callback 19135b0581aeSJohannes Doerfert // call-aware) call graph. For now we stick to simple patterns that 19145b0581aeSJohannes Doerfert // are less powerful, basically the worst fixpoint. 19155b0581aeSJohannes Doerfert if (isKernel(F)) { 19165b0581aeSJohannes Doerfert CachedKernel = Kernel(&F); 19175b0581aeSJohannes Doerfert return *CachedKernel; 19185b0581aeSJohannes Doerfert } 19195b0581aeSJohannes Doerfert 19205b0581aeSJohannes Doerfert CachedKernel = nullptr; 1921994bb6ebSJohannes Doerfert if (!F.hasLocalLinkage()) { 1922994bb6ebSJohannes Doerfert 1923994bb6ebSJohannes Doerfert // See https://openmp.llvm.org/remarks/OptimizationRemarks.html 19242db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 1925eef6601bSJoseph Huber return ORA << "Potentially unknown OpenMP target region caller."; 1926994bb6ebSJohannes Doerfert }; 19272db182ffSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(&F, "OMP100", Remark); 1928994bb6ebSJohannes Doerfert 19295b0581aeSJohannes Doerfert return nullptr; 19305b0581aeSJohannes Doerfert } 1931994bb6ebSJohannes Doerfert } 19325b0581aeSJohannes Doerfert 19335b0581aeSJohannes Doerfert auto GetUniqueKernelForUse = [&](const Use &U) -> Kernel { 19345b0581aeSJohannes Doerfert if (auto *Cmp = dyn_cast<ICmpInst>(U.getUser())) { 19355b0581aeSJohannes Doerfert // Allow use in equality comparisons. 19365b0581aeSJohannes Doerfert if (Cmp->isEquality()) 19375b0581aeSJohannes Doerfert return getUniqueKernelFor(*Cmp); 19385b0581aeSJohannes Doerfert return nullptr; 19395b0581aeSJohannes Doerfert } 19405b0581aeSJohannes Doerfert if (auto *CB = dyn_cast<CallBase>(U.getUser())) { 19415b0581aeSJohannes Doerfert // Allow direct calls. 19425b0581aeSJohannes Doerfert if (CB->isCallee(&U)) 19435b0581aeSJohannes Doerfert return getUniqueKernelFor(*CB); 1944a2dbfb6bSGiorgis Georgakoudis 1945a2dbfb6bSGiorgis Georgakoudis OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI = 1946a2dbfb6bSGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_51]; 1947a2dbfb6bSGiorgis Georgakoudis // Allow the use in __kmpc_parallel_51 calls. 1948a2dbfb6bSGiorgis Georgakoudis if (OpenMPOpt::getCallIfRegularCall(*U.getUser(), &KernelParallelRFI)) 19495b0581aeSJohannes Doerfert return getUniqueKernelFor(*CB); 19505b0581aeSJohannes Doerfert return nullptr; 19515b0581aeSJohannes Doerfert } 19525b0581aeSJohannes Doerfert // Disallow every other use. 19535b0581aeSJohannes Doerfert return nullptr; 19545b0581aeSJohannes Doerfert }; 19555b0581aeSJohannes Doerfert 19565b0581aeSJohannes Doerfert // TODO: In the future we want to track more than just a unique kernel. 19575b0581aeSJohannes Doerfert SmallPtrSet<Kernel, 2> PotentialKernels; 19588d8ce85bSsstefan1 OMPInformationCache::foreachUse(F, [&](const Use &U) { 19595b0581aeSJohannes Doerfert PotentialKernels.insert(GetUniqueKernelForUse(U)); 19605b0581aeSJohannes Doerfert }); 19615b0581aeSJohannes Doerfert 19625b0581aeSJohannes Doerfert Kernel K = nullptr; 19635b0581aeSJohannes Doerfert if (PotentialKernels.size() == 1) 19645b0581aeSJohannes Doerfert K = *PotentialKernels.begin(); 19655b0581aeSJohannes Doerfert 19665b0581aeSJohannes Doerfert // Cache the result. 19675b0581aeSJohannes Doerfert UniqueKernelMap[&F] = K; 19685b0581aeSJohannes Doerfert 19695b0581aeSJohannes Doerfert return K; 19705b0581aeSJohannes Doerfert } 19715b0581aeSJohannes Doerfert 19725b0581aeSJohannes Doerfert bool OpenMPOpt::rewriteDeviceCodeStateMachine() { 1973a2dbfb6bSGiorgis Georgakoudis OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI = 1974a2dbfb6bSGiorgis Georgakoudis OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_51]; 19755b0581aeSJohannes Doerfert 19765b0581aeSJohannes Doerfert bool Changed = false; 1977a2dbfb6bSGiorgis Georgakoudis if (!KernelParallelRFI) 19785b0581aeSJohannes Doerfert return Changed; 19795b0581aeSJohannes Doerfert 1980cd0dd8ecSJoseph Huber // If we have disabled state machine changes, exit 1981cd0dd8ecSJoseph Huber if (DisableOpenMPOptStateMachineRewrite) 1982cd0dd8ecSJoseph Huber return Changed; 1983cd0dd8ecSJoseph Huber 19845b0581aeSJohannes Doerfert for (Function *F : SCC) { 19855b0581aeSJohannes Doerfert 1986a2dbfb6bSGiorgis Georgakoudis // Check if the function is a use in a __kmpc_parallel_51 call at 19875b0581aeSJohannes Doerfert // all. 19885b0581aeSJohannes Doerfert bool UnknownUse = false; 1989a2dbfb6bSGiorgis Georgakoudis bool KernelParallelUse = false; 19905b0581aeSJohannes Doerfert unsigned NumDirectCalls = 0; 19915b0581aeSJohannes Doerfert 19925b0581aeSJohannes Doerfert SmallVector<Use *, 2> ToBeReplacedStateMachineUses; 19938d8ce85bSsstefan1 OMPInformationCache::foreachUse(*F, [&](Use &U) { 19945b0581aeSJohannes Doerfert if (auto *CB = dyn_cast<CallBase>(U.getUser())) 19955b0581aeSJohannes Doerfert if (CB->isCallee(&U)) { 19965b0581aeSJohannes Doerfert ++NumDirectCalls; 19975b0581aeSJohannes Doerfert return; 19985b0581aeSJohannes Doerfert } 19995b0581aeSJohannes Doerfert 200081db6144SMichael Liao if (isa<ICmpInst>(U.getUser())) { 20015b0581aeSJohannes Doerfert ToBeReplacedStateMachineUses.push_back(&U); 20025b0581aeSJohannes Doerfert return; 20035b0581aeSJohannes Doerfert } 2004a2dbfb6bSGiorgis Georgakoudis 2005a2dbfb6bSGiorgis Georgakoudis // Find wrapper functions that represent parallel kernels. 2006a2dbfb6bSGiorgis Georgakoudis CallInst *CI = 2007a2dbfb6bSGiorgis Georgakoudis OpenMPOpt::getCallIfRegularCall(*U.getUser(), &KernelParallelRFI); 2008a2dbfb6bSGiorgis Georgakoudis const unsigned int WrapperFunctionArgNo = 6; 2009a2dbfb6bSGiorgis Georgakoudis if (!KernelParallelUse && CI && 2010a2dbfb6bSGiorgis Georgakoudis CI->getArgOperandNo(&U) == WrapperFunctionArgNo) { 2011a2dbfb6bSGiorgis Georgakoudis KernelParallelUse = true; 20125b0581aeSJohannes Doerfert ToBeReplacedStateMachineUses.push_back(&U); 20135b0581aeSJohannes Doerfert return; 20145b0581aeSJohannes Doerfert } 20155b0581aeSJohannes Doerfert UnknownUse = true; 20165b0581aeSJohannes Doerfert }); 20175b0581aeSJohannes Doerfert 2018a2dbfb6bSGiorgis Georgakoudis // Do not emit a remark if we haven't seen a __kmpc_parallel_51 2019fec1f210SJohannes Doerfert // use. 2020a2dbfb6bSGiorgis Georgakoudis if (!KernelParallelUse) 20215b0581aeSJohannes Doerfert continue; 20225b0581aeSJohannes Doerfert 2023fec1f210SJohannes Doerfert // If this ever hits, we should investigate. 2024fec1f210SJohannes Doerfert // TODO: Checking the number of uses is not a necessary restriction and 2025fec1f210SJohannes Doerfert // should be lifted. 2026fec1f210SJohannes Doerfert if (UnknownUse || NumDirectCalls != 1 || 2027d9659bf6SJohannes Doerfert ToBeReplacedStateMachineUses.size() > 2) { 20282db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 20292db182ffSJoseph Huber return ORA << "Parallel region is used in " 2030fec1f210SJohannes Doerfert << (UnknownUse ? "unknown" : "unexpected") 2031eef6601bSJoseph Huber << " ways. Will not attempt to rewrite the state machine."; 2032fec1f210SJohannes Doerfert }; 20332c31d5ebSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(F, "OMP101", Remark); 20345b0581aeSJohannes Doerfert continue; 2035fec1f210SJohannes Doerfert } 20365b0581aeSJohannes Doerfert 2037a2dbfb6bSGiorgis Georgakoudis // Even if we have __kmpc_parallel_51 calls, we (for now) give 20385b0581aeSJohannes Doerfert // up if the function is not called from a unique kernel. 20395b0581aeSJohannes Doerfert Kernel K = getUniqueKernelFor(*F); 2040fec1f210SJohannes Doerfert if (!K) { 20412db182ffSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis ORA) { 2042eef6601bSJoseph Huber return ORA << "Parallel region is not called from a unique kernel. " 2043eef6601bSJoseph Huber "Will not attempt to rewrite the state machine."; 2044fec1f210SJohannes Doerfert }; 20452c31d5ebSJoseph Huber emitRemark<OptimizationRemarkAnalysis>(F, "OMP102", Remark); 20465b0581aeSJohannes Doerfert continue; 2047fec1f210SJohannes Doerfert } 20485b0581aeSJohannes Doerfert 20495b0581aeSJohannes Doerfert // We now know F is a parallel body function called only from the kernel K. 20505b0581aeSJohannes Doerfert // We also identified the state machine uses in which we replace the 20515b0581aeSJohannes Doerfert // function pointer by a new global symbol for identification purposes. This 20525b0581aeSJohannes Doerfert // ensures only direct calls to the function are left. 20535b0581aeSJohannes Doerfert 20545b0581aeSJohannes Doerfert Module &M = *F->getParent(); 20555b0581aeSJohannes Doerfert Type *Int8Ty = Type::getInt8Ty(M.getContext()); 20565b0581aeSJohannes Doerfert 20575b0581aeSJohannes Doerfert auto *ID = new GlobalVariable( 20585b0581aeSJohannes Doerfert M, Int8Ty, /* isConstant */ true, GlobalValue::PrivateLinkage, 20595b0581aeSJohannes Doerfert UndefValue::get(Int8Ty), F->getName() + ".ID"); 20605b0581aeSJohannes Doerfert 20615b0581aeSJohannes Doerfert for (Use *U : ToBeReplacedStateMachineUses) 206271052ea1SJon Chesterfield U->set(ConstantExpr::getPointerBitCastOrAddrSpaceCast( 206371052ea1SJon Chesterfield ID, U->get()->getType())); 20645b0581aeSJohannes Doerfert 20655b0581aeSJohannes Doerfert ++NumOpenMPParallelRegionsReplacedInGPUStateMachine; 20665b0581aeSJohannes Doerfert 20675b0581aeSJohannes Doerfert Changed = true; 20685b0581aeSJohannes Doerfert } 20695b0581aeSJohannes Doerfert 20705b0581aeSJohannes Doerfert return Changed; 20715b0581aeSJohannes Doerfert } 20725b0581aeSJohannes Doerfert 2073b8235d2bSsstefan1 /// Abstract Attribute for tracking ICV values. 2074b8235d2bSsstefan1 struct AAICVTracker : public StateWrapper<BooleanState, AbstractAttribute> { 2075b8235d2bSsstefan1 using Base = StateWrapper<BooleanState, AbstractAttribute>; 2076b8235d2bSsstefan1 AAICVTracker(const IRPosition &IRP, Attributor &A) : Base(IRP) {} 2077b8235d2bSsstefan1 20785dfd7cc4Ssstefan1 void initialize(Attributor &A) override { 20795dfd7cc4Ssstefan1 Function *F = getAnchorScope(); 20805dfd7cc4Ssstefan1 if (!F || !A.isFunctionIPOAmendable(*F)) 20815dfd7cc4Ssstefan1 indicatePessimisticFixpoint(); 20825dfd7cc4Ssstefan1 } 20835dfd7cc4Ssstefan1 2084b8235d2bSsstefan1 /// Returns true if value is assumed to be tracked. 2085b8235d2bSsstefan1 bool isAssumedTracked() const { return getAssumed(); } 2086b8235d2bSsstefan1 2087b8235d2bSsstefan1 /// Returns true if value is known to be tracked. 2088b8235d2bSsstefan1 bool isKnownTracked() const { return getAssumed(); } 2089b8235d2bSsstefan1 2090b8235d2bSsstefan1 /// Create an abstract attribute biew for the position \p IRP. 2091b8235d2bSsstefan1 static AAICVTracker &createForPosition(const IRPosition &IRP, Attributor &A); 2092b8235d2bSsstefan1 2093b8235d2bSsstefan1 /// Return the value with which \p I can be replaced for specific \p ICV. 20945dfd7cc4Ssstefan1 virtual Optional<Value *> getReplacementValue(InternalControlVar ICV, 20955dfd7cc4Ssstefan1 const Instruction *I, 20965dfd7cc4Ssstefan1 Attributor &A) const { 20975dfd7cc4Ssstefan1 return None; 20985dfd7cc4Ssstefan1 } 20995dfd7cc4Ssstefan1 21005dfd7cc4Ssstefan1 /// Return an assumed unique ICV value if a single candidate is found. If 21015dfd7cc4Ssstefan1 /// there cannot be one, return a nullptr. If it is not clear yet, return the 21025dfd7cc4Ssstefan1 /// Optional::NoneType. 21035dfd7cc4Ssstefan1 virtual Optional<Value *> 21045dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const = 0; 21055dfd7cc4Ssstefan1 21065dfd7cc4Ssstefan1 // Currently only nthreads is being tracked. 21075dfd7cc4Ssstefan1 // this array will only grow with time. 21085dfd7cc4Ssstefan1 InternalControlVar TrackableICVs[1] = {ICV_nthreads}; 2109b8235d2bSsstefan1 2110b8235d2bSsstefan1 /// See AbstractAttribute::getName() 2111b8235d2bSsstefan1 const std::string getName() const override { return "AAICVTracker"; } 2112b8235d2bSsstefan1 2113233af895SLuofan Chen /// See AbstractAttribute::getIdAddr() 2114233af895SLuofan Chen const char *getIdAddr() const override { return &ID; } 2115233af895SLuofan Chen 2116233af895SLuofan Chen /// This function should return true if the type of the \p AA is AAICVTracker 2117233af895SLuofan Chen static bool classof(const AbstractAttribute *AA) { 2118233af895SLuofan Chen return (AA->getIdAddr() == &ID); 2119233af895SLuofan Chen } 2120233af895SLuofan Chen 2121b8235d2bSsstefan1 static const char ID; 2122b8235d2bSsstefan1 }; 2123b8235d2bSsstefan1 2124b8235d2bSsstefan1 struct AAICVTrackerFunction : public AAICVTracker { 2125b8235d2bSsstefan1 AAICVTrackerFunction(const IRPosition &IRP, Attributor &A) 2126b8235d2bSsstefan1 : AAICVTracker(IRP, A) {} 2127b8235d2bSsstefan1 2128b8235d2bSsstefan1 // FIXME: come up with better string. 21295dfd7cc4Ssstefan1 const std::string getAsStr() const override { return "ICVTrackerFunction"; } 2130b8235d2bSsstefan1 2131b8235d2bSsstefan1 // FIXME: come up with some stats. 2132b8235d2bSsstefan1 void trackStatistics() const override {} 2133b8235d2bSsstefan1 21345dfd7cc4Ssstefan1 /// We don't manifest anything for this AA. 2135b8235d2bSsstefan1 ChangeStatus manifest(Attributor &A) override { 21365dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 2137b8235d2bSsstefan1 } 2138b8235d2bSsstefan1 2139b8235d2bSsstefan1 // Map of ICV to their values at specific program point. 21405dfd7cc4Ssstefan1 EnumeratedArray<DenseMap<Instruction *, Value *>, InternalControlVar, 2141b8235d2bSsstefan1 InternalControlVar::ICV___last> 21425dfd7cc4Ssstefan1 ICVReplacementValuesMap; 2143b8235d2bSsstefan1 2144b8235d2bSsstefan1 ChangeStatus updateImpl(Attributor &A) override { 2145b8235d2bSsstefan1 ChangeStatus HasChanged = ChangeStatus::UNCHANGED; 2146b8235d2bSsstefan1 2147b8235d2bSsstefan1 Function *F = getAnchorScope(); 2148b8235d2bSsstefan1 2149b8235d2bSsstefan1 auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2150b8235d2bSsstefan1 2151b8235d2bSsstefan1 for (InternalControlVar ICV : TrackableICVs) { 2152b8235d2bSsstefan1 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter]; 2153b8235d2bSsstefan1 21545dfd7cc4Ssstefan1 auto &ValuesMap = ICVReplacementValuesMap[ICV]; 2155b8235d2bSsstefan1 auto TrackValues = [&](Use &U, Function &) { 2156b8235d2bSsstefan1 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U); 2157b8235d2bSsstefan1 if (!CI) 2158b8235d2bSsstefan1 return false; 2159b8235d2bSsstefan1 2160b8235d2bSsstefan1 // FIXME: handle setters with more that 1 arguments. 2161b8235d2bSsstefan1 /// Track new value. 21625dfd7cc4Ssstefan1 if (ValuesMap.insert(std::make_pair(CI, CI->getArgOperand(0))).second) 2163b8235d2bSsstefan1 HasChanged = ChangeStatus::CHANGED; 2164b8235d2bSsstefan1 2165b8235d2bSsstefan1 return false; 2166b8235d2bSsstefan1 }; 2167b8235d2bSsstefan1 21685dfd7cc4Ssstefan1 auto CallCheck = [&](Instruction &I) { 21695dfd7cc4Ssstefan1 Optional<Value *> ReplVal = getValueForCall(A, &I, ICV); 21705dfd7cc4Ssstefan1 if (ReplVal.hasValue() && 21715dfd7cc4Ssstefan1 ValuesMap.insert(std::make_pair(&I, *ReplVal)).second) 21725dfd7cc4Ssstefan1 HasChanged = ChangeStatus::CHANGED; 21735dfd7cc4Ssstefan1 21745dfd7cc4Ssstefan1 return true; 21755dfd7cc4Ssstefan1 }; 21765dfd7cc4Ssstefan1 21775dfd7cc4Ssstefan1 // Track all changes of an ICV. 2178b8235d2bSsstefan1 SetterRFI.foreachUse(TrackValues, F); 21795dfd7cc4Ssstefan1 2180792aac98SJohannes Doerfert bool UsedAssumedInformation = false; 21815dfd7cc4Ssstefan1 A.checkForAllInstructions(CallCheck, *this, {Instruction::Call}, 2182792aac98SJohannes Doerfert UsedAssumedInformation, 21835dfd7cc4Ssstefan1 /* CheckBBLivenessOnly */ true); 21845dfd7cc4Ssstefan1 21855dfd7cc4Ssstefan1 /// TODO: Figure out a way to avoid adding entry in 21865dfd7cc4Ssstefan1 /// ICVReplacementValuesMap 21875dfd7cc4Ssstefan1 Instruction *Entry = &F->getEntryBlock().front(); 21885dfd7cc4Ssstefan1 if (HasChanged == ChangeStatus::CHANGED && !ValuesMap.count(Entry)) 21895dfd7cc4Ssstefan1 ValuesMap.insert(std::make_pair(Entry, nullptr)); 2190b8235d2bSsstefan1 } 2191b8235d2bSsstefan1 2192b8235d2bSsstefan1 return HasChanged; 2193b8235d2bSsstefan1 } 2194b8235d2bSsstefan1 21955dfd7cc4Ssstefan1 /// Hepler to check if \p I is a call and get the value for it if it is 21965dfd7cc4Ssstefan1 /// unique. 21975dfd7cc4Ssstefan1 Optional<Value *> getValueForCall(Attributor &A, const Instruction *I, 21985dfd7cc4Ssstefan1 InternalControlVar &ICV) const { 2199b8235d2bSsstefan1 22005dfd7cc4Ssstefan1 const auto *CB = dyn_cast<CallBase>(I); 2201dcaec812SJohannes Doerfert if (!CB || CB->hasFnAttr("no_openmp") || 2202dcaec812SJohannes Doerfert CB->hasFnAttr("no_openmp_routines")) 22035dfd7cc4Ssstefan1 return None; 22045dfd7cc4Ssstefan1 2205b8235d2bSsstefan1 auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2206b8235d2bSsstefan1 auto &GetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Getter]; 22075dfd7cc4Ssstefan1 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter]; 22085dfd7cc4Ssstefan1 Function *CalledFunction = CB->getCalledFunction(); 2209b8235d2bSsstefan1 22104eef14f9SWei Wang // Indirect call, assume ICV changes. 22114eef14f9SWei Wang if (CalledFunction == nullptr) 22124eef14f9SWei Wang return nullptr; 22135dfd7cc4Ssstefan1 if (CalledFunction == GetterRFI.Declaration) 22145dfd7cc4Ssstefan1 return None; 22155dfd7cc4Ssstefan1 if (CalledFunction == SetterRFI.Declaration) { 22165dfd7cc4Ssstefan1 if (ICVReplacementValuesMap[ICV].count(I)) 22175dfd7cc4Ssstefan1 return ICVReplacementValuesMap[ICV].lookup(I); 22185dfd7cc4Ssstefan1 22195dfd7cc4Ssstefan1 return nullptr; 22205dfd7cc4Ssstefan1 } 22215dfd7cc4Ssstefan1 22225dfd7cc4Ssstefan1 // Since we don't know, assume it changes the ICV. 22235dfd7cc4Ssstefan1 if (CalledFunction->isDeclaration()) 22245dfd7cc4Ssstefan1 return nullptr; 22255dfd7cc4Ssstefan1 22265b70c12fSJohannes Doerfert const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 22275b70c12fSJohannes Doerfert *this, IRPosition::callsite_returned(*CB), DepClassTy::REQUIRED); 22285dfd7cc4Ssstefan1 22295dfd7cc4Ssstefan1 if (ICVTrackingAA.isAssumedTracked()) 22305dfd7cc4Ssstefan1 return ICVTrackingAA.getUniqueReplacementValue(ICV); 22315dfd7cc4Ssstefan1 22325dfd7cc4Ssstefan1 // If we don't know, assume it changes. 22335dfd7cc4Ssstefan1 return nullptr; 22345dfd7cc4Ssstefan1 } 22355dfd7cc4Ssstefan1 22365dfd7cc4Ssstefan1 // We don't check unique value for a function, so return None. 22375dfd7cc4Ssstefan1 Optional<Value *> 22385dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 22395dfd7cc4Ssstefan1 return None; 22405dfd7cc4Ssstefan1 } 22415dfd7cc4Ssstefan1 22425dfd7cc4Ssstefan1 /// Return the value with which \p I can be replaced for specific \p ICV. 22435dfd7cc4Ssstefan1 Optional<Value *> getReplacementValue(InternalControlVar ICV, 22445dfd7cc4Ssstefan1 const Instruction *I, 22455dfd7cc4Ssstefan1 Attributor &A) const override { 22465dfd7cc4Ssstefan1 const auto &ValuesMap = ICVReplacementValuesMap[ICV]; 22475dfd7cc4Ssstefan1 if (ValuesMap.count(I)) 22485dfd7cc4Ssstefan1 return ValuesMap.lookup(I); 22495dfd7cc4Ssstefan1 22505dfd7cc4Ssstefan1 SmallVector<const Instruction *, 16> Worklist; 22515dfd7cc4Ssstefan1 SmallPtrSet<const Instruction *, 16> Visited; 22525dfd7cc4Ssstefan1 Worklist.push_back(I); 22535dfd7cc4Ssstefan1 22545dfd7cc4Ssstefan1 Optional<Value *> ReplVal; 22555dfd7cc4Ssstefan1 22565dfd7cc4Ssstefan1 while (!Worklist.empty()) { 22575dfd7cc4Ssstefan1 const Instruction *CurrInst = Worklist.pop_back_val(); 22585dfd7cc4Ssstefan1 if (!Visited.insert(CurrInst).second) 2259b8235d2bSsstefan1 continue; 2260b8235d2bSsstefan1 22615dfd7cc4Ssstefan1 const BasicBlock *CurrBB = CurrInst->getParent(); 22625dfd7cc4Ssstefan1 22635dfd7cc4Ssstefan1 // Go up and look for all potential setters/calls that might change the 22645dfd7cc4Ssstefan1 // ICV. 22655dfd7cc4Ssstefan1 while ((CurrInst = CurrInst->getPrevNode())) { 22665dfd7cc4Ssstefan1 if (ValuesMap.count(CurrInst)) { 22675dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = ValuesMap.lookup(CurrInst); 22685dfd7cc4Ssstefan1 // Unknown value, track new. 22695dfd7cc4Ssstefan1 if (!ReplVal.hasValue()) { 22705dfd7cc4Ssstefan1 ReplVal = NewReplVal; 22715dfd7cc4Ssstefan1 break; 22725dfd7cc4Ssstefan1 } 22735dfd7cc4Ssstefan1 22745dfd7cc4Ssstefan1 // If we found a new value, we can't know the icv value anymore. 22755dfd7cc4Ssstefan1 if (NewReplVal.hasValue()) 22765dfd7cc4Ssstefan1 if (ReplVal != NewReplVal) 2277b8235d2bSsstefan1 return nullptr; 2278b8235d2bSsstefan1 22795dfd7cc4Ssstefan1 break; 2280b8235d2bSsstefan1 } 2281b8235d2bSsstefan1 22825dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = getValueForCall(A, CurrInst, ICV); 22835dfd7cc4Ssstefan1 if (!NewReplVal.hasValue()) 22845dfd7cc4Ssstefan1 continue; 22855dfd7cc4Ssstefan1 22865dfd7cc4Ssstefan1 // Unknown value, track new. 22875dfd7cc4Ssstefan1 if (!ReplVal.hasValue()) { 22885dfd7cc4Ssstefan1 ReplVal = NewReplVal; 22895dfd7cc4Ssstefan1 break; 2290b8235d2bSsstefan1 } 2291b8235d2bSsstefan1 22925dfd7cc4Ssstefan1 // if (NewReplVal.hasValue()) 22935dfd7cc4Ssstefan1 // We found a new value, we can't know the icv value anymore. 22945dfd7cc4Ssstefan1 if (ReplVal != NewReplVal) 2295b8235d2bSsstefan1 return nullptr; 2296b8235d2bSsstefan1 } 22975dfd7cc4Ssstefan1 22985dfd7cc4Ssstefan1 // If we are in the same BB and we have a value, we are done. 22995dfd7cc4Ssstefan1 if (CurrBB == I->getParent() && ReplVal.hasValue()) 23005dfd7cc4Ssstefan1 return ReplVal; 23015dfd7cc4Ssstefan1 23025dfd7cc4Ssstefan1 // Go through all predecessors and add terminators for analysis. 23035dfd7cc4Ssstefan1 for (const BasicBlock *Pred : predecessors(CurrBB)) 23045dfd7cc4Ssstefan1 if (const Instruction *Terminator = Pred->getTerminator()) 23055dfd7cc4Ssstefan1 Worklist.push_back(Terminator); 23065dfd7cc4Ssstefan1 } 23075dfd7cc4Ssstefan1 23085dfd7cc4Ssstefan1 return ReplVal; 23095dfd7cc4Ssstefan1 } 23105dfd7cc4Ssstefan1 }; 23115dfd7cc4Ssstefan1 23125dfd7cc4Ssstefan1 struct AAICVTrackerFunctionReturned : AAICVTracker { 23135dfd7cc4Ssstefan1 AAICVTrackerFunctionReturned(const IRPosition &IRP, Attributor &A) 23145dfd7cc4Ssstefan1 : AAICVTracker(IRP, A) {} 23155dfd7cc4Ssstefan1 23165dfd7cc4Ssstefan1 // FIXME: come up with better string. 23175dfd7cc4Ssstefan1 const std::string getAsStr() const override { 23185dfd7cc4Ssstefan1 return "ICVTrackerFunctionReturned"; 23195dfd7cc4Ssstefan1 } 23205dfd7cc4Ssstefan1 23215dfd7cc4Ssstefan1 // FIXME: come up with some stats. 23225dfd7cc4Ssstefan1 void trackStatistics() const override {} 23235dfd7cc4Ssstefan1 23245dfd7cc4Ssstefan1 /// We don't manifest anything for this AA. 23255dfd7cc4Ssstefan1 ChangeStatus manifest(Attributor &A) override { 23265dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 23275dfd7cc4Ssstefan1 } 23285dfd7cc4Ssstefan1 23295dfd7cc4Ssstefan1 // Map of ICV to their values at specific program point. 23305dfd7cc4Ssstefan1 EnumeratedArray<Optional<Value *>, InternalControlVar, 23315dfd7cc4Ssstefan1 InternalControlVar::ICV___last> 23325dfd7cc4Ssstefan1 ICVReplacementValuesMap; 23335dfd7cc4Ssstefan1 23345dfd7cc4Ssstefan1 /// Return the value with which \p I can be replaced for specific \p ICV. 23355dfd7cc4Ssstefan1 Optional<Value *> 23365dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 23375dfd7cc4Ssstefan1 return ICVReplacementValuesMap[ICV]; 23385dfd7cc4Ssstefan1 } 23395dfd7cc4Ssstefan1 23405dfd7cc4Ssstefan1 ChangeStatus updateImpl(Attributor &A) override { 23415dfd7cc4Ssstefan1 ChangeStatus Changed = ChangeStatus::UNCHANGED; 23425dfd7cc4Ssstefan1 const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 23435b70c12fSJohannes Doerfert *this, IRPosition::function(*getAnchorScope()), DepClassTy::REQUIRED); 23445dfd7cc4Ssstefan1 23455dfd7cc4Ssstefan1 if (!ICVTrackingAA.isAssumedTracked()) 23465dfd7cc4Ssstefan1 return indicatePessimisticFixpoint(); 23475dfd7cc4Ssstefan1 23485dfd7cc4Ssstefan1 for (InternalControlVar ICV : TrackableICVs) { 23495dfd7cc4Ssstefan1 Optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV]; 23505dfd7cc4Ssstefan1 Optional<Value *> UniqueICVValue; 23515dfd7cc4Ssstefan1 23525dfd7cc4Ssstefan1 auto CheckReturnInst = [&](Instruction &I) { 23535dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = 23545dfd7cc4Ssstefan1 ICVTrackingAA.getReplacementValue(ICV, &I, A); 23555dfd7cc4Ssstefan1 23565dfd7cc4Ssstefan1 // If we found a second ICV value there is no unique returned value. 23575dfd7cc4Ssstefan1 if (UniqueICVValue.hasValue() && UniqueICVValue != NewReplVal) 23585dfd7cc4Ssstefan1 return false; 23595dfd7cc4Ssstefan1 23605dfd7cc4Ssstefan1 UniqueICVValue = NewReplVal; 23615dfd7cc4Ssstefan1 23625dfd7cc4Ssstefan1 return true; 23635dfd7cc4Ssstefan1 }; 23645dfd7cc4Ssstefan1 2365792aac98SJohannes Doerfert bool UsedAssumedInformation = false; 23665dfd7cc4Ssstefan1 if (!A.checkForAllInstructions(CheckReturnInst, *this, {Instruction::Ret}, 2367792aac98SJohannes Doerfert UsedAssumedInformation, 23685dfd7cc4Ssstefan1 /* CheckBBLivenessOnly */ true)) 23695dfd7cc4Ssstefan1 UniqueICVValue = nullptr; 23705dfd7cc4Ssstefan1 23715dfd7cc4Ssstefan1 if (UniqueICVValue == ReplVal) 23725dfd7cc4Ssstefan1 continue; 23735dfd7cc4Ssstefan1 23745dfd7cc4Ssstefan1 ReplVal = UniqueICVValue; 23755dfd7cc4Ssstefan1 Changed = ChangeStatus::CHANGED; 23765dfd7cc4Ssstefan1 } 23775dfd7cc4Ssstefan1 23785dfd7cc4Ssstefan1 return Changed; 23795dfd7cc4Ssstefan1 } 23805dfd7cc4Ssstefan1 }; 23815dfd7cc4Ssstefan1 23825dfd7cc4Ssstefan1 struct AAICVTrackerCallSite : AAICVTracker { 23835dfd7cc4Ssstefan1 AAICVTrackerCallSite(const IRPosition &IRP, Attributor &A) 23845dfd7cc4Ssstefan1 : AAICVTracker(IRP, A) {} 23855dfd7cc4Ssstefan1 23865dfd7cc4Ssstefan1 void initialize(Attributor &A) override { 23875dfd7cc4Ssstefan1 Function *F = getAnchorScope(); 23885dfd7cc4Ssstefan1 if (!F || !A.isFunctionIPOAmendable(*F)) 23895dfd7cc4Ssstefan1 indicatePessimisticFixpoint(); 23905dfd7cc4Ssstefan1 23915dfd7cc4Ssstefan1 // We only initialize this AA for getters, so we need to know which ICV it 23925dfd7cc4Ssstefan1 // gets. 23935dfd7cc4Ssstefan1 auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 23945dfd7cc4Ssstefan1 for (InternalControlVar ICV : TrackableICVs) { 23955dfd7cc4Ssstefan1 auto ICVInfo = OMPInfoCache.ICVs[ICV]; 23965dfd7cc4Ssstefan1 auto &Getter = OMPInfoCache.RFIs[ICVInfo.Getter]; 23975dfd7cc4Ssstefan1 if (Getter.Declaration == getAssociatedFunction()) { 23985dfd7cc4Ssstefan1 AssociatedICV = ICVInfo.Kind; 23995dfd7cc4Ssstefan1 return; 24005dfd7cc4Ssstefan1 } 24015dfd7cc4Ssstefan1 } 24025dfd7cc4Ssstefan1 24035dfd7cc4Ssstefan1 /// Unknown ICV. 24045dfd7cc4Ssstefan1 indicatePessimisticFixpoint(); 24055dfd7cc4Ssstefan1 } 24065dfd7cc4Ssstefan1 24075dfd7cc4Ssstefan1 ChangeStatus manifest(Attributor &A) override { 24085dfd7cc4Ssstefan1 if (!ReplVal.hasValue() || !ReplVal.getValue()) 24095dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 24105dfd7cc4Ssstefan1 24115dfd7cc4Ssstefan1 A.changeValueAfterManifest(*getCtxI(), **ReplVal); 24125dfd7cc4Ssstefan1 A.deleteAfterManifest(*getCtxI()); 24135dfd7cc4Ssstefan1 24145dfd7cc4Ssstefan1 return ChangeStatus::CHANGED; 24155dfd7cc4Ssstefan1 } 24165dfd7cc4Ssstefan1 24175dfd7cc4Ssstefan1 // FIXME: come up with better string. 24185dfd7cc4Ssstefan1 const std::string getAsStr() const override { return "ICVTrackerCallSite"; } 24195dfd7cc4Ssstefan1 24205dfd7cc4Ssstefan1 // FIXME: come up with some stats. 24215dfd7cc4Ssstefan1 void trackStatistics() const override {} 24225dfd7cc4Ssstefan1 24235dfd7cc4Ssstefan1 InternalControlVar AssociatedICV; 24245dfd7cc4Ssstefan1 Optional<Value *> ReplVal; 24255dfd7cc4Ssstefan1 24265dfd7cc4Ssstefan1 ChangeStatus updateImpl(Attributor &A) override { 24275dfd7cc4Ssstefan1 const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 24285b70c12fSJohannes Doerfert *this, IRPosition::function(*getAnchorScope()), DepClassTy::REQUIRED); 24295dfd7cc4Ssstefan1 24305dfd7cc4Ssstefan1 // We don't have any information, so we assume it changes the ICV. 24315dfd7cc4Ssstefan1 if (!ICVTrackingAA.isAssumedTracked()) 24325dfd7cc4Ssstefan1 return indicatePessimisticFixpoint(); 24335dfd7cc4Ssstefan1 24345dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = 24355dfd7cc4Ssstefan1 ICVTrackingAA.getReplacementValue(AssociatedICV, getCtxI(), A); 24365dfd7cc4Ssstefan1 24375dfd7cc4Ssstefan1 if (ReplVal == NewReplVal) 24385dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 24395dfd7cc4Ssstefan1 24405dfd7cc4Ssstefan1 ReplVal = NewReplVal; 24415dfd7cc4Ssstefan1 return ChangeStatus::CHANGED; 24425dfd7cc4Ssstefan1 } 24435dfd7cc4Ssstefan1 24445dfd7cc4Ssstefan1 // Return the value with which associated value can be replaced for specific 24455dfd7cc4Ssstefan1 // \p ICV. 24465dfd7cc4Ssstefan1 Optional<Value *> 24475dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 24485dfd7cc4Ssstefan1 return ReplVal; 24495dfd7cc4Ssstefan1 } 24505dfd7cc4Ssstefan1 }; 24515dfd7cc4Ssstefan1 24525dfd7cc4Ssstefan1 struct AAICVTrackerCallSiteReturned : AAICVTracker { 24535dfd7cc4Ssstefan1 AAICVTrackerCallSiteReturned(const IRPosition &IRP, Attributor &A) 24545dfd7cc4Ssstefan1 : AAICVTracker(IRP, A) {} 24555dfd7cc4Ssstefan1 24565dfd7cc4Ssstefan1 // FIXME: come up with better string. 24575dfd7cc4Ssstefan1 const std::string getAsStr() const override { 24585dfd7cc4Ssstefan1 return "ICVTrackerCallSiteReturned"; 24595dfd7cc4Ssstefan1 } 24605dfd7cc4Ssstefan1 24615dfd7cc4Ssstefan1 // FIXME: come up with some stats. 24625dfd7cc4Ssstefan1 void trackStatistics() const override {} 24635dfd7cc4Ssstefan1 24645dfd7cc4Ssstefan1 /// We don't manifest anything for this AA. 24655dfd7cc4Ssstefan1 ChangeStatus manifest(Attributor &A) override { 24665dfd7cc4Ssstefan1 return ChangeStatus::UNCHANGED; 24675dfd7cc4Ssstefan1 } 24685dfd7cc4Ssstefan1 24695dfd7cc4Ssstefan1 // Map of ICV to their values at specific program point. 24705dfd7cc4Ssstefan1 EnumeratedArray<Optional<Value *>, InternalControlVar, 24715dfd7cc4Ssstefan1 InternalControlVar::ICV___last> 24725dfd7cc4Ssstefan1 ICVReplacementValuesMap; 24735dfd7cc4Ssstefan1 24745dfd7cc4Ssstefan1 /// Return the value with which associated value can be replaced for specific 24755dfd7cc4Ssstefan1 /// \p ICV. 24765dfd7cc4Ssstefan1 Optional<Value *> 24775dfd7cc4Ssstefan1 getUniqueReplacementValue(InternalControlVar ICV) const override { 24785dfd7cc4Ssstefan1 return ICVReplacementValuesMap[ICV]; 24795dfd7cc4Ssstefan1 } 24805dfd7cc4Ssstefan1 24815dfd7cc4Ssstefan1 ChangeStatus updateImpl(Attributor &A) override { 24825dfd7cc4Ssstefan1 ChangeStatus Changed = ChangeStatus::UNCHANGED; 24835dfd7cc4Ssstefan1 const auto &ICVTrackingAA = A.getAAFor<AAICVTracker>( 24845b70c12fSJohannes Doerfert *this, IRPosition::returned(*getAssociatedFunction()), 24855b70c12fSJohannes Doerfert DepClassTy::REQUIRED); 24865dfd7cc4Ssstefan1 24875dfd7cc4Ssstefan1 // We don't have any information, so we assume it changes the ICV. 24885dfd7cc4Ssstefan1 if (!ICVTrackingAA.isAssumedTracked()) 24895dfd7cc4Ssstefan1 return indicatePessimisticFixpoint(); 24905dfd7cc4Ssstefan1 24915dfd7cc4Ssstefan1 for (InternalControlVar ICV : TrackableICVs) { 24925dfd7cc4Ssstefan1 Optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV]; 24935dfd7cc4Ssstefan1 Optional<Value *> NewReplVal = 24945dfd7cc4Ssstefan1 ICVTrackingAA.getUniqueReplacementValue(ICV); 24955dfd7cc4Ssstefan1 24965dfd7cc4Ssstefan1 if (ReplVal == NewReplVal) 24975dfd7cc4Ssstefan1 continue; 24985dfd7cc4Ssstefan1 24995dfd7cc4Ssstefan1 ReplVal = NewReplVal; 25005dfd7cc4Ssstefan1 Changed = ChangeStatus::CHANGED; 25015dfd7cc4Ssstefan1 } 25025dfd7cc4Ssstefan1 return Changed; 25035dfd7cc4Ssstefan1 } 25049548b74aSJohannes Doerfert }; 250518283125SJoseph Huber 250618283125SJoseph Huber struct AAExecutionDomainFunction : public AAExecutionDomain { 250718283125SJoseph Huber AAExecutionDomainFunction(const IRPosition &IRP, Attributor &A) 250818283125SJoseph Huber : AAExecutionDomain(IRP, A) {} 250918283125SJoseph Huber 251018283125SJoseph Huber const std::string getAsStr() const override { 251118283125SJoseph Huber return "[AAExecutionDomain] " + std::to_string(SingleThreadedBBs.size()) + 251218283125SJoseph Huber "/" + std::to_string(NumBBs) + " BBs thread 0 only."; 251318283125SJoseph Huber } 251418283125SJoseph Huber 251518283125SJoseph Huber /// See AbstractAttribute::trackStatistics(). 251618283125SJoseph Huber void trackStatistics() const override {} 251718283125SJoseph Huber 251818283125SJoseph Huber void initialize(Attributor &A) override { 251918283125SJoseph Huber Function *F = getAnchorScope(); 252018283125SJoseph Huber for (const auto &BB : *F) 252118283125SJoseph Huber SingleThreadedBBs.insert(&BB); 252218283125SJoseph Huber NumBBs = SingleThreadedBBs.size(); 252318283125SJoseph Huber } 252418283125SJoseph Huber 252518283125SJoseph Huber ChangeStatus manifest(Attributor &A) override { 252618283125SJoseph Huber LLVM_DEBUG({ 252718283125SJoseph Huber for (const BasicBlock *BB : SingleThreadedBBs) 252818283125SJoseph Huber dbgs() << TAG << " Basic block @" << getAnchorScope()->getName() << " " 252918283125SJoseph Huber << BB->getName() << " is executed by a single thread.\n"; 253018283125SJoseph Huber }); 253118283125SJoseph Huber return ChangeStatus::UNCHANGED; 253218283125SJoseph Huber } 253318283125SJoseph Huber 253418283125SJoseph Huber ChangeStatus updateImpl(Attributor &A) override; 253518283125SJoseph Huber 253618283125SJoseph Huber /// Check if an instruction is executed by a single thread. 25379a23e673SJohannes Doerfert bool isExecutedByInitialThreadOnly(const Instruction &I) const override { 25389a23e673SJohannes Doerfert return isExecutedByInitialThreadOnly(*I.getParent()); 253918283125SJoseph Huber } 254018283125SJoseph Huber 25419a23e673SJohannes Doerfert bool isExecutedByInitialThreadOnly(const BasicBlock &BB) const override { 25421cfdcae6SJoseph Huber return isValidState() && SingleThreadedBBs.contains(&BB); 254318283125SJoseph Huber } 254418283125SJoseph Huber 254518283125SJoseph Huber /// Set of basic blocks that are executed by a single thread. 25469f04a0eaSJohannes Doerfert SmallSetVector<const BasicBlock *, 16> SingleThreadedBBs; 254718283125SJoseph Huber 254818283125SJoseph Huber /// Total number of basic blocks in this function. 254918283125SJoseph Huber long unsigned NumBBs; 255018283125SJoseph Huber }; 255118283125SJoseph Huber 255218283125SJoseph Huber ChangeStatus AAExecutionDomainFunction::updateImpl(Attributor &A) { 255318283125SJoseph Huber Function *F = getAnchorScope(); 255418283125SJoseph Huber ReversePostOrderTraversal<Function *> RPOT(F); 255518283125SJoseph Huber auto NumSingleThreadedBBs = SingleThreadedBBs.size(); 255618283125SJoseph Huber 255718283125SJoseph Huber bool AllCallSitesKnown; 255818283125SJoseph Huber auto PredForCallSite = [&](AbstractCallSite ACS) { 255918283125SJoseph Huber const auto &ExecutionDomainAA = A.getAAFor<AAExecutionDomain>( 256018283125SJoseph Huber *this, IRPosition::function(*ACS.getInstruction()->getFunction()), 256118283125SJoseph Huber DepClassTy::REQUIRED); 25621cfdcae6SJoseph Huber return ACS.isDirectCall() && 25631cfdcae6SJoseph Huber ExecutionDomainAA.isExecutedByInitialThreadOnly( 25649a23e673SJohannes Doerfert *ACS.getInstruction()); 256518283125SJoseph Huber }; 256618283125SJoseph Huber 256718283125SJoseph Huber if (!A.checkForAllCallSites(PredForCallSite, *this, 256818283125SJoseph Huber /* RequiresAllCallSites */ true, 256918283125SJoseph Huber AllCallSitesKnown)) 25709f04a0eaSJohannes Doerfert SingleThreadedBBs.remove(&F->getEntryBlock()); 257118283125SJoseph Huber 2572e2cfbfccSJohannes Doerfert auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2573e2cfbfccSJohannes Doerfert auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_target_init]; 2574e2cfbfccSJohannes Doerfert 257527905eebSJoseph Huber // Check if the edge into the successor block contains a condition that only 257627905eebSJoseph Huber // lets the main thread execute it. 25776fc51c9fSJoseph Huber auto IsInitialThreadOnly = [&](BranchInst *Edge, BasicBlock *SuccessorBB) { 257818283125SJoseph Huber if (!Edge || !Edge->isConditional()) 257918283125SJoseph Huber return false; 258018283125SJoseph Huber if (Edge->getSuccessor(0) != SuccessorBB) 258118283125SJoseph Huber return false; 258218283125SJoseph Huber 258318283125SJoseph Huber auto *Cmp = dyn_cast<CmpInst>(Edge->getCondition()); 258418283125SJoseph Huber if (!Cmp || !Cmp->isTrueWhenEqual() || !Cmp->isEquality()) 258518283125SJoseph Huber return false; 258618283125SJoseph Huber 258718283125SJoseph Huber ConstantInt *C = dyn_cast<ConstantInt>(Cmp->getOperand(1)); 2588e2cfbfccSJohannes Doerfert if (!C) 258918283125SJoseph Huber return false; 259018283125SJoseph Huber 2591e2cfbfccSJohannes Doerfert // Match: -1 == __kmpc_target_init (for non-SPMD kernels only!) 2592e2cfbfccSJohannes Doerfert if (C->isAllOnesValue()) { 2593e2cfbfccSJohannes Doerfert auto *CB = dyn_cast<CallBase>(Cmp->getOperand(0)); 2594c4b1fe05SJohannes Doerfert CB = CB ? OpenMPOpt::getCallIfRegularCall(*CB, &RFI) : nullptr; 2595c4b1fe05SJohannes Doerfert if (!CB) 2596e2cfbfccSJohannes Doerfert return false; 2597423d34f7SShilei Tian const int InitModeArgNo = 1; 2598423d34f7SShilei Tian auto *ModeCI = dyn_cast<ConstantInt>(CB->getOperand(InitModeArgNo)); 2599423d34f7SShilei Tian return ModeCI && (ModeCI->getSExtValue() & OMP_TGT_EXEC_MODE_GENERIC); 2600e2cfbfccSJohannes Doerfert } 260118283125SJoseph Huber 260227905eebSJoseph Huber if (C->isZero()) { 260327905eebSJoseph Huber // Match: 0 == llvm.nvvm.read.ptx.sreg.tid.x() 260427905eebSJoseph Huber if (auto *II = dyn_cast<IntrinsicInst>(Cmp->getOperand(0))) 260527905eebSJoseph Huber if (II->getIntrinsicID() == Intrinsic::nvvm_read_ptx_sreg_tid_x) 260627905eebSJoseph Huber return true; 260727905eebSJoseph Huber 260827905eebSJoseph Huber // Match: 0 == llvm.amdgcn.workitem.id.x() 260927905eebSJoseph Huber if (auto *II = dyn_cast<IntrinsicInst>(Cmp->getOperand(0))) 261027905eebSJoseph Huber if (II->getIntrinsicID() == Intrinsic::amdgcn_workitem_id_x) 261127905eebSJoseph Huber return true; 261227905eebSJoseph Huber } 261327905eebSJoseph Huber 261418283125SJoseph Huber return false; 261518283125SJoseph Huber }; 261618283125SJoseph Huber 261718283125SJoseph Huber // Merge all the predecessor states into the current basic block. A basic 261818283125SJoseph Huber // block is executed by a single thread if all of its predecessors are. 261918283125SJoseph Huber auto MergePredecessorStates = [&](BasicBlock *BB) { 262098007313SKazu Hirata if (pred_empty(BB)) 262118283125SJoseph Huber return SingleThreadedBBs.contains(BB); 262218283125SJoseph Huber 26236fc51c9fSJoseph Huber bool IsInitialThread = true; 262498007313SKazu Hirata for (BasicBlock *PredBB : predecessors(BB)) { 262598007313SKazu Hirata if (!IsInitialThreadOnly(dyn_cast<BranchInst>(PredBB->getTerminator()), 262618283125SJoseph Huber BB)) 262798007313SKazu Hirata IsInitialThread &= SingleThreadedBBs.contains(PredBB); 262818283125SJoseph Huber } 262918283125SJoseph Huber 26306fc51c9fSJoseph Huber return IsInitialThread; 263118283125SJoseph Huber }; 263218283125SJoseph Huber 263318283125SJoseph Huber for (auto *BB : RPOT) { 263418283125SJoseph Huber if (!MergePredecessorStates(BB)) 26359f04a0eaSJohannes Doerfert SingleThreadedBBs.remove(BB); 263618283125SJoseph Huber } 263718283125SJoseph Huber 263818283125SJoseph Huber return (NumSingleThreadedBBs == SingleThreadedBBs.size()) 263918283125SJoseph Huber ? ChangeStatus::UNCHANGED 264018283125SJoseph Huber : ChangeStatus::CHANGED; 264118283125SJoseph Huber } 264218283125SJoseph Huber 26436fc51c9fSJoseph Huber /// Try to replace memory allocation calls called by a single thread with a 26446fc51c9fSJoseph Huber /// static buffer of shared memory. 26456fc51c9fSJoseph Huber struct AAHeapToShared : public StateWrapper<BooleanState, AbstractAttribute> { 26466fc51c9fSJoseph Huber using Base = StateWrapper<BooleanState, AbstractAttribute>; 26476fc51c9fSJoseph Huber AAHeapToShared(const IRPosition &IRP, Attributor &A) : Base(IRP) {} 26486fc51c9fSJoseph Huber 26496fc51c9fSJoseph Huber /// Create an abstract attribute view for the position \p IRP. 26506fc51c9fSJoseph Huber static AAHeapToShared &createForPosition(const IRPosition &IRP, 26516fc51c9fSJoseph Huber Attributor &A); 26526fc51c9fSJoseph Huber 2653f8c40ed8SGiorgis Georgakoudis /// Returns true if HeapToShared conversion is assumed to be possible. 2654f8c40ed8SGiorgis Georgakoudis virtual bool isAssumedHeapToShared(CallBase &CB) const = 0; 2655f8c40ed8SGiorgis Georgakoudis 2656f8c40ed8SGiorgis Georgakoudis /// Returns true if HeapToShared conversion is assumed and the CB is a 2657f8c40ed8SGiorgis Georgakoudis /// callsite to a free operation to be removed. 2658f8c40ed8SGiorgis Georgakoudis virtual bool isAssumedHeapToSharedRemovedFree(CallBase &CB) const = 0; 2659f8c40ed8SGiorgis Georgakoudis 26606fc51c9fSJoseph Huber /// See AbstractAttribute::getName(). 26616fc51c9fSJoseph Huber const std::string getName() const override { return "AAHeapToShared"; } 26626fc51c9fSJoseph Huber 26636fc51c9fSJoseph Huber /// See AbstractAttribute::getIdAddr(). 26646fc51c9fSJoseph Huber const char *getIdAddr() const override { return &ID; } 26656fc51c9fSJoseph Huber 26666fc51c9fSJoseph Huber /// This function should return true if the type of the \p AA is 26676fc51c9fSJoseph Huber /// AAHeapToShared. 26686fc51c9fSJoseph Huber static bool classof(const AbstractAttribute *AA) { 26696fc51c9fSJoseph Huber return (AA->getIdAddr() == &ID); 26706fc51c9fSJoseph Huber } 26716fc51c9fSJoseph Huber 26726fc51c9fSJoseph Huber /// Unique ID (due to the unique address) 26736fc51c9fSJoseph Huber static const char ID; 26746fc51c9fSJoseph Huber }; 26756fc51c9fSJoseph Huber 26766fc51c9fSJoseph Huber struct AAHeapToSharedFunction : public AAHeapToShared { 26776fc51c9fSJoseph Huber AAHeapToSharedFunction(const IRPosition &IRP, Attributor &A) 26786fc51c9fSJoseph Huber : AAHeapToShared(IRP, A) {} 26796fc51c9fSJoseph Huber 26806fc51c9fSJoseph Huber const std::string getAsStr() const override { 26816fc51c9fSJoseph Huber return "[AAHeapToShared] " + std::to_string(MallocCalls.size()) + 26826fc51c9fSJoseph Huber " malloc calls eligible."; 26836fc51c9fSJoseph Huber } 26846fc51c9fSJoseph Huber 26856fc51c9fSJoseph Huber /// See AbstractAttribute::trackStatistics(). 26866fc51c9fSJoseph Huber void trackStatistics() const override {} 26876fc51c9fSJoseph Huber 2688f8c40ed8SGiorgis Georgakoudis /// This functions finds free calls that will be removed by the 2689f8c40ed8SGiorgis Georgakoudis /// HeapToShared transformation. 2690f8c40ed8SGiorgis Georgakoudis void findPotentialRemovedFreeCalls(Attributor &A) { 2691f8c40ed8SGiorgis Georgakoudis auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2692f8c40ed8SGiorgis Georgakoudis auto &FreeRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_free_shared]; 2693f8c40ed8SGiorgis Georgakoudis 2694f8c40ed8SGiorgis Georgakoudis PotentialRemovedFreeCalls.clear(); 2695f8c40ed8SGiorgis Georgakoudis // Update free call users of found malloc calls. 2696f8c40ed8SGiorgis Georgakoudis for (CallBase *CB : MallocCalls) { 2697f8c40ed8SGiorgis Georgakoudis SmallVector<CallBase *, 4> FreeCalls; 2698f8c40ed8SGiorgis Georgakoudis for (auto *U : CB->users()) { 2699f8c40ed8SGiorgis Georgakoudis CallBase *C = dyn_cast<CallBase>(U); 2700f8c40ed8SGiorgis Georgakoudis if (C && C->getCalledFunction() == FreeRFI.Declaration) 2701f8c40ed8SGiorgis Georgakoudis FreeCalls.push_back(C); 2702f8c40ed8SGiorgis Georgakoudis } 2703f8c40ed8SGiorgis Georgakoudis 2704f8c40ed8SGiorgis Georgakoudis if (FreeCalls.size() != 1) 2705f8c40ed8SGiorgis Georgakoudis continue; 2706f8c40ed8SGiorgis Georgakoudis 2707f8c40ed8SGiorgis Georgakoudis PotentialRemovedFreeCalls.insert(FreeCalls.front()); 2708f8c40ed8SGiorgis Georgakoudis } 2709f8c40ed8SGiorgis Georgakoudis } 2710f8c40ed8SGiorgis Georgakoudis 27116fc51c9fSJoseph Huber void initialize(Attributor &A) override { 27126fc51c9fSJoseph Huber auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 27136fc51c9fSJoseph Huber auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared]; 27146fc51c9fSJoseph Huber 27156fc51c9fSJoseph Huber for (User *U : RFI.Declaration->users()) 27166fc51c9fSJoseph Huber if (CallBase *CB = dyn_cast<CallBase>(U)) 27176fc51c9fSJoseph Huber MallocCalls.insert(CB); 2718f8c40ed8SGiorgis Georgakoudis 2719f8c40ed8SGiorgis Georgakoudis findPotentialRemovedFreeCalls(A); 2720f8c40ed8SGiorgis Georgakoudis } 2721f8c40ed8SGiorgis Georgakoudis 2722eaab880eSGiorgis Georgakoudis bool isAssumedHeapToShared(CallBase &CB) const override { 2723f8c40ed8SGiorgis Georgakoudis return isValidState() && MallocCalls.count(&CB); 2724f8c40ed8SGiorgis Georgakoudis } 2725f8c40ed8SGiorgis Georgakoudis 2726eaab880eSGiorgis Georgakoudis bool isAssumedHeapToSharedRemovedFree(CallBase &CB) const override { 2727f8c40ed8SGiorgis Georgakoudis return isValidState() && PotentialRemovedFreeCalls.count(&CB); 27286fc51c9fSJoseph Huber } 27296fc51c9fSJoseph Huber 27306fc51c9fSJoseph Huber ChangeStatus manifest(Attributor &A) override { 27316fc51c9fSJoseph Huber if (MallocCalls.empty()) 27326fc51c9fSJoseph Huber return ChangeStatus::UNCHANGED; 27336fc51c9fSJoseph Huber 27346fc51c9fSJoseph Huber auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 27356fc51c9fSJoseph Huber auto &FreeCall = OMPInfoCache.RFIs[OMPRTL___kmpc_free_shared]; 27366fc51c9fSJoseph Huber 27376fc51c9fSJoseph Huber Function *F = getAnchorScope(); 27386fc51c9fSJoseph Huber auto *HS = A.lookupAAFor<AAHeapToStack>(IRPosition::function(*F), this, 27396fc51c9fSJoseph Huber DepClassTy::OPTIONAL); 27406fc51c9fSJoseph Huber 27416fc51c9fSJoseph Huber ChangeStatus Changed = ChangeStatus::UNCHANGED; 27426fc51c9fSJoseph Huber for (CallBase *CB : MallocCalls) { 27436fc51c9fSJoseph Huber // Skip replacing this if HeapToStack has already claimed it. 2744c1c1fe93SJohannes Doerfert if (HS && HS->isAssumedHeapToStack(*CB)) 27456fc51c9fSJoseph Huber continue; 27466fc51c9fSJoseph Huber 27476fc51c9fSJoseph Huber // Find the unique free call to remove it. 27486fc51c9fSJoseph Huber SmallVector<CallBase *, 4> FreeCalls; 27496fc51c9fSJoseph Huber for (auto *U : CB->users()) { 27506fc51c9fSJoseph Huber CallBase *C = dyn_cast<CallBase>(U); 27516fc51c9fSJoseph Huber if (C && C->getCalledFunction() == FreeCall.Declaration) 27526fc51c9fSJoseph Huber FreeCalls.push_back(C); 27536fc51c9fSJoseph Huber } 27546fc51c9fSJoseph Huber if (FreeCalls.size() != 1) 27556fc51c9fSJoseph Huber continue; 27566fc51c9fSJoseph Huber 2757274359cfSSimon Pilgrim auto *AllocSize = cast<ConstantInt>(CB->getArgOperand(0)); 27586fc51c9fSJoseph Huber 275913d8f000SJoseph Huber LLVM_DEBUG(dbgs() << TAG << "Replace globalization call " << *CB 276013d8f000SJoseph Huber << " with " << AllocSize->getZExtValue() 27616fc51c9fSJoseph Huber << " bytes of shared memory\n"); 27626fc51c9fSJoseph Huber 27636fc51c9fSJoseph Huber // Create a new shared memory buffer of the same size as the allocation 27646fc51c9fSJoseph Huber // and replace all the uses of the original allocation with it. 27656fc51c9fSJoseph Huber Module *M = CB->getModule(); 27666fc51c9fSJoseph Huber Type *Int8Ty = Type::getInt8Ty(M->getContext()); 27676fc51c9fSJoseph Huber Type *Int8ArrTy = ArrayType::get(Int8Ty, AllocSize->getZExtValue()); 27686fc51c9fSJoseph Huber auto *SharedMem = new GlobalVariable( 27696fc51c9fSJoseph Huber *M, Int8ArrTy, /* IsConstant */ false, GlobalValue::InternalLinkage, 27706fc51c9fSJoseph Huber UndefValue::get(Int8ArrTy), CB->getName(), nullptr, 27716fc51c9fSJoseph Huber GlobalValue::NotThreadLocal, 27726fc51c9fSJoseph Huber static_cast<unsigned>(AddressSpace::Shared)); 27736fc51c9fSJoseph Huber auto *NewBuffer = 27746fc51c9fSJoseph Huber ConstantExpr::getPointerCast(SharedMem, Int8Ty->getPointerTo()); 27756fc51c9fSJoseph Huber 277630e36c9bSJoseph Huber auto Remark = [&](OptimizationRemark OR) { 277730e36c9bSJoseph Huber return OR << "Replaced globalized variable with " 277830e36c9bSJoseph Huber << ore::NV("SharedMemory", AllocSize->getZExtValue()) 277930e36c9bSJoseph Huber << ((AllocSize->getZExtValue() != 1) ? " bytes " : " byte ") 2780eef6601bSJoseph Huber << "of shared memory."; 278130e36c9bSJoseph Huber }; 27822c31d5ebSJoseph Huber A.emitRemark<OptimizationRemark>(CB, "OMP111", Remark); 278330e36c9bSJoseph Huber 27846e220296SJoseph Huber MaybeAlign Alignment = CB->getRetAlign(); 27856e220296SJoseph Huber assert(Alignment && 27866e220296SJoseph Huber "HeapToShared on allocation without alignment attribute"); 27876e220296SJoseph Huber SharedMem->setAlignment(MaybeAlign(Alignment)); 27886fc51c9fSJoseph Huber 27896fc51c9fSJoseph Huber A.changeValueAfterManifest(*CB, *NewBuffer); 27906fc51c9fSJoseph Huber A.deleteAfterManifest(*CB); 27916fc51c9fSJoseph Huber A.deleteAfterManifest(*FreeCalls.front()); 27926fc51c9fSJoseph Huber 27936fc51c9fSJoseph Huber NumBytesMovedToSharedMemory += AllocSize->getZExtValue(); 27946fc51c9fSJoseph Huber Changed = ChangeStatus::CHANGED; 27956fc51c9fSJoseph Huber } 27966fc51c9fSJoseph Huber 27976fc51c9fSJoseph Huber return Changed; 27986fc51c9fSJoseph Huber } 27996fc51c9fSJoseph Huber 28006fc51c9fSJoseph Huber ChangeStatus updateImpl(Attributor &A) override { 28016fc51c9fSJoseph Huber auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 28026fc51c9fSJoseph Huber auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared]; 28036fc51c9fSJoseph Huber Function *F = getAnchorScope(); 28046fc51c9fSJoseph Huber 28056fc51c9fSJoseph Huber auto NumMallocCalls = MallocCalls.size(); 28066fc51c9fSJoseph Huber 28076fc51c9fSJoseph Huber // Only consider malloc calls executed by a single thread with a constant. 28086fc51c9fSJoseph Huber for (User *U : RFI.Declaration->users()) { 28096fc51c9fSJoseph Huber const auto &ED = A.getAAFor<AAExecutionDomain>( 28106fc51c9fSJoseph Huber *this, IRPosition::function(*F), DepClassTy::REQUIRED); 28116fc51c9fSJoseph Huber if (CallBase *CB = dyn_cast<CallBase>(U)) 2812d243cbf8SKazu Hirata if (!isa<ConstantInt>(CB->getArgOperand(0)) || 28136fc51c9fSJoseph Huber !ED.isExecutedByInitialThreadOnly(*CB)) 2814ba70f3a5SJohannes Doerfert MallocCalls.remove(CB); 28156fc51c9fSJoseph Huber } 28166fc51c9fSJoseph Huber 2817f8c40ed8SGiorgis Georgakoudis findPotentialRemovedFreeCalls(A); 2818f8c40ed8SGiorgis Georgakoudis 28196fc51c9fSJoseph Huber if (NumMallocCalls != MallocCalls.size()) 28206fc51c9fSJoseph Huber return ChangeStatus::CHANGED; 28216fc51c9fSJoseph Huber 28226fc51c9fSJoseph Huber return ChangeStatus::UNCHANGED; 28236fc51c9fSJoseph Huber } 28246fc51c9fSJoseph Huber 28256fc51c9fSJoseph Huber /// Collection of all malloc calls in a function. 2826ba70f3a5SJohannes Doerfert SmallSetVector<CallBase *, 4> MallocCalls; 2827f8c40ed8SGiorgis Georgakoudis /// Collection of potentially removed free calls in a function. 2828f8c40ed8SGiorgis Georgakoudis SmallPtrSet<CallBase *, 4> PotentialRemovedFreeCalls; 28296fc51c9fSJoseph Huber }; 28306fc51c9fSJoseph Huber 2831d9659bf6SJohannes Doerfert struct AAKernelInfo : public StateWrapper<KernelInfoState, AbstractAttribute> { 2832d9659bf6SJohannes Doerfert using Base = StateWrapper<KernelInfoState, AbstractAttribute>; 2833d9659bf6SJohannes Doerfert AAKernelInfo(const IRPosition &IRP, Attributor &A) : Base(IRP) {} 2834d9659bf6SJohannes Doerfert 2835d9659bf6SJohannes Doerfert /// Statistics are tracked as part of manifest for now. 2836d9659bf6SJohannes Doerfert void trackStatistics() const override {} 2837d9659bf6SJohannes Doerfert 2838d9659bf6SJohannes Doerfert /// See AbstractAttribute::getAsStr() 2839d9659bf6SJohannes Doerfert const std::string getAsStr() const override { 2840d9659bf6SJohannes Doerfert if (!isValidState()) 2841d9659bf6SJohannes Doerfert return "<invalid>"; 2842514c033dSJohannes Doerfert return std::string(SPMDCompatibilityTracker.isAssumed() ? "SPMD" 2843514c033dSJohannes Doerfert : "generic") + 2844514c033dSJohannes Doerfert std::string(SPMDCompatibilityTracker.isAtFixpoint() ? " [FIX]" 2845514c033dSJohannes Doerfert : "") + 2846d9659bf6SJohannes Doerfert std::string(" #PRs: ") + 2847c6457dcaSJohannes Doerfert (ReachedKnownParallelRegions.isValidState() 2848c6457dcaSJohannes Doerfert ? std::to_string(ReachedKnownParallelRegions.size()) 2849c6457dcaSJohannes Doerfert : "<invalid>") + 2850d9659bf6SJohannes Doerfert ", #Unknown PRs: " + 2851c6457dcaSJohannes Doerfert (ReachedUnknownParallelRegions.isValidState() 2852c6457dcaSJohannes Doerfert ? std::to_string(ReachedUnknownParallelRegions.size()) 2853c6457dcaSJohannes Doerfert : "<invalid>") + 28540a16c560SJohannes Doerfert ", #Reaching Kernels: " + 28550a16c560SJohannes Doerfert (ReachingKernelEntries.isValidState() 28560a16c560SJohannes Doerfert ? std::to_string(ReachingKernelEntries.size()) 28570a16c560SJohannes Doerfert : "<invalid>"); 2858d9659bf6SJohannes Doerfert } 2859d9659bf6SJohannes Doerfert 2860d9659bf6SJohannes Doerfert /// Create an abstract attribute biew for the position \p IRP. 2861d9659bf6SJohannes Doerfert static AAKernelInfo &createForPosition(const IRPosition &IRP, Attributor &A); 2862d9659bf6SJohannes Doerfert 2863d9659bf6SJohannes Doerfert /// See AbstractAttribute::getName() 2864d9659bf6SJohannes Doerfert const std::string getName() const override { return "AAKernelInfo"; } 2865d9659bf6SJohannes Doerfert 2866d9659bf6SJohannes Doerfert /// See AbstractAttribute::getIdAddr() 2867d9659bf6SJohannes Doerfert const char *getIdAddr() const override { return &ID; } 2868d9659bf6SJohannes Doerfert 2869d9659bf6SJohannes Doerfert /// This function should return true if the type of the \p AA is AAKernelInfo 2870d9659bf6SJohannes Doerfert static bool classof(const AbstractAttribute *AA) { 2871d9659bf6SJohannes Doerfert return (AA->getIdAddr() == &ID); 2872d9659bf6SJohannes Doerfert } 2873d9659bf6SJohannes Doerfert 2874d9659bf6SJohannes Doerfert static const char ID; 2875d9659bf6SJohannes Doerfert }; 2876d9659bf6SJohannes Doerfert 2877d9659bf6SJohannes Doerfert /// The function kernel info abstract attribute, basically, what can we say 2878d9659bf6SJohannes Doerfert /// about a function with regards to the KernelInfoState. 2879d9659bf6SJohannes Doerfert struct AAKernelInfoFunction : AAKernelInfo { 2880d9659bf6SJohannes Doerfert AAKernelInfoFunction(const IRPosition &IRP, Attributor &A) 2881d9659bf6SJohannes Doerfert : AAKernelInfo(IRP, A) {} 2882d9659bf6SJohannes Doerfert 288329a3e3ddSGiorgis Georgakoudis SmallPtrSet<Instruction *, 4> GuardedInstructions; 288429a3e3ddSGiorgis Georgakoudis 288529a3e3ddSGiorgis Georgakoudis SmallPtrSetImpl<Instruction *> &getGuardedInstructions() { 288629a3e3ddSGiorgis Georgakoudis return GuardedInstructions; 288729a3e3ddSGiorgis Georgakoudis } 288829a3e3ddSGiorgis Georgakoudis 2889d9659bf6SJohannes Doerfert /// See AbstractAttribute::initialize(...). 2890d9659bf6SJohannes Doerfert void initialize(Attributor &A) override { 2891d9659bf6SJohannes Doerfert // This is a high-level transform that might change the constant arguments 2892d9659bf6SJohannes Doerfert // of the init and dinit calls. We need to tell the Attributor about this 2893d9659bf6SJohannes Doerfert // to avoid other parts using the current constant value for simpliication. 2894d9659bf6SJohannes Doerfert auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 2895d9659bf6SJohannes Doerfert 2896d9659bf6SJohannes Doerfert Function *Fn = getAnchorScope(); 2897d9659bf6SJohannes Doerfert if (!OMPInfoCache.Kernels.count(Fn)) 2898d9659bf6SJohannes Doerfert return; 2899d9659bf6SJohannes Doerfert 2900ca662297SShilei Tian // Add itself to the reaching kernel and set IsKernelEntry. 2901ca662297SShilei Tian ReachingKernelEntries.insert(Fn); 2902ca662297SShilei Tian IsKernelEntry = true; 2903ca662297SShilei Tian 2904d9659bf6SJohannes Doerfert OMPInformationCache::RuntimeFunctionInfo &InitRFI = 2905d9659bf6SJohannes Doerfert OMPInfoCache.RFIs[OMPRTL___kmpc_target_init]; 2906d9659bf6SJohannes Doerfert OMPInformationCache::RuntimeFunctionInfo &DeinitRFI = 2907d9659bf6SJohannes Doerfert OMPInfoCache.RFIs[OMPRTL___kmpc_target_deinit]; 2908d9659bf6SJohannes Doerfert 2909d9659bf6SJohannes Doerfert // For kernels we perform more initialization work, first we find the init 2910d9659bf6SJohannes Doerfert // and deinit calls. 2911d9659bf6SJohannes Doerfert auto StoreCallBase = [](Use &U, 2912d9659bf6SJohannes Doerfert OMPInformationCache::RuntimeFunctionInfo &RFI, 2913d9659bf6SJohannes Doerfert CallBase *&Storage) { 2914d9659bf6SJohannes Doerfert CallBase *CB = OpenMPOpt::getCallIfRegularCall(U, &RFI); 2915d9659bf6SJohannes Doerfert assert(CB && 2916d9659bf6SJohannes Doerfert "Unexpected use of __kmpc_target_init or __kmpc_target_deinit!"); 2917d9659bf6SJohannes Doerfert assert(!Storage && 2918d9659bf6SJohannes Doerfert "Multiple uses of __kmpc_target_init or __kmpc_target_deinit!"); 2919d9659bf6SJohannes Doerfert Storage = CB; 2920d9659bf6SJohannes Doerfert return false; 2921d9659bf6SJohannes Doerfert }; 2922d9659bf6SJohannes Doerfert InitRFI.foreachUse( 2923d9659bf6SJohannes Doerfert [&](Use &U, Function &) { 2924d9659bf6SJohannes Doerfert StoreCallBase(U, InitRFI, KernelInitCB); 2925d9659bf6SJohannes Doerfert return false; 2926d9659bf6SJohannes Doerfert }, 2927d9659bf6SJohannes Doerfert Fn); 2928d9659bf6SJohannes Doerfert DeinitRFI.foreachUse( 2929d9659bf6SJohannes Doerfert [&](Use &U, Function &) { 2930d9659bf6SJohannes Doerfert StoreCallBase(U, DeinitRFI, KernelDeinitCB); 2931d9659bf6SJohannes Doerfert return false; 2932d9659bf6SJohannes Doerfert }, 2933d9659bf6SJohannes Doerfert Fn); 2934d9659bf6SJohannes Doerfert 293558f93264SJoseph Huber // Ignore kernels without initializers such as global constructors. 293658f93264SJoseph Huber if (!KernelInitCB || !KernelDeinitCB) { 293758f93264SJoseph Huber indicateOptimisticFixpoint(); 293858f93264SJoseph Huber return; 293958f93264SJoseph Huber } 2940d9659bf6SJohannes Doerfert 2941514c033dSJohannes Doerfert // For kernels we might need to initialize/finalize the IsSPMD state and 2942514c033dSJohannes Doerfert // we need to register a simplification callback so that the Attributor 2943514c033dSJohannes Doerfert // knows the constant arguments to __kmpc_target_init and 2944d9659bf6SJohannes Doerfert // __kmpc_target_deinit might actually change. 2945d9659bf6SJohannes Doerfert 2946d9659bf6SJohannes Doerfert Attributor::SimplifictionCallbackTy StateMachineSimplifyCB = 2947d9659bf6SJohannes Doerfert [&](const IRPosition &IRP, const AbstractAttribute *AA, 2948d9659bf6SJohannes Doerfert bool &UsedAssumedInformation) -> Optional<Value *> { 2949d9659bf6SJohannes Doerfert // IRP represents the "use generic state machine" argument of an 2950d9659bf6SJohannes Doerfert // __kmpc_target_init call. We will answer this one with the internal 2951d9659bf6SJohannes Doerfert // state. As long as we are not in an invalid state, we will create a 2952d9659bf6SJohannes Doerfert // custom state machine so the value should be a `i1 false`. If we are 2953d9659bf6SJohannes Doerfert // in an invalid state, we won't change the value that is in the IR. 2954e6e440aeSJohannes Doerfert if (!ReachedKnownParallelRegions.isValidState()) 2955d9659bf6SJohannes Doerfert return nullptr; 2956e0c5d83aSJohannes Doerfert // If we have disabled state machine rewrites, don't make a custom one. 2957e0c5d83aSJohannes Doerfert if (DisableOpenMPOptStateMachineRewrite) 2958e0c5d83aSJohannes Doerfert return nullptr; 2959d9659bf6SJohannes Doerfert if (AA) 2960d9659bf6SJohannes Doerfert A.recordDependence(*this, *AA, DepClassTy::OPTIONAL); 2961d9659bf6SJohannes Doerfert UsedAssumedInformation = !isAtFixpoint(); 2962d9659bf6SJohannes Doerfert auto *FalseVal = 29632aed0813SKazu Hirata ConstantInt::getBool(IRP.getAnchorValue().getContext(), false); 2964d9659bf6SJohannes Doerfert return FalseVal; 2965d9659bf6SJohannes Doerfert }; 2966d9659bf6SJohannes Doerfert 2967423d34f7SShilei Tian Attributor::SimplifictionCallbackTy ModeSimplifyCB = 2968514c033dSJohannes Doerfert [&](const IRPosition &IRP, const AbstractAttribute *AA, 2969514c033dSJohannes Doerfert bool &UsedAssumedInformation) -> Optional<Value *> { 2970514c033dSJohannes Doerfert // IRP represents the "SPMDCompatibilityTracker" argument of an 2971514c033dSJohannes Doerfert // __kmpc_target_init or 2972514c033dSJohannes Doerfert // __kmpc_target_deinit call. We will answer this one with the internal 2973514c033dSJohannes Doerfert // state. 297497387fdfSJohannes Doerfert if (!SPMDCompatibilityTracker.isValidState()) 2975514c033dSJohannes Doerfert return nullptr; 2976514c033dSJohannes Doerfert if (!SPMDCompatibilityTracker.isAtFixpoint()) { 2977514c033dSJohannes Doerfert if (AA) 2978514c033dSJohannes Doerfert A.recordDependence(*this, *AA, DepClassTy::OPTIONAL); 2979514c033dSJohannes Doerfert UsedAssumedInformation = true; 2980514c033dSJohannes Doerfert } else { 2981514c033dSJohannes Doerfert UsedAssumedInformation = false; 2982514c033dSJohannes Doerfert } 2983423d34f7SShilei Tian auto *Val = ConstantInt::getSigned( 2984423d34f7SShilei Tian IntegerType::getInt8Ty(IRP.getAnchorValue().getContext()), 2985423d34f7SShilei Tian SPMDCompatibilityTracker.isAssumed() ? OMP_TGT_EXEC_MODE_SPMD 2986423d34f7SShilei Tian : OMP_TGT_EXEC_MODE_GENERIC); 2987514c033dSJohannes Doerfert return Val; 2988514c033dSJohannes Doerfert }; 2989514c033dSJohannes Doerfert 2990e8439ec8SGiorgis Georgakoudis Attributor::SimplifictionCallbackTy IsGenericModeSimplifyCB = 2991e8439ec8SGiorgis Georgakoudis [&](const IRPosition &IRP, const AbstractAttribute *AA, 2992e8439ec8SGiorgis Georgakoudis bool &UsedAssumedInformation) -> Optional<Value *> { 2993e8439ec8SGiorgis Georgakoudis // IRP represents the "RequiresFullRuntime" argument of an 2994e8439ec8SGiorgis Georgakoudis // __kmpc_target_init or __kmpc_target_deinit call. We will answer this 2995e8439ec8SGiorgis Georgakoudis // one with the internal state of the SPMDCompatibilityTracker, so if 2996e8439ec8SGiorgis Georgakoudis // generic then true, if SPMD then false. 2997e8439ec8SGiorgis Georgakoudis if (!SPMDCompatibilityTracker.isValidState()) 2998e8439ec8SGiorgis Georgakoudis return nullptr; 2999e8439ec8SGiorgis Georgakoudis if (!SPMDCompatibilityTracker.isAtFixpoint()) { 3000e8439ec8SGiorgis Georgakoudis if (AA) 3001e8439ec8SGiorgis Georgakoudis A.recordDependence(*this, *AA, DepClassTy::OPTIONAL); 3002e8439ec8SGiorgis Georgakoudis UsedAssumedInformation = true; 3003e8439ec8SGiorgis Georgakoudis } else { 3004e8439ec8SGiorgis Georgakoudis UsedAssumedInformation = false; 3005e8439ec8SGiorgis Georgakoudis } 3006e8439ec8SGiorgis Georgakoudis auto *Val = ConstantInt::getBool(IRP.getAnchorValue().getContext(), 3007e8439ec8SGiorgis Georgakoudis !SPMDCompatibilityTracker.isAssumed()); 3008e8439ec8SGiorgis Georgakoudis return Val; 3009e8439ec8SGiorgis Georgakoudis }; 3010e8439ec8SGiorgis Georgakoudis 3011423d34f7SShilei Tian constexpr const int InitModeArgNo = 1; 3012423d34f7SShilei Tian constexpr const int DeinitModeArgNo = 1; 3013d9659bf6SJohannes Doerfert constexpr const int InitUseStateMachineArgNo = 2; 3014e8439ec8SGiorgis Georgakoudis constexpr const int InitRequiresFullRuntimeArgNo = 3; 3015e8439ec8SGiorgis Georgakoudis constexpr const int DeinitRequiresFullRuntimeArgNo = 2; 3016d9659bf6SJohannes Doerfert A.registerSimplificationCallback( 3017d9659bf6SJohannes Doerfert IRPosition::callsite_argument(*KernelInitCB, InitUseStateMachineArgNo), 3018d9659bf6SJohannes Doerfert StateMachineSimplifyCB); 3019514c033dSJohannes Doerfert A.registerSimplificationCallback( 3020423d34f7SShilei Tian IRPosition::callsite_argument(*KernelInitCB, InitModeArgNo), 3021423d34f7SShilei Tian ModeSimplifyCB); 3022514c033dSJohannes Doerfert A.registerSimplificationCallback( 3023423d34f7SShilei Tian IRPosition::callsite_argument(*KernelDeinitCB, DeinitModeArgNo), 3024423d34f7SShilei Tian ModeSimplifyCB); 3025e8439ec8SGiorgis Georgakoudis A.registerSimplificationCallback( 3026e8439ec8SGiorgis Georgakoudis IRPosition::callsite_argument(*KernelInitCB, 3027e8439ec8SGiorgis Georgakoudis InitRequiresFullRuntimeArgNo), 3028e8439ec8SGiorgis Georgakoudis IsGenericModeSimplifyCB); 3029e8439ec8SGiorgis Georgakoudis A.registerSimplificationCallback( 3030e8439ec8SGiorgis Georgakoudis IRPosition::callsite_argument(*KernelDeinitCB, 3031e8439ec8SGiorgis Georgakoudis DeinitRequiresFullRuntimeArgNo), 3032e8439ec8SGiorgis Georgakoudis IsGenericModeSimplifyCB); 3033514c033dSJohannes Doerfert 3034514c033dSJohannes Doerfert // Check if we know we are in SPMD-mode already. 3035423d34f7SShilei Tian ConstantInt *ModeArg = 3036423d34f7SShilei Tian dyn_cast<ConstantInt>(KernelInitCB->getArgOperand(InitModeArgNo)); 3037423d34f7SShilei Tian if (ModeArg && (ModeArg->getSExtValue() & OMP_TGT_EXEC_MODE_SPMD)) 3038514c033dSJohannes Doerfert SPMDCompatibilityTracker.indicateOptimisticFixpoint(); 303960e643feSGiorgis Georgakoudis // This is a generic region but SPMDization is disabled so stop tracking. 304060e643feSGiorgis Georgakoudis else if (DisableOpenMPOptSPMDization) 304160e643feSGiorgis Georgakoudis SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 3042d9659bf6SJohannes Doerfert } 3043d9659bf6SJohannes Doerfert 3044e6e440aeSJohannes Doerfert /// Sanitize the string \p S such that it is a suitable global symbol name. 3045e6e440aeSJohannes Doerfert static std::string sanitizeForGlobalName(std::string S) { 3046e6e440aeSJohannes Doerfert std::replace_if( 3047e6e440aeSJohannes Doerfert S.begin(), S.end(), 3048e6e440aeSJohannes Doerfert [](const char C) { 3049e6e440aeSJohannes Doerfert return !((C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z') || 3050e6e440aeSJohannes Doerfert (C >= '0' && C <= '9') || C == '_'); 3051e6e440aeSJohannes Doerfert }, 3052e6e440aeSJohannes Doerfert '.'); 3053e6e440aeSJohannes Doerfert return S; 3054e6e440aeSJohannes Doerfert } 3055e6e440aeSJohannes Doerfert 3056d9659bf6SJohannes Doerfert /// Modify the IR based on the KernelInfoState as the fixpoint iteration is 3057d9659bf6SJohannes Doerfert /// finished now. 3058d9659bf6SJohannes Doerfert ChangeStatus manifest(Attributor &A) override { 3059d9659bf6SJohannes Doerfert // If we are not looking at a kernel with __kmpc_target_init and 3060d9659bf6SJohannes Doerfert // __kmpc_target_deinit call we cannot actually manifest the information. 3061d9659bf6SJohannes Doerfert if (!KernelInitCB || !KernelDeinitCB) 3062d9659bf6SJohannes Doerfert return ChangeStatus::UNCHANGED; 3063d9659bf6SJohannes Doerfert 3064514c033dSJohannes Doerfert // If we can we change the execution mode to SPMD-mode otherwise we build a 3065514c033dSJohannes Doerfert // custom state machine. 3066d61aac76SJohannes Doerfert ChangeStatus Changed = ChangeStatus::UNCHANGED; 3067d61aac76SJohannes Doerfert if (!changeToSPMDMode(A, Changed)) 3068c6457dcaSJohannes Doerfert return buildCustomStateMachine(A); 3069d9659bf6SJohannes Doerfert 3070d61aac76SJohannes Doerfert return Changed; 3071d9659bf6SJohannes Doerfert } 3072d9659bf6SJohannes Doerfert 3073d61aac76SJohannes Doerfert bool changeToSPMDMode(Attributor &A, ChangeStatus &Changed) { 3074eef6601bSJoseph Huber auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 3075eef6601bSJoseph Huber 3076514c033dSJohannes Doerfert if (!SPMDCompatibilityTracker.isAssumed()) { 3077514c033dSJohannes Doerfert for (Instruction *NonCompatibleI : SPMDCompatibilityTracker) { 3078514c033dSJohannes Doerfert if (!NonCompatibleI) 3079514c033dSJohannes Doerfert continue; 3080eef6601bSJoseph Huber 3081eef6601bSJoseph Huber // Skip diagnostics on calls to known OpenMP runtime functions for now. 3082eef6601bSJoseph Huber if (auto *CB = dyn_cast<CallBase>(NonCompatibleI)) 3083eef6601bSJoseph Huber if (OMPInfoCache.RTLFunctions.contains(CB->getCalledFunction())) 3084eef6601bSJoseph Huber continue; 3085eef6601bSJoseph Huber 3086514c033dSJohannes Doerfert auto Remark = [&](OptimizationRemarkAnalysis ORA) { 3087eef6601bSJoseph Huber ORA << "Value has potential side effects preventing SPMD-mode " 3088eef6601bSJoseph Huber "execution"; 3089eef6601bSJoseph Huber if (isa<CallBase>(NonCompatibleI)) { 3090eef6601bSJoseph Huber ORA << ". Add `__attribute__((assume(\"ompx_spmd_amenable\")))` to " 3091eef6601bSJoseph Huber "the called function to override"; 3092514c033dSJohannes Doerfert } 3093514c033dSJohannes Doerfert return ORA << "."; 3094514c033dSJohannes Doerfert }; 30952c31d5ebSJoseph Huber A.emitRemark<OptimizationRemarkAnalysis>(NonCompatibleI, "OMP121", 30962c31d5ebSJoseph Huber Remark); 3097514c033dSJohannes Doerfert 3098514c033dSJohannes Doerfert LLVM_DEBUG(dbgs() << TAG << "SPMD-incompatible side-effect: " 3099514c033dSJohannes Doerfert << *NonCompatibleI << "\n"); 3100514c033dSJohannes Doerfert } 3101514c033dSJohannes Doerfert 3102514c033dSJohannes Doerfert return false; 3103514c033dSJohannes Doerfert } 3104514c033dSJohannes Doerfert 3105d61aac76SJohannes Doerfert // Check if the kernel is already in SPMD mode, if so, return success. 3106d61aac76SJohannes Doerfert Function *Kernel = getAnchorScope(); 3107d61aac76SJohannes Doerfert GlobalVariable *ExecMode = Kernel->getParent()->getGlobalVariable( 3108d61aac76SJohannes Doerfert (Kernel->getName() + "_exec_mode").str()); 3109d61aac76SJohannes Doerfert assert(ExecMode && "Kernel without exec mode?"); 3110d61aac76SJohannes Doerfert assert(ExecMode->getInitializer() && "ExecMode doesn't have initializer!"); 3111d61aac76SJohannes Doerfert 3112d61aac76SJohannes Doerfert // Set the global exec mode flag to indicate SPMD-Generic mode. 3113d61aac76SJohannes Doerfert assert(isa<ConstantInt>(ExecMode->getInitializer()) && 3114d61aac76SJohannes Doerfert "ExecMode is not an integer!"); 3115d61aac76SJohannes Doerfert const int8_t ExecModeVal = 3116d61aac76SJohannes Doerfert cast<ConstantInt>(ExecMode->getInitializer())->getSExtValue(); 3117d61aac76SJohannes Doerfert if (ExecModeVal != OMP_TGT_EXEC_MODE_GENERIC) 3118d61aac76SJohannes Doerfert return true; 3119d61aac76SJohannes Doerfert 3120d61aac76SJohannes Doerfert // We will now unconditionally modify the IR, indicate a change. 3121d61aac76SJohannes Doerfert Changed = ChangeStatus::CHANGED; 3122d61aac76SJohannes Doerfert 312329a3e3ddSGiorgis Georgakoudis auto CreateGuardedRegion = [&](Instruction *RegionStartI, 312429a3e3ddSGiorgis Georgakoudis Instruction *RegionEndI) { 312529a3e3ddSGiorgis Georgakoudis LoopInfo *LI = nullptr; 312629a3e3ddSGiorgis Georgakoudis DominatorTree *DT = nullptr; 312729a3e3ddSGiorgis Georgakoudis MemorySSAUpdater *MSU = nullptr; 312829a3e3ddSGiorgis Georgakoudis using InsertPointTy = OpenMPIRBuilder::InsertPointTy; 312929a3e3ddSGiorgis Georgakoudis 313029a3e3ddSGiorgis Georgakoudis BasicBlock *ParentBB = RegionStartI->getParent(); 313129a3e3ddSGiorgis Georgakoudis Function *Fn = ParentBB->getParent(); 313229a3e3ddSGiorgis Georgakoudis Module &M = *Fn->getParent(); 313329a3e3ddSGiorgis Georgakoudis 313429a3e3ddSGiorgis Georgakoudis // Create all the blocks and logic. 313529a3e3ddSGiorgis Georgakoudis // ParentBB: 313629a3e3ddSGiorgis Georgakoudis // goto RegionCheckTidBB 313729a3e3ddSGiorgis Georgakoudis // RegionCheckTidBB: 313829a3e3ddSGiorgis Georgakoudis // Tid = __kmpc_hardware_thread_id() 313929a3e3ddSGiorgis Georgakoudis // if (Tid != 0) 314029a3e3ddSGiorgis Georgakoudis // goto RegionBarrierBB 314129a3e3ddSGiorgis Georgakoudis // RegionStartBB: 314229a3e3ddSGiorgis Georgakoudis // <execute instructions guarded> 314329a3e3ddSGiorgis Georgakoudis // goto RegionEndBB 314429a3e3ddSGiorgis Georgakoudis // RegionEndBB: 314529a3e3ddSGiorgis Georgakoudis // <store escaping values to shared mem> 314629a3e3ddSGiorgis Georgakoudis // goto RegionBarrierBB 314729a3e3ddSGiorgis Georgakoudis // RegionBarrierBB: 314829a3e3ddSGiorgis Georgakoudis // __kmpc_simple_barrier_spmd() 314929a3e3ddSGiorgis Georgakoudis // // second barrier is omitted if lacking escaping values. 315029a3e3ddSGiorgis Georgakoudis // <load escaping values from shared mem> 315129a3e3ddSGiorgis Georgakoudis // __kmpc_simple_barrier_spmd() 315229a3e3ddSGiorgis Georgakoudis // goto RegionExitBB 315329a3e3ddSGiorgis Georgakoudis // RegionExitBB: 315429a3e3ddSGiorgis Georgakoudis // <execute rest of instructions> 315529a3e3ddSGiorgis Georgakoudis 315629a3e3ddSGiorgis Georgakoudis BasicBlock *RegionEndBB = SplitBlock(ParentBB, RegionEndI->getNextNode(), 315729a3e3ddSGiorgis Georgakoudis DT, LI, MSU, "region.guarded.end"); 315829a3e3ddSGiorgis Georgakoudis BasicBlock *RegionBarrierBB = 315929a3e3ddSGiorgis Georgakoudis SplitBlock(RegionEndBB, &*RegionEndBB->getFirstInsertionPt(), DT, LI, 316029a3e3ddSGiorgis Georgakoudis MSU, "region.barrier"); 316129a3e3ddSGiorgis Georgakoudis BasicBlock *RegionExitBB = 316229a3e3ddSGiorgis Georgakoudis SplitBlock(RegionBarrierBB, &*RegionBarrierBB->getFirstInsertionPt(), 316329a3e3ddSGiorgis Georgakoudis DT, LI, MSU, "region.exit"); 316429a3e3ddSGiorgis Georgakoudis BasicBlock *RegionStartBB = 316529a3e3ddSGiorgis Georgakoudis SplitBlock(ParentBB, RegionStartI, DT, LI, MSU, "region.guarded"); 316629a3e3ddSGiorgis Georgakoudis 316729a3e3ddSGiorgis Georgakoudis assert(ParentBB->getUniqueSuccessor() == RegionStartBB && 316829a3e3ddSGiorgis Georgakoudis "Expected a different CFG"); 316929a3e3ddSGiorgis Georgakoudis 317029a3e3ddSGiorgis Georgakoudis BasicBlock *RegionCheckTidBB = SplitBlock( 317129a3e3ddSGiorgis Georgakoudis ParentBB, ParentBB->getTerminator(), DT, LI, MSU, "region.check.tid"); 317229a3e3ddSGiorgis Georgakoudis 317329a3e3ddSGiorgis Georgakoudis // Register basic blocks with the Attributor. 317429a3e3ddSGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*RegionEndBB); 317529a3e3ddSGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*RegionBarrierBB); 317629a3e3ddSGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*RegionExitBB); 317729a3e3ddSGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*RegionStartBB); 317829a3e3ddSGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*RegionCheckTidBB); 317929a3e3ddSGiorgis Georgakoudis 318029a3e3ddSGiorgis Georgakoudis bool HasBroadcastValues = false; 318129a3e3ddSGiorgis Georgakoudis // Find escaping outputs from the guarded region to outside users and 318229a3e3ddSGiorgis Georgakoudis // broadcast their values to them. 318329a3e3ddSGiorgis Georgakoudis for (Instruction &I : *RegionStartBB) { 318429a3e3ddSGiorgis Georgakoudis SmallPtrSet<Instruction *, 4> OutsideUsers; 318529a3e3ddSGiorgis Georgakoudis for (User *Usr : I.users()) { 318629a3e3ddSGiorgis Georgakoudis Instruction &UsrI = *cast<Instruction>(Usr); 318729a3e3ddSGiorgis Georgakoudis if (UsrI.getParent() != RegionStartBB) 318829a3e3ddSGiorgis Georgakoudis OutsideUsers.insert(&UsrI); 318929a3e3ddSGiorgis Georgakoudis } 319029a3e3ddSGiorgis Georgakoudis 319129a3e3ddSGiorgis Georgakoudis if (OutsideUsers.empty()) 319229a3e3ddSGiorgis Georgakoudis continue; 319329a3e3ddSGiorgis Georgakoudis 319429a3e3ddSGiorgis Georgakoudis HasBroadcastValues = true; 319529a3e3ddSGiorgis Georgakoudis 319629a3e3ddSGiorgis Georgakoudis // Emit a global variable in shared memory to store the broadcasted 319729a3e3ddSGiorgis Georgakoudis // value. 319829a3e3ddSGiorgis Georgakoudis auto *SharedMem = new GlobalVariable( 319929a3e3ddSGiorgis Georgakoudis M, I.getType(), /* IsConstant */ false, 320029a3e3ddSGiorgis Georgakoudis GlobalValue::InternalLinkage, UndefValue::get(I.getType()), 3201e6e440aeSJohannes Doerfert sanitizeForGlobalName( 3202e6e440aeSJohannes Doerfert (I.getName() + ".guarded.output.alloc").str()), 3203e6e440aeSJohannes Doerfert nullptr, GlobalValue::NotThreadLocal, 320429a3e3ddSGiorgis Georgakoudis static_cast<unsigned>(AddressSpace::Shared)); 320529a3e3ddSGiorgis Georgakoudis 320629a3e3ddSGiorgis Georgakoudis // Emit a store instruction to update the value. 320729a3e3ddSGiorgis Georgakoudis new StoreInst(&I, SharedMem, RegionEndBB->getTerminator()); 320829a3e3ddSGiorgis Georgakoudis 320929a3e3ddSGiorgis Georgakoudis LoadInst *LoadI = new LoadInst(I.getType(), SharedMem, 321029a3e3ddSGiorgis Georgakoudis I.getName() + ".guarded.output.load", 321129a3e3ddSGiorgis Georgakoudis RegionBarrierBB->getTerminator()); 321229a3e3ddSGiorgis Georgakoudis 321329a3e3ddSGiorgis Georgakoudis // Emit a load instruction and replace uses of the output value. 3214e6e440aeSJohannes Doerfert for (Instruction *UsrI : OutsideUsers) 321529a3e3ddSGiorgis Georgakoudis UsrI->replaceUsesOfWith(&I, LoadI); 321629a3e3ddSGiorgis Georgakoudis } 321729a3e3ddSGiorgis Georgakoudis 321829a3e3ddSGiorgis Georgakoudis auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 321929a3e3ddSGiorgis Georgakoudis 322029a3e3ddSGiorgis Georgakoudis // Go to tid check BB in ParentBB. 322129a3e3ddSGiorgis Georgakoudis const DebugLoc DL = ParentBB->getTerminator()->getDebugLoc(); 322229a3e3ddSGiorgis Georgakoudis ParentBB->getTerminator()->eraseFromParent(); 322329a3e3ddSGiorgis Georgakoudis OpenMPIRBuilder::LocationDescription Loc( 322429a3e3ddSGiorgis Georgakoudis InsertPointTy(ParentBB, ParentBB->end()), DL); 322529a3e3ddSGiorgis Georgakoudis OMPInfoCache.OMPBuilder.updateToLocation(Loc); 3226944aa042SJohannes Doerfert uint32_t SrcLocStrSize; 3227944aa042SJohannes Doerfert auto *SrcLocStr = 3228944aa042SJohannes Doerfert OMPInfoCache.OMPBuilder.getOrCreateSrcLocStr(Loc, SrcLocStrSize); 3229944aa042SJohannes Doerfert Value *Ident = 3230944aa042SJohannes Doerfert OMPInfoCache.OMPBuilder.getOrCreateIdent(SrcLocStr, SrcLocStrSize); 323129a3e3ddSGiorgis Georgakoudis BranchInst::Create(RegionCheckTidBB, ParentBB)->setDebugLoc(DL); 323229a3e3ddSGiorgis Georgakoudis 323329a3e3ddSGiorgis Georgakoudis // Add check for Tid in RegionCheckTidBB 323429a3e3ddSGiorgis Georgakoudis RegionCheckTidBB->getTerminator()->eraseFromParent(); 323529a3e3ddSGiorgis Georgakoudis OpenMPIRBuilder::LocationDescription LocRegionCheckTid( 323629a3e3ddSGiorgis Georgakoudis InsertPointTy(RegionCheckTidBB, RegionCheckTidBB->end()), DL); 323729a3e3ddSGiorgis Georgakoudis OMPInfoCache.OMPBuilder.updateToLocation(LocRegionCheckTid); 323829a3e3ddSGiorgis Georgakoudis FunctionCallee HardwareTidFn = 323929a3e3ddSGiorgis Georgakoudis OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 324029a3e3ddSGiorgis Georgakoudis M, OMPRTL___kmpc_get_hardware_thread_id_in_block); 324129a3e3ddSGiorgis Georgakoudis Value *Tid = 324229a3e3ddSGiorgis Georgakoudis OMPInfoCache.OMPBuilder.Builder.CreateCall(HardwareTidFn, {}); 324329a3e3ddSGiorgis Georgakoudis Value *TidCheck = OMPInfoCache.OMPBuilder.Builder.CreateIsNull(Tid); 324429a3e3ddSGiorgis Georgakoudis OMPInfoCache.OMPBuilder.Builder 324529a3e3ddSGiorgis Georgakoudis .CreateCondBr(TidCheck, RegionStartBB, RegionBarrierBB) 324629a3e3ddSGiorgis Georgakoudis ->setDebugLoc(DL); 324729a3e3ddSGiorgis Georgakoudis 324829a3e3ddSGiorgis Georgakoudis // First barrier for synchronization, ensures main thread has updated 324929a3e3ddSGiorgis Georgakoudis // values. 325029a3e3ddSGiorgis Georgakoudis FunctionCallee BarrierFn = 325129a3e3ddSGiorgis Georgakoudis OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 325229a3e3ddSGiorgis Georgakoudis M, OMPRTL___kmpc_barrier_simple_spmd); 325329a3e3ddSGiorgis Georgakoudis OMPInfoCache.OMPBuilder.updateToLocation(InsertPointTy( 325429a3e3ddSGiorgis Georgakoudis RegionBarrierBB, RegionBarrierBB->getFirstInsertionPt())); 325529a3e3ddSGiorgis Georgakoudis OMPInfoCache.OMPBuilder.Builder.CreateCall(BarrierFn, {Ident, Tid}) 325629a3e3ddSGiorgis Georgakoudis ->setDebugLoc(DL); 325729a3e3ddSGiorgis Georgakoudis 325829a3e3ddSGiorgis Georgakoudis // Second barrier ensures workers have read broadcast values. 325929a3e3ddSGiorgis Georgakoudis if (HasBroadcastValues) 326029a3e3ddSGiorgis Georgakoudis CallInst::Create(BarrierFn, {Ident, Tid}, "", 326129a3e3ddSGiorgis Georgakoudis RegionBarrierBB->getTerminator()) 326229a3e3ddSGiorgis Georgakoudis ->setDebugLoc(DL); 326329a3e3ddSGiorgis Georgakoudis }; 326429a3e3ddSGiorgis Georgakoudis 326599ea8ac9SJohannes Doerfert auto &AllocSharedRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared]; 326699ea8ac9SJohannes Doerfert SmallPtrSet<BasicBlock *, 8> Visited; 326799ea8ac9SJohannes Doerfert for (Instruction *GuardedI : SPMDCompatibilityTracker) { 326899ea8ac9SJohannes Doerfert BasicBlock *BB = GuardedI->getParent(); 326999ea8ac9SJohannes Doerfert if (!Visited.insert(BB).second) 327099ea8ac9SJohannes Doerfert continue; 327199ea8ac9SJohannes Doerfert 327299ea8ac9SJohannes Doerfert SmallVector<std::pair<Instruction *, Instruction *>> Reorders; 327399ea8ac9SJohannes Doerfert Instruction *LastEffect = nullptr; 327499ea8ac9SJohannes Doerfert BasicBlock::reverse_iterator IP = BB->rbegin(), IPEnd = BB->rend(); 327599ea8ac9SJohannes Doerfert while (++IP != IPEnd) { 327699ea8ac9SJohannes Doerfert if (!IP->mayHaveSideEffects() && !IP->mayReadFromMemory()) 327799ea8ac9SJohannes Doerfert continue; 327899ea8ac9SJohannes Doerfert Instruction *I = &*IP; 327999ea8ac9SJohannes Doerfert if (OpenMPOpt::getCallIfRegularCall(*I, &AllocSharedRFI)) 328099ea8ac9SJohannes Doerfert continue; 328199ea8ac9SJohannes Doerfert if (!I->user_empty() || !SPMDCompatibilityTracker.contains(I)) { 328299ea8ac9SJohannes Doerfert LastEffect = nullptr; 328399ea8ac9SJohannes Doerfert continue; 328499ea8ac9SJohannes Doerfert } 328599ea8ac9SJohannes Doerfert if (LastEffect) 328699ea8ac9SJohannes Doerfert Reorders.push_back({I, LastEffect}); 328799ea8ac9SJohannes Doerfert LastEffect = &*IP; 328899ea8ac9SJohannes Doerfert } 328999ea8ac9SJohannes Doerfert for (auto &Reorder : Reorders) 329099ea8ac9SJohannes Doerfert Reorder.first->moveBefore(Reorder.second); 329199ea8ac9SJohannes Doerfert } 329299ea8ac9SJohannes Doerfert 329329a3e3ddSGiorgis Georgakoudis SmallVector<std::pair<Instruction *, Instruction *>, 4> GuardedRegions; 329429a3e3ddSGiorgis Georgakoudis 329529a3e3ddSGiorgis Georgakoudis for (Instruction *GuardedI : SPMDCompatibilityTracker) { 329629a3e3ddSGiorgis Georgakoudis BasicBlock *BB = GuardedI->getParent(); 329729a3e3ddSGiorgis Georgakoudis auto *CalleeAA = A.lookupAAFor<AAKernelInfo>( 329829a3e3ddSGiorgis Georgakoudis IRPosition::function(*GuardedI->getFunction()), nullptr, 329929a3e3ddSGiorgis Georgakoudis DepClassTy::NONE); 330029a3e3ddSGiorgis Georgakoudis assert(CalleeAA != nullptr && "Expected Callee AAKernelInfo"); 330129a3e3ddSGiorgis Georgakoudis auto &CalleeAAFunction = *cast<AAKernelInfoFunction>(CalleeAA); 330229a3e3ddSGiorgis Georgakoudis // Continue if instruction is already guarded. 330329a3e3ddSGiorgis Georgakoudis if (CalleeAAFunction.getGuardedInstructions().contains(GuardedI)) 330429a3e3ddSGiorgis Georgakoudis continue; 330529a3e3ddSGiorgis Georgakoudis 330629a3e3ddSGiorgis Georgakoudis Instruction *GuardedRegionStart = nullptr, *GuardedRegionEnd = nullptr; 330729a3e3ddSGiorgis Georgakoudis for (Instruction &I : *BB) { 330829a3e3ddSGiorgis Georgakoudis // If instruction I needs to be guarded update the guarded region 330929a3e3ddSGiorgis Georgakoudis // bounds. 331029a3e3ddSGiorgis Georgakoudis if (SPMDCompatibilityTracker.contains(&I)) { 331129a3e3ddSGiorgis Georgakoudis CalleeAAFunction.getGuardedInstructions().insert(&I); 331229a3e3ddSGiorgis Georgakoudis if (GuardedRegionStart) 331329a3e3ddSGiorgis Georgakoudis GuardedRegionEnd = &I; 331429a3e3ddSGiorgis Georgakoudis else 331529a3e3ddSGiorgis Georgakoudis GuardedRegionStart = GuardedRegionEnd = &I; 331629a3e3ddSGiorgis Georgakoudis 331729a3e3ddSGiorgis Georgakoudis continue; 331829a3e3ddSGiorgis Georgakoudis } 331929a3e3ddSGiorgis Georgakoudis 332029a3e3ddSGiorgis Georgakoudis // Instruction I does not need guarding, store 332129a3e3ddSGiorgis Georgakoudis // any region found and reset bounds. 332229a3e3ddSGiorgis Georgakoudis if (GuardedRegionStart) { 332329a3e3ddSGiorgis Georgakoudis GuardedRegions.push_back( 332429a3e3ddSGiorgis Georgakoudis std::make_pair(GuardedRegionStart, GuardedRegionEnd)); 332529a3e3ddSGiorgis Georgakoudis GuardedRegionStart = nullptr; 332629a3e3ddSGiorgis Georgakoudis GuardedRegionEnd = nullptr; 332729a3e3ddSGiorgis Georgakoudis } 332829a3e3ddSGiorgis Georgakoudis } 332929a3e3ddSGiorgis Georgakoudis } 333029a3e3ddSGiorgis Georgakoudis 333129a3e3ddSGiorgis Georgakoudis for (auto &GR : GuardedRegions) 333229a3e3ddSGiorgis Georgakoudis CreateGuardedRegion(GR.first, GR.second); 333329a3e3ddSGiorgis Georgakoudis 3334514c033dSJohannes Doerfert // Adjust the global exec mode flag that tells the runtime what mode this 3335514c033dSJohannes Doerfert // kernel is executed in. 3336ca999f71SShilei Tian assert(ExecModeVal == OMP_TGT_EXEC_MODE_GENERIC && 3337ca999f71SShilei Tian "Initially non-SPMD kernel has SPMD exec mode!"); 3338514c033dSJohannes Doerfert ExecMode->setInitializer( 3339ca999f71SShilei Tian ConstantInt::get(ExecMode->getInitializer()->getType(), 3340ca999f71SShilei Tian ExecModeVal | OMP_TGT_EXEC_MODE_GENERIC_SPMD)); 3341514c033dSJohannes Doerfert 3342514c033dSJohannes Doerfert // Next rewrite the init and deinit calls to indicate we use SPMD-mode now. 3343423d34f7SShilei Tian const int InitModeArgNo = 1; 3344423d34f7SShilei Tian const int DeinitModeArgNo = 1; 3345514c033dSJohannes Doerfert const int InitUseStateMachineArgNo = 2; 3346e8439ec8SGiorgis Georgakoudis const int InitRequiresFullRuntimeArgNo = 3; 3347e8439ec8SGiorgis Georgakoudis const int DeinitRequiresFullRuntimeArgNo = 2; 3348514c033dSJohannes Doerfert 3349514c033dSJohannes Doerfert auto &Ctx = getAnchorValue().getContext(); 3350423d34f7SShilei Tian A.changeUseAfterManifest( 3351423d34f7SShilei Tian KernelInitCB->getArgOperandUse(InitModeArgNo), 3352423d34f7SShilei Tian *ConstantInt::getSigned(IntegerType::getInt8Ty(Ctx), 3353423d34f7SShilei Tian OMP_TGT_EXEC_MODE_SPMD)); 3354514c033dSJohannes Doerfert A.changeUseAfterManifest( 3355514c033dSJohannes Doerfert KernelInitCB->getArgOperandUse(InitUseStateMachineArgNo), 33562aed0813SKazu Hirata *ConstantInt::getBool(Ctx, false)); 3357514c033dSJohannes Doerfert A.changeUseAfterManifest( 3358423d34f7SShilei Tian KernelDeinitCB->getArgOperandUse(DeinitModeArgNo), 3359423d34f7SShilei Tian *ConstantInt::getSigned(IntegerType::getInt8Ty(Ctx), 3360423d34f7SShilei Tian OMP_TGT_EXEC_MODE_SPMD)); 3361e8439ec8SGiorgis Georgakoudis A.changeUseAfterManifest( 3362e8439ec8SGiorgis Georgakoudis KernelInitCB->getArgOperandUse(InitRequiresFullRuntimeArgNo), 33632aed0813SKazu Hirata *ConstantInt::getBool(Ctx, false)); 3364e8439ec8SGiorgis Georgakoudis A.changeUseAfterManifest( 3365e8439ec8SGiorgis Georgakoudis KernelDeinitCB->getArgOperandUse(DeinitRequiresFullRuntimeArgNo), 33662aed0813SKazu Hirata *ConstantInt::getBool(Ctx, false)); 3367e8439ec8SGiorgis Georgakoudis 3368514c033dSJohannes Doerfert ++NumOpenMPTargetRegionKernelsSPMD; 3369514c033dSJohannes Doerfert 3370514c033dSJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 3371eef6601bSJoseph Huber return OR << "Transformed generic-mode kernel to SPMD-mode."; 3372514c033dSJohannes Doerfert }; 33732c31d5ebSJoseph Huber A.emitRemark<OptimizationRemark>(KernelInitCB, "OMP120", Remark); 3374514c033dSJohannes Doerfert return true; 3375514c033dSJohannes Doerfert }; 3376514c033dSJohannes Doerfert 3377d9659bf6SJohannes Doerfert ChangeStatus buildCustomStateMachine(Attributor &A) { 3378cd0dd8ecSJoseph Huber // If we have disabled state machine rewrites, don't make a custom one 3379cd0dd8ecSJoseph Huber if (DisableOpenMPOptStateMachineRewrite) 3380c6457dcaSJohannes Doerfert return ChangeStatus::UNCHANGED; 3381cd0dd8ecSJoseph Huber 3382f074a6a0SJoseph Huber // Don't rewrite the state machine if we are not in a valid state. 3383f074a6a0SJoseph Huber if (!ReachedKnownParallelRegions.isValidState()) 3384f074a6a0SJoseph Huber return ChangeStatus::UNCHANGED; 3385d9659bf6SJohannes Doerfert 3386423d34f7SShilei Tian const int InitModeArgNo = 1; 3387d9659bf6SJohannes Doerfert const int InitUseStateMachineArgNo = 2; 3388d9659bf6SJohannes Doerfert 3389d9659bf6SJohannes Doerfert // Check if the current configuration is non-SPMD and generic state machine. 3390d9659bf6SJohannes Doerfert // If we already have SPMD mode or a custom state machine we do not need to 3391d9659bf6SJohannes Doerfert // go any further. If it is anything but a constant something is weird and 3392d9659bf6SJohannes Doerfert // we give up. 3393d9659bf6SJohannes Doerfert ConstantInt *UseStateMachine = dyn_cast<ConstantInt>( 3394d9659bf6SJohannes Doerfert KernelInitCB->getArgOperand(InitUseStateMachineArgNo)); 3395423d34f7SShilei Tian ConstantInt *Mode = 3396423d34f7SShilei Tian dyn_cast<ConstantInt>(KernelInitCB->getArgOperand(InitModeArgNo)); 3397d9659bf6SJohannes Doerfert 3398d9659bf6SJohannes Doerfert // If we are stuck with generic mode, try to create a custom device (=GPU) 3399d9659bf6SJohannes Doerfert // state machine which is specialized for the parallel regions that are 3400d9659bf6SJohannes Doerfert // reachable by the kernel. 3401423d34f7SShilei Tian if (!UseStateMachine || UseStateMachine->isZero() || !Mode || 3402423d34f7SShilei Tian (Mode->getSExtValue() & OMP_TGT_EXEC_MODE_SPMD)) 3403d9659bf6SJohannes Doerfert return ChangeStatus::UNCHANGED; 3404d9659bf6SJohannes Doerfert 3405514c033dSJohannes Doerfert // If not SPMD mode, indicate we use a custom state machine now. 3406d9659bf6SJohannes Doerfert auto &Ctx = getAnchorValue().getContext(); 34072aed0813SKazu Hirata auto *FalseVal = ConstantInt::getBool(Ctx, false); 3408d9659bf6SJohannes Doerfert A.changeUseAfterManifest( 3409d9659bf6SJohannes Doerfert KernelInitCB->getArgOperandUse(InitUseStateMachineArgNo), *FalseVal); 3410d9659bf6SJohannes Doerfert 3411d9659bf6SJohannes Doerfert // If we don't actually need a state machine we are done here. This can 3412d9659bf6SJohannes Doerfert // happen if there simply are no parallel regions. In the resulting kernel 3413d9659bf6SJohannes Doerfert // all worker threads will simply exit right away, leaving the main thread 3414d9659bf6SJohannes Doerfert // to do the work alone. 34156b9a3ec3SJoseph Huber if (!mayContainParallelRegion()) { 3416d9659bf6SJohannes Doerfert ++NumOpenMPTargetRegionKernelsWithoutStateMachine; 3417d9659bf6SJohannes Doerfert 3418d9659bf6SJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 3419eef6601bSJoseph Huber return OR << "Removing unused state machine from generic-mode kernel."; 3420d9659bf6SJohannes Doerfert }; 34212c31d5ebSJoseph Huber A.emitRemark<OptimizationRemark>(KernelInitCB, "OMP130", Remark); 3422d9659bf6SJohannes Doerfert 3423d9659bf6SJohannes Doerfert return ChangeStatus::CHANGED; 3424d9659bf6SJohannes Doerfert } 3425d9659bf6SJohannes Doerfert 3426d9659bf6SJohannes Doerfert // Keep track in the statistics of our new shiny custom state machine. 3427d9659bf6SJohannes Doerfert if (ReachedUnknownParallelRegions.empty()) { 3428d9659bf6SJohannes Doerfert ++NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback; 3429d9659bf6SJohannes Doerfert 3430d9659bf6SJohannes Doerfert auto Remark = [&](OptimizationRemark OR) { 3431eef6601bSJoseph Huber return OR << "Rewriting generic-mode kernel with a customized state " 3432eef6601bSJoseph Huber "machine."; 3433d9659bf6SJohannes Doerfert }; 34342c31d5ebSJoseph Huber A.emitRemark<OptimizationRemark>(KernelInitCB, "OMP131", Remark); 3435d9659bf6SJohannes Doerfert } else { 3436d9659bf6SJohannes Doerfert ++NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback; 3437d9659bf6SJohannes Doerfert 3438eef6601bSJoseph Huber auto Remark = [&](OptimizationRemarkAnalysis OR) { 3439d9659bf6SJohannes Doerfert return OR << "Generic-mode kernel is executed with a customized state " 3440eef6601bSJoseph Huber "machine that requires a fallback."; 3441d9659bf6SJohannes Doerfert }; 34422c31d5ebSJoseph Huber A.emitRemark<OptimizationRemarkAnalysis>(KernelInitCB, "OMP132", Remark); 3443d9659bf6SJohannes Doerfert 3444d9659bf6SJohannes Doerfert // Tell the user why we ended up with a fallback. 3445d9659bf6SJohannes Doerfert for (CallBase *UnknownParallelRegionCB : ReachedUnknownParallelRegions) { 3446d9659bf6SJohannes Doerfert if (!UnknownParallelRegionCB) 3447d9659bf6SJohannes Doerfert continue; 3448d9659bf6SJohannes Doerfert auto Remark = [&](OptimizationRemarkAnalysis ORA) { 3449eef6601bSJoseph Huber return ORA << "Call may contain unknown parallel regions. Use " 3450eef6601bSJoseph Huber << "`__attribute__((assume(\"omp_no_parallelism\")))` to " 3451eef6601bSJoseph Huber "override."; 3452d9659bf6SJohannes Doerfert }; 34532c31d5ebSJoseph Huber A.emitRemark<OptimizationRemarkAnalysis>(UnknownParallelRegionCB, 34542c31d5ebSJoseph Huber "OMP133", Remark); 3455d9659bf6SJohannes Doerfert } 3456d9659bf6SJohannes Doerfert } 3457d9659bf6SJohannes Doerfert 3458d9659bf6SJohannes Doerfert // Create all the blocks: 3459d9659bf6SJohannes Doerfert // 3460d9659bf6SJohannes Doerfert // InitCB = __kmpc_target_init(...) 3461c9dfe322SJoel E. Denny // BlockHwSize = 3462c9dfe322SJoel E. Denny // __kmpc_get_hardware_num_threads_in_block(); 3463c9dfe322SJoel E. Denny // WarpSize = __kmpc_get_warp_size(); 3464c9dfe322SJoel E. Denny // BlockSize = BlockHwSize - WarpSize; 3465c9dfe322SJoel E. Denny // if (InitCB >= BlockSize) return; 3466c9dfe322SJoel E. Denny // IsWorkerCheckBB: bool IsWorker = InitCB >= 0; 3467d9659bf6SJohannes Doerfert // if (IsWorker) { 346873720c80SJohannes Doerfert // SMBeginBB: __kmpc_barrier_simple_generic(...); 3469d9659bf6SJohannes Doerfert // void *WorkFn; 3470d9659bf6SJohannes Doerfert // bool Active = __kmpc_kernel_parallel(&WorkFn); 3471d9659bf6SJohannes Doerfert // if (!WorkFn) return; 3472d9659bf6SJohannes Doerfert // SMIsActiveCheckBB: if (Active) { 3473d9659bf6SJohannes Doerfert // SMIfCascadeCurrentBB: if (WorkFn == <ParFn0>) 3474d9659bf6SJohannes Doerfert // ParFn0(...); 3475d9659bf6SJohannes Doerfert // SMIfCascadeCurrentBB: else if (WorkFn == <ParFn1>) 3476d9659bf6SJohannes Doerfert // ParFn1(...); 3477d9659bf6SJohannes Doerfert // ... 3478d9659bf6SJohannes Doerfert // SMIfCascadeCurrentBB: else 3479d9659bf6SJohannes Doerfert // ((WorkFnTy*)WorkFn)(...); 3480d9659bf6SJohannes Doerfert // SMEndParallelBB: __kmpc_kernel_end_parallel(...); 3481d9659bf6SJohannes Doerfert // } 348273720c80SJohannes Doerfert // SMDoneBB: __kmpc_barrier_simple_generic(...); 3483d9659bf6SJohannes Doerfert // goto SMBeginBB; 3484d9659bf6SJohannes Doerfert // } 3485d9659bf6SJohannes Doerfert // UserCodeEntryBB: // user code 3486d9659bf6SJohannes Doerfert // __kmpc_target_deinit(...) 3487d9659bf6SJohannes Doerfert // 3488d9659bf6SJohannes Doerfert Function *Kernel = getAssociatedFunction(); 3489d9659bf6SJohannes Doerfert assert(Kernel && "Expected an associated function!"); 3490d9659bf6SJohannes Doerfert 3491d9659bf6SJohannes Doerfert BasicBlock *InitBB = KernelInitCB->getParent(); 3492d9659bf6SJohannes Doerfert BasicBlock *UserCodeEntryBB = InitBB->splitBasicBlock( 3493d9659bf6SJohannes Doerfert KernelInitCB->getNextNode(), "thread.user_code.check"); 3494c9dfe322SJoel E. Denny BasicBlock *IsWorkerCheckBB = 3495c9dfe322SJoel E. Denny BasicBlock::Create(Ctx, "is_worker_check", Kernel, UserCodeEntryBB); 3496d9659bf6SJohannes Doerfert BasicBlock *StateMachineBeginBB = BasicBlock::Create( 3497d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.begin", Kernel, UserCodeEntryBB); 3498d9659bf6SJohannes Doerfert BasicBlock *StateMachineFinishedBB = BasicBlock::Create( 3499d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.finished", Kernel, UserCodeEntryBB); 3500d9659bf6SJohannes Doerfert BasicBlock *StateMachineIsActiveCheckBB = BasicBlock::Create( 3501d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.is_active.check", Kernel, UserCodeEntryBB); 3502d9659bf6SJohannes Doerfert BasicBlock *StateMachineIfCascadeCurrentBB = 3503d9659bf6SJohannes Doerfert BasicBlock::Create(Ctx, "worker_state_machine.parallel_region.check", 3504d9659bf6SJohannes Doerfert Kernel, UserCodeEntryBB); 3505d9659bf6SJohannes Doerfert BasicBlock *StateMachineEndParallelBB = 3506d9659bf6SJohannes Doerfert BasicBlock::Create(Ctx, "worker_state_machine.parallel_region.end", 3507d9659bf6SJohannes Doerfert Kernel, UserCodeEntryBB); 3508d9659bf6SJohannes Doerfert BasicBlock *StateMachineDoneBarrierBB = BasicBlock::Create( 3509d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.done.barrier", Kernel, UserCodeEntryBB); 35103f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*InitBB); 35113f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*UserCodeEntryBB); 3512c9dfe322SJoel E. Denny A.registerManifestAddedBasicBlock(*IsWorkerCheckBB); 35133f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineBeginBB); 35143f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineFinishedBB); 35153f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineIsActiveCheckBB); 35163f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineIfCascadeCurrentBB); 35173f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineEndParallelBB); 35183f71b425SGiorgis Georgakoudis A.registerManifestAddedBasicBlock(*StateMachineDoneBarrierBB); 3519d9659bf6SJohannes Doerfert 3520d9659bf6SJohannes Doerfert const DebugLoc &DLoc = KernelInitCB->getDebugLoc(); 3521d9659bf6SJohannes Doerfert ReturnInst::Create(Ctx, StateMachineFinishedBB)->setDebugLoc(DLoc); 3522d9659bf6SJohannes Doerfert InitBB->getTerminator()->eraseFromParent(); 3523c9dfe322SJoel E. Denny 3524c9dfe322SJoel E. Denny Module &M = *Kernel->getParent(); 3525c9dfe322SJoel E. Denny auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 3526c9dfe322SJoel E. Denny FunctionCallee BlockHwSizeFn = 3527c9dfe322SJoel E. Denny OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 3528c9dfe322SJoel E. Denny M, OMPRTL___kmpc_get_hardware_num_threads_in_block); 3529c9dfe322SJoel E. Denny FunctionCallee WarpSizeFn = 3530c9dfe322SJoel E. Denny OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 3531c9dfe322SJoel E. Denny M, OMPRTL___kmpc_get_warp_size); 3532c9dfe322SJoel E. Denny Instruction *BlockHwSize = 3533c9dfe322SJoel E. Denny CallInst::Create(BlockHwSizeFn, "block.hw_size", InitBB); 3534c9dfe322SJoel E. Denny BlockHwSize->setDebugLoc(DLoc); 3535c9dfe322SJoel E. Denny Instruction *WarpSize = CallInst::Create(WarpSizeFn, "warp.size", InitBB); 3536c9dfe322SJoel E. Denny WarpSize->setDebugLoc(DLoc); 3537c9dfe322SJoel E. Denny Instruction *BlockSize = 3538c9dfe322SJoel E. Denny BinaryOperator::CreateSub(BlockHwSize, WarpSize, "block.size", InitBB); 3539c9dfe322SJoel E. Denny BlockSize->setDebugLoc(DLoc); 3540c9dfe322SJoel E. Denny Instruction *IsMainOrWorker = 3541c9dfe322SJoel E. Denny ICmpInst::Create(ICmpInst::ICmp, llvm::CmpInst::ICMP_SLT, KernelInitCB, 3542c9dfe322SJoel E. Denny BlockSize, "thread.is_main_or_worker", InitBB); 3543c9dfe322SJoel E. Denny IsMainOrWorker->setDebugLoc(DLoc); 3544c9dfe322SJoel E. Denny BranchInst::Create(IsWorkerCheckBB, StateMachineFinishedBB, IsMainOrWorker, 3545c9dfe322SJoel E. Denny InitBB); 3546c9dfe322SJoel E. Denny 3547d9659bf6SJohannes Doerfert Instruction *IsWorker = 3548d9659bf6SJohannes Doerfert ICmpInst::Create(ICmpInst::ICmp, llvm::CmpInst::ICMP_NE, KernelInitCB, 3549d9659bf6SJohannes Doerfert ConstantInt::get(KernelInitCB->getType(), -1), 3550c9dfe322SJoel E. Denny "thread.is_worker", IsWorkerCheckBB); 3551d9659bf6SJohannes Doerfert IsWorker->setDebugLoc(DLoc); 3552c9dfe322SJoel E. Denny BranchInst::Create(StateMachineBeginBB, UserCodeEntryBB, IsWorker, 3553c9dfe322SJoel E. Denny IsWorkerCheckBB); 355471052ea1SJon Chesterfield 3555d9659bf6SJohannes Doerfert // Create local storage for the work function pointer. 355671052ea1SJon Chesterfield const DataLayout &DL = M.getDataLayout(); 3557d9659bf6SJohannes Doerfert Type *VoidPtrTy = Type::getInt8PtrTy(Ctx); 355871052ea1SJon Chesterfield Instruction *WorkFnAI = 355971052ea1SJon Chesterfield new AllocaInst(VoidPtrTy, DL.getAllocaAddrSpace(), nullptr, 356071052ea1SJon Chesterfield "worker.work_fn.addr", &Kernel->getEntryBlock().front()); 3561d9659bf6SJohannes Doerfert WorkFnAI->setDebugLoc(DLoc); 3562d9659bf6SJohannes Doerfert 3563d9659bf6SJohannes Doerfert OMPInfoCache.OMPBuilder.updateToLocation( 3564d9659bf6SJohannes Doerfert OpenMPIRBuilder::LocationDescription( 3565d9659bf6SJohannes Doerfert IRBuilder<>::InsertPoint(StateMachineBeginBB, 3566d9659bf6SJohannes Doerfert StateMachineBeginBB->end()), 3567d9659bf6SJohannes Doerfert DLoc)); 3568d9659bf6SJohannes Doerfert 3569d9659bf6SJohannes Doerfert Value *Ident = KernelInitCB->getArgOperand(0); 3570d9659bf6SJohannes Doerfert Value *GTid = KernelInitCB; 3571d9659bf6SJohannes Doerfert 3572d9659bf6SJohannes Doerfert FunctionCallee BarrierFn = 3573d9659bf6SJohannes Doerfert OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 357473720c80SJohannes Doerfert M, OMPRTL___kmpc_barrier_simple_generic); 3575d9659bf6SJohannes Doerfert CallInst::Create(BarrierFn, {Ident, GTid}, "", StateMachineBeginBB) 3576d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3577d9659bf6SJohannes Doerfert 357871052ea1SJon Chesterfield if (WorkFnAI->getType()->getPointerAddressSpace() != 357971052ea1SJon Chesterfield (unsigned int)AddressSpace::Generic) { 358071052ea1SJon Chesterfield WorkFnAI = new AddrSpaceCastInst( 358171052ea1SJon Chesterfield WorkFnAI, 358271052ea1SJon Chesterfield PointerType::getWithSamePointeeType( 358371052ea1SJon Chesterfield cast<PointerType>(WorkFnAI->getType()), 358471052ea1SJon Chesterfield (unsigned int)AddressSpace::Generic), 358571052ea1SJon Chesterfield WorkFnAI->getName() + ".generic", StateMachineBeginBB); 358671052ea1SJon Chesterfield WorkFnAI->setDebugLoc(DLoc); 358771052ea1SJon Chesterfield } 358871052ea1SJon Chesterfield 3589d9659bf6SJohannes Doerfert FunctionCallee KernelParallelFn = 3590d9659bf6SJohannes Doerfert OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 3591d9659bf6SJohannes Doerfert M, OMPRTL___kmpc_kernel_parallel); 3592d9659bf6SJohannes Doerfert Instruction *IsActiveWorker = CallInst::Create( 3593d9659bf6SJohannes Doerfert KernelParallelFn, {WorkFnAI}, "worker.is_active", StateMachineBeginBB); 3594d9659bf6SJohannes Doerfert IsActiveWorker->setDebugLoc(DLoc); 3595d9659bf6SJohannes Doerfert Instruction *WorkFn = new LoadInst(VoidPtrTy, WorkFnAI, "worker.work_fn", 3596d9659bf6SJohannes Doerfert StateMachineBeginBB); 3597d9659bf6SJohannes Doerfert WorkFn->setDebugLoc(DLoc); 3598d9659bf6SJohannes Doerfert 3599d9659bf6SJohannes Doerfert FunctionType *ParallelRegionFnTy = FunctionType::get( 3600d9659bf6SJohannes Doerfert Type::getVoidTy(Ctx), {Type::getInt16Ty(Ctx), Type::getInt32Ty(Ctx)}, 3601d9659bf6SJohannes Doerfert false); 3602d9659bf6SJohannes Doerfert Value *WorkFnCast = BitCastInst::CreatePointerBitCastOrAddrSpaceCast( 3603d9659bf6SJohannes Doerfert WorkFn, ParallelRegionFnTy->getPointerTo(), "worker.work_fn.addr_cast", 3604d9659bf6SJohannes Doerfert StateMachineBeginBB); 3605d9659bf6SJohannes Doerfert 3606d9659bf6SJohannes Doerfert Instruction *IsDone = 3607d9659bf6SJohannes Doerfert ICmpInst::Create(ICmpInst::ICmp, llvm::CmpInst::ICMP_EQ, WorkFn, 3608d9659bf6SJohannes Doerfert Constant::getNullValue(VoidPtrTy), "worker.is_done", 3609d9659bf6SJohannes Doerfert StateMachineBeginBB); 3610d9659bf6SJohannes Doerfert IsDone->setDebugLoc(DLoc); 3611d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineFinishedBB, StateMachineIsActiveCheckBB, 3612d9659bf6SJohannes Doerfert IsDone, StateMachineBeginBB) 3613d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3614d9659bf6SJohannes Doerfert 3615d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineIfCascadeCurrentBB, 3616d9659bf6SJohannes Doerfert StateMachineDoneBarrierBB, IsActiveWorker, 3617d9659bf6SJohannes Doerfert StateMachineIsActiveCheckBB) 3618d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3619d9659bf6SJohannes Doerfert 3620d9659bf6SJohannes Doerfert Value *ZeroArg = 3621d9659bf6SJohannes Doerfert Constant::getNullValue(ParallelRegionFnTy->getParamType(0)); 3622d9659bf6SJohannes Doerfert 3623d9659bf6SJohannes Doerfert // Now that we have most of the CFG skeleton it is time for the if-cascade 3624d9659bf6SJohannes Doerfert // that checks the function pointer we got from the runtime against the 3625d9659bf6SJohannes Doerfert // parallel regions we expect, if there are any. 3626c11ebfeaSJoseph Huber for (int I = 0, E = ReachedKnownParallelRegions.size(); I < E; ++I) { 3627c11ebfeaSJoseph Huber auto *ParallelRegion = ReachedKnownParallelRegions[I]; 3628d9659bf6SJohannes Doerfert BasicBlock *PRExecuteBB = BasicBlock::Create( 3629d9659bf6SJohannes Doerfert Ctx, "worker_state_machine.parallel_region.execute", Kernel, 3630d9659bf6SJohannes Doerfert StateMachineEndParallelBB); 3631d9659bf6SJohannes Doerfert CallInst::Create(ParallelRegion, {ZeroArg, GTid}, "", PRExecuteBB) 3632d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3633d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineEndParallelBB, PRExecuteBB) 3634d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3635d9659bf6SJohannes Doerfert 3636d9659bf6SJohannes Doerfert BasicBlock *PRNextBB = 3637d9659bf6SJohannes Doerfert BasicBlock::Create(Ctx, "worker_state_machine.parallel_region.check", 3638d9659bf6SJohannes Doerfert Kernel, StateMachineEndParallelBB); 3639d9659bf6SJohannes Doerfert 3640d9659bf6SJohannes Doerfert // Check if we need to compare the pointer at all or if we can just 3641d9659bf6SJohannes Doerfert // call the parallel region function. 3642d9659bf6SJohannes Doerfert Value *IsPR; 3643c11ebfeaSJoseph Huber if (I + 1 < E || !ReachedUnknownParallelRegions.empty()) { 3644d9659bf6SJohannes Doerfert Instruction *CmpI = ICmpInst::Create( 3645d9659bf6SJohannes Doerfert ICmpInst::ICmp, llvm::CmpInst::ICMP_EQ, WorkFnCast, ParallelRegion, 3646d9659bf6SJohannes Doerfert "worker.check_parallel_region", StateMachineIfCascadeCurrentBB); 3647d9659bf6SJohannes Doerfert CmpI->setDebugLoc(DLoc); 3648d9659bf6SJohannes Doerfert IsPR = CmpI; 3649d9659bf6SJohannes Doerfert } else { 3650d9659bf6SJohannes Doerfert IsPR = ConstantInt::getTrue(Ctx); 3651d9659bf6SJohannes Doerfert } 3652d9659bf6SJohannes Doerfert 3653d9659bf6SJohannes Doerfert BranchInst::Create(PRExecuteBB, PRNextBB, IsPR, 3654d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB) 3655d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3656d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB = PRNextBB; 3657d9659bf6SJohannes Doerfert } 3658d9659bf6SJohannes Doerfert 3659d9659bf6SJohannes Doerfert // At the end of the if-cascade we place the indirect function pointer call 3660d9659bf6SJohannes Doerfert // in case we might need it, that is if there can be parallel regions we 3661d9659bf6SJohannes Doerfert // have not handled in the if-cascade above. 3662d9659bf6SJohannes Doerfert if (!ReachedUnknownParallelRegions.empty()) { 3663d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB->setName( 3664d9659bf6SJohannes Doerfert "worker_state_machine.parallel_region.fallback.execute"); 3665d9659bf6SJohannes Doerfert CallInst::Create(ParallelRegionFnTy, WorkFnCast, {ZeroArg, GTid}, "", 3666d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB) 3667d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3668d9659bf6SJohannes Doerfert } 3669d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineEndParallelBB, 3670d9659bf6SJohannes Doerfert StateMachineIfCascadeCurrentBB) 3671d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3672d9659bf6SJohannes Doerfert 3673d9659bf6SJohannes Doerfert CallInst::Create(OMPInfoCache.OMPBuilder.getOrCreateRuntimeFunction( 3674d9659bf6SJohannes Doerfert M, OMPRTL___kmpc_kernel_end_parallel), 3675d9659bf6SJohannes Doerfert {}, "", StateMachineEndParallelBB) 3676d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3677d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineDoneBarrierBB, StateMachineEndParallelBB) 3678d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3679d9659bf6SJohannes Doerfert 3680d9659bf6SJohannes Doerfert CallInst::Create(BarrierFn, {Ident, GTid}, "", StateMachineDoneBarrierBB) 3681d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3682d9659bf6SJohannes Doerfert BranchInst::Create(StateMachineBeginBB, StateMachineDoneBarrierBB) 3683d9659bf6SJohannes Doerfert ->setDebugLoc(DLoc); 3684d9659bf6SJohannes Doerfert 3685d9659bf6SJohannes Doerfert return ChangeStatus::CHANGED; 3686d9659bf6SJohannes Doerfert } 3687d9659bf6SJohannes Doerfert 3688d9659bf6SJohannes Doerfert /// Fixpoint iteration update function. Will be called every time a dependence 3689d9659bf6SJohannes Doerfert /// changed its state (and in the beginning). 3690d9659bf6SJohannes Doerfert ChangeStatus updateImpl(Attributor &A) override { 3691d9659bf6SJohannes Doerfert KernelInfoState StateBefore = getState(); 3692d9659bf6SJohannes Doerfert 3693514c033dSJohannes Doerfert // Callback to check a read/write instruction. 3694514c033dSJohannes Doerfert auto CheckRWInst = [&](Instruction &I) { 3695514c033dSJohannes Doerfert // We handle calls later. 3696514c033dSJohannes Doerfert if (isa<CallBase>(I)) 3697514c033dSJohannes Doerfert return true; 3698514c033dSJohannes Doerfert // We only care about write effects. 3699514c033dSJohannes Doerfert if (!I.mayWriteToMemory()) 3700514c033dSJohannes Doerfert return true; 3701514c033dSJohannes Doerfert if (auto *SI = dyn_cast<StoreInst>(&I)) { 3702514c033dSJohannes Doerfert SmallVector<const Value *> Objects; 3703514c033dSJohannes Doerfert getUnderlyingObjects(SI->getPointerOperand(), Objects); 3704514c033dSJohannes Doerfert if (llvm::all_of(Objects, 3705514c033dSJohannes Doerfert [](const Value *Obj) { return isa<AllocaInst>(Obj); })) 3706514c033dSJohannes Doerfert return true; 370729a3e3ddSGiorgis Georgakoudis // Check for AAHeapToStack moved objects which must not be guarded. 370829a3e3ddSGiorgis Georgakoudis auto &HS = A.getAAFor<AAHeapToStack>( 370929a3e3ddSGiorgis Georgakoudis *this, IRPosition::function(*I.getFunction()), 3710e6e440aeSJohannes Doerfert DepClassTy::OPTIONAL); 371129a3e3ddSGiorgis Georgakoudis if (llvm::all_of(Objects, [&HS](const Value *Obj) { 371229a3e3ddSGiorgis Georgakoudis auto *CB = dyn_cast<CallBase>(Obj); 371329a3e3ddSGiorgis Georgakoudis if (!CB) 371429a3e3ddSGiorgis Georgakoudis return false; 371529a3e3ddSGiorgis Georgakoudis return HS.isAssumedHeapToStack(*CB); 371629a3e3ddSGiorgis Georgakoudis })) { 371729a3e3ddSGiorgis Georgakoudis return true; 3718514c033dSJohannes Doerfert } 371929a3e3ddSGiorgis Georgakoudis } 372029a3e3ddSGiorgis Georgakoudis 372129a3e3ddSGiorgis Georgakoudis // Insert instruction that needs guarding. 3722514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&I); 3723514c033dSJohannes Doerfert return true; 3724514c033dSJohannes Doerfert }; 3725792aac98SJohannes Doerfert 3726792aac98SJohannes Doerfert bool UsedAssumedInformationInCheckRWInst = false; 372797387fdfSJohannes Doerfert if (!SPMDCompatibilityTracker.isAtFixpoint()) 3728792aac98SJohannes Doerfert if (!A.checkForAllReadWriteInstructions( 3729792aac98SJohannes Doerfert CheckRWInst, *this, UsedAssumedInformationInCheckRWInst)) 3730514c033dSJohannes Doerfert SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 3731514c033dSJohannes Doerfert 3732058c312aSJoseph Huber bool UsedAssumedInformationFromReachingKernels = false; 3733e97e0a4fSShilei Tian if (!IsKernelEntry) { 3734e97e0a4fSShilei Tian updateParallelLevels(A); 373529a3e3ddSGiorgis Georgakoudis 3736058c312aSJoseph Huber bool AllReachingKernelsKnown = true; 3737058c312aSJoseph Huber updateReachingKernelEntries(A, AllReachingKernelsKnown); 3738058c312aSJoseph Huber UsedAssumedInformationFromReachingKernels = !AllReachingKernelsKnown; 3739058c312aSJoseph Huber 374029a3e3ddSGiorgis Georgakoudis if (!ParallelLevels.isValidState()) 374129a3e3ddSGiorgis Georgakoudis SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 3742058c312aSJoseph Huber else if (!ReachingKernelEntries.isValidState()) 3743058c312aSJoseph Huber SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 3744058c312aSJoseph Huber else if (!SPMDCompatibilityTracker.empty()) { 3745058c312aSJoseph Huber // Check if all reaching kernels agree on the mode as we can otherwise 3746058c312aSJoseph Huber // not guard instructions. We might not be sure about the mode so we 3747058c312aSJoseph Huber // we cannot fix the internal spmd-zation state either. 3748058c312aSJoseph Huber int SPMD = 0, Generic = 0; 3749058c312aSJoseph Huber for (auto *Kernel : ReachingKernelEntries) { 3750058c312aSJoseph Huber auto &CBAA = A.getAAFor<AAKernelInfo>( 3751058c312aSJoseph Huber *this, IRPosition::function(*Kernel), DepClassTy::OPTIONAL); 3752058c312aSJoseph Huber if (CBAA.SPMDCompatibilityTracker.isValidState() && 3753058c312aSJoseph Huber CBAA.SPMDCompatibilityTracker.isAssumed()) 3754058c312aSJoseph Huber ++SPMD; 3755058c312aSJoseph Huber else 3756058c312aSJoseph Huber ++Generic; 3757058c312aSJoseph Huber if (!CBAA.SPMDCompatibilityTracker.isAtFixpoint()) 3758058c312aSJoseph Huber UsedAssumedInformationFromReachingKernels = true; 3759058c312aSJoseph Huber } 3760058c312aSJoseph Huber if (SPMD != 0 && Generic != 0) 3761058c312aSJoseph Huber SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 3762058c312aSJoseph Huber } 3763e97e0a4fSShilei Tian } 3764ca662297SShilei Tian 3765d9659bf6SJohannes Doerfert // Callback to check a call instruction. 3766d61aac76SJohannes Doerfert bool AllParallelRegionStatesWereFixed = true; 376797387fdfSJohannes Doerfert bool AllSPMDStatesWereFixed = true; 3768d9659bf6SJohannes Doerfert auto CheckCallInst = [&](Instruction &I) { 3769d9659bf6SJohannes Doerfert auto &CB = cast<CallBase>(I); 3770d9659bf6SJohannes Doerfert auto &CBAA = A.getAAFor<AAKernelInfo>( 3771d9659bf6SJohannes Doerfert *this, IRPosition::callsite_function(CB), DepClassTy::OPTIONAL); 3772d9659bf6SJohannes Doerfert getState() ^= CBAA.getState(); 377397387fdfSJohannes Doerfert AllSPMDStatesWereFixed &= CBAA.SPMDCompatibilityTracker.isAtFixpoint(); 3774d61aac76SJohannes Doerfert AllParallelRegionStatesWereFixed &= 3775d61aac76SJohannes Doerfert CBAA.ReachedKnownParallelRegions.isAtFixpoint(); 3776d61aac76SJohannes Doerfert AllParallelRegionStatesWereFixed &= 3777d61aac76SJohannes Doerfert CBAA.ReachedUnknownParallelRegions.isAtFixpoint(); 3778d9659bf6SJohannes Doerfert return true; 3779d9659bf6SJohannes Doerfert }; 3780d9659bf6SJohannes Doerfert 3781792aac98SJohannes Doerfert bool UsedAssumedInformationInCheckCallInst = false; 3782792aac98SJohannes Doerfert if (!A.checkForAllCallLikeInstructions( 3783c6457dcaSJohannes Doerfert CheckCallInst, *this, UsedAssumedInformationInCheckCallInst)) { 3784e6e440aeSJohannes Doerfert LLVM_DEBUG(dbgs() << TAG 3785e6e440aeSJohannes Doerfert << "Failed to visit all call-like instructions!\n";); 3786d9659bf6SJohannes Doerfert return indicatePessimisticFixpoint(); 3787c6457dcaSJohannes Doerfert } 3788d9659bf6SJohannes Doerfert 3789d61aac76SJohannes Doerfert // If we haven't used any assumed information for the reached parallel 3790d61aac76SJohannes Doerfert // region states we can fix it. 3791d61aac76SJohannes Doerfert if (!UsedAssumedInformationInCheckCallInst && 3792d61aac76SJohannes Doerfert AllParallelRegionStatesWereFixed) { 3793d61aac76SJohannes Doerfert ReachedKnownParallelRegions.indicateOptimisticFixpoint(); 3794d61aac76SJohannes Doerfert ReachedUnknownParallelRegions.indicateOptimisticFixpoint(); 3795d61aac76SJohannes Doerfert } 3796d61aac76SJohannes Doerfert 3797d61aac76SJohannes Doerfert // If we are sure there are no parallel regions in the kernel we do not 3798d61aac76SJohannes Doerfert // want SPMD mode. 3799d61aac76SJohannes Doerfert if (IsKernelEntry && ReachedUnknownParallelRegions.isAtFixpoint() && 3800d61aac76SJohannes Doerfert ReachedKnownParallelRegions.isAtFixpoint() && 3801d61aac76SJohannes Doerfert ReachedUnknownParallelRegions.isValidState() && 3802d61aac76SJohannes Doerfert ReachedKnownParallelRegions.isValidState() && 3803d61aac76SJohannes Doerfert !mayContainParallelRegion()) 3804d61aac76SJohannes Doerfert SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 3805d61aac76SJohannes Doerfert 380697387fdfSJohannes Doerfert // If we haven't used any assumed information for the SPMD state we can fix 380797387fdfSJohannes Doerfert // it. 380897387fdfSJohannes Doerfert if (!UsedAssumedInformationInCheckRWInst && 3809058c312aSJoseph Huber !UsedAssumedInformationInCheckCallInst && 3810058c312aSJoseph Huber !UsedAssumedInformationFromReachingKernels && AllSPMDStatesWereFixed) 381197387fdfSJohannes Doerfert SPMDCompatibilityTracker.indicateOptimisticFixpoint(); 381297387fdfSJohannes Doerfert 3813d9659bf6SJohannes Doerfert return StateBefore == getState() ? ChangeStatus::UNCHANGED 3814d9659bf6SJohannes Doerfert : ChangeStatus::CHANGED; 3815d9659bf6SJohannes Doerfert } 3816ca662297SShilei Tian 3817ca662297SShilei Tian private: 3818ca662297SShilei Tian /// Update info regarding reaching kernels. 3819058c312aSJoseph Huber void updateReachingKernelEntries(Attributor &A, 3820058c312aSJoseph Huber bool &AllReachingKernelsKnown) { 3821ca662297SShilei Tian auto PredCallSite = [&](AbstractCallSite ACS) { 3822ca662297SShilei Tian Function *Caller = ACS.getInstruction()->getFunction(); 3823ca662297SShilei Tian 3824ca662297SShilei Tian assert(Caller && "Caller is nullptr"); 3825ca662297SShilei Tian 3826d3454ee8SShilei Tian auto &CAA = A.getOrCreateAAFor<AAKernelInfo>( 3827d3454ee8SShilei Tian IRPosition::function(*Caller), this, DepClassTy::REQUIRED); 3828ca662297SShilei Tian if (CAA.ReachingKernelEntries.isValidState()) { 3829ca662297SShilei Tian ReachingKernelEntries ^= CAA.ReachingKernelEntries; 3830ca662297SShilei Tian return true; 3831ca662297SShilei Tian } 3832ca662297SShilei Tian 3833ca662297SShilei Tian // We lost track of the caller of the associated function, any kernel 3834ca662297SShilei Tian // could reach now. 3835ca662297SShilei Tian ReachingKernelEntries.indicatePessimisticFixpoint(); 3836ca662297SShilei Tian 3837ca662297SShilei Tian return true; 3838ca662297SShilei Tian }; 3839ca662297SShilei Tian 3840ca662297SShilei Tian if (!A.checkForAllCallSites(PredCallSite, *this, 3841ca662297SShilei Tian true /* RequireAllCallSites */, 3842058c312aSJoseph Huber AllReachingKernelsKnown)) 3843ca662297SShilei Tian ReachingKernelEntries.indicatePessimisticFixpoint(); 3844ca662297SShilei Tian } 3845e97e0a4fSShilei Tian 3846e97e0a4fSShilei Tian /// Update info regarding parallel levels. 3847e97e0a4fSShilei Tian void updateParallelLevels(Attributor &A) { 3848e97e0a4fSShilei Tian auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 3849e97e0a4fSShilei Tian OMPInformationCache::RuntimeFunctionInfo &Parallel51RFI = 3850e97e0a4fSShilei Tian OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_51]; 3851e97e0a4fSShilei Tian 3852e97e0a4fSShilei Tian auto PredCallSite = [&](AbstractCallSite ACS) { 3853e97e0a4fSShilei Tian Function *Caller = ACS.getInstruction()->getFunction(); 3854e97e0a4fSShilei Tian 3855e97e0a4fSShilei Tian assert(Caller && "Caller is nullptr"); 3856e97e0a4fSShilei Tian 3857e97e0a4fSShilei Tian auto &CAA = 3858e97e0a4fSShilei Tian A.getOrCreateAAFor<AAKernelInfo>(IRPosition::function(*Caller)); 3859e97e0a4fSShilei Tian if (CAA.ParallelLevels.isValidState()) { 3860e97e0a4fSShilei Tian // Any function that is called by `__kmpc_parallel_51` will not be 3861e97e0a4fSShilei Tian // folded as the parallel level in the function is updated. In order to 3862e97e0a4fSShilei Tian // get it right, all the analysis would depend on the implentation. That 3863e97e0a4fSShilei Tian // said, if in the future any change to the implementation, the analysis 3864e97e0a4fSShilei Tian // could be wrong. As a consequence, we are just conservative here. 3865e97e0a4fSShilei Tian if (Caller == Parallel51RFI.Declaration) { 3866e97e0a4fSShilei Tian ParallelLevels.indicatePessimisticFixpoint(); 3867e97e0a4fSShilei Tian return true; 3868e97e0a4fSShilei Tian } 3869e97e0a4fSShilei Tian 3870e97e0a4fSShilei Tian ParallelLevels ^= CAA.ParallelLevels; 3871e97e0a4fSShilei Tian 3872e97e0a4fSShilei Tian return true; 3873e97e0a4fSShilei Tian } 3874e97e0a4fSShilei Tian 3875e97e0a4fSShilei Tian // We lost track of the caller of the associated function, any kernel 3876e97e0a4fSShilei Tian // could reach now. 3877e97e0a4fSShilei Tian ParallelLevels.indicatePessimisticFixpoint(); 3878e97e0a4fSShilei Tian 3879e97e0a4fSShilei Tian return true; 3880e97e0a4fSShilei Tian }; 3881e97e0a4fSShilei Tian 3882e97e0a4fSShilei Tian bool AllCallSitesKnown = true; 3883e97e0a4fSShilei Tian if (!A.checkForAllCallSites(PredCallSite, *this, 3884e97e0a4fSShilei Tian true /* RequireAllCallSites */, 3885e97e0a4fSShilei Tian AllCallSitesKnown)) 3886e97e0a4fSShilei Tian ParallelLevels.indicatePessimisticFixpoint(); 3887e97e0a4fSShilei Tian } 3888d9659bf6SJohannes Doerfert }; 3889d9659bf6SJohannes Doerfert 3890d9659bf6SJohannes Doerfert /// The call site kernel info abstract attribute, basically, what can we say 3891d9659bf6SJohannes Doerfert /// about a call site with regards to the KernelInfoState. For now this simply 3892d9659bf6SJohannes Doerfert /// forwards the information from the callee. 3893d9659bf6SJohannes Doerfert struct AAKernelInfoCallSite : AAKernelInfo { 3894d9659bf6SJohannes Doerfert AAKernelInfoCallSite(const IRPosition &IRP, Attributor &A) 3895d9659bf6SJohannes Doerfert : AAKernelInfo(IRP, A) {} 3896d9659bf6SJohannes Doerfert 3897d9659bf6SJohannes Doerfert /// See AbstractAttribute::initialize(...). 3898d9659bf6SJohannes Doerfert void initialize(Attributor &A) override { 3899d9659bf6SJohannes Doerfert AAKernelInfo::initialize(A); 3900d9659bf6SJohannes Doerfert 3901d9659bf6SJohannes Doerfert CallBase &CB = cast<CallBase>(getAssociatedValue()); 3902d9659bf6SJohannes Doerfert Function *Callee = getAssociatedFunction(); 3903d9659bf6SJohannes Doerfert 3904e52937ebSJoseph Huber auto &AssumptionAA = A.getAAFor<AAAssumptionInfo>( 3905e52937ebSJoseph Huber *this, IRPosition::callsite_function(CB), DepClassTy::OPTIONAL); 3906d9659bf6SJohannes Doerfert 3907514c033dSJohannes Doerfert // Check for SPMD-mode assumptions. 3908e52937ebSJoseph Huber if (AssumptionAA.hasAssumption("ompx_spmd_amenable")) { 3909514c033dSJohannes Doerfert SPMDCompatibilityTracker.indicateOptimisticFixpoint(); 39109e2fc0baSJoseph Huber indicateOptimisticFixpoint(); 39119e2fc0baSJoseph Huber } 3912514c033dSJohannes Doerfert 3913d9659bf6SJohannes Doerfert // First weed out calls we do not care about, that is readonly/readnone 3914d9659bf6SJohannes Doerfert // calls, intrinsics, and "no_openmp" calls. Neither of these can reach a 3915d9659bf6SJohannes Doerfert // parallel region or anything else we are looking for. 3916d9659bf6SJohannes Doerfert if (!CB.mayWriteToMemory() || isa<IntrinsicInst>(CB)) { 3917d9659bf6SJohannes Doerfert indicateOptimisticFixpoint(); 3918d9659bf6SJohannes Doerfert return; 3919d9659bf6SJohannes Doerfert } 3920d9659bf6SJohannes Doerfert 3921d9659bf6SJohannes Doerfert // Next we check if we know the callee. If it is a known OpenMP function 3922d9659bf6SJohannes Doerfert // we will handle them explicitly in the switch below. If it is not, we 3923d9659bf6SJohannes Doerfert // will use an AAKernelInfo object on the callee to gather information and 3924d9659bf6SJohannes Doerfert // merge that into the current state. The latter happens in the updateImpl. 3925d9659bf6SJohannes Doerfert auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 3926d9659bf6SJohannes Doerfert const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee); 3927d9659bf6SJohannes Doerfert if (It == OMPInfoCache.RuntimeFunctionIDMap.end()) { 3928d9659bf6SJohannes Doerfert // Unknown caller or declarations are not analyzable, we give up. 3929d9659bf6SJohannes Doerfert if (!Callee || !A.isFunctionIPOAmendable(*Callee)) { 3930d9659bf6SJohannes Doerfert 3931d9659bf6SJohannes Doerfert // Unknown callees might contain parallel regions, except if they have 3932d9659bf6SJohannes Doerfert // an appropriate assumption attached. 3933e52937ebSJoseph Huber if (!(AssumptionAA.hasAssumption("omp_no_openmp") || 3934e52937ebSJoseph Huber AssumptionAA.hasAssumption("omp_no_parallelism"))) 3935d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions.insert(&CB); 3936d9659bf6SJohannes Doerfert 3937514c033dSJohannes Doerfert // If SPMDCompatibilityTracker is not fixed, we need to give up on the 3938514c033dSJohannes Doerfert // idea we can run something unknown in SPMD-mode. 393929a3e3ddSGiorgis Georgakoudis if (!SPMDCompatibilityTracker.isAtFixpoint()) { 394029a3e3ddSGiorgis Georgakoudis SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 3941514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&CB); 394229a3e3ddSGiorgis Georgakoudis } 3943514c033dSJohannes Doerfert 3944d9659bf6SJohannes Doerfert // We have updated the state for this unknown call properly, there won't 3945d9659bf6SJohannes Doerfert // be any change so we indicate a fixpoint. 3946d9659bf6SJohannes Doerfert indicateOptimisticFixpoint(); 3947d9659bf6SJohannes Doerfert } 3948d9659bf6SJohannes Doerfert // If the callee is known and can be used in IPO, we will update the state 3949d9659bf6SJohannes Doerfert // based on the callee state in updateImpl. 3950d9659bf6SJohannes Doerfert return; 3951d9659bf6SJohannes Doerfert } 3952d9659bf6SJohannes Doerfert 3953d9659bf6SJohannes Doerfert const unsigned int WrapperFunctionArgNo = 6; 3954d9659bf6SJohannes Doerfert RuntimeFunction RF = It->getSecond(); 3955d9659bf6SJohannes Doerfert switch (RF) { 3956514c033dSJohannes Doerfert // All the functions we know are compatible with SPMD mode. 3957514c033dSJohannes Doerfert case OMPRTL___kmpc_is_spmd_exec_mode: 395887ce7e65SJoseph Huber case OMPRTL___kmpc_distribute_static_fini: 3959514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_fini: 3960514c033dSJohannes Doerfert case OMPRTL___kmpc_global_thread_num: 39615ab6aeddSJose M Monsalve Diaz case OMPRTL___kmpc_get_hardware_num_threads_in_block: 39625ab6aeddSJose M Monsalve Diaz case OMPRTL___kmpc_get_hardware_num_blocks: 3963514c033dSJohannes Doerfert case OMPRTL___kmpc_single: 3964514c033dSJohannes Doerfert case OMPRTL___kmpc_end_single: 3965514c033dSJohannes Doerfert case OMPRTL___kmpc_master: 3966514c033dSJohannes Doerfert case OMPRTL___kmpc_end_master: 3967514c033dSJohannes Doerfert case OMPRTL___kmpc_barrier: 3968744aa09fSJoseph Huber case OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2: 3969744aa09fSJoseph Huber case OMPRTL___kmpc_nvptx_teams_reduce_nowait_v2: 3970744aa09fSJoseph Huber case OMPRTL___kmpc_nvptx_end_reduce_nowait: 3971514c033dSJohannes Doerfert break; 397287ce7e65SJoseph Huber case OMPRTL___kmpc_distribute_static_init_4: 397387ce7e65SJoseph Huber case OMPRTL___kmpc_distribute_static_init_4u: 397487ce7e65SJoseph Huber case OMPRTL___kmpc_distribute_static_init_8: 397587ce7e65SJoseph Huber case OMPRTL___kmpc_distribute_static_init_8u: 3976514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_init_4: 3977514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_init_4u: 3978514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_init_8: 3979514c033dSJohannes Doerfert case OMPRTL___kmpc_for_static_init_8u: { 3980514c033dSJohannes Doerfert // Check the schedule and allow static schedule in SPMD mode. 3981514c033dSJohannes Doerfert unsigned ScheduleArgOpNo = 2; 3982514c033dSJohannes Doerfert auto *ScheduleTypeCI = 3983514c033dSJohannes Doerfert dyn_cast<ConstantInt>(CB.getArgOperand(ScheduleArgOpNo)); 3984514c033dSJohannes Doerfert unsigned ScheduleTypeVal = 3985514c033dSJohannes Doerfert ScheduleTypeCI ? ScheduleTypeCI->getZExtValue() : 0; 3986514c033dSJohannes Doerfert switch (OMPScheduleType(ScheduleTypeVal)) { 3987514c033dSJohannes Doerfert case OMPScheduleType::Static: 3988514c033dSJohannes Doerfert case OMPScheduleType::StaticChunked: 3989514c033dSJohannes Doerfert case OMPScheduleType::Distribute: 3990514c033dSJohannes Doerfert case OMPScheduleType::DistributeChunked: 3991514c033dSJohannes Doerfert break; 3992514c033dSJohannes Doerfert default: 399329a3e3ddSGiorgis Georgakoudis SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 3994514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&CB); 3995514c033dSJohannes Doerfert break; 3996514c033dSJohannes Doerfert }; 3997514c033dSJohannes Doerfert } break; 3998d9659bf6SJohannes Doerfert case OMPRTL___kmpc_target_init: 3999d9659bf6SJohannes Doerfert KernelInitCB = &CB; 4000d9659bf6SJohannes Doerfert break; 4001d9659bf6SJohannes Doerfert case OMPRTL___kmpc_target_deinit: 4002d9659bf6SJohannes Doerfert KernelDeinitCB = &CB; 4003d9659bf6SJohannes Doerfert break; 4004d9659bf6SJohannes Doerfert case OMPRTL___kmpc_parallel_51: 4005d9659bf6SJohannes Doerfert if (auto *ParallelRegion = dyn_cast<Function>( 4006d9659bf6SJohannes Doerfert CB.getArgOperand(WrapperFunctionArgNo)->stripPointerCasts())) { 4007d9659bf6SJohannes Doerfert ReachedKnownParallelRegions.insert(ParallelRegion); 4008d9659bf6SJohannes Doerfert break; 4009d9659bf6SJohannes Doerfert } 4010d9659bf6SJohannes Doerfert // The condition above should usually get the parallel region function 4011d9659bf6SJohannes Doerfert // pointer and record it. In the off chance it doesn't we assume the 4012d9659bf6SJohannes Doerfert // worst. 4013d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions.insert(&CB); 4014d9659bf6SJohannes Doerfert break; 4015d9659bf6SJohannes Doerfert case OMPRTL___kmpc_omp_task: 4016d9659bf6SJohannes Doerfert // We do not look into tasks right now, just give up. 40179ea5b972SJoseph Huber SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 4018514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&CB); 4019d9659bf6SJohannes Doerfert ReachedUnknownParallelRegions.insert(&CB); 4020c6457dcaSJohannes Doerfert break; 4021f8c40ed8SGiorgis Georgakoudis case OMPRTL___kmpc_alloc_shared: 4022f8c40ed8SGiorgis Georgakoudis case OMPRTL___kmpc_free_shared: 4023f8c40ed8SGiorgis Georgakoudis // Return without setting a fixpoint, to be resolved in updateImpl. 4024f8c40ed8SGiorgis Georgakoudis return; 4025d9659bf6SJohannes Doerfert default: 4026514c033dSJohannes Doerfert // Unknown OpenMP runtime calls cannot be executed in SPMD-mode, 4027c6457dcaSJohannes Doerfert // generally. However, they do not hide parallel regions. 40289ea5b972SJoseph Huber SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 4029514c033dSJohannes Doerfert SPMDCompatibilityTracker.insert(&CB); 4030c6457dcaSJohannes Doerfert break; 4031d9659bf6SJohannes Doerfert } 4032d9659bf6SJohannes Doerfert // All other OpenMP runtime calls will not reach parallel regions so they 4033d9659bf6SJohannes Doerfert // can be safely ignored for now. Since it is a known OpenMP runtime call we 4034d9659bf6SJohannes Doerfert // have now modeled all effects and there is no need for any update. 4035d9659bf6SJohannes Doerfert indicateOptimisticFixpoint(); 4036d9659bf6SJohannes Doerfert } 4037d9659bf6SJohannes Doerfert 4038d9659bf6SJohannes Doerfert ChangeStatus updateImpl(Attributor &A) override { 4039d9659bf6SJohannes Doerfert // TODO: Once we have call site specific value information we can provide 4040d9659bf6SJohannes Doerfert // call site specific liveness information and then it makes 4041d9659bf6SJohannes Doerfert // sense to specialize attributes for call sites arguments instead of 4042d9659bf6SJohannes Doerfert // redirecting requests to the callee argument. 4043d9659bf6SJohannes Doerfert Function *F = getAssociatedFunction(); 4044f8c40ed8SGiorgis Georgakoudis 4045f8c40ed8SGiorgis Georgakoudis auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 4046f8c40ed8SGiorgis Georgakoudis const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(F); 4047f8c40ed8SGiorgis Georgakoudis 4048f8c40ed8SGiorgis Georgakoudis // If F is not a runtime function, propagate the AAKernelInfo of the callee. 4049f8c40ed8SGiorgis Georgakoudis if (It == OMPInfoCache.RuntimeFunctionIDMap.end()) { 4050d9659bf6SJohannes Doerfert const IRPosition &FnPos = IRPosition::function(*F); 4051d9659bf6SJohannes Doerfert auto &FnAA = A.getAAFor<AAKernelInfo>(*this, FnPos, DepClassTy::REQUIRED); 4052d9659bf6SJohannes Doerfert if (getState() == FnAA.getState()) 4053d9659bf6SJohannes Doerfert return ChangeStatus::UNCHANGED; 4054d9659bf6SJohannes Doerfert getState() = FnAA.getState(); 4055d9659bf6SJohannes Doerfert return ChangeStatus::CHANGED; 4056d9659bf6SJohannes Doerfert } 4057f8c40ed8SGiorgis Georgakoudis 4058f8c40ed8SGiorgis Georgakoudis // F is a runtime function that allocates or frees memory, check 4059f8c40ed8SGiorgis Georgakoudis // AAHeapToStack and AAHeapToShared. 4060f8c40ed8SGiorgis Georgakoudis KernelInfoState StateBefore = getState(); 4061f8c40ed8SGiorgis Georgakoudis assert((It->getSecond() == OMPRTL___kmpc_alloc_shared || 4062f8c40ed8SGiorgis Georgakoudis It->getSecond() == OMPRTL___kmpc_free_shared) && 4063f8c40ed8SGiorgis Georgakoudis "Expected a __kmpc_alloc_shared or __kmpc_free_shared runtime call"); 4064f8c40ed8SGiorgis Georgakoudis 4065f8c40ed8SGiorgis Georgakoudis CallBase &CB = cast<CallBase>(getAssociatedValue()); 4066f8c40ed8SGiorgis Georgakoudis 4067f8c40ed8SGiorgis Georgakoudis auto &HeapToStackAA = A.getAAFor<AAHeapToStack>( 4068f8c40ed8SGiorgis Georgakoudis *this, IRPosition::function(*CB.getCaller()), DepClassTy::OPTIONAL); 4069f8c40ed8SGiorgis Georgakoudis auto &HeapToSharedAA = A.getAAFor<AAHeapToShared>( 4070f8c40ed8SGiorgis Georgakoudis *this, IRPosition::function(*CB.getCaller()), DepClassTy::OPTIONAL); 4071f8c40ed8SGiorgis Georgakoudis 4072f8c40ed8SGiorgis Georgakoudis RuntimeFunction RF = It->getSecond(); 4073f8c40ed8SGiorgis Georgakoudis 4074f8c40ed8SGiorgis Georgakoudis switch (RF) { 4075f8c40ed8SGiorgis Georgakoudis // If neither HeapToStack nor HeapToShared assume the call is removed, 4076f8c40ed8SGiorgis Georgakoudis // assume SPMD incompatibility. 4077f8c40ed8SGiorgis Georgakoudis case OMPRTL___kmpc_alloc_shared: 4078f8c40ed8SGiorgis Georgakoudis if (!HeapToStackAA.isAssumedHeapToStack(CB) && 4079f8c40ed8SGiorgis Georgakoudis !HeapToSharedAA.isAssumedHeapToShared(CB)) 4080f8c40ed8SGiorgis Georgakoudis SPMDCompatibilityTracker.insert(&CB); 4081f8c40ed8SGiorgis Georgakoudis break; 4082f8c40ed8SGiorgis Georgakoudis case OMPRTL___kmpc_free_shared: 4083f8c40ed8SGiorgis Georgakoudis if (!HeapToStackAA.isAssumedHeapToStackRemovedFree(CB) && 4084f8c40ed8SGiorgis Georgakoudis !HeapToSharedAA.isAssumedHeapToSharedRemovedFree(CB)) 4085f8c40ed8SGiorgis Georgakoudis SPMDCompatibilityTracker.insert(&CB); 4086f8c40ed8SGiorgis Georgakoudis break; 4087f8c40ed8SGiorgis Georgakoudis default: 40889ea5b972SJoseph Huber SPMDCompatibilityTracker.indicatePessimisticFixpoint(); 4089f8c40ed8SGiorgis Georgakoudis SPMDCompatibilityTracker.insert(&CB); 4090f8c40ed8SGiorgis Georgakoudis } 4091f8c40ed8SGiorgis Georgakoudis 4092f8c40ed8SGiorgis Georgakoudis return StateBefore == getState() ? ChangeStatus::UNCHANGED 4093f8c40ed8SGiorgis Georgakoudis : ChangeStatus::CHANGED; 4094f8c40ed8SGiorgis Georgakoudis } 4095d9659bf6SJohannes Doerfert }; 4096d9659bf6SJohannes Doerfert 4097ca662297SShilei Tian struct AAFoldRuntimeCall 4098ca662297SShilei Tian : public StateWrapper<BooleanState, AbstractAttribute> { 4099ca662297SShilei Tian using Base = StateWrapper<BooleanState, AbstractAttribute>; 4100ca662297SShilei Tian 4101ca662297SShilei Tian AAFoldRuntimeCall(const IRPosition &IRP, Attributor &A) : Base(IRP) {} 4102ca662297SShilei Tian 4103ca662297SShilei Tian /// Statistics are tracked as part of manifest for now. 4104ca662297SShilei Tian void trackStatistics() const override {} 4105ca662297SShilei Tian 4106ca662297SShilei Tian /// Create an abstract attribute biew for the position \p IRP. 4107ca662297SShilei Tian static AAFoldRuntimeCall &createForPosition(const IRPosition &IRP, 4108ca662297SShilei Tian Attributor &A); 4109ca662297SShilei Tian 4110ca662297SShilei Tian /// See AbstractAttribute::getName() 4111ca662297SShilei Tian const std::string getName() const override { return "AAFoldRuntimeCall"; } 4112ca662297SShilei Tian 4113ca662297SShilei Tian /// See AbstractAttribute::getIdAddr() 4114ca662297SShilei Tian const char *getIdAddr() const override { return &ID; } 4115ca662297SShilei Tian 4116ca662297SShilei Tian /// This function should return true if the type of the \p AA is 4117ca662297SShilei Tian /// AAFoldRuntimeCall 4118ca662297SShilei Tian static bool classof(const AbstractAttribute *AA) { 4119ca662297SShilei Tian return (AA->getIdAddr() == &ID); 4120ca662297SShilei Tian } 4121ca662297SShilei Tian 4122ca662297SShilei Tian static const char ID; 4123ca662297SShilei Tian }; 4124ca662297SShilei Tian 4125ca662297SShilei Tian struct AAFoldRuntimeCallCallSiteReturned : AAFoldRuntimeCall { 4126ca662297SShilei Tian AAFoldRuntimeCallCallSiteReturned(const IRPosition &IRP, Attributor &A) 4127ca662297SShilei Tian : AAFoldRuntimeCall(IRP, A) {} 4128ca662297SShilei Tian 4129ca662297SShilei Tian /// See AbstractAttribute::getAsStr() 4130ca662297SShilei Tian const std::string getAsStr() const override { 4131ca662297SShilei Tian if (!isValidState()) 4132ca662297SShilei Tian return "<invalid>"; 4133ca662297SShilei Tian 4134ca662297SShilei Tian std::string Str("simplified value: "); 4135ca662297SShilei Tian 4136ca662297SShilei Tian if (!SimplifiedValue.hasValue()) 4137ca662297SShilei Tian return Str + std::string("none"); 4138ca662297SShilei Tian 4139ca662297SShilei Tian if (!SimplifiedValue.getValue()) 4140ca662297SShilei Tian return Str + std::string("nullptr"); 4141ca662297SShilei Tian 4142ca662297SShilei Tian if (ConstantInt *CI = dyn_cast<ConstantInt>(SimplifiedValue.getValue())) 4143ca662297SShilei Tian return Str + std::to_string(CI->getSExtValue()); 4144ca662297SShilei Tian 4145ca662297SShilei Tian return Str + std::string("unknown"); 4146ca662297SShilei Tian } 4147ca662297SShilei Tian 4148ca662297SShilei Tian void initialize(Attributor &A) override { 4149cd0dd8ecSJoseph Huber if (DisableOpenMPOptFolding) 4150cd0dd8ecSJoseph Huber indicatePessimisticFixpoint(); 4151cd0dd8ecSJoseph Huber 4152ca662297SShilei Tian Function *Callee = getAssociatedFunction(); 4153ca662297SShilei Tian 4154ca662297SShilei Tian auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache()); 4155ca662297SShilei Tian const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee); 4156ca662297SShilei Tian assert(It != OMPInfoCache.RuntimeFunctionIDMap.end() && 4157ca662297SShilei Tian "Expected a known OpenMP runtime function"); 4158ca662297SShilei Tian 4159ca662297SShilei Tian RFKind = It->getSecond(); 4160ca662297SShilei Tian 4161ca662297SShilei Tian CallBase &CB = cast<CallBase>(getAssociatedValue()); 4162ca662297SShilei Tian A.registerSimplificationCallback( 4163ca662297SShilei Tian IRPosition::callsite_returned(CB), 4164ca662297SShilei Tian [&](const IRPosition &IRP, const AbstractAttribute *AA, 4165ca662297SShilei Tian bool &UsedAssumedInformation) -> Optional<Value *> { 4166ca662297SShilei Tian assert((isValidState() || (SimplifiedValue.hasValue() && 4167ca662297SShilei Tian SimplifiedValue.getValue() == nullptr)) && 4168ca662297SShilei Tian "Unexpected invalid state!"); 4169ca662297SShilei Tian 4170ca662297SShilei Tian if (!isAtFixpoint()) { 4171ca662297SShilei Tian UsedAssumedInformation = true; 4172ca662297SShilei Tian if (AA) 4173ca662297SShilei Tian A.recordDependence(*this, *AA, DepClassTy::OPTIONAL); 4174ca662297SShilei Tian } 4175ca662297SShilei Tian return SimplifiedValue; 4176ca662297SShilei Tian }); 4177ca662297SShilei Tian } 4178ca662297SShilei Tian 4179ca662297SShilei Tian ChangeStatus updateImpl(Attributor &A) override { 4180ca662297SShilei Tian ChangeStatus Changed = ChangeStatus::UNCHANGED; 4181ca662297SShilei Tian switch (RFKind) { 4182ca662297SShilei Tian case OMPRTL___kmpc_is_spmd_exec_mode: 4183c23da666SShilei Tian Changed |= foldIsSPMDExecMode(A); 4184ca662297SShilei Tian break; 4185196fe994SJoseph Huber case OMPRTL___kmpc_is_generic_main_thread_id: 4186196fe994SJoseph Huber Changed |= foldIsGenericMainThread(A); 4187196fe994SJoseph Huber break; 4188e97e0a4fSShilei Tian case OMPRTL___kmpc_parallel_level: 4189e97e0a4fSShilei Tian Changed |= foldParallelLevel(A); 4190e97e0a4fSShilei Tian break; 41915ab6aeddSJose M Monsalve Diaz case OMPRTL___kmpc_get_hardware_num_threads_in_block: 41925ab6aeddSJose M Monsalve Diaz Changed = Changed | foldKernelFnAttribute(A, "omp_target_thread_limit"); 41935ab6aeddSJose M Monsalve Diaz break; 41945ab6aeddSJose M Monsalve Diaz case OMPRTL___kmpc_get_hardware_num_blocks: 41955ab6aeddSJose M Monsalve Diaz Changed = Changed | foldKernelFnAttribute(A, "omp_target_num_teams"); 41965ab6aeddSJose M Monsalve Diaz break; 4197ca662297SShilei Tian default: 4198ca662297SShilei Tian llvm_unreachable("Unhandled OpenMP runtime function!"); 4199ca662297SShilei Tian } 4200ca662297SShilei Tian 4201ca662297SShilei Tian return Changed; 4202ca662297SShilei Tian } 4203ca662297SShilei Tian 4204ca662297SShilei Tian ChangeStatus manifest(Attributor &A) override { 4205ca662297SShilei Tian ChangeStatus Changed = ChangeStatus::UNCHANGED; 4206ca662297SShilei Tian 4207ca662297SShilei Tian if (SimplifiedValue.hasValue() && SimplifiedValue.getValue()) { 42087eb899cbSJoseph Huber Instruction &I = *getCtxI(); 42097eb899cbSJoseph Huber A.changeValueAfterManifest(I, **SimplifiedValue); 42107eb899cbSJoseph Huber A.deleteAfterManifest(I); 4211196fe994SJoseph Huber 42127eb899cbSJoseph Huber CallBase *CB = dyn_cast<CallBase>(&I); 42137eb899cbSJoseph Huber auto Remark = [&](OptimizationRemark OR) { 42147eb899cbSJoseph Huber if (auto *C = dyn_cast<ConstantInt>(*SimplifiedValue)) 42157eb899cbSJoseph Huber return OR << "Replacing OpenMP runtime call " 42167eb899cbSJoseph Huber << CB->getCalledFunction()->getName() << " with " 42177eb899cbSJoseph Huber << ore::NV("FoldedValue", C->getZExtValue()) << "."; 42187eb899cbSJoseph Huber return OR << "Replacing OpenMP runtime call " 42197eb899cbSJoseph Huber << CB->getCalledFunction()->getName() << "."; 42207eb899cbSJoseph Huber }; 42217eb899cbSJoseph Huber 42227eb899cbSJoseph Huber if (CB && EnableVerboseRemarks) 42237eb899cbSJoseph Huber A.emitRemark<OptimizationRemark>(CB, "OMP180", Remark); 42247eb899cbSJoseph Huber 42257eb899cbSJoseph Huber LLVM_DEBUG(dbgs() << TAG << "Replacing runtime call: " << I << " with " 4226196fe994SJoseph Huber << **SimplifiedValue << "\n"); 4227196fe994SJoseph Huber 4228ca662297SShilei Tian Changed = ChangeStatus::CHANGED; 4229ca662297SShilei Tian } 4230ca662297SShilei Tian 4231ca662297SShilei Tian return Changed; 4232ca662297SShilei Tian } 4233ca662297SShilei Tian 4234ca662297SShilei Tian ChangeStatus indicatePessimisticFixpoint() override { 4235ca662297SShilei Tian SimplifiedValue = nullptr; 4236ca662297SShilei Tian return AAFoldRuntimeCall::indicatePessimisticFixpoint(); 4237ca662297SShilei Tian } 4238ca662297SShilei Tian 4239ca662297SShilei Tian private: 4240ca662297SShilei Tian /// Fold __kmpc_is_spmd_exec_mode into a constant if possible. 4241ca662297SShilei Tian ChangeStatus foldIsSPMDExecMode(Attributor &A) { 4242ca662297SShilei Tian Optional<Value *> SimplifiedValueBefore = SimplifiedValue; 4243ca662297SShilei Tian 4244ca662297SShilei Tian unsigned AssumedSPMDCount = 0, KnownSPMDCount = 0; 4245ca662297SShilei Tian unsigned AssumedNonSPMDCount = 0, KnownNonSPMDCount = 0; 4246ca662297SShilei Tian auto &CallerKernelInfoAA = A.getAAFor<AAKernelInfo>( 4247ca662297SShilei Tian *this, IRPosition::function(*getAnchorScope()), DepClassTy::REQUIRED); 4248ca662297SShilei Tian 4249ca662297SShilei Tian if (!CallerKernelInfoAA.ReachingKernelEntries.isValidState()) 4250ca662297SShilei Tian return indicatePessimisticFixpoint(); 4251ca662297SShilei Tian 4252ca662297SShilei Tian for (Kernel K : CallerKernelInfoAA.ReachingKernelEntries) { 4253ca662297SShilei Tian auto &AA = A.getAAFor<AAKernelInfo>(*this, IRPosition::function(*K), 4254ca662297SShilei Tian DepClassTy::REQUIRED); 4255ca662297SShilei Tian 4256ca662297SShilei Tian if (!AA.isValidState()) { 4257ca662297SShilei Tian SimplifiedValue = nullptr; 4258ca662297SShilei Tian return indicatePessimisticFixpoint(); 4259ca662297SShilei Tian } 4260ca662297SShilei Tian 4261ca662297SShilei Tian if (AA.SPMDCompatibilityTracker.isAssumed()) { 4262ca662297SShilei Tian if (AA.SPMDCompatibilityTracker.isAtFixpoint()) 4263ca662297SShilei Tian ++KnownSPMDCount; 4264ca662297SShilei Tian else 4265ca662297SShilei Tian ++AssumedSPMDCount; 4266ca662297SShilei Tian } else { 4267ca662297SShilei Tian if (AA.SPMDCompatibilityTracker.isAtFixpoint()) 4268ca662297SShilei Tian ++KnownNonSPMDCount; 4269ca662297SShilei Tian else 4270ca662297SShilei Tian ++AssumedNonSPMDCount; 4271ca662297SShilei Tian } 4272ca662297SShilei Tian } 4273ca662297SShilei Tian 4274ae69f468SShilei Tian if ((AssumedSPMDCount + KnownSPMDCount) && 4275ae69f468SShilei Tian (AssumedNonSPMDCount + KnownNonSPMDCount)) 4276ca662297SShilei Tian return indicatePessimisticFixpoint(); 4277ca662297SShilei Tian 4278ca662297SShilei Tian auto &Ctx = getAnchorValue().getContext(); 4279ca662297SShilei Tian if (KnownSPMDCount || AssumedSPMDCount) { 4280ca662297SShilei Tian assert(KnownNonSPMDCount == 0 && AssumedNonSPMDCount == 0 && 4281ca662297SShilei Tian "Expected only SPMD kernels!"); 4282ca662297SShilei Tian // All reaching kernels are in SPMD mode. Update all function calls to 4283ca662297SShilei Tian // __kmpc_is_spmd_exec_mode to 1. 4284ca662297SShilei Tian SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), true); 4285d3454ee8SShilei Tian } else if (KnownNonSPMDCount || AssumedNonSPMDCount) { 4286ca662297SShilei Tian assert(KnownSPMDCount == 0 && AssumedSPMDCount == 0 && 4287ca662297SShilei Tian "Expected only non-SPMD kernels!"); 4288ca662297SShilei Tian // All reaching kernels are in non-SPMD mode. Update all function 4289ca662297SShilei Tian // calls to __kmpc_is_spmd_exec_mode to 0. 4290ca662297SShilei Tian SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), false); 4291d3454ee8SShilei Tian } else { 4292d3454ee8SShilei Tian // We have empty reaching kernels, therefore we cannot tell if the 4293d3454ee8SShilei Tian // associated call site can be folded. At this moment, SimplifiedValue 4294d3454ee8SShilei Tian // must be none. 4295d3454ee8SShilei Tian assert(!SimplifiedValue.hasValue() && "SimplifiedValue should be none"); 4296ca662297SShilei Tian } 4297ca662297SShilei Tian 4298ca662297SShilei Tian return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED 4299ca662297SShilei Tian : ChangeStatus::CHANGED; 4300ca662297SShilei Tian } 4301ca662297SShilei Tian 4302196fe994SJoseph Huber /// Fold __kmpc_is_generic_main_thread_id into a constant if possible. 4303196fe994SJoseph Huber ChangeStatus foldIsGenericMainThread(Attributor &A) { 4304196fe994SJoseph Huber Optional<Value *> SimplifiedValueBefore = SimplifiedValue; 4305196fe994SJoseph Huber 4306196fe994SJoseph Huber CallBase &CB = cast<CallBase>(getAssociatedValue()); 4307196fe994SJoseph Huber Function *F = CB.getFunction(); 4308196fe994SJoseph Huber const auto &ExecutionDomainAA = A.getAAFor<AAExecutionDomain>( 4309196fe994SJoseph Huber *this, IRPosition::function(*F), DepClassTy::REQUIRED); 4310196fe994SJoseph Huber 4311196fe994SJoseph Huber if (!ExecutionDomainAA.isValidState()) 4312196fe994SJoseph Huber return indicatePessimisticFixpoint(); 4313196fe994SJoseph Huber 4314196fe994SJoseph Huber auto &Ctx = getAnchorValue().getContext(); 4315196fe994SJoseph Huber if (ExecutionDomainAA.isExecutedByInitialThreadOnly(CB)) 4316196fe994SJoseph Huber SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), true); 4317196fe994SJoseph Huber else 4318196fe994SJoseph Huber return indicatePessimisticFixpoint(); 4319196fe994SJoseph Huber 4320196fe994SJoseph Huber return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED 4321196fe994SJoseph Huber : ChangeStatus::CHANGED; 4322196fe994SJoseph Huber } 4323196fe994SJoseph Huber 4324e97e0a4fSShilei Tian /// Fold __kmpc_parallel_level into a constant if possible. 4325e97e0a4fSShilei Tian ChangeStatus foldParallelLevel(Attributor &A) { 4326e97e0a4fSShilei Tian Optional<Value *> SimplifiedValueBefore = SimplifiedValue; 4327e97e0a4fSShilei Tian 4328e97e0a4fSShilei Tian auto &CallerKernelInfoAA = A.getAAFor<AAKernelInfo>( 4329e97e0a4fSShilei Tian *this, IRPosition::function(*getAnchorScope()), DepClassTy::REQUIRED); 4330e97e0a4fSShilei Tian 4331e97e0a4fSShilei Tian if (!CallerKernelInfoAA.ParallelLevels.isValidState()) 4332e97e0a4fSShilei Tian return indicatePessimisticFixpoint(); 4333e97e0a4fSShilei Tian 4334e97e0a4fSShilei Tian if (!CallerKernelInfoAA.ReachingKernelEntries.isValidState()) 4335e97e0a4fSShilei Tian return indicatePessimisticFixpoint(); 4336e97e0a4fSShilei Tian 4337e97e0a4fSShilei Tian if (CallerKernelInfoAA.ReachingKernelEntries.empty()) { 4338e97e0a4fSShilei Tian assert(!SimplifiedValue.hasValue() && 4339e97e0a4fSShilei Tian "SimplifiedValue should keep none at this point"); 4340e97e0a4fSShilei Tian return ChangeStatus::UNCHANGED; 4341e97e0a4fSShilei Tian } 4342e97e0a4fSShilei Tian 4343e97e0a4fSShilei Tian unsigned AssumedSPMDCount = 0, KnownSPMDCount = 0; 4344e97e0a4fSShilei Tian unsigned AssumedNonSPMDCount = 0, KnownNonSPMDCount = 0; 4345e97e0a4fSShilei Tian for (Kernel K : CallerKernelInfoAA.ReachingKernelEntries) { 4346e97e0a4fSShilei Tian auto &AA = A.getAAFor<AAKernelInfo>(*this, IRPosition::function(*K), 4347e97e0a4fSShilei Tian DepClassTy::REQUIRED); 4348e97e0a4fSShilei Tian if (!AA.SPMDCompatibilityTracker.isValidState()) 4349e97e0a4fSShilei Tian return indicatePessimisticFixpoint(); 4350e97e0a4fSShilei Tian 4351e97e0a4fSShilei Tian if (AA.SPMDCompatibilityTracker.isAssumed()) { 4352e97e0a4fSShilei Tian if (AA.SPMDCompatibilityTracker.isAtFixpoint()) 4353e97e0a4fSShilei Tian ++KnownSPMDCount; 4354e97e0a4fSShilei Tian else 4355e97e0a4fSShilei Tian ++AssumedSPMDCount; 4356e97e0a4fSShilei Tian } else { 4357e97e0a4fSShilei Tian if (AA.SPMDCompatibilityTracker.isAtFixpoint()) 4358e97e0a4fSShilei Tian ++KnownNonSPMDCount; 4359e97e0a4fSShilei Tian else 4360e97e0a4fSShilei Tian ++AssumedNonSPMDCount; 4361e97e0a4fSShilei Tian } 4362e97e0a4fSShilei Tian } 4363e97e0a4fSShilei Tian 4364e97e0a4fSShilei Tian if ((AssumedSPMDCount + KnownSPMDCount) && 4365e97e0a4fSShilei Tian (AssumedNonSPMDCount + KnownNonSPMDCount)) 4366e97e0a4fSShilei Tian return indicatePessimisticFixpoint(); 4367e97e0a4fSShilei Tian 4368e97e0a4fSShilei Tian auto &Ctx = getAnchorValue().getContext(); 4369e97e0a4fSShilei Tian // If the caller can only be reached by SPMD kernel entries, the parallel 4370e97e0a4fSShilei Tian // level is 1. Similarly, if the caller can only be reached by non-SPMD 4371e97e0a4fSShilei Tian // kernel entries, it is 0. 4372e97e0a4fSShilei Tian if (AssumedSPMDCount || KnownSPMDCount) { 4373e97e0a4fSShilei Tian assert(KnownNonSPMDCount == 0 && AssumedNonSPMDCount == 0 && 4374e97e0a4fSShilei Tian "Expected only SPMD kernels!"); 4375e97e0a4fSShilei Tian SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), 1); 4376e97e0a4fSShilei Tian } else { 4377e97e0a4fSShilei Tian assert(KnownSPMDCount == 0 && AssumedSPMDCount == 0 && 4378e97e0a4fSShilei Tian "Expected only non-SPMD kernels!"); 4379e97e0a4fSShilei Tian SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), 0); 4380e97e0a4fSShilei Tian } 43815ab6aeddSJose M Monsalve Diaz return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED 43825ab6aeddSJose M Monsalve Diaz : ChangeStatus::CHANGED; 43835ab6aeddSJose M Monsalve Diaz } 4384e97e0a4fSShilei Tian 43855ab6aeddSJose M Monsalve Diaz ChangeStatus foldKernelFnAttribute(Attributor &A, llvm::StringRef Attr) { 43865ab6aeddSJose M Monsalve Diaz // Specialize only if all the calls agree with the attribute constant value 43875ab6aeddSJose M Monsalve Diaz int32_t CurrentAttrValue = -1; 43885ab6aeddSJose M Monsalve Diaz Optional<Value *> SimplifiedValueBefore = SimplifiedValue; 43895ab6aeddSJose M Monsalve Diaz 43905ab6aeddSJose M Monsalve Diaz auto &CallerKernelInfoAA = A.getAAFor<AAKernelInfo>( 43915ab6aeddSJose M Monsalve Diaz *this, IRPosition::function(*getAnchorScope()), DepClassTy::REQUIRED); 43925ab6aeddSJose M Monsalve Diaz 43935ab6aeddSJose M Monsalve Diaz if (!CallerKernelInfoAA.ReachingKernelEntries.isValidState()) 43945ab6aeddSJose M Monsalve Diaz return indicatePessimisticFixpoint(); 43955ab6aeddSJose M Monsalve Diaz 43965ab6aeddSJose M Monsalve Diaz // Iterate over the kernels that reach this function 43975ab6aeddSJose M Monsalve Diaz for (Kernel K : CallerKernelInfoAA.ReachingKernelEntries) { 43985ab6aeddSJose M Monsalve Diaz int32_t NextAttrVal = -1; 43995ab6aeddSJose M Monsalve Diaz if (K->hasFnAttribute(Attr)) 44005ab6aeddSJose M Monsalve Diaz NextAttrVal = 44015ab6aeddSJose M Monsalve Diaz std::stoi(K->getFnAttribute(Attr).getValueAsString().str()); 44025ab6aeddSJose M Monsalve Diaz 44035ab6aeddSJose M Monsalve Diaz if (NextAttrVal == -1 || 44045ab6aeddSJose M Monsalve Diaz (CurrentAttrValue != -1 && CurrentAttrValue != NextAttrVal)) 44055ab6aeddSJose M Monsalve Diaz return indicatePessimisticFixpoint(); 44065ab6aeddSJose M Monsalve Diaz CurrentAttrValue = NextAttrVal; 44075ab6aeddSJose M Monsalve Diaz } 44085ab6aeddSJose M Monsalve Diaz 44095ab6aeddSJose M Monsalve Diaz if (CurrentAttrValue != -1) { 44105ab6aeddSJose M Monsalve Diaz auto &Ctx = getAnchorValue().getContext(); 44115ab6aeddSJose M Monsalve Diaz SimplifiedValue = 44125ab6aeddSJose M Monsalve Diaz ConstantInt::get(Type::getInt32Ty(Ctx), CurrentAttrValue); 44135ab6aeddSJose M Monsalve Diaz } 4414e97e0a4fSShilei Tian return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED 4415e97e0a4fSShilei Tian : ChangeStatus::CHANGED; 4416e97e0a4fSShilei Tian } 4417e97e0a4fSShilei Tian 4418ca662297SShilei Tian /// An optional value the associated value is assumed to fold to. That is, we 4419ca662297SShilei Tian /// assume the associated value (which is a call) can be replaced by this 4420ca662297SShilei Tian /// simplified value. 4421ca662297SShilei Tian Optional<Value *> SimplifiedValue; 4422ca662297SShilei Tian 4423ca662297SShilei Tian /// The runtime function kind of the callee of the associated call site. 4424ca662297SShilei Tian RuntimeFunction RFKind; 4425ca662297SShilei Tian }; 4426ca662297SShilei Tian 44279548b74aSJohannes Doerfert } // namespace 44289548b74aSJohannes Doerfert 44295ab6aeddSJose M Monsalve Diaz /// Register folding callsite 44305ab6aeddSJose M Monsalve Diaz void OpenMPOpt::registerFoldRuntimeCall(RuntimeFunction RF) { 44315ab6aeddSJose M Monsalve Diaz auto &RFI = OMPInfoCache.RFIs[RF]; 44325ab6aeddSJose M Monsalve Diaz RFI.foreachUse(SCC, [&](Use &U, Function &F) { 44335ab6aeddSJose M Monsalve Diaz CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &RFI); 44345ab6aeddSJose M Monsalve Diaz if (!CI) 44355ab6aeddSJose M Monsalve Diaz return false; 44365ab6aeddSJose M Monsalve Diaz A.getOrCreateAAFor<AAFoldRuntimeCall>( 44375ab6aeddSJose M Monsalve Diaz IRPosition::callsite_returned(*CI), /* QueryingAA */ nullptr, 44385ab6aeddSJose M Monsalve Diaz DepClassTy::NONE, /* ForceUpdate */ false, 44395ab6aeddSJose M Monsalve Diaz /* UpdateAfterInit */ false); 44405ab6aeddSJose M Monsalve Diaz return false; 44415ab6aeddSJose M Monsalve Diaz }); 44425ab6aeddSJose M Monsalve Diaz } 44435ab6aeddSJose M Monsalve Diaz 4444d9659bf6SJohannes Doerfert void OpenMPOpt::registerAAs(bool IsModulePass) { 4445d9659bf6SJohannes Doerfert if (SCC.empty()) 4446d9659bf6SJohannes Doerfert 4447d9659bf6SJohannes Doerfert return; 4448d9659bf6SJohannes Doerfert if (IsModulePass) { 4449d9659bf6SJohannes Doerfert // Ensure we create the AAKernelInfo AAs first and without triggering an 4450d9659bf6SJohannes Doerfert // update. This will make sure we register all value simplification 4451d9659bf6SJohannes Doerfert // callbacks before any other AA has the chance to create an AAValueSimplify 4452d9659bf6SJohannes Doerfert // or similar. 4453d9659bf6SJohannes Doerfert for (Function *Kernel : OMPInfoCache.Kernels) 4454d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAKernelInfo>( 4455d9659bf6SJohannes Doerfert IRPosition::function(*Kernel), /* QueryingAA */ nullptr, 4456d9659bf6SJohannes Doerfert DepClassTy::NONE, /* ForceUpdate */ false, 4457d9659bf6SJohannes Doerfert /* UpdateAfterInit */ false); 4458ca662297SShilei Tian 44595ab6aeddSJose M Monsalve Diaz registerFoldRuntimeCall(OMPRTL___kmpc_is_generic_main_thread_id); 44605ab6aeddSJose M Monsalve Diaz registerFoldRuntimeCall(OMPRTL___kmpc_is_spmd_exec_mode); 44615ab6aeddSJose M Monsalve Diaz registerFoldRuntimeCall(OMPRTL___kmpc_parallel_level); 44625ab6aeddSJose M Monsalve Diaz registerFoldRuntimeCall(OMPRTL___kmpc_get_hardware_num_threads_in_block); 44635ab6aeddSJose M Monsalve Diaz registerFoldRuntimeCall(OMPRTL___kmpc_get_hardware_num_blocks); 4464d9659bf6SJohannes Doerfert } 4465d9659bf6SJohannes Doerfert 4466d9659bf6SJohannes Doerfert // Create CallSite AA for all Getters. 4467d9659bf6SJohannes Doerfert for (int Idx = 0; Idx < OMPInfoCache.ICVs.size() - 1; ++Idx) { 4468d9659bf6SJohannes Doerfert auto ICVInfo = OMPInfoCache.ICVs[static_cast<InternalControlVar>(Idx)]; 4469d9659bf6SJohannes Doerfert 4470d9659bf6SJohannes Doerfert auto &GetterRFI = OMPInfoCache.RFIs[ICVInfo.Getter]; 4471d9659bf6SJohannes Doerfert 4472d9659bf6SJohannes Doerfert auto CreateAA = [&](Use &U, Function &Caller) { 4473d9659bf6SJohannes Doerfert CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &GetterRFI); 4474d9659bf6SJohannes Doerfert if (!CI) 4475d9659bf6SJohannes Doerfert return false; 4476d9659bf6SJohannes Doerfert 4477d9659bf6SJohannes Doerfert auto &CB = cast<CallBase>(*CI); 4478d9659bf6SJohannes Doerfert 4479d9659bf6SJohannes Doerfert IRPosition CBPos = IRPosition::callsite_function(CB); 4480d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAICVTracker>(CBPos); 4481d9659bf6SJohannes Doerfert return false; 4482d9659bf6SJohannes Doerfert }; 4483d9659bf6SJohannes Doerfert 4484d9659bf6SJohannes Doerfert GetterRFI.foreachUse(SCC, CreateAA); 4485d9659bf6SJohannes Doerfert } 4486d9659bf6SJohannes Doerfert auto &GlobalizationRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared]; 4487d9659bf6SJohannes Doerfert auto CreateAA = [&](Use &U, Function &F) { 4488d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAHeapToShared>(IRPosition::function(F)); 4489d9659bf6SJohannes Doerfert return false; 4490d9659bf6SJohannes Doerfert }; 4491cd0dd8ecSJoseph Huber if (!DisableOpenMPOptDeglobalization) 4492d9659bf6SJohannes Doerfert GlobalizationRFI.foreachUse(SCC, CreateAA); 4493d9659bf6SJohannes Doerfert 4494d9659bf6SJohannes Doerfert // Create an ExecutionDomain AA for every function and a HeapToStack AA for 4495d9659bf6SJohannes Doerfert // every function if there is a device kernel. 449670b75f62SJohannes Doerfert if (!isOpenMPDevice(M)) 449770b75f62SJohannes Doerfert return; 449870b75f62SJohannes Doerfert 4499d9659bf6SJohannes Doerfert for (auto *F : SCC) { 450070b75f62SJohannes Doerfert if (F->isDeclaration()) 450170b75f62SJohannes Doerfert continue; 450270b75f62SJohannes Doerfert 4503d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAExecutionDomain>(IRPosition::function(*F)); 4504cd0dd8ecSJoseph Huber if (!DisableOpenMPOptDeglobalization) 4505d9659bf6SJohannes Doerfert A.getOrCreateAAFor<AAHeapToStack>(IRPosition::function(*F)); 450670b75f62SJohannes Doerfert 450770b75f62SJohannes Doerfert for (auto &I : instructions(*F)) { 450870b75f62SJohannes Doerfert if (auto *LI = dyn_cast<LoadInst>(&I)) { 450970b75f62SJohannes Doerfert bool UsedAssumedInformation = false; 451070b75f62SJohannes Doerfert A.getAssumedSimplified(IRPosition::value(*LI), /* AA */ nullptr, 451170b75f62SJohannes Doerfert UsedAssumedInformation); 45123e0c512cSJohannes Doerfert } else if (auto *SI = dyn_cast<StoreInst>(&I)) { 45133e0c512cSJohannes Doerfert A.getOrCreateAAFor<AAIsDead>(IRPosition::value(*SI)); 451470b75f62SJohannes Doerfert } 451570b75f62SJohannes Doerfert } 4516d9659bf6SJohannes Doerfert } 4517d9659bf6SJohannes Doerfert } 4518d9659bf6SJohannes Doerfert 4519b8235d2bSsstefan1 const char AAICVTracker::ID = 0; 4520d9659bf6SJohannes Doerfert const char AAKernelInfo::ID = 0; 452118283125SJoseph Huber const char AAExecutionDomain::ID = 0; 45226fc51c9fSJoseph Huber const char AAHeapToShared::ID = 0; 4523ca662297SShilei Tian const char AAFoldRuntimeCall::ID = 0; 4524b8235d2bSsstefan1 4525b8235d2bSsstefan1 AAICVTracker &AAICVTracker::createForPosition(const IRPosition &IRP, 4526b8235d2bSsstefan1 Attributor &A) { 4527b8235d2bSsstefan1 AAICVTracker *AA = nullptr; 4528b8235d2bSsstefan1 switch (IRP.getPositionKind()) { 4529b8235d2bSsstefan1 case IRPosition::IRP_INVALID: 4530b8235d2bSsstefan1 case IRPosition::IRP_FLOAT: 4531b8235d2bSsstefan1 case IRPosition::IRP_ARGUMENT: 4532b8235d2bSsstefan1 case IRPosition::IRP_CALL_SITE_ARGUMENT: 45331de70a72SJohannes Doerfert llvm_unreachable("ICVTracker can only be created for function position!"); 45345dfd7cc4Ssstefan1 case IRPosition::IRP_RETURNED: 45355dfd7cc4Ssstefan1 AA = new (A.Allocator) AAICVTrackerFunctionReturned(IRP, A); 45365dfd7cc4Ssstefan1 break; 45375dfd7cc4Ssstefan1 case IRPosition::IRP_CALL_SITE_RETURNED: 45385dfd7cc4Ssstefan1 AA = new (A.Allocator) AAICVTrackerCallSiteReturned(IRP, A); 45395dfd7cc4Ssstefan1 break; 45405dfd7cc4Ssstefan1 case IRPosition::IRP_CALL_SITE: 45415dfd7cc4Ssstefan1 AA = new (A.Allocator) AAICVTrackerCallSite(IRP, A); 45425dfd7cc4Ssstefan1 break; 4543b8235d2bSsstefan1 case IRPosition::IRP_FUNCTION: 4544b8235d2bSsstefan1 AA = new (A.Allocator) AAICVTrackerFunction(IRP, A); 4545b8235d2bSsstefan1 break; 4546b8235d2bSsstefan1 } 4547b8235d2bSsstefan1 4548b8235d2bSsstefan1 return *AA; 4549b8235d2bSsstefan1 } 4550b8235d2bSsstefan1 455118283125SJoseph Huber AAExecutionDomain &AAExecutionDomain::createForPosition(const IRPosition &IRP, 455218283125SJoseph Huber Attributor &A) { 455318283125SJoseph Huber AAExecutionDomainFunction *AA = nullptr; 455418283125SJoseph Huber switch (IRP.getPositionKind()) { 455518283125SJoseph Huber case IRPosition::IRP_INVALID: 455618283125SJoseph Huber case IRPosition::IRP_FLOAT: 455718283125SJoseph Huber case IRPosition::IRP_ARGUMENT: 455818283125SJoseph Huber case IRPosition::IRP_CALL_SITE_ARGUMENT: 455918283125SJoseph Huber case IRPosition::IRP_RETURNED: 456018283125SJoseph Huber case IRPosition::IRP_CALL_SITE_RETURNED: 456118283125SJoseph Huber case IRPosition::IRP_CALL_SITE: 456218283125SJoseph Huber llvm_unreachable( 456318283125SJoseph Huber "AAExecutionDomain can only be created for function position!"); 456418283125SJoseph Huber case IRPosition::IRP_FUNCTION: 456518283125SJoseph Huber AA = new (A.Allocator) AAExecutionDomainFunction(IRP, A); 456618283125SJoseph Huber break; 456718283125SJoseph Huber } 456818283125SJoseph Huber 456918283125SJoseph Huber return *AA; 457018283125SJoseph Huber } 457118283125SJoseph Huber 45726fc51c9fSJoseph Huber AAHeapToShared &AAHeapToShared::createForPosition(const IRPosition &IRP, 45736fc51c9fSJoseph Huber Attributor &A) { 45746fc51c9fSJoseph Huber AAHeapToSharedFunction *AA = nullptr; 45756fc51c9fSJoseph Huber switch (IRP.getPositionKind()) { 45766fc51c9fSJoseph Huber case IRPosition::IRP_INVALID: 45776fc51c9fSJoseph Huber case IRPosition::IRP_FLOAT: 45786fc51c9fSJoseph Huber case IRPosition::IRP_ARGUMENT: 45796fc51c9fSJoseph Huber case IRPosition::IRP_CALL_SITE_ARGUMENT: 45806fc51c9fSJoseph Huber case IRPosition::IRP_RETURNED: 45816fc51c9fSJoseph Huber case IRPosition::IRP_CALL_SITE_RETURNED: 45826fc51c9fSJoseph Huber case IRPosition::IRP_CALL_SITE: 45836fc51c9fSJoseph Huber llvm_unreachable( 45846fc51c9fSJoseph Huber "AAHeapToShared can only be created for function position!"); 45856fc51c9fSJoseph Huber case IRPosition::IRP_FUNCTION: 45866fc51c9fSJoseph Huber AA = new (A.Allocator) AAHeapToSharedFunction(IRP, A); 45876fc51c9fSJoseph Huber break; 45886fc51c9fSJoseph Huber } 45896fc51c9fSJoseph Huber 45906fc51c9fSJoseph Huber return *AA; 45916fc51c9fSJoseph Huber } 45926fc51c9fSJoseph Huber 4593d9659bf6SJohannes Doerfert AAKernelInfo &AAKernelInfo::createForPosition(const IRPosition &IRP, 4594d9659bf6SJohannes Doerfert Attributor &A) { 4595d9659bf6SJohannes Doerfert AAKernelInfo *AA = nullptr; 4596d9659bf6SJohannes Doerfert switch (IRP.getPositionKind()) { 4597d9659bf6SJohannes Doerfert case IRPosition::IRP_INVALID: 4598d9659bf6SJohannes Doerfert case IRPosition::IRP_FLOAT: 4599d9659bf6SJohannes Doerfert case IRPosition::IRP_ARGUMENT: 4600d9659bf6SJohannes Doerfert case IRPosition::IRP_RETURNED: 4601d9659bf6SJohannes Doerfert case IRPosition::IRP_CALL_SITE_RETURNED: 4602d9659bf6SJohannes Doerfert case IRPosition::IRP_CALL_SITE_ARGUMENT: 4603d9659bf6SJohannes Doerfert llvm_unreachable("KernelInfo can only be created for function position!"); 4604d9659bf6SJohannes Doerfert case IRPosition::IRP_CALL_SITE: 4605d9659bf6SJohannes Doerfert AA = new (A.Allocator) AAKernelInfoCallSite(IRP, A); 4606d9659bf6SJohannes Doerfert break; 4607d9659bf6SJohannes Doerfert case IRPosition::IRP_FUNCTION: 4608d9659bf6SJohannes Doerfert AA = new (A.Allocator) AAKernelInfoFunction(IRP, A); 4609d9659bf6SJohannes Doerfert break; 4610d9659bf6SJohannes Doerfert } 4611d9659bf6SJohannes Doerfert 4612d9659bf6SJohannes Doerfert return *AA; 4613d9659bf6SJohannes Doerfert } 4614d9659bf6SJohannes Doerfert 4615ca662297SShilei Tian AAFoldRuntimeCall &AAFoldRuntimeCall::createForPosition(const IRPosition &IRP, 4616ca662297SShilei Tian Attributor &A) { 4617ca662297SShilei Tian AAFoldRuntimeCall *AA = nullptr; 4618ca662297SShilei Tian switch (IRP.getPositionKind()) { 4619ca662297SShilei Tian case IRPosition::IRP_INVALID: 4620ca662297SShilei Tian case IRPosition::IRP_FLOAT: 4621ca662297SShilei Tian case IRPosition::IRP_ARGUMENT: 4622ca662297SShilei Tian case IRPosition::IRP_RETURNED: 4623ca662297SShilei Tian case IRPosition::IRP_FUNCTION: 4624ca662297SShilei Tian case IRPosition::IRP_CALL_SITE: 4625ca662297SShilei Tian case IRPosition::IRP_CALL_SITE_ARGUMENT: 4626ca662297SShilei Tian llvm_unreachable("KernelInfo can only be created for call site position!"); 4627ca662297SShilei Tian case IRPosition::IRP_CALL_SITE_RETURNED: 4628ca662297SShilei Tian AA = new (A.Allocator) AAFoldRuntimeCallCallSiteReturned(IRP, A); 4629ca662297SShilei Tian break; 4630ca662297SShilei Tian } 4631ca662297SShilei Tian 4632ca662297SShilei Tian return *AA; 4633ca662297SShilei Tian } 4634ca662297SShilei Tian 4635b2ad63d3SJoseph Huber PreservedAnalyses OpenMPOptPass::run(Module &M, ModuleAnalysisManager &AM) { 46365ccb7424SJoseph Huber if (!containsOpenMP(M)) 4637b2ad63d3SJoseph Huber return PreservedAnalyses::all(); 4638b2ad63d3SJoseph Huber if (DisableOpenMPOptimizations) 4639b2ad63d3SJoseph Huber return PreservedAnalyses::all(); 4640b2ad63d3SJoseph Huber 46410edb8777SJoseph Huber FunctionAnalysisManager &FAM = 46420edb8777SJoseph Huber AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); 46435ccb7424SJoseph Huber KernelSet Kernels = getDeviceKernels(M); 46445ccb7424SJoseph Huber 464557ad2e10SJoseph Huber auto IsCalled = [&](Function &F) { 464657ad2e10SJoseph Huber if (Kernels.contains(&F)) 464757ad2e10SJoseph Huber return true; 464857ad2e10SJoseph Huber for (const User *U : F.users()) 464957ad2e10SJoseph Huber if (!isa<BlockAddress>(U)) 465057ad2e10SJoseph Huber return true; 465157ad2e10SJoseph Huber return false; 465257ad2e10SJoseph Huber }; 465357ad2e10SJoseph Huber 46540edb8777SJoseph Huber auto EmitRemark = [&](Function &F) { 46550edb8777SJoseph Huber auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F); 46560edb8777SJoseph Huber ORE.emit([&]() { 46572c31d5ebSJoseph Huber OptimizationRemarkAnalysis ORA(DEBUG_TYPE, "OMP140", &F); 4658ecabc668SJoseph Huber return ORA << "Could not internalize function. " 4659adbaa39dSJoseph Huber << "Some optimizations may not be possible. [OMP140]"; 46600edb8777SJoseph Huber }); 46610edb8777SJoseph Huber }; 46620edb8777SJoseph Huber 466357ad2e10SJoseph Huber // Create internal copies of each function if this is a kernel Module. This 466457ad2e10SJoseph Huber // allows iterprocedural passes to see every call edge. 4665adbaa39dSJoseph Huber DenseMap<Function *, Function *> InternalizedMap; 4666adbaa39dSJoseph Huber if (isOpenMPDevice(M)) { 4667adbaa39dSJoseph Huber SmallPtrSet<Function *, 16> InternalizeFns; 466803d7e61cSJoseph Huber for (Function &F : M) 46694a668604SJoseph Huber if (!F.isDeclaration() && !Kernels.contains(&F) && IsCalled(F) && 46704a668604SJoseph Huber !DisableInternalization) { 4671adbaa39dSJoseph Huber if (Attributor::isInternalizable(F)) { 4672adbaa39dSJoseph Huber InternalizeFns.insert(&F); 4673ecabc668SJoseph Huber } else if (!F.hasLocalLinkage() && !F.hasFnAttribute(Attribute::Cold)) { 46740edb8777SJoseph Huber EmitRemark(F); 46750edb8777SJoseph Huber } 46760edb8777SJoseph Huber } 467703d7e61cSJoseph Huber 4678adbaa39dSJoseph Huber Attributor::internalizeFunctions(InternalizeFns, InternalizedMap); 4679adbaa39dSJoseph Huber } 4680adbaa39dSJoseph Huber 468157ad2e10SJoseph Huber // Look at every function in the Module unless it was internalized. 4682b2ad63d3SJoseph Huber SmallVector<Function *, 16> SCC; 468303d7e61cSJoseph Huber for (Function &F : M) 4684adbaa39dSJoseph Huber if (!F.isDeclaration() && !InternalizedMap.lookup(&F)) 468503d7e61cSJoseph Huber SCC.push_back(&F); 4686b2ad63d3SJoseph Huber 4687b2ad63d3SJoseph Huber if (SCC.empty()) 4688b2ad63d3SJoseph Huber return PreservedAnalyses::all(); 4689b2ad63d3SJoseph Huber 4690b2ad63d3SJoseph Huber AnalysisGetter AG(FAM); 4691b2ad63d3SJoseph Huber 4692b2ad63d3SJoseph Huber auto OREGetter = [&FAM](Function *F) -> OptimizationRemarkEmitter & { 4693b2ad63d3SJoseph Huber return FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F); 4694b2ad63d3SJoseph Huber }; 4695b2ad63d3SJoseph Huber 4696b2ad63d3SJoseph Huber BumpPtrAllocator Allocator; 4697b2ad63d3SJoseph Huber CallGraphUpdater CGUpdater; 4698b2ad63d3SJoseph Huber 4699b2ad63d3SJoseph Huber SetVector<Function *> Functions(SCC.begin(), SCC.end()); 47005ccb7424SJoseph Huber OMPInformationCache InfoCache(M, AG, Allocator, /*CGSCC*/ Functions, Kernels); 4701b2ad63d3SJoseph Huber 4702f074a6a0SJoseph Huber unsigned MaxFixpointIterations = 4703f074a6a0SJoseph Huber (isOpenMPDevice(M)) ? SetFixpointIterations : 32; 47044a6bd8e3SJoseph Huber Attributor A(Functions, InfoCache, CGUpdater, nullptr, true, false, 470513b2fba2SJoseph Huber MaxFixpointIterations, OREGetter, DEBUG_TYPE); 4706b2ad63d3SJoseph Huber 4707b2ad63d3SJoseph Huber OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A); 4708b2ad63d3SJoseph Huber bool Changed = OMPOpt.run(true); 4709339aa765SJoseph Huber 471029a74a39SJoseph Huber // Optionally inline device functions for potentially better performance. 471129a74a39SJoseph Huber if (AlwaysInlineDeviceFunctions && isOpenMPDevice(M)) 471229a74a39SJoseph Huber for (Function &F : M) 471329a74a39SJoseph Huber if (!F.isDeclaration() && !Kernels.contains(&F) && 471429a74a39SJoseph Huber !F.hasFnAttribute(Attribute::NoInline)) 471529a74a39SJoseph Huber F.addFnAttr(Attribute::AlwaysInline); 471629a74a39SJoseph Huber 4717339aa765SJoseph Huber if (PrintModuleAfterOptimizations) 4718339aa765SJoseph Huber LLVM_DEBUG(dbgs() << TAG << "Module after OpenMPOpt Module Pass:\n" << M); 4719339aa765SJoseph Huber 4720b2ad63d3SJoseph Huber if (Changed) 4721b2ad63d3SJoseph Huber return PreservedAnalyses::none(); 4722b2ad63d3SJoseph Huber 4723b2ad63d3SJoseph Huber return PreservedAnalyses::all(); 4724b2ad63d3SJoseph Huber } 4725b2ad63d3SJoseph Huber 4726b2ad63d3SJoseph Huber PreservedAnalyses OpenMPOptCGSCCPass::run(LazyCallGraph::SCC &C, 47279548b74aSJohannes Doerfert CGSCCAnalysisManager &AM, 4728b2ad63d3SJoseph Huber LazyCallGraph &CG, 4729b2ad63d3SJoseph Huber CGSCCUpdateResult &UR) { 47305ccb7424SJoseph Huber if (!containsOpenMP(*C.begin()->getFunction().getParent())) 47319548b74aSJohannes Doerfert return PreservedAnalyses::all(); 47329548b74aSJohannes Doerfert if (DisableOpenMPOptimizations) 47339548b74aSJohannes Doerfert return PreservedAnalyses::all(); 47349548b74aSJohannes Doerfert 4735ee17263aSJohannes Doerfert SmallVector<Function *, 16> SCC; 4736351d234dSRoman Lebedev // If there are kernels in the module, we have to run on all SCC's. 4737351d234dSRoman Lebedev for (LazyCallGraph::Node &N : C) { 4738351d234dSRoman Lebedev Function *Fn = &N.getFunction(); 4739351d234dSRoman Lebedev SCC.push_back(Fn); 4740351d234dSRoman Lebedev } 4741351d234dSRoman Lebedev 47425ccb7424SJoseph Huber if (SCC.empty()) 47439548b74aSJohannes Doerfert return PreservedAnalyses::all(); 47449548b74aSJohannes Doerfert 47455ccb7424SJoseph Huber Module &M = *C.begin()->getFunction().getParent(); 47465ccb7424SJoseph Huber 47475ccb7424SJoseph Huber KernelSet Kernels = getDeviceKernels(M); 47485ccb7424SJoseph Huber 47494d4ea9acSHuber, Joseph FunctionAnalysisManager &FAM = 47504d4ea9acSHuber, Joseph AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager(); 47517cfd267cSsstefan1 47527cfd267cSsstefan1 AnalysisGetter AG(FAM); 47537cfd267cSsstefan1 47547cfd267cSsstefan1 auto OREGetter = [&FAM](Function *F) -> OptimizationRemarkEmitter & { 47554d4ea9acSHuber, Joseph return FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F); 47564d4ea9acSHuber, Joseph }; 47574d4ea9acSHuber, Joseph 4758b2ad63d3SJoseph Huber BumpPtrAllocator Allocator; 47599548b74aSJohannes Doerfert CallGraphUpdater CGUpdater; 47609548b74aSJohannes Doerfert CGUpdater.initialize(CG, C, AM, UR); 47617cfd267cSsstefan1 47627cfd267cSsstefan1 SetVector<Function *> Functions(SCC.begin(), SCC.end()); 47637cfd267cSsstefan1 OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, Allocator, 47645ccb7424SJoseph Huber /*CGSCC*/ Functions, Kernels); 47657cfd267cSsstefan1 4766f074a6a0SJoseph Huber unsigned MaxFixpointIterations = 4767f074a6a0SJoseph Huber (isOpenMPDevice(M)) ? SetFixpointIterations : 32; 47684a6bd8e3SJoseph Huber Attributor A(Functions, InfoCache, CGUpdater, nullptr, false, true, 476913b2fba2SJoseph Huber MaxFixpointIterations, OREGetter, DEBUG_TYPE); 4770b8235d2bSsstefan1 4771b8235d2bSsstefan1 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A); 4772b2ad63d3SJoseph Huber bool Changed = OMPOpt.run(false); 4773339aa765SJoseph Huber 4774339aa765SJoseph Huber if (PrintModuleAfterOptimizations) 4775339aa765SJoseph Huber LLVM_DEBUG(dbgs() << TAG << "Module after OpenMPOpt CGSCC Pass:\n" << M); 4776339aa765SJoseph Huber 4777694ded37SGiorgis Georgakoudis if (Changed) 4778694ded37SGiorgis Georgakoudis return PreservedAnalyses::none(); 4779694ded37SGiorgis Georgakoudis 47809548b74aSJohannes Doerfert return PreservedAnalyses::all(); 47819548b74aSJohannes Doerfert } 47828b57ed09SJoseph Huber 47839548b74aSJohannes Doerfert namespace { 47849548b74aSJohannes Doerfert 4785b2ad63d3SJoseph Huber struct OpenMPOptCGSCCLegacyPass : public CallGraphSCCPass { 47869548b74aSJohannes Doerfert CallGraphUpdater CGUpdater; 47879548b74aSJohannes Doerfert static char ID; 47889548b74aSJohannes Doerfert 4789b2ad63d3SJoseph Huber OpenMPOptCGSCCLegacyPass() : CallGraphSCCPass(ID) { 4790b2ad63d3SJoseph Huber initializeOpenMPOptCGSCCLegacyPassPass(*PassRegistry::getPassRegistry()); 47919548b74aSJohannes Doerfert } 47929548b74aSJohannes Doerfert 47939548b74aSJohannes Doerfert void getAnalysisUsage(AnalysisUsage &AU) const override { 47949548b74aSJohannes Doerfert CallGraphSCCPass::getAnalysisUsage(AU); 47959548b74aSJohannes Doerfert } 47969548b74aSJohannes Doerfert 47979548b74aSJohannes Doerfert bool runOnSCC(CallGraphSCC &CGSCC) override { 47985ccb7424SJoseph Huber if (!containsOpenMP(CGSCC.getCallGraph().getModule())) 47999548b74aSJohannes Doerfert return false; 48009548b74aSJohannes Doerfert if (DisableOpenMPOptimizations || skipSCC(CGSCC)) 48019548b74aSJohannes Doerfert return false; 48029548b74aSJohannes Doerfert 4803ee17263aSJohannes Doerfert SmallVector<Function *, 16> SCC; 4804351d234dSRoman Lebedev // If there are kernels in the module, we have to run on all SCC's. 4805351d234dSRoman Lebedev for (CallGraphNode *CGN : CGSCC) { 4806351d234dSRoman Lebedev Function *Fn = CGN->getFunction(); 4807351d234dSRoman Lebedev if (!Fn || Fn->isDeclaration()) 4808351d234dSRoman Lebedev continue; 4809ee17263aSJohannes Doerfert SCC.push_back(Fn); 4810351d234dSRoman Lebedev } 4811351d234dSRoman Lebedev 48125ccb7424SJoseph Huber if (SCC.empty()) 48139548b74aSJohannes Doerfert return false; 48149548b74aSJohannes Doerfert 48155ccb7424SJoseph Huber Module &M = CGSCC.getCallGraph().getModule(); 48165ccb7424SJoseph Huber KernelSet Kernels = getDeviceKernels(M); 48175ccb7424SJoseph Huber 48189548b74aSJohannes Doerfert CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph(); 48199548b74aSJohannes Doerfert CGUpdater.initialize(CG, CGSCC); 48209548b74aSJohannes Doerfert 48214d4ea9acSHuber, Joseph // Maintain a map of functions to avoid rebuilding the ORE 48224d4ea9acSHuber, Joseph DenseMap<Function *, std::unique_ptr<OptimizationRemarkEmitter>> OREMap; 48234d4ea9acSHuber, Joseph auto OREGetter = [&OREMap](Function *F) -> OptimizationRemarkEmitter & { 48244d4ea9acSHuber, Joseph std::unique_ptr<OptimizationRemarkEmitter> &ORE = OREMap[F]; 48254d4ea9acSHuber, Joseph if (!ORE) 48264d4ea9acSHuber, Joseph ORE = std::make_unique<OptimizationRemarkEmitter>(F); 48274d4ea9acSHuber, Joseph return *ORE; 48284d4ea9acSHuber, Joseph }; 48294d4ea9acSHuber, Joseph 48307cfd267cSsstefan1 AnalysisGetter AG; 48317cfd267cSsstefan1 SetVector<Function *> Functions(SCC.begin(), SCC.end()); 48327cfd267cSsstefan1 BumpPtrAllocator Allocator; 48335ccb7424SJoseph Huber OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, 48345ccb7424SJoseph Huber Allocator, 48355ccb7424SJoseph Huber /*CGSCC*/ Functions, Kernels); 48367cfd267cSsstefan1 4837f074a6a0SJoseph Huber unsigned MaxFixpointIterations = 4838f074a6a0SJoseph Huber (isOpenMPDevice(M)) ? SetFixpointIterations : 32; 483930e36c9bSJoseph Huber Attributor A(Functions, InfoCache, CGUpdater, nullptr, false, true, 484013b2fba2SJoseph Huber MaxFixpointIterations, OREGetter, DEBUG_TYPE); 4841b8235d2bSsstefan1 4842b8235d2bSsstefan1 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A); 4843339aa765SJoseph Huber bool Result = OMPOpt.run(false); 4844339aa765SJoseph Huber 4845339aa765SJoseph Huber if (PrintModuleAfterOptimizations) 4846339aa765SJoseph Huber LLVM_DEBUG(dbgs() << TAG << "Module after OpenMPOpt CGSCC Pass:\n" << M); 4847339aa765SJoseph Huber 4848339aa765SJoseph Huber return Result; 48499548b74aSJohannes Doerfert } 48509548b74aSJohannes Doerfert 48519548b74aSJohannes Doerfert bool doFinalization(CallGraph &CG) override { return CGUpdater.finalize(); } 48529548b74aSJohannes Doerfert }; 48539548b74aSJohannes Doerfert 48549548b74aSJohannes Doerfert } // end anonymous namespace 48559548b74aSJohannes Doerfert 48565ccb7424SJoseph Huber KernelSet llvm::omp::getDeviceKernels(Module &M) { 48575ccb7424SJoseph Huber // TODO: Create a more cross-platform way of determining device kernels. 4858e8039ad4SJohannes Doerfert NamedMDNode *MD = M.getOrInsertNamedMetadata("nvvm.annotations"); 48595ccb7424SJoseph Huber KernelSet Kernels; 48605ccb7424SJoseph Huber 4861e8039ad4SJohannes Doerfert if (!MD) 48625ccb7424SJoseph Huber return Kernels; 4863e8039ad4SJohannes Doerfert 4864e8039ad4SJohannes Doerfert for (auto *Op : MD->operands()) { 4865e8039ad4SJohannes Doerfert if (Op->getNumOperands() < 2) 4866e8039ad4SJohannes Doerfert continue; 4867e8039ad4SJohannes Doerfert MDString *KindID = dyn_cast<MDString>(Op->getOperand(1)); 4868e8039ad4SJohannes Doerfert if (!KindID || KindID->getString() != "kernel") 4869e8039ad4SJohannes Doerfert continue; 4870e8039ad4SJohannes Doerfert 4871e8039ad4SJohannes Doerfert Function *KernelFn = 4872e8039ad4SJohannes Doerfert mdconst::dyn_extract_or_null<Function>(Op->getOperand(0)); 4873e8039ad4SJohannes Doerfert if (!KernelFn) 4874e8039ad4SJohannes Doerfert continue; 4875e8039ad4SJohannes Doerfert 4876e8039ad4SJohannes Doerfert ++NumOpenMPTargetRegionKernels; 4877e8039ad4SJohannes Doerfert 4878e8039ad4SJohannes Doerfert Kernels.insert(KernelFn); 4879e8039ad4SJohannes Doerfert } 48805ccb7424SJoseph Huber 48815ccb7424SJoseph Huber return Kernels; 4882e8039ad4SJohannes Doerfert } 4883e8039ad4SJohannes Doerfert 48845ccb7424SJoseph Huber bool llvm::omp::containsOpenMP(Module &M) { 48855ccb7424SJoseph Huber Metadata *MD = M.getModuleFlag("openmp"); 48865ccb7424SJoseph Huber if (!MD) 48875ccb7424SJoseph Huber return false; 4888dce6bc18SJohannes Doerfert 4889e8039ad4SJohannes Doerfert return true; 4890e8039ad4SJohannes Doerfert } 4891e8039ad4SJohannes Doerfert 48925ccb7424SJoseph Huber bool llvm::omp::isOpenMPDevice(Module &M) { 48935ccb7424SJoseph Huber Metadata *MD = M.getModuleFlag("openmp-device"); 48945ccb7424SJoseph Huber if (!MD) 48955ccb7424SJoseph Huber return false; 48965ccb7424SJoseph Huber 48975ccb7424SJoseph Huber return true; 48989548b74aSJohannes Doerfert } 48999548b74aSJohannes Doerfert 4900b2ad63d3SJoseph Huber char OpenMPOptCGSCCLegacyPass::ID = 0; 49019548b74aSJohannes Doerfert 4902b2ad63d3SJoseph Huber INITIALIZE_PASS_BEGIN(OpenMPOptCGSCCLegacyPass, "openmp-opt-cgscc", 49039548b74aSJohannes Doerfert "OpenMP specific optimizations", false, false) 49049548b74aSJohannes Doerfert INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) 4905b2ad63d3SJoseph Huber INITIALIZE_PASS_END(OpenMPOptCGSCCLegacyPass, "openmp-opt-cgscc", 49069548b74aSJohannes Doerfert "OpenMP specific optimizations", false, false) 49079548b74aSJohannes Doerfert 4908b2ad63d3SJoseph Huber Pass *llvm::createOpenMPOptCGSCCLegacyPass() { 4909b2ad63d3SJoseph Huber return new OpenMPOptCGSCCLegacyPass(); 4910b2ad63d3SJoseph Huber } 4911