51#include "llvm/IR/IntrinsicsAMDGPU.h"
52#include "llvm/IR/IntrinsicsNVPTX.h"
69#define DEBUG_TYPE "openmp-opt"
72 "openmp-opt-disable",
cl::desc(
"Disable OpenMP specific optimizations."),
76 "openmp-opt-enable-merging",
82 cl::desc(
"Disable function internalization."),
93 "openmp-hide-memory-transfer-latency",
94 cl::desc(
"[WIP] Tries to hide the latency of host to device memory"
99 "openmp-opt-disable-deglobalization",
100 cl::desc(
"Disable OpenMP optimizations involving deglobalization."),
104 "openmp-opt-disable-spmdization",
105 cl::desc(
"Disable OpenMP optimizations involving SPMD-ization."),
109 "openmp-opt-disable-folding",
114 "openmp-opt-disable-state-machine-rewrite",
115 cl::desc(
"Disable OpenMP optimizations that replace the state machine."),
119 "openmp-opt-disable-barrier-elimination",
120 cl::desc(
"Disable OpenMP optimizations that eliminate barriers."),
124 "openmp-opt-print-module-after",
125 cl::desc(
"Print the current module after OpenMP optimizations."),
129 "openmp-opt-print-module-before",
130 cl::desc(
"Print the current module before OpenMP optimizations."),
134 "openmp-opt-inline-device",
145 cl::desc(
"Maximal number of attributor iterations."),
150 cl::desc(
"Maximum amount of shared memory to use."),
151 cl::init(std::numeric_limits<unsigned>::max()));
154 "openmp-opt-max-callees-for-specialization",
cl::Hidden,
155 cl::desc(
"Number of possible callees above which an indirect call site is "
156 "left alone rather than specialized into an if-cascade."),
160 "Number of OpenMP runtime calls deduplicated");
162 "Number of OpenMP parallel regions deleted");
164 "Number of OpenMP runtime functions identified");
166 "Number of OpenMP runtime function uses identified");
168 "Number of OpenMP target region entry points (=kernels) identified");
170 "Number of non-OpenMP target region kernels identified");
172 "Number of OpenMP target region entry points (=kernels) executed in "
173 "SPMD-mode instead of generic-mode");
174STATISTIC(NumOpenMPTargetRegionKernelsWithoutStateMachine,
175 "Number of OpenMP target region entry points (=kernels) executed in "
176 "generic-mode without a state machines");
177STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback,
178 "Number of OpenMP target region entry points (=kernels) executed in "
179 "generic-mode with customized state machines with fallback");
180STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback,
181 "Number of OpenMP target region entry points (=kernels) executed in "
182 "generic-mode with customized state machines without fallback");
184 NumOpenMPParallelRegionsReplacedInGPUStateMachine,
185 "Number of OpenMP parallel regions replaced with ID in GPU state machines");
187 "Number of OpenMP parallel regions merged");
189 "Amount of memory pushed to shared memory");
190STATISTIC(NumBarriersEliminated,
"Number of redundant barriers eliminated");
218#define KERNEL_ENVIRONMENT_IDX(MEMBER, IDX) \
219 constexpr unsigned MEMBER##Idx = IDX;
224#undef KERNEL_ENVIRONMENT_IDX
226#define KERNEL_ENVIRONMENT_CONFIGURATION_IDX(MEMBER, IDX) \
227 constexpr unsigned MEMBER##Idx = IDX;
237#undef KERNEL_ENVIRONMENT_CONFIGURATION_IDX
239#define KERNEL_ENVIRONMENT_GETTER(MEMBER, RETURNTYPE) \
240 RETURNTYPE *get##MEMBER##FromKernelEnvironment(ConstantStruct *KernelEnvC) { \
241 return cast<RETURNTYPE>(KernelEnvC->getAggregateElement(MEMBER##Idx)); \
247#undef KERNEL_ENVIRONMENT_GETTER
249#define KERNEL_ENVIRONMENT_CONFIGURATION_GETTER(MEMBER) \
250 ConstantInt *get##MEMBER##FromKernelEnvironment( \
251 ConstantStruct *KernelEnvC) { \
252 ConstantStruct *ConfigC = \
253 getConfigurationFromKernelEnvironment(KernelEnvC); \
254 return dyn_cast<ConstantInt>(ConfigC->getAggregateElement(MEMBER##Idx)); \
265#undef KERNEL_ENVIRONMENT_CONFIGURATION_GETTER
269 constexpr int InitKernelEnvironmentArgNo = 0;
284struct AAHeapToShared;
291 OMPInformationCache(
Module &M, AnalysisGetter &AG,
295 OpenMPPostLink(OpenMPPostLink) {
298 const Triple
T(OMPBuilder.M.getTargetTriple());
299 switch (
T.getArch()) {
303 assert(OMPBuilder.Config.IsTargetDevice &&
304 "OpenMP AMDGPU/NVPTX is only prepared to deal with device code.");
305 OMPBuilder.Config.IsGPU =
true;
308 OMPBuilder.Config.IsGPU =
false;
311 OMPBuilder.initialize();
312 initializeRuntimeFunctions(M);
313 initializeInternalControlVars();
317 struct InternalControlVarInfo {
325 StringRef EnvVarName;
331 ConstantInt *InitValue;
344 struct RuntimeFunctionInfo {
365 using UseVector = SmallVector<Use *, 16>;
368 void clearUsesMap() { UsesMap.clear(); }
371 operator bool()
const {
return Declaration; }
374 UseVector &getOrCreateUseVector(
Function *
F) {
375 std::shared_ptr<UseVector> &UV = UsesMap[
F];
377 UV = std::make_shared<UseVector>();
383 const UseVector *getUseVector(
Function &
F)
const {
384 auto I = UsesMap.find(&
F);
385 if (
I != UsesMap.end())
386 return I->second.get();
391 size_t getNumFunctionsWithUses()
const {
return UsesMap.size(); }
395 size_t getNumArgs()
const {
return ArgumentTypes.size(); }
400 void foreachUse(SmallVectorImpl<Function *> &SCC,
401 function_ref<
bool(Use &,
Function &)> CB) {
409 SmallVector<unsigned, 8> ToBeDeleted;
413 UseVector &UV = getOrCreateUseVector(
F);
423 while (!ToBeDeleted.
empty()) {
433 DenseMap<Function *, std::shared_ptr<UseVector>> UsesMap;
437 decltype(UsesMap)::iterator
begin() {
return UsesMap.begin(); }
438 decltype(UsesMap)::iterator
end() {
return UsesMap.end(); }
442 OpenMPIRBuilder OMPBuilder;
446 RuntimeFunction::OMPRTL___last>
450 DenseMap<Function *, RuntimeFunction> RuntimeFunctionIDMap;
454 InternalControlVar::ICV___last>
459 void initializeInternalControlVars() {
460#define ICV_RT_SET(_Name, RTL) \
462 auto &ICV = ICVs[_Name]; \
465#define ICV_RT_GET(Name, RTL) \
467 auto &ICV = ICVs[Name]; \
470#define ICV_DATA_ENV(Enum, _Name, _EnvVarName, Init) \
472 auto &ICV = ICVs[Enum]; \
475 ICV.InitKind = Init; \
476 ICV.EnvVarName = _EnvVarName; \
477 switch (ICV.InitKind) { \
478 case ICV_IMPLEMENTATION_DEFINED: \
479 ICV.InitValue = nullptr; \
482 ICV.InitValue = ConstantInt::get( \
483 Type::getInt32Ty(OMPBuilder.Int32->getContext()), 0); \
486 ICV.InitValue = ConstantInt::getFalse(OMPBuilder.Int1->getContext()); \
492#include "llvm/Frontend/OpenMP/OMPKinds.def"
498 static bool declMatchesRTFTypes(
Function *
F,
Type *RTFRetType,
505 if (
F->getReturnType() != RTFRetType)
507 if (
F->arg_size() != RTFArgTypes.
size())
510 auto *RTFTyIt = RTFArgTypes.
begin();
511 for (Argument &Arg :
F->args()) {
512 if (Arg.getType() != *RTFTyIt)
522 unsigned collectUses(RuntimeFunctionInfo &RFI,
bool CollectStats =
true) {
523 unsigned NumUses = 0;
524 if (!RFI.Declaration)
526 OMPBuilder.addAttributes(RFI.Kind, *RFI.Declaration);
529 NumOpenMPRuntimeFunctionsIdentified += 1;
530 NumOpenMPRuntimeFunctionUsesIdentified += RFI.Declaration->getNumUses();
534 for (Use &U : RFI.Declaration->uses()) {
536 if (!
CGSCC ||
CGSCC->empty() ||
CGSCC->contains(UserI->getFunction())) {
537 RFI.getOrCreateUseVector(UserI->getFunction()).push_back(&U);
541 RFI.getOrCreateUseVector(
nullptr).push_back(&U);
550 auto &RFI = RFIs[RTF];
552 collectUses(RFI,
false);
560 void setCallbackMetadata(
Function *
F,
unsigned ArgNo, ArrayRef<int> Indices,
562 if (!
F ||
F->hasMetadata(LLVMContext::MD_callback))
565 LLVMContext &Ctx =
F->getContext();
567 F->addMetadata(LLVMContext::MD_callback,
568 *
MDNode::get(Ctx, {MDB.createCallbackEncoding(ArgNo, Indices,
576 static Function *getAnalyzableCallback(
const CallBase &CB) {
580 MDNode *CallbackMD =
Callee->getMetadata(LLVMContext::MD_callback);
586 if (!Encoding || Encoding->getNumOperands() == 0)
597 if (!Callback ||
Callback->isDeclaration())
603 void recollectUses() {
604 for (
int Idx = 0; Idx < RFIs.size(); ++Idx)
609 void setCallingConvention(FunctionCallee Callee, CallInst *CI) {
624 RuntimeFunctionInfo &RFI = RFIs[Fn];
626 if (!RFI.Declaration || RFI.Declaration->isDeclaration())
634 void initializeRuntimeFunctions(
Module &M) {
637#define OMP_TYPE(VarName, ...) \
638 Type *VarName = OMPBuilder.VarName; \
641#define OMP_ARRAY_TYPE(VarName, ...) \
642 ArrayType *VarName##Ty = OMPBuilder.VarName##Ty; \
644 PointerType *VarName##PtrTy = OMPBuilder.VarName##PtrTy; \
645 (void)VarName##PtrTy;
647#define OMP_FUNCTION_TYPE(VarName, ...) \
648 FunctionType *VarName = OMPBuilder.VarName; \
650 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \
653#define OMP_STRUCT_TYPE(VarName, ...) \
654 StructType *VarName = OMPBuilder.VarName; \
656 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \
659#define OMP_RTL(_Enum, _Name, _IsVarArg, _ReturnType, ...) \
661 SmallVector<Type *, 8> ArgsTypes({__VA_ARGS__}); \
662 Function *F = M.getFunction(_Name); \
663 RTLFunctions.insert(F); \
664 if (declMatchesRTFTypes(F, OMPBuilder._ReturnType, ArgsTypes)) { \
665 RuntimeFunctionIDMap[F] = _Enum; \
666 auto &RFI = RFIs[_Enum]; \
669 RFI.IsVarArg = _IsVarArg; \
670 RFI.ReturnType = OMPBuilder._ReturnType; \
671 RFI.ArgumentTypes = std::move(ArgsTypes); \
672 RFI.Declaration = F; \
673 unsigned NumUses = collectUses(RFI); \
676 dbgs() << TAG << RFI.Name << (RFI.Declaration ? "" : " not") \
678 if (RFI.Declaration) \
679 dbgs() << TAG << "-> got " << NumUses << " uses in " \
680 << RFI.getNumFunctionsWithUses() \
681 << " different functions.\n"; \
686#define OMP_RTL_CB_INFO(_Enum, _Name, _ArgNo, _ArgIndices, _IsVarArg) \
687 setCallbackMetadata(M.getFunction(_Name), _ArgNo, _ArgIndices, _IsVarArg);
689#include "llvm/Frontend/OpenMP/OMPKinds.def"
695 for (StringRef Prefix : {
"__kmpc",
"_ZN4ompx",
"omp_"})
696 if (
F.hasFnAttribute(Attribute::NoInline) &&
697 F.getName().starts_with(Prefix) &&
698 !
F.hasFnAttribute(Attribute::OptimizeNone))
699 F.removeFnAttr(Attribute::NoInline);
707 DenseSet<const Function *> RTLFunctions;
710 bool OpenMPPostLink =
false;
717 SmallPtrSet<Function *, 8> SPMDizedKernels;
720template <
typename Ty,
bool InsertInval
idates = true>
722 bool contains(
const Ty &Elem)
const {
return Set.contains(Elem); }
723 bool insert(
const Ty &Elem) {
724 if (InsertInvalidates)
725 BooleanState::indicatePessimisticFixpoint();
726 return Set.insert(Elem);
729 const Ty &operator[](
int Idx)
const {
return Set[Idx]; }
730 bool operator==(
const BooleanStateWithSetVector &
RHS)
const {
731 return BooleanState::operator==(
RHS) && Set ==
RHS.Set;
733 bool operator!=(
const BooleanStateWithSetVector &
RHS)
const {
734 return !(*
this ==
RHS);
737 bool empty()
const {
return Set.empty(); }
738 size_t size()
const {
return Set.size(); }
741 BooleanStateWithSetVector &
operator^=(
const BooleanStateWithSetVector &
RHS) {
742 BooleanState::operator^=(
RHS);
743 Set.insert_range(
RHS.Set);
752 typename decltype(Set)::iterator
begin() {
return Set.begin(); }
753 typename decltype(Set)::iterator
end() {
return Set.end(); }
754 typename decltype(Set)::const_iterator
begin()
const {
return Set.begin(); }
755 typename decltype(Set)::const_iterator
end()
const {
return Set.end(); }
758template <
typename Ty,
bool InsertInval
idates = true>
759using BooleanStateWithPtrSetVector =
760 BooleanStateWithSetVector<Ty *, InsertInvalidates>;
764 bool IsAtFixpoint =
false;
768 BooleanStateWithPtrSetVector<CallBase,
false>
769 ReachedKnownParallelRegions;
772 BooleanStateWithPtrSetVector<CallBase> ReachedUnknownParallelRegions;
777 BooleanStateWithPtrSetVector<Instruction, false> SPMDCompatibilityTracker;
781 CallBase *KernelInitCB =
nullptr;
785 ConstantStruct *KernelEnvC =
nullptr;
789 CallBase *KernelDeinitCB =
nullptr;
792 bool IsKernelEntry =
false;
795 BooleanStateWithPtrSetVector<Function, false> ReachingKernelEntries;
800 BooleanStateWithSetVector<uint8_t> ParallelLevels;
803 bool NestedParallelism =
false;
808 KernelInfoState() =
default;
809 KernelInfoState(
bool BestState) {
811 indicatePessimisticFixpoint();
815 bool isValidState()
const override {
return true; }
818 bool isAtFixpoint()
const override {
return IsAtFixpoint; }
823 ParallelLevels.indicatePessimisticFixpoint();
824 ReachingKernelEntries.indicatePessimisticFixpoint();
825 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
826 ReachedKnownParallelRegions.indicatePessimisticFixpoint();
827 ReachedUnknownParallelRegions.indicatePessimisticFixpoint();
828 NestedParallelism =
true;
829 return ChangeStatus::CHANGED;
835 ParallelLevels.indicateOptimisticFixpoint();
836 ReachingKernelEntries.indicateOptimisticFixpoint();
837 SPMDCompatibilityTracker.indicateOptimisticFixpoint();
838 ReachedKnownParallelRegions.indicateOptimisticFixpoint();
839 ReachedUnknownParallelRegions.indicateOptimisticFixpoint();
840 return ChangeStatus::UNCHANGED;
844 KernelInfoState &getAssumed() {
return *
this; }
845 const KernelInfoState &getAssumed()
const {
return *
this; }
848 if (SPMDCompatibilityTracker !=
RHS.SPMDCompatibilityTracker)
850 if (ReachedKnownParallelRegions !=
RHS.ReachedKnownParallelRegions)
852 if (ReachedUnknownParallelRegions !=
RHS.ReachedUnknownParallelRegions)
854 if (ReachingKernelEntries !=
RHS.ReachingKernelEntries)
856 if (ParallelLevels !=
RHS.ParallelLevels)
858 if (NestedParallelism !=
RHS.NestedParallelism)
864 bool mayContainParallelRegion() {
865 return !ReachedKnownParallelRegions.empty() ||
866 !ReachedUnknownParallelRegions.empty();
870 static KernelInfoState getBestState() {
return KernelInfoState(
true); }
872 static KernelInfoState getBestState(KernelInfoState &KIS) {
873 return getBestState();
877 static KernelInfoState getWorstState() {
return KernelInfoState(
false); }
880 KernelInfoState
operator^=(
const KernelInfoState &KIS) {
882 if (KIS.KernelInitCB) {
883 if (KernelInitCB && KernelInitCB != KIS.KernelInitCB)
886 KernelInitCB = KIS.KernelInitCB;
888 if (KIS.KernelDeinitCB) {
889 if (KernelDeinitCB && KernelDeinitCB != KIS.KernelDeinitCB)
892 KernelDeinitCB = KIS.KernelDeinitCB;
894 if (KIS.KernelEnvC) {
895 if (KernelEnvC && KernelEnvC != KIS.KernelEnvC)
898 KernelEnvC = KIS.KernelEnvC;
900 SPMDCompatibilityTracker ^= KIS.SPMDCompatibilityTracker;
901 ReachedKnownParallelRegions ^= KIS.ReachedKnownParallelRegions;
902 ReachedUnknownParallelRegions ^= KIS.ReachedUnknownParallelRegions;
903 NestedParallelism |= KIS.NestedParallelism;
907 KernelInfoState
operator&=(
const KernelInfoState &KIS) {
908 return (*
this ^= KIS);
918 AllocaInst *Array =
nullptr;
920 SmallVector<Value *, 8> StoredValues;
922 SmallVector<StoreInst *, 8> LastAccesses;
924 OffloadArray() =
default;
930 bool initialize(AllocaInst &Array, Instruction &Before) {
931 if (!getValues(Array, Before))
934 this->Array = &Array;
938 static const unsigned DeviceIDArgNum = 1;
939 static const unsigned BasePtrsArgNum = 3;
940 static const unsigned PtrsArgNum = 4;
941 static const unsigned SizesArgNum = 5;
947 bool getValues(AllocaInst &Array, Instruction &Before) {
949 const DataLayout &
DL = Array.getDataLayout();
950 std::optional<TypeSize> ArraySize = Array.getAllocationSize(
DL);
951 if (!ArraySize || !ArraySize->isFixed())
955 StoredValues.assign(NumValues,
nullptr);
956 LastAccesses.assign(NumValues,
nullptr);
964 for (Instruction &
I : *BB) {
980 LastAccesses[Idx] = S;
991 const unsigned NumValues = StoredValues.size();
992 for (
unsigned I = 0;
I < NumValues; ++
I) {
993 if (!StoredValues[
I] || !LastAccesses[
I])
1003 using OptimizationRemarkGetter =
1004 function_ref<OptimizationRemarkEmitter &(
Function *)>;
1006 OpenMPOpt(SmallVectorImpl<Function *> &SCC, CallGraphUpdater &CGUpdater,
1007 OptimizationRemarkGetter OREGetter,
1008 OMPInformationCache &OMPInfoCache, Attributor &A)
1009 : M(*(*SCC.
begin())->
getParent()), SCC(SCC), CGUpdater(CGUpdater),
1010 OREGetter(OREGetter), OMPInfoCache(OMPInfoCache), A(A) {}
1013 bool remarksEnabled() {
1014 auto &Ctx = M.getContext();
1019 bool run(
bool IsModulePass) {
1029 Changed |= runAttributor(IsModulePass);
1032 OMPInfoCache.recollectUses();
1035 Changed |= rewriteDeviceCodeStateMachine();
1041 Changed |= removeSPMDParallelWrappers();
1043 if (remarksEnabled())
1044 analysisGlobalization();
1051 Changed |= runAttributor(IsModulePass);
1054 OMPInfoCache.recollectUses();
1056 Changed |= deleteParallelRegions();
1059 Changed |= hideMemTransfersLatency();
1060 Changed |= deduplicateRuntimeCalls();
1062 if (mergeParallelRegions()) {
1063 deduplicateRuntimeCalls();
1069 if (OMPInfoCache.OpenMPPostLink)
1070 Changed |= removeRuntimeSymbols();
1077 void printICVs()
const {
1082 for (
auto ICV : ICVs) {
1083 auto ICVInfo = OMPInfoCache.ICVs[ICV];
1084 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
1085 return ORA <<
"OpenMP ICV " <<
ore::NV(
"OpenMPICV", ICVInfo.Name)
1087 << (ICVInfo.InitValue
1088 ?
toString(ICVInfo.InitValue->getValue(), 10,
true)
1089 :
"IMPLEMENTATION_DEFINED");
1098 void printKernels()
const {
1103 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
1104 return ORA <<
"OpenMP GPU kernel "
1105 <<
ore::NV(
"OpenMPGPUKernel",
F->getName()) <<
"\n";
1114 static CallInst *getCallIfRegularCall(
1115 Use &U, OMPInformationCache::RuntimeFunctionInfo *RFI =
nullptr) {
1126 static CallInst *getCallIfRegularCall(
1127 Value &V, OMPInformationCache::RuntimeFunctionInfo *RFI =
nullptr) {
1138 bool mergeParallelRegions() {
1139 const unsigned CallbackCalleeOperand = 2;
1140 const unsigned CallbackFirstArgOperand = 3;
1144 OMPInformationCache::RuntimeFunctionInfo &RFI =
1145 OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call];
1147 if (!RFI.Declaration)
1151 OMPInformationCache::RuntimeFunctionInfo UnmergableCallsInfo[] = {
1152 OMPInfoCache.RFIs[OMPRTL___kmpc_push_proc_bind],
1153 OMPInfoCache.RFIs[OMPRTL___kmpc_push_num_threads],
1157 LoopInfo *LI =
nullptr;
1158 DominatorTree *DT =
nullptr;
1160 SmallDenseMap<BasicBlock *, SmallPtrSet<Instruction *, 4>> BB2PRMap;
1162 BasicBlock *StartBB =
nullptr, *EndBB =
nullptr;
1163 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
1165 BasicBlock *CGStartBB = CodeGenIP.getNodeParent();
1167 assert(StartBB !=
nullptr &&
"StartBB should not be null");
1169 assert(EndBB !=
nullptr &&
"EndBB should not be null");
1170 EndBB->getTerminator()->setSuccessor(0, CGEndBB);
1174 auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
Value &,
1175 Value &Inner,
Value *&ReplacementValue) -> InsertPointTy {
1176 ReplacementValue = &Inner;
1180 auto FiniCB = [&](InsertPointTy CodeGenIP) {
return Error::success(); };
1184 auto CreateSequentialRegion = [&](
Function *OuterFn,
1190 BasicBlock *ParentBB = SeqStartI->getParent();
1192 SplitBlock(ParentBB, SeqEndI->getNextNode(), DT, LI);
1196 SplitBlock(ParentBB, SeqStartI, DT, LI,
nullptr,
"seq.par.merged");
1199 "Expected a different CFG");
1203 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
1205 BasicBlock *CGStartBB = CodeGenIP.getNodeParent();
1207 assert(SeqStartBB !=
nullptr &&
"SeqStartBB should not be null");
1209 assert(SeqEndBB !=
nullptr &&
"SeqEndBB should not be null");
1213 auto FiniCB = [&](InsertPointTy CodeGenIP) {
return Error::success(); };
1217 for (Instruction &
I : *SeqStartBB) {
1218 SmallPtrSet<Instruction *, 4> OutsideUsers;
1219 for (User *Usr :
I.users()) {
1227 OutsideUsers.
insert(&UsrI);
1230 if (OutsideUsers.
empty())
1235 const DataLayout &
DL = M.getDataLayout();
1236 AllocaInst *AllocaI =
new AllocaInst(
1237 I.getType(),
DL.getAllocaAddrSpace(),
nullptr,
1242 new StoreInst(&
I, AllocaI, SeqStartBB->getTerminator()->getIterator());
1246 for (Instruction *UsrI : OutsideUsers) {
1247 LoadInst *LoadI =
new LoadInst(
I.getType(), AllocaI,
1248 I.getName() +
".seq.output.load",
1254 OpenMPIRBuilder::LocationDescription Loc(ParentBB->
end(),
DL);
1256 OMPInfoCache.OMPBuilder.createMaster(Loc, BodyGenCB, FiniCB));
1257 cantFail(OMPInfoCache.OMPBuilder.createBarrier({SeqAfterIP, DL},
1273 auto Merge = [&](
const SmallVectorImpl<CallInst *> &MergableCIs,
1277 assert(MergableCIs.
size() > 1 &&
"Assumed multiple mergable CIs");
1279 auto Remark = [&](OptimizationRemark
OR) {
1280 OR <<
"Parallel region merged with parallel region"
1281 << (MergableCIs.
size() > 2 ?
"s" :
"") <<
" at ";
1284 if (CI != MergableCIs.
back())
1292 Function *OriginalFn = BB->getParent();
1294 <<
" parallel regions in " << OriginalFn->
getName()
1298 EndBB =
SplitBlock(BB, MergableCIs.
back()->getNextNode(), DT, LI);
1300 SplitBlock(EndBB, &*EndBB->getFirstInsertionPt(), DT, LI);
1304 assert(BB->getUniqueSuccessor() == StartBB &&
"Expected a different CFG");
1305 const DebugLoc DL = BB->getTerminator()->getDebugLoc();
1310 for (
auto *It = MergableCIs.
begin(), *End = MergableCIs.
end() - 1;
1319 CreateSequentialRegion(OriginalFn, BB, ForkCI->
getNextNode(),
1323 OpenMPIRBuilder::LocationDescription Loc(BB->end(),
DL);
1324 IRBuilder<>::InsertPoint AllocaIP(
1329 cantFail(OMPInfoCache.OMPBuilder.createParallel(
1330 Loc, AllocaIP, {}, BodyGenCB, PrivCB, FiniCB,
1331 nullptr,
nullptr, OMP_PROC_BIND_default,
1336 OMPInfoCache.OMPBuilder.finalize(OriginalFn);
1342 SmallVector<Value *, 8>
Args;
1343 for (
auto *CI : MergableCIs) {
1345 FunctionType *FT = OMPInfoCache.OMPBuilder.ParallelTask;
1349 for (
unsigned U = CallbackFirstArgOperand,
E = CI->
arg_size(); U <
E;
1359 for (
unsigned U = CallbackFirstArgOperand,
E = CI->
arg_size(); U <
E;
1363 U - (CallbackFirstArgOperand - CallbackCalleeOperand), A);
1366 if (CI != MergableCIs.back()) {
1369 cantFail(OMPInfoCache.OMPBuilder.createBarrier(
1370 {NewCI->getNextNode()->getIterator(), NewCI->getDebugLoc()},
1377 assert(OutlinedFn != OriginalFn &&
"Outlining failed");
1378 CGUpdater.registerOutlinedFunction(*OriginalFn, *OutlinedFn);
1379 CGUpdater.reanalyzeFunction(*OriginalFn);
1381 NumOpenMPParallelRegionsMerged += MergableCIs.size();
1389 CallInst *CI = getCallIfRegularCall(U, &RFI);
1396 RFI.foreachUse(SCC, DetectPRsCB);
1402 for (
auto &It : BB2PRMap) {
1403 auto &CIs = It.getSecond();
1418 auto IsMergable = [&](
Instruction &
I,
bool IsBeforeMergableRegion) {
1421 if (
I.isTerminator())
1428 if (IsBeforeMergableRegion) {
1430 if (!CalledFunction)
1437 for (
const auto &RFI : UnmergableCallsInfo) {
1438 if (CalledFunction == RFI.Declaration)
1453 for (
auto It = BB->
begin(), End = BB->
end(); It != End;) {
1457 if (CIs.count(&
I)) {
1463 if (IsMergable(
I, MergableCIs.
empty()))
1468 for (; It != End; ++It) {
1470 if (CIs.count(&SkipI)) {
1472 <<
" due to " <<
I <<
"\n");
1479 if (MergableCIs.
size() > 1) {
1480 MergableCIsVector.
push_back(MergableCIs);
1482 <<
" parallel regions in block " << BB->
getName()
1487 MergableCIs.
clear();
1490 if (!MergableCIsVector.
empty()) {
1493 for (
auto &MergableCIs : MergableCIsVector)
1494 Merge(MergableCIs, BB);
1495 MergableCIsVector.clear();
1502 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_fork_call);
1503 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_barrier);
1504 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_master);
1505 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_end_master);
1512 bool deleteParallelRegions() {
1513 const unsigned CallbackCalleeOperand = 2;
1515 OMPInformationCache::RuntimeFunctionInfo &RFI =
1516 OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call];
1518 if (!RFI.Declaration)
1523 CallInst *CI = getCallIfRegularCall(U);
1530 if (!Fn->onlyReadsMemory())
1532 if (!Fn->hasFnAttribute(Attribute::WillReturn))
1538 auto Remark = [&](OptimizationRemark
OR) {
1539 return OR <<
"Removing parallel region with no side-effects.";
1545 ++NumOpenMPParallelRegionsDeleted;
1549 RFI.foreachUse(SCC, DeleteCallCB);
1555 bool deduplicateRuntimeCalls() {
1559 OMPRTL_omp_get_num_threads,
1560 OMPRTL_omp_in_parallel,
1561 OMPRTL_omp_get_cancellation,
1562 OMPRTL_omp_get_supported_active_levels,
1563 OMPRTL_omp_get_level,
1564 OMPRTL_omp_get_ancestor_thread_num,
1565 OMPRTL_omp_get_team_size,
1566 OMPRTL_omp_get_active_level,
1567 OMPRTL_omp_in_final,
1568 OMPRTL_omp_get_proc_bind,
1569 OMPRTL_omp_get_num_places,
1570 OMPRTL_omp_get_num_procs,
1571 OMPRTL_omp_get_place_num,
1572 OMPRTL_omp_get_partition_num_places,
1573 OMPRTL_omp_get_partition_place_nums};
1576 SmallSetVector<Value *, 16> GTIdArgs;
1577 collectGlobalThreadIdArguments(GTIdArgs);
1579 <<
" global thread ID arguments\n");
1582 for (
auto DeduplicableRuntimeCallID : DeduplicableRuntimeCallIDs)
1583 Changed |= deduplicateRuntimeCalls(
1584 *
F, OMPInfoCache.RFIs[DeduplicableRuntimeCallID]);
1588 Value *GTIdArg =
nullptr;
1589 for (Argument &Arg :
F->args())
1590 if (GTIdArgs.
count(&Arg)) {
1594 Changed |= deduplicateRuntimeCalls(
1595 *
F, OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num], GTIdArg);
1602 bool removeRuntimeSymbols() {
1607 if (GlobalVariable *GV = M.getNamedGlobal(
"__llvm_rpc_client")) {
1608 if (GV->hasNUsesOrMore(1))
1612 GV->eraseFromParent();
1624 bool hideMemTransfersLatency() {
1625 auto &RFI = OMPInfoCache.RFIs[OMPRTL___tgt_target_data_begin_mapper];
1628 auto *RTCall = getCallIfRegularCall(U, &RFI);
1632 OffloadArray OffloadArrays[3];
1633 if (!getValuesInOffloadArrays(*RTCall, OffloadArrays))
1636 LLVM_DEBUG(dumpValuesInOffloadArrays(OffloadArrays));
1639 bool WasSplit =
false;
1640 Instruction *WaitMovementPoint = canBeMovedDownwards(*RTCall);
1641 if (WaitMovementPoint)
1642 WasSplit = splitTargetDataBeginRTC(*RTCall, *WaitMovementPoint);
1647 if (OMPInfoCache.runtimeFnsAvailable(
1648 {OMPRTL___tgt_target_data_begin_mapper_issue,
1649 OMPRTL___tgt_target_data_begin_mapper_wait}))
1650 RFI.foreachUse(SCC, SplitMemTransfers);
1655 void analysisGlobalization() {
1656 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
1658 auto CheckGlobalization = [&](
Use &
U,
Function &Decl) {
1659 if (CallInst *CI = getCallIfRegularCall(U, &RFI)) {
1660 auto Remark = [&](OptimizationRemarkMissed ORM) {
1662 <<
"Found thread data sharing on the GPU. "
1663 <<
"Expect degraded performance due to data globalization.";
1671 RFI.foreachUse(SCC, CheckGlobalization);
1676 bool getValuesInOffloadArrays(CallInst &RuntimeCall,
1678 assert(OAs.
size() == 3 &&
"Need space for three offload arrays!");
1688 Value *BasePtrsArg =
1700 if (!OAs[0].
initialize(*BasePtrsArray, RuntimeCall))
1708 if (!OAs[1].
initialize(*PtrsArray, RuntimeCall))
1720 if (!OAs[2].
initialize(*SizesArray, RuntimeCall))
1731 assert(OAs.
size() == 3 &&
"There are three offload arrays to debug!");
1734 std::string ValuesStr;
1735 raw_string_ostream
Printer(ValuesStr);
1736 std::string Separator =
" --- ";
1738 for (
auto *BP : OAs[0].StoredValues) {
1742 LLVM_DEBUG(
dbgs() <<
"\t\toffload_baseptrs: " << ValuesStr <<
"\n");
1745 for (
auto *
P : OAs[1].StoredValues) {
1752 for (
auto *S : OAs[2].StoredValues) {
1756 LLVM_DEBUG(
dbgs() <<
"\t\toffload_sizes: " << ValuesStr <<
"\n");
1761 Instruction *canBeMovedDownwards(CallInst &RuntimeCall) {
1766 bool IsWorthIt =
false;
1785 return RuntimeCall.
getParent()->getTerminator();
1789 bool splitTargetDataBeginRTC(CallInst &RuntimeCall,
1790 Instruction &WaitMovementPoint) {
1794 auto &
IRBuilder = OMPInfoCache.OMPBuilder;
1797 IRBuilder.Builder.SetInsertPoint(&Entry,
1798 Entry.getFirstNonPHIOrDbgOrAlloca());
1800 IRBuilder.AsyncInfo,
nullptr,
"handle");
1807 FunctionCallee IssueDecl =
IRBuilder.getOrCreateRuntimeFunction(
1808 M, OMPRTL___tgt_target_data_begin_mapper_issue);
1811 SmallVector<Value *, 16>
Args;
1812 for (
auto &Arg : RuntimeCall.
args())
1813 Args.push_back(Arg.get());
1814 Args.push_back(Handle);
1818 OMPInfoCache.setCallingConvention(IssueDecl, IssueCallsite);
1823 FunctionCallee WaitDecl =
IRBuilder.getOrCreateRuntimeFunction(
1824 M, OMPRTL___tgt_target_data_begin_mapper_wait);
1826 Value *WaitParams[2] = {
1828 OffloadArray::DeviceIDArgNum),
1832 WaitDecl, WaitParams,
"", WaitMovementPoint.
getIterator());
1833 OMPInfoCache.setCallingConvention(WaitDecl, WaitCallsite);
1838 static Value *combinedIdentStruct(
Value *CurrentIdent,
Value *NextIdent,
1839 bool GlobalOnly,
bool &SingleChoice) {
1840 if (CurrentIdent == NextIdent)
1841 return CurrentIdent;
1846 SingleChoice = !CurrentIdent;
1858 getCombinedIdentFromCallUsesIn(OMPInformationCache::RuntimeFunctionInfo &RFI,
1860 bool SingleChoice =
true;
1861 Value *Ident =
nullptr;
1863 CallInst *CI = getCallIfRegularCall(U, &RFI);
1864 if (!CI || &
F != &Caller)
1867 true, SingleChoice);
1870 RFI.foreachUse(SCC, CombineIdentStruct);
1872 if (!Ident || !SingleChoice) {
1876 if (!OMPInfoCache.OMPBuilder.getInsertionPoint().isValid())
1877 OMPInfoCache.OMPBuilder.updateToLocation(
1878 {
F.getEntryBlock().begin(),
DebugLoc()});
1881 uint32_t SrcLocStrSize;
1883 OMPInfoCache.OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1884 Ident = OMPInfoCache.OMPBuilder.getOrCreateIdent(Loc, SrcLocStrSize);
1891 bool deduplicateRuntimeCalls(
Function &
F,
1892 OMPInformationCache::RuntimeFunctionInfo &RFI,
1893 Value *ReplVal =
nullptr) {
1894 auto *UV = RFI.getUseVector(
F);
1895 if (!UV || UV->size() + (ReplVal !=
nullptr) < 2)
1899 dbgs() <<
TAG <<
"Deduplicate " << UV->size() <<
" uses of " << RFI.Name
1900 << (ReplVal ?
" with an existing value\n" :
"\n") <<
"\n");
1904 "Unexpected replacement value!");
1907 auto CanBeMoved = [
this](CallBase &CB) {
1913 for (
unsigned U = 1;
U < NumArgs; ++
U)
1921 OMPInfoCache.getAnalysisResultForFunction<DominatorTreeAnalysis>(
F);
1925 for (Use *U : *UV) {
1926 if (CallInst *CI = getCallIfRegularCall(*U, &RFI)) {
1931 if (!CanBeMoved(*CI))
1939 assert(IP &&
"Expected insertion point!");
1949 Value *Ident = getCombinedIdentFromCallUsesIn(RFI,
F,
1957 CallInst *CI = getCallIfRegularCall(U, &RFI);
1958 if (!CI || CI == ReplVal || &
F != &Caller)
1962 auto Remark = [&](OptimizationRemark
OR) {
1963 return OR <<
"OpenMP runtime call "
1964 <<
ore::NV(
"OpenMPOptRuntime", RFI.Name) <<
" deduplicated.";
1973 ++NumOpenMPRuntimeCallsDeduplicated;
1977 RFI.foreachUse(SCC, ReplaceAndDeleteCB);
1983 void collectGlobalThreadIdArguments(SmallSetVector<Value *, 16> >IdArgs) {
1990 auto CallArgOpIsGTId = [&](
Function &
F,
unsigned ArgNo, CallInst &RefCI) {
1991 if (!
F.hasLocalLinkage())
1993 for (Use &U :
F.uses()) {
1994 if (CallInst *CI = getCallIfRegularCall(U)) {
1996 if (CI == &RefCI || GTIdArgs.
count(ArgOp) ||
1997 getCallIfRegularCall(
1998 *ArgOp, &OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num]))
2007 auto AddUserArgs = [&](
Value >Id) {
2008 for (Use &U : GTId.uses())
2012 if (CallArgOpIsGTId(*Callee,
U.getOperandNo(), *CI))
2017 OMPInformationCache::RuntimeFunctionInfo &GlobThreadNumRFI =
2018 OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num];
2020 GlobThreadNumRFI.foreachUse(SCC, [&](Use &U,
Function &
F) {
2021 if (CallInst *CI = getCallIfRegularCall(U, &GlobThreadNumRFI))
2029 for (
unsigned U = 0;
U < GTIdArgs.
size(); ++
U)
2030 AddUserArgs(*GTIdArgs[U]);
2038 DenseMap<Function *, std::optional<Kernel>> UniqueKernelMap;
2044 Kernel getUniqueKernelFor(Instruction &
I) {
2045 return getUniqueKernelFor(*
I.getFunction());
2050 bool rewriteDeviceCodeStateMachine();
2055 bool removeSPMDParallelWrappers();
2071 template <
typename RemarkKind,
typename RemarkCallBack>
2072 void emitRemark(Instruction *
I, StringRef RemarkName,
2073 RemarkCallBack &&RemarkCB)
const {
2075 auto &ORE = OREGetter(
F);
2079 return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
I))
2080 <<
" [" << RemarkName <<
"]";
2084 [&]() {
return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
I)); });
2088 template <
typename RemarkKind,
typename RemarkCallBack>
2090 RemarkCallBack &&RemarkCB)
const {
2091 auto &ORE = OREGetter(
F);
2095 return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
F))
2096 <<
" [" << RemarkName <<
"]";
2100 [&]() {
return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
F)); });
2107 SmallVectorImpl<Function *> &SCC;
2111 CallGraphUpdater &CGUpdater;
2114 OptimizationRemarkGetter OREGetter;
2117 OMPInformationCache &OMPInfoCache;
2123 bool runAttributor(
bool IsModulePass) {
2127 registerAAs(IsModulePass);
2132 <<
" functions, result: " <<
Changed <<
".\n");
2134 if (
Changed == ChangeStatus::CHANGED)
2135 OMPInfoCache.invalidateAnalyses();
2137 return Changed == ChangeStatus::CHANGED;
2144 void registerAAs(
bool IsModulePass);
2149 static void registerAAsForFunction(Attributor &A,
const Function &
F);
2153 if (OMPInfoCache.CGSCC && !OMPInfoCache.CGSCC->empty() &&
2154 !OMPInfoCache.CGSCC->contains(&
F))
2159 std::optional<Kernel> &CachedKernel = UniqueKernelMap[&
F];
2161 return *CachedKernel;
2168 return *CachedKernel;
2171 CachedKernel =
nullptr;
2172 if (!
F.hasLocalLinkage()) {
2175 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
2176 return ORA <<
"Potentially unknown OpenMP target region caller.";
2184 auto GetUniqueKernelForUse = [&](
const Use &
U) ->
Kernel {
2187 if (
Cmp->isEquality())
2188 return getUniqueKernelFor(*Cmp);
2194 return getUniqueKernelFor(*CB);
2196 OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI =
2197 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
2199 if (OpenMPOpt::getCallIfRegularCall(*
U.getUser(), &KernelParallelRFI))
2200 return getUniqueKernelFor(*CB);
2208 SmallPtrSet<Kernel, 2> PotentialKernels;
2209 OMPInformationCache::foreachUse(
F, [&](
const Use &U) {
2210 PotentialKernels.
insert(GetUniqueKernelForUse(U));
2214 if (PotentialKernels.
size() == 1)
2215 K = *PotentialKernels.
begin();
2218 UniqueKernelMap[&
F] =
K;
2223bool OpenMPOpt::rewriteDeviceCodeStateMachine() {
2224 OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI =
2225 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
2228 if (!KernelParallelRFI)
2239 bool UnknownUse =
false;
2240 bool KernelParallelUse =
false;
2241 unsigned NumDirectCalls = 0;
2244 OMPInformationCache::foreachUse(*
F, [&](Use &U) {
2252 ToBeReplacedStateMachineUses.
push_back(&U);
2258 OpenMPOpt::getCallIfRegularCall(*
U.getUser(), &KernelParallelRFI);
2259 const unsigned int WrapperFunctionArgNo = 6;
2260 if (!KernelParallelUse && CI &&
2262 KernelParallelUse =
true;
2263 ToBeReplacedStateMachineUses.
push_back(&U);
2271 if (!KernelParallelUse)
2277 if (UnknownUse || NumDirectCalls != 1 ||
2278 ToBeReplacedStateMachineUses.
size() > 2) {
2279 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
2280 return ORA <<
"Parallel region is used in "
2281 << (UnknownUse ?
"unknown" :
"unexpected")
2282 <<
" ways. Will not attempt to rewrite the state machine.";
2292 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
2293 return ORA <<
"Parallel region is not called from a unique kernel. "
2294 "Will not attempt to rewrite the state machine.";
2306 Type *Int8Ty = Type::getInt8Ty(
M.getContext());
2308 auto *
ID =
new GlobalVariable(
2312 for (Use *U : ToBeReplacedStateMachineUses)
2314 ID,
U->get()->getType()));
2316 ++NumOpenMPParallelRegionsReplacedInGPUStateMachine;
2324bool OpenMPOpt::removeSPMDParallelWrappers() {
2326 if (OMPInfoCache.SPMDizedKernels.empty())
2329 OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI =
2330 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
2331 if (!KernelParallelRFI || !KernelParallelRFI.Declaration)
2334 constexpr unsigned WrapperFunctionArgNo = 6;
2336 for (User *U : KernelParallelRFI.Declaration->
users()) {
2339 CI->
arg_size() <= WrapperFunctionArgNo)
2353 if (!K || !OMPInfoCache.SPMDizedKernels.contains(K))
2357 WrapperFunctionArgNo,
2366struct AAICVTracker :
public StateWrapper<BooleanState, AbstractAttribute> {
2367 using Base = StateWrapper<BooleanState, AbstractAttribute>;
2368 AAICVTracker(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
2371 bool isAssumedTracked()
const {
return getAssumed(); }
2374 bool isKnownTracked()
const {
return getAssumed(); }
2377 static AAICVTracker &createForPosition(
const IRPosition &IRP, Attributor &
A);
2381 const Instruction *
I,
2382 Attributor &
A)
const {
2383 return std::nullopt;
2389 virtual std::optional<Value *>
2397 StringRef
getName()
const override {
return "AAICVTracker"; }
2400 const char *getIdAddr()
const override {
return &ID; }
2403 static bool classof(
const AbstractAttribute *AA) {
2407 static const char ID;
2410struct AAICVTrackerFunction :
public AAICVTracker {
2411 AAICVTrackerFunction(
const IRPosition &IRP, Attributor &
A)
2412 : AAICVTracker(IRP,
A) {}
2415 const std::string getAsStr(Attributor *)
const override {
2416 return "ICVTrackerFunction";
2420 void trackStatistics()
const override {}
2424 return ChangeStatus::UNCHANGED;
2429 InternalControlVar::ICV___last>
2430 ICVReplacementValuesMap;
2437 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
2440 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter];
2442 auto &ValuesMap = ICVReplacementValuesMap[ICV];
2444 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U);
2450 if (ValuesMap.insert(std::make_pair(CI, CI->
getArgOperand(0))).second)
2451 HasChanged = ChangeStatus::CHANGED;
2457 std::optional<Value *> ReplVal = getValueForCall(
A,
I, ICV);
2458 if (ReplVal && ValuesMap.insert(std::make_pair(&
I, *ReplVal)).second)
2459 HasChanged = ChangeStatus::CHANGED;
2465 SetterRFI.foreachUse(TrackValues,
F);
2467 bool UsedAssumedInformation =
false;
2468 A.checkForAllInstructions(CallCheck, *
this, {Instruction::Call},
2469 UsedAssumedInformation,
2475 if (HasChanged == ChangeStatus::CHANGED)
2476 ValuesMap.try_emplace(Entry);
2484 std::optional<Value *> getValueForCall(Attributor &
A,
const Instruction &
I,
2488 if (!CB || CB->
hasFnAttr(
"no_openmp") ||
2491 return std::nullopt;
2493 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
2494 auto &GetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Getter];
2495 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter];
2499 if (CalledFunction ==
nullptr)
2501 if (CalledFunction == GetterRFI.Declaration)
2502 return std::nullopt;
2503 if (CalledFunction == SetterRFI.Declaration) {
2504 if (ICVReplacementValuesMap[ICV].
count(&
I))
2505 return ICVReplacementValuesMap[ICV].lookup(&
I);
2514 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2517 if (ICVTrackingAA->isAssumedTracked()) {
2518 std::optional<Value *> URV =
2519 ICVTrackingAA->getUniqueReplacementValue(ICV);
2530 std::optional<Value *>
2532 return std::nullopt;
2537 const Instruction *
I,
2538 Attributor &
A)
const override {
2539 const auto &ValuesMap = ICVReplacementValuesMap[ICV];
2540 if (ValuesMap.count(
I))
2541 return ValuesMap.lookup(
I);
2544 SmallPtrSet<const Instruction *, 16> Visited;
2547 std::optional<Value *> ReplVal;
2549 while (!Worklist.
empty()) {
2551 if (!Visited.
insert(CurrInst).second)
2559 if (ValuesMap.count(CurrInst)) {
2560 std::optional<Value *> NewReplVal = ValuesMap.lookup(CurrInst);
2563 ReplVal = NewReplVal;
2569 if (ReplVal != NewReplVal)
2575 std::optional<Value *> NewReplVal = getValueForCall(
A, *CurrInst, ICV);
2581 ReplVal = NewReplVal;
2587 if (ReplVal != NewReplVal)
2592 if (CurrBB ==
I->getParent() && ReplVal)
2597 if (
const Instruction *Terminator = Pred->getTerminator())
2605struct AAICVTrackerFunctionReturned : AAICVTracker {
2606 AAICVTrackerFunctionReturned(
const IRPosition &IRP, Attributor &
A)
2607 : AAICVTracker(IRP,
A) {}
2610 const std::string getAsStr(Attributor *)
const override {
2611 return "ICVTrackerFunctionReturned";
2615 void trackStatistics()
const override {}
2619 return ChangeStatus::UNCHANGED;
2624 InternalControlVar::ICV___last>
2625 ICVReplacementValuesMap;
2628 std::optional<Value *>
2630 return ICVReplacementValuesMap[ICV];
2635 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2638 if (!ICVTrackingAA->isAssumedTracked())
2639 return indicatePessimisticFixpoint();
2642 std::optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV];
2643 std::optional<Value *> UniqueICVValue;
2646 std::optional<Value *> NewReplVal =
2647 ICVTrackingAA->getReplacementValue(ICV, &
I,
A);
2650 if (UniqueICVValue && UniqueICVValue != NewReplVal)
2653 UniqueICVValue = NewReplVal;
2658 bool UsedAssumedInformation =
false;
2659 if (!
A.checkForAllInstructions(CheckReturnInst, *
this, {Instruction::Ret},
2660 UsedAssumedInformation,
2662 UniqueICVValue =
nullptr;
2664 if (UniqueICVValue == ReplVal)
2667 ReplVal = UniqueICVValue;
2668 Changed = ChangeStatus::CHANGED;
2675struct AAICVTrackerCallSite : AAICVTracker {
2676 AAICVTrackerCallSite(
const IRPosition &IRP, Attributor &
A)
2677 : AAICVTracker(IRP,
A) {}
2680 assert(getAnchorScope() &&
"Expected anchor function");
2684 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
2686 auto ICVInfo = OMPInfoCache.ICVs[ICV];
2687 auto &Getter = OMPInfoCache.RFIs[ICVInfo.Getter];
2688 if (Getter.Declaration == getAssociatedFunction()) {
2689 AssociatedICV = ICVInfo.Kind;
2695 indicatePessimisticFixpoint();
2699 if (!ReplVal || !*ReplVal)
2700 return ChangeStatus::UNCHANGED;
2703 A.deleteAfterManifest(*getCtxI());
2705 return ChangeStatus::CHANGED;
2709 const std::string getAsStr(Attributor *)
const override {
2710 return "ICVTrackerCallSite";
2714 void trackStatistics()
const override {}
2717 std::optional<Value *> ReplVal;
2720 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2724 if (!ICVTrackingAA->isAssumedTracked())
2725 return indicatePessimisticFixpoint();
2727 std::optional<Value *> NewReplVal =
2728 ICVTrackingAA->getReplacementValue(AssociatedICV, getCtxI(),
A);
2730 if (ReplVal == NewReplVal)
2731 return ChangeStatus::UNCHANGED;
2733 ReplVal = NewReplVal;
2734 return ChangeStatus::CHANGED;
2739 std::optional<Value *>
2745struct AAICVTrackerCallSiteReturned : AAICVTracker {
2746 AAICVTrackerCallSiteReturned(
const IRPosition &IRP, Attributor &
A)
2747 : AAICVTracker(IRP,
A) {}
2750 const std::string getAsStr(Attributor *)
const override {
2751 return "ICVTrackerCallSiteReturned";
2755 void trackStatistics()
const override {}
2759 return ChangeStatus::UNCHANGED;
2764 InternalControlVar::ICV___last>
2765 ICVReplacementValuesMap;
2769 std::optional<Value *>
2771 return ICVReplacementValuesMap[ICV];
2776 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2778 DepClassTy::REQUIRED);
2781 if (!ICVTrackingAA->isAssumedTracked())
2782 return indicatePessimisticFixpoint();
2785 std::optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV];
2786 std::optional<Value *> NewReplVal =
2787 ICVTrackingAA->getUniqueReplacementValue(ICV);
2789 if (ReplVal == NewReplVal)
2792 ReplVal = NewReplVal;
2793 Changed = ChangeStatus::CHANGED;
2801static bool hasFunctionEndAsUniqueSuccessor(
const BasicBlock *BB) {
2807 return hasFunctionEndAsUniqueSuccessor(
Successor);
2810struct AAExecutionDomainFunction :
public AAExecutionDomain {
2811 AAExecutionDomainFunction(
const IRPosition &IRP, Attributor &
A)
2812 : AAExecutionDomain(IRP,
A) {}
2814 ~AAExecutionDomainFunction()
override {
delete RPOT; }
2818 assert(
F &&
"Expected anchor function");
2819 RPOT =
new ReversePostOrderTraversal<Function *>(
F);
2822 const std::string getAsStr(Attributor *)
const override {
2823 unsigned TotalBlocks = 0, InitialThreadBlocks = 0, AlignedBlocks = 0;
2824 for (
auto &It : BEDMap) {
2828 InitialThreadBlocks += It.getSecond().IsExecutedByInitialThreadOnly;
2829 AlignedBlocks += It.getSecond().IsReachedFromAlignedBarrierOnly &&
2830 It.getSecond().IsReachingAlignedBarrierOnly;
2832 return "[AAExecutionDomain] " + std::to_string(InitialThreadBlocks) +
"/" +
2833 std::to_string(AlignedBlocks) +
" of " +
2834 std::to_string(TotalBlocks) +
2835 " executed by initial thread / aligned";
2839 void trackStatistics()
const override {}
2843 for (
const BasicBlock &BB : *getAnchorScope()) {
2844 if (!isExecutedByInitialThreadOnly(BB))
2846 dbgs() <<
TAG <<
" Basic block @" << getAnchorScope()->getName() <<
" "
2847 << BB.
getName() <<
" is executed by a single thread.\n";
2856 SmallPtrSet<CallBase *, 16> DeletedBarriers;
2857 auto HandleAlignedBarrier = [&](CallBase *CB) {
2858 const ExecutionDomainTy &ED = CB ? CEDMap[{CB, PRE}] : BEDMap[
nullptr];
2859 if (!ED.IsReachedFromAlignedBarrierOnly ||
2860 ED.EncounteredNonLocalSideEffect)
2862 if (!ED.EncounteredAssumes.empty() && !
A.isModulePass())
2873 DeletedBarriers.
insert(CB);
2874 A.deleteAfterManifest(*CB);
2875 ++NumBarriersEliminated;
2876 Changed = ChangeStatus::CHANGED;
2877 }
else if (!ED.AlignedBarriers.empty()) {
2878 Changed = ChangeStatus::CHANGED;
2880 ED.AlignedBarriers.end());
2881 SmallSetVector<CallBase *, 16> Visited;
2882 while (!Worklist.
empty()) {
2884 if (!Visited.
insert(LastCB))
2888 if (!hasFunctionEndAsUniqueSuccessor(LastCB->
getParent()))
2890 if (!DeletedBarriers.
count(LastCB)) {
2891 ++NumBarriersEliminated;
2892 A.deleteAfterManifest(*LastCB);
2898 const ExecutionDomainTy &LastED = CEDMap[{LastCB, PRE}];
2899 Worklist.
append(LastED.AlignedBarriers.begin(),
2900 LastED.AlignedBarriers.end());
2906 if (!ED.EncounteredAssumes.empty() && (CB || !ED.AlignedBarriers.empty()))
2907 for (
auto *AssumeCB : ED.EncounteredAssumes)
2908 A.deleteAfterManifest(*AssumeCB);
2911 for (
auto *CB : AlignedBarriers)
2912 HandleAlignedBarrier(CB);
2916 HandleAlignedBarrier(
nullptr);
2921 bool isNoOpFence(
const FenceInst &FI)
const override {
2922 return getState().isValidState() && !NonNoOpFences.count(&FI);
2928 mergeInPredecessorBarriersAndAssumptions(Attributor &
A, ExecutionDomainTy &ED,
2929 const ExecutionDomainTy &PredED);
2934 bool mergeInPredecessor(Attributor &
A, ExecutionDomainTy &ED,
2935 const ExecutionDomainTy &PredED,
2936 bool InitialEdgeOnly =
false);
2939 bool handleCallees(Attributor &
A, ExecutionDomainTy &EntryBBED);
2946 bool isExecutedByInitialThreadOnly(
const BasicBlock &BB)
const override {
2947 if (!isValidState())
2949 assert(BB.
getParent() == getAnchorScope() &&
"Block is out of scope!");
2950 return BEDMap.lookup(&BB).IsExecutedByInitialThreadOnly;
2953 bool isExecutedInAlignedRegion(Attributor &
A,
2954 const Instruction &
I)
const override {
2955 assert(
I.getFunction() == getAnchorScope() &&
2956 "Instruction is out of scope!");
2957 if (!isValidState())
2960 bool ForwardIsOk =
true;
2969 if (CB != &
I && AlignedBarriers.contains(
const_cast<CallBase *
>(CB)))
2971 const auto &It = CEDMap.find({CB, PRE});
2972 if (It == CEDMap.end())
2974 if (!It->getSecond().IsReachingAlignedBarrierOnly)
2975 ForwardIsOk =
false;
2979 if (!CurI && !BEDMap.lookup(
I.getParent()).IsReachingAlignedBarrierOnly)
2980 ForwardIsOk =
false;
2988 if (CB != &
I && AlignedBarriers.contains(
const_cast<CallBase *
>(CB)))
2990 const auto &It = CEDMap.find({CB, POST});
2991 if (It == CEDMap.end())
2993 if (It->getSecond().IsReachedFromAlignedBarrierOnly)
3006 return BEDMap.lookup(
nullptr).IsReachedFromAlignedBarrierOnly;
3008 return BEDMap.lookup(PredBB).IsReachedFromAlignedBarrierOnly;
3018 ExecutionDomainTy getExecutionDomain(
const BasicBlock &BB)
const override {
3020 "No request should be made against an invalid state!");
3021 return BEDMap.lookup(&BB);
3023 std::pair<ExecutionDomainTy, ExecutionDomainTy>
3024 getExecutionDomain(
const CallBase &CB)
const override {
3026 "No request should be made against an invalid state!");
3027 return {CEDMap.lookup({&CB, PRE}), CEDMap.lookup({&CB, POST})};
3029 ExecutionDomainTy getFunctionExecutionDomain()
const override {
3031 "No request should be made against an invalid state!");
3032 return InterProceduralED;
3038 static bool isInitialThreadOnlyEdge(Attributor &
A, CondBrInst *
Edge,
3039 BasicBlock &SuccessorBB) {
3042 if (
Edge->getSuccessor(0) != &SuccessorBB)
3046 if (!Cmp || !
Cmp->isTrueWhenEqual() || !
Cmp->isEquality())
3054 if (
C->isAllOnesValue()) {
3056 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3057 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_target_init];
3058 CB = CB ? OpenMPOpt::getCallIfRegularCall(*CB, &RFI) : nullptr;
3061 ConstantStruct *KernelEnvC =
3063 ConstantInt *ExecModeC =
3064 KernelInfo::getExecModeFromKernelEnvironment(KernelEnvC);
3071 if (
II->getIntrinsicID() == Intrinsic::nvvm_read_ptx_sreg_tid_x)
3076 if (
II->getIntrinsicID() == Intrinsic::amdgcn_workitem_id_x)
3084 ExecutionDomainTy InterProceduralED;
3088 DenseMap<const BasicBlock *, ExecutionDomainTy> BEDMap;
3089 DenseMap<PointerIntPair<const CallBase *, 1, Direction>, ExecutionDomainTy>
3091 SmallSetVector<CallBase *, 16> AlignedBarriers;
3093 ReversePostOrderTraversal<Function *> *RPOT =
nullptr;
3096 static bool setAndRecord(
bool &R,
bool V) {
3104 SmallPtrSet<const FenceInst *, 8> NonNoOpFences;
3107void AAExecutionDomainFunction::mergeInPredecessorBarriersAndAssumptions(
3108 Attributor &
A, ExecutionDomainTy &ED,
const ExecutionDomainTy &PredED) {
3109 for (
auto *EA : PredED.EncounteredAssumes)
3110 ED.addAssumeInst(
A, *EA);
3112 for (
auto *AB : PredED.AlignedBarriers)
3113 ED.addAlignedBarrier(
A, *AB);
3116bool AAExecutionDomainFunction::mergeInPredecessor(
3117 Attributor &
A, ExecutionDomainTy &ED,
const ExecutionDomainTy &PredED,
3118 bool InitialEdgeOnly) {
3122 setAndRecord(ED.IsExecutedByInitialThreadOnly,
3123 InitialEdgeOnly || (PredED.IsExecutedByInitialThreadOnly &&
3124 ED.IsExecutedByInitialThreadOnly));
3126 Changed |= setAndRecord(ED.IsReachedFromAlignedBarrierOnly,
3127 ED.IsReachedFromAlignedBarrierOnly &&
3128 PredED.IsReachedFromAlignedBarrierOnly);
3129 Changed |= setAndRecord(ED.EncounteredNonLocalSideEffect,
3130 ED.EncounteredNonLocalSideEffect |
3131 PredED.EncounteredNonLocalSideEffect);
3133 if (ED.IsReachedFromAlignedBarrierOnly)
3134 mergeInPredecessorBarriersAndAssumptions(
A, ED, PredED);
3136 ED.clearAssumeInstAndAlignedBarriers();
3140bool AAExecutionDomainFunction::handleCallees(Attributor &
A,
3141 ExecutionDomainTy &EntryBBED) {
3143 auto PredForCallSite = [&](AbstractCallSite ACS) {
3144 const auto *EDAA =
A.getAAFor<AAExecutionDomain>(
3146 DepClassTy::OPTIONAL);
3147 if (!EDAA || !EDAA->getState().isValidState())
3150 EDAA->getExecutionDomain(*
cast<CallBase>(ACS.getInstruction())));
3154 ExecutionDomainTy ExitED;
3155 bool AllCallSitesKnown;
3156 if (
A.checkForAllCallSites(PredForCallSite, *
this,
3158 AllCallSitesKnown)) {
3159 for (
const auto &[CSInED, CSOutED] : CallSiteEDs) {
3160 mergeInPredecessor(
A, EntryBBED, CSInED);
3161 ExitED.IsReachingAlignedBarrierOnly &=
3162 CSOutED.IsReachingAlignedBarrierOnly;
3169 EntryBBED.IsExecutedByInitialThreadOnly =
false;
3170 EntryBBED.IsReachedFromAlignedBarrierOnly =
true;
3171 EntryBBED.EncounteredNonLocalSideEffect =
false;
3172 ExitED.IsReachingAlignedBarrierOnly =
false;
3174 EntryBBED.IsExecutedByInitialThreadOnly =
false;
3175 EntryBBED.IsReachedFromAlignedBarrierOnly =
false;
3176 EntryBBED.EncounteredNonLocalSideEffect =
true;
3177 ExitED.IsReachingAlignedBarrierOnly =
false;
3182 auto &FnED = BEDMap[
nullptr];
3183 Changed |= setAndRecord(FnED.IsReachedFromAlignedBarrierOnly,
3184 FnED.IsReachedFromAlignedBarrierOnly &
3185 EntryBBED.IsReachedFromAlignedBarrierOnly);
3186 Changed |= setAndRecord(FnED.IsReachingAlignedBarrierOnly,
3187 FnED.IsReachingAlignedBarrierOnly &
3188 ExitED.IsReachingAlignedBarrierOnly);
3189 Changed |= setAndRecord(FnED.IsExecutedByInitialThreadOnly,
3190 EntryBBED.IsExecutedByInitialThreadOnly);
3194ChangeStatus AAExecutionDomainFunction::updateImpl(Attributor &
A) {
3201 auto HandleAlignedBarrier = [&](CallBase &CB, ExecutionDomainTy &ED) {
3202 Changed |= AlignedBarriers.insert(&CB);
3204 auto &CallInED = CEDMap[{&CB, PRE}];
3205 Changed |= mergeInPredecessor(
A, CallInED, ED);
3206 CallInED.IsReachingAlignedBarrierOnly =
true;
3208 ED.EncounteredNonLocalSideEffect =
false;
3209 ED.IsReachedFromAlignedBarrierOnly =
true;
3211 ED.clearAssumeInstAndAlignedBarriers();
3212 ED.addAlignedBarrier(
A, CB);
3213 auto &CallOutED = CEDMap[{&CB, POST}];
3214 Changed |= mergeInPredecessor(
A, CallOutED, ED);
3218 A.getAAFor<AAIsDead>(*
this, getIRPosition(), DepClassTy::OPTIONAL);
3224 SmallVector<Instruction *> SyncInstWorklist;
3225 for (
auto &RIt : *RPOT) {
3228 bool IsEntryBB = &BB == &EntryBB;
3231 bool AlignedBarrierLastInBlock = IsEntryBB && IsKernel;
3232 bool IsExplicitlyAligned = IsEntryBB && IsKernel;
3233 ExecutionDomainTy ED;
3240 if (LivenessAA && LivenessAA->isAssumedDead(&BB))
3244 if (LivenessAA && LivenessAA->isEdgeDead(PredBB, &BB))
3246 bool InitialEdgeOnly = isInitialThreadOnlyEdge(
3248 mergeInPredecessor(
A, ED, BEDMap[PredBB], InitialEdgeOnly);
3254 for (Instruction &
I : BB) {
3255 bool UsedAssumedInformation;
3256 if (
A.isAssumedDead(
I, *
this, LivenessAA, UsedAssumedInformation,
3257 false, DepClassTy::OPTIONAL,
3265 ED.addAssumeInst(
A, *AI);
3269 if (
II->isAssumeLikeIntrinsic())
3274 if (!ED.EncounteredNonLocalSideEffect) {
3276 if (ED.IsReachedFromAlignedBarrierOnly)
3281 case AtomicOrdering::NotAtomic:
3283 case AtomicOrdering::Unordered:
3285 case AtomicOrdering::Monotonic:
3287 case AtomicOrdering::Acquire:
3289 case AtomicOrdering::Release:
3291 case AtomicOrdering::AcquireRelease:
3293 case AtomicOrdering::SequentiallyConsistent:
3297 NonNoOpFences.insert(FI);
3302 bool IsAlignedBarrier =
3306 AlignedBarrierLastInBlock &= IsNoSync;
3307 IsExplicitlyAligned &= IsNoSync;
3313 if (IsAlignedBarrier) {
3314 HandleAlignedBarrier(*CB, ED);
3315 AlignedBarrierLastInBlock =
true;
3316 IsExplicitlyAligned =
true;
3322 if (!ED.EncounteredNonLocalSideEffect &&
3324 ED.EncounteredNonLocalSideEffect =
true;
3326 ED.IsReachedFromAlignedBarrierOnly =
false;
3334 auto &CallInED = CEDMap[{CB, PRE}];
3335 Changed |= mergeInPredecessor(
A, CallInED, ED);
3341 if (!IsNoSync && Callee && !
Callee->isDeclaration()) {
3342 const auto *EDAA =
A.getAAFor<AAExecutionDomain>(
3344 if (EDAA && EDAA->getState().isValidState()) {
3345 const auto &CalleeED = EDAA->getFunctionExecutionDomain();
3346 ED.IsReachedFromAlignedBarrierOnly =
3347 CalleeED.IsReachedFromAlignedBarrierOnly;
3348 AlignedBarrierLastInBlock = ED.IsReachedFromAlignedBarrierOnly;
3349 if (IsNoSync || !CalleeED.IsReachedFromAlignedBarrierOnly)
3350 ED.EncounteredNonLocalSideEffect |=
3351 CalleeED.EncounteredNonLocalSideEffect;
3353 ED.EncounteredNonLocalSideEffect =
3354 CalleeED.EncounteredNonLocalSideEffect;
3355 if (!CalleeED.IsReachingAlignedBarrierOnly) {
3357 setAndRecord(CallInED.IsReachingAlignedBarrierOnly,
false);
3360 if (CalleeED.IsReachedFromAlignedBarrierOnly)
3361 mergeInPredecessorBarriersAndAssumptions(
A, ED, CalleeED);
3362 auto &CallOutED = CEDMap[{CB, POST}];
3363 Changed |= mergeInPredecessor(
A, CallOutED, ED);
3368 ED.IsReachedFromAlignedBarrierOnly =
false;
3369 Changed |= setAndRecord(CallInED.IsReachingAlignedBarrierOnly,
false);
3372 AlignedBarrierLastInBlock &= ED.IsReachedFromAlignedBarrierOnly;
3374 auto &CallOutED = CEDMap[{CB, POST}];
3375 Changed |= mergeInPredecessor(
A, CallOutED, ED);
3378 if (!
I.mayHaveSideEffects() && !
I.mayReadFromMemory())
3384 const auto *MemAA =
A.getAAFor<AAMemoryLocation>(
3392 if (MemAA && MemAA->getState().isValidState() &&
3393 MemAA->checkForAllAccessesToMemoryKind(
3398 auto &InfoCache =
A.getInfoCache();
3399 if (!
I.mayHaveSideEffects() && InfoCache.isOnlyUsedByAssume(
I))
3403 if (LI->hasMetadata(LLVMContext::MD_invariant_load))
3406 if (!ED.EncounteredNonLocalSideEffect &&
3408 ED.EncounteredNonLocalSideEffect =
true;
3411 bool IsEndAndNotReachingAlignedBarriersOnly =
false;
3413 !BB.getTerminator()->getNumSuccessors()) {
3415 Changed |= mergeInPredecessor(
A, InterProceduralED, ED);
3417 auto &FnED = BEDMap[
nullptr];
3418 if (IsKernel && !IsExplicitlyAligned)
3419 FnED.IsReachingAlignedBarrierOnly =
false;
3420 Changed |= mergeInPredecessor(
A, FnED, ED);
3422 if (!FnED.IsReachingAlignedBarrierOnly) {
3423 IsEndAndNotReachingAlignedBarriersOnly =
true;
3424 SyncInstWorklist.
push_back(BB.getTerminator());
3425 auto &BBED = BEDMap[&BB];
3426 Changed |= setAndRecord(BBED.IsReachingAlignedBarrierOnly,
false);
3430 ExecutionDomainTy &StoredED = BEDMap[&BB];
3431 ED.IsReachingAlignedBarrierOnly = StoredED.IsReachingAlignedBarrierOnly &&
3432 !IsEndAndNotReachingAlignedBarriersOnly;
3438 if (ED.IsExecutedByInitialThreadOnly !=
3439 StoredED.IsExecutedByInitialThreadOnly ||
3440 ED.IsReachedFromAlignedBarrierOnly !=
3441 StoredED.IsReachedFromAlignedBarrierOnly ||
3442 ED.EncounteredNonLocalSideEffect !=
3443 StoredED.EncounteredNonLocalSideEffect)
3447 StoredED = std::move(ED);
3452 SmallSetVector<BasicBlock *, 16> Visited;
3453 while (!SyncInstWorklist.
empty()) {
3456 bool HitAlignedBarrierOrKnownEnd =
false;
3461 auto &CallOutED = CEDMap[{CB, POST}];
3462 Changed |= setAndRecord(CallOutED.IsReachingAlignedBarrierOnly,
false);
3463 auto &CallInED = CEDMap[{CB, PRE}];
3464 HitAlignedBarrierOrKnownEnd =
3465 AlignedBarriers.count(CB) || !CallInED.IsReachingAlignedBarrierOnly;
3466 if (HitAlignedBarrierOrKnownEnd)
3468 Changed |= setAndRecord(CallInED.IsReachingAlignedBarrierOnly,
false);
3470 if (HitAlignedBarrierOrKnownEnd)
3474 if (LivenessAA && LivenessAA->isEdgeDead(PredBB, SyncBB))
3476 if (!Visited.
insert(PredBB))
3478 auto &PredED = BEDMap[PredBB];
3479 if (setAndRecord(PredED.IsReachingAlignedBarrierOnly,
false)) {
3481 SyncInstWorklist.
push_back(PredBB->getTerminator());
3484 if (SyncBB != &EntryBB)
3487 setAndRecord(InterProceduralED.IsReachingAlignedBarrierOnly,
false);
3490 return Changed ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;
3495struct AAHeapToShared :
public StateWrapper<BooleanState, AbstractAttribute> {
3496 using Base = StateWrapper<BooleanState, AbstractAttribute>;
3497 AAHeapToShared(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
3500 static AAHeapToShared &createForPosition(
const IRPosition &IRP,
3504 virtual bool isAssumedHeapToShared(CallBase &CB)
const = 0;
3508 virtual bool isAssumedHeapToSharedRemovedFree(CallBase &CB)
const = 0;
3511 StringRef
getName()
const override {
return "AAHeapToShared"; }
3514 const char *getIdAddr()
const override {
return &ID; }
3518 static bool classof(
const AbstractAttribute *AA) {
3523 static const char ID;
3526struct AAHeapToSharedFunction :
public AAHeapToShared {
3527 AAHeapToSharedFunction(
const IRPosition &IRP, Attributor &
A)
3528 : AAHeapToShared(IRP,
A) {}
3530 const std::string getAsStr(Attributor *)
const override {
3531 return "[AAHeapToShared] " + std::to_string(MallocCalls.size()) +
3532 " malloc calls eligible.";
3536 void trackStatistics()
const override {}
3540 void findPotentialRemovedFreeCalls(Attributor &
A) {
3541 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3542 auto &FreeRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_free_shared];
3544 PotentialRemovedFreeCalls.clear();
3546 for (CallBase *CB : MallocCalls) {
3548 for (
auto *U : CB->
users()) {
3550 if (
C &&
C->getCalledFunction() == FreeRFI.Declaration)
3554 if (FreeCalls.
size() != 1)
3557 PotentialRemovedFreeCalls.insert(FreeCalls.
front());
3563 indicatePessimisticFixpoint();
3567 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3568 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
3569 if (!RFI.Declaration)
3573 [](
const IRPosition &,
const AbstractAttribute *,
3574 bool &) -> std::optional<Value *> {
return nullptr; };
3577 const OMPInformationCache::RuntimeFunctionInfo::UseVector *
Uses =
3578 RFI.getUseVector(*
F);
3582 for (Use *U : *
Uses)
3584 MallocCalls.insert(CB);
3589 findPotentialRemovedFreeCalls(
A);
3592 bool isAssumedHeapToShared(CallBase &CB)
const override {
3593 return isValidState() && MallocCalls.count(&CB);
3596 bool isAssumedHeapToSharedRemovedFree(CallBase &CB)
const override {
3597 return isValidState() && PotentialRemovedFreeCalls.count(&CB);
3601 if (MallocCalls.empty())
3602 return ChangeStatus::UNCHANGED;
3604 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3605 auto &FreeCall = OMPInfoCache.RFIs[OMPRTL___kmpc_free_shared];
3609 DepClassTy::OPTIONAL);
3612 for (CallBase *CB : MallocCalls) {
3614 if (HS &&
HS->isAssumedHeapToStack(*CB))
3619 for (
auto *U : CB->
users()) {
3621 if (
C &&
C->getCalledFunction() == FreeCall.Declaration)
3624 if (FreeCalls.
size() != 1)
3631 <<
" with shared memory."
3632 <<
" Shared memory usage is limited to "
3638 <<
" with " << AllocSize->getZExtValue()
3639 <<
" bytes of shared memory\n");
3644 Type *Int8Ty = Type::getInt8Ty(
M->getContext());
3645 Type *Int8ArrTy = ArrayType::get(Int8Ty, AllocSize->getZExtValue());
3646 auto *SharedMem =
new GlobalVariable(
3650 static_cast<unsigned>(AddressSpace::Shared));
3652 SharedMem, PointerType::getUnqual(
M->getContext()));
3654 auto Remark = [&](OptimizationRemark
OR) {
3655 return OR <<
"Replaced globalized variable with "
3656 <<
ore::NV(
"SharedMemory", AllocSize->getZExtValue())
3657 << (AllocSize->isOne() ?
" byte " :
" bytes ")
3658 <<
"of shared memory.";
3660 A.emitRemark<OptimizationRemark>(CB,
"OMP111",
Remark);
3662 MaybeAlign
Alignment = CB->getRetAlign();
3664 "HeapToShared on allocation without alignment attribute");
3668 A.deleteAfterManifest(*CB);
3669 A.deleteAfterManifest(*FreeCalls.
front());
3671 SharedMemoryUsed += AllocSize->getZExtValue();
3672 NumBytesMovedToSharedMemory = SharedMemoryUsed;
3673 Changed = ChangeStatus::CHANGED;
3680 if (MallocCalls.empty())
3681 return indicatePessimisticFixpoint();
3682 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3683 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
3684 if (!RFI.Declaration)
3685 return ChangeStatus::UNCHANGED;
3689 auto NumMallocCalls = MallocCalls.size();
3692 for (User *U : RFI.Declaration->
users()) {
3694 if (CB->getCaller() !=
F)
3696 if (!MallocCalls.count(CB))
3699 MallocCalls.remove(CB);
3702 const auto *ED =
A.getAAFor<AAExecutionDomain>(
3704 if (!ED || !ED->isExecutedByInitialThreadOnly(*CB))
3705 MallocCalls.remove(CB);
3709 findPotentialRemovedFreeCalls(
A);
3711 if (NumMallocCalls != MallocCalls.size())
3712 return ChangeStatus::CHANGED;
3714 return ChangeStatus::UNCHANGED;
3718 SmallSetVector<CallBase *, 4> MallocCalls;
3720 SmallPtrSet<CallBase *, 4> PotentialRemovedFreeCalls;
3722 unsigned SharedMemoryUsed = 0;
3725struct AAKernelInfo :
public StateWrapper<KernelInfoState, AbstractAttribute> {
3726 using Base = StateWrapper<KernelInfoState, AbstractAttribute>;
3727 AAKernelInfo(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
3731 static bool requiresCalleeForCallBase() {
return false; }
3734 void trackStatistics()
const override {}
3737 const std::string getAsStr(Attributor *)
const override {
3738 if (!isValidState())
3740 return std::string(SPMDCompatibilityTracker.isAssumed() ?
"SPMD"
3742 std::string(SPMDCompatibilityTracker.isAtFixpoint() ?
" [FIX]"
3744 std::string(
" #PRs: ") +
3745 (ReachedKnownParallelRegions.isValidState()
3746 ? std::to_string(ReachedKnownParallelRegions.size())
3748 ", #Unknown PRs: " +
3749 (ReachedUnknownParallelRegions.isValidState()
3750 ? std::to_string(ReachedUnknownParallelRegions.size())
3752 ", #Reaching Kernels: " +
3753 (ReachingKernelEntries.isValidState()
3754 ? std::to_string(ReachingKernelEntries.size())
3757 (ParallelLevels.isValidState()
3758 ? std::to_string(ParallelLevels.size())
3760 ", NestedPar: " + (NestedParallelism ?
"yes" :
"no");
3764 static AAKernelInfo &createForPosition(
const IRPosition &IRP, Attributor &
A);
3767 StringRef
getName()
const override {
return "AAKernelInfo"; }
3770 const char *getIdAddr()
const override {
return &ID; }
3773 static bool classof(
const AbstractAttribute *AA) {
3777 static const char ID;
3782struct AAKernelInfoFunction : AAKernelInfo {
3783 AAKernelInfoFunction(
const IRPosition &IRP, Attributor &
A)
3784 : AAKernelInfo(IRP,
A) {}
3786 SmallPtrSet<Instruction *, 4> GuardedInstructions;
3788 SmallPtrSetImpl<Instruction *> &getGuardedInstructions() {
3789 return GuardedInstructions;
3792 void setConfigurationOfKernelEnvironment(ConstantStruct *ConfigC) {
3794 KernelEnvC, ConfigC, {KernelInfo::ConfigurationIdx});
3795 assert(NewKernelEnvC &&
"Failed to create new kernel environment");
3799#define KERNEL_ENVIRONMENT_CONFIGURATION_SETTER(MEMBER) \
3800 void set##MEMBER##OfKernelEnvironment(ConstantInt *NewVal) { \
3801 ConstantStruct *ConfigC = \
3802 KernelInfo::getConfigurationFromKernelEnvironment(KernelEnvC); \
3803 Constant *NewConfigC = ConstantFoldInsertValueInstruction( \
3804 ConfigC, NewVal, {KernelInfo::MEMBER##Idx}); \
3805 assert(NewConfigC && "Failed to create new configuration environment"); \
3806 setConfigurationOfKernelEnvironment(cast<ConstantStruct>(NewConfigC)); \
3817#undef KERNEL_ENVIRONMENT_CONFIGURATION_SETTER
3824 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3828 OMPInformationCache::RuntimeFunctionInfo &InitRFI =
3829 OMPInfoCache.RFIs[OMPRTL___kmpc_target_init];
3830 OMPInformationCache::RuntimeFunctionInfo &DeinitRFI =
3831 OMPInfoCache.RFIs[OMPRTL___kmpc_target_deinit];
3835 auto StoreCallBase = [](
Use &U,
3836 OMPInformationCache::RuntimeFunctionInfo &RFI,
3838 CallBase *CB = OpenMPOpt::getCallIfRegularCall(U, &RFI);
3840 "Unexpected use of __kmpc_target_init or __kmpc_target_deinit!");
3842 "Multiple uses of __kmpc_target_init or __kmpc_target_deinit!");
3848 StoreCallBase(U, InitRFI, KernelInitCB);
3852 DeinitRFI.foreachUse(
3854 StoreCallBase(U, DeinitRFI, KernelDeinitCB);
3860 if (!KernelInitCB || !KernelDeinitCB)
3864 ReachingKernelEntries.insert(Fn);
3865 IsKernelEntry =
true;
3873 KernelConfigurationSimplifyCB =
3875 bool &UsedAssumedInformation) -> std::optional<Constant *> {
3876 if (!isAtFixpoint()) {
3879 UsedAssumedInformation =
true;
3885 A.registerGlobalVariableSimplificationCallback(
3886 *KernelEnvGV, KernelConfigurationSimplifyCB);
3889 bool CanChangeToSPMD = OMPInfoCache.runtimeFnsAvailable(
3890 {OMPRTL___kmpc_get_hardware_thread_id_in_block,
3891 OMPRTL___kmpc_barrier_simple_spmd});
3895 KernelInfo::getExecModeFromKernelEnvironment(KernelEnvC);
3900 SPMDCompatibilityTracker.indicateOptimisticFixpoint();
3904 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
3906 setExecModeOfKernelEnvironment(AssumedExecModeC);
3913 setMinThreadsOfKernelEnvironment(ConstantInt::get(Int32Ty, MinThreads));
3915 setMaxThreadsOfKernelEnvironment(ConstantInt::get(Int32Ty,
MaxThreads));
3916 auto [MinTeams, MaxTeams] =
3919 setMinTeamsOfKernelEnvironment(ConstantInt::get(Int32Ty, MinTeams));
3921 setMaxTeamsOfKernelEnvironment(ConstantInt::get(Int32Ty, MaxTeams));
3924 KernelInfo::getMayUseNestedParallelismFromKernelEnvironment(KernelEnvC);
3925 ConstantInt *AssumedMayUseNestedParallelismC = ConstantInt::get(
3927 setMayUseNestedParallelismOfKernelEnvironment(
3928 AssumedMayUseNestedParallelismC);
3932 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
3935 ConstantInt::get(UseGenericStateMachineC->
getIntegerType(),
false);
3936 setUseGenericStateMachineOfKernelEnvironment(
3937 AssumedUseGenericStateMachineC);
3943 if (!OMPInfoCache.RFIs[RFKind].Declaration)
3945 A.registerVirtualUseCallback(*OMPInfoCache.RFIs[RFKind].Declaration, CB);
3949 auto AddDependence = [](
Attributor &
A,
const AAKernelInfo *KI,
3965 if (SPMDCompatibilityTracker.isValidState())
3966 return AddDependence(
A,
this, QueryingAA);
3968 if (!ReachedKnownParallelRegions.isValidState())
3969 return AddDependence(
A,
this, QueryingAA);
3975 RegisterVirtualUse(OMPRTL___kmpc_get_max_team_threads,
3976 CustomStateMachineUseCB);
3977 RegisterVirtualUse(OMPRTL___kmpc_barrier_simple_generic,
3978 CustomStateMachineUseCB);
3979 RegisterVirtualUse(OMPRTL___kmpc_kernel_parallel,
3980 CustomStateMachineUseCB);
3981 RegisterVirtualUse(OMPRTL___kmpc_kernel_end_parallel,
3982 CustomStateMachineUseCB);
3986 if (SPMDCompatibilityTracker.isAtFixpoint())
3993 if (!SPMDCompatibilityTracker.isValidState())
3994 return AddDependence(
A,
this, QueryingAA);
3997 RegisterVirtualUse(OMPRTL___kmpc_get_hardware_thread_id_in_block,
4006 if (!SPMDCompatibilityTracker.isValidState())
4007 return AddDependence(
A,
this, QueryingAA);
4008 if (SPMDCompatibilityTracker.empty())
4009 return AddDependence(
A,
this, QueryingAA);
4010 if (!mayContainParallelRegion())
4011 return AddDependence(
A,
this, QueryingAA);
4014 RegisterVirtualUse(OMPRTL___kmpc_barrier_simple_spmd, SPMDBarrierUseCB);
4018 static std::string sanitizeForGlobalName(std::string S) {
4022 return !((C >=
'a' && C <=
'z') || (C >=
'A' && C <=
'Z') ||
4023 (C >=
'0' && C <=
'9') || C ==
'_');
4034 if (!KernelInitCB || !KernelDeinitCB)
4035 return ChangeStatus::UNCHANGED;
4039 bool HasBuiltStateMachine =
true;
4040 if (!changeToSPMDMode(
A,
Changed)) {
4042 HasBuiltStateMachine = buildCustomStateMachine(
A,
Changed);
4044 HasBuiltStateMachine =
false;
4048 ConstantStruct *ExistingKernelEnvC =
4050 ConstantInt *OldUseGenericStateMachineVal =
4051 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
4052 ExistingKernelEnvC);
4053 if (!HasBuiltStateMachine)
4054 setUseGenericStateMachineOfKernelEnvironment(
4055 OldUseGenericStateMachineVal);
4058 GlobalVariable *KernelEnvGV =
4062 Changed = ChangeStatus::CHANGED;
4068 void insertInstructionGuardsHelper(Attributor &
A) {
4069 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4071 auto CreateGuardedRegion = [&](
Instruction *RegionStartI,
4073 LoopInfo *LI =
nullptr;
4074 DominatorTree *DT =
nullptr;
4075 MemorySSAUpdater *MSU =
nullptr;
4104 DT, LI, MSU,
"region.guarded.end");
4107 MSU,
"region.barrier");
4110 DT, LI, MSU,
"region.exit");
4112 SplitBlock(ParentBB, RegionStartI, DT, LI, MSU,
"region.guarded");
4115 "Expected a different CFG");
4118 ParentBB, ParentBB->
getTerminator(), DT, LI, MSU,
"region.check.tid");
4121 A.registerManifestAddedBasicBlock(*RegionEndBB);
4122 A.registerManifestAddedBasicBlock(*RegionBarrierBB);
4123 A.registerManifestAddedBasicBlock(*RegionExitBB);
4124 A.registerManifestAddedBasicBlock(*RegionStartBB);
4125 A.registerManifestAddedBasicBlock(*RegionCheckTidBB);
4127 bool HasBroadcastValues =
false;
4130 for (Instruction &
I : *RegionStartBB) {
4132 for (Use &U :
I.uses()) {
4138 if (OutsideUses.
empty())
4141 HasBroadcastValues =
true;
4145 auto *SharedMem =
new GlobalVariable(
4146 M,
I.getType(),
false,
4148 sanitizeForGlobalName(
4149 (
I.getName() +
".guarded.output.alloc").str()),
4151 static_cast<unsigned>(AddressSpace::Shared));
4154 new StoreInst(&
I, SharedMem,
4157 LoadInst *LoadI =
new LoadInst(
4158 I.getType(), SharedMem,
I.getName() +
".guarded.output.load",
4162 for (Use *U : OutsideUses)
4163 A.changeUseAfterManifest(*U, *LoadI);
4166 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4171 OpenMPIRBuilder::LocationDescription Loc(ParentBB->
end(),
DL);
4173 uint32_t SrcLocStrSize;
4182 OpenMPIRBuilder::LocationDescription LocRegionCheckTid(
4183 RegionCheckTidBB->
end(),
DL);
4185 FunctionCallee HardwareTidFn =
4187 M, OMPRTL___kmpc_get_hardware_thread_id_in_block);
4191 OMPInfoCache.setCallingConvention(HardwareTidFn, Tid);
4193 OMPInfoCache.OMPBuilder.
Builder
4194 .
CreateCondBr(TidCheck, RegionStartBB, RegionBarrierBB)
4199 FunctionCallee BarrierFn =
4201 M, OMPRTL___kmpc_barrier_simple_spmd);
4206 OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
4209 if (HasBroadcastValues) {
4214 OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
4218 auto &AllocSharedRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
4219 SmallPtrSet<BasicBlock *, 8> Visited;
4220 for (Instruction *GuardedI : SPMDCompatibilityTracker) {
4222 if (!Visited.
insert(BB).second)
4228 while (++IP != IPEnd) {
4229 if (!IP->mayHaveSideEffects() && !IP->mayReadFromMemory())
4232 if (OpenMPOpt::getCallIfRegularCall(*
I, &AllocSharedRFI))
4234 if (!
I->user_empty() || !SPMDCompatibilityTracker.contains(
I)) {
4235 LastEffect =
nullptr;
4242 for (
auto &Reorder : Reorders)
4243 Reorder.first->moveBefore(Reorder.second->getIterator());
4248 for (Instruction *GuardedI : SPMDCompatibilityTracker) {
4250 auto *CalleeAA =
A.lookupAAFor<AAKernelInfo>(
4253 assert(CalleeAA !=
nullptr &&
"Expected Callee AAKernelInfo");
4256 if (CalleeAAFunction.getGuardedInstructions().contains(GuardedI))
4259 Instruction *GuardedRegionStart =
nullptr, *GuardedRegionEnd =
nullptr;
4260 for (Instruction &
I : *BB) {
4263 if (SPMDCompatibilityTracker.contains(&
I)) {
4264 CalleeAAFunction.getGuardedInstructions().insert(&
I);
4265 if (GuardedRegionStart)
4266 GuardedRegionEnd = &
I;
4268 GuardedRegionStart = GuardedRegionEnd = &
I;
4275 if (GuardedRegionStart) {
4277 std::make_pair(GuardedRegionStart, GuardedRegionEnd));
4278 GuardedRegionStart =
nullptr;
4279 GuardedRegionEnd =
nullptr;
4284 for (
auto &GR : GuardedRegions)
4285 CreateGuardedRegion(GR.first, GR.second);
4288 void forceSingleThreadPerWorkgroupHelper(Attributor &
A) {
4297 auto &Ctx = getAnchorValue().getContext();
4304 KernelInitCB->
getNextNode(),
"main.thread.user_code");
4309 A.registerManifestAddedBasicBlock(*InitBB);
4310 A.registerManifestAddedBasicBlock(*UserCodeBB);
4311 A.registerManifestAddedBasicBlock(*ReturnBB);
4320 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4321 FunctionCallee ThreadIdInBlockFn =
4323 M, OMPRTL___kmpc_get_hardware_thread_id_in_block);
4326 CallInst *ThreadIdInBlock =
4328 OMPInfoCache.setCallingConvention(ThreadIdInBlockFn, ThreadIdInBlock);
4334 ConstantInt::get(ThreadIdInBlock->
getType(), 0),
4335 "thread.is_main", InitBB);
4341 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4343 if (!SPMDCompatibilityTracker.isAssumed()) {
4344 for (Instruction *NonCompatibleI : SPMDCompatibilityTracker) {
4345 if (!NonCompatibleI)
4350 if (OMPInfoCache.RTLFunctions.contains(CB->getCalledFunction()))
4353 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
4354 ORA <<
"Value has potential side effects preventing SPMD-mode "
4357 ORA <<
". Add `[[omp::assume(\"ompx_spmd_amenable\")]]` to "
4358 "the called function to override";
4362 A.emitRemark<OptimizationRemarkAnalysis>(NonCompatibleI,
"OMP121",
4366 << *NonCompatibleI <<
"\n");
4378 Kernel = CB->getCaller();
4383 ConstantStruct *ExistingKernelEnvC =
4386 KernelInfo::getExecModeFromKernelEnvironment(ExistingKernelEnvC);
4392 Changed = ChangeStatus::CHANGED;
4396 if (mayContainParallelRegion())
4397 insertInstructionGuardsHelper(
A);
4399 forceSingleThreadPerWorkgroupHelper(
A);
4404 "Initially non-SPMD kernel has SPMD exec mode!");
4405 setExecModeOfKernelEnvironment(
4409 ++NumOpenMPTargetRegionKernelsSPMD;
4413 OMPInfoCache.SPMDizedKernels.insert(
Kernel);
4415 auto Remark = [&](OptimizationRemark
OR) {
4416 return OR <<
"Transformed generic-mode kernel to SPMD-mode.";
4418 A.emitRemark<OptimizationRemark>(KernelInitCB,
"OMP120",
Remark);
4428 if (!ReachedKnownParallelRegions.isValidState())
4431 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4432 if (!OMPInfoCache.runtimeFnsAvailable({OMPRTL___kmpc_get_max_team_threads,
4433 OMPRTL___kmpc_barrier_simple_generic,
4434 OMPRTL___kmpc_kernel_parallel,
4435 OMPRTL___kmpc_kernel_end_parallel}))
4438 ConstantStruct *ExistingKernelEnvC =
4445 ConstantInt *UseStateMachineC =
4446 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
4447 ExistingKernelEnvC);
4448 ConstantInt *ModeC =
4449 KernelInfo::getExecModeFromKernelEnvironment(ExistingKernelEnvC);
4454 if (UseStateMachineC->
isZero() ||
4458 Changed = ChangeStatus::CHANGED;
4461 setUseGenericStateMachineOfKernelEnvironment(
4468 if (!mayContainParallelRegion()) {
4469 ++NumOpenMPTargetRegionKernelsWithoutStateMachine;
4471 auto Remark = [&](OptimizationRemark
OR) {
4472 return OR <<
"Removing unused state machine from generic-mode kernel.";
4474 A.emitRemark<OptimizationRemark>(KernelInitCB,
"OMP130",
Remark);
4480 if (ReachedUnknownParallelRegions.empty()) {
4481 ++NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback;
4483 auto Remark = [&](OptimizationRemark
OR) {
4484 return OR <<
"Rewriting generic-mode kernel with a customized state "
4487 A.emitRemark<OptimizationRemark>(KernelInitCB,
"OMP131",
Remark);
4489 ++NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback;
4491 auto Remark = [&](OptimizationRemarkAnalysis
OR) {
4492 return OR <<
"Generic-mode kernel is executed with a customized state "
4493 "machine that requires a fallback.";
4495 A.emitRemark<OptimizationRemarkAnalysis>(KernelInitCB,
"OMP132",
Remark);
4498 for (CallBase *UnknownParallelRegionCB : ReachedUnknownParallelRegions) {
4499 if (!UnknownParallelRegionCB)
4501 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
4502 return ORA <<
"Call may contain unknown parallel regions. Use "
4503 <<
"`[[omp::assume(\"omp_no_parallelism\")]]` to "
4506 A.emitRemark<OptimizationRemarkAnalysis>(UnknownParallelRegionCB,
4539 auto &Ctx = getAnchorValue().getContext();
4543 BasicBlock *InitBB = KernelInitCB->getParent();
4545 KernelInitCB->getNextNode(),
"thread.user_code.check");
4549 Ctx,
"worker_state_machine.begin",
Kernel, UserCodeEntryBB);
4551 Ctx,
"worker_state_machine.finished",
Kernel, UserCodeEntryBB);
4553 Ctx,
"worker_state_machine.is_active.check",
Kernel, UserCodeEntryBB);
4556 Kernel, UserCodeEntryBB);
4559 Kernel, UserCodeEntryBB);
4561 Ctx,
"worker_state_machine.done.barrier",
Kernel, UserCodeEntryBB);
4562 A.registerManifestAddedBasicBlock(*InitBB);
4563 A.registerManifestAddedBasicBlock(*UserCodeEntryBB);
4564 A.registerManifestAddedBasicBlock(*IsWorkerCheckBB);
4565 A.registerManifestAddedBasicBlock(*StateMachineBeginBB);
4566 A.registerManifestAddedBasicBlock(*StateMachineFinishedBB);
4567 A.registerManifestAddedBasicBlock(*StateMachineIsActiveCheckBB);
4568 A.registerManifestAddedBasicBlock(*StateMachineIfCascadeCurrentBB);
4569 A.registerManifestAddedBasicBlock(*StateMachineEndParallelBB);
4570 A.registerManifestAddedBasicBlock(*StateMachineDoneBarrierBB);
4572 const DebugLoc &DLoc = KernelInitCB->getDebugLoc();
4578 ConstantInt::getAllOnesValue(KernelInitCB->getType()),
4579 "thread.is_worker", InitBB);
4588 FunctionCallee MaxTeamThreadsFn =
4590 M, OMPRTL___kmpc_get_max_team_threads);
4591 Constant *IsSPMDArg = ConstantInt::get(OMPInfoCache.OMPBuilder.Int32, 0);
4593 MaxTeamThreadsFn, {IsSPMDArg},
"max_team_threads", IsWorkerCheckBB);
4594 OMPInfoCache.setCallingConvention(MaxTeamThreadsFn, MaxTeamThreads);
4598 "thread.is_main_or_worker", IsWorkerCheckBB);
4601 StateMachineFinishedBB, IsWorkerCheckBB);
4604 const DataLayout &
DL =
M.getDataLayout();
4605 Type *VoidPtrTy = PointerType::getUnqual(Ctx);
4607 new AllocaInst(VoidPtrTy,
DL.getAllocaAddrSpace(),
nullptr,
4612 OpenMPIRBuilder::LocationDescription(StateMachineBeginBB->
end(), DLoc));
4614 Value *Ident = KernelInfo::getIdentFromKernelEnvironment(KernelEnvC);
4615 Value *GTid = KernelInitCB;
4617 FunctionCallee BarrierFn =
4619 M, OMPRTL___kmpc_barrier_simple_generic);
4622 OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
4626 (
unsigned int)AddressSpace::Generic) {
4627 WorkFnAI =
new AddrSpaceCastInst(
4628 WorkFnAI, PointerType::get(Ctx, (
unsigned int)AddressSpace::Generic),
4629 WorkFnAI->
getName() +
".generic", StateMachineBeginBB);
4633 FunctionCallee KernelParallelFn =
4635 M, OMPRTL___kmpc_kernel_parallel);
4637 KernelParallelFn, {WorkFnAI},
"worker.is_active", StateMachineBeginBB);
4638 OMPInfoCache.setCallingConvention(KernelParallelFn, IsActiveWorker);
4640 Instruction *WorkFn =
new LoadInst(VoidPtrTy, WorkFnAI,
"worker.work_fn",
4641 StateMachineBeginBB);
4644 FunctionType *ParallelRegionFnTy = FunctionType::get(
4645 Type::getVoidTy(Ctx), {Type::getInt16Ty(Ctx), Type::getInt32Ty(Ctx)},
4651 StateMachineBeginBB);
4652 IsDone->setDebugLoc(DLoc);
4654 StateMachineIsActiveCheckBB, StateMachineBeginBB)
4658 StateMachineDoneBarrierBB, StateMachineIsActiveCheckBB)
4664 const unsigned int WrapperFunctionArgNo = 6;
4669 for (
int I = 0,
E = ReachedKnownParallelRegions.size();
I <
E; ++
I) {
4670 auto *CB = ReachedKnownParallelRegions[
I];
4672 CB->getArgOperand(WrapperFunctionArgNo)->stripPointerCasts());
4674 Ctx,
"worker_state_machine.parallel_region.execute",
Kernel,
4675 StateMachineEndParallelBB);
4677 ->setDebugLoc(DLoc);
4683 Kernel, StateMachineEndParallelBB);
4684 A.registerManifestAddedBasicBlock(*PRExecuteBB);
4685 A.registerManifestAddedBasicBlock(*PRNextBB);
4690 if (
I + 1 <
E || !ReachedUnknownParallelRegions.empty()) {
4693 "worker.check_parallel_region", StateMachineIfCascadeCurrentBB);
4701 StateMachineIfCascadeCurrentBB)
4703 StateMachineIfCascadeCurrentBB = PRNextBB;
4709 if (!ReachedUnknownParallelRegions.empty()) {
4710 StateMachineIfCascadeCurrentBB->
setName(
4711 "worker_state_machine.parallel_region.fallback.execute");
4713 StateMachineIfCascadeCurrentBB)
4714 ->setDebugLoc(DLoc);
4717 StateMachineIfCascadeCurrentBB)
4720 FunctionCallee EndParallelFn =
4722 M, OMPRTL___kmpc_kernel_end_parallel);
4723 CallInst *EndParallel =
4725 OMPInfoCache.setCallingConvention(EndParallelFn, EndParallel);
4731 ->setDebugLoc(DLoc);
4741 KernelInfoState StateBefore = getState();
4747 struct UpdateKernelEnvCRAII {
4748 AAKernelInfoFunction &AA;
4750 UpdateKernelEnvCRAII(AAKernelInfoFunction &AA) : AA(AA) {}
4752 ~UpdateKernelEnvCRAII() {
4756 ConstantStruct *ExistingKernelEnvC =
4759 if (!AA.isValidState()) {
4760 AA.KernelEnvC = ExistingKernelEnvC;
4764 if (!AA.ReachedKnownParallelRegions.isValidState())
4765 AA.setUseGenericStateMachineOfKernelEnvironment(
4766 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
4767 ExistingKernelEnvC));
4769 if (!AA.SPMDCompatibilityTracker.isValidState())
4770 AA.setExecModeOfKernelEnvironment(
4771 KernelInfo::getExecModeFromKernelEnvironment(ExistingKernelEnvC));
4773 ConstantInt *MayUseNestedParallelismC =
4774 KernelInfo::getMayUseNestedParallelismFromKernelEnvironment(
4776 ConstantInt *NewMayUseNestedParallelismC = ConstantInt::get(
4777 MayUseNestedParallelismC->
getIntegerType(), AA.NestedParallelism);
4778 AA.setMayUseNestedParallelismOfKernelEnvironment(
4779 NewMayUseNestedParallelismC);
4789 if (!
I.mayWriteToMemory())
4792 const auto *UnderlyingObjsAA =
A.getAAFor<AAUnderlyingObjects>(
4794 DepClassTy::OPTIONAL);
4795 auto *
HS =
A.getAAFor<AAHeapToStack>(
4797 DepClassTy::OPTIONAL);
4798 if (UnderlyingObjsAA &&
4799 UnderlyingObjsAA->forallUnderlyingObjects([&](
Value &Obj) {
4800 if (AA::isAssumedThreadLocalObject(A, Obj, *this))
4804 auto *CB = dyn_cast<CallBase>(&Obj);
4805 return CB && HS && HS->isAssumedHeapToStack(*CB);
4811 SPMDCompatibilityTracker.insert(&
I);
4815 bool UsedAssumedInformationInCheckRWInst =
false;
4816 if (!SPMDCompatibilityTracker.isAtFixpoint())
4817 if (!
A.checkForAllReadWriteInstructions(
4818 CheckRWInst, *
this, UsedAssumedInformationInCheckRWInst))
4819 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4821 bool UsedAssumedInformationFromReachingKernels =
false;
4822 if (!IsKernelEntry) {
4823 updateParallelLevels(
A);
4825 bool AllReachingKernelsKnown =
true;
4826 updateReachingKernelEntries(
A, AllReachingKernelsKnown);
4827 UsedAssumedInformationFromReachingKernels = !AllReachingKernelsKnown;
4829 if (!SPMDCompatibilityTracker.empty()) {
4830 if (!ParallelLevels.isValidState())
4831 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4832 else if (!ReachingKernelEntries.isValidState())
4833 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4839 for (
auto *
Kernel : ReachingKernelEntries) {
4840 auto *CBAA =
A.getAAFor<AAKernelInfo>(
4842 if (CBAA && CBAA->SPMDCompatibilityTracker.isValidState() &&
4843 CBAA->SPMDCompatibilityTracker.isAssumed())
4847 if (!CBAA || !CBAA->SPMDCompatibilityTracker.isAtFixpoint())
4848 UsedAssumedInformationFromReachingKernels =
true;
4850 if (SPMD != 0 &&
Generic != 0)
4851 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4857 bool AllParallelRegionStatesWereFixed =
true;
4858 bool AllSPMDStatesWereFixed =
true;
4865 if (
Function *Callback = OMPInformationCache::getAnalyzableCallback(CB)) {
4867 <<
Callback->getName() <<
" of " << CB <<
"\n");
4868 if (
auto *CallbackAA =
A.getAAFor<AAKernelInfo>(
4870 getState() ^= CallbackAA->getState();
4871 AllSPMDStatesWereFixed &=
4872 CallbackAA->SPMDCompatibilityTracker.isAtFixpoint();
4873 AllParallelRegionStatesWereFixed &=
4874 CallbackAA->ReachedKnownParallelRegions.isAtFixpoint();
4875 AllParallelRegionStatesWereFixed &=
4876 CallbackAA->ReachedUnknownParallelRegions.isAtFixpoint();
4879 auto *CBAA =
A.getAAFor<AAKernelInfo>(
4883 getState() ^= CBAA->getState();
4884 AllSPMDStatesWereFixed &= CBAA->SPMDCompatibilityTracker.isAtFixpoint();
4885 AllParallelRegionStatesWereFixed &=
4886 CBAA->ReachedKnownParallelRegions.isAtFixpoint();
4887 AllParallelRegionStatesWereFixed &=
4888 CBAA->ReachedUnknownParallelRegions.isAtFixpoint();
4892 bool UsedAssumedInformationInCheckCallInst =
false;
4893 if (!
A.checkForAllCallLikeInstructions(
4894 CheckCallInst, *
this, UsedAssumedInformationInCheckCallInst)) {
4896 <<
"Failed to visit all call-like instructions!\n";);
4897 return indicatePessimisticFixpoint();
4902 if (!UsedAssumedInformationInCheckCallInst &&
4903 AllParallelRegionStatesWereFixed) {
4904 ReachedKnownParallelRegions.indicateOptimisticFixpoint();
4905 ReachedUnknownParallelRegions.indicateOptimisticFixpoint();
4910 if (!UsedAssumedInformationInCheckRWInst &&
4911 !UsedAssumedInformationInCheckCallInst &&
4912 !UsedAssumedInformationFromReachingKernels && AllSPMDStatesWereFixed)
4913 SPMDCompatibilityTracker.indicateOptimisticFixpoint();
4915 return StateBefore == getState() ? ChangeStatus::UNCHANGED
4916 : ChangeStatus::CHANGED;
4921 void updateReachingKernelEntries(Attributor &
A,
4922 bool &AllReachingKernelsKnown) {
4923 auto PredCallSite = [&](AbstractCallSite ACS) {
4926 assert(Caller &&
"Caller is nullptr");
4928 auto *CAA =
A.getOrCreateAAFor<AAKernelInfo>(
4930 if (CAA && CAA->ReachingKernelEntries.isValidState()) {
4931 ReachingKernelEntries ^= CAA->ReachingKernelEntries;
4937 ReachingKernelEntries.indicatePessimisticFixpoint();
4942 if (!
A.checkForAllCallSites(PredCallSite, *
this,
4944 AllReachingKernelsKnown))
4945 ReachingKernelEntries.indicatePessimisticFixpoint();
4949 void updateParallelLevels(Attributor &
A) {
4950 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4951 OMPInformationCache::RuntimeFunctionInfo &Parallel60RFI =
4952 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
4954 auto PredCallSite = [&](AbstractCallSite ACS) {
4957 assert(Caller &&
"Caller is nullptr");
4961 if (CAA && CAA->ParallelLevels.isValidState()) {
4967 if (Caller == Parallel60RFI.Declaration) {
4968 ParallelLevels.indicatePessimisticFixpoint();
4972 ParallelLevels ^= CAA->ParallelLevels;
4979 ParallelLevels.indicatePessimisticFixpoint();
4984 bool AllCallSitesKnown =
true;
4985 if (!
A.checkForAllCallSites(PredCallSite, *
this,
4988 ParallelLevels.indicatePessimisticFixpoint();
4995struct AAKernelInfoCallSite : AAKernelInfo {
4996 AAKernelInfoCallSite(
const IRPosition &IRP, Attributor &
A)
4997 : AAKernelInfo(IRP,
A) {}
5001 AAKernelInfo::initialize(
A);
5004 auto *AssumptionAA =
A.getAAFor<AAAssumptionInfo>(
5008 if (AssumptionAA && AssumptionAA->hasAssumption(
"ompx_spmd_amenable")) {
5009 indicateOptimisticFixpoint();
5017 indicateOptimisticFixpoint();
5026 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5027 const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee);
5028 if (It == OMPInfoCache.RuntimeFunctionIDMap.end()) {
5030 if (!Callee || !
A.isFunctionIPOAmendable(*Callee)) {
5034 if (!AssumptionAA ||
5035 !(AssumptionAA->hasAssumption(
"omp_no_openmp") ||
5036 AssumptionAA->hasAssumption(
"omp_no_parallelism")))
5037 ReachedUnknownParallelRegions.insert(&CB);
5041 if (!SPMDCompatibilityTracker.isAtFixpoint()) {
5042 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5043 SPMDCompatibilityTracker.insert(&CB);
5048 indicateOptimisticFixpoint();
5057 if (NumCallees > 1 && !
Callee->hasMetadata(LLVMContext::MD_callback)) {
5058 indicatePessimisticFixpoint();
5065 case OMPRTL___kmpc_is_spmd_exec_mode:
5066 case OMPRTL___kmpc_distribute_static_fini:
5067 case OMPRTL___kmpc_for_static_fini:
5068 case OMPRTL___kmpc_global_thread_num:
5069 case OMPRTL___kmpc_get_hardware_num_threads_in_block:
5070 case OMPRTL___kmpc_get_hardware_num_blocks:
5071 case OMPRTL___kmpc_single:
5072 case OMPRTL___kmpc_end_single:
5073 case OMPRTL___kmpc_master:
5074 case OMPRTL___kmpc_end_master:
5075 case OMPRTL___kmpc_barrier:
5076 case OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2:
5077 case OMPRTL___kmpc_gpu_xteam_reduce_nowait:
5078 case OMPRTL___kmpc_error:
5079 case OMPRTL___kmpc_flush:
5080 case OMPRTL___kmpc_get_hardware_thread_id_in_block:
5081 case OMPRTL___kmpc_get_warp_size:
5082 case OMPRTL_omp_get_thread_num:
5083 case OMPRTL_omp_get_num_threads:
5084 case OMPRTL_omp_get_max_threads:
5085 case OMPRTL_omp_in_parallel:
5086 case OMPRTL_omp_get_dynamic:
5087 case OMPRTL_omp_get_cancellation:
5088 case OMPRTL_omp_get_nested:
5089 case OMPRTL_omp_get_schedule:
5090 case OMPRTL_omp_get_thread_limit:
5091 case OMPRTL_omp_get_supported_active_levels:
5092 case OMPRTL_omp_get_max_active_levels:
5093 case OMPRTL_omp_get_level:
5094 case OMPRTL_omp_get_ancestor_thread_num:
5095 case OMPRTL_omp_get_team_size:
5096 case OMPRTL_omp_get_active_level:
5097 case OMPRTL_omp_in_final:
5098 case OMPRTL_omp_get_proc_bind:
5099 case OMPRTL_omp_get_num_places:
5100 case OMPRTL_omp_get_num_procs:
5101 case OMPRTL_omp_get_place_proc_ids:
5102 case OMPRTL_omp_get_place_num:
5103 case OMPRTL_omp_get_partition_num_places:
5104 case OMPRTL_omp_get_partition_place_nums:
5105 case OMPRTL_omp_get_wtime:
5107 case OMPRTL___kmpc_distribute_static_init_4:
5108 case OMPRTL___kmpc_distribute_static_init_4u:
5109 case OMPRTL___kmpc_distribute_static_init_8:
5110 case OMPRTL___kmpc_distribute_static_init_8u:
5111 case OMPRTL___kmpc_for_static_init_4:
5112 case OMPRTL___kmpc_for_static_init_4u:
5113 case OMPRTL___kmpc_for_static_init_8:
5114 case OMPRTL___kmpc_for_static_init_8u: {
5116 unsigned ScheduleArgOpNo = 2;
5117 auto *ScheduleTypeCI =
5119 unsigned ScheduleTypeVal =
5120 ScheduleTypeCI ? ScheduleTypeCI->getZExtValue() : 0;
5122 case OMPScheduleType::UnorderedStatic:
5123 case OMPScheduleType::UnorderedStaticChunked:
5124 case OMPScheduleType::OrderedDistribute:
5125 case OMPScheduleType::OrderedDistributeChunked:
5128 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5129 SPMDCompatibilityTracker.insert(&CB);
5133 case OMPRTL___kmpc_target_init:
5136 case OMPRTL___kmpc_target_deinit:
5137 KernelDeinitCB = &CB;
5139 case OMPRTL___kmpc_parallel_60:
5140 if (!handleParallel60(
A, CB))
5141 indicatePessimisticFixpoint();
5143 case OMPRTL___kmpc_omp_task:
5145 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5146 SPMDCompatibilityTracker.insert(&CB);
5147 ReachedUnknownParallelRegions.insert(&CB);
5149 case OMPRTL___kmpc_alloc_shared:
5150 case OMPRTL___kmpc_free_shared:
5158 case OMPRTL___kmpc_distribute_static_loop_4:
5159 case OMPRTL___kmpc_distribute_static_loop_4u:
5160 case OMPRTL___kmpc_distribute_static_loop_8:
5161 case OMPRTL___kmpc_distribute_static_loop_8u:
5169 if (!OMPInformationCache::getAnalyzableCallback(CB))
5170 ReachedUnknownParallelRegions.insert(&CB);
5171 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5172 SPMDCompatibilityTracker.insert(&CB);
5174 case OMPRTL___kmpc_distribute_for_static_loop_4:
5175 case OMPRTL___kmpc_distribute_for_static_loop_4u:
5176 case OMPRTL___kmpc_distribute_for_static_loop_8:
5177 case OMPRTL___kmpc_distribute_for_static_loop_8u:
5178 case OMPRTL___kmpc_for_static_loop_4:
5179 case OMPRTL___kmpc_for_static_loop_4u:
5180 case OMPRTL___kmpc_for_static_loop_8:
5181 case OMPRTL___kmpc_for_static_loop_8u:
5190 if (!OMPInformationCache::getAnalyzableCallback(CB))
5191 ReachedUnknownParallelRegions.insert(&CB);
5192 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5193 SPMDCompatibilityTracker.insert(&CB);
5198 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5199 SPMDCompatibilityTracker.insert(&CB);
5205 indicateOptimisticFixpoint();
5209 A.getAAFor<AACallEdges>(*
this, getIRPosition(), DepClassTy::OPTIONAL);
5210 if (!AACE || !AACE->getState().isValidState() || AACE->hasUnknownCallee()) {
5211 CheckCallee(getAssociatedFunction(), 1);
5214 const auto &OptimisticEdges = AACE->getOptimisticEdges();
5215 for (
auto *Callee : OptimisticEdges) {
5216 CheckCallee(Callee, OptimisticEdges.size());
5227 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5228 KernelInfoState StateBefore = getState();
5230 auto CheckCallee = [&](
Function *
F,
int NumCallees) {
5231 const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(
F);
5235 if (It == OMPInfoCache.RuntimeFunctionIDMap.end()) {
5238 A.getAAFor<AAKernelInfo>(*
this, FnPos, DepClassTy::REQUIRED);
5240 return indicatePessimisticFixpoint();
5241 if (getState() == FnAA->getState())
5242 return ChangeStatus::UNCHANGED;
5243 getState() = FnAA->getState();
5244 return ChangeStatus::CHANGED;
5248 if (NumCallees > 1 && !
F->hasMetadata(LLVMContext::MD_callback))
5249 return indicatePessimisticFixpoint();
5252 if (It->getSecond() == OMPRTL___kmpc_parallel_60) {
5253 if (!handleParallel60(
A, CB))
5254 return indicatePessimisticFixpoint();
5255 return StateBefore == getState() ? ChangeStatus::UNCHANGED
5256 : ChangeStatus::CHANGED;
5262 (It->getSecond() == OMPRTL___kmpc_alloc_shared ||
5263 It->getSecond() == OMPRTL___kmpc_free_shared) &&
5264 "Expected a __kmpc_alloc_shared or __kmpc_free_shared runtime call");
5266 auto *HeapToStackAA =
A.getAAFor<AAHeapToStack>(
5268 auto *HeapToSharedAA =
A.getAAFor<AAHeapToShared>(
5276 case OMPRTL___kmpc_alloc_shared:
5277 if ((!HeapToStackAA || !HeapToStackAA->isAssumedHeapToStack(CB)) &&
5278 (!HeapToSharedAA || !HeapToSharedAA->isAssumedHeapToShared(CB)))
5279 SPMDCompatibilityTracker.insert(&CB);
5281 case OMPRTL___kmpc_free_shared:
5282 if ((!HeapToStackAA ||
5283 !HeapToStackAA->isAssumedHeapToStackRemovedFree(CB)) &&
5285 !HeapToSharedAA->isAssumedHeapToSharedRemovedFree(CB)))
5286 SPMDCompatibilityTracker.insert(&CB);
5289 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5290 SPMDCompatibilityTracker.insert(&CB);
5292 return ChangeStatus::CHANGED;
5296 A.getAAFor<AACallEdges>(*
this, getIRPosition(), DepClassTy::OPTIONAL);
5297 if (!AACE || !AACE->getState().isValidState() || AACE->hasUnknownCallee()) {
5298 if (
Function *
F = getAssociatedFunction())
5301 const auto &OptimisticEdges = AACE->getOptimisticEdges();
5302 for (
auto *Callee : OptimisticEdges) {
5303 CheckCallee(Callee, OptimisticEdges.size());
5309 return StateBefore == getState() ? ChangeStatus::UNCHANGED
5310 : ChangeStatus::CHANGED;
5315 bool handleParallel60(Attributor &
A, CallBase &CB) {
5316 const unsigned int NonWrapperFunctionArgNo = 5;
5317 const unsigned int WrapperFunctionArgNo = 6;
5318 auto ParallelRegionOpArgNo = SPMDCompatibilityTracker.isAssumed()
5319 ? NonWrapperFunctionArgNo
5320 : WrapperFunctionArgNo;
5324 if (!ParallelRegion)
5327 ReachedKnownParallelRegions.insert(&CB);
5329 auto *FnAA =
A.getAAFor<AAKernelInfo>(
5331 NestedParallelism |= !FnAA || !FnAA->getState().isValidState() ||
5332 !FnAA->ReachedKnownParallelRegions.empty() ||
5333 !FnAA->ReachedKnownParallelRegions.isValidState() ||
5334 !FnAA->ReachedUnknownParallelRegions.isValidState() ||
5335 !FnAA->ReachedUnknownParallelRegions.empty();
5340struct AAFoldRuntimeCall
5341 :
public StateWrapper<BooleanState, AbstractAttribute> {
5342 using Base = StateWrapper<BooleanState, AbstractAttribute>;
5344 AAFoldRuntimeCall(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
5347 void trackStatistics()
const override {}
5350 static AAFoldRuntimeCall &createForPosition(
const IRPosition &IRP,
5354 StringRef
getName()
const override {
return "AAFoldRuntimeCall"; }
5357 const char *getIdAddr()
const override {
return &ID; }
5361 static bool classof(
const AbstractAttribute *AA) {
5365 static const char ID;
5368struct AAFoldRuntimeCallCallSiteReturned : AAFoldRuntimeCall {
5369 AAFoldRuntimeCallCallSiteReturned(
const IRPosition &IRP, Attributor &
A)
5370 : AAFoldRuntimeCall(IRP,
A) {}
5373 const std::string getAsStr(Attributor *)
const override {
5374 if (!isValidState())
5377 std::string Str(
"simplified value: ");
5379 if (!SimplifiedValue)
5380 return Str + std::string(
"none");
5382 if (!*SimplifiedValue)
5383 return Str + std::string(
"nullptr");
5386 return Str + std::to_string(CI->getSExtValue());
5388 return Str + std::string(
"unknown");
5393 indicatePessimisticFixpoint();
5397 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5398 const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee);
5399 assert(It != OMPInfoCache.RuntimeFunctionIDMap.end() &&
5400 "Expected a known OpenMP runtime function");
5402 RFKind = It->getSecond();
5405 A.registerSimplificationCallback(
5407 [&](
const IRPosition &IRP,
const AbstractAttribute *AA,
5408 bool &UsedAssumedInformation) -> std::optional<Value *> {
5409 assert((isValidState() || SimplifiedValue ==
nullptr) &&
5410 "Unexpected invalid state!");
5412 if (!isAtFixpoint()) {
5413 UsedAssumedInformation =
true;
5415 A.recordDependence(*
this, *AA, DepClassTy::OPTIONAL);
5417 return SimplifiedValue;
5424 case OMPRTL___kmpc_is_spmd_exec_mode:
5427 case OMPRTL___kmpc_parallel_level:
5430 case OMPRTL___kmpc_get_hardware_num_threads_in_block:
5431 Changed =
Changed | foldKernelFnAttribute(
A,
"omp_target_thread_limit");
5433 case OMPRTL___kmpc_get_hardware_num_blocks:
5446 if (SimplifiedValue && *SimplifiedValue) {
5449 A.deleteAfterManifest(
I);
5452 auto Remark = [&](OptimizationRemark
OR) {
5454 return OR <<
"Replacing OpenMP runtime call "
5456 <<
ore::NV(
"FoldedValue",
C->getZExtValue()) <<
".";
5457 return OR <<
"Replacing OpenMP runtime call "
5462 A.emitRemark<OptimizationRemark>(CB,
"OMP180",
Remark);
5465 << **SimplifiedValue <<
"\n");
5467 Changed = ChangeStatus::CHANGED;
5474 SimplifiedValue =
nullptr;
5475 return AAFoldRuntimeCall::indicatePessimisticFixpoint();
5481 std::optional<Value *> SimplifiedValueBefore = SimplifiedValue;
5483 unsigned AssumedSPMDCount = 0, KnownSPMDCount = 0;
5484 unsigned AssumedNonSPMDCount = 0, KnownNonSPMDCount = 0;
5485 auto *CallerKernelInfoAA =
A.getAAFor<AAKernelInfo>(
5488 if (!CallerKernelInfoAA ||
5489 !CallerKernelInfoAA->ReachingKernelEntries.isValidState())
5490 return indicatePessimisticFixpoint();
5492 for (
Kernel K : CallerKernelInfoAA->ReachingKernelEntries) {
5494 DepClassTy::REQUIRED);
5496 if (!AA || !AA->isValidState()) {
5497 SimplifiedValue =
nullptr;
5498 return indicatePessimisticFixpoint();
5501 if (AA->SPMDCompatibilityTracker.isAssumed()) {
5502 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5507 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5508 ++KnownNonSPMDCount;
5510 ++AssumedNonSPMDCount;
5514 if ((AssumedSPMDCount + KnownSPMDCount) &&
5515 (AssumedNonSPMDCount + KnownNonSPMDCount))
5516 return indicatePessimisticFixpoint();
5518 auto &Ctx = getAnchorValue().getContext();
5519 if (KnownSPMDCount || AssumedSPMDCount) {
5520 assert(KnownNonSPMDCount == 0 && AssumedNonSPMDCount == 0 &&
5521 "Expected only SPMD kernels!");
5524 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx),
true);
5525 }
else if (KnownNonSPMDCount || AssumedNonSPMDCount) {
5526 assert(KnownSPMDCount == 0 && AssumedSPMDCount == 0 &&
5527 "Expected only non-SPMD kernels!");
5530 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx),
false);
5535 assert(!SimplifiedValue &&
"SimplifiedValue should be none");
5538 return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED
5539 : ChangeStatus::CHANGED;
5544 std::optional<Value *> SimplifiedValueBefore = SimplifiedValue;
5546 auto *CallerKernelInfoAA =
A.getAAFor<AAKernelInfo>(
5549 if (!CallerKernelInfoAA ||
5550 !CallerKernelInfoAA->ParallelLevels.isValidState())
5551 return indicatePessimisticFixpoint();
5553 if (!CallerKernelInfoAA->ReachingKernelEntries.isValidState())
5554 return indicatePessimisticFixpoint();
5556 if (CallerKernelInfoAA->ReachingKernelEntries.empty()) {
5557 assert(!SimplifiedValue &&
5558 "SimplifiedValue should keep none at this point");
5559 return ChangeStatus::UNCHANGED;
5562 unsigned AssumedSPMDCount = 0, KnownSPMDCount = 0;
5563 unsigned AssumedNonSPMDCount = 0, KnownNonSPMDCount = 0;
5564 for (
Kernel K : CallerKernelInfoAA->ReachingKernelEntries) {
5566 DepClassTy::REQUIRED);
5567 if (!AA || !AA->SPMDCompatibilityTracker.isValidState())
5568 return indicatePessimisticFixpoint();
5570 if (AA->SPMDCompatibilityTracker.isAssumed()) {
5571 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5576 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5577 ++KnownNonSPMDCount;
5579 ++AssumedNonSPMDCount;
5583 if ((AssumedSPMDCount + KnownSPMDCount) &&
5584 (AssumedNonSPMDCount + KnownNonSPMDCount))
5585 return indicatePessimisticFixpoint();
5587 auto &Ctx = getAnchorValue().getContext();
5591 if (AssumedSPMDCount || KnownSPMDCount) {
5592 assert(KnownNonSPMDCount == 0 && AssumedNonSPMDCount == 0 &&
5593 "Expected only SPMD kernels!");
5594 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), 1);
5596 assert(KnownSPMDCount == 0 && AssumedSPMDCount == 0 &&
5597 "Expected only non-SPMD kernels!");
5598 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), 0);
5600 return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED
5601 : ChangeStatus::CHANGED;
5604 ChangeStatus foldKernelFnAttribute(Attributor &
A, llvm::StringRef Attr) {
5606 int32_t CurrentAttrValue = -1;
5607 std::optional<Value *> SimplifiedValueBefore = SimplifiedValue;
5609 auto *CallerKernelInfoAA =
A.getAAFor<AAKernelInfo>(
5612 if (!CallerKernelInfoAA ||
5613 !CallerKernelInfoAA->ReachingKernelEntries.isValidState())
5614 return indicatePessimisticFixpoint();
5617 for (
Kernel K : CallerKernelInfoAA->ReachingKernelEntries) {
5618 int32_t NextAttrVal =
K->getFnAttributeAsParsedInteger(Attr, -1);
5620 if (NextAttrVal == -1 ||
5621 (CurrentAttrValue != -1 && CurrentAttrValue != NextAttrVal))
5622 return indicatePessimisticFixpoint();
5623 CurrentAttrValue = NextAttrVal;
5626 if (CurrentAttrValue != -1) {
5627 auto &Ctx = getAnchorValue().getContext();
5629 ConstantInt::get(Type::getInt32Ty(Ctx), CurrentAttrValue);
5631 return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED
5632 : ChangeStatus::CHANGED;
5638 std::optional<Value *> SimplifiedValue;
5648 auto &RFI = OMPInfoCache.RFIs[RF];
5649 RFI.foreachUse(SCC, [&](Use &U,
Function &
F) {
5650 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &RFI);
5653 A.getOrCreateAAFor<AAFoldRuntimeCall>(
5655 DepClassTy::NONE,
false,
5661void OpenMPOpt::registerAAs(
bool IsModulePass) {
5671 A.getOrCreateAAFor<AAKernelInfo>(
5673 DepClassTy::NONE,
false,
5677 OMPInformationCache::RuntimeFunctionInfo &InitRFI =
5678 OMPInfoCache.RFIs[OMPRTL___kmpc_target_init];
5679 InitRFI.foreachUse(SCC, CreateKernelInfoCB);
5681 registerFoldRuntimeCall(OMPRTL___kmpc_is_spmd_exec_mode);
5682 registerFoldRuntimeCall(OMPRTL___kmpc_parallel_level);
5683 registerFoldRuntimeCall(OMPRTL___kmpc_get_hardware_num_threads_in_block);
5684 registerFoldRuntimeCall(OMPRTL___kmpc_get_hardware_num_blocks);
5689 for (
int Idx = 0; Idx < OMPInfoCache.ICVs.size() - 1; ++Idx) {
5692 auto &GetterRFI = OMPInfoCache.RFIs[ICVInfo.Getter];
5695 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &GetterRFI);
5702 A.getOrCreateAAFor<AAICVTracker>(CBPos);
5706 GetterRFI.foreachUse(SCC, CreateAA);
5715 for (
auto *
F : SCC) {
5716 if (
F->isDeclaration())
5722 if (
F->hasLocalLinkage()) {
5724 const auto *CB = dyn_cast<CallBase>(U.getUser());
5725 return CB && CB->isCallee(&U) &&
5726 A.isRunOn(const_cast<Function *>(CB->getCaller()));
5730 registerAAsForFunction(
A, *
F);
5734void OpenMPOpt::registerAAsForFunction(Attributor &
A,
const Function &
F) {
5735 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5738 A.getOrCreateAAFor<AAExecutionDomain>(FPos);
5739 if (
F.hasFnAttribute(Attribute::Convergent))
5740 A.getOrCreateAAFor<AANonConvergent>(FPos);
5742 bool FunctionUsesSharedAlloc =
false;
5744 const OMPInformationCache::RuntimeFunctionInfo::UseVector *SharedAllocUses =
5745 OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared].getUseVector(
5747 FunctionUsesSharedAlloc = SharedAllocUses && !SharedAllocUses->
empty();
5749 bool HasHeapToStackCandidate =
false;
5750 const TargetLibraryInfo *TLI =
nullptr;
5754 bool UsedAssumedInformation =
false;
5757 A.getOrCreateAAFor<AAAddressSpace>(
5764 TLI =
A.getInfoCache().getTargetLibraryInfoForFunction(
F);
5765 HasHeapToStackCandidate =
5769 A.getOrCreateAAFor<AAIndirectCallInfo>(
5774 A.getOrCreateAAFor<AAAddressSpace>(
5783 if (
II->getIntrinsicID() == Intrinsic::assume) {
5784 A.getOrCreateAAFor<AAPotentialValues>(
5791 if (FunctionUsesSharedAlloc)
5792 A.getOrCreateAAFor<AAHeapToShared>(FPos);
5793 if (HasHeapToStackCandidate)
5794 A.getOrCreateAAFor<AAHeapToStack>(FPos);
5797const char AAICVTracker::ID = 0;
5798const char AAKernelInfo::ID = 0;
5800const char AAHeapToShared::ID = 0;
5801const char AAFoldRuntimeCall::ID = 0;
5803AAICVTracker &AAICVTracker::createForPosition(
const IRPosition &IRP,
5805 AAICVTracker *AA =
nullptr;
5813 AA =
new (
A.Allocator) AAICVTrackerFunctionReturned(IRP,
A);
5816 AA =
new (
A.Allocator) AAICVTrackerCallSiteReturned(IRP,
A);
5819 AA =
new (
A.Allocator) AAICVTrackerCallSite(IRP,
A);
5822 AA =
new (
A.Allocator) AAICVTrackerFunction(IRP,
A);
5831 AAExecutionDomainFunction *
AA =
nullptr;
5841 "AAExecutionDomain can only be created for function position!");
5843 AA =
new (
A.Allocator) AAExecutionDomainFunction(IRP,
A);
5850AAHeapToShared &AAHeapToShared::createForPosition(
const IRPosition &IRP,
5852 AAHeapToSharedFunction *
AA =
nullptr;
5862 "AAHeapToShared can only be created for function position!");
5864 AA =
new (
A.Allocator) AAHeapToSharedFunction(IRP,
A);
5871AAKernelInfo &AAKernelInfo::createForPosition(
const IRPosition &IRP,
5873 AAKernelInfo *AA =
nullptr;
5883 AA =
new (
A.Allocator) AAKernelInfoCallSite(IRP,
A);
5886 AA =
new (
A.Allocator) AAKernelInfoFunction(IRP,
A);
5893AAFoldRuntimeCall &AAFoldRuntimeCall::createForPosition(
const IRPosition &IRP,
5895 AAFoldRuntimeCall *AA =
nullptr;
5904 llvm_unreachable(
"KernelInfo can only be created for call site position!");
5906 AA =
new (
A.Allocator) AAFoldRuntimeCallCallSiteReturned(IRP,
A);
5926 unsigned NumAssumedCallees) {
5944 if (Kernels.contains(&
F))
5946 return !
F.use_empty();
5953 return ORA <<
"Could not internalize function. "
5954 <<
"Some optimizations may not be possible. [OMP140]";
5966 if (!
F.isDeclaration() && !Kernels.contains(&
F) && IsCalled(
F) &&
5970 }
else if (!
F.hasLocalLinkage() && !
F.hasFnAttribute(Attribute::Cold)) {
5983 if (!
F.isDeclaration() && !InternalizedMap.
lookup(&
F)) {
5985 Functions.insert(&
F);
6003 OMPInformationCache InfoCache(M, AG, Allocator,
nullptr, PostLink);
6005 unsigned MaxFixpointIterations =
6018 return F.hasFnAttribute(
"kernel");
6023 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache,
A);
6029 if (!
F.isDeclaration() && !Kernels.contains(&
F) &&
6030 !
F.hasFnAttribute(Attribute::NoInline))
6031 F.addFnAttr(Attribute::AlwaysInline);
6061 Module &M = *
C.begin()->getFunction().getParent();
6083 OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, Allocator,
6084 &Functions, PostLink);
6086 unsigned MaxFixpointIterations =
6101 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache,
A);
6102 bool Changed = OMPOpt.run(
false);
6121 if (
F.hasKernelCallingConv()) {
6126 ++NumOpenMPTargetRegionKernels;
6129 ++NumNonOpenMPTargetRegionKernels;
6136 Metadata *MD = M.getModuleFlag(
"openmp");
6144 Metadata *MD = M.getModuleFlag(
"openmp-device");
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
amdgpu aa AMDGPU Address space based Alias Analysis Wrapper
amdgpu next use AMDGPU Next Use Analysis Printer
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Expand Atomic instructions
static cl::opt< unsigned > SetFixpointIterations("attributor-max-iterations", cl::Hidden, cl::desc("Maximal number of fixpoint iterations."), cl::init(32))
static const Function * getParent(const Value *V)
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file provides interfaces used to manipulate a call graph, regardless if it is a "old style" Call...
This file provides interfaces used to build and manipulate a call graph, which is a very useful tool ...
This file contains the declarations for the subclasses of Constant, which represent the different fla...
This file defines the DenseSet and SmallDenseSet classes.
This file defines an array type that can be indexed using scoped enum values.
static void emitRemark(const Function &F, OptimizationRemarkEmitter &ORE, bool Skip)
Loop::LoopBounds::Direction Direction
Machine Check Debug Module
This file provides utility analysis objects describing memory locations.
uint64_t IntrinsicInst * II
This file defines constans and helpers used when dealing with OpenMP.
This file defines constans that will be used by both host and device compilation.
static constexpr auto TAG
static cl::opt< bool > HideMemoryTransferLatency("openmp-hide-memory-transfer-latency", cl::desc("[WIP] Tries to hide the latency of host to device memory" " transfers"), cl::Hidden, cl::init(false))
static cl::opt< bool > DisableOpenMPOptStateMachineRewrite("openmp-opt-disable-state-machine-rewrite", cl::desc("Disable OpenMP optimizations that replace the state machine."), cl::Hidden, cl::init(false))
static cl::opt< bool > EnableParallelRegionMerging("openmp-opt-enable-merging", cl::desc("Enable the OpenMP region merging optimization."), cl::Hidden, cl::init(false))
static cl::opt< bool > PrintModuleAfterOptimizations("openmp-opt-print-module-after", cl::desc("Print the current module after OpenMP optimizations."), cl::Hidden, cl::init(false))
#define KERNEL_ENVIRONMENT_CONFIGURATION_GETTER(MEMBER)
#define KERNEL_ENVIRONMENT_CONFIGURATION_IDX(MEMBER, IDX)
#define KERNEL_ENVIRONMENT_CONFIGURATION_SETTER(MEMBER)
static cl::opt< bool > PrintOpenMPKernels("openmp-print-gpu-kernels", cl::init(false), cl::Hidden)
static cl::opt< bool > DisableOpenMPOptFolding("openmp-opt-disable-folding", cl::desc("Disable OpenMP optimizations involving folding."), cl::Hidden, cl::init(false))
static bool shouldSpecializeIndirectCallee(Attributor &, const AbstractAttribute &, CallBase &, Function &, unsigned NumAssumedCallees)
Bound the if-cascade AAIndirectCallInfo builds for an indirect call.
static cl::opt< bool > PrintModuleBeforeOptimizations("openmp-opt-print-module-before", cl::desc("Print the current module before OpenMP optimizations."), cl::Hidden, cl::init(false))
static cl::opt< unsigned > SetFixpointIterations("openmp-opt-max-iterations", cl::Hidden, cl::desc("Maximal number of attributor iterations."), cl::init(256))
static cl::opt< bool > DisableInternalization("openmp-opt-disable-internalization", cl::desc("Disable function internalization."), cl::Hidden, cl::init(false))
static cl::opt< bool > PrintICVValues("openmp-print-icv-values", cl::init(false), cl::Hidden)
static cl::opt< bool > DisableOpenMPOptimizations("openmp-opt-disable", cl::desc("Disable OpenMP specific optimizations."), cl::Hidden, cl::init(false))
static cl::opt< unsigned > SharedMemoryLimit("openmp-opt-shared-limit", cl::Hidden, cl::desc("Maximum amount of shared memory to use."), cl::init(std::numeric_limits< unsigned >::max()))
static cl::opt< bool > EnableVerboseRemarks("openmp-opt-verbose-remarks", cl::desc("Enables more verbose remarks."), cl::Hidden, cl::init(false))
static cl::opt< unsigned > MaxCalleesForSpecialization("openmp-opt-max-callees-for-specialization", cl::Hidden, cl::desc("Number of possible callees above which an indirect call site is " "left alone rather than specialized into an if-cascade."), cl::init(3))
static cl::opt< bool > DisableOpenMPOptDeglobalization("openmp-opt-disable-deglobalization", cl::desc("Disable OpenMP optimizations involving deglobalization."), cl::Hidden, cl::init(false))
static cl::opt< bool > DisableOpenMPOptBarrierElimination("openmp-opt-disable-barrier-elimination", cl::desc("Disable OpenMP optimizations that eliminate barriers."), cl::Hidden, cl::init(false))
static cl::opt< bool > DeduceICVValues("openmp-deduce-icv-values", cl::init(false), cl::Hidden)
#define KERNEL_ENVIRONMENT_IDX(MEMBER, IDX)
#define KERNEL_ENVIRONMENT_GETTER(MEMBER, RETURNTYPE)
static cl::opt< bool > DisableOpenMPOptSPMDization("openmp-opt-disable-spmdization", cl::desc("Disable OpenMP optimizations involving SPMD-ization."), cl::Hidden, cl::init(false))
static cl::opt< bool > AlwaysInlineDeviceFunctions("openmp-opt-inline-device", cl::desc("Inline all applicable functions on the device."), cl::Hidden, cl::init(false))
FunctionAnalysisManager FAM
This file builds on the ADT/GraphTraits.h file to build a generic graph post order iterator.
static StringRef getName(Value *V)
Remove Loads Into Fake Uses
std::pair< BasicBlock *, BasicBlock * > Edge
static bool contains(SmallPtrSetImpl< ConstantExpr * > &Cache, ConstantExpr *Expr, Constant *C)
This file implements a set that has insertion order iteration characteristics.
This file defines the SmallPtrSet class.
This file defines the SmallVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
static void initialize(TargetLibraryInfoImpl &TLI, const Triple &T, const llvm::StringTable &StandardNames, VectorLibrary VecLib)
Initialize the set of available library functions based on the specified target triple.
static cl::opt< unsigned > MaxThreads("xcore-max-threads", cl::desc("Maximum number of threads (for emulation thread-local storage)"), cl::Hidden, cl::value_desc("number"), cl::init(8))
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
size_t size() const
Get the array size.
iterator begin()
Instruction iterator methods.
LLVM_ABI const_iterator getFirstInsertionPt() const
Returns an iterator to the first instruction in this block that is suitable for inserting a non-PHI i...
LLVM_ABI BasicBlock * splitBasicBlock(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction.
const Function * getParent() const
Return the enclosing method, or null if none.
reverse_iterator rbegin()
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
LLVM_ABI const BasicBlock * getUniqueSuccessor() const
Return the successor of this block if it has a unique successor.
InstListType::reverse_iterator reverse_iterator
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
void setCallingConv(CallingConv::ID CC)
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
bool doesNotAccessMemory(unsigned OpNo) const
bool hasFnAttr(Attribute::AttrKind Kind) const
Determine whether this call has the given attribute.
LLVM_ABI bool isIndirectCall() const
Return true if the callsite is an indirect call.
bool isCallee(Value::const_user_iterator UI) const
Determine whether the passed iterator points to the callee operand's Use.
Value * getCalledOperand() const
Value * getArgOperand(unsigned i) const
void setArgOperand(unsigned i, Value *v)
iterator_range< User::op_iterator > args()
Iteration adapter for range-for loops.
unsigned getArgOperandNo(const Use *U) const
Given a use for a arg operand, get the arg operand number that corresponds to it.
unsigned arg_size() const
AttributeList getAttributes() const
Return the attributes for this call.
void addParamAttr(unsigned ArgNo, Attribute::AttrKind Kind)
Adds the attribute to the indicated argument.
bool isArgOperand(const Use *U) const
bool hasOperandBundles() const
Return true if this User has any operand bundles.
LLVM_ABI Function * getCaller()
Helper to get the caller (the parent function).
Wrapper to unify "old style" CallGraph and "new style" LazyCallGraph.
void initialize(LazyCallGraph &LCG, LazyCallGraph::SCC &SCC, CGSCCAnalysisManager &AM, CGSCCUpdateResult &UR)
Initializers for usage outside of a CGSCC pass, inside a CGSCC pass in the old and new pass manager (...
static CallInst * Create(FunctionType *Ty, Value *F, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
@ ICMP_SLT
signed less than
static CondBrInst * Create(Value *Cond, BasicBlock *IfTrue, BasicBlock *IfFalse, InsertPosition InsertBefore=nullptr)
static LLVM_ABI Constant * getPointerCast(Constant *C, Type *Ty)
Create a BitCast, AddrSpaceCast, or a PtrToInt cast constant expression.
static LLVM_ABI Constant * getPointerBitCastOrAddrSpaceCast(Constant *C, Type *Ty)
Create a BitCast or AddrSpaceCast for a pointer type depending on the address space.
This is the shared class of boolean and integer constants.
IntegerType * getIntegerType() const
Variant of the getType() method to always return an IntegerType, which reduces the amount of casting ...
static LLVM_ABI ConstantInt * getTrue(LLVMContext &Context)
bool isZero() const
This is just a convenience method to make client code smaller for a common code.
int64_t getSExtValue() const
Return the constant as a 64-bit integer value after it has been sign extended as appropriate for the ...
static LLVM_ABI ConstantPointerNull * get(PointerType *T)
Static factory methods - Return objects of the specified value.
This is an important base class in LLVM.
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
ValueT lookup(const_arg_type_t< KeyT > Val) const
Return the entry for the specified key, or a default constructed value if no such entry exists.
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
LLVM_ABI Instruction * findNearestCommonDominator(Instruction *I1, Instruction *I2) const
Find the nearest instruction I that dominates both I1 and I2, in the sense that a result produced bef...
static ErrorSuccess success()
Create a success value.
AtomicOrdering getOrdering() const
Returns the ordering constraint of this fence instruction.
A proxy from a FunctionAnalysisManager to an SCC.
const BasicBlock & getEntryBlock() const
const BasicBlock & front() const
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Argument * getArg(unsigned i) const
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
LLVM_ABI bool isDeclaration() const
Return true if the primary definition of this global value is outside of the current translation unit...
bool hasLocalLinkage() const
Module * getParent()
Get the module that this global value is contained inside of...
@ PrivateLinkage
Like Internal, but omit from symbol table.
@ InternalLinkage
Rename collisions when linking (static functions).
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
LLVM_ABI void setInitializer(Constant *InitVal)
setInitializer - Sets the initializer for this global variable, removing any existing initializer if ...
CondBrInst * CreateCondBr(Value *Cond, BasicBlock *True, BasicBlock *False, MDNode *BranchWeights=nullptr, MDNode *Unpredictable=nullptr)
Create a conditional 'br Cond, TrueDest, FalseDest' instruction.
CallInst * CreateCall(FunctionType *FTy, Value *Callee, ArrayRef< Value * > Args={}, const Twine &Name="", MDNode *FPMathTag=nullptr)
Value * CreateIsNull(Value *Arg, const Twine &Name="")
Return a boolean value testing if Arg == 0.
LLVM_ABI bool isLifetimeStartOrEnd() const LLVM_READONLY
Return true if the instruction is a llvm.lifetime.start or llvm.lifetime.end marker.
LLVM_ABI bool mayWriteToMemory() const LLVM_READONLY
Return true if this instruction may modify memory.
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
LLVM_ABI const Function * getFunction() const
Return the function this instruction belongs to.
LLVM_ABI bool mayHaveSideEffects() const LLVM_READONLY
Return true if the instruction may have side effects.
LLVM_ABI bool mayReadFromMemory() const LLVM_READONLY
Return true if this instruction may read memory.
iterator_range< user_iterator > users()
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
LLVM_ABI void setSuccessor(unsigned Idx, BasicBlock *BB)
Update the specified successor to point at the provided block.
LLVM_ABI const DiagnosticHandler * getDiagHandlerPtr() const
getDiagHandlerPtr - Returns const raw pointer of DiagnosticHandler set by setDiagnosticHandler.
A node in the call graph.
An SCC of the call graph.
A lazily constructed view of the call graph of a module.
const MDOperand & getOperand(unsigned I) const
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
unsigned getNumOperands() const
Return number of MDNode operands.
LLVM_ABI void eraseFromParent()
This method unlinks 'this' from the containing function and deletes it.
LLVM_ABI StringRef getName() const
Return the name of the corresponding LLVM basic block, or an empty string.
A Module instance is used to store all the information related to an LLVM module.
const Triple & getTargetTriple() const
Get the target triple which is a string describing the target host.
LLVM_ABI Constant * getOrCreateIdent(Constant *SrcLocStr, uint32_t SrcLocStrSize, omp::IdentFlag Flags=omp::IdentFlag(0), unsigned Reserve2Flags=0)
Return an ident_t* encoding the source location SrcLocStr and Flags.
LLVM_ABI FunctionCallee getOrCreateRuntimeFunction(Module &M, omp::RuntimeFunction FnID)
Return the function declaration for the runtime function with FnID.
static LLVM_ABI std::pair< int32_t, int32_t > readThreadBoundsForKernel(const Triple &T, Function &Kernel)
}
LLVM_ABI Constant * getOrCreateSrcLocStr(StringRef LocStr, uint32_t &SrcLocStrSize)
Return the (LLVM-IR) string describing the source location LocStr.
IRBuilder<>::InsertPoint InsertPointTy
Type used throughout for insertion points.
IRBuilder Builder
The LLVM-IR Builder used to create IR.
static LLVM_ABI std::pair< int32_t, int32_t > readTeamBoundsForKernel(const Triple &T, Function &Kernel)
Read/write a bounds on teams for Kernel.
bool updateToLocation(const LocationDescription &Loc)
Update the internal location to Loc.
LLVM_ABI PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG, CGSCCUpdateResult &UR)
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
static ReturnInst * Create(LLVMContext &C, Value *retVal=nullptr, InsertPosition InsertBefore=nullptr)
A vector that has set insertion semantics.
size_type size() const
Determine the number of elements in the SetVector.
size_type count(const_arg_type key) const
Count the number of elements of a given key in the SetVector.
bool insert(const value_type &X)
Insert a new element into the SetVector.
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
reference emplace_back(ArgTypes &&... Args)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Triple - Helper class for working with autoconf configuration names.
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
LLVM_ABI unsigned getPointerAddressSpace() const
Get the address space of this pointer or pointer vector type.
static UncondBrInst * Create(BasicBlock *Target, InsertPosition InsertBefore=nullptr)
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
A Use represents the edge between a Value definition and its users.
LLVM_ABI bool replaceUsesOfWith(Value *From, Value *To)
Replace uses of one Value with another.
Type * getType() const
All values are typed, get the type of this value.
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
bool hasOneUse() const
Return true if there is exactly one use of this value.
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
iterator_range< user_iterator > users()
LLVM_ABI const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
const ParentTy * getParent() const
self_iterator getIterator()
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
GlobalVariable * getKernelEnvironementGVFromKernelInitCB(CallBase *KernelInitCB)
ConstantStruct * getKernelEnvironementFromKernelInitCB(CallBase *KernelInitCB)
Abstract Attribute helper functions.
LLVM_ABI bool isValidAtPosition(const ValueAndContext &VAC, InformationCache &InfoCache)
Return true if the value of VAC is a valid at the position of VAC, that is a constant,...
LLVM_ABI bool isPotentiallyAffectedByBarrier(Attributor &A, const Instruction &I, const AbstractAttribute &QueryingAA)
Return true if I is potentially affected by a barrier.
LLVM_ABI bool isNoSyncInst(Attributor &A, const Instruction &I, const AbstractAttribute &QueryingAA)
Return true if I is a nosync instruction.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
E & operator^=(E &LHS, E RHS)
@ BasicBlock
Various leaf nodes.
initializer< Ty > init(const Ty &Val)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
constexpr uint64_t PointerSize
aarch64 pointer size.
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
LLVM_ABI bool isOpenMPDevice(Module &M)
Helper to determine if M is a OpenMP target offloading device module.
LLVM_ABI bool containsOpenMP(Module &M)
Helper to determine if M contains OpenMP.
InternalControlVar
IDs for all Internal Control Variables (ICVs).
RuntimeFunction
IDs for all omp runtime library (RTL) functions.
LLVM_ABI KernelSet getDeviceKernels(Module &M)
Get OpenMP device kernels in M.
@ OMP_TGT_EXEC_MODE_GENERIC_SPMD
@ OMP_TGT_EXEC_MODE_GENERIC
SetVector< Kernel > KernelSet
Set of kernels in the module.
Function * Kernel
Summary of a kernel (=entry point for target offloading).
LLVM_ABI bool isOpenMPKernel(Function &Fn)
Return true iff Fn is an OpenMP GPU kernel; Fn has the "kernel" attribute.
DiagnosticInfoOptimizationBase::Argument NV
NodeAddr< UseNode * > Use
friend class Instruction
Iterator for Instructions in a `BasicBlock.
LLVM_ABI iterator begin() const
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
bool succ_empty(const Instruction *I)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
LLVM_ABI bool isRemovableAlloc(const CallBase *V, const TargetLibraryInfo *TLI)
Return true if this is a call to an allocation function that does not have side effects that we are r...
bool operator!=(uint64_t V1, const APInt &V2)
constexpr from_range_t from_range
Value * GetPointerBaseWithConstantOffset(Value *Ptr, int64_t &Offset, const DataLayout &DL, bool AllowNonInbounds=true)
Analyze the specified pointer to see if it can be expressed as a base pointer plus a constant offset.
InnerAnalysisManagerProxy< FunctionAnalysisManager, Module > FunctionAnalysisManagerModuleProxy
Provide the FunctionAnalysisManager to Module proxy.
bool operator==(const AddressRangeValuePair &LHS, const AddressRangeValuePair &RHS)
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
AnalysisManager< LazyCallGraph::SCC, LazyCallGraph & > CGSCCAnalysisManager
The CGSCC analysis manager.
@ ThinLTOPostLink
ThinLTO postlink (backend compile) phase.
@ FullLTOPostLink
Full LTO postlink (backend compile) phase.
@ ThinLTOPreLink
ThinLTO prelink (summary) phase.
auto dyn_cast_or_null(const Y &Val)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
IRBuilder(LLVMContext &, FolderTy, InserterTy) -> IRBuilder< FolderTy, InserterTy >
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
LLVM_ABI const Value * getUnderlyingObject(const Value *V, unsigned MaxLookup=MaxLookupSearchDepth, bool MustPreserveProvenance=false)
This method strips off any GEP address adjustments, pointer casts or llvm.threadlocal....
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
MutableArrayRef(T &OneElt) -> MutableArrayRef< T >
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
bool operator&=(SparseBitVector< ElementSize > *LHS, const SparseBitVector< ElementSize > &RHS)
LLVM_ABI BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="")
Split the specified block at the specified instruction.
auto count(R &&Range, const E &Element)
Wrapper function around std::count to count the number of times an element Element occurs in the give...
ArrayRef(const T &OneElt) -> ArrayRef< T >
LLVM_ABI Value * getFreedOperand(const CallBase *CB, const TargetLibraryInfo *TLI)
If this if a call to a free function, return the freed operand.
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
auto predecessors(const MachineBasicBlock *BB)
LLVM_ABI Constant * ConstantFoldInsertValueInstruction(Constant *Agg, Constant *Val, ArrayRef< unsigned > Idxs)
Attempt to constant fold an insertvalue instruction with the specified operands and indices.
@ OPTIONAL
The target may be valid if the source is not.
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
BumpPtrAllocatorImpl<> BumpPtrAllocator
The standard BumpPtrAllocator which just uses the default template parameters.
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
static LLVM_ABI AAExecutionDomain & createForPosition(const IRPosition &IRP, Attributor &A)
Create an abstract attribute view for the position IRP.
AAExecutionDomain(const IRPosition &IRP, Attributor &A)
static LLVM_ABI const char ID
Unique ID (due to the unique address)
AccessKind
Simple enum to distinguish read/write/read-write accesses.
StateType::base_t MemoryLocationsKind
static LLVM_ABI bool isAlignedBarrier(const CallBase &CB, bool ExecutedAligned)
Helper function to determine if CB is an aligned (GPU) barrier.
Base struct for all "concrete attribute" deductions.
virtual const char * getIdAddr() const =0
This function should return the address of the ID of the AbstractAttribute.
An interface to query the internal state of an abstract attribute.
Wrapper for FunctionAnalysisManager.
Configuration for the Attributor.
std::function< void(Attributor &A, const Function &F)> InitializationCallback
Callback function to be invoked on internal functions marked live.
std::optional< unsigned > MaxFixpointIterations
Maximum number of iterations to run until fixpoint.
bool RewriteSignatures
Flag to determine if we rewrite function signatures.
OptimizationRemarkGetter OREGetter
IPOAmendableCBTy IPOAmendableCB
bool IsModulePass
Is the user of the Attributor a module pass or not.
std::function< bool(Attributor &A, const AbstractAttribute &AA, CallBase &CB, Function &AssumedCallee, unsigned NumAssumedCallees)> IndirectCalleeSpecializationCallback
Callback function to determine if an indirect call targets should be made direct call targets (with a...
bool DefaultInitializeLiveInternals
Flag to determine if we want to initialize all default AAs for an internal function marked live.
The fixpoint analysis framework that orchestrates the attribute deduction.
static LLVM_ABI bool isInternalizable(Function &F)
Returns true if the function F can be internalized.
std::function< std::optional< Value * >( const IRPosition &, const AbstractAttribute *, bool &)> SimplifictionCallbackTy
Register CB as a simplification callback.
std::function< std::optional< Constant * >( const GlobalVariable &, const AbstractAttribute *, bool &)> GlobalVariableSimplifictionCallbackTy
Register CB as a simplification callback.
std::function< bool(Attributor &, const AbstractAttribute *)> VirtualUseCallbackTy
static LLVM_ABI bool internalizeFunctions(SmallPtrSetImpl< Function * > &FnSet, DenseMap< Function *, Function * > &FnMap)
Make copies of each function in the set FnSet such that the copied version has internal linkage after...
Simple wrapper for a single bit (boolean) state.
Support structure for SCC passes to communicate updates the call graph back to the CGSCC pass manager...
bool isAnyRemarkEnabled(StringRef PassName) const
Return true if any type of remarks are enabled for this pass.
Helper to describe and deal with positions in the LLVM-IR.
static const IRPosition callsite_returned(const CallBase &CB)
Create a position describing the returned value of CB.
static const IRPosition returned(const Function &F, const CallBaseContext *CBContext=nullptr)
Create a position describing the returned value of F.
static const IRPosition value(const Value &V, const CallBaseContext *CBContext=nullptr)
Create a position describing the value of V.
static const IRPosition inst(const Instruction &I, const CallBaseContext *CBContext=nullptr)
Create a position describing the instruction I.
@ IRP_ARGUMENT
An attribute for a function argument.
@ IRP_RETURNED
An attribute for the function return value.
@ IRP_CALL_SITE
An attribute for a call site (function scope).
@ IRP_CALL_SITE_RETURNED
An attribute for a call site return value.
@ IRP_FUNCTION
An attribute for a function (scope).
@ IRP_FLOAT
A position that is not associated with a spot suitable for attributes.
@ IRP_CALL_SITE_ARGUMENT
An attribute for a call site argument.
@ IRP_INVALID
An invalid position.
static const IRPosition function(const Function &F, const CallBaseContext *CBContext=nullptr)
Create a position describing the function scope of F.
Kind getPositionKind() const
Return the associated position kind.
static const IRPosition callsite_function(const CallBase &CB)
Create a position describing the function scope of CB.
Defines various target-specific GPU grid values that must be consistent between host RTL (plugin),...