51#include "llvm/IR/IntrinsicsAMDGPU.h"
52#include "llvm/IR/IntrinsicsNVPTX.h"
68#define DEBUG_TYPE "openmp-opt"
71 "openmp-opt-disable",
cl::desc(
"Disable OpenMP specific optimizations."),
75 "openmp-opt-enable-merging",
81 cl::desc(
"Disable function internalization."),
92 "openmp-hide-memory-transfer-latency",
93 cl::desc(
"[WIP] Tries to hide the latency of host to device memory"
98 "openmp-opt-disable-deglobalization",
99 cl::desc(
"Disable OpenMP optimizations involving deglobalization."),
103 "openmp-opt-disable-spmdization",
104 cl::desc(
"Disable OpenMP optimizations involving SPMD-ization."),
108 "openmp-opt-disable-folding",
113 "openmp-opt-disable-state-machine-rewrite",
114 cl::desc(
"Disable OpenMP optimizations that replace the state machine."),
118 "openmp-opt-disable-barrier-elimination",
119 cl::desc(
"Disable OpenMP optimizations that eliminate barriers."),
123 "openmp-opt-print-module-after",
124 cl::desc(
"Print the current module after OpenMP optimizations."),
128 "openmp-opt-print-module-before",
129 cl::desc(
"Print the current module before OpenMP optimizations."),
133 "openmp-opt-inline-device",
144 cl::desc(
"Maximal number of attributor iterations."),
149 cl::desc(
"Maximum amount of shared memory to use."),
150 cl::init(std::numeric_limits<unsigned>::max()));
153 "Number of OpenMP runtime calls deduplicated");
155 "Number of OpenMP parallel regions deleted");
157 "Number of OpenMP runtime functions identified");
159 "Number of OpenMP runtime function uses identified");
161 "Number of OpenMP target region entry points (=kernels) identified");
163 "Number of non-OpenMP target region kernels identified");
165 "Number of OpenMP target region entry points (=kernels) executed in "
166 "SPMD-mode instead of generic-mode");
167STATISTIC(NumOpenMPTargetRegionKernelsWithoutStateMachine,
168 "Number of OpenMP target region entry points (=kernels) executed in "
169 "generic-mode without a state machines");
170STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback,
171 "Number of OpenMP target region entry points (=kernels) executed in "
172 "generic-mode with customized state machines with fallback");
173STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback,
174 "Number of OpenMP target region entry points (=kernels) executed in "
175 "generic-mode with customized state machines without fallback");
177 NumOpenMPParallelRegionsReplacedInGPUStateMachine,
178 "Number of OpenMP parallel regions replaced with ID in GPU state machines");
180 "Number of OpenMP parallel regions merged");
182 "Amount of memory pushed to shared memory");
183STATISTIC(NumBarriersEliminated,
"Number of redundant barriers eliminated");
211#define KERNEL_ENVIRONMENT_IDX(MEMBER, IDX) \
212 constexpr unsigned MEMBER##Idx = IDX;
217#undef KERNEL_ENVIRONMENT_IDX
219#define KERNEL_ENVIRONMENT_CONFIGURATION_IDX(MEMBER, IDX) \
220 constexpr unsigned MEMBER##Idx = IDX;
230#undef KERNEL_ENVIRONMENT_CONFIGURATION_IDX
232#define KERNEL_ENVIRONMENT_GETTER(MEMBER, RETURNTYPE) \
233 RETURNTYPE *get##MEMBER##FromKernelEnvironment(ConstantStruct *KernelEnvC) { \
234 return cast<RETURNTYPE>(KernelEnvC->getAggregateElement(MEMBER##Idx)); \
240#undef KERNEL_ENVIRONMENT_GETTER
242#define KERNEL_ENVIRONMENT_CONFIGURATION_GETTER(MEMBER) \
243 ConstantInt *get##MEMBER##FromKernelEnvironment( \
244 ConstantStruct *KernelEnvC) { \
245 ConstantStruct *ConfigC = \
246 getConfigurationFromKernelEnvironment(KernelEnvC); \
247 return dyn_cast<ConstantInt>(ConfigC->getAggregateElement(MEMBER##Idx)); \
258#undef KERNEL_ENVIRONMENT_CONFIGURATION_GETTER
262 constexpr int InitKernelEnvironmentArgNo = 0;
277struct AAHeapToShared;
284 OMPInformationCache(
Module &M, AnalysisGetter &AG,
288 OpenMPPostLink(OpenMPPostLink) {
291 const Triple
T(OMPBuilder.M.getTargetTriple());
292 switch (
T.getArch()) {
296 assert(OMPBuilder.Config.IsTargetDevice &&
297 "OpenMP AMDGPU/NVPTX is only prepared to deal with device code.");
298 OMPBuilder.Config.IsGPU =
true;
301 OMPBuilder.Config.IsGPU =
false;
304 OMPBuilder.initialize();
305 initializeRuntimeFunctions(M);
306 initializeInternalControlVars();
310 struct InternalControlVarInfo {
318 StringRef EnvVarName;
324 ConstantInt *InitValue;
337 struct RuntimeFunctionInfo {
358 using UseVector = SmallVector<Use *, 16>;
361 void clearUsesMap() { UsesMap.clear(); }
364 operator bool()
const {
return Declaration; }
367 UseVector &getOrCreateUseVector(Function *
F) {
368 std::shared_ptr<UseVector> &UV = UsesMap[
F];
370 UV = std::make_shared<UseVector>();
376 const UseVector *getUseVector(Function &
F)
const {
377 auto I = UsesMap.find(&
F);
378 if (
I != UsesMap.end())
379 return I->second.get();
384 size_t getNumFunctionsWithUses()
const {
return UsesMap.size(); }
388 size_t getNumArgs()
const {
return ArgumentTypes.size(); }
393 void foreachUse(SmallVectorImpl<Function *> &SCC,
394 function_ref<
bool(Use &, Function &)> CB) {
395 for (Function *
F : SCC)
401 void foreachUse(function_ref<
bool(Use &, Function &)> CB, Function *
F) {
402 SmallVector<unsigned, 8> ToBeDeleted;
406 UseVector &UV = getOrCreateUseVector(
F);
416 while (!ToBeDeleted.
empty()) {
426 DenseMap<Function *, std::shared_ptr<UseVector>> UsesMap;
430 decltype(UsesMap)::iterator
begin() {
return UsesMap.begin(); }
431 decltype(UsesMap)::iterator
end() {
return UsesMap.end(); }
435 OpenMPIRBuilder OMPBuilder;
439 RuntimeFunction::OMPRTL___last>
443 DenseMap<Function *, RuntimeFunction> RuntimeFunctionIDMap;
447 InternalControlVar::ICV___last>
452 void initializeInternalControlVars() {
453#define ICV_RT_SET(_Name, RTL) \
455 auto &ICV = ICVs[_Name]; \
458#define ICV_RT_GET(Name, RTL) \
460 auto &ICV = ICVs[Name]; \
463#define ICV_DATA_ENV(Enum, _Name, _EnvVarName, Init) \
465 auto &ICV = ICVs[Enum]; \
468 ICV.InitKind = Init; \
469 ICV.EnvVarName = _EnvVarName; \
470 switch (ICV.InitKind) { \
471 case ICV_IMPLEMENTATION_DEFINED: \
472 ICV.InitValue = nullptr; \
475 ICV.InitValue = ConstantInt::get( \
476 Type::getInt32Ty(OMPBuilder.Int32->getContext()), 0); \
479 ICV.InitValue = ConstantInt::getFalse(OMPBuilder.Int1->getContext()); \
485#include "llvm/Frontend/OpenMP/OMPKinds.def"
491 static bool declMatchesRTFTypes(Function *
F,
Type *RTFRetType,
498 if (
F->getReturnType() != RTFRetType)
500 if (
F->arg_size() != RTFArgTypes.
size())
503 auto *RTFTyIt = RTFArgTypes.
begin();
504 for (Argument &Arg :
F->args()) {
505 if (Arg.getType() != *RTFTyIt)
515 unsigned collectUses(RuntimeFunctionInfo &RFI,
bool CollectStats =
true) {
516 unsigned NumUses = 0;
517 if (!RFI.Declaration)
519 OMPBuilder.addAttributes(RFI.Kind, *RFI.Declaration);
522 NumOpenMPRuntimeFunctionsIdentified += 1;
523 NumOpenMPRuntimeFunctionUsesIdentified += RFI.Declaration->getNumUses();
527 for (Use &U : RFI.Declaration->uses()) {
529 if (!
CGSCC ||
CGSCC->empty() ||
CGSCC->contains(UserI->getFunction())) {
530 RFI.getOrCreateUseVector(UserI->getFunction()).push_back(&U);
534 RFI.getOrCreateUseVector(
nullptr).push_back(&U);
543 auto &RFI = RFIs[RTF];
545 collectUses(RFI,
false);
549 void recollectUses() {
550 for (
int Idx = 0; Idx < RFIs.size(); ++Idx)
555 void setCallingConvention(FunctionCallee Callee, CallInst *CI) {
570 RuntimeFunctionInfo &RFI = RFIs[Fn];
572 if (!RFI.Declaration || RFI.Declaration->isDeclaration())
580 void initializeRuntimeFunctions(
Module &M) {
583#define OMP_TYPE(VarName, ...) \
584 Type *VarName = OMPBuilder.VarName; \
587#define OMP_ARRAY_TYPE(VarName, ...) \
588 ArrayType *VarName##Ty = OMPBuilder.VarName##Ty; \
590 PointerType *VarName##PtrTy = OMPBuilder.VarName##PtrTy; \
591 (void)VarName##PtrTy;
593#define OMP_FUNCTION_TYPE(VarName, ...) \
594 FunctionType *VarName = OMPBuilder.VarName; \
596 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \
599#define OMP_STRUCT_TYPE(VarName, ...) \
600 StructType *VarName = OMPBuilder.VarName; \
602 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \
605#define OMP_RTL(_Enum, _Name, _IsVarArg, _ReturnType, ...) \
607 SmallVector<Type *, 8> ArgsTypes({__VA_ARGS__}); \
608 Function *F = M.getFunction(_Name); \
609 RTLFunctions.insert(F); \
610 if (declMatchesRTFTypes(F, OMPBuilder._ReturnType, ArgsTypes)) { \
611 RuntimeFunctionIDMap[F] = _Enum; \
612 auto &RFI = RFIs[_Enum]; \
615 RFI.IsVarArg = _IsVarArg; \
616 RFI.ReturnType = OMPBuilder._ReturnType; \
617 RFI.ArgumentTypes = std::move(ArgsTypes); \
618 RFI.Declaration = F; \
619 unsigned NumUses = collectUses(RFI); \
622 dbgs() << TAG << RFI.Name << (RFI.Declaration ? "" : " not") \
624 if (RFI.Declaration) \
625 dbgs() << TAG << "-> got " << NumUses << " uses in " \
626 << RFI.getNumFunctionsWithUses() \
627 << " different functions.\n"; \
631#include "llvm/Frontend/OpenMP/OMPKinds.def"
636 for (Function &
F : M) {
637 for (StringRef Prefix : {
"__kmpc",
"_ZN4ompx",
"omp_"})
638 if (
F.hasFnAttribute(Attribute::NoInline) &&
639 F.getName().starts_with(Prefix) &&
640 !
F.hasFnAttribute(Attribute::OptimizeNone))
641 F.removeFnAttr(Attribute::NoInline);
649 DenseSet<const Function *> RTLFunctions;
652 bool OpenMPPostLink =
false;
659 SmallPtrSet<Function *, 8> SPMDizedKernels;
662template <
typename Ty,
bool InsertInval
idates = true>
664 bool contains(
const Ty &Elem)
const {
return Set.contains(Elem); }
665 bool insert(
const Ty &Elem) {
666 if (InsertInvalidates)
667 BooleanState::indicatePessimisticFixpoint();
668 return Set.insert(Elem);
671 const Ty &operator[](
int Idx)
const {
return Set[Idx]; }
672 bool operator==(
const BooleanStateWithSetVector &
RHS)
const {
673 return BooleanState::operator==(
RHS) && Set ==
RHS.Set;
675 bool operator!=(
const BooleanStateWithSetVector &
RHS)
const {
676 return !(*
this ==
RHS);
679 bool empty()
const {
return Set.empty(); }
680 size_t size()
const {
return Set.size(); }
683 BooleanStateWithSetVector &
operator^=(
const BooleanStateWithSetVector &
RHS) {
684 BooleanState::operator^=(
RHS);
685 Set.insert_range(
RHS.Set);
694 typename decltype(Set)::iterator
begin() {
return Set.begin(); }
695 typename decltype(Set)::iterator
end() {
return Set.end(); }
696 typename decltype(Set)::const_iterator
begin()
const {
return Set.begin(); }
697 typename decltype(Set)::const_iterator
end()
const {
return Set.end(); }
700template <
typename Ty,
bool InsertInval
idates = true>
701using BooleanStateWithPtrSetVector =
702 BooleanStateWithSetVector<Ty *, InsertInvalidates>;
706 bool IsAtFixpoint =
false;
710 BooleanStateWithPtrSetVector<CallBase,
false>
711 ReachedKnownParallelRegions;
714 BooleanStateWithPtrSetVector<CallBase> ReachedUnknownParallelRegions;
719 BooleanStateWithPtrSetVector<Instruction, false> SPMDCompatibilityTracker;
723 CallBase *KernelInitCB =
nullptr;
727 ConstantStruct *KernelEnvC =
nullptr;
731 CallBase *KernelDeinitCB =
nullptr;
734 bool IsKernelEntry =
false;
737 BooleanStateWithPtrSetVector<Function, false> ReachingKernelEntries;
742 BooleanStateWithSetVector<uint8_t> ParallelLevels;
745 bool NestedParallelism =
false;
750 KernelInfoState() =
default;
751 KernelInfoState(
bool BestState) {
753 indicatePessimisticFixpoint();
757 bool isValidState()
const override {
return true; }
760 bool isAtFixpoint()
const override {
return IsAtFixpoint; }
765 ParallelLevels.indicatePessimisticFixpoint();
766 ReachingKernelEntries.indicatePessimisticFixpoint();
767 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
768 ReachedKnownParallelRegions.indicatePessimisticFixpoint();
769 ReachedUnknownParallelRegions.indicatePessimisticFixpoint();
770 NestedParallelism =
true;
771 return ChangeStatus::CHANGED;
777 ParallelLevels.indicateOptimisticFixpoint();
778 ReachingKernelEntries.indicateOptimisticFixpoint();
779 SPMDCompatibilityTracker.indicateOptimisticFixpoint();
780 ReachedKnownParallelRegions.indicateOptimisticFixpoint();
781 ReachedUnknownParallelRegions.indicateOptimisticFixpoint();
782 return ChangeStatus::UNCHANGED;
786 KernelInfoState &getAssumed() {
return *
this; }
787 const KernelInfoState &getAssumed()
const {
return *
this; }
790 if (SPMDCompatibilityTracker !=
RHS.SPMDCompatibilityTracker)
792 if (ReachedKnownParallelRegions !=
RHS.ReachedKnownParallelRegions)
794 if (ReachedUnknownParallelRegions !=
RHS.ReachedUnknownParallelRegions)
796 if (ReachingKernelEntries !=
RHS.ReachingKernelEntries)
798 if (ParallelLevels !=
RHS.ParallelLevels)
800 if (NestedParallelism !=
RHS.NestedParallelism)
806 bool mayContainParallelRegion() {
807 return !ReachedKnownParallelRegions.empty() ||
808 !ReachedUnknownParallelRegions.empty();
812 static KernelInfoState getBestState() {
return KernelInfoState(
true); }
814 static KernelInfoState getBestState(KernelInfoState &KIS) {
815 return getBestState();
819 static KernelInfoState getWorstState() {
return KernelInfoState(
false); }
822 KernelInfoState
operator^=(
const KernelInfoState &KIS) {
824 if (KIS.KernelInitCB) {
825 if (KernelInitCB && KernelInitCB != KIS.KernelInitCB)
828 KernelInitCB = KIS.KernelInitCB;
830 if (KIS.KernelDeinitCB) {
831 if (KernelDeinitCB && KernelDeinitCB != KIS.KernelDeinitCB)
834 KernelDeinitCB = KIS.KernelDeinitCB;
836 if (KIS.KernelEnvC) {
837 if (KernelEnvC && KernelEnvC != KIS.KernelEnvC)
840 KernelEnvC = KIS.KernelEnvC;
842 SPMDCompatibilityTracker ^= KIS.SPMDCompatibilityTracker;
843 ReachedKnownParallelRegions ^= KIS.ReachedKnownParallelRegions;
844 ReachedUnknownParallelRegions ^= KIS.ReachedUnknownParallelRegions;
845 NestedParallelism |= KIS.NestedParallelism;
849 KernelInfoState
operator&=(
const KernelInfoState &KIS) {
850 return (*
this ^= KIS);
860 AllocaInst *Array =
nullptr;
862 SmallVector<Value *, 8> StoredValues;
864 SmallVector<StoreInst *, 8> LastAccesses;
866 OffloadArray() =
default;
872 bool initialize(AllocaInst &Array, Instruction &Before) {
873 if (!getValues(Array, Before))
876 this->Array = &Array;
880 static const unsigned DeviceIDArgNum = 1;
881 static const unsigned BasePtrsArgNum = 3;
882 static const unsigned PtrsArgNum = 4;
883 static const unsigned SizesArgNum = 5;
889 bool getValues(AllocaInst &Array, Instruction &Before) {
891 const DataLayout &
DL = Array.getDataLayout();
892 std::optional<TypeSize> ArraySize = Array.getAllocationSize(
DL);
893 if (!ArraySize || !ArraySize->isFixed())
896 const uint64_t NumValues = ArraySize->getFixedValue() /
PointerSize;
897 StoredValues.assign(NumValues,
nullptr);
898 LastAccesses.assign(NumValues,
nullptr);
906 for (Instruction &
I : *BB) {
920 if ((uint64_t)Idx < NumValues) {
922 LastAccesses[Idx] = S;
933 const unsigned NumValues = StoredValues.size();
934 for (
unsigned I = 0;
I < NumValues; ++
I) {
935 if (!StoredValues[
I] || !LastAccesses[
I])
945 using OptimizationRemarkGetter =
946 function_ref<OptimizationRemarkEmitter &(
Function *)>;
948 OpenMPOpt(SmallVectorImpl<Function *> &SCC, CallGraphUpdater &CGUpdater,
949 OptimizationRemarkGetter OREGetter,
950 OMPInformationCache &OMPInfoCache, Attributor &A)
951 : M(*(*SCC.
begin())->
getParent()), SCC(SCC), CGUpdater(CGUpdater),
952 OREGetter(OREGetter), OMPInfoCache(OMPInfoCache), A(A) {}
955 bool remarksEnabled() {
956 auto &Ctx = M.getContext();
957 return Ctx.getDiagHandlerPtr()->isAnyRemarkEnabled(
DEBUG_TYPE);
961 bool run(
bool IsModulePass) {
971 Changed |= runAttributor(IsModulePass);
974 OMPInfoCache.recollectUses();
977 Changed |= rewriteDeviceCodeStateMachine();
983 Changed |= removeSPMDParallelWrappers();
985 if (remarksEnabled())
986 analysisGlobalization();
993 Changed |= runAttributor(IsModulePass);
996 OMPInfoCache.recollectUses();
998 Changed |= deleteParallelRegions();
1001 Changed |= hideMemTransfersLatency();
1002 Changed |= deduplicateRuntimeCalls();
1004 if (mergeParallelRegions()) {
1005 deduplicateRuntimeCalls();
1011 if (OMPInfoCache.OpenMPPostLink)
1012 Changed |= removeRuntimeSymbols();
1019 void printICVs()
const {
1023 for (Function *
F : SCC) {
1024 for (
auto ICV : ICVs) {
1025 auto ICVInfo = OMPInfoCache.ICVs[ICV];
1026 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
1027 return ORA <<
"OpenMP ICV " <<
ore::NV(
"OpenMPICV", ICVInfo.Name)
1029 << (ICVInfo.InitValue
1030 ?
toString(ICVInfo.InitValue->getValue(), 10,
true)
1031 :
"IMPLEMENTATION_DEFINED");
1040 void printKernels()
const {
1041 for (Function *
F : SCC) {
1045 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
1046 return ORA <<
"OpenMP GPU kernel "
1047 <<
ore::NV(
"OpenMPGPUKernel",
F->getName()) <<
"\n";
1056 static CallInst *getCallIfRegularCall(
1057 Use &U, OMPInformationCache::RuntimeFunctionInfo *RFI =
nullptr) {
1068 static CallInst *getCallIfRegularCall(
1069 Value &V, OMPInformationCache::RuntimeFunctionInfo *RFI =
nullptr) {
1080 bool mergeParallelRegions() {
1081 const unsigned CallbackCalleeOperand = 2;
1082 const unsigned CallbackFirstArgOperand = 3;
1086 OMPInformationCache::RuntimeFunctionInfo &RFI =
1087 OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call];
1089 if (!RFI.Declaration)
1093 OMPInformationCache::RuntimeFunctionInfo UnmergableCallsInfo[] = {
1094 OMPInfoCache.RFIs[OMPRTL___kmpc_push_proc_bind],
1095 OMPInfoCache.RFIs[OMPRTL___kmpc_push_num_threads],
1099 LoopInfo *LI =
nullptr;
1100 DominatorTree *DT =
nullptr;
1102 SmallDenseMap<BasicBlock *, SmallPtrSet<Instruction *, 4>> BB2PRMap;
1104 BasicBlock *StartBB =
nullptr, *EndBB =
nullptr;
1105 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
1107 BasicBlock *CGStartBB = CodeGenIP.getBlock();
1109 SplitBlock(CGStartBB, &*CodeGenIP.getPoint(), DT, LI);
1110 assert(StartBB !=
nullptr &&
"StartBB should not be null");
1112 assert(EndBB !=
nullptr &&
"EndBB should not be null");
1113 EndBB->getTerminator()->setSuccessor(0, CGEndBB);
1117 auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
Value &,
1118 Value &Inner,
Value *&ReplacementValue) -> InsertPointTy {
1119 ReplacementValue = &Inner;
1123 auto FiniCB = [&](InsertPointTy CodeGenIP) {
return Error::success(); };
1127 auto CreateSequentialRegion = [&](
Function *OuterFn,
1133 BasicBlock *ParentBB = SeqStartI->getParent();
1135 SplitBlock(ParentBB, SeqEndI->getNextNode(), DT, LI);
1139 SplitBlock(ParentBB, SeqStartI, DT, LI,
nullptr,
"seq.par.merged");
1142 "Expected a different CFG");
1146 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
1148 BasicBlock *CGStartBB = CodeGenIP.getBlock();
1150 SplitBlock(CGStartBB, &*CodeGenIP.getPoint(), DT, LI);
1151 assert(SeqStartBB !=
nullptr &&
"SeqStartBB should not be null");
1153 assert(SeqEndBB !=
nullptr &&
"SeqEndBB should not be null");
1157 auto FiniCB = [&](InsertPointTy CodeGenIP) {
return Error::success(); };
1161 for (Instruction &
I : *SeqStartBB) {
1162 SmallPtrSet<Instruction *, 4> OutsideUsers;
1163 for (User *Usr :
I.users()) {
1171 OutsideUsers.
insert(&UsrI);
1174 if (OutsideUsers.
empty())
1179 const DataLayout &
DL = M.getDataLayout();
1180 AllocaInst *AllocaI =
new AllocaInst(
1181 I.getType(),
DL.getAllocaAddrSpace(),
nullptr,
1186 new StoreInst(&
I, AllocaI, SeqStartBB->getTerminator()->getIterator());
1190 for (Instruction *UsrI : OutsideUsers) {
1191 LoadInst *LoadI =
new LoadInst(
I.getType(), AllocaI,
1192 I.getName() +
".seq.output.load",
1198 OpenMPIRBuilder::LocationDescription Loc(
1199 InsertPointTy(ParentBB, ParentBB->
end()),
DL);
1201 OMPInfoCache.OMPBuilder.createMaster(Loc, BodyGenCB, FiniCB));
1203 OMPInfoCache.OMPBuilder.createBarrier(SeqAfterIP, OMPD_parallel));
1218 auto Merge = [&](
const SmallVectorImpl<CallInst *> &MergableCIs,
1222 assert(MergableCIs.
size() > 1 &&
"Assumed multiple mergable CIs");
1224 auto Remark = [&](OptimizationRemark
OR) {
1225 OR <<
"Parallel region merged with parallel region"
1226 << (MergableCIs.
size() > 2 ?
"s" :
"") <<
" at ";
1229 if (CI != MergableCIs.
back())
1237 Function *OriginalFn = BB->getParent();
1239 <<
" parallel regions in " << OriginalFn->
getName()
1243 EndBB =
SplitBlock(BB, MergableCIs.
back()->getNextNode(), DT, LI);
1245 SplitBlock(EndBB, &*EndBB->getFirstInsertionPt(), DT, LI);
1249 assert(BB->getUniqueSuccessor() == StartBB &&
"Expected a different CFG");
1250 const DebugLoc DL = BB->getTerminator()->getDebugLoc();
1255 for (
auto *It = MergableCIs.
begin(), *End = MergableCIs.
end() - 1;
1264 CreateSequentialRegion(OriginalFn, BB, ForkCI->
getNextNode(),
1268 OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BB, BB->end()),
1270 IRBuilder<>::InsertPoint AllocaIP(
1276 cantFail(OMPInfoCache.OMPBuilder.createParallel(
1277 Loc, AllocaIP, {}, BodyGenCB, PrivCB, FiniCB,
1278 nullptr,
nullptr, OMP_PROC_BIND_default,
1283 OMPInfoCache.OMPBuilder.finalize(OriginalFn);
1289 SmallVector<Value *, 8>
Args;
1290 for (
auto *CI : MergableCIs) {
1292 FunctionType *FT = OMPInfoCache.OMPBuilder.ParallelTask;
1296 for (
unsigned U = CallbackFirstArgOperand,
E = CI->
arg_size(); U <
E;
1306 for (
unsigned U = CallbackFirstArgOperand,
E = CI->
arg_size(); U <
E;
1310 U - (CallbackFirstArgOperand - CallbackCalleeOperand), A);
1313 if (CI != MergableCIs.back()) {
1316 cantFail(OMPInfoCache.OMPBuilder.createBarrier(
1325 assert(OutlinedFn != OriginalFn &&
"Outlining failed");
1326 CGUpdater.registerOutlinedFunction(*OriginalFn, *OutlinedFn);
1327 CGUpdater.reanalyzeFunction(*OriginalFn);
1329 NumOpenMPParallelRegionsMerged += MergableCIs.size();
1337 CallInst *CI = getCallIfRegularCall(U, &RFI);
1344 RFI.foreachUse(SCC, DetectPRsCB);
1350 for (
auto &It : BB2PRMap) {
1351 auto &CIs = It.getSecond();
1366 auto IsMergable = [&](
Instruction &
I,
bool IsBeforeMergableRegion) {
1369 if (
I.isTerminator())
1376 if (IsBeforeMergableRegion) {
1378 if (!CalledFunction)
1385 for (
const auto &RFI : UnmergableCallsInfo) {
1386 if (CalledFunction == RFI.Declaration)
1401 for (
auto It = BB->
begin(), End = BB->
end(); It != End;) {
1405 if (CIs.count(&
I)) {
1411 if (IsMergable(
I, MergableCIs.
empty()))
1416 for (; It != End; ++It) {
1418 if (CIs.count(&SkipI)) {
1420 <<
" due to " <<
I <<
"\n");
1427 if (MergableCIs.
size() > 1) {
1428 MergableCIsVector.
push_back(MergableCIs);
1430 <<
" parallel regions in block " << BB->
getName()
1435 MergableCIs.
clear();
1438 if (!MergableCIsVector.
empty()) {
1441 for (
auto &MergableCIs : MergableCIsVector)
1442 Merge(MergableCIs, BB);
1443 MergableCIsVector.clear();
1450 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_fork_call);
1451 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_barrier);
1452 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_master);
1453 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_end_master);
1460 bool deleteParallelRegions() {
1461 const unsigned CallbackCalleeOperand = 2;
1463 OMPInformationCache::RuntimeFunctionInfo &RFI =
1464 OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call];
1466 if (!RFI.Declaration)
1471 CallInst *CI = getCallIfRegularCall(U);
1478 if (!Fn->onlyReadsMemory())
1480 if (!Fn->hasFnAttribute(Attribute::WillReturn))
1486 auto Remark = [&](OptimizationRemark
OR) {
1487 return OR <<
"Removing parallel region with no side-effects.";
1493 ++NumOpenMPParallelRegionsDeleted;
1497 RFI.foreachUse(SCC, DeleteCallCB);
1503 bool deduplicateRuntimeCalls() {
1507 OMPRTL_omp_get_num_threads,
1508 OMPRTL_omp_in_parallel,
1509 OMPRTL_omp_get_cancellation,
1510 OMPRTL_omp_get_supported_active_levels,
1511 OMPRTL_omp_get_level,
1512 OMPRTL_omp_get_ancestor_thread_num,
1513 OMPRTL_omp_get_team_size,
1514 OMPRTL_omp_get_active_level,
1515 OMPRTL_omp_in_final,
1516 OMPRTL_omp_get_proc_bind,
1517 OMPRTL_omp_get_num_places,
1518 OMPRTL_omp_get_num_procs,
1519 OMPRTL_omp_get_place_num,
1520 OMPRTL_omp_get_partition_num_places,
1521 OMPRTL_omp_get_partition_place_nums};
1524 SmallSetVector<Value *, 16> GTIdArgs;
1525 collectGlobalThreadIdArguments(GTIdArgs);
1527 <<
" global thread ID arguments\n");
1529 for (Function *
F : SCC) {
1530 for (
auto DeduplicableRuntimeCallID : DeduplicableRuntimeCallIDs)
1531 Changed |= deduplicateRuntimeCalls(
1532 *
F, OMPInfoCache.RFIs[DeduplicableRuntimeCallID]);
1536 Value *GTIdArg =
nullptr;
1537 for (Argument &Arg :
F->args())
1538 if (GTIdArgs.
count(&Arg)) {
1542 Changed |= deduplicateRuntimeCalls(
1543 *
F, OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num], GTIdArg);
1550 bool removeRuntimeSymbols() {
1555 if (GlobalVariable *GV = M.getNamedGlobal(
"__llvm_rpc_client")) {
1556 if (GV->hasNUsesOrMore(1))
1560 GV->eraseFromParent();
1572 bool hideMemTransfersLatency() {
1573 auto &RFI = OMPInfoCache.RFIs[OMPRTL___tgt_target_data_begin_mapper];
1576 auto *RTCall = getCallIfRegularCall(U, &RFI);
1580 OffloadArray OffloadArrays[3];
1581 if (!getValuesInOffloadArrays(*RTCall, OffloadArrays))
1584 LLVM_DEBUG(dumpValuesInOffloadArrays(OffloadArrays));
1587 bool WasSplit =
false;
1588 Instruction *WaitMovementPoint = canBeMovedDownwards(*RTCall);
1589 if (WaitMovementPoint)
1590 WasSplit = splitTargetDataBeginRTC(*RTCall, *WaitMovementPoint);
1595 if (OMPInfoCache.runtimeFnsAvailable(
1596 {OMPRTL___tgt_target_data_begin_mapper_issue,
1597 OMPRTL___tgt_target_data_begin_mapper_wait}))
1598 RFI.foreachUse(SCC, SplitMemTransfers);
1603 void analysisGlobalization() {
1604 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
1606 auto CheckGlobalization = [&](
Use &
U,
Function &Decl) {
1607 if (CallInst *CI = getCallIfRegularCall(U, &RFI)) {
1608 auto Remark = [&](OptimizationRemarkMissed ORM) {
1610 <<
"Found thread data sharing on the GPU. "
1611 <<
"Expect degraded performance due to data globalization.";
1619 RFI.foreachUse(SCC, CheckGlobalization);
1624 bool getValuesInOffloadArrays(CallInst &RuntimeCall,
1626 assert(OAs.
size() == 3 &&
"Need space for three offload arrays!");
1636 Value *BasePtrsArg =
1648 if (!OAs[0].
initialize(*BasePtrsArray, RuntimeCall))
1656 if (!OAs[1].
initialize(*PtrsArray, RuntimeCall))
1668 if (!OAs[2].
initialize(*SizesArray, RuntimeCall))
1679 assert(OAs.
size() == 3 &&
"There are three offload arrays to debug!");
1682 std::string ValuesStr;
1683 raw_string_ostream
Printer(ValuesStr);
1684 std::string Separator =
" --- ";
1686 for (
auto *BP : OAs[0].StoredValues) {
1690 LLVM_DEBUG(
dbgs() <<
"\t\toffload_baseptrs: " << ValuesStr <<
"\n");
1693 for (
auto *
P : OAs[1].StoredValues) {
1700 for (
auto *S : OAs[2].StoredValues) {
1704 LLVM_DEBUG(
dbgs() <<
"\t\toffload_sizes: " << ValuesStr <<
"\n");
1709 Instruction *canBeMovedDownwards(CallInst &RuntimeCall) {
1714 bool IsWorthIt =
false;
1733 return RuntimeCall.
getParent()->getTerminator();
1737 bool splitTargetDataBeginRTC(CallInst &RuntimeCall,
1738 Instruction &WaitMovementPoint) {
1742 auto &
IRBuilder = OMPInfoCache.OMPBuilder;
1745 IRBuilder.Builder.SetInsertPoint(&Entry,
1746 Entry.getFirstNonPHIOrDbgOrAlloca());
1748 IRBuilder.AsyncInfo,
nullptr,
"handle");
1755 FunctionCallee IssueDecl =
IRBuilder.getOrCreateRuntimeFunction(
1756 M, OMPRTL___tgt_target_data_begin_mapper_issue);
1759 SmallVector<Value *, 16>
Args;
1760 for (
auto &Arg : RuntimeCall.
args())
1761 Args.push_back(Arg.get());
1762 Args.push_back(Handle);
1766 OMPInfoCache.setCallingConvention(IssueDecl, IssueCallsite);
1771 FunctionCallee WaitDecl =
IRBuilder.getOrCreateRuntimeFunction(
1772 M, OMPRTL___tgt_target_data_begin_mapper_wait);
1774 Value *WaitParams[2] = {
1776 OffloadArray::DeviceIDArgNum),
1780 WaitDecl, WaitParams,
"", WaitMovementPoint.
getIterator());
1781 OMPInfoCache.setCallingConvention(WaitDecl, WaitCallsite);
1786 static Value *combinedIdentStruct(
Value *CurrentIdent,
Value *NextIdent,
1787 bool GlobalOnly,
bool &SingleChoice) {
1788 if (CurrentIdent == NextIdent)
1789 return CurrentIdent;
1794 SingleChoice = !CurrentIdent;
1806 getCombinedIdentFromCallUsesIn(OMPInformationCache::RuntimeFunctionInfo &RFI,
1807 Function &
F,
bool GlobalOnly) {
1808 bool SingleChoice =
true;
1809 Value *Ident =
nullptr;
1811 CallInst *CI = getCallIfRegularCall(U, &RFI);
1812 if (!CI || &
F != &Caller)
1815 true, SingleChoice);
1818 RFI.foreachUse(SCC, CombineIdentStruct);
1820 if (!Ident || !SingleChoice) {
1823 if (!OMPInfoCache.OMPBuilder.getInsertionPoint().getBlock())
1825 &
F.getEntryBlock(),
F.getEntryBlock().begin()));
1828 uint32_t SrcLocStrSize;
1830 OMPInfoCache.OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1831 Ident = OMPInfoCache.OMPBuilder.getOrCreateIdent(Loc, SrcLocStrSize);
1838 bool deduplicateRuntimeCalls(Function &
F,
1839 OMPInformationCache::RuntimeFunctionInfo &RFI,
1840 Value *ReplVal =
nullptr) {
1841 auto *UV = RFI.getUseVector(
F);
1842 if (!UV || UV->size() + (ReplVal !=
nullptr) < 2)
1846 dbgs() <<
TAG <<
"Deduplicate " << UV->size() <<
" uses of " << RFI.Name
1847 << (ReplVal ?
" with an existing value\n" :
"\n") <<
"\n");
1851 "Unexpected replacement value!");
1854 auto CanBeMoved = [
this](CallBase &CB) {
1855 unsigned NumArgs = CB.arg_size();
1858 if (CB.getArgOperand(0)->getType() != OMPInfoCache.OMPBuilder.IdentPtr)
1860 for (
unsigned U = 1;
U < NumArgs; ++
U)
1868 OMPInfoCache.getAnalysisResultForFunction<DominatorTreeAnalysis>(
F);
1872 for (Use *U : *UV) {
1873 if (CallInst *CI = getCallIfRegularCall(*U, &RFI)) {
1878 if (!CanBeMoved(*CI))
1886 assert(IP &&
"Expected insertion point!");
1896 Value *Ident = getCombinedIdentFromCallUsesIn(RFI,
F,
1904 CallInst *CI = getCallIfRegularCall(U, &RFI);
1905 if (!CI || CI == ReplVal || &
F != &Caller)
1909 auto Remark = [&](OptimizationRemark
OR) {
1910 return OR <<
"OpenMP runtime call "
1911 <<
ore::NV(
"OpenMPOptRuntime", RFI.Name) <<
" deduplicated.";
1920 ++NumOpenMPRuntimeCallsDeduplicated;
1924 RFI.foreachUse(SCC, ReplaceAndDeleteCB);
1930 void collectGlobalThreadIdArguments(SmallSetVector<Value *, 16> >IdArgs) {
1937 auto CallArgOpIsGTId = [&](
Function &
F,
unsigned ArgNo, CallInst &RefCI) {
1938 if (!
F.hasLocalLinkage())
1940 for (Use &U :
F.uses()) {
1941 if (CallInst *CI = getCallIfRegularCall(U)) {
1943 if (CI == &RefCI || GTIdArgs.
count(ArgOp) ||
1944 getCallIfRegularCall(
1945 *ArgOp, &OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num]))
1954 auto AddUserArgs = [&](
Value >Id) {
1955 for (Use &U : GTId.uses())
1959 if (CallArgOpIsGTId(*Callee,
U.getOperandNo(), *CI))
1964 OMPInformationCache::RuntimeFunctionInfo &GlobThreadNumRFI =
1965 OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num];
1967 GlobThreadNumRFI.foreachUse(SCC, [&](Use &U, Function &
F) {
1968 if (CallInst *CI = getCallIfRegularCall(U, &GlobThreadNumRFI))
1976 for (
unsigned U = 0;
U < GTIdArgs.
size(); ++
U)
1977 AddUserArgs(*GTIdArgs[U]);
1985 DenseMap<Function *, std::optional<Kernel>> UniqueKernelMap;
1988 Kernel getUniqueKernelFor(Function &
F);
1991 Kernel getUniqueKernelFor(Instruction &
I) {
1992 return getUniqueKernelFor(*
I.getFunction());
1997 bool rewriteDeviceCodeStateMachine();
2002 bool removeSPMDParallelWrappers();
2018 template <
typename RemarkKind,
typename RemarkCallBack>
2019 void emitRemark(Instruction *
I, StringRef RemarkName,
2020 RemarkCallBack &&RemarkCB)
const {
2022 auto &ORE = OREGetter(
F);
2026 return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
I))
2027 <<
" [" << RemarkName <<
"]";
2031 [&]() {
return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
I)); });
2035 template <
typename RemarkKind,
typename RemarkCallBack>
2036 void emitRemark(Function *
F, StringRef RemarkName,
2037 RemarkCallBack &&RemarkCB)
const {
2038 auto &ORE = OREGetter(
F);
2042 return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
F))
2043 <<
" [" << RemarkName <<
"]";
2047 [&]() {
return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
F)); });
2054 SmallVectorImpl<Function *> &SCC;
2058 CallGraphUpdater &CGUpdater;
2061 OptimizationRemarkGetter OREGetter;
2064 OMPInformationCache &OMPInfoCache;
2070 bool runAttributor(
bool IsModulePass) {
2074 registerAAs(IsModulePass);
2079 <<
" functions, result: " <<
Changed <<
".\n");
2081 if (
Changed == ChangeStatus::CHANGED)
2082 OMPInfoCache.invalidateAnalyses();
2084 return Changed == ChangeStatus::CHANGED;
2091 void registerAAs(
bool IsModulePass);
2096 static void registerAAsForFunction(Attributor &A,
const Function &
F);
2100 if (OMPInfoCache.CGSCC && !OMPInfoCache.CGSCC->empty() &&
2101 !OMPInfoCache.CGSCC->contains(&
F))
2106 std::optional<Kernel> &CachedKernel = UniqueKernelMap[&
F];
2108 return *CachedKernel;
2115 return *CachedKernel;
2118 CachedKernel =
nullptr;
2119 if (!
F.hasLocalLinkage()) {
2122 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
2123 return ORA <<
"Potentially unknown OpenMP target region caller.";
2131 auto GetUniqueKernelForUse = [&](
const Use &
U) ->
Kernel {
2134 if (
Cmp->isEquality())
2135 return getUniqueKernelFor(*Cmp);
2140 if (CB->isCallee(&U))
2141 return getUniqueKernelFor(*CB);
2143 OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI =
2144 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
2146 if (OpenMPOpt::getCallIfRegularCall(*
U.getUser(), &KernelParallelRFI))
2147 return getUniqueKernelFor(*CB);
2155 SmallPtrSet<Kernel, 2> PotentialKernels;
2156 OMPInformationCache::foreachUse(
F, [&](
const Use &U) {
2157 PotentialKernels.
insert(GetUniqueKernelForUse(U));
2161 if (PotentialKernels.
size() == 1)
2162 K = *PotentialKernels.
begin();
2165 UniqueKernelMap[&
F] =
K;
2170bool OpenMPOpt::rewriteDeviceCodeStateMachine() {
2171 OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI =
2172 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
2175 if (!KernelParallelRFI)
2182 for (Function *
F : SCC) {
2186 bool UnknownUse =
false;
2187 bool KernelParallelUse =
false;
2188 unsigned NumDirectCalls = 0;
2191 OMPInformationCache::foreachUse(*
F, [&](Use &U) {
2193 if (CB->isCallee(&U)) {
2199 ToBeReplacedStateMachineUses.
push_back(&U);
2205 OpenMPOpt::getCallIfRegularCall(*
U.getUser(), &KernelParallelRFI);
2206 const unsigned int WrapperFunctionArgNo = 6;
2207 if (!KernelParallelUse && CI &&
2209 KernelParallelUse =
true;
2210 ToBeReplacedStateMachineUses.
push_back(&U);
2218 if (!KernelParallelUse)
2224 if (UnknownUse || NumDirectCalls != 1 ||
2225 ToBeReplacedStateMachineUses.
size() > 2) {
2226 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
2227 return ORA <<
"Parallel region is used in "
2228 << (UnknownUse ?
"unknown" :
"unexpected")
2229 <<
" ways. Will not attempt to rewrite the state machine.";
2239 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
2240 return ORA <<
"Parallel region is not called from a unique kernel. "
2241 "Will not attempt to rewrite the state machine.";
2253 Type *Int8Ty = Type::getInt8Ty(
M.getContext());
2255 auto *
ID =
new GlobalVariable(
2259 for (Use *U : ToBeReplacedStateMachineUses)
2261 ID,
U->get()->getType()));
2263 ++NumOpenMPParallelRegionsReplacedInGPUStateMachine;
2271bool OpenMPOpt::removeSPMDParallelWrappers() {
2273 if (OMPInfoCache.SPMDizedKernels.empty())
2276 OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI =
2277 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
2278 if (!KernelParallelRFI || !KernelParallelRFI.Declaration)
2281 constexpr unsigned WrapperFunctionArgNo = 6;
2283 for (User *U : KernelParallelRFI.Declaration->
users()) {
2286 CI->
arg_size() <= WrapperFunctionArgNo)
2300 if (!K || !OMPInfoCache.SPMDizedKernels.contains(K))
2304 WrapperFunctionArgNo,
2313struct AAICVTracker :
public StateWrapper<BooleanState, AbstractAttribute> {
2314 using Base = StateWrapper<BooleanState, AbstractAttribute>;
2315 AAICVTracker(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
2318 bool isAssumedTracked()
const {
return getAssumed(); }
2321 bool isKnownTracked()
const {
return getAssumed(); }
2324 static AAICVTracker &createForPosition(
const IRPosition &IRP, Attributor &
A);
2328 const Instruction *
I,
2329 Attributor &
A)
const {
2330 return std::nullopt;
2336 virtual std::optional<Value *>
2344 StringRef
getName()
const override {
return "AAICVTracker"; }
2347 const char *getIdAddr()
const override {
return &ID; }
2350 static bool classof(
const AbstractAttribute *AA) {
2354 static const char ID;
2357struct AAICVTrackerFunction :
public AAICVTracker {
2358 AAICVTrackerFunction(
const IRPosition &IRP, Attributor &
A)
2359 : AAICVTracker(IRP,
A) {}
2362 const std::string getAsStr(Attributor *)
const override {
2363 return "ICVTrackerFunction";
2367 void trackStatistics()
const override {}
2371 return ChangeStatus::UNCHANGED;
2376 InternalControlVar::ICV___last>
2377 ICVReplacementValuesMap;
2384 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
2387 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter];
2389 auto &ValuesMap = ICVReplacementValuesMap[ICV];
2391 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U);
2397 if (ValuesMap.insert(std::make_pair(CI, CI->
getArgOperand(0))).second)
2398 HasChanged = ChangeStatus::CHANGED;
2404 std::optional<Value *> ReplVal = getValueForCall(
A,
I, ICV);
2405 if (ReplVal && ValuesMap.insert(std::make_pair(&
I, *ReplVal)).second)
2406 HasChanged = ChangeStatus::CHANGED;
2412 SetterRFI.foreachUse(TrackValues,
F);
2414 bool UsedAssumedInformation =
false;
2415 A.checkForAllInstructions(CallCheck, *
this, {Instruction::Call},
2416 UsedAssumedInformation,
2422 if (HasChanged == ChangeStatus::CHANGED)
2423 ValuesMap.try_emplace(Entry);
2431 std::optional<Value *> getValueForCall(Attributor &
A,
const Instruction &
I,
2435 if (!CB || CB->hasFnAttr(
"no_openmp") ||
2436 CB->hasFnAttr(
"no_openmp_routines") ||
2437 CB->hasFnAttr(
"no_openmp_constructs"))
2438 return std::nullopt;
2440 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
2441 auto &GetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Getter];
2442 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter];
2443 Function *CalledFunction = CB->getCalledFunction();
2446 if (CalledFunction ==
nullptr)
2448 if (CalledFunction == GetterRFI.Declaration)
2449 return std::nullopt;
2450 if (CalledFunction == SetterRFI.Declaration) {
2451 if (ICVReplacementValuesMap[ICV].
count(&
I))
2452 return ICVReplacementValuesMap[ICV].lookup(&
I);
2461 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2464 if (ICVTrackingAA->isAssumedTracked()) {
2465 std::optional<Value *> URV =
2466 ICVTrackingAA->getUniqueReplacementValue(ICV);
2477 std::optional<Value *>
2479 return std::nullopt;
2484 const Instruction *
I,
2485 Attributor &
A)
const override {
2486 const auto &ValuesMap = ICVReplacementValuesMap[ICV];
2487 if (ValuesMap.count(
I))
2488 return ValuesMap.lookup(
I);
2491 SmallPtrSet<const Instruction *, 16> Visited;
2494 std::optional<Value *> ReplVal;
2496 while (!Worklist.
empty()) {
2498 if (!Visited.
insert(CurrInst).second)
2506 if (ValuesMap.count(CurrInst)) {
2507 std::optional<Value *> NewReplVal = ValuesMap.lookup(CurrInst);
2510 ReplVal = NewReplVal;
2516 if (ReplVal != NewReplVal)
2522 std::optional<Value *> NewReplVal = getValueForCall(
A, *CurrInst, ICV);
2528 ReplVal = NewReplVal;
2534 if (ReplVal != NewReplVal)
2539 if (CurrBB ==
I->getParent() && ReplVal)
2544 if (
const Instruction *Terminator = Pred->getTerminator())
2552struct AAICVTrackerFunctionReturned : AAICVTracker {
2553 AAICVTrackerFunctionReturned(
const IRPosition &IRP, Attributor &
A)
2554 : AAICVTracker(IRP,
A) {}
2557 const std::string getAsStr(Attributor *)
const override {
2558 return "ICVTrackerFunctionReturned";
2562 void trackStatistics()
const override {}
2566 return ChangeStatus::UNCHANGED;
2571 InternalControlVar::ICV___last>
2572 ICVReplacementValuesMap;
2575 std::optional<Value *>
2577 return ICVReplacementValuesMap[ICV];
2582 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2585 if (!ICVTrackingAA->isAssumedTracked())
2586 return indicatePessimisticFixpoint();
2589 std::optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV];
2590 std::optional<Value *> UniqueICVValue;
2593 std::optional<Value *> NewReplVal =
2594 ICVTrackingAA->getReplacementValue(ICV, &
I,
A);
2597 if (UniqueICVValue && UniqueICVValue != NewReplVal)
2600 UniqueICVValue = NewReplVal;
2605 bool UsedAssumedInformation =
false;
2606 if (!
A.checkForAllInstructions(CheckReturnInst, *
this, {Instruction::Ret},
2607 UsedAssumedInformation,
2609 UniqueICVValue =
nullptr;
2611 if (UniqueICVValue == ReplVal)
2614 ReplVal = UniqueICVValue;
2615 Changed = ChangeStatus::CHANGED;
2622struct AAICVTrackerCallSite : AAICVTracker {
2623 AAICVTrackerCallSite(
const IRPosition &IRP, Attributor &
A)
2624 : AAICVTracker(IRP,
A) {}
2627 assert(getAnchorScope() &&
"Expected anchor function");
2631 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
2633 auto ICVInfo = OMPInfoCache.ICVs[ICV];
2634 auto &Getter = OMPInfoCache.RFIs[ICVInfo.Getter];
2635 if (Getter.Declaration == getAssociatedFunction()) {
2636 AssociatedICV = ICVInfo.Kind;
2642 indicatePessimisticFixpoint();
2646 if (!ReplVal || !*ReplVal)
2647 return ChangeStatus::UNCHANGED;
2650 A.deleteAfterManifest(*getCtxI());
2652 return ChangeStatus::CHANGED;
2656 const std::string getAsStr(Attributor *)
const override {
2657 return "ICVTrackerCallSite";
2661 void trackStatistics()
const override {}
2664 std::optional<Value *> ReplVal;
2667 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2671 if (!ICVTrackingAA->isAssumedTracked())
2672 return indicatePessimisticFixpoint();
2674 std::optional<Value *> NewReplVal =
2675 ICVTrackingAA->getReplacementValue(AssociatedICV, getCtxI(),
A);
2677 if (ReplVal == NewReplVal)
2678 return ChangeStatus::UNCHANGED;
2680 ReplVal = NewReplVal;
2681 return ChangeStatus::CHANGED;
2686 std::optional<Value *>
2692struct AAICVTrackerCallSiteReturned : AAICVTracker {
2693 AAICVTrackerCallSiteReturned(
const IRPosition &IRP, Attributor &
A)
2694 : AAICVTracker(IRP,
A) {}
2697 const std::string getAsStr(Attributor *)
const override {
2698 return "ICVTrackerCallSiteReturned";
2702 void trackStatistics()
const override {}
2706 return ChangeStatus::UNCHANGED;
2711 InternalControlVar::ICV___last>
2712 ICVReplacementValuesMap;
2716 std::optional<Value *>
2718 return ICVReplacementValuesMap[ICV];
2723 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2725 DepClassTy::REQUIRED);
2728 if (!ICVTrackingAA->isAssumedTracked())
2729 return indicatePessimisticFixpoint();
2732 std::optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV];
2733 std::optional<Value *> NewReplVal =
2734 ICVTrackingAA->getUniqueReplacementValue(ICV);
2736 if (ReplVal == NewReplVal)
2739 ReplVal = NewReplVal;
2740 Changed = ChangeStatus::CHANGED;
2748static bool hasFunctionEndAsUniqueSuccessor(
const BasicBlock *BB) {
2754 return hasFunctionEndAsUniqueSuccessor(
Successor);
2757struct AAExecutionDomainFunction :
public AAExecutionDomain {
2758 AAExecutionDomainFunction(
const IRPosition &IRP, Attributor &
A)
2759 : AAExecutionDomain(IRP,
A) {}
2761 ~AAExecutionDomainFunction()
override {
delete RPOT; }
2765 assert(
F &&
"Expected anchor function");
2766 RPOT =
new ReversePostOrderTraversal<Function *>(
F);
2769 const std::string getAsStr(Attributor *)
const override {
2770 unsigned TotalBlocks = 0, InitialThreadBlocks = 0, AlignedBlocks = 0;
2771 for (
auto &It : BEDMap) {
2775 InitialThreadBlocks += It.getSecond().IsExecutedByInitialThreadOnly;
2776 AlignedBlocks += It.getSecond().IsReachedFromAlignedBarrierOnly &&
2777 It.getSecond().IsReachingAlignedBarrierOnly;
2779 return "[AAExecutionDomain] " + std::to_string(InitialThreadBlocks) +
"/" +
2780 std::to_string(AlignedBlocks) +
" of " +
2781 std::to_string(TotalBlocks) +
2782 " executed by initial thread / aligned";
2786 void trackStatistics()
const override {}
2790 for (
const BasicBlock &BB : *getAnchorScope()) {
2791 if (!isExecutedByInitialThreadOnly(BB))
2793 dbgs() <<
TAG <<
" Basic block @" << getAnchorScope()->getName() <<
" "
2794 << BB.
getName() <<
" is executed by a single thread.\n";
2803 SmallPtrSet<CallBase *, 16> DeletedBarriers;
2804 auto HandleAlignedBarrier = [&](CallBase *CB) {
2805 const ExecutionDomainTy &ED = CB ? CEDMap[{CB, PRE}] : BEDMap[
nullptr];
2806 if (!ED.IsReachedFromAlignedBarrierOnly ||
2807 ED.EncounteredNonLocalSideEffect)
2809 if (!ED.EncounteredAssumes.empty() && !
A.isModulePass())
2820 DeletedBarriers.
insert(CB);
2821 A.deleteAfterManifest(*CB);
2822 ++NumBarriersEliminated;
2823 Changed = ChangeStatus::CHANGED;
2824 }
else if (!ED.AlignedBarriers.empty()) {
2825 Changed = ChangeStatus::CHANGED;
2827 ED.AlignedBarriers.end());
2828 SmallSetVector<CallBase *, 16> Visited;
2829 while (!Worklist.
empty()) {
2831 if (!Visited.
insert(LastCB))
2835 if (!hasFunctionEndAsUniqueSuccessor(LastCB->
getParent()))
2837 if (!DeletedBarriers.
count(LastCB)) {
2838 ++NumBarriersEliminated;
2839 A.deleteAfterManifest(*LastCB);
2845 const ExecutionDomainTy &LastED = CEDMap[{LastCB, PRE}];
2846 Worklist.
append(LastED.AlignedBarriers.begin(),
2847 LastED.AlignedBarriers.end());
2853 if (!ED.EncounteredAssumes.empty() && (CB || !ED.AlignedBarriers.empty()))
2854 for (
auto *AssumeCB : ED.EncounteredAssumes)
2855 A.deleteAfterManifest(*AssumeCB);
2858 for (
auto *CB : AlignedBarriers)
2859 HandleAlignedBarrier(CB);
2863 HandleAlignedBarrier(
nullptr);
2868 bool isNoOpFence(
const FenceInst &FI)
const override {
2869 return getState().isValidState() && !NonNoOpFences.count(&FI);
2875 mergeInPredecessorBarriersAndAssumptions(Attributor &
A, ExecutionDomainTy &ED,
2876 const ExecutionDomainTy &PredED);
2881 bool mergeInPredecessor(Attributor &
A, ExecutionDomainTy &ED,
2882 const ExecutionDomainTy &PredED,
2883 bool InitialEdgeOnly =
false);
2886 bool handleCallees(Attributor &
A, ExecutionDomainTy &EntryBBED);
2893 bool isExecutedByInitialThreadOnly(
const BasicBlock &BB)
const override {
2894 if (!isValidState())
2896 assert(BB.
getParent() == getAnchorScope() &&
"Block is out of scope!");
2897 return BEDMap.lookup(&BB).IsExecutedByInitialThreadOnly;
2900 bool isExecutedInAlignedRegion(Attributor &
A,
2901 const Instruction &
I)
const override {
2902 assert(
I.getFunction() == getAnchorScope() &&
2903 "Instruction is out of scope!");
2904 if (!isValidState())
2907 bool ForwardIsOk =
true;
2916 if (CB != &
I && AlignedBarriers.contains(
const_cast<CallBase *
>(CB)))
2918 const auto &It = CEDMap.find({CB, PRE});
2919 if (It == CEDMap.end())
2921 if (!It->getSecond().IsReachingAlignedBarrierOnly)
2922 ForwardIsOk =
false;
2926 if (!CurI && !BEDMap.lookup(
I.getParent()).IsReachingAlignedBarrierOnly)
2927 ForwardIsOk =
false;
2935 if (CB != &
I && AlignedBarriers.contains(
const_cast<CallBase *
>(CB)))
2937 const auto &It = CEDMap.find({CB, POST});
2938 if (It == CEDMap.end())
2940 if (It->getSecond().IsReachedFromAlignedBarrierOnly)
2953 return BEDMap.lookup(
nullptr).IsReachedFromAlignedBarrierOnly;
2955 return BEDMap.lookup(PredBB).IsReachedFromAlignedBarrierOnly;
2965 ExecutionDomainTy getExecutionDomain(
const BasicBlock &BB)
const override {
2967 "No request should be made against an invalid state!");
2968 return BEDMap.lookup(&BB);
2970 std::pair<ExecutionDomainTy, ExecutionDomainTy>
2971 getExecutionDomain(
const CallBase &CB)
const override {
2973 "No request should be made against an invalid state!");
2974 return {CEDMap.lookup({&CB, PRE}), CEDMap.lookup({&CB, POST})};
2976 ExecutionDomainTy getFunctionExecutionDomain()
const override {
2978 "No request should be made against an invalid state!");
2979 return InterProceduralED;
2985 static bool isInitialThreadOnlyEdge(Attributor &
A, CondBrInst *
Edge,
2986 BasicBlock &SuccessorBB) {
2989 if (
Edge->getSuccessor(0) != &SuccessorBB)
2993 if (!Cmp || !
Cmp->isTrueWhenEqual() || !
Cmp->isEquality())
3001 if (
C->isAllOnesValue()) {
3003 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3004 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_target_init];
3005 CB = CB ? OpenMPOpt::getCallIfRegularCall(*CB, &RFI) : nullptr;
3008 ConstantStruct *KernelEnvC =
3010 ConstantInt *ExecModeC =
3011 KernelInfo::getExecModeFromKernelEnvironment(KernelEnvC);
3018 if (
II->getIntrinsicID() == Intrinsic::nvvm_read_ptx_sreg_tid_x)
3023 if (
II->getIntrinsicID() == Intrinsic::amdgcn_workitem_id_x)
3031 ExecutionDomainTy InterProceduralED;
3035 DenseMap<const BasicBlock *, ExecutionDomainTy> BEDMap;
3036 DenseMap<PointerIntPair<const CallBase *, 1, Direction>, ExecutionDomainTy>
3038 SmallSetVector<CallBase *, 16> AlignedBarriers;
3040 ReversePostOrderTraversal<Function *> *RPOT =
nullptr;
3043 static bool setAndRecord(
bool &R,
bool V) {
3051 SmallPtrSet<const FenceInst *, 8> NonNoOpFences;
3054void AAExecutionDomainFunction::mergeInPredecessorBarriersAndAssumptions(
3055 Attributor &
A, ExecutionDomainTy &ED,
const ExecutionDomainTy &PredED) {
3056 for (
auto *EA : PredED.EncounteredAssumes)
3057 ED.addAssumeInst(
A, *EA);
3059 for (
auto *AB : PredED.AlignedBarriers)
3060 ED.addAlignedBarrier(
A, *AB);
3063bool AAExecutionDomainFunction::mergeInPredecessor(
3064 Attributor &
A, ExecutionDomainTy &ED,
const ExecutionDomainTy &PredED,
3065 bool InitialEdgeOnly) {
3069 setAndRecord(ED.IsExecutedByInitialThreadOnly,
3070 InitialEdgeOnly || (PredED.IsExecutedByInitialThreadOnly &&
3071 ED.IsExecutedByInitialThreadOnly));
3073 Changed |= setAndRecord(ED.IsReachedFromAlignedBarrierOnly,
3074 ED.IsReachedFromAlignedBarrierOnly &&
3075 PredED.IsReachedFromAlignedBarrierOnly);
3076 Changed |= setAndRecord(ED.EncounteredNonLocalSideEffect,
3077 ED.EncounteredNonLocalSideEffect |
3078 PredED.EncounteredNonLocalSideEffect);
3080 if (ED.IsReachedFromAlignedBarrierOnly)
3081 mergeInPredecessorBarriersAndAssumptions(
A, ED, PredED);
3083 ED.clearAssumeInstAndAlignedBarriers();
3087bool AAExecutionDomainFunction::handleCallees(Attributor &
A,
3088 ExecutionDomainTy &EntryBBED) {
3090 auto PredForCallSite = [&](AbstractCallSite ACS) {
3091 const auto *EDAA =
A.getAAFor<AAExecutionDomain>(
3093 DepClassTy::OPTIONAL);
3094 if (!EDAA || !EDAA->getState().isValidState())
3097 EDAA->getExecutionDomain(*
cast<CallBase>(ACS.getInstruction())));
3101 ExecutionDomainTy ExitED;
3102 bool AllCallSitesKnown;
3103 if (
A.checkForAllCallSites(PredForCallSite, *
this,
3105 AllCallSitesKnown)) {
3106 for (
const auto &[CSInED, CSOutED] : CallSiteEDs) {
3107 mergeInPredecessor(
A, EntryBBED, CSInED);
3108 ExitED.IsReachingAlignedBarrierOnly &=
3109 CSOutED.IsReachingAlignedBarrierOnly;
3116 EntryBBED.IsExecutedByInitialThreadOnly =
false;
3117 EntryBBED.IsReachedFromAlignedBarrierOnly =
true;
3118 EntryBBED.EncounteredNonLocalSideEffect =
false;
3119 ExitED.IsReachingAlignedBarrierOnly =
false;
3121 EntryBBED.IsExecutedByInitialThreadOnly =
false;
3122 EntryBBED.IsReachedFromAlignedBarrierOnly =
false;
3123 EntryBBED.EncounteredNonLocalSideEffect =
true;
3124 ExitED.IsReachingAlignedBarrierOnly =
false;
3129 auto &FnED = BEDMap[
nullptr];
3130 Changed |= setAndRecord(FnED.IsReachedFromAlignedBarrierOnly,
3131 FnED.IsReachedFromAlignedBarrierOnly &
3132 EntryBBED.IsReachedFromAlignedBarrierOnly);
3133 Changed |= setAndRecord(FnED.IsReachingAlignedBarrierOnly,
3134 FnED.IsReachingAlignedBarrierOnly &
3135 ExitED.IsReachingAlignedBarrierOnly);
3136 Changed |= setAndRecord(FnED.IsExecutedByInitialThreadOnly,
3137 EntryBBED.IsExecutedByInitialThreadOnly);
3141ChangeStatus AAExecutionDomainFunction::updateImpl(Attributor &
A) {
3148 auto HandleAlignedBarrier = [&](CallBase &CB, ExecutionDomainTy &ED) {
3149 Changed |= AlignedBarriers.insert(&CB);
3151 auto &CallInED = CEDMap[{&CB, PRE}];
3152 Changed |= mergeInPredecessor(
A, CallInED, ED);
3153 CallInED.IsReachingAlignedBarrierOnly =
true;
3155 ED.EncounteredNonLocalSideEffect =
false;
3156 ED.IsReachedFromAlignedBarrierOnly =
true;
3158 ED.clearAssumeInstAndAlignedBarriers();
3159 ED.addAlignedBarrier(
A, CB);
3160 auto &CallOutED = CEDMap[{&CB, POST}];
3161 Changed |= mergeInPredecessor(
A, CallOutED, ED);
3165 A.getAAFor<AAIsDead>(*
this, getIRPosition(), DepClassTy::OPTIONAL);
3171 SmallVector<Instruction *> SyncInstWorklist;
3172 for (
auto &RIt : *RPOT) {
3175 bool IsEntryBB = &BB == &EntryBB;
3178 bool AlignedBarrierLastInBlock = IsEntryBB && IsKernel;
3179 bool IsExplicitlyAligned = IsEntryBB && IsKernel;
3180 ExecutionDomainTy ED;
3187 if (LivenessAA && LivenessAA->isAssumedDead(&BB))
3191 if (LivenessAA && LivenessAA->isEdgeDead(PredBB, &BB))
3193 bool InitialEdgeOnly = isInitialThreadOnlyEdge(
3195 mergeInPredecessor(
A, ED, BEDMap[PredBB], InitialEdgeOnly);
3201 for (Instruction &
I : BB) {
3202 bool UsedAssumedInformation;
3203 if (
A.isAssumedDead(
I, *
this, LivenessAA, UsedAssumedInformation,
3204 false, DepClassTy::OPTIONAL,
3212 ED.addAssumeInst(
A, *AI);
3216 if (
II->isAssumeLikeIntrinsic())
3221 if (!ED.EncounteredNonLocalSideEffect) {
3223 if (ED.IsReachedFromAlignedBarrierOnly)
3228 case AtomicOrdering::NotAtomic:
3230 case AtomicOrdering::Unordered:
3232 case AtomicOrdering::Monotonic:
3234 case AtomicOrdering::Acquire:
3236 case AtomicOrdering::Release:
3238 case AtomicOrdering::AcquireRelease:
3240 case AtomicOrdering::SequentiallyConsistent:
3244 NonNoOpFences.insert(FI);
3249 bool IsAlignedBarrier =
3253 AlignedBarrierLastInBlock &= IsNoSync;
3254 IsExplicitlyAligned &= IsNoSync;
3260 if (IsAlignedBarrier) {
3261 HandleAlignedBarrier(*CB, ED);
3262 AlignedBarrierLastInBlock =
true;
3263 IsExplicitlyAligned =
true;
3269 if (!ED.EncounteredNonLocalSideEffect &&
3271 ED.EncounteredNonLocalSideEffect =
true;
3273 ED.IsReachedFromAlignedBarrierOnly =
false;
3281 auto &CallInED = CEDMap[{CB, PRE}];
3282 Changed |= mergeInPredecessor(
A, CallInED, ED);
3288 if (!IsNoSync && Callee && !
Callee->isDeclaration()) {
3289 const auto *EDAA =
A.getAAFor<AAExecutionDomain>(
3291 if (EDAA && EDAA->getState().isValidState()) {
3292 const auto &CalleeED = EDAA->getFunctionExecutionDomain();
3293 ED.IsReachedFromAlignedBarrierOnly =
3294 CalleeED.IsReachedFromAlignedBarrierOnly;
3295 AlignedBarrierLastInBlock = ED.IsReachedFromAlignedBarrierOnly;
3296 if (IsNoSync || !CalleeED.IsReachedFromAlignedBarrierOnly)
3297 ED.EncounteredNonLocalSideEffect |=
3298 CalleeED.EncounteredNonLocalSideEffect;
3300 ED.EncounteredNonLocalSideEffect =
3301 CalleeED.EncounteredNonLocalSideEffect;
3302 if (!CalleeED.IsReachingAlignedBarrierOnly) {
3304 setAndRecord(CallInED.IsReachingAlignedBarrierOnly,
false);
3307 if (CalleeED.IsReachedFromAlignedBarrierOnly)
3308 mergeInPredecessorBarriersAndAssumptions(
A, ED, CalleeED);
3309 auto &CallOutED = CEDMap[{CB, POST}];
3310 Changed |= mergeInPredecessor(
A, CallOutED, ED);
3315 ED.IsReachedFromAlignedBarrierOnly =
false;
3316 Changed |= setAndRecord(CallInED.IsReachingAlignedBarrierOnly,
false);
3319 AlignedBarrierLastInBlock &= ED.IsReachedFromAlignedBarrierOnly;
3321 auto &CallOutED = CEDMap[{CB, POST}];
3322 Changed |= mergeInPredecessor(
A, CallOutED, ED);
3325 if (!
I.mayHaveSideEffects() && !
I.mayReadFromMemory())
3331 const auto *MemAA =
A.getAAFor<AAMemoryLocation>(
3339 if (MemAA && MemAA->getState().isValidState() &&
3340 MemAA->checkForAllAccessesToMemoryKind(
3345 auto &InfoCache =
A.getInfoCache();
3346 if (!
I.mayHaveSideEffects() && InfoCache.isOnlyUsedByAssume(
I))
3350 if (LI->hasMetadata(LLVMContext::MD_invariant_load))
3353 if (!ED.EncounteredNonLocalSideEffect &&
3355 ED.EncounteredNonLocalSideEffect =
true;
3358 bool IsEndAndNotReachingAlignedBarriersOnly =
false;
3360 !BB.getTerminator()->getNumSuccessors()) {
3362 Changed |= mergeInPredecessor(
A, InterProceduralED, ED);
3364 auto &FnED = BEDMap[
nullptr];
3365 if (IsKernel && !IsExplicitlyAligned)
3366 FnED.IsReachingAlignedBarrierOnly =
false;
3367 Changed |= mergeInPredecessor(
A, FnED, ED);
3369 if (!FnED.IsReachingAlignedBarrierOnly) {
3370 IsEndAndNotReachingAlignedBarriersOnly =
true;
3371 SyncInstWorklist.
push_back(BB.getTerminator());
3372 auto &BBED = BEDMap[&BB];
3373 Changed |= setAndRecord(BBED.IsReachingAlignedBarrierOnly,
false);
3377 ExecutionDomainTy &StoredED = BEDMap[&BB];
3378 ED.IsReachingAlignedBarrierOnly = StoredED.IsReachingAlignedBarrierOnly &
3379 !IsEndAndNotReachingAlignedBarriersOnly;
3385 if (ED.IsExecutedByInitialThreadOnly !=
3386 StoredED.IsExecutedByInitialThreadOnly ||
3387 ED.IsReachedFromAlignedBarrierOnly !=
3388 StoredED.IsReachedFromAlignedBarrierOnly ||
3389 ED.EncounteredNonLocalSideEffect !=
3390 StoredED.EncounteredNonLocalSideEffect)
3394 StoredED = std::move(ED);
3399 SmallSetVector<BasicBlock *, 16> Visited;
3400 while (!SyncInstWorklist.
empty()) {
3403 bool HitAlignedBarrierOrKnownEnd =
false;
3408 auto &CallOutED = CEDMap[{CB, POST}];
3409 Changed |= setAndRecord(CallOutED.IsReachingAlignedBarrierOnly,
false);
3410 auto &CallInED = CEDMap[{CB, PRE}];
3411 HitAlignedBarrierOrKnownEnd =
3412 AlignedBarriers.count(CB) || !CallInED.IsReachingAlignedBarrierOnly;
3413 if (HitAlignedBarrierOrKnownEnd)
3415 Changed |= setAndRecord(CallInED.IsReachingAlignedBarrierOnly,
false);
3417 if (HitAlignedBarrierOrKnownEnd)
3421 if (LivenessAA && LivenessAA->isEdgeDead(PredBB, SyncBB))
3423 if (!Visited.
insert(PredBB))
3425 auto &PredED = BEDMap[PredBB];
3426 if (setAndRecord(PredED.IsReachingAlignedBarrierOnly,
false)) {
3428 SyncInstWorklist.
push_back(PredBB->getTerminator());
3431 if (SyncBB != &EntryBB)
3434 setAndRecord(InterProceduralED.IsReachingAlignedBarrierOnly,
false);
3437 return Changed ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;
3442struct AAHeapToShared :
public StateWrapper<BooleanState, AbstractAttribute> {
3443 using Base = StateWrapper<BooleanState, AbstractAttribute>;
3444 AAHeapToShared(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
3447 static AAHeapToShared &createForPosition(
const IRPosition &IRP,
3451 virtual bool isAssumedHeapToShared(CallBase &CB)
const = 0;
3455 virtual bool isAssumedHeapToSharedRemovedFree(CallBase &CB)
const = 0;
3458 StringRef
getName()
const override {
return "AAHeapToShared"; }
3461 const char *getIdAddr()
const override {
return &ID; }
3465 static bool classof(
const AbstractAttribute *AA) {
3470 static const char ID;
3473struct AAHeapToSharedFunction :
public AAHeapToShared {
3474 AAHeapToSharedFunction(
const IRPosition &IRP, Attributor &
A)
3475 : AAHeapToShared(IRP,
A) {}
3477 const std::string getAsStr(Attributor *)
const override {
3478 return "[AAHeapToShared] " + std::to_string(MallocCalls.size()) +
3479 " malloc calls eligible.";
3483 void trackStatistics()
const override {}
3487 void findPotentialRemovedFreeCalls(Attributor &
A) {
3488 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3489 auto &FreeRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_free_shared];
3491 PotentialRemovedFreeCalls.clear();
3493 for (CallBase *CB : MallocCalls) {
3495 for (
auto *U : CB->
users()) {
3497 if (
C &&
C->getCalledFunction() == FreeRFI.Declaration)
3501 if (FreeCalls.
size() != 1)
3504 PotentialRemovedFreeCalls.insert(FreeCalls.
front());
3510 indicatePessimisticFixpoint();
3514 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3515 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
3516 if (!RFI.Declaration)
3520 [](
const IRPosition &,
const AbstractAttribute *,
3521 bool &) -> std::optional<Value *> {
return nullptr; };
3524 const OMPInformationCache::RuntimeFunctionInfo::UseVector *
Uses =
3525 RFI.getUseVector(*
F);
3529 for (Use *U : *
Uses)
3531 MallocCalls.insert(CB);
3536 findPotentialRemovedFreeCalls(
A);
3539 bool isAssumedHeapToShared(CallBase &CB)
const override {
3540 return isValidState() && MallocCalls.count(&CB);
3543 bool isAssumedHeapToSharedRemovedFree(CallBase &CB)
const override {
3544 return isValidState() && PotentialRemovedFreeCalls.count(&CB);
3548 if (MallocCalls.empty())
3549 return ChangeStatus::UNCHANGED;
3551 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3552 auto &FreeCall = OMPInfoCache.RFIs[OMPRTL___kmpc_free_shared];
3556 DepClassTy::OPTIONAL);
3559 for (CallBase *CB : MallocCalls) {
3561 if (HS &&
HS->isAssumedHeapToStack(*CB))
3566 for (
auto *U : CB->
users()) {
3568 if (
C &&
C->getCalledFunction() == FreeCall.Declaration)
3571 if (FreeCalls.
size() != 1)
3578 <<
" with shared memory."
3579 <<
" Shared memory usage is limited to "
3585 <<
" with " << AllocSize->getZExtValue()
3586 <<
" bytes of shared memory\n");
3591 Type *Int8Ty = Type::getInt8Ty(
M->getContext());
3592 Type *Int8ArrTy = ArrayType::get(Int8Ty, AllocSize->getZExtValue());
3593 auto *SharedMem =
new GlobalVariable(
3597 static_cast<unsigned>(AddressSpace::Shared));
3599 SharedMem, PointerType::getUnqual(
M->getContext()));
3601 auto Remark = [&](OptimizationRemark
OR) {
3602 return OR <<
"Replaced globalized variable with "
3603 <<
ore::NV(
"SharedMemory", AllocSize->getZExtValue())
3604 << (AllocSize->isOne() ?
" byte " :
" bytes ")
3605 <<
"of shared memory.";
3607 A.emitRemark<OptimizationRemark>(CB,
"OMP111",
Remark);
3609 MaybeAlign Alignment = CB->getRetAlign();
3611 "HeapToShared on allocation without alignment attribute");
3612 SharedMem->setAlignment(*Alignment);
3615 A.deleteAfterManifest(*CB);
3616 A.deleteAfterManifest(*FreeCalls.
front());
3618 SharedMemoryUsed += AllocSize->getZExtValue();
3619 NumBytesMovedToSharedMemory = SharedMemoryUsed;
3620 Changed = ChangeStatus::CHANGED;
3627 if (MallocCalls.empty())
3628 return indicatePessimisticFixpoint();
3629 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3630 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
3631 if (!RFI.Declaration)
3632 return ChangeStatus::UNCHANGED;
3636 auto NumMallocCalls = MallocCalls.size();
3639 for (User *U : RFI.Declaration->
users()) {
3641 if (CB->getCaller() !=
F)
3643 if (!MallocCalls.count(CB))
3646 MallocCalls.remove(CB);
3649 const auto *ED =
A.getAAFor<AAExecutionDomain>(
3651 if (!ED || !ED->isExecutedByInitialThreadOnly(*CB))
3652 MallocCalls.remove(CB);
3656 findPotentialRemovedFreeCalls(
A);
3658 if (NumMallocCalls != MallocCalls.size())
3659 return ChangeStatus::CHANGED;
3661 return ChangeStatus::UNCHANGED;
3665 SmallSetVector<CallBase *, 4> MallocCalls;
3667 SmallPtrSet<CallBase *, 4> PotentialRemovedFreeCalls;
3669 unsigned SharedMemoryUsed = 0;
3672struct AAKernelInfo :
public StateWrapper<KernelInfoState, AbstractAttribute> {
3673 using Base = StateWrapper<KernelInfoState, AbstractAttribute>;
3674 AAKernelInfo(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
3678 static bool requiresCalleeForCallBase() {
return false; }
3681 void trackStatistics()
const override {}
3684 const std::string getAsStr(Attributor *)
const override {
3685 if (!isValidState())
3687 return std::string(SPMDCompatibilityTracker.isAssumed() ?
"SPMD"
3689 std::string(SPMDCompatibilityTracker.isAtFixpoint() ?
" [FIX]"
3691 std::string(
" #PRs: ") +
3692 (ReachedKnownParallelRegions.isValidState()
3693 ? std::to_string(ReachedKnownParallelRegions.size())
3695 ", #Unknown PRs: " +
3696 (ReachedUnknownParallelRegions.isValidState()
3697 ? std::to_string(ReachedUnknownParallelRegions.size())
3699 ", #Reaching Kernels: " +
3700 (ReachingKernelEntries.isValidState()
3701 ? std::to_string(ReachingKernelEntries.size())
3704 (ParallelLevels.isValidState()
3705 ? std::to_string(ParallelLevels.size())
3707 ", NestedPar: " + (NestedParallelism ?
"yes" :
"no");
3711 static AAKernelInfo &createForPosition(
const IRPosition &IRP, Attributor &
A);
3714 StringRef
getName()
const override {
return "AAKernelInfo"; }
3717 const char *getIdAddr()
const override {
return &ID; }
3720 static bool classof(
const AbstractAttribute *AA) {
3724 static const char ID;
3729struct AAKernelInfoFunction : AAKernelInfo {
3730 AAKernelInfoFunction(
const IRPosition &IRP, Attributor &
A)
3731 : AAKernelInfo(IRP,
A) {}
3733 SmallPtrSet<Instruction *, 4> GuardedInstructions;
3735 SmallPtrSetImpl<Instruction *> &getGuardedInstructions() {
3736 return GuardedInstructions;
3739 void setConfigurationOfKernelEnvironment(ConstantStruct *ConfigC) {
3741 KernelEnvC, ConfigC, {KernelInfo::ConfigurationIdx});
3742 assert(NewKernelEnvC &&
"Failed to create new kernel environment");
3746#define KERNEL_ENVIRONMENT_CONFIGURATION_SETTER(MEMBER) \
3747 void set##MEMBER##OfKernelEnvironment(ConstantInt *NewVal) { \
3748 ConstantStruct *ConfigC = \
3749 KernelInfo::getConfigurationFromKernelEnvironment(KernelEnvC); \
3750 Constant *NewConfigC = ConstantFoldInsertValueInstruction( \
3751 ConfigC, NewVal, {KernelInfo::MEMBER##Idx}); \
3752 assert(NewConfigC && "Failed to create new configuration environment"); \
3753 setConfigurationOfKernelEnvironment(cast<ConstantStruct>(NewConfigC)); \
3764#undef KERNEL_ENVIRONMENT_CONFIGURATION_SETTER
3771 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3775 OMPInformationCache::RuntimeFunctionInfo &InitRFI =
3776 OMPInfoCache.RFIs[OMPRTL___kmpc_target_init];
3777 OMPInformationCache::RuntimeFunctionInfo &DeinitRFI =
3778 OMPInfoCache.RFIs[OMPRTL___kmpc_target_deinit];
3782 auto StoreCallBase = [](
Use &U,
3783 OMPInformationCache::RuntimeFunctionInfo &RFI,
3785 CallBase *CB = OpenMPOpt::getCallIfRegularCall(U, &RFI);
3787 "Unexpected use of __kmpc_target_init or __kmpc_target_deinit!");
3789 "Multiple uses of __kmpc_target_init or __kmpc_target_deinit!");
3795 StoreCallBase(U, InitRFI, KernelInitCB);
3799 DeinitRFI.foreachUse(
3801 StoreCallBase(U, DeinitRFI, KernelDeinitCB);
3807 if (!KernelInitCB || !KernelDeinitCB)
3811 ReachingKernelEntries.insert(Fn);
3812 IsKernelEntry =
true;
3820 KernelConfigurationSimplifyCB =
3822 bool &UsedAssumedInformation) -> std::optional<Constant *> {
3823 if (!isAtFixpoint()) {
3826 UsedAssumedInformation =
true;
3832 A.registerGlobalVariableSimplificationCallback(
3833 *KernelEnvGV, KernelConfigurationSimplifyCB);
3836 bool CanChangeToSPMD = OMPInfoCache.runtimeFnsAvailable(
3837 {OMPRTL___kmpc_get_hardware_thread_id_in_block,
3838 OMPRTL___kmpc_barrier_simple_spmd});
3842 KernelInfo::getExecModeFromKernelEnvironment(KernelEnvC);
3847 SPMDCompatibilityTracker.indicateOptimisticFixpoint();
3851 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
3853 setExecModeOfKernelEnvironment(AssumedExecModeC);
3860 setMinThreadsOfKernelEnvironment(ConstantInt::get(Int32Ty, MinThreads));
3862 setMaxThreadsOfKernelEnvironment(ConstantInt::get(Int32Ty,
MaxThreads));
3863 auto [MinTeams, MaxTeams] =
3866 setMinTeamsOfKernelEnvironment(ConstantInt::get(Int32Ty, MinTeams));
3868 setMaxTeamsOfKernelEnvironment(ConstantInt::get(Int32Ty, MaxTeams));
3871 KernelInfo::getMayUseNestedParallelismFromKernelEnvironment(KernelEnvC);
3872 ConstantInt *AssumedMayUseNestedParallelismC = ConstantInt::get(
3874 setMayUseNestedParallelismOfKernelEnvironment(
3875 AssumedMayUseNestedParallelismC);
3879 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
3882 ConstantInt::get(UseGenericStateMachineC->
getIntegerType(),
false);
3883 setUseGenericStateMachineOfKernelEnvironment(
3884 AssumedUseGenericStateMachineC);
3890 if (!OMPInfoCache.RFIs[RFKind].Declaration)
3892 A.registerVirtualUseCallback(*OMPInfoCache.RFIs[RFKind].Declaration, CB);
3896 auto AddDependence = [](
Attributor &
A,
const AAKernelInfo *KI,
3913 if (SPMDCompatibilityTracker.isValidState())
3914 return AddDependence(
A,
this, QueryingAA);
3916 if (!ReachedKnownParallelRegions.isValidState())
3917 return AddDependence(
A,
this, QueryingAA);
3923 RegisterVirtualUse(OMPRTL___kmpc_get_hardware_num_threads_in_block,
3924 CustomStateMachineUseCB);
3925 RegisterVirtualUse(OMPRTL___kmpc_get_warp_size, CustomStateMachineUseCB);
3926 RegisterVirtualUse(OMPRTL___kmpc_barrier_simple_generic,
3927 CustomStateMachineUseCB);
3928 RegisterVirtualUse(OMPRTL___kmpc_kernel_parallel,
3929 CustomStateMachineUseCB);
3930 RegisterVirtualUse(OMPRTL___kmpc_kernel_end_parallel,
3931 CustomStateMachineUseCB);
3935 if (SPMDCompatibilityTracker.isAtFixpoint())
3942 if (!SPMDCompatibilityTracker.isValidState())
3943 return AddDependence(
A,
this, QueryingAA);
3946 RegisterVirtualUse(OMPRTL___kmpc_get_hardware_thread_id_in_block,
3955 if (!SPMDCompatibilityTracker.isValidState())
3956 return AddDependence(
A,
this, QueryingAA);
3957 if (SPMDCompatibilityTracker.empty())
3958 return AddDependence(
A,
this, QueryingAA);
3959 if (!mayContainParallelRegion())
3960 return AddDependence(
A,
this, QueryingAA);
3963 RegisterVirtualUse(OMPRTL___kmpc_barrier_simple_spmd, SPMDBarrierUseCB);
3967 static std::string sanitizeForGlobalName(std::string S) {
3971 return !((C >=
'a' && C <=
'z') || (C >=
'A' && C <=
'Z') ||
3972 (C >=
'0' && C <=
'9') || C ==
'_');
3983 if (!KernelInitCB || !KernelDeinitCB)
3984 return ChangeStatus::UNCHANGED;
3988 bool HasBuiltStateMachine =
true;
3989 if (!changeToSPMDMode(
A,
Changed)) {
3991 HasBuiltStateMachine = buildCustomStateMachine(
A,
Changed);
3993 HasBuiltStateMachine =
false;
3997 ConstantStruct *ExistingKernelEnvC =
3999 ConstantInt *OldUseGenericStateMachineVal =
4000 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
4001 ExistingKernelEnvC);
4002 if (!HasBuiltStateMachine)
4003 setUseGenericStateMachineOfKernelEnvironment(
4004 OldUseGenericStateMachineVal);
4007 GlobalVariable *KernelEnvGV =
4011 Changed = ChangeStatus::CHANGED;
4017 void insertInstructionGuardsHelper(Attributor &
A) {
4018 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4020 auto CreateGuardedRegion = [&](
Instruction *RegionStartI,
4022 LoopInfo *LI =
nullptr;
4023 DominatorTree *DT =
nullptr;
4024 MemorySSAUpdater *MSU =
nullptr;
4054 DT, LI, MSU,
"region.guarded.end");
4057 MSU,
"region.barrier");
4060 DT, LI, MSU,
"region.exit");
4062 SplitBlock(ParentBB, RegionStartI, DT, LI, MSU,
"region.guarded");
4065 "Expected a different CFG");
4068 ParentBB, ParentBB->
getTerminator(), DT, LI, MSU,
"region.check.tid");
4071 A.registerManifestAddedBasicBlock(*RegionEndBB);
4072 A.registerManifestAddedBasicBlock(*RegionBarrierBB);
4073 A.registerManifestAddedBasicBlock(*RegionExitBB);
4074 A.registerManifestAddedBasicBlock(*RegionStartBB);
4075 A.registerManifestAddedBasicBlock(*RegionCheckTidBB);
4077 bool HasBroadcastValues =
false;
4080 for (Instruction &
I : *RegionStartBB) {
4082 for (Use &U :
I.uses()) {
4088 if (OutsideUses.
empty())
4091 HasBroadcastValues =
true;
4095 auto *SharedMem =
new GlobalVariable(
4096 M,
I.getType(),
false,
4098 sanitizeForGlobalName(
4099 (
I.getName() +
".guarded.output.alloc").str()),
4101 static_cast<unsigned>(AddressSpace::Shared));
4104 new StoreInst(&
I, SharedMem,
4107 LoadInst *LoadI =
new LoadInst(
4108 I.getType(), SharedMem,
I.getName() +
".guarded.output.load",
4112 for (Use *U : OutsideUses)
4113 A.changeUseAfterManifest(*U, *LoadI);
4116 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4121 OpenMPIRBuilder::LocationDescription Loc(
4122 InsertPointTy(ParentBB, ParentBB->
end()),
DL);
4124 uint32_t SrcLocStrSize;
4133 OpenMPIRBuilder::LocationDescription LocRegionCheckTid(
4134 InsertPointTy(RegionCheckTidBB, RegionCheckTidBB->
end()),
DL);
4136 FunctionCallee HardwareTidFn =
4138 M, OMPRTL___kmpc_get_hardware_thread_id_in_block);
4142 OMPInfoCache.setCallingConvention(HardwareTidFn, Tid);
4144 OMPInfoCache.OMPBuilder.
Builder
4145 .
CreateCondBr(TidCheck, RegionStartBB, RegionBarrierBB)
4150 FunctionCallee BarrierFn =
4152 M, OMPRTL___kmpc_barrier_simple_spmd);
4158 OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
4161 if (HasBroadcastValues) {
4166 OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
4170 auto &AllocSharedRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
4171 SmallPtrSet<BasicBlock *, 8> Visited;
4172 for (Instruction *GuardedI : SPMDCompatibilityTracker) {
4174 if (!Visited.
insert(BB).second)
4180 while (++IP != IPEnd) {
4181 if (!IP->mayHaveSideEffects() && !IP->mayReadFromMemory())
4184 if (OpenMPOpt::getCallIfRegularCall(*
I, &AllocSharedRFI))
4186 if (!
I->user_empty() || !SPMDCompatibilityTracker.contains(
I)) {
4187 LastEffect =
nullptr;
4194 for (
auto &Reorder : Reorders)
4195 Reorder.first->moveBefore(Reorder.second->getIterator());
4200 for (Instruction *GuardedI : SPMDCompatibilityTracker) {
4202 auto *CalleeAA =
A.lookupAAFor<AAKernelInfo>(
4205 assert(CalleeAA !=
nullptr &&
"Expected Callee AAKernelInfo");
4208 if (CalleeAAFunction.getGuardedInstructions().contains(GuardedI))
4211 Instruction *GuardedRegionStart =
nullptr, *GuardedRegionEnd =
nullptr;
4212 for (Instruction &
I : *BB) {
4215 if (SPMDCompatibilityTracker.contains(&
I)) {
4216 CalleeAAFunction.getGuardedInstructions().insert(&
I);
4217 if (GuardedRegionStart)
4218 GuardedRegionEnd = &
I;
4220 GuardedRegionStart = GuardedRegionEnd = &
I;
4227 if (GuardedRegionStart) {
4229 std::make_pair(GuardedRegionStart, GuardedRegionEnd));
4230 GuardedRegionStart =
nullptr;
4231 GuardedRegionEnd =
nullptr;
4236 for (
auto &GR : GuardedRegions)
4237 CreateGuardedRegion(GR.first, GR.second);
4240 void forceSingleThreadPerWorkgroupHelper(Attributor &
A) {
4249 auto &Ctx = getAnchorValue().getContext();
4256 KernelInitCB->
getNextNode(),
"main.thread.user_code");
4261 A.registerManifestAddedBasicBlock(*InitBB);
4262 A.registerManifestAddedBasicBlock(*UserCodeBB);
4263 A.registerManifestAddedBasicBlock(*ReturnBB);
4272 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4273 FunctionCallee ThreadIdInBlockFn =
4275 M, OMPRTL___kmpc_get_hardware_thread_id_in_block);
4278 CallInst *ThreadIdInBlock =
4280 OMPInfoCache.setCallingConvention(ThreadIdInBlockFn, ThreadIdInBlock);
4286 ConstantInt::get(ThreadIdInBlock->
getType(), 0),
4287 "thread.is_main", InitBB);
4293 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4295 if (!SPMDCompatibilityTracker.isAssumed()) {
4296 for (Instruction *NonCompatibleI : SPMDCompatibilityTracker) {
4297 if (!NonCompatibleI)
4302 if (OMPInfoCache.RTLFunctions.contains(CB->getCalledFunction()))
4305 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
4306 ORA <<
"Value has potential side effects preventing SPMD-mode "
4309 ORA <<
". Add `[[omp::assume(\"ompx_spmd_amenable\")]]` to "
4310 "the called function to override";
4314 A.emitRemark<OptimizationRemarkAnalysis>(NonCompatibleI,
"OMP121",
4318 << *NonCompatibleI <<
"\n");
4330 Kernel = CB->getCaller();
4335 ConstantStruct *ExistingKernelEnvC =
4338 KernelInfo::getExecModeFromKernelEnvironment(ExistingKernelEnvC);
4344 Changed = ChangeStatus::CHANGED;
4348 if (mayContainParallelRegion())
4349 insertInstructionGuardsHelper(
A);
4351 forceSingleThreadPerWorkgroupHelper(
A);
4356 "Initially non-SPMD kernel has SPMD exec mode!");
4357 setExecModeOfKernelEnvironment(
4361 ++NumOpenMPTargetRegionKernelsSPMD;
4365 OMPInfoCache.SPMDizedKernels.insert(
Kernel);
4367 auto Remark = [&](OptimizationRemark
OR) {
4368 return OR <<
"Transformed generic-mode kernel to SPMD-mode.";
4370 A.emitRemark<OptimizationRemark>(KernelInitCB,
"OMP120",
Remark);
4380 if (!ReachedKnownParallelRegions.isValidState())
4383 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4384 if (!OMPInfoCache.runtimeFnsAvailable(
4385 {OMPRTL___kmpc_get_hardware_num_threads_in_block,
4386 OMPRTL___kmpc_get_warp_size, OMPRTL___kmpc_barrier_simple_generic,
4387 OMPRTL___kmpc_kernel_parallel, OMPRTL___kmpc_kernel_end_parallel}))
4390 ConstantStruct *ExistingKernelEnvC =
4397 ConstantInt *UseStateMachineC =
4398 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
4399 ExistingKernelEnvC);
4400 ConstantInt *ModeC =
4401 KernelInfo::getExecModeFromKernelEnvironment(ExistingKernelEnvC);
4406 if (UseStateMachineC->
isZero() ||
4410 Changed = ChangeStatus::CHANGED;
4413 setUseGenericStateMachineOfKernelEnvironment(
4420 if (!mayContainParallelRegion()) {
4421 ++NumOpenMPTargetRegionKernelsWithoutStateMachine;
4423 auto Remark = [&](OptimizationRemark
OR) {
4424 return OR <<
"Removing unused state machine from generic-mode kernel.";
4426 A.emitRemark<OptimizationRemark>(KernelInitCB,
"OMP130",
Remark);
4432 if (ReachedUnknownParallelRegions.empty()) {
4433 ++NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback;
4435 auto Remark = [&](OptimizationRemark
OR) {
4436 return OR <<
"Rewriting generic-mode kernel with a customized state "
4439 A.emitRemark<OptimizationRemark>(KernelInitCB,
"OMP131",
Remark);
4441 ++NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback;
4443 auto Remark = [&](OptimizationRemarkAnalysis
OR) {
4444 return OR <<
"Generic-mode kernel is executed with a customized state "
4445 "machine that requires a fallback.";
4447 A.emitRemark<OptimizationRemarkAnalysis>(KernelInitCB,
"OMP132",
Remark);
4450 for (CallBase *UnknownParallelRegionCB : ReachedUnknownParallelRegions) {
4451 if (!UnknownParallelRegionCB)
4453 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
4454 return ORA <<
"Call may contain unknown parallel regions. Use "
4455 <<
"`[[omp::assume(\"omp_no_parallelism\")]]` to "
4458 A.emitRemark<OptimizationRemarkAnalysis>(UnknownParallelRegionCB,
4493 auto &Ctx = getAnchorValue().getContext();
4497 BasicBlock *InitBB = KernelInitCB->getParent();
4499 KernelInitCB->getNextNode(),
"thread.user_code.check");
4503 Ctx,
"worker_state_machine.begin",
Kernel, UserCodeEntryBB);
4505 Ctx,
"worker_state_machine.finished",
Kernel, UserCodeEntryBB);
4507 Ctx,
"worker_state_machine.is_active.check",
Kernel, UserCodeEntryBB);
4510 Kernel, UserCodeEntryBB);
4513 Kernel, UserCodeEntryBB);
4515 Ctx,
"worker_state_machine.done.barrier",
Kernel, UserCodeEntryBB);
4516 A.registerManifestAddedBasicBlock(*InitBB);
4517 A.registerManifestAddedBasicBlock(*UserCodeEntryBB);
4518 A.registerManifestAddedBasicBlock(*IsWorkerCheckBB);
4519 A.registerManifestAddedBasicBlock(*StateMachineBeginBB);
4520 A.registerManifestAddedBasicBlock(*StateMachineFinishedBB);
4521 A.registerManifestAddedBasicBlock(*StateMachineIsActiveCheckBB);
4522 A.registerManifestAddedBasicBlock(*StateMachineIfCascadeCurrentBB);
4523 A.registerManifestAddedBasicBlock(*StateMachineEndParallelBB);
4524 A.registerManifestAddedBasicBlock(*StateMachineDoneBarrierBB);
4526 const DebugLoc &DLoc = KernelInitCB->getDebugLoc();
4532 ConstantInt::getAllOnesValue(KernelInitCB->getType()),
4533 "thread.is_worker", InitBB);
4538 FunctionCallee BlockHwSizeFn =
4540 M, OMPRTL___kmpc_get_hardware_num_threads_in_block);
4541 FunctionCallee WarpSizeFn =
4543 M, OMPRTL___kmpc_get_warp_size);
4544 CallInst *BlockHwSize =
4546 OMPInfoCache.setCallingConvention(BlockHwSizeFn, BlockHwSize);
4548 CallInst *WarpSize =
4550 OMPInfoCache.setCallingConvention(WarpSizeFn, WarpSize);
4553 BlockHwSize, WarpSize,
"block.size", IsWorkerCheckBB);
4557 "thread.is_main_or_worker", IsWorkerCheckBB);
4560 StateMachineFinishedBB, IsWorkerCheckBB);
4563 const DataLayout &
DL =
M.getDataLayout();
4564 Type *VoidPtrTy = PointerType::getUnqual(Ctx);
4566 new AllocaInst(VoidPtrTy,
DL.getAllocaAddrSpace(),
nullptr,
4571 OpenMPIRBuilder::LocationDescription(
4572 IRBuilder<>::InsertPoint(StateMachineBeginBB,
4573 StateMachineBeginBB->
end()),
4576 Value *Ident = KernelInfo::getIdentFromKernelEnvironment(KernelEnvC);
4577 Value *GTid = KernelInitCB;
4579 FunctionCallee BarrierFn =
4581 M, OMPRTL___kmpc_barrier_simple_generic);
4584 OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
4588 (
unsigned int)AddressSpace::Generic) {
4589 WorkFnAI =
new AddrSpaceCastInst(
4590 WorkFnAI, PointerType::get(Ctx, (
unsigned int)AddressSpace::Generic),
4591 WorkFnAI->
getName() +
".generic", StateMachineBeginBB);
4595 FunctionCallee KernelParallelFn =
4597 M, OMPRTL___kmpc_kernel_parallel);
4599 KernelParallelFn, {WorkFnAI},
"worker.is_active", StateMachineBeginBB);
4600 OMPInfoCache.setCallingConvention(KernelParallelFn, IsActiveWorker);
4602 Instruction *WorkFn =
new LoadInst(VoidPtrTy, WorkFnAI,
"worker.work_fn",
4603 StateMachineBeginBB);
4606 FunctionType *ParallelRegionFnTy = FunctionType::get(
4607 Type::getVoidTy(Ctx), {Type::getInt16Ty(Ctx), Type::getInt32Ty(Ctx)},
4613 StateMachineBeginBB);
4614 IsDone->setDebugLoc(DLoc);
4616 StateMachineIsActiveCheckBB, StateMachineBeginBB)
4620 StateMachineDoneBarrierBB, StateMachineIsActiveCheckBB)
4626 const unsigned int WrapperFunctionArgNo = 6;
4631 for (
int I = 0,
E = ReachedKnownParallelRegions.size();
I <
E; ++
I) {
4632 auto *CB = ReachedKnownParallelRegions[
I];
4634 CB->getArgOperand(WrapperFunctionArgNo)->stripPointerCasts());
4636 Ctx,
"worker_state_machine.parallel_region.execute",
Kernel,
4637 StateMachineEndParallelBB);
4639 ->setDebugLoc(DLoc);
4645 Kernel, StateMachineEndParallelBB);
4646 A.registerManifestAddedBasicBlock(*PRExecuteBB);
4647 A.registerManifestAddedBasicBlock(*PRNextBB);
4652 if (
I + 1 <
E || !ReachedUnknownParallelRegions.empty()) {
4655 "worker.check_parallel_region", StateMachineIfCascadeCurrentBB);
4663 StateMachineIfCascadeCurrentBB)
4665 StateMachineIfCascadeCurrentBB = PRNextBB;
4671 if (!ReachedUnknownParallelRegions.empty()) {
4672 StateMachineIfCascadeCurrentBB->
setName(
4673 "worker_state_machine.parallel_region.fallback.execute");
4675 StateMachineIfCascadeCurrentBB)
4676 ->setDebugLoc(DLoc);
4679 StateMachineIfCascadeCurrentBB)
4682 FunctionCallee EndParallelFn =
4684 M, OMPRTL___kmpc_kernel_end_parallel);
4685 CallInst *EndParallel =
4687 OMPInfoCache.setCallingConvention(EndParallelFn, EndParallel);
4693 ->setDebugLoc(DLoc);
4703 KernelInfoState StateBefore = getState();
4709 struct UpdateKernelEnvCRAII {
4710 AAKernelInfoFunction &AA;
4712 UpdateKernelEnvCRAII(AAKernelInfoFunction &AA) : AA(AA) {}
4714 ~UpdateKernelEnvCRAII() {
4718 ConstantStruct *ExistingKernelEnvC =
4721 if (!AA.isValidState()) {
4722 AA.KernelEnvC = ExistingKernelEnvC;
4726 if (!AA.ReachedKnownParallelRegions.isValidState())
4727 AA.setUseGenericStateMachineOfKernelEnvironment(
4728 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
4729 ExistingKernelEnvC));
4731 if (!AA.SPMDCompatibilityTracker.isValidState())
4732 AA.setExecModeOfKernelEnvironment(
4733 KernelInfo::getExecModeFromKernelEnvironment(ExistingKernelEnvC));
4735 ConstantInt *MayUseNestedParallelismC =
4736 KernelInfo::getMayUseNestedParallelismFromKernelEnvironment(
4738 ConstantInt *NewMayUseNestedParallelismC = ConstantInt::get(
4739 MayUseNestedParallelismC->
getIntegerType(), AA.NestedParallelism);
4740 AA.setMayUseNestedParallelismOfKernelEnvironment(
4741 NewMayUseNestedParallelismC);
4751 if (!
I.mayWriteToMemory())
4754 const auto *UnderlyingObjsAA =
A.getAAFor<AAUnderlyingObjects>(
4756 DepClassTy::OPTIONAL);
4757 auto *
HS =
A.getAAFor<AAHeapToStack>(
4759 DepClassTy::OPTIONAL);
4760 if (UnderlyingObjsAA &&
4761 UnderlyingObjsAA->forallUnderlyingObjects([&](
Value &Obj) {
4762 if (AA::isAssumedThreadLocalObject(A, Obj, *this))
4766 auto *CB = dyn_cast<CallBase>(&Obj);
4767 return CB && HS && HS->isAssumedHeapToStack(*CB);
4773 SPMDCompatibilityTracker.insert(&
I);
4777 bool UsedAssumedInformationInCheckRWInst =
false;
4778 if (!SPMDCompatibilityTracker.isAtFixpoint())
4779 if (!
A.checkForAllReadWriteInstructions(
4780 CheckRWInst, *
this, UsedAssumedInformationInCheckRWInst))
4781 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4783 bool UsedAssumedInformationFromReachingKernels =
false;
4784 if (!IsKernelEntry) {
4785 updateParallelLevels(
A);
4787 bool AllReachingKernelsKnown =
true;
4788 updateReachingKernelEntries(
A, AllReachingKernelsKnown);
4789 UsedAssumedInformationFromReachingKernels = !AllReachingKernelsKnown;
4791 if (!SPMDCompatibilityTracker.empty()) {
4792 if (!ParallelLevels.isValidState())
4793 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4794 else if (!ReachingKernelEntries.isValidState())
4795 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4801 for (
auto *
Kernel : ReachingKernelEntries) {
4802 auto *CBAA =
A.getAAFor<AAKernelInfo>(
4804 if (CBAA && CBAA->SPMDCompatibilityTracker.isValidState() &&
4805 CBAA->SPMDCompatibilityTracker.isAssumed())
4809 if (!CBAA || !CBAA->SPMDCompatibilityTracker.isAtFixpoint())
4810 UsedAssumedInformationFromReachingKernels =
true;
4812 if (SPMD != 0 &&
Generic != 0)
4813 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4819 bool AllParallelRegionStatesWereFixed =
true;
4820 bool AllSPMDStatesWereFixed =
true;
4823 auto *CBAA =
A.getAAFor<AAKernelInfo>(
4827 getState() ^= CBAA->getState();
4828 AllSPMDStatesWereFixed &= CBAA->SPMDCompatibilityTracker.isAtFixpoint();
4829 AllParallelRegionStatesWereFixed &=
4830 CBAA->ReachedKnownParallelRegions.isAtFixpoint();
4831 AllParallelRegionStatesWereFixed &=
4832 CBAA->ReachedUnknownParallelRegions.isAtFixpoint();
4836 bool UsedAssumedInformationInCheckCallInst =
false;
4837 if (!
A.checkForAllCallLikeInstructions(
4838 CheckCallInst, *
this, UsedAssumedInformationInCheckCallInst)) {
4840 <<
"Failed to visit all call-like instructions!\n";);
4841 return indicatePessimisticFixpoint();
4846 if (!UsedAssumedInformationInCheckCallInst &&
4847 AllParallelRegionStatesWereFixed) {
4848 ReachedKnownParallelRegions.indicateOptimisticFixpoint();
4849 ReachedUnknownParallelRegions.indicateOptimisticFixpoint();
4854 if (!UsedAssumedInformationInCheckRWInst &&
4855 !UsedAssumedInformationInCheckCallInst &&
4856 !UsedAssumedInformationFromReachingKernels && AllSPMDStatesWereFixed)
4857 SPMDCompatibilityTracker.indicateOptimisticFixpoint();
4859 return StateBefore == getState() ? ChangeStatus::UNCHANGED
4860 : ChangeStatus::CHANGED;
4865 void updateReachingKernelEntries(Attributor &
A,
4866 bool &AllReachingKernelsKnown) {
4867 auto PredCallSite = [&](AbstractCallSite ACS) {
4870 assert(Caller &&
"Caller is nullptr");
4872 auto *CAA =
A.getOrCreateAAFor<AAKernelInfo>(
4874 if (CAA && CAA->ReachingKernelEntries.isValidState()) {
4875 ReachingKernelEntries ^= CAA->ReachingKernelEntries;
4881 ReachingKernelEntries.indicatePessimisticFixpoint();
4886 if (!
A.checkForAllCallSites(PredCallSite, *
this,
4888 AllReachingKernelsKnown))
4889 ReachingKernelEntries.indicatePessimisticFixpoint();
4893 void updateParallelLevels(Attributor &
A) {
4894 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4895 OMPInformationCache::RuntimeFunctionInfo &Parallel60RFI =
4896 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
4898 auto PredCallSite = [&](AbstractCallSite ACS) {
4901 assert(Caller &&
"Caller is nullptr");
4905 if (CAA && CAA->ParallelLevels.isValidState()) {
4911 if (Caller == Parallel60RFI.Declaration) {
4912 ParallelLevels.indicatePessimisticFixpoint();
4916 ParallelLevels ^= CAA->ParallelLevels;
4923 ParallelLevels.indicatePessimisticFixpoint();
4928 bool AllCallSitesKnown =
true;
4929 if (!
A.checkForAllCallSites(PredCallSite, *
this,
4932 ParallelLevels.indicatePessimisticFixpoint();
4939struct AAKernelInfoCallSite : AAKernelInfo {
4940 AAKernelInfoCallSite(
const IRPosition &IRP, Attributor &
A)
4941 : AAKernelInfo(IRP,
A) {}
4945 AAKernelInfo::initialize(
A);
4948 auto *AssumptionAA =
A.getAAFor<AAAssumptionInfo>(
4952 if (AssumptionAA && AssumptionAA->hasAssumption(
"ompx_spmd_amenable")) {
4953 indicateOptimisticFixpoint();
4961 indicateOptimisticFixpoint();
4970 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4971 const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee);
4972 if (It == OMPInfoCache.RuntimeFunctionIDMap.end()) {
4974 if (!Callee || !
A.isFunctionIPOAmendable(*Callee)) {
4978 if (!AssumptionAA ||
4979 !(AssumptionAA->hasAssumption(
"omp_no_openmp") ||
4980 AssumptionAA->hasAssumption(
"omp_no_parallelism")))
4981 ReachedUnknownParallelRegions.insert(&CB);
4985 if (!SPMDCompatibilityTracker.isAtFixpoint()) {
4986 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4987 SPMDCompatibilityTracker.insert(&CB);
4992 indicateOptimisticFixpoint();
4998 if (NumCallees > 1) {
4999 indicatePessimisticFixpoint();
5006 case OMPRTL___kmpc_is_spmd_exec_mode:
5007 case OMPRTL___kmpc_distribute_static_fini:
5008 case OMPRTL___kmpc_for_static_fini:
5009 case OMPRTL___kmpc_global_thread_num:
5010 case OMPRTL___kmpc_get_hardware_num_threads_in_block:
5011 case OMPRTL___kmpc_get_hardware_num_blocks:
5012 case OMPRTL___kmpc_single:
5013 case OMPRTL___kmpc_end_single:
5014 case OMPRTL___kmpc_master:
5015 case OMPRTL___kmpc_end_master:
5016 case OMPRTL___kmpc_barrier:
5017 case OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2:
5018 case OMPRTL___kmpc_gpu_xteam_reduce_nowait:
5019 case OMPRTL___kmpc_error:
5020 case OMPRTL___kmpc_flush:
5021 case OMPRTL___kmpc_get_hardware_thread_id_in_block:
5022 case OMPRTL___kmpc_get_warp_size:
5023 case OMPRTL_omp_get_thread_num:
5024 case OMPRTL_omp_get_num_threads:
5025 case OMPRTL_omp_get_max_threads:
5026 case OMPRTL_omp_in_parallel:
5027 case OMPRTL_omp_get_dynamic:
5028 case OMPRTL_omp_get_cancellation:
5029 case OMPRTL_omp_get_nested:
5030 case OMPRTL_omp_get_schedule:
5031 case OMPRTL_omp_get_thread_limit:
5032 case OMPRTL_omp_get_supported_active_levels:
5033 case OMPRTL_omp_get_max_active_levels:
5034 case OMPRTL_omp_get_level:
5035 case OMPRTL_omp_get_ancestor_thread_num:
5036 case OMPRTL_omp_get_team_size:
5037 case OMPRTL_omp_get_active_level:
5038 case OMPRTL_omp_in_final:
5039 case OMPRTL_omp_get_proc_bind:
5040 case OMPRTL_omp_get_num_places:
5041 case OMPRTL_omp_get_num_procs:
5042 case OMPRTL_omp_get_place_proc_ids:
5043 case OMPRTL_omp_get_place_num:
5044 case OMPRTL_omp_get_partition_num_places:
5045 case OMPRTL_omp_get_partition_place_nums:
5046 case OMPRTL_omp_get_wtime:
5048 case OMPRTL___kmpc_distribute_static_init_4:
5049 case OMPRTL___kmpc_distribute_static_init_4u:
5050 case OMPRTL___kmpc_distribute_static_init_8:
5051 case OMPRTL___kmpc_distribute_static_init_8u:
5052 case OMPRTL___kmpc_for_static_init_4:
5053 case OMPRTL___kmpc_for_static_init_4u:
5054 case OMPRTL___kmpc_for_static_init_8:
5055 case OMPRTL___kmpc_for_static_init_8u: {
5057 unsigned ScheduleArgOpNo = 2;
5058 auto *ScheduleTypeCI =
5060 unsigned ScheduleTypeVal =
5061 ScheduleTypeCI ? ScheduleTypeCI->getZExtValue() : 0;
5063 case OMPScheduleType::UnorderedStatic:
5064 case OMPScheduleType::UnorderedStaticChunked:
5065 case OMPScheduleType::OrderedDistribute:
5066 case OMPScheduleType::OrderedDistributeChunked:
5069 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5070 SPMDCompatibilityTracker.insert(&CB);
5074 case OMPRTL___kmpc_target_init:
5077 case OMPRTL___kmpc_target_deinit:
5078 KernelDeinitCB = &CB;
5080 case OMPRTL___kmpc_parallel_60:
5081 if (!handleParallel60(
A, CB))
5082 indicatePessimisticFixpoint();
5084 case OMPRTL___kmpc_omp_task:
5086 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5087 SPMDCompatibilityTracker.insert(&CB);
5088 ReachedUnknownParallelRegions.insert(&CB);
5090 case OMPRTL___kmpc_alloc_shared:
5091 case OMPRTL___kmpc_free_shared:
5094 case OMPRTL___kmpc_distribute_static_loop_4:
5095 case OMPRTL___kmpc_distribute_static_loop_4u:
5096 case OMPRTL___kmpc_distribute_static_loop_8:
5097 case OMPRTL___kmpc_distribute_static_loop_8u:
5098 case OMPRTL___kmpc_distribute_for_static_loop_4:
5099 case OMPRTL___kmpc_distribute_for_static_loop_4u:
5100 case OMPRTL___kmpc_distribute_for_static_loop_8:
5101 case OMPRTL___kmpc_distribute_for_static_loop_8u:
5102 case OMPRTL___kmpc_for_static_loop_4:
5103 case OMPRTL___kmpc_for_static_loop_4u:
5104 case OMPRTL___kmpc_for_static_loop_8:
5105 case OMPRTL___kmpc_for_static_loop_8u:
5106 handleStaticLoop(
A, CB);
5111 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5112 SPMDCompatibilityTracker.insert(&CB);
5118 indicateOptimisticFixpoint();
5122 A.getAAFor<AACallEdges>(*
this, getIRPosition(), DepClassTy::OPTIONAL);
5123 if (!AACE || !AACE->getState().isValidState() || AACE->hasUnknownCallee()) {
5124 CheckCallee(getAssociatedFunction(), 1);
5127 const auto &OptimisticEdges = AACE->getOptimisticEdges();
5128 for (
auto *Callee : OptimisticEdges) {
5129 CheckCallee(Callee, OptimisticEdges.size());
5140 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5141 KernelInfoState StateBefore = getState();
5143 auto CheckCallee = [&](
Function *
F,
int NumCallees) {
5144 const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(
F);
5148 if (It == OMPInfoCache.RuntimeFunctionIDMap.end()) {
5151 A.getAAFor<AAKernelInfo>(*
this, FnPos, DepClassTy::REQUIRED);
5153 return indicatePessimisticFixpoint();
5154 if (getState() == FnAA->getState())
5155 return ChangeStatus::UNCHANGED;
5156 getState() = FnAA->getState();
5157 return ChangeStatus::CHANGED;
5160 return indicatePessimisticFixpoint();
5163 if (isStaticLoopRTL(It->getSecond())) {
5164 handleStaticLoop(
A, CB);
5165 return StateBefore == getState() ? ChangeStatus::UNCHANGED
5166 : ChangeStatus::CHANGED;
5169 if (It->getSecond() == OMPRTL___kmpc_parallel_60) {
5170 if (!handleParallel60(
A, CB))
5171 return indicatePessimisticFixpoint();
5172 return StateBefore == getState() ? ChangeStatus::UNCHANGED
5173 : ChangeStatus::CHANGED;
5179 (It->getSecond() == OMPRTL___kmpc_alloc_shared ||
5180 It->getSecond() == OMPRTL___kmpc_free_shared) &&
5181 "Expected a __kmpc_alloc_shared or __kmpc_free_shared runtime call");
5183 auto *HeapToStackAA =
A.getAAFor<AAHeapToStack>(
5185 auto *HeapToSharedAA =
A.getAAFor<AAHeapToShared>(
5193 case OMPRTL___kmpc_alloc_shared:
5194 if ((!HeapToStackAA || !HeapToStackAA->isAssumedHeapToStack(CB)) &&
5195 (!HeapToSharedAA || !HeapToSharedAA->isAssumedHeapToShared(CB)))
5196 SPMDCompatibilityTracker.insert(&CB);
5198 case OMPRTL___kmpc_free_shared:
5199 if ((!HeapToStackAA ||
5200 !HeapToStackAA->isAssumedHeapToStackRemovedFree(CB)) &&
5202 !HeapToSharedAA->isAssumedHeapToSharedRemovedFree(CB)))
5203 SPMDCompatibilityTracker.insert(&CB);
5206 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5207 SPMDCompatibilityTracker.insert(&CB);
5209 return ChangeStatus::CHANGED;
5213 A.getAAFor<AACallEdges>(*
this, getIRPosition(), DepClassTy::OPTIONAL);
5214 if (!AACE || !AACE->getState().isValidState() || AACE->hasUnknownCallee()) {
5215 if (Function *
F = getAssociatedFunction())
5218 const auto &OptimisticEdges = AACE->getOptimisticEdges();
5219 for (
auto *Callee : OptimisticEdges) {
5220 CheckCallee(Callee, OptimisticEdges.size());
5226 return StateBefore == getState() ? ChangeStatus::UNCHANGED
5227 : ChangeStatus::CHANGED;
5235 void handleStaticLoop(Attributor &
A, CallBase &CB) {
5236 const unsigned int LoopBodyArgNo = 1;
5243 DepClassTy::OPTIONAL)
5245 if (!BodyAA || !BodyAA->getState().isValidState() ||
5246 !BodyAA->ReachedKnownParallelRegions.isValidState() ||
5247 !BodyAA->ReachedKnownParallelRegions.empty() ||
5248 !BodyAA->ReachedUnknownParallelRegions.isValidState() ||
5249 !BodyAA->ReachedUnknownParallelRegions.empty())
5250 ReachedUnknownParallelRegions.insert(&CB);
5256 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5257 SPMDCompatibilityTracker.insert(&CB);
5262 case OMPRTL___kmpc_distribute_static_loop_4:
5263 case OMPRTL___kmpc_distribute_static_loop_4u:
5264 case OMPRTL___kmpc_distribute_static_loop_8:
5265 case OMPRTL___kmpc_distribute_static_loop_8u:
5266 case OMPRTL___kmpc_distribute_for_static_loop_4:
5267 case OMPRTL___kmpc_distribute_for_static_loop_4u:
5268 case OMPRTL___kmpc_distribute_for_static_loop_8:
5269 case OMPRTL___kmpc_distribute_for_static_loop_8u:
5270 case OMPRTL___kmpc_for_static_loop_4:
5271 case OMPRTL___kmpc_for_static_loop_4u:
5272 case OMPRTL___kmpc_for_static_loop_8:
5273 case OMPRTL___kmpc_for_static_loop_8u:
5280 bool handleParallel60(Attributor &
A, CallBase &CB) {
5281 const unsigned int NonWrapperFunctionArgNo = 5;
5282 const unsigned int WrapperFunctionArgNo = 6;
5283 auto ParallelRegionOpArgNo = SPMDCompatibilityTracker.isAssumed()
5284 ? NonWrapperFunctionArgNo
5285 : WrapperFunctionArgNo;
5289 if (!ParallelRegion)
5292 ReachedKnownParallelRegions.insert(&CB);
5294 auto *FnAA =
A.getAAFor<AAKernelInfo>(
5296 NestedParallelism |= !FnAA || !FnAA->getState().isValidState() ||
5297 !FnAA->ReachedKnownParallelRegions.empty() ||
5298 !FnAA->ReachedKnownParallelRegions.isValidState() ||
5299 !FnAA->ReachedUnknownParallelRegions.isValidState() ||
5300 !FnAA->ReachedUnknownParallelRegions.empty();
5305struct AAFoldRuntimeCall
5306 :
public StateWrapper<BooleanState, AbstractAttribute> {
5307 using Base = StateWrapper<BooleanState, AbstractAttribute>;
5309 AAFoldRuntimeCall(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
5312 void trackStatistics()
const override {}
5315 static AAFoldRuntimeCall &createForPosition(
const IRPosition &IRP,
5319 StringRef
getName()
const override {
return "AAFoldRuntimeCall"; }
5322 const char *getIdAddr()
const override {
return &ID; }
5326 static bool classof(
const AbstractAttribute *AA) {
5330 static const char ID;
5333struct AAFoldRuntimeCallCallSiteReturned : AAFoldRuntimeCall {
5334 AAFoldRuntimeCallCallSiteReturned(
const IRPosition &IRP, Attributor &
A)
5335 : AAFoldRuntimeCall(IRP,
A) {}
5338 const std::string getAsStr(Attributor *)
const override {
5339 if (!isValidState())
5342 std::string Str(
"simplified value: ");
5344 if (!SimplifiedValue)
5345 return Str + std::string(
"none");
5347 if (!*SimplifiedValue)
5348 return Str + std::string(
"nullptr");
5351 return Str + std::to_string(CI->getSExtValue());
5353 return Str + std::string(
"unknown");
5358 indicatePessimisticFixpoint();
5362 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5363 const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee);
5364 assert(It != OMPInfoCache.RuntimeFunctionIDMap.end() &&
5365 "Expected a known OpenMP runtime function");
5367 RFKind = It->getSecond();
5370 A.registerSimplificationCallback(
5372 [&](
const IRPosition &IRP,
const AbstractAttribute *AA,
5373 bool &UsedAssumedInformation) -> std::optional<Value *> {
5374 assert((isValidState() || SimplifiedValue ==
nullptr) &&
5375 "Unexpected invalid state!");
5377 if (!isAtFixpoint()) {
5378 UsedAssumedInformation =
true;
5380 A.recordDependence(*
this, *AA, DepClassTy::OPTIONAL);
5382 return SimplifiedValue;
5389 case OMPRTL___kmpc_is_spmd_exec_mode:
5392 case OMPRTL___kmpc_parallel_level:
5395 case OMPRTL___kmpc_get_hardware_num_threads_in_block:
5396 Changed =
Changed | foldKernelFnAttribute(
A,
"omp_target_thread_limit");
5398 case OMPRTL___kmpc_get_hardware_num_blocks:
5411 if (SimplifiedValue && *SimplifiedValue) {
5414 A.deleteAfterManifest(
I);
5417 auto Remark = [&](OptimizationRemark
OR) {
5419 return OR <<
"Replacing OpenMP runtime call "
5421 <<
ore::NV(
"FoldedValue",
C->getZExtValue()) <<
".";
5422 return OR <<
"Replacing OpenMP runtime call "
5427 A.emitRemark<OptimizationRemark>(CB,
"OMP180",
Remark);
5430 << **SimplifiedValue <<
"\n");
5432 Changed = ChangeStatus::CHANGED;
5439 SimplifiedValue =
nullptr;
5440 return AAFoldRuntimeCall::indicatePessimisticFixpoint();
5446 std::optional<Value *> SimplifiedValueBefore = SimplifiedValue;
5448 unsigned AssumedSPMDCount = 0, KnownSPMDCount = 0;
5449 unsigned AssumedNonSPMDCount = 0, KnownNonSPMDCount = 0;
5450 auto *CallerKernelInfoAA =
A.getAAFor<AAKernelInfo>(
5453 if (!CallerKernelInfoAA ||
5454 !CallerKernelInfoAA->ReachingKernelEntries.isValidState())
5455 return indicatePessimisticFixpoint();
5457 for (
Kernel K : CallerKernelInfoAA->ReachingKernelEntries) {
5459 DepClassTy::REQUIRED);
5461 if (!AA || !AA->isValidState()) {
5462 SimplifiedValue =
nullptr;
5463 return indicatePessimisticFixpoint();
5466 if (AA->SPMDCompatibilityTracker.isAssumed()) {
5467 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5472 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5473 ++KnownNonSPMDCount;
5475 ++AssumedNonSPMDCount;
5479 if ((AssumedSPMDCount + KnownSPMDCount) &&
5480 (AssumedNonSPMDCount + KnownNonSPMDCount))
5481 return indicatePessimisticFixpoint();
5483 auto &Ctx = getAnchorValue().getContext();
5484 if (KnownSPMDCount || AssumedSPMDCount) {
5485 assert(KnownNonSPMDCount == 0 && AssumedNonSPMDCount == 0 &&
5486 "Expected only SPMD kernels!");
5489 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx),
true);
5490 }
else if (KnownNonSPMDCount || AssumedNonSPMDCount) {
5491 assert(KnownSPMDCount == 0 && AssumedSPMDCount == 0 &&
5492 "Expected only non-SPMD kernels!");
5495 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx),
false);
5500 assert(!SimplifiedValue &&
"SimplifiedValue should be none");
5503 return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED
5504 : ChangeStatus::CHANGED;
5509 std::optional<Value *> SimplifiedValueBefore = SimplifiedValue;
5511 auto *CallerKernelInfoAA =
A.getAAFor<AAKernelInfo>(
5514 if (!CallerKernelInfoAA ||
5515 !CallerKernelInfoAA->ParallelLevels.isValidState())
5516 return indicatePessimisticFixpoint();
5518 if (!CallerKernelInfoAA->ReachingKernelEntries.isValidState())
5519 return indicatePessimisticFixpoint();
5521 if (CallerKernelInfoAA->ReachingKernelEntries.empty()) {
5522 assert(!SimplifiedValue &&
5523 "SimplifiedValue should keep none at this point");
5524 return ChangeStatus::UNCHANGED;
5527 unsigned AssumedSPMDCount = 0, KnownSPMDCount = 0;
5528 unsigned AssumedNonSPMDCount = 0, KnownNonSPMDCount = 0;
5529 for (
Kernel K : CallerKernelInfoAA->ReachingKernelEntries) {
5531 DepClassTy::REQUIRED);
5532 if (!AA || !AA->SPMDCompatibilityTracker.isValidState())
5533 return indicatePessimisticFixpoint();
5535 if (AA->SPMDCompatibilityTracker.isAssumed()) {
5536 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5541 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5542 ++KnownNonSPMDCount;
5544 ++AssumedNonSPMDCount;
5548 if ((AssumedSPMDCount + KnownSPMDCount) &&
5549 (AssumedNonSPMDCount + KnownNonSPMDCount))
5550 return indicatePessimisticFixpoint();
5552 auto &Ctx = getAnchorValue().getContext();
5556 if (AssumedSPMDCount || KnownSPMDCount) {
5557 assert(KnownNonSPMDCount == 0 && AssumedNonSPMDCount == 0 &&
5558 "Expected only SPMD kernels!");
5559 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), 1);
5561 assert(KnownSPMDCount == 0 && AssumedSPMDCount == 0 &&
5562 "Expected only non-SPMD kernels!");
5563 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), 0);
5565 return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED
5566 : ChangeStatus::CHANGED;
5569 ChangeStatus foldKernelFnAttribute(Attributor &
A, llvm::StringRef Attr) {
5571 int32_t CurrentAttrValue = -1;
5572 std::optional<Value *> SimplifiedValueBefore = SimplifiedValue;
5574 auto *CallerKernelInfoAA =
A.getAAFor<AAKernelInfo>(
5577 if (!CallerKernelInfoAA ||
5578 !CallerKernelInfoAA->ReachingKernelEntries.isValidState())
5579 return indicatePessimisticFixpoint();
5582 for (
Kernel K : CallerKernelInfoAA->ReachingKernelEntries) {
5583 int32_t NextAttrVal =
K->getFnAttributeAsParsedInteger(Attr, -1);
5585 if (NextAttrVal == -1 ||
5586 (CurrentAttrValue != -1 && CurrentAttrValue != NextAttrVal))
5587 return indicatePessimisticFixpoint();
5588 CurrentAttrValue = NextAttrVal;
5591 if (CurrentAttrValue != -1) {
5592 auto &Ctx = getAnchorValue().getContext();
5594 ConstantInt::get(Type::getInt32Ty(Ctx), CurrentAttrValue);
5596 return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED
5597 : ChangeStatus::CHANGED;
5603 std::optional<Value *> SimplifiedValue;
5613 auto &RFI = OMPInfoCache.RFIs[RF];
5614 RFI.foreachUse(SCC, [&](Use &U, Function &
F) {
5615 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &RFI);
5618 A.getOrCreateAAFor<AAFoldRuntimeCall>(
5620 DepClassTy::NONE,
false,
5626void OpenMPOpt::registerAAs(
bool IsModulePass) {
5636 A.getOrCreateAAFor<AAKernelInfo>(
5638 DepClassTy::NONE,
false,
5642 OMPInformationCache::RuntimeFunctionInfo &InitRFI =
5643 OMPInfoCache.RFIs[OMPRTL___kmpc_target_init];
5644 InitRFI.foreachUse(SCC, CreateKernelInfoCB);
5646 registerFoldRuntimeCall(OMPRTL___kmpc_is_spmd_exec_mode);
5647 registerFoldRuntimeCall(OMPRTL___kmpc_parallel_level);
5648 registerFoldRuntimeCall(OMPRTL___kmpc_get_hardware_num_threads_in_block);
5649 registerFoldRuntimeCall(OMPRTL___kmpc_get_hardware_num_blocks);
5654 for (
int Idx = 0; Idx < OMPInfoCache.ICVs.size() - 1; ++Idx) {
5657 auto &GetterRFI = OMPInfoCache.RFIs[ICVInfo.Getter];
5660 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &GetterRFI);
5667 A.getOrCreateAAFor<AAICVTracker>(CBPos);
5671 GetterRFI.foreachUse(SCC, CreateAA);
5680 for (
auto *
F : SCC) {
5681 if (
F->isDeclaration())
5687 if (
F->hasLocalLinkage()) {
5689 const auto *CB = dyn_cast<CallBase>(U.getUser());
5690 return CB && CB->isCallee(&U) &&
5691 A.isRunOn(const_cast<Function *>(CB->getCaller()));
5695 registerAAsForFunction(
A, *
F);
5699void OpenMPOpt::registerAAsForFunction(Attributor &
A,
const Function &
F) {
5700 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5703 A.getOrCreateAAFor<AAExecutionDomain>(FPos);
5704 if (
F.hasFnAttribute(Attribute::Convergent))
5705 A.getOrCreateAAFor<AANonConvergent>(FPos);
5707 bool FunctionUsesSharedAlloc =
false;
5709 const OMPInformationCache::RuntimeFunctionInfo::UseVector *SharedAllocUses =
5710 OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared].getUseVector(
5712 FunctionUsesSharedAlloc = SharedAllocUses && !SharedAllocUses->
empty();
5714 bool HasHeapToStackCandidate =
false;
5715 const TargetLibraryInfo *TLI =
nullptr;
5719 bool UsedAssumedInformation =
false;
5722 A.getOrCreateAAFor<AAAddressSpace>(
5729 TLI =
A.getInfoCache().getTargetLibraryInfoForFunction(
F);
5730 HasHeapToStackCandidate =
5734 A.getOrCreateAAFor<AAIndirectCallInfo>(
5739 A.getOrCreateAAFor<AAAddressSpace>(
5748 if (
II->getIntrinsicID() == Intrinsic::assume) {
5749 A.getOrCreateAAFor<AAPotentialValues>(
5756 if (FunctionUsesSharedAlloc)
5757 A.getOrCreateAAFor<AAHeapToShared>(FPos);
5758 if (HasHeapToStackCandidate)
5759 A.getOrCreateAAFor<AAHeapToStack>(FPos);
5762const char AAICVTracker::ID = 0;
5763const char AAKernelInfo::ID = 0;
5765const char AAHeapToShared::ID = 0;
5766const char AAFoldRuntimeCall::ID = 0;
5768AAICVTracker &AAICVTracker::createForPosition(
const IRPosition &IRP,
5770 AAICVTracker *AA =
nullptr;
5778 AA =
new (
A.Allocator) AAICVTrackerFunctionReturned(IRP,
A);
5781 AA =
new (
A.Allocator) AAICVTrackerCallSiteReturned(IRP,
A);
5784 AA =
new (
A.Allocator) AAICVTrackerCallSite(IRP,
A);
5787 AA =
new (
A.Allocator) AAICVTrackerFunction(IRP,
A);
5796 AAExecutionDomainFunction *
AA =
nullptr;
5806 "AAExecutionDomain can only be created for function position!");
5808 AA =
new (
A.Allocator) AAExecutionDomainFunction(IRP,
A);
5815AAHeapToShared &AAHeapToShared::createForPosition(
const IRPosition &IRP,
5817 AAHeapToSharedFunction *
AA =
nullptr;
5827 "AAHeapToShared can only be created for function position!");
5829 AA =
new (
A.Allocator) AAHeapToSharedFunction(IRP,
A);
5836AAKernelInfo &AAKernelInfo::createForPosition(
const IRPosition &IRP,
5838 AAKernelInfo *AA =
nullptr;
5848 AA =
new (
A.Allocator) AAKernelInfoCallSite(IRP,
A);
5851 AA =
new (
A.Allocator) AAKernelInfoFunction(IRP,
A);
5858AAFoldRuntimeCall &AAFoldRuntimeCall::createForPosition(
const IRPosition &IRP,
5860 AAFoldRuntimeCall *AA =
nullptr;
5869 llvm_unreachable(
"KernelInfo can only be created for call site position!");
5871 AA =
new (
A.Allocator) AAFoldRuntimeCallCallSiteReturned(IRP,
A);
5892 if (Kernels.contains(&
F))
5894 return !
F.use_empty();
5901 return ORA <<
"Could not internalize function. "
5902 <<
"Some optimizations may not be possible. [OMP140]";
5914 if (!
F.isDeclaration() && !Kernels.contains(&
F) && IsCalled(
F) &&
5918 }
else if (!
F.hasLocalLinkage() && !
F.hasFnAttribute(Attribute::Cold)) {
5931 if (!
F.isDeclaration() && !InternalizedMap.
lookup(&
F)) {
5933 Functions.insert(&
F);
5951 OMPInformationCache InfoCache(M, AG, Allocator,
nullptr, PostLink);
5953 unsigned MaxFixpointIterations =
5965 return F.hasFnAttribute(
"kernel");
5970 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache,
A);
5976 if (!
F.isDeclaration() && !Kernels.contains(&
F) &&
5977 !
F.hasFnAttribute(Attribute::NoInline))
5978 F.addFnAttr(Attribute::AlwaysInline);
6008 Module &M = *
C.begin()->getFunction().getParent();
6030 OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, Allocator,
6031 &Functions, PostLink);
6033 unsigned MaxFixpointIterations =
6047 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache,
A);
6048 bool Changed = OMPOpt.run(
false);
6067 if (
F.hasKernelCallingConv()) {
6072 ++NumOpenMPTargetRegionKernels;
6075 ++NumNonOpenMPTargetRegionKernels;
6082 Metadata *MD = M.getModuleFlag(
"openmp");
6090 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 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< 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 const int BlockSize
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::Optional, 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
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 ...
BasicBlock * getBlock() const
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.
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.
A node in the call graph.
An SCC of the call graph.
A lazily constructed view of the call graph of a module.
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)
DXILDebugInfoMap run(Module &M)
constexpr uint64_t PointerSize
aarch64 pointer size.
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.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
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.
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
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.
LLVM_ABI const Value * getUnderlyingObject(const Value *V, unsigned MaxLookup=MaxLookupSearchDepth)
This method strips off any GEP address adjustments, pointer casts or llvm.threadlocal....
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.
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...
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),...