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 "openmp-opt-max-callees-for-specialization",
cl::Hidden,
154 cl::desc(
"Number of possible callees above which an indirect call site is "
155 "left alone rather than specialized into an if-cascade."),
159 "Number of OpenMP runtime calls deduplicated");
161 "Number of OpenMP parallel regions deleted");
163 "Number of OpenMP runtime functions identified");
165 "Number of OpenMP runtime function uses identified");
167 "Number of OpenMP target region entry points (=kernels) identified");
169 "Number of non-OpenMP target region kernels identified");
171 "Number of OpenMP target region entry points (=kernels) executed in "
172 "SPMD-mode instead of generic-mode");
173STATISTIC(NumOpenMPTargetRegionKernelsWithoutStateMachine,
174 "Number of OpenMP target region entry points (=kernels) executed in "
175 "generic-mode without a state machines");
176STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback,
177 "Number of OpenMP target region entry points (=kernels) executed in "
178 "generic-mode with customized state machines with fallback");
179STATISTIC(NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback,
180 "Number of OpenMP target region entry points (=kernels) executed in "
181 "generic-mode with customized state machines without fallback");
183 NumOpenMPParallelRegionsReplacedInGPUStateMachine,
184 "Number of OpenMP parallel regions replaced with ID in GPU state machines");
186 "Number of OpenMP parallel regions merged");
188 "Amount of memory pushed to shared memory");
189STATISTIC(NumBarriersEliminated,
"Number of redundant barriers eliminated");
217#define KERNEL_ENVIRONMENT_IDX(MEMBER, IDX) \
218 constexpr unsigned MEMBER##Idx = IDX;
223#undef KERNEL_ENVIRONMENT_IDX
225#define KERNEL_ENVIRONMENT_CONFIGURATION_IDX(MEMBER, IDX) \
226 constexpr unsigned MEMBER##Idx = IDX;
236#undef KERNEL_ENVIRONMENT_CONFIGURATION_IDX
238#define KERNEL_ENVIRONMENT_GETTER(MEMBER, RETURNTYPE) \
239 RETURNTYPE *get##MEMBER##FromKernelEnvironment(ConstantStruct *KernelEnvC) { \
240 return cast<RETURNTYPE>(KernelEnvC->getAggregateElement(MEMBER##Idx)); \
246#undef KERNEL_ENVIRONMENT_GETTER
248#define KERNEL_ENVIRONMENT_CONFIGURATION_GETTER(MEMBER) \
249 ConstantInt *get##MEMBER##FromKernelEnvironment( \
250 ConstantStruct *KernelEnvC) { \
251 ConstantStruct *ConfigC = \
252 getConfigurationFromKernelEnvironment(KernelEnvC); \
253 return dyn_cast<ConstantInt>(ConfigC->getAggregateElement(MEMBER##Idx)); \
264#undef KERNEL_ENVIRONMENT_CONFIGURATION_GETTER
268 constexpr int InitKernelEnvironmentArgNo = 0;
283struct AAHeapToShared;
290 OMPInformationCache(
Module &M, AnalysisGetter &AG,
294 OpenMPPostLink(OpenMPPostLink) {
297 const Triple
T(OMPBuilder.M.getTargetTriple());
298 switch (
T.getArch()) {
302 assert(OMPBuilder.Config.IsTargetDevice &&
303 "OpenMP AMDGPU/NVPTX is only prepared to deal with device code.");
304 OMPBuilder.Config.IsGPU =
true;
307 OMPBuilder.Config.IsGPU =
false;
310 OMPBuilder.initialize();
311 initializeRuntimeFunctions(M);
312 initializeInternalControlVars();
316 struct InternalControlVarInfo {
324 StringRef EnvVarName;
330 ConstantInt *InitValue;
343 struct RuntimeFunctionInfo {
364 using UseVector = SmallVector<Use *, 16>;
367 void clearUsesMap() { UsesMap.clear(); }
370 operator bool()
const {
return Declaration; }
373 UseVector &getOrCreateUseVector(
Function *
F) {
374 std::shared_ptr<UseVector> &UV = UsesMap[
F];
376 UV = std::make_shared<UseVector>();
382 const UseVector *getUseVector(
Function &
F)
const {
383 auto I = UsesMap.find(&
F);
384 if (
I != UsesMap.end())
385 return I->second.get();
390 size_t getNumFunctionsWithUses()
const {
return UsesMap.size(); }
394 size_t getNumArgs()
const {
return ArgumentTypes.size(); }
399 void foreachUse(SmallVectorImpl<Function *> &SCC,
400 function_ref<
bool(Use &,
Function &)> CB) {
408 SmallVector<unsigned, 8> ToBeDeleted;
412 UseVector &UV = getOrCreateUseVector(
F);
422 while (!ToBeDeleted.
empty()) {
432 DenseMap<Function *, std::shared_ptr<UseVector>> UsesMap;
436 decltype(UsesMap)::iterator
begin() {
return UsesMap.begin(); }
437 decltype(UsesMap)::iterator
end() {
return UsesMap.end(); }
441 OpenMPIRBuilder OMPBuilder;
445 RuntimeFunction::OMPRTL___last>
449 DenseMap<Function *, RuntimeFunction> RuntimeFunctionIDMap;
453 InternalControlVar::ICV___last>
458 void initializeInternalControlVars() {
459#define ICV_RT_SET(_Name, RTL) \
461 auto &ICV = ICVs[_Name]; \
464#define ICV_RT_GET(Name, RTL) \
466 auto &ICV = ICVs[Name]; \
469#define ICV_DATA_ENV(Enum, _Name, _EnvVarName, Init) \
471 auto &ICV = ICVs[Enum]; \
474 ICV.InitKind = Init; \
475 ICV.EnvVarName = _EnvVarName; \
476 switch (ICV.InitKind) { \
477 case ICV_IMPLEMENTATION_DEFINED: \
478 ICV.InitValue = nullptr; \
481 ICV.InitValue = ConstantInt::get( \
482 Type::getInt32Ty(OMPBuilder.Int32->getContext()), 0); \
485 ICV.InitValue = ConstantInt::getFalse(OMPBuilder.Int1->getContext()); \
491#include "llvm/Frontend/OpenMP/OMPKinds.def"
497 static bool declMatchesRTFTypes(
Function *
F,
Type *RTFRetType,
504 if (
F->getReturnType() != RTFRetType)
506 if (
F->arg_size() != RTFArgTypes.
size())
509 auto *RTFTyIt = RTFArgTypes.
begin();
510 for (Argument &Arg :
F->args()) {
511 if (Arg.getType() != *RTFTyIt)
521 unsigned collectUses(RuntimeFunctionInfo &RFI,
bool CollectStats =
true) {
522 unsigned NumUses = 0;
523 if (!RFI.Declaration)
525 OMPBuilder.addAttributes(RFI.Kind, *RFI.Declaration);
528 NumOpenMPRuntimeFunctionsIdentified += 1;
529 NumOpenMPRuntimeFunctionUsesIdentified += RFI.Declaration->getNumUses();
533 for (Use &U : RFI.Declaration->uses()) {
535 if (!
CGSCC ||
CGSCC->empty() ||
CGSCC->contains(UserI->getFunction())) {
536 RFI.getOrCreateUseVector(UserI->getFunction()).push_back(&U);
540 RFI.getOrCreateUseVector(
nullptr).push_back(&U);
549 auto &RFI = RFIs[RTF];
551 collectUses(RFI,
false);
555 void recollectUses() {
556 for (
int Idx = 0; Idx < RFIs.size(); ++Idx)
561 void setCallingConvention(FunctionCallee Callee, CallInst *CI) {
576 RuntimeFunctionInfo &RFI = RFIs[Fn];
578 if (!RFI.Declaration || RFI.Declaration->isDeclaration())
586 void initializeRuntimeFunctions(
Module &M) {
589#define OMP_TYPE(VarName, ...) \
590 Type *VarName = OMPBuilder.VarName; \
593#define OMP_ARRAY_TYPE(VarName, ...) \
594 ArrayType *VarName##Ty = OMPBuilder.VarName##Ty; \
596 PointerType *VarName##PtrTy = OMPBuilder.VarName##PtrTy; \
597 (void)VarName##PtrTy;
599#define OMP_FUNCTION_TYPE(VarName, ...) \
600 FunctionType *VarName = OMPBuilder.VarName; \
602 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \
605#define OMP_STRUCT_TYPE(VarName, ...) \
606 StructType *VarName = OMPBuilder.VarName; \
608 PointerType *VarName##Ptr = OMPBuilder.VarName##Ptr; \
611#define OMP_RTL(_Enum, _Name, _IsVarArg, _ReturnType, ...) \
613 SmallVector<Type *, 8> ArgsTypes({__VA_ARGS__}); \
614 Function *F = M.getFunction(_Name); \
615 RTLFunctions.insert(F); \
616 if (declMatchesRTFTypes(F, OMPBuilder._ReturnType, ArgsTypes)) { \
617 RuntimeFunctionIDMap[F] = _Enum; \
618 auto &RFI = RFIs[_Enum]; \
621 RFI.IsVarArg = _IsVarArg; \
622 RFI.ReturnType = OMPBuilder._ReturnType; \
623 RFI.ArgumentTypes = std::move(ArgsTypes); \
624 RFI.Declaration = F; \
625 unsigned NumUses = collectUses(RFI); \
628 dbgs() << TAG << RFI.Name << (RFI.Declaration ? "" : " not") \
630 if (RFI.Declaration) \
631 dbgs() << TAG << "-> got " << NumUses << " uses in " \
632 << RFI.getNumFunctionsWithUses() \
633 << " different functions.\n"; \
637#include "llvm/Frontend/OpenMP/OMPKinds.def"
643 for (StringRef Prefix : {
"__kmpc",
"_ZN4ompx",
"omp_"})
644 if (
F.hasFnAttribute(Attribute::NoInline) &&
645 F.getName().starts_with(Prefix) &&
646 !
F.hasFnAttribute(Attribute::OptimizeNone))
647 F.removeFnAttr(Attribute::NoInline);
655 DenseSet<const Function *> RTLFunctions;
658 bool OpenMPPostLink =
false;
665 SmallPtrSet<Function *, 8> SPMDizedKernels;
668template <
typename Ty,
bool InsertInval
idates = true>
670 bool contains(
const Ty &Elem)
const {
return Set.contains(Elem); }
671 bool insert(
const Ty &Elem) {
672 if (InsertInvalidates)
673 BooleanState::indicatePessimisticFixpoint();
674 return Set.insert(Elem);
677 const Ty &operator[](
int Idx)
const {
return Set[Idx]; }
678 bool operator==(
const BooleanStateWithSetVector &
RHS)
const {
679 return BooleanState::operator==(
RHS) && Set ==
RHS.Set;
681 bool operator!=(
const BooleanStateWithSetVector &
RHS)
const {
682 return !(*
this ==
RHS);
685 bool empty()
const {
return Set.empty(); }
686 size_t size()
const {
return Set.size(); }
689 BooleanStateWithSetVector &
operator^=(
const BooleanStateWithSetVector &
RHS) {
690 BooleanState::operator^=(
RHS);
691 Set.insert_range(
RHS.Set);
700 typename decltype(Set)::iterator
begin() {
return Set.begin(); }
701 typename decltype(Set)::iterator
end() {
return Set.end(); }
702 typename decltype(Set)::const_iterator
begin()
const {
return Set.begin(); }
703 typename decltype(Set)::const_iterator
end()
const {
return Set.end(); }
706template <
typename Ty,
bool InsertInval
idates = true>
707using BooleanStateWithPtrSetVector =
708 BooleanStateWithSetVector<Ty *, InsertInvalidates>;
712 bool IsAtFixpoint =
false;
716 BooleanStateWithPtrSetVector<CallBase,
false>
717 ReachedKnownParallelRegions;
720 BooleanStateWithPtrSetVector<CallBase> ReachedUnknownParallelRegions;
725 BooleanStateWithPtrSetVector<Instruction, false> SPMDCompatibilityTracker;
729 CallBase *KernelInitCB =
nullptr;
733 ConstantStruct *KernelEnvC =
nullptr;
737 CallBase *KernelDeinitCB =
nullptr;
740 bool IsKernelEntry =
false;
743 BooleanStateWithPtrSetVector<Function, false> ReachingKernelEntries;
748 BooleanStateWithSetVector<uint8_t> ParallelLevels;
751 bool NestedParallelism =
false;
756 KernelInfoState() =
default;
757 KernelInfoState(
bool BestState) {
759 indicatePessimisticFixpoint();
763 bool isValidState()
const override {
return true; }
766 bool isAtFixpoint()
const override {
return IsAtFixpoint; }
771 ParallelLevels.indicatePessimisticFixpoint();
772 ReachingKernelEntries.indicatePessimisticFixpoint();
773 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
774 ReachedKnownParallelRegions.indicatePessimisticFixpoint();
775 ReachedUnknownParallelRegions.indicatePessimisticFixpoint();
776 NestedParallelism =
true;
777 return ChangeStatus::CHANGED;
783 ParallelLevels.indicateOptimisticFixpoint();
784 ReachingKernelEntries.indicateOptimisticFixpoint();
785 SPMDCompatibilityTracker.indicateOptimisticFixpoint();
786 ReachedKnownParallelRegions.indicateOptimisticFixpoint();
787 ReachedUnknownParallelRegions.indicateOptimisticFixpoint();
788 return ChangeStatus::UNCHANGED;
792 KernelInfoState &getAssumed() {
return *
this; }
793 const KernelInfoState &getAssumed()
const {
return *
this; }
796 if (SPMDCompatibilityTracker !=
RHS.SPMDCompatibilityTracker)
798 if (ReachedKnownParallelRegions !=
RHS.ReachedKnownParallelRegions)
800 if (ReachedUnknownParallelRegions !=
RHS.ReachedUnknownParallelRegions)
802 if (ReachingKernelEntries !=
RHS.ReachingKernelEntries)
804 if (ParallelLevels !=
RHS.ParallelLevels)
806 if (NestedParallelism !=
RHS.NestedParallelism)
812 bool mayContainParallelRegion() {
813 return !ReachedKnownParallelRegions.empty() ||
814 !ReachedUnknownParallelRegions.empty();
818 static KernelInfoState getBestState() {
return KernelInfoState(
true); }
820 static KernelInfoState getBestState(KernelInfoState &KIS) {
821 return getBestState();
825 static KernelInfoState getWorstState() {
return KernelInfoState(
false); }
828 KernelInfoState
operator^=(
const KernelInfoState &KIS) {
830 if (KIS.KernelInitCB) {
831 if (KernelInitCB && KernelInitCB != KIS.KernelInitCB)
834 KernelInitCB = KIS.KernelInitCB;
836 if (KIS.KernelDeinitCB) {
837 if (KernelDeinitCB && KernelDeinitCB != KIS.KernelDeinitCB)
840 KernelDeinitCB = KIS.KernelDeinitCB;
842 if (KIS.KernelEnvC) {
843 if (KernelEnvC && KernelEnvC != KIS.KernelEnvC)
846 KernelEnvC = KIS.KernelEnvC;
848 SPMDCompatibilityTracker ^= KIS.SPMDCompatibilityTracker;
849 ReachedKnownParallelRegions ^= KIS.ReachedKnownParallelRegions;
850 ReachedUnknownParallelRegions ^= KIS.ReachedUnknownParallelRegions;
851 NestedParallelism |= KIS.NestedParallelism;
855 KernelInfoState
operator&=(
const KernelInfoState &KIS) {
856 return (*
this ^= KIS);
866 AllocaInst *Array =
nullptr;
868 SmallVector<Value *, 8> StoredValues;
870 SmallVector<StoreInst *, 8> LastAccesses;
872 OffloadArray() =
default;
878 bool initialize(AllocaInst &Array, Instruction &Before) {
879 if (!getValues(Array, Before))
882 this->Array = &Array;
886 static const unsigned DeviceIDArgNum = 1;
887 static const unsigned BasePtrsArgNum = 3;
888 static const unsigned PtrsArgNum = 4;
889 static const unsigned SizesArgNum = 5;
895 bool getValues(AllocaInst &Array, Instruction &Before) {
897 const DataLayout &
DL = Array.getDataLayout();
898 std::optional<TypeSize> ArraySize = Array.getAllocationSize(
DL);
899 if (!ArraySize || !ArraySize->isFixed())
903 StoredValues.assign(NumValues,
nullptr);
904 LastAccesses.assign(NumValues,
nullptr);
912 for (Instruction &
I : *BB) {
928 LastAccesses[Idx] = S;
939 const unsigned NumValues = StoredValues.size();
940 for (
unsigned I = 0;
I < NumValues; ++
I) {
941 if (!StoredValues[
I] || !LastAccesses[
I])
951 using OptimizationRemarkGetter =
952 function_ref<OptimizationRemarkEmitter &(
Function *)>;
954 OpenMPOpt(SmallVectorImpl<Function *> &SCC, CallGraphUpdater &CGUpdater,
955 OptimizationRemarkGetter OREGetter,
956 OMPInformationCache &OMPInfoCache, Attributor &A)
957 : M(*(*SCC.
begin())->
getParent()), SCC(SCC), CGUpdater(CGUpdater),
958 OREGetter(OREGetter), OMPInfoCache(OMPInfoCache), A(A) {}
961 bool remarksEnabled() {
962 auto &Ctx = M.getContext();
963 return Ctx.getDiagHandlerPtr()->isAnyRemarkEnabled(
DEBUG_TYPE);
967 bool run(
bool IsModulePass) {
977 Changed |= runAttributor(IsModulePass);
980 OMPInfoCache.recollectUses();
983 Changed |= rewriteDeviceCodeStateMachine();
989 Changed |= removeSPMDParallelWrappers();
991 if (remarksEnabled())
992 analysisGlobalization();
999 Changed |= runAttributor(IsModulePass);
1002 OMPInfoCache.recollectUses();
1004 Changed |= deleteParallelRegions();
1007 Changed |= hideMemTransfersLatency();
1008 Changed |= deduplicateRuntimeCalls();
1010 if (mergeParallelRegions()) {
1011 deduplicateRuntimeCalls();
1017 if (OMPInfoCache.OpenMPPostLink)
1018 Changed |= removeRuntimeSymbols();
1025 void printICVs()
const {
1030 for (
auto ICV : ICVs) {
1031 auto ICVInfo = OMPInfoCache.ICVs[ICV];
1032 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
1033 return ORA <<
"OpenMP ICV " <<
ore::NV(
"OpenMPICV", ICVInfo.Name)
1035 << (ICVInfo.InitValue
1036 ?
toString(ICVInfo.InitValue->getValue(), 10,
true)
1037 :
"IMPLEMENTATION_DEFINED");
1046 void printKernels()
const {
1051 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
1052 return ORA <<
"OpenMP GPU kernel "
1053 <<
ore::NV(
"OpenMPGPUKernel",
F->getName()) <<
"\n";
1062 static CallInst *getCallIfRegularCall(
1063 Use &U, OMPInformationCache::RuntimeFunctionInfo *RFI =
nullptr) {
1074 static CallInst *getCallIfRegularCall(
1075 Value &V, OMPInformationCache::RuntimeFunctionInfo *RFI =
nullptr) {
1086 bool mergeParallelRegions() {
1087 const unsigned CallbackCalleeOperand = 2;
1088 const unsigned CallbackFirstArgOperand = 3;
1092 OMPInformationCache::RuntimeFunctionInfo &RFI =
1093 OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call];
1095 if (!RFI.Declaration)
1099 OMPInformationCache::RuntimeFunctionInfo UnmergableCallsInfo[] = {
1100 OMPInfoCache.RFIs[OMPRTL___kmpc_push_proc_bind],
1101 OMPInfoCache.RFIs[OMPRTL___kmpc_push_num_threads],
1105 LoopInfo *LI =
nullptr;
1106 DominatorTree *DT =
nullptr;
1108 SmallDenseMap<BasicBlock *, SmallPtrSet<Instruction *, 4>> BB2PRMap;
1110 BasicBlock *StartBB =
nullptr, *EndBB =
nullptr;
1111 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
1113 BasicBlock *CGStartBB = CodeGenIP.getBlock();
1115 SplitBlock(CGStartBB, &*CodeGenIP.getPoint(), DT, LI);
1116 assert(StartBB !=
nullptr &&
"StartBB should not be null");
1118 assert(EndBB !=
nullptr &&
"EndBB should not be null");
1119 EndBB->getTerminator()->setSuccessor(0, CGEndBB);
1123 auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
Value &,
1124 Value &Inner,
Value *&ReplacementValue) -> InsertPointTy {
1125 ReplacementValue = &Inner;
1129 auto FiniCB = [&](InsertPointTy CodeGenIP) {
return Error::success(); };
1133 auto CreateSequentialRegion = [&](
Function *OuterFn,
1139 BasicBlock *ParentBB = SeqStartI->getParent();
1141 SplitBlock(ParentBB, SeqEndI->getNextNode(), DT, LI);
1145 SplitBlock(ParentBB, SeqStartI, DT, LI,
nullptr,
"seq.par.merged");
1148 "Expected a different CFG");
1152 auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP,
1154 BasicBlock *CGStartBB = CodeGenIP.getBlock();
1156 SplitBlock(CGStartBB, &*CodeGenIP.getPoint(), DT, LI);
1157 assert(SeqStartBB !=
nullptr &&
"SeqStartBB should not be null");
1159 assert(SeqEndBB !=
nullptr &&
"SeqEndBB should not be null");
1163 auto FiniCB = [&](InsertPointTy CodeGenIP) {
return Error::success(); };
1167 for (Instruction &
I : *SeqStartBB) {
1168 SmallPtrSet<Instruction *, 4> OutsideUsers;
1169 for (User *Usr :
I.users()) {
1177 OutsideUsers.
insert(&UsrI);
1180 if (OutsideUsers.
empty())
1185 const DataLayout &
DL = M.getDataLayout();
1186 AllocaInst *AllocaI =
new AllocaInst(
1187 I.getType(),
DL.getAllocaAddrSpace(),
nullptr,
1192 new StoreInst(&
I, AllocaI, SeqStartBB->getTerminator()->getIterator());
1196 for (Instruction *UsrI : OutsideUsers) {
1197 LoadInst *LoadI =
new LoadInst(
I.getType(), AllocaI,
1198 I.getName() +
".seq.output.load",
1204 OpenMPIRBuilder::LocationDescription Loc(
1205 InsertPointTy(ParentBB, ParentBB->
end()),
DL);
1207 OMPInfoCache.OMPBuilder.createMaster(Loc, BodyGenCB, FiniCB));
1208 cantFail(OMPInfoCache.OMPBuilder.createBarrier({SeqAfterIP, DL},
1224 auto Merge = [&](
const SmallVectorImpl<CallInst *> &MergableCIs,
1228 assert(MergableCIs.
size() > 1 &&
"Assumed multiple mergable CIs");
1230 auto Remark = [&](OptimizationRemark
OR) {
1231 OR <<
"Parallel region merged with parallel region"
1232 << (MergableCIs.
size() > 2 ?
"s" :
"") <<
" at ";
1235 if (CI != MergableCIs.
back())
1243 Function *OriginalFn = BB->getParent();
1245 <<
" parallel regions in " << OriginalFn->
getName()
1249 EndBB =
SplitBlock(BB, MergableCIs.
back()->getNextNode(), DT, LI);
1251 SplitBlock(EndBB, &*EndBB->getFirstInsertionPt(), DT, LI);
1255 assert(BB->getUniqueSuccessor() == StartBB &&
"Expected a different CFG");
1256 const DebugLoc DL = BB->getTerminator()->getDebugLoc();
1261 for (
auto *It = MergableCIs.
begin(), *End = MergableCIs.
end() - 1;
1270 CreateSequentialRegion(OriginalFn, BB, ForkCI->
getNextNode(),
1274 OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BB, BB->end()),
1276 IRBuilder<>::InsertPoint AllocaIP(
1282 cantFail(OMPInfoCache.OMPBuilder.createParallel(
1283 Loc, AllocaIP, {}, BodyGenCB, PrivCB, FiniCB,
1284 nullptr,
nullptr, OMP_PROC_BIND_default,
1289 OMPInfoCache.OMPBuilder.finalize(OriginalFn);
1295 SmallVector<Value *, 8>
Args;
1296 for (
auto *CI : MergableCIs) {
1298 FunctionType *FT = OMPInfoCache.OMPBuilder.ParallelTask;
1302 for (
unsigned U = CallbackFirstArgOperand,
E = CI->
arg_size(); U <
E;
1312 for (
unsigned U = CallbackFirstArgOperand,
E = CI->
arg_size(); U <
E;
1316 U - (CallbackFirstArgOperand - CallbackCalleeOperand), A);
1319 if (CI != MergableCIs.back()) {
1322 cantFail(OMPInfoCache.OMPBuilder.createBarrier(
1323 {InsertPointTy(NewCI->getParent(),
1324 NewCI->getNextNode()->getIterator()),
1325 NewCI->getDebugLoc()},
1332 assert(OutlinedFn != OriginalFn &&
"Outlining failed");
1333 CGUpdater.registerOutlinedFunction(*OriginalFn, *OutlinedFn);
1334 CGUpdater.reanalyzeFunction(*OriginalFn);
1336 NumOpenMPParallelRegionsMerged += MergableCIs.size();
1344 CallInst *CI = getCallIfRegularCall(U, &RFI);
1351 RFI.foreachUse(SCC, DetectPRsCB);
1357 for (
auto &It : BB2PRMap) {
1358 auto &CIs = It.getSecond();
1373 auto IsMergable = [&](
Instruction &
I,
bool IsBeforeMergableRegion) {
1376 if (
I.isTerminator())
1383 if (IsBeforeMergableRegion) {
1385 if (!CalledFunction)
1392 for (
const auto &RFI : UnmergableCallsInfo) {
1393 if (CalledFunction == RFI.Declaration)
1408 for (
auto It = BB->
begin(), End = BB->
end(); It != End;) {
1412 if (CIs.count(&
I)) {
1418 if (IsMergable(
I, MergableCIs.
empty()))
1423 for (; It != End; ++It) {
1425 if (CIs.count(&SkipI)) {
1427 <<
" due to " <<
I <<
"\n");
1434 if (MergableCIs.
size() > 1) {
1435 MergableCIsVector.
push_back(MergableCIs);
1437 <<
" parallel regions in block " << BB->
getName()
1442 MergableCIs.
clear();
1445 if (!MergableCIsVector.
empty()) {
1448 for (
auto &MergableCIs : MergableCIsVector)
1449 Merge(MergableCIs, BB);
1450 MergableCIsVector.clear();
1457 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_fork_call);
1458 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_barrier);
1459 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_master);
1460 OMPInfoCache.recollectUsesForFunction(OMPRTL___kmpc_end_master);
1467 bool deleteParallelRegions() {
1468 const unsigned CallbackCalleeOperand = 2;
1470 OMPInformationCache::RuntimeFunctionInfo &RFI =
1471 OMPInfoCache.RFIs[OMPRTL___kmpc_fork_call];
1473 if (!RFI.Declaration)
1478 CallInst *CI = getCallIfRegularCall(U);
1485 if (!Fn->onlyReadsMemory())
1487 if (!Fn->hasFnAttribute(Attribute::WillReturn))
1493 auto Remark = [&](OptimizationRemark
OR) {
1494 return OR <<
"Removing parallel region with no side-effects.";
1500 ++NumOpenMPParallelRegionsDeleted;
1504 RFI.foreachUse(SCC, DeleteCallCB);
1510 bool deduplicateRuntimeCalls() {
1514 OMPRTL_omp_get_num_threads,
1515 OMPRTL_omp_in_parallel,
1516 OMPRTL_omp_get_cancellation,
1517 OMPRTL_omp_get_supported_active_levels,
1518 OMPRTL_omp_get_level,
1519 OMPRTL_omp_get_ancestor_thread_num,
1520 OMPRTL_omp_get_team_size,
1521 OMPRTL_omp_get_active_level,
1522 OMPRTL_omp_in_final,
1523 OMPRTL_omp_get_proc_bind,
1524 OMPRTL_omp_get_num_places,
1525 OMPRTL_omp_get_num_procs,
1526 OMPRTL_omp_get_place_num,
1527 OMPRTL_omp_get_partition_num_places,
1528 OMPRTL_omp_get_partition_place_nums};
1531 SmallSetVector<Value *, 16> GTIdArgs;
1532 collectGlobalThreadIdArguments(GTIdArgs);
1534 <<
" global thread ID arguments\n");
1537 for (
auto DeduplicableRuntimeCallID : DeduplicableRuntimeCallIDs)
1538 Changed |= deduplicateRuntimeCalls(
1539 *
F, OMPInfoCache.RFIs[DeduplicableRuntimeCallID]);
1543 Value *GTIdArg =
nullptr;
1544 for (Argument &Arg :
F->args())
1545 if (GTIdArgs.
count(&Arg)) {
1549 Changed |= deduplicateRuntimeCalls(
1550 *
F, OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num], GTIdArg);
1557 bool removeRuntimeSymbols() {
1562 if (GlobalVariable *GV = M.getNamedGlobal(
"__llvm_rpc_client")) {
1563 if (GV->hasNUsesOrMore(1))
1567 GV->eraseFromParent();
1579 bool hideMemTransfersLatency() {
1580 auto &RFI = OMPInfoCache.RFIs[OMPRTL___tgt_target_data_begin_mapper];
1583 auto *RTCall = getCallIfRegularCall(U, &RFI);
1587 OffloadArray OffloadArrays[3];
1588 if (!getValuesInOffloadArrays(*RTCall, OffloadArrays))
1591 LLVM_DEBUG(dumpValuesInOffloadArrays(OffloadArrays));
1594 bool WasSplit =
false;
1595 Instruction *WaitMovementPoint = canBeMovedDownwards(*RTCall);
1596 if (WaitMovementPoint)
1597 WasSplit = splitTargetDataBeginRTC(*RTCall, *WaitMovementPoint);
1602 if (OMPInfoCache.runtimeFnsAvailable(
1603 {OMPRTL___tgt_target_data_begin_mapper_issue,
1604 OMPRTL___tgt_target_data_begin_mapper_wait}))
1605 RFI.foreachUse(SCC, SplitMemTransfers);
1610 void analysisGlobalization() {
1611 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
1613 auto CheckGlobalization = [&](
Use &
U,
Function &Decl) {
1614 if (CallInst *CI = getCallIfRegularCall(U, &RFI)) {
1615 auto Remark = [&](OptimizationRemarkMissed ORM) {
1617 <<
"Found thread data sharing on the GPU. "
1618 <<
"Expect degraded performance due to data globalization.";
1626 RFI.foreachUse(SCC, CheckGlobalization);
1631 bool getValuesInOffloadArrays(CallInst &RuntimeCall,
1633 assert(OAs.
size() == 3 &&
"Need space for three offload arrays!");
1643 Value *BasePtrsArg =
1655 if (!OAs[0].
initialize(*BasePtrsArray, RuntimeCall))
1663 if (!OAs[1].
initialize(*PtrsArray, RuntimeCall))
1675 if (!OAs[2].
initialize(*SizesArray, RuntimeCall))
1686 assert(OAs.
size() == 3 &&
"There are three offload arrays to debug!");
1689 std::string ValuesStr;
1690 raw_string_ostream
Printer(ValuesStr);
1691 std::string Separator =
" --- ";
1693 for (
auto *BP : OAs[0].StoredValues) {
1697 LLVM_DEBUG(
dbgs() <<
"\t\toffload_baseptrs: " << ValuesStr <<
"\n");
1700 for (
auto *
P : OAs[1].StoredValues) {
1707 for (
auto *S : OAs[2].StoredValues) {
1711 LLVM_DEBUG(
dbgs() <<
"\t\toffload_sizes: " << ValuesStr <<
"\n");
1716 Instruction *canBeMovedDownwards(CallInst &RuntimeCall) {
1721 bool IsWorthIt =
false;
1740 return RuntimeCall.
getParent()->getTerminator();
1744 bool splitTargetDataBeginRTC(CallInst &RuntimeCall,
1745 Instruction &WaitMovementPoint) {
1749 auto &
IRBuilder = OMPInfoCache.OMPBuilder;
1752 IRBuilder.Builder.SetInsertPoint(&Entry,
1753 Entry.getFirstNonPHIOrDbgOrAlloca());
1755 IRBuilder.AsyncInfo,
nullptr,
"handle");
1762 FunctionCallee IssueDecl =
IRBuilder.getOrCreateRuntimeFunction(
1763 M, OMPRTL___tgt_target_data_begin_mapper_issue);
1766 SmallVector<Value *, 16>
Args;
1767 for (
auto &Arg : RuntimeCall.
args())
1768 Args.push_back(Arg.get());
1769 Args.push_back(Handle);
1773 OMPInfoCache.setCallingConvention(IssueDecl, IssueCallsite);
1778 FunctionCallee WaitDecl =
IRBuilder.getOrCreateRuntimeFunction(
1779 M, OMPRTL___tgt_target_data_begin_mapper_wait);
1781 Value *WaitParams[2] = {
1783 OffloadArray::DeviceIDArgNum),
1787 WaitDecl, WaitParams,
"", WaitMovementPoint.
getIterator());
1788 OMPInfoCache.setCallingConvention(WaitDecl, WaitCallsite);
1793 static Value *combinedIdentStruct(
Value *CurrentIdent,
Value *NextIdent,
1794 bool GlobalOnly,
bool &SingleChoice) {
1795 if (CurrentIdent == NextIdent)
1796 return CurrentIdent;
1801 SingleChoice = !CurrentIdent;
1813 getCombinedIdentFromCallUsesIn(OMPInformationCache::RuntimeFunctionInfo &RFI,
1815 bool SingleChoice =
true;
1816 Value *Ident =
nullptr;
1818 CallInst *CI = getCallIfRegularCall(U, &RFI);
1819 if (!CI || &
F != &Caller)
1822 true, SingleChoice);
1825 RFI.foreachUse(SCC, CombineIdentStruct);
1827 if (!Ident || !SingleChoice) {
1831 if (!OMPInfoCache.OMPBuilder.getInsertionPoint().getBlock())
1832 OMPInfoCache.OMPBuilder.updateToLocation(
1834 F.getEntryBlock().begin()),
1838 uint32_t SrcLocStrSize;
1840 OMPInfoCache.OMPBuilder.getOrCreateDefaultSrcLocStr(SrcLocStrSize);
1841 Ident = OMPInfoCache.OMPBuilder.getOrCreateIdent(Loc, SrcLocStrSize);
1848 bool deduplicateRuntimeCalls(
Function &
F,
1849 OMPInformationCache::RuntimeFunctionInfo &RFI,
1850 Value *ReplVal =
nullptr) {
1851 auto *UV = RFI.getUseVector(
F);
1852 if (!UV || UV->size() + (ReplVal !=
nullptr) < 2)
1856 dbgs() <<
TAG <<
"Deduplicate " << UV->size() <<
" uses of " << RFI.Name
1857 << (ReplVal ?
" with an existing value\n" :
"\n") <<
"\n");
1861 "Unexpected replacement value!");
1864 auto CanBeMoved = [
this](CallBase &CB) {
1865 unsigned NumArgs = CB.arg_size();
1868 if (CB.getArgOperand(0)->getType() != OMPInfoCache.OMPBuilder.IdentPtr)
1870 for (
unsigned U = 1;
U < NumArgs; ++
U)
1878 OMPInfoCache.getAnalysisResultForFunction<DominatorTreeAnalysis>(
F);
1882 for (Use *U : *UV) {
1883 if (CallInst *CI = getCallIfRegularCall(*U, &RFI)) {
1888 if (!CanBeMoved(*CI))
1896 assert(IP &&
"Expected insertion point!");
1906 Value *Ident = getCombinedIdentFromCallUsesIn(RFI,
F,
1914 CallInst *CI = getCallIfRegularCall(U, &RFI);
1915 if (!CI || CI == ReplVal || &
F != &Caller)
1919 auto Remark = [&](OptimizationRemark
OR) {
1920 return OR <<
"OpenMP runtime call "
1921 <<
ore::NV(
"OpenMPOptRuntime", RFI.Name) <<
" deduplicated.";
1930 ++NumOpenMPRuntimeCallsDeduplicated;
1934 RFI.foreachUse(SCC, ReplaceAndDeleteCB);
1940 void collectGlobalThreadIdArguments(SmallSetVector<Value *, 16> >IdArgs) {
1947 auto CallArgOpIsGTId = [&](
Function &
F,
unsigned ArgNo, CallInst &RefCI) {
1948 if (!
F.hasLocalLinkage())
1950 for (Use &U :
F.uses()) {
1951 if (CallInst *CI = getCallIfRegularCall(U)) {
1953 if (CI == &RefCI || GTIdArgs.
count(ArgOp) ||
1954 getCallIfRegularCall(
1955 *ArgOp, &OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num]))
1964 auto AddUserArgs = [&](
Value >Id) {
1965 for (Use &U : GTId.uses())
1969 if (CallArgOpIsGTId(*Callee,
U.getOperandNo(), *CI))
1974 OMPInformationCache::RuntimeFunctionInfo &GlobThreadNumRFI =
1975 OMPInfoCache.RFIs[OMPRTL___kmpc_global_thread_num];
1977 GlobThreadNumRFI.foreachUse(SCC, [&](Use &U,
Function &
F) {
1978 if (CallInst *CI = getCallIfRegularCall(U, &GlobThreadNumRFI))
1986 for (
unsigned U = 0;
U < GTIdArgs.
size(); ++
U)
1987 AddUserArgs(*GTIdArgs[U]);
1995 DenseMap<Function *, std::optional<Kernel>> UniqueKernelMap;
2001 Kernel getUniqueKernelFor(Instruction &
I) {
2002 return getUniqueKernelFor(*
I.getFunction());
2007 bool rewriteDeviceCodeStateMachine();
2012 bool removeSPMDParallelWrappers();
2028 template <
typename RemarkKind,
typename RemarkCallBack>
2029 void emitRemark(Instruction *
I, StringRef RemarkName,
2030 RemarkCallBack &&RemarkCB)
const {
2032 auto &ORE = OREGetter(
F);
2036 return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
I))
2037 <<
" [" << RemarkName <<
"]";
2041 [&]() {
return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
I)); });
2045 template <
typename RemarkKind,
typename RemarkCallBack>
2047 RemarkCallBack &&RemarkCB)
const {
2048 auto &ORE = OREGetter(
F);
2052 return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
F))
2053 <<
" [" << RemarkName <<
"]";
2057 [&]() {
return RemarkCB(RemarkKind(
DEBUG_TYPE, RemarkName,
F)); });
2064 SmallVectorImpl<Function *> &SCC;
2068 CallGraphUpdater &CGUpdater;
2071 OptimizationRemarkGetter OREGetter;
2074 OMPInformationCache &OMPInfoCache;
2080 bool runAttributor(
bool IsModulePass) {
2084 registerAAs(IsModulePass);
2089 <<
" functions, result: " <<
Changed <<
".\n");
2091 if (
Changed == ChangeStatus::CHANGED)
2092 OMPInfoCache.invalidateAnalyses();
2094 return Changed == ChangeStatus::CHANGED;
2101 void registerAAs(
bool IsModulePass);
2106 static void registerAAsForFunction(Attributor &A,
const Function &
F);
2110 if (OMPInfoCache.CGSCC && !OMPInfoCache.CGSCC->empty() &&
2111 !OMPInfoCache.CGSCC->contains(&
F))
2116 std::optional<Kernel> &CachedKernel = UniqueKernelMap[&
F];
2118 return *CachedKernel;
2125 return *CachedKernel;
2128 CachedKernel =
nullptr;
2129 if (!
F.hasLocalLinkage()) {
2132 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
2133 return ORA <<
"Potentially unknown OpenMP target region caller.";
2141 auto GetUniqueKernelForUse = [&](
const Use &
U) ->
Kernel {
2144 if (
Cmp->isEquality())
2145 return getUniqueKernelFor(*Cmp);
2150 if (CB->isCallee(&U))
2151 return getUniqueKernelFor(*CB);
2153 OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI =
2154 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
2156 if (OpenMPOpt::getCallIfRegularCall(*
U.getUser(), &KernelParallelRFI))
2157 return getUniqueKernelFor(*CB);
2165 SmallPtrSet<Kernel, 2> PotentialKernels;
2166 OMPInformationCache::foreachUse(
F, [&](
const Use &U) {
2167 PotentialKernels.
insert(GetUniqueKernelForUse(U));
2171 if (PotentialKernels.
size() == 1)
2172 K = *PotentialKernels.
begin();
2175 UniqueKernelMap[&
F] =
K;
2180bool OpenMPOpt::rewriteDeviceCodeStateMachine() {
2181 OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI =
2182 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
2185 if (!KernelParallelRFI)
2196 bool UnknownUse =
false;
2197 bool KernelParallelUse =
false;
2198 unsigned NumDirectCalls = 0;
2201 OMPInformationCache::foreachUse(*
F, [&](Use &U) {
2203 if (CB->isCallee(&U)) {
2209 ToBeReplacedStateMachineUses.
push_back(&U);
2215 OpenMPOpt::getCallIfRegularCall(*
U.getUser(), &KernelParallelRFI);
2216 const unsigned int WrapperFunctionArgNo = 6;
2217 if (!KernelParallelUse && CI &&
2219 KernelParallelUse =
true;
2220 ToBeReplacedStateMachineUses.
push_back(&U);
2228 if (!KernelParallelUse)
2234 if (UnknownUse || NumDirectCalls != 1 ||
2235 ToBeReplacedStateMachineUses.
size() > 2) {
2236 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
2237 return ORA <<
"Parallel region is used in "
2238 << (UnknownUse ?
"unknown" :
"unexpected")
2239 <<
" ways. Will not attempt to rewrite the state machine.";
2249 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
2250 return ORA <<
"Parallel region is not called from a unique kernel. "
2251 "Will not attempt to rewrite the state machine.";
2263 Type *Int8Ty = Type::getInt8Ty(
M.getContext());
2265 auto *
ID =
new GlobalVariable(
2269 for (Use *U : ToBeReplacedStateMachineUses)
2271 ID,
U->get()->getType()));
2273 ++NumOpenMPParallelRegionsReplacedInGPUStateMachine;
2281bool OpenMPOpt::removeSPMDParallelWrappers() {
2283 if (OMPInfoCache.SPMDizedKernels.empty())
2286 OMPInformationCache::RuntimeFunctionInfo &KernelParallelRFI =
2287 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
2288 if (!KernelParallelRFI || !KernelParallelRFI.Declaration)
2291 constexpr unsigned WrapperFunctionArgNo = 6;
2293 for (User *U : KernelParallelRFI.Declaration->
users()) {
2296 CI->
arg_size() <= WrapperFunctionArgNo)
2310 if (!K || !OMPInfoCache.SPMDizedKernels.contains(K))
2314 WrapperFunctionArgNo,
2323struct AAICVTracker :
public StateWrapper<BooleanState, AbstractAttribute> {
2324 using Base = StateWrapper<BooleanState, AbstractAttribute>;
2325 AAICVTracker(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
2328 bool isAssumedTracked()
const {
return getAssumed(); }
2331 bool isKnownTracked()
const {
return getAssumed(); }
2334 static AAICVTracker &createForPosition(
const IRPosition &IRP, Attributor &
A);
2338 const Instruction *
I,
2339 Attributor &
A)
const {
2340 return std::nullopt;
2346 virtual std::optional<Value *>
2354 StringRef
getName()
const override {
return "AAICVTracker"; }
2357 const char *getIdAddr()
const override {
return &ID; }
2360 static bool classof(
const AbstractAttribute *AA) {
2364 static const char ID;
2367struct AAICVTrackerFunction :
public AAICVTracker {
2368 AAICVTrackerFunction(
const IRPosition &IRP, Attributor &
A)
2369 : AAICVTracker(IRP,
A) {}
2372 const std::string getAsStr(Attributor *)
const override {
2373 return "ICVTrackerFunction";
2377 void trackStatistics()
const override {}
2381 return ChangeStatus::UNCHANGED;
2386 InternalControlVar::ICV___last>
2387 ICVReplacementValuesMap;
2394 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
2397 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter];
2399 auto &ValuesMap = ICVReplacementValuesMap[ICV];
2401 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U);
2407 if (ValuesMap.insert(std::make_pair(CI, CI->
getArgOperand(0))).second)
2408 HasChanged = ChangeStatus::CHANGED;
2414 std::optional<Value *> ReplVal = getValueForCall(
A,
I, ICV);
2415 if (ReplVal && ValuesMap.insert(std::make_pair(&
I, *ReplVal)).second)
2416 HasChanged = ChangeStatus::CHANGED;
2422 SetterRFI.foreachUse(TrackValues,
F);
2424 bool UsedAssumedInformation =
false;
2425 A.checkForAllInstructions(CallCheck, *
this, {Instruction::Call},
2426 UsedAssumedInformation,
2432 if (HasChanged == ChangeStatus::CHANGED)
2433 ValuesMap.try_emplace(Entry);
2441 std::optional<Value *> getValueForCall(Attributor &
A,
const Instruction &
I,
2445 if (!CB || CB->hasFnAttr(
"no_openmp") ||
2446 CB->hasFnAttr(
"no_openmp_routines") ||
2447 CB->hasFnAttr(
"no_openmp_constructs"))
2448 return std::nullopt;
2450 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
2451 auto &GetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Getter];
2452 auto &SetterRFI = OMPInfoCache.RFIs[OMPInfoCache.ICVs[ICV].Setter];
2453 Function *CalledFunction = CB->getCalledFunction();
2456 if (CalledFunction ==
nullptr)
2458 if (CalledFunction == GetterRFI.Declaration)
2459 return std::nullopt;
2460 if (CalledFunction == SetterRFI.Declaration) {
2461 if (ICVReplacementValuesMap[ICV].
count(&
I))
2462 return ICVReplacementValuesMap[ICV].lookup(&
I);
2471 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2474 if (ICVTrackingAA->isAssumedTracked()) {
2475 std::optional<Value *> URV =
2476 ICVTrackingAA->getUniqueReplacementValue(ICV);
2487 std::optional<Value *>
2489 return std::nullopt;
2494 const Instruction *
I,
2495 Attributor &
A)
const override {
2496 const auto &ValuesMap = ICVReplacementValuesMap[ICV];
2497 if (ValuesMap.count(
I))
2498 return ValuesMap.lookup(
I);
2501 SmallPtrSet<const Instruction *, 16> Visited;
2504 std::optional<Value *> ReplVal;
2506 while (!Worklist.
empty()) {
2508 if (!Visited.
insert(CurrInst).second)
2516 if (ValuesMap.count(CurrInst)) {
2517 std::optional<Value *> NewReplVal = ValuesMap.lookup(CurrInst);
2520 ReplVal = NewReplVal;
2526 if (ReplVal != NewReplVal)
2532 std::optional<Value *> NewReplVal = getValueForCall(
A, *CurrInst, ICV);
2538 ReplVal = NewReplVal;
2544 if (ReplVal != NewReplVal)
2549 if (CurrBB ==
I->getParent() && ReplVal)
2554 if (
const Instruction *Terminator = Pred->getTerminator())
2562struct AAICVTrackerFunctionReturned : AAICVTracker {
2563 AAICVTrackerFunctionReturned(
const IRPosition &IRP, Attributor &
A)
2564 : AAICVTracker(IRP,
A) {}
2567 const std::string getAsStr(Attributor *)
const override {
2568 return "ICVTrackerFunctionReturned";
2572 void trackStatistics()
const override {}
2576 return ChangeStatus::UNCHANGED;
2581 InternalControlVar::ICV___last>
2582 ICVReplacementValuesMap;
2585 std::optional<Value *>
2587 return ICVReplacementValuesMap[ICV];
2592 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2595 if (!ICVTrackingAA->isAssumedTracked())
2596 return indicatePessimisticFixpoint();
2599 std::optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV];
2600 std::optional<Value *> UniqueICVValue;
2603 std::optional<Value *> NewReplVal =
2604 ICVTrackingAA->getReplacementValue(ICV, &
I,
A);
2607 if (UniqueICVValue && UniqueICVValue != NewReplVal)
2610 UniqueICVValue = NewReplVal;
2615 bool UsedAssumedInformation =
false;
2616 if (!
A.checkForAllInstructions(CheckReturnInst, *
this, {Instruction::Ret},
2617 UsedAssumedInformation,
2619 UniqueICVValue =
nullptr;
2621 if (UniqueICVValue == ReplVal)
2624 ReplVal = UniqueICVValue;
2625 Changed = ChangeStatus::CHANGED;
2632struct AAICVTrackerCallSite : AAICVTracker {
2633 AAICVTrackerCallSite(
const IRPosition &IRP, Attributor &
A)
2634 : AAICVTracker(IRP,
A) {}
2637 assert(getAnchorScope() &&
"Expected anchor function");
2641 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
2643 auto ICVInfo = OMPInfoCache.ICVs[ICV];
2644 auto &Getter = OMPInfoCache.RFIs[ICVInfo.Getter];
2645 if (Getter.Declaration == getAssociatedFunction()) {
2646 AssociatedICV = ICVInfo.Kind;
2652 indicatePessimisticFixpoint();
2656 if (!ReplVal || !*ReplVal)
2657 return ChangeStatus::UNCHANGED;
2660 A.deleteAfterManifest(*getCtxI());
2662 return ChangeStatus::CHANGED;
2666 const std::string getAsStr(Attributor *)
const override {
2667 return "ICVTrackerCallSite";
2671 void trackStatistics()
const override {}
2674 std::optional<Value *> ReplVal;
2677 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2681 if (!ICVTrackingAA->isAssumedTracked())
2682 return indicatePessimisticFixpoint();
2684 std::optional<Value *> NewReplVal =
2685 ICVTrackingAA->getReplacementValue(AssociatedICV, getCtxI(),
A);
2687 if (ReplVal == NewReplVal)
2688 return ChangeStatus::UNCHANGED;
2690 ReplVal = NewReplVal;
2691 return ChangeStatus::CHANGED;
2696 std::optional<Value *>
2702struct AAICVTrackerCallSiteReturned : AAICVTracker {
2703 AAICVTrackerCallSiteReturned(
const IRPosition &IRP, Attributor &
A)
2704 : AAICVTracker(IRP,
A) {}
2707 const std::string getAsStr(Attributor *)
const override {
2708 return "ICVTrackerCallSiteReturned";
2712 void trackStatistics()
const override {}
2716 return ChangeStatus::UNCHANGED;
2721 InternalControlVar::ICV___last>
2722 ICVReplacementValuesMap;
2726 std::optional<Value *>
2728 return ICVReplacementValuesMap[ICV];
2733 const auto *ICVTrackingAA =
A.getAAFor<AAICVTracker>(
2735 DepClassTy::REQUIRED);
2738 if (!ICVTrackingAA->isAssumedTracked())
2739 return indicatePessimisticFixpoint();
2742 std::optional<Value *> &ReplVal = ICVReplacementValuesMap[ICV];
2743 std::optional<Value *> NewReplVal =
2744 ICVTrackingAA->getUniqueReplacementValue(ICV);
2746 if (ReplVal == NewReplVal)
2749 ReplVal = NewReplVal;
2750 Changed = ChangeStatus::CHANGED;
2758static bool hasFunctionEndAsUniqueSuccessor(
const BasicBlock *BB) {
2764 return hasFunctionEndAsUniqueSuccessor(
Successor);
2767struct AAExecutionDomainFunction :
public AAExecutionDomain {
2768 AAExecutionDomainFunction(
const IRPosition &IRP, Attributor &
A)
2769 : AAExecutionDomain(IRP,
A) {}
2771 ~AAExecutionDomainFunction()
override {
delete RPOT; }
2775 assert(
F &&
"Expected anchor function");
2776 RPOT =
new ReversePostOrderTraversal<Function *>(
F);
2779 const std::string getAsStr(Attributor *)
const override {
2780 unsigned TotalBlocks = 0, InitialThreadBlocks = 0, AlignedBlocks = 0;
2781 for (
auto &It : BEDMap) {
2785 InitialThreadBlocks += It.getSecond().IsExecutedByInitialThreadOnly;
2786 AlignedBlocks += It.getSecond().IsReachedFromAlignedBarrierOnly &&
2787 It.getSecond().IsReachingAlignedBarrierOnly;
2789 return "[AAExecutionDomain] " + std::to_string(InitialThreadBlocks) +
"/" +
2790 std::to_string(AlignedBlocks) +
" of " +
2791 std::to_string(TotalBlocks) +
2792 " executed by initial thread / aligned";
2796 void trackStatistics()
const override {}
2800 for (
const BasicBlock &BB : *getAnchorScope()) {
2801 if (!isExecutedByInitialThreadOnly(BB))
2803 dbgs() <<
TAG <<
" Basic block @" << getAnchorScope()->getName() <<
" "
2804 << BB.
getName() <<
" is executed by a single thread.\n";
2813 SmallPtrSet<CallBase *, 16> DeletedBarriers;
2814 auto HandleAlignedBarrier = [&](CallBase *CB) {
2815 const ExecutionDomainTy &ED = CB ? CEDMap[{CB, PRE}] : BEDMap[
nullptr];
2816 if (!ED.IsReachedFromAlignedBarrierOnly ||
2817 ED.EncounteredNonLocalSideEffect)
2819 if (!ED.EncounteredAssumes.empty() && !
A.isModulePass())
2830 DeletedBarriers.
insert(CB);
2831 A.deleteAfterManifest(*CB);
2832 ++NumBarriersEliminated;
2833 Changed = ChangeStatus::CHANGED;
2834 }
else if (!ED.AlignedBarriers.empty()) {
2835 Changed = ChangeStatus::CHANGED;
2837 ED.AlignedBarriers.end());
2838 SmallSetVector<CallBase *, 16> Visited;
2839 while (!Worklist.
empty()) {
2841 if (!Visited.
insert(LastCB))
2845 if (!hasFunctionEndAsUniqueSuccessor(LastCB->
getParent()))
2847 if (!DeletedBarriers.
count(LastCB)) {
2848 ++NumBarriersEliminated;
2849 A.deleteAfterManifest(*LastCB);
2855 const ExecutionDomainTy &LastED = CEDMap[{LastCB, PRE}];
2856 Worklist.
append(LastED.AlignedBarriers.begin(),
2857 LastED.AlignedBarriers.end());
2863 if (!ED.EncounteredAssumes.empty() && (CB || !ED.AlignedBarriers.empty()))
2864 for (
auto *AssumeCB : ED.EncounteredAssumes)
2865 A.deleteAfterManifest(*AssumeCB);
2868 for (
auto *CB : AlignedBarriers)
2869 HandleAlignedBarrier(CB);
2873 HandleAlignedBarrier(
nullptr);
2878 bool isNoOpFence(
const FenceInst &FI)
const override {
2879 return getState().isValidState() && !NonNoOpFences.count(&FI);
2885 mergeInPredecessorBarriersAndAssumptions(Attributor &
A, ExecutionDomainTy &ED,
2886 const ExecutionDomainTy &PredED);
2891 bool mergeInPredecessor(Attributor &
A, ExecutionDomainTy &ED,
2892 const ExecutionDomainTy &PredED,
2893 bool InitialEdgeOnly =
false);
2896 bool handleCallees(Attributor &
A, ExecutionDomainTy &EntryBBED);
2903 bool isExecutedByInitialThreadOnly(
const BasicBlock &BB)
const override {
2904 if (!isValidState())
2906 assert(BB.
getParent() == getAnchorScope() &&
"Block is out of scope!");
2907 return BEDMap.lookup(&BB).IsExecutedByInitialThreadOnly;
2910 bool isExecutedInAlignedRegion(Attributor &
A,
2911 const Instruction &
I)
const override {
2912 assert(
I.getFunction() == getAnchorScope() &&
2913 "Instruction is out of scope!");
2914 if (!isValidState())
2917 bool ForwardIsOk =
true;
2926 if (CB != &
I && AlignedBarriers.contains(
const_cast<CallBase *
>(CB)))
2928 const auto &It = CEDMap.find({CB, PRE});
2929 if (It == CEDMap.end())
2931 if (!It->getSecond().IsReachingAlignedBarrierOnly)
2932 ForwardIsOk =
false;
2936 if (!CurI && !BEDMap.lookup(
I.getParent()).IsReachingAlignedBarrierOnly)
2937 ForwardIsOk =
false;
2945 if (CB != &
I && AlignedBarriers.contains(
const_cast<CallBase *
>(CB)))
2947 const auto &It = CEDMap.find({CB, POST});
2948 if (It == CEDMap.end())
2950 if (It->getSecond().IsReachedFromAlignedBarrierOnly)
2963 return BEDMap.lookup(
nullptr).IsReachedFromAlignedBarrierOnly;
2965 return BEDMap.lookup(PredBB).IsReachedFromAlignedBarrierOnly;
2975 ExecutionDomainTy getExecutionDomain(
const BasicBlock &BB)
const override {
2977 "No request should be made against an invalid state!");
2978 return BEDMap.lookup(&BB);
2980 std::pair<ExecutionDomainTy, ExecutionDomainTy>
2981 getExecutionDomain(
const CallBase &CB)
const override {
2983 "No request should be made against an invalid state!");
2984 return {CEDMap.lookup({&CB, PRE}), CEDMap.lookup({&CB, POST})};
2986 ExecutionDomainTy getFunctionExecutionDomain()
const override {
2988 "No request should be made against an invalid state!");
2989 return InterProceduralED;
2995 static bool isInitialThreadOnlyEdge(Attributor &
A, CondBrInst *
Edge,
2996 BasicBlock &SuccessorBB) {
2999 if (
Edge->getSuccessor(0) != &SuccessorBB)
3003 if (!Cmp || !
Cmp->isTrueWhenEqual() || !
Cmp->isEquality())
3011 if (
C->isAllOnesValue()) {
3013 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3014 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_target_init];
3015 CB = CB ? OpenMPOpt::getCallIfRegularCall(*CB, &RFI) : nullptr;
3018 ConstantStruct *KernelEnvC =
3020 ConstantInt *ExecModeC =
3021 KernelInfo::getExecModeFromKernelEnvironment(KernelEnvC);
3028 if (
II->getIntrinsicID() == Intrinsic::nvvm_read_ptx_sreg_tid_x)
3033 if (
II->getIntrinsicID() == Intrinsic::amdgcn_workitem_id_x)
3041 ExecutionDomainTy InterProceduralED;
3045 DenseMap<const BasicBlock *, ExecutionDomainTy> BEDMap;
3046 DenseMap<PointerIntPair<const CallBase *, 1, Direction>, ExecutionDomainTy>
3048 SmallSetVector<CallBase *, 16> AlignedBarriers;
3050 ReversePostOrderTraversal<Function *> *RPOT =
nullptr;
3053 static bool setAndRecord(
bool &R,
bool V) {
3061 SmallPtrSet<const FenceInst *, 8> NonNoOpFences;
3064void AAExecutionDomainFunction::mergeInPredecessorBarriersAndAssumptions(
3065 Attributor &
A, ExecutionDomainTy &ED,
const ExecutionDomainTy &PredED) {
3066 for (
auto *EA : PredED.EncounteredAssumes)
3067 ED.addAssumeInst(
A, *EA);
3069 for (
auto *AB : PredED.AlignedBarriers)
3070 ED.addAlignedBarrier(
A, *AB);
3073bool AAExecutionDomainFunction::mergeInPredecessor(
3074 Attributor &
A, ExecutionDomainTy &ED,
const ExecutionDomainTy &PredED,
3075 bool InitialEdgeOnly) {
3079 setAndRecord(ED.IsExecutedByInitialThreadOnly,
3080 InitialEdgeOnly || (PredED.IsExecutedByInitialThreadOnly &&
3081 ED.IsExecutedByInitialThreadOnly));
3083 Changed |= setAndRecord(ED.IsReachedFromAlignedBarrierOnly,
3084 ED.IsReachedFromAlignedBarrierOnly &&
3085 PredED.IsReachedFromAlignedBarrierOnly);
3086 Changed |= setAndRecord(ED.EncounteredNonLocalSideEffect,
3087 ED.EncounteredNonLocalSideEffect |
3088 PredED.EncounteredNonLocalSideEffect);
3090 if (ED.IsReachedFromAlignedBarrierOnly)
3091 mergeInPredecessorBarriersAndAssumptions(
A, ED, PredED);
3093 ED.clearAssumeInstAndAlignedBarriers();
3097bool AAExecutionDomainFunction::handleCallees(Attributor &
A,
3098 ExecutionDomainTy &EntryBBED) {
3100 auto PredForCallSite = [&](AbstractCallSite ACS) {
3101 const auto *EDAA =
A.getAAFor<AAExecutionDomain>(
3103 DepClassTy::OPTIONAL);
3104 if (!EDAA || !EDAA->getState().isValidState())
3107 EDAA->getExecutionDomain(*
cast<CallBase>(ACS.getInstruction())));
3111 ExecutionDomainTy ExitED;
3112 bool AllCallSitesKnown;
3113 if (
A.checkForAllCallSites(PredForCallSite, *
this,
3115 AllCallSitesKnown)) {
3116 for (
const auto &[CSInED, CSOutED] : CallSiteEDs) {
3117 mergeInPredecessor(
A, EntryBBED, CSInED);
3118 ExitED.IsReachingAlignedBarrierOnly &=
3119 CSOutED.IsReachingAlignedBarrierOnly;
3126 EntryBBED.IsExecutedByInitialThreadOnly =
false;
3127 EntryBBED.IsReachedFromAlignedBarrierOnly =
true;
3128 EntryBBED.EncounteredNonLocalSideEffect =
false;
3129 ExitED.IsReachingAlignedBarrierOnly =
false;
3131 EntryBBED.IsExecutedByInitialThreadOnly =
false;
3132 EntryBBED.IsReachedFromAlignedBarrierOnly =
false;
3133 EntryBBED.EncounteredNonLocalSideEffect =
true;
3134 ExitED.IsReachingAlignedBarrierOnly =
false;
3139 auto &FnED = BEDMap[
nullptr];
3140 Changed |= setAndRecord(FnED.IsReachedFromAlignedBarrierOnly,
3141 FnED.IsReachedFromAlignedBarrierOnly &
3142 EntryBBED.IsReachedFromAlignedBarrierOnly);
3143 Changed |= setAndRecord(FnED.IsReachingAlignedBarrierOnly,
3144 FnED.IsReachingAlignedBarrierOnly &
3145 ExitED.IsReachingAlignedBarrierOnly);
3146 Changed |= setAndRecord(FnED.IsExecutedByInitialThreadOnly,
3147 EntryBBED.IsExecutedByInitialThreadOnly);
3151ChangeStatus AAExecutionDomainFunction::updateImpl(Attributor &
A) {
3158 auto HandleAlignedBarrier = [&](CallBase &CB, ExecutionDomainTy &ED) {
3159 Changed |= AlignedBarriers.insert(&CB);
3161 auto &CallInED = CEDMap[{&CB, PRE}];
3162 Changed |= mergeInPredecessor(
A, CallInED, ED);
3163 CallInED.IsReachingAlignedBarrierOnly =
true;
3165 ED.EncounteredNonLocalSideEffect =
false;
3166 ED.IsReachedFromAlignedBarrierOnly =
true;
3168 ED.clearAssumeInstAndAlignedBarriers();
3169 ED.addAlignedBarrier(
A, CB);
3170 auto &CallOutED = CEDMap[{&CB, POST}];
3171 Changed |= mergeInPredecessor(
A, CallOutED, ED);
3175 A.getAAFor<AAIsDead>(*
this, getIRPosition(), DepClassTy::OPTIONAL);
3181 SmallVector<Instruction *> SyncInstWorklist;
3182 for (
auto &RIt : *RPOT) {
3185 bool IsEntryBB = &BB == &EntryBB;
3188 bool AlignedBarrierLastInBlock = IsEntryBB && IsKernel;
3189 bool IsExplicitlyAligned = IsEntryBB && IsKernel;
3190 ExecutionDomainTy ED;
3197 if (LivenessAA && LivenessAA->isAssumedDead(&BB))
3201 if (LivenessAA && LivenessAA->isEdgeDead(PredBB, &BB))
3203 bool InitialEdgeOnly = isInitialThreadOnlyEdge(
3205 mergeInPredecessor(
A, ED, BEDMap[PredBB], InitialEdgeOnly);
3211 for (Instruction &
I : BB) {
3212 bool UsedAssumedInformation;
3213 if (
A.isAssumedDead(
I, *
this, LivenessAA, UsedAssumedInformation,
3214 false, DepClassTy::OPTIONAL,
3222 ED.addAssumeInst(
A, *AI);
3226 if (
II->isAssumeLikeIntrinsic())
3231 if (!ED.EncounteredNonLocalSideEffect) {
3233 if (ED.IsReachedFromAlignedBarrierOnly)
3238 case AtomicOrdering::NotAtomic:
3240 case AtomicOrdering::Unordered:
3242 case AtomicOrdering::Monotonic:
3244 case AtomicOrdering::Acquire:
3246 case AtomicOrdering::Release:
3248 case AtomicOrdering::AcquireRelease:
3250 case AtomicOrdering::SequentiallyConsistent:
3254 NonNoOpFences.insert(FI);
3259 bool IsAlignedBarrier =
3263 AlignedBarrierLastInBlock &= IsNoSync;
3264 IsExplicitlyAligned &= IsNoSync;
3270 if (IsAlignedBarrier) {
3271 HandleAlignedBarrier(*CB, ED);
3272 AlignedBarrierLastInBlock =
true;
3273 IsExplicitlyAligned =
true;
3279 if (!ED.EncounteredNonLocalSideEffect &&
3281 ED.EncounteredNonLocalSideEffect =
true;
3283 ED.IsReachedFromAlignedBarrierOnly =
false;
3291 auto &CallInED = CEDMap[{CB, PRE}];
3292 Changed |= mergeInPredecessor(
A, CallInED, ED);
3298 if (!IsNoSync && Callee && !
Callee->isDeclaration()) {
3299 const auto *EDAA =
A.getAAFor<AAExecutionDomain>(
3301 if (EDAA && EDAA->getState().isValidState()) {
3302 const auto &CalleeED = EDAA->getFunctionExecutionDomain();
3303 ED.IsReachedFromAlignedBarrierOnly =
3304 CalleeED.IsReachedFromAlignedBarrierOnly;
3305 AlignedBarrierLastInBlock = ED.IsReachedFromAlignedBarrierOnly;
3306 if (IsNoSync || !CalleeED.IsReachedFromAlignedBarrierOnly)
3307 ED.EncounteredNonLocalSideEffect |=
3308 CalleeED.EncounteredNonLocalSideEffect;
3310 ED.EncounteredNonLocalSideEffect =
3311 CalleeED.EncounteredNonLocalSideEffect;
3312 if (!CalleeED.IsReachingAlignedBarrierOnly) {
3314 setAndRecord(CallInED.IsReachingAlignedBarrierOnly,
false);
3317 if (CalleeED.IsReachedFromAlignedBarrierOnly)
3318 mergeInPredecessorBarriersAndAssumptions(
A, ED, CalleeED);
3319 auto &CallOutED = CEDMap[{CB, POST}];
3320 Changed |= mergeInPredecessor(
A, CallOutED, ED);
3325 ED.IsReachedFromAlignedBarrierOnly =
false;
3326 Changed |= setAndRecord(CallInED.IsReachingAlignedBarrierOnly,
false);
3329 AlignedBarrierLastInBlock &= ED.IsReachedFromAlignedBarrierOnly;
3331 auto &CallOutED = CEDMap[{CB, POST}];
3332 Changed |= mergeInPredecessor(
A, CallOutED, ED);
3335 if (!
I.mayHaveSideEffects() && !
I.mayReadFromMemory())
3341 const auto *MemAA =
A.getAAFor<AAMemoryLocation>(
3349 if (MemAA && MemAA->getState().isValidState() &&
3350 MemAA->checkForAllAccessesToMemoryKind(
3355 auto &InfoCache =
A.getInfoCache();
3356 if (!
I.mayHaveSideEffects() && InfoCache.isOnlyUsedByAssume(
I))
3360 if (LI->hasMetadata(LLVMContext::MD_invariant_load))
3363 if (!ED.EncounteredNonLocalSideEffect &&
3365 ED.EncounteredNonLocalSideEffect =
true;
3368 bool IsEndAndNotReachingAlignedBarriersOnly =
false;
3370 !BB.getTerminator()->getNumSuccessors()) {
3372 Changed |= mergeInPredecessor(
A, InterProceduralED, ED);
3374 auto &FnED = BEDMap[
nullptr];
3375 if (IsKernel && !IsExplicitlyAligned)
3376 FnED.IsReachingAlignedBarrierOnly =
false;
3377 Changed |= mergeInPredecessor(
A, FnED, ED);
3379 if (!FnED.IsReachingAlignedBarrierOnly) {
3380 IsEndAndNotReachingAlignedBarriersOnly =
true;
3381 SyncInstWorklist.
push_back(BB.getTerminator());
3382 auto &BBED = BEDMap[&BB];
3383 Changed |= setAndRecord(BBED.IsReachingAlignedBarrierOnly,
false);
3387 ExecutionDomainTy &StoredED = BEDMap[&BB];
3388 ED.IsReachingAlignedBarrierOnly = StoredED.IsReachingAlignedBarrierOnly &
3389 !IsEndAndNotReachingAlignedBarriersOnly;
3395 if (ED.IsExecutedByInitialThreadOnly !=
3396 StoredED.IsExecutedByInitialThreadOnly ||
3397 ED.IsReachedFromAlignedBarrierOnly !=
3398 StoredED.IsReachedFromAlignedBarrierOnly ||
3399 ED.EncounteredNonLocalSideEffect !=
3400 StoredED.EncounteredNonLocalSideEffect)
3404 StoredED = std::move(ED);
3409 SmallSetVector<BasicBlock *, 16> Visited;
3410 while (!SyncInstWorklist.
empty()) {
3413 bool HitAlignedBarrierOrKnownEnd =
false;
3418 auto &CallOutED = CEDMap[{CB, POST}];
3419 Changed |= setAndRecord(CallOutED.IsReachingAlignedBarrierOnly,
false);
3420 auto &CallInED = CEDMap[{CB, PRE}];
3421 HitAlignedBarrierOrKnownEnd =
3422 AlignedBarriers.count(CB) || !CallInED.IsReachingAlignedBarrierOnly;
3423 if (HitAlignedBarrierOrKnownEnd)
3425 Changed |= setAndRecord(CallInED.IsReachingAlignedBarrierOnly,
false);
3427 if (HitAlignedBarrierOrKnownEnd)
3431 if (LivenessAA && LivenessAA->isEdgeDead(PredBB, SyncBB))
3433 if (!Visited.
insert(PredBB))
3435 auto &PredED = BEDMap[PredBB];
3436 if (setAndRecord(PredED.IsReachingAlignedBarrierOnly,
false)) {
3438 SyncInstWorklist.
push_back(PredBB->getTerminator());
3441 if (SyncBB != &EntryBB)
3444 setAndRecord(InterProceduralED.IsReachingAlignedBarrierOnly,
false);
3447 return Changed ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;
3452struct AAHeapToShared :
public StateWrapper<BooleanState, AbstractAttribute> {
3453 using Base = StateWrapper<BooleanState, AbstractAttribute>;
3454 AAHeapToShared(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
3457 static AAHeapToShared &createForPosition(
const IRPosition &IRP,
3461 virtual bool isAssumedHeapToShared(CallBase &CB)
const = 0;
3465 virtual bool isAssumedHeapToSharedRemovedFree(CallBase &CB)
const = 0;
3468 StringRef
getName()
const override {
return "AAHeapToShared"; }
3471 const char *getIdAddr()
const override {
return &ID; }
3475 static bool classof(
const AbstractAttribute *AA) {
3480 static const char ID;
3483struct AAHeapToSharedFunction :
public AAHeapToShared {
3484 AAHeapToSharedFunction(
const IRPosition &IRP, Attributor &
A)
3485 : AAHeapToShared(IRP,
A) {}
3487 const std::string getAsStr(Attributor *)
const override {
3488 return "[AAHeapToShared] " + std::to_string(MallocCalls.size()) +
3489 " malloc calls eligible.";
3493 void trackStatistics()
const override {}
3497 void findPotentialRemovedFreeCalls(Attributor &
A) {
3498 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3499 auto &FreeRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_free_shared];
3501 PotentialRemovedFreeCalls.clear();
3503 for (CallBase *CB : MallocCalls) {
3505 for (
auto *U : CB->
users()) {
3507 if (
C &&
C->getCalledFunction() == FreeRFI.Declaration)
3511 if (FreeCalls.
size() != 1)
3514 PotentialRemovedFreeCalls.insert(FreeCalls.
front());
3520 indicatePessimisticFixpoint();
3524 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3525 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
3526 if (!RFI.Declaration)
3530 [](
const IRPosition &,
const AbstractAttribute *,
3531 bool &) -> std::optional<Value *> {
return nullptr; };
3534 const OMPInformationCache::RuntimeFunctionInfo::UseVector *
Uses =
3535 RFI.getUseVector(*
F);
3539 for (Use *U : *
Uses)
3541 MallocCalls.insert(CB);
3546 findPotentialRemovedFreeCalls(
A);
3549 bool isAssumedHeapToShared(CallBase &CB)
const override {
3550 return isValidState() && MallocCalls.count(&CB);
3553 bool isAssumedHeapToSharedRemovedFree(CallBase &CB)
const override {
3554 return isValidState() && PotentialRemovedFreeCalls.count(&CB);
3558 if (MallocCalls.empty())
3559 return ChangeStatus::UNCHANGED;
3561 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3562 auto &FreeCall = OMPInfoCache.RFIs[OMPRTL___kmpc_free_shared];
3566 DepClassTy::OPTIONAL);
3569 for (CallBase *CB : MallocCalls) {
3571 if (HS &&
HS->isAssumedHeapToStack(*CB))
3576 for (
auto *U : CB->
users()) {
3578 if (
C &&
C->getCalledFunction() == FreeCall.Declaration)
3581 if (FreeCalls.
size() != 1)
3588 <<
" with shared memory."
3589 <<
" Shared memory usage is limited to "
3595 <<
" with " << AllocSize->getZExtValue()
3596 <<
" bytes of shared memory\n");
3601 Type *Int8Ty = Type::getInt8Ty(
M->getContext());
3602 Type *Int8ArrTy = ArrayType::get(Int8Ty, AllocSize->getZExtValue());
3603 auto *SharedMem =
new GlobalVariable(
3607 static_cast<unsigned>(AddressSpace::Shared));
3609 SharedMem, PointerType::getUnqual(
M->getContext()));
3611 auto Remark = [&](OptimizationRemark
OR) {
3612 return OR <<
"Replaced globalized variable with "
3613 <<
ore::NV(
"SharedMemory", AllocSize->getZExtValue())
3614 << (AllocSize->isOne() ?
" byte " :
" bytes ")
3615 <<
"of shared memory.";
3617 A.emitRemark<OptimizationRemark>(CB,
"OMP111",
Remark);
3619 MaybeAlign
Alignment = CB->getRetAlign();
3621 "HeapToShared on allocation without alignment attribute");
3625 A.deleteAfterManifest(*CB);
3626 A.deleteAfterManifest(*FreeCalls.
front());
3628 SharedMemoryUsed += AllocSize->getZExtValue();
3629 NumBytesMovedToSharedMemory = SharedMemoryUsed;
3630 Changed = ChangeStatus::CHANGED;
3637 if (MallocCalls.empty())
3638 return indicatePessimisticFixpoint();
3639 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3640 auto &RFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
3641 if (!RFI.Declaration)
3642 return ChangeStatus::UNCHANGED;
3646 auto NumMallocCalls = MallocCalls.size();
3649 for (User *U : RFI.Declaration->
users()) {
3651 if (CB->getCaller() !=
F)
3653 if (!MallocCalls.count(CB))
3656 MallocCalls.remove(CB);
3659 const auto *ED =
A.getAAFor<AAExecutionDomain>(
3661 if (!ED || !ED->isExecutedByInitialThreadOnly(*CB))
3662 MallocCalls.remove(CB);
3666 findPotentialRemovedFreeCalls(
A);
3668 if (NumMallocCalls != MallocCalls.size())
3669 return ChangeStatus::CHANGED;
3671 return ChangeStatus::UNCHANGED;
3675 SmallSetVector<CallBase *, 4> MallocCalls;
3677 SmallPtrSet<CallBase *, 4> PotentialRemovedFreeCalls;
3679 unsigned SharedMemoryUsed = 0;
3682struct AAKernelInfo :
public StateWrapper<KernelInfoState, AbstractAttribute> {
3683 using Base = StateWrapper<KernelInfoState, AbstractAttribute>;
3684 AAKernelInfo(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
3688 static bool requiresCalleeForCallBase() {
return false; }
3691 void trackStatistics()
const override {}
3694 const std::string getAsStr(Attributor *)
const override {
3695 if (!isValidState())
3697 return std::string(SPMDCompatibilityTracker.isAssumed() ?
"SPMD"
3699 std::string(SPMDCompatibilityTracker.isAtFixpoint() ?
" [FIX]"
3701 std::string(
" #PRs: ") +
3702 (ReachedKnownParallelRegions.isValidState()
3703 ? std::to_string(ReachedKnownParallelRegions.size())
3705 ", #Unknown PRs: " +
3706 (ReachedUnknownParallelRegions.isValidState()
3707 ? std::to_string(ReachedUnknownParallelRegions.size())
3709 ", #Reaching Kernels: " +
3710 (ReachingKernelEntries.isValidState()
3711 ? std::to_string(ReachingKernelEntries.size())
3714 (ParallelLevels.isValidState()
3715 ? std::to_string(ParallelLevels.size())
3717 ", NestedPar: " + (NestedParallelism ?
"yes" :
"no");
3721 static AAKernelInfo &createForPosition(
const IRPosition &IRP, Attributor &
A);
3724 StringRef
getName()
const override {
return "AAKernelInfo"; }
3727 const char *getIdAddr()
const override {
return &ID; }
3730 static bool classof(
const AbstractAttribute *AA) {
3734 static const char ID;
3739struct AAKernelInfoFunction : AAKernelInfo {
3740 AAKernelInfoFunction(
const IRPosition &IRP, Attributor &
A)
3741 : AAKernelInfo(IRP,
A) {}
3743 SmallPtrSet<Instruction *, 4> GuardedInstructions;
3745 SmallPtrSetImpl<Instruction *> &getGuardedInstructions() {
3746 return GuardedInstructions;
3749 void setConfigurationOfKernelEnvironment(ConstantStruct *ConfigC) {
3751 KernelEnvC, ConfigC, {KernelInfo::ConfigurationIdx});
3752 assert(NewKernelEnvC &&
"Failed to create new kernel environment");
3756#define KERNEL_ENVIRONMENT_CONFIGURATION_SETTER(MEMBER) \
3757 void set##MEMBER##OfKernelEnvironment(ConstantInt *NewVal) { \
3758 ConstantStruct *ConfigC = \
3759 KernelInfo::getConfigurationFromKernelEnvironment(KernelEnvC); \
3760 Constant *NewConfigC = ConstantFoldInsertValueInstruction( \
3761 ConfigC, NewVal, {KernelInfo::MEMBER##Idx}); \
3762 assert(NewConfigC && "Failed to create new configuration environment"); \
3763 setConfigurationOfKernelEnvironment(cast<ConstantStruct>(NewConfigC)); \
3774#undef KERNEL_ENVIRONMENT_CONFIGURATION_SETTER
3781 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
3785 OMPInformationCache::RuntimeFunctionInfo &InitRFI =
3786 OMPInfoCache.RFIs[OMPRTL___kmpc_target_init];
3787 OMPInformationCache::RuntimeFunctionInfo &DeinitRFI =
3788 OMPInfoCache.RFIs[OMPRTL___kmpc_target_deinit];
3792 auto StoreCallBase = [](
Use &U,
3793 OMPInformationCache::RuntimeFunctionInfo &RFI,
3795 CallBase *CB = OpenMPOpt::getCallIfRegularCall(U, &RFI);
3797 "Unexpected use of __kmpc_target_init or __kmpc_target_deinit!");
3799 "Multiple uses of __kmpc_target_init or __kmpc_target_deinit!");
3805 StoreCallBase(U, InitRFI, KernelInitCB);
3809 DeinitRFI.foreachUse(
3811 StoreCallBase(U, DeinitRFI, KernelDeinitCB);
3817 if (!KernelInitCB || !KernelDeinitCB)
3821 ReachingKernelEntries.insert(Fn);
3822 IsKernelEntry =
true;
3830 KernelConfigurationSimplifyCB =
3832 bool &UsedAssumedInformation) -> std::optional<Constant *> {
3833 if (!isAtFixpoint()) {
3836 UsedAssumedInformation =
true;
3842 A.registerGlobalVariableSimplificationCallback(
3843 *KernelEnvGV, KernelConfigurationSimplifyCB);
3846 bool CanChangeToSPMD = OMPInfoCache.runtimeFnsAvailable(
3847 {OMPRTL___kmpc_get_hardware_thread_id_in_block,
3848 OMPRTL___kmpc_barrier_simple_spmd});
3852 KernelInfo::getExecModeFromKernelEnvironment(KernelEnvC);
3857 SPMDCompatibilityTracker.indicateOptimisticFixpoint();
3861 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
3863 setExecModeOfKernelEnvironment(AssumedExecModeC);
3870 setMinThreadsOfKernelEnvironment(ConstantInt::get(Int32Ty, MinThreads));
3872 setMaxThreadsOfKernelEnvironment(ConstantInt::get(Int32Ty,
MaxThreads));
3873 auto [MinTeams, MaxTeams] =
3876 setMinTeamsOfKernelEnvironment(ConstantInt::get(Int32Ty, MinTeams));
3878 setMaxTeamsOfKernelEnvironment(ConstantInt::get(Int32Ty, MaxTeams));
3881 KernelInfo::getMayUseNestedParallelismFromKernelEnvironment(KernelEnvC);
3882 ConstantInt *AssumedMayUseNestedParallelismC = ConstantInt::get(
3884 setMayUseNestedParallelismOfKernelEnvironment(
3885 AssumedMayUseNestedParallelismC);
3889 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
3892 ConstantInt::get(UseGenericStateMachineC->
getIntegerType(),
false);
3893 setUseGenericStateMachineOfKernelEnvironment(
3894 AssumedUseGenericStateMachineC);
3900 if (!OMPInfoCache.RFIs[RFKind].Declaration)
3902 A.registerVirtualUseCallback(*OMPInfoCache.RFIs[RFKind].Declaration, CB);
3906 auto AddDependence = [](
Attributor &
A,
const AAKernelInfo *KI,
3923 if (SPMDCompatibilityTracker.isValidState())
3924 return AddDependence(
A,
this, QueryingAA);
3926 if (!ReachedKnownParallelRegions.isValidState())
3927 return AddDependence(
A,
this, QueryingAA);
3933 RegisterVirtualUse(OMPRTL___kmpc_get_hardware_num_threads_in_block,
3934 CustomStateMachineUseCB);
3935 RegisterVirtualUse(OMPRTL___kmpc_get_warp_size, CustomStateMachineUseCB);
3936 RegisterVirtualUse(OMPRTL___kmpc_barrier_simple_generic,
3937 CustomStateMachineUseCB);
3938 RegisterVirtualUse(OMPRTL___kmpc_kernel_parallel,
3939 CustomStateMachineUseCB);
3940 RegisterVirtualUse(OMPRTL___kmpc_kernel_end_parallel,
3941 CustomStateMachineUseCB);
3945 if (SPMDCompatibilityTracker.isAtFixpoint())
3952 if (!SPMDCompatibilityTracker.isValidState())
3953 return AddDependence(
A,
this, QueryingAA);
3956 RegisterVirtualUse(OMPRTL___kmpc_get_hardware_thread_id_in_block,
3965 if (!SPMDCompatibilityTracker.isValidState())
3966 return AddDependence(
A,
this, QueryingAA);
3967 if (SPMDCompatibilityTracker.empty())
3968 return AddDependence(
A,
this, QueryingAA);
3969 if (!mayContainParallelRegion())
3970 return AddDependence(
A,
this, QueryingAA);
3973 RegisterVirtualUse(OMPRTL___kmpc_barrier_simple_spmd, SPMDBarrierUseCB);
3977 static std::string sanitizeForGlobalName(std::string S) {
3981 return !((C >=
'a' && C <=
'z') || (C >=
'A' && C <=
'Z') ||
3982 (C >=
'0' && C <=
'9') || C ==
'_');
3993 if (!KernelInitCB || !KernelDeinitCB)
3994 return ChangeStatus::UNCHANGED;
3998 bool HasBuiltStateMachine =
true;
3999 if (!changeToSPMDMode(
A,
Changed)) {
4001 HasBuiltStateMachine = buildCustomStateMachine(
A,
Changed);
4003 HasBuiltStateMachine =
false;
4007 ConstantStruct *ExistingKernelEnvC =
4009 ConstantInt *OldUseGenericStateMachineVal =
4010 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
4011 ExistingKernelEnvC);
4012 if (!HasBuiltStateMachine)
4013 setUseGenericStateMachineOfKernelEnvironment(
4014 OldUseGenericStateMachineVal);
4017 GlobalVariable *KernelEnvGV =
4021 Changed = ChangeStatus::CHANGED;
4027 void insertInstructionGuardsHelper(Attributor &
A) {
4028 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4030 auto CreateGuardedRegion = [&](
Instruction *RegionStartI,
4032 LoopInfo *LI =
nullptr;
4033 DominatorTree *DT =
nullptr;
4034 MemorySSAUpdater *MSU =
nullptr;
4064 DT, LI, MSU,
"region.guarded.end");
4067 MSU,
"region.barrier");
4070 DT, LI, MSU,
"region.exit");
4072 SplitBlock(ParentBB, RegionStartI, DT, LI, MSU,
"region.guarded");
4075 "Expected a different CFG");
4078 ParentBB, ParentBB->
getTerminator(), DT, LI, MSU,
"region.check.tid");
4081 A.registerManifestAddedBasicBlock(*RegionEndBB);
4082 A.registerManifestAddedBasicBlock(*RegionBarrierBB);
4083 A.registerManifestAddedBasicBlock(*RegionExitBB);
4084 A.registerManifestAddedBasicBlock(*RegionStartBB);
4085 A.registerManifestAddedBasicBlock(*RegionCheckTidBB);
4087 bool HasBroadcastValues =
false;
4090 for (Instruction &
I : *RegionStartBB) {
4092 for (Use &U :
I.uses()) {
4098 if (OutsideUses.
empty())
4101 HasBroadcastValues =
true;
4105 auto *SharedMem =
new GlobalVariable(
4106 M,
I.getType(),
false,
4108 sanitizeForGlobalName(
4109 (
I.getName() +
".guarded.output.alloc").str()),
4111 static_cast<unsigned>(AddressSpace::Shared));
4114 new StoreInst(&
I, SharedMem,
4117 LoadInst *LoadI =
new LoadInst(
4118 I.getType(), SharedMem,
I.getName() +
".guarded.output.load",
4122 for (Use *U : OutsideUses)
4123 A.changeUseAfterManifest(*U, *LoadI);
4126 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4131 OpenMPIRBuilder::LocationDescription Loc(
4132 InsertPointTy(ParentBB, ParentBB->
end()),
DL);
4134 uint32_t SrcLocStrSize;
4143 OpenMPIRBuilder::LocationDescription LocRegionCheckTid(
4144 InsertPointTy(RegionCheckTidBB, RegionCheckTidBB->
end()),
DL);
4146 FunctionCallee HardwareTidFn =
4148 M, OMPRTL___kmpc_get_hardware_thread_id_in_block);
4152 OMPInfoCache.setCallingConvention(HardwareTidFn, Tid);
4154 OMPInfoCache.OMPBuilder.
Builder
4155 .
CreateCondBr(TidCheck, RegionStartBB, RegionBarrierBB)
4160 FunctionCallee BarrierFn =
4162 M, OMPRTL___kmpc_barrier_simple_spmd);
4164 {InsertPointTy(RegionBarrierBB,
4169 OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
4172 if (HasBroadcastValues) {
4177 OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
4181 auto &AllocSharedRFI = OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared];
4182 SmallPtrSet<BasicBlock *, 8> Visited;
4183 for (Instruction *GuardedI : SPMDCompatibilityTracker) {
4185 if (!Visited.
insert(BB).second)
4191 while (++IP != IPEnd) {
4192 if (!IP->mayHaveSideEffects() && !IP->mayReadFromMemory())
4195 if (OpenMPOpt::getCallIfRegularCall(*
I, &AllocSharedRFI))
4197 if (!
I->user_empty() || !SPMDCompatibilityTracker.contains(
I)) {
4198 LastEffect =
nullptr;
4205 for (
auto &Reorder : Reorders)
4206 Reorder.first->moveBefore(Reorder.second->getIterator());
4211 for (Instruction *GuardedI : SPMDCompatibilityTracker) {
4213 auto *CalleeAA =
A.lookupAAFor<AAKernelInfo>(
4216 assert(CalleeAA !=
nullptr &&
"Expected Callee AAKernelInfo");
4219 if (CalleeAAFunction.getGuardedInstructions().contains(GuardedI))
4222 Instruction *GuardedRegionStart =
nullptr, *GuardedRegionEnd =
nullptr;
4223 for (Instruction &
I : *BB) {
4226 if (SPMDCompatibilityTracker.contains(&
I)) {
4227 CalleeAAFunction.getGuardedInstructions().insert(&
I);
4228 if (GuardedRegionStart)
4229 GuardedRegionEnd = &
I;
4231 GuardedRegionStart = GuardedRegionEnd = &
I;
4238 if (GuardedRegionStart) {
4240 std::make_pair(GuardedRegionStart, GuardedRegionEnd));
4241 GuardedRegionStart =
nullptr;
4242 GuardedRegionEnd =
nullptr;
4247 for (
auto &GR : GuardedRegions)
4248 CreateGuardedRegion(GR.first, GR.second);
4251 void forceSingleThreadPerWorkgroupHelper(Attributor &
A) {
4260 auto &Ctx = getAnchorValue().getContext();
4267 KernelInitCB->
getNextNode(),
"main.thread.user_code");
4272 A.registerManifestAddedBasicBlock(*InitBB);
4273 A.registerManifestAddedBasicBlock(*UserCodeBB);
4274 A.registerManifestAddedBasicBlock(*ReturnBB);
4283 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4284 FunctionCallee ThreadIdInBlockFn =
4286 M, OMPRTL___kmpc_get_hardware_thread_id_in_block);
4289 CallInst *ThreadIdInBlock =
4291 OMPInfoCache.setCallingConvention(ThreadIdInBlockFn, ThreadIdInBlock);
4297 ConstantInt::get(ThreadIdInBlock->
getType(), 0),
4298 "thread.is_main", InitBB);
4304 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4306 if (!SPMDCompatibilityTracker.isAssumed()) {
4307 for (Instruction *NonCompatibleI : SPMDCompatibilityTracker) {
4308 if (!NonCompatibleI)
4313 if (OMPInfoCache.RTLFunctions.contains(CB->getCalledFunction()))
4316 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
4317 ORA <<
"Value has potential side effects preventing SPMD-mode "
4320 ORA <<
". Add `[[omp::assume(\"ompx_spmd_amenable\")]]` to "
4321 "the called function to override";
4325 A.emitRemark<OptimizationRemarkAnalysis>(NonCompatibleI,
"OMP121",
4329 << *NonCompatibleI <<
"\n");
4341 Kernel = CB->getCaller();
4346 ConstantStruct *ExistingKernelEnvC =
4349 KernelInfo::getExecModeFromKernelEnvironment(ExistingKernelEnvC);
4355 Changed = ChangeStatus::CHANGED;
4359 if (mayContainParallelRegion())
4360 insertInstructionGuardsHelper(
A);
4362 forceSingleThreadPerWorkgroupHelper(
A);
4367 "Initially non-SPMD kernel has SPMD exec mode!");
4368 setExecModeOfKernelEnvironment(
4372 ++NumOpenMPTargetRegionKernelsSPMD;
4376 OMPInfoCache.SPMDizedKernels.insert(
Kernel);
4378 auto Remark = [&](OptimizationRemark
OR) {
4379 return OR <<
"Transformed generic-mode kernel to SPMD-mode.";
4381 A.emitRemark<OptimizationRemark>(KernelInitCB,
"OMP120",
Remark);
4391 if (!ReachedKnownParallelRegions.isValidState())
4394 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4395 if (!OMPInfoCache.runtimeFnsAvailable(
4396 {OMPRTL___kmpc_get_hardware_num_threads_in_block,
4397 OMPRTL___kmpc_get_warp_size, OMPRTL___kmpc_barrier_simple_generic,
4398 OMPRTL___kmpc_kernel_parallel, OMPRTL___kmpc_kernel_end_parallel}))
4401 ConstantStruct *ExistingKernelEnvC =
4408 ConstantInt *UseStateMachineC =
4409 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
4410 ExistingKernelEnvC);
4411 ConstantInt *ModeC =
4412 KernelInfo::getExecModeFromKernelEnvironment(ExistingKernelEnvC);
4417 if (UseStateMachineC->
isZero() ||
4421 Changed = ChangeStatus::CHANGED;
4424 setUseGenericStateMachineOfKernelEnvironment(
4431 if (!mayContainParallelRegion()) {
4432 ++NumOpenMPTargetRegionKernelsWithoutStateMachine;
4434 auto Remark = [&](OptimizationRemark
OR) {
4435 return OR <<
"Removing unused state machine from generic-mode kernel.";
4437 A.emitRemark<OptimizationRemark>(KernelInitCB,
"OMP130",
Remark);
4443 if (ReachedUnknownParallelRegions.empty()) {
4444 ++NumOpenMPTargetRegionKernelsCustomStateMachineWithoutFallback;
4446 auto Remark = [&](OptimizationRemark
OR) {
4447 return OR <<
"Rewriting generic-mode kernel with a customized state "
4450 A.emitRemark<OptimizationRemark>(KernelInitCB,
"OMP131",
Remark);
4452 ++NumOpenMPTargetRegionKernelsCustomStateMachineWithFallback;
4454 auto Remark = [&](OptimizationRemarkAnalysis
OR) {
4455 return OR <<
"Generic-mode kernel is executed with a customized state "
4456 "machine that requires a fallback.";
4458 A.emitRemark<OptimizationRemarkAnalysis>(KernelInitCB,
"OMP132",
Remark);
4461 for (CallBase *UnknownParallelRegionCB : ReachedUnknownParallelRegions) {
4462 if (!UnknownParallelRegionCB)
4464 auto Remark = [&](OptimizationRemarkAnalysis ORA) {
4465 return ORA <<
"Call may contain unknown parallel regions. Use "
4466 <<
"`[[omp::assume(\"omp_no_parallelism\")]]` to "
4469 A.emitRemark<OptimizationRemarkAnalysis>(UnknownParallelRegionCB,
4504 auto &Ctx = getAnchorValue().getContext();
4508 BasicBlock *InitBB = KernelInitCB->getParent();
4510 KernelInitCB->getNextNode(),
"thread.user_code.check");
4514 Ctx,
"worker_state_machine.begin",
Kernel, UserCodeEntryBB);
4516 Ctx,
"worker_state_machine.finished",
Kernel, UserCodeEntryBB);
4518 Ctx,
"worker_state_machine.is_active.check",
Kernel, UserCodeEntryBB);
4521 Kernel, UserCodeEntryBB);
4524 Kernel, UserCodeEntryBB);
4526 Ctx,
"worker_state_machine.done.barrier",
Kernel, UserCodeEntryBB);
4527 A.registerManifestAddedBasicBlock(*InitBB);
4528 A.registerManifestAddedBasicBlock(*UserCodeEntryBB);
4529 A.registerManifestAddedBasicBlock(*IsWorkerCheckBB);
4530 A.registerManifestAddedBasicBlock(*StateMachineBeginBB);
4531 A.registerManifestAddedBasicBlock(*StateMachineFinishedBB);
4532 A.registerManifestAddedBasicBlock(*StateMachineIsActiveCheckBB);
4533 A.registerManifestAddedBasicBlock(*StateMachineIfCascadeCurrentBB);
4534 A.registerManifestAddedBasicBlock(*StateMachineEndParallelBB);
4535 A.registerManifestAddedBasicBlock(*StateMachineDoneBarrierBB);
4537 const DebugLoc &DLoc = KernelInitCB->getDebugLoc();
4543 ConstantInt::getAllOnesValue(KernelInitCB->getType()),
4544 "thread.is_worker", InitBB);
4549 FunctionCallee BlockHwSizeFn =
4551 M, OMPRTL___kmpc_get_hardware_num_threads_in_block);
4552 FunctionCallee WarpSizeFn =
4554 M, OMPRTL___kmpc_get_warp_size);
4555 CallInst *BlockHwSize =
4557 OMPInfoCache.setCallingConvention(BlockHwSizeFn, BlockHwSize);
4559 CallInst *WarpSize =
4561 OMPInfoCache.setCallingConvention(WarpSizeFn, WarpSize);
4564 BlockHwSize, WarpSize,
"block.size", IsWorkerCheckBB);
4568 "thread.is_main_or_worker", IsWorkerCheckBB);
4571 StateMachineFinishedBB, IsWorkerCheckBB);
4574 const DataLayout &
DL =
M.getDataLayout();
4575 Type *VoidPtrTy = PointerType::getUnqual(Ctx);
4577 new AllocaInst(VoidPtrTy,
DL.getAllocaAddrSpace(),
nullptr,
4582 OpenMPIRBuilder::LocationDescription(
4583 IRBuilder<>::InsertPoint(StateMachineBeginBB,
4584 StateMachineBeginBB->
end()),
4587 Value *Ident = KernelInfo::getIdentFromKernelEnvironment(KernelEnvC);
4588 Value *GTid = KernelInitCB;
4590 FunctionCallee BarrierFn =
4592 M, OMPRTL___kmpc_barrier_simple_generic);
4595 OMPInfoCache.setCallingConvention(BarrierFn, Barrier);
4599 (
unsigned int)AddressSpace::Generic) {
4600 WorkFnAI =
new AddrSpaceCastInst(
4601 WorkFnAI, PointerType::get(Ctx, (
unsigned int)AddressSpace::Generic),
4602 WorkFnAI->
getName() +
".generic", StateMachineBeginBB);
4606 FunctionCallee KernelParallelFn =
4608 M, OMPRTL___kmpc_kernel_parallel);
4610 KernelParallelFn, {WorkFnAI},
"worker.is_active", StateMachineBeginBB);
4611 OMPInfoCache.setCallingConvention(KernelParallelFn, IsActiveWorker);
4613 Instruction *WorkFn =
new LoadInst(VoidPtrTy, WorkFnAI,
"worker.work_fn",
4614 StateMachineBeginBB);
4617 FunctionType *ParallelRegionFnTy = FunctionType::get(
4618 Type::getVoidTy(Ctx), {Type::getInt16Ty(Ctx), Type::getInt32Ty(Ctx)},
4624 StateMachineBeginBB);
4625 IsDone->setDebugLoc(DLoc);
4627 StateMachineIsActiveCheckBB, StateMachineBeginBB)
4631 StateMachineDoneBarrierBB, StateMachineIsActiveCheckBB)
4637 const unsigned int WrapperFunctionArgNo = 6;
4642 for (
int I = 0,
E = ReachedKnownParallelRegions.size();
I <
E; ++
I) {
4643 auto *CB = ReachedKnownParallelRegions[
I];
4645 CB->getArgOperand(WrapperFunctionArgNo)->stripPointerCasts());
4647 Ctx,
"worker_state_machine.parallel_region.execute",
Kernel,
4648 StateMachineEndParallelBB);
4650 ->setDebugLoc(DLoc);
4656 Kernel, StateMachineEndParallelBB);
4657 A.registerManifestAddedBasicBlock(*PRExecuteBB);
4658 A.registerManifestAddedBasicBlock(*PRNextBB);
4663 if (
I + 1 <
E || !ReachedUnknownParallelRegions.empty()) {
4666 "worker.check_parallel_region", StateMachineIfCascadeCurrentBB);
4674 StateMachineIfCascadeCurrentBB)
4676 StateMachineIfCascadeCurrentBB = PRNextBB;
4682 if (!ReachedUnknownParallelRegions.empty()) {
4683 StateMachineIfCascadeCurrentBB->
setName(
4684 "worker_state_machine.parallel_region.fallback.execute");
4686 StateMachineIfCascadeCurrentBB)
4687 ->setDebugLoc(DLoc);
4690 StateMachineIfCascadeCurrentBB)
4693 FunctionCallee EndParallelFn =
4695 M, OMPRTL___kmpc_kernel_end_parallel);
4696 CallInst *EndParallel =
4698 OMPInfoCache.setCallingConvention(EndParallelFn, EndParallel);
4704 ->setDebugLoc(DLoc);
4714 KernelInfoState StateBefore = getState();
4720 struct UpdateKernelEnvCRAII {
4721 AAKernelInfoFunction &AA;
4723 UpdateKernelEnvCRAII(AAKernelInfoFunction &AA) : AA(AA) {}
4725 ~UpdateKernelEnvCRAII() {
4729 ConstantStruct *ExistingKernelEnvC =
4732 if (!AA.isValidState()) {
4733 AA.KernelEnvC = ExistingKernelEnvC;
4737 if (!AA.ReachedKnownParallelRegions.isValidState())
4738 AA.setUseGenericStateMachineOfKernelEnvironment(
4739 KernelInfo::getUseGenericStateMachineFromKernelEnvironment(
4740 ExistingKernelEnvC));
4742 if (!AA.SPMDCompatibilityTracker.isValidState())
4743 AA.setExecModeOfKernelEnvironment(
4744 KernelInfo::getExecModeFromKernelEnvironment(ExistingKernelEnvC));
4746 ConstantInt *MayUseNestedParallelismC =
4747 KernelInfo::getMayUseNestedParallelismFromKernelEnvironment(
4749 ConstantInt *NewMayUseNestedParallelismC = ConstantInt::get(
4750 MayUseNestedParallelismC->
getIntegerType(), AA.NestedParallelism);
4751 AA.setMayUseNestedParallelismOfKernelEnvironment(
4752 NewMayUseNestedParallelismC);
4762 if (!
I.mayWriteToMemory())
4765 const auto *UnderlyingObjsAA =
A.getAAFor<AAUnderlyingObjects>(
4767 DepClassTy::OPTIONAL);
4768 auto *
HS =
A.getAAFor<AAHeapToStack>(
4770 DepClassTy::OPTIONAL);
4771 if (UnderlyingObjsAA &&
4772 UnderlyingObjsAA->forallUnderlyingObjects([&](
Value &Obj) {
4773 if (AA::isAssumedThreadLocalObject(A, Obj, *this))
4777 auto *CB = dyn_cast<CallBase>(&Obj);
4778 return CB && HS && HS->isAssumedHeapToStack(*CB);
4784 SPMDCompatibilityTracker.insert(&
I);
4788 bool UsedAssumedInformationInCheckRWInst =
false;
4789 if (!SPMDCompatibilityTracker.isAtFixpoint())
4790 if (!
A.checkForAllReadWriteInstructions(
4791 CheckRWInst, *
this, UsedAssumedInformationInCheckRWInst))
4792 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4794 bool UsedAssumedInformationFromReachingKernels =
false;
4795 if (!IsKernelEntry) {
4796 updateParallelLevels(
A);
4798 bool AllReachingKernelsKnown =
true;
4799 updateReachingKernelEntries(
A, AllReachingKernelsKnown);
4800 UsedAssumedInformationFromReachingKernels = !AllReachingKernelsKnown;
4802 if (!SPMDCompatibilityTracker.empty()) {
4803 if (!ParallelLevels.isValidState())
4804 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4805 else if (!ReachingKernelEntries.isValidState())
4806 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4812 for (
auto *
Kernel : ReachingKernelEntries) {
4813 auto *CBAA =
A.getAAFor<AAKernelInfo>(
4815 if (CBAA && CBAA->SPMDCompatibilityTracker.isValidState() &&
4816 CBAA->SPMDCompatibilityTracker.isAssumed())
4820 if (!CBAA || !CBAA->SPMDCompatibilityTracker.isAtFixpoint())
4821 UsedAssumedInformationFromReachingKernels =
true;
4823 if (SPMD != 0 &&
Generic != 0)
4824 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4830 bool AllParallelRegionStatesWereFixed =
true;
4831 bool AllSPMDStatesWereFixed =
true;
4834 auto *CBAA =
A.getAAFor<AAKernelInfo>(
4838 getState() ^= CBAA->getState();
4839 AllSPMDStatesWereFixed &= CBAA->SPMDCompatibilityTracker.isAtFixpoint();
4840 AllParallelRegionStatesWereFixed &=
4841 CBAA->ReachedKnownParallelRegions.isAtFixpoint();
4842 AllParallelRegionStatesWereFixed &=
4843 CBAA->ReachedUnknownParallelRegions.isAtFixpoint();
4847 bool UsedAssumedInformationInCheckCallInst =
false;
4848 if (!
A.checkForAllCallLikeInstructions(
4849 CheckCallInst, *
this, UsedAssumedInformationInCheckCallInst)) {
4851 <<
"Failed to visit all call-like instructions!\n";);
4852 return indicatePessimisticFixpoint();
4857 if (!UsedAssumedInformationInCheckCallInst &&
4858 AllParallelRegionStatesWereFixed) {
4859 ReachedKnownParallelRegions.indicateOptimisticFixpoint();
4860 ReachedUnknownParallelRegions.indicateOptimisticFixpoint();
4865 if (!UsedAssumedInformationInCheckRWInst &&
4866 !UsedAssumedInformationInCheckCallInst &&
4867 !UsedAssumedInformationFromReachingKernels && AllSPMDStatesWereFixed)
4868 SPMDCompatibilityTracker.indicateOptimisticFixpoint();
4870 return StateBefore == getState() ? ChangeStatus::UNCHANGED
4871 : ChangeStatus::CHANGED;
4876 void updateReachingKernelEntries(Attributor &
A,
4877 bool &AllReachingKernelsKnown) {
4878 auto PredCallSite = [&](AbstractCallSite ACS) {
4881 assert(Caller &&
"Caller is nullptr");
4883 auto *CAA =
A.getOrCreateAAFor<AAKernelInfo>(
4885 if (CAA && CAA->ReachingKernelEntries.isValidState()) {
4886 ReachingKernelEntries ^= CAA->ReachingKernelEntries;
4892 ReachingKernelEntries.indicatePessimisticFixpoint();
4897 if (!
A.checkForAllCallSites(PredCallSite, *
this,
4899 AllReachingKernelsKnown))
4900 ReachingKernelEntries.indicatePessimisticFixpoint();
4904 void updateParallelLevels(Attributor &
A) {
4905 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4906 OMPInformationCache::RuntimeFunctionInfo &Parallel60RFI =
4907 OMPInfoCache.RFIs[OMPRTL___kmpc_parallel_60];
4909 auto PredCallSite = [&](AbstractCallSite ACS) {
4912 assert(Caller &&
"Caller is nullptr");
4916 if (CAA && CAA->ParallelLevels.isValidState()) {
4922 if (Caller == Parallel60RFI.Declaration) {
4923 ParallelLevels.indicatePessimisticFixpoint();
4927 ParallelLevels ^= CAA->ParallelLevels;
4934 ParallelLevels.indicatePessimisticFixpoint();
4939 bool AllCallSitesKnown =
true;
4940 if (!
A.checkForAllCallSites(PredCallSite, *
this,
4943 ParallelLevels.indicatePessimisticFixpoint();
4950struct AAKernelInfoCallSite : AAKernelInfo {
4951 AAKernelInfoCallSite(
const IRPosition &IRP, Attributor &
A)
4952 : AAKernelInfo(IRP,
A) {}
4956 AAKernelInfo::initialize(
A);
4959 auto *AssumptionAA =
A.getAAFor<AAAssumptionInfo>(
4963 if (AssumptionAA && AssumptionAA->hasAssumption(
"ompx_spmd_amenable")) {
4964 indicateOptimisticFixpoint();
4972 indicateOptimisticFixpoint();
4981 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
4982 const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee);
4983 if (It == OMPInfoCache.RuntimeFunctionIDMap.end()) {
4985 if (!Callee || !
A.isFunctionIPOAmendable(*Callee)) {
4989 if (!AssumptionAA ||
4990 !(AssumptionAA->hasAssumption(
"omp_no_openmp") ||
4991 AssumptionAA->hasAssumption(
"omp_no_parallelism")))
4992 ReachedUnknownParallelRegions.insert(&CB);
4996 if (!SPMDCompatibilityTracker.isAtFixpoint()) {
4997 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
4998 SPMDCompatibilityTracker.insert(&CB);
5003 indicateOptimisticFixpoint();
5009 if (NumCallees > 1) {
5010 indicatePessimisticFixpoint();
5017 case OMPRTL___kmpc_is_spmd_exec_mode:
5018 case OMPRTL___kmpc_distribute_static_fini:
5019 case OMPRTL___kmpc_for_static_fini:
5020 case OMPRTL___kmpc_global_thread_num:
5021 case OMPRTL___kmpc_get_hardware_num_threads_in_block:
5022 case OMPRTL___kmpc_get_hardware_num_blocks:
5023 case OMPRTL___kmpc_single:
5024 case OMPRTL___kmpc_end_single:
5025 case OMPRTL___kmpc_master:
5026 case OMPRTL___kmpc_end_master:
5027 case OMPRTL___kmpc_barrier:
5028 case OMPRTL___kmpc_nvptx_parallel_reduce_nowait_v2:
5029 case OMPRTL___kmpc_gpu_xteam_reduce_nowait:
5030 case OMPRTL___kmpc_error:
5031 case OMPRTL___kmpc_flush:
5032 case OMPRTL___kmpc_get_hardware_thread_id_in_block:
5033 case OMPRTL___kmpc_get_warp_size:
5034 case OMPRTL_omp_get_thread_num:
5035 case OMPRTL_omp_get_num_threads:
5036 case OMPRTL_omp_get_max_threads:
5037 case OMPRTL_omp_in_parallel:
5038 case OMPRTL_omp_get_dynamic:
5039 case OMPRTL_omp_get_cancellation:
5040 case OMPRTL_omp_get_nested:
5041 case OMPRTL_omp_get_schedule:
5042 case OMPRTL_omp_get_thread_limit:
5043 case OMPRTL_omp_get_supported_active_levels:
5044 case OMPRTL_omp_get_max_active_levels:
5045 case OMPRTL_omp_get_level:
5046 case OMPRTL_omp_get_ancestor_thread_num:
5047 case OMPRTL_omp_get_team_size:
5048 case OMPRTL_omp_get_active_level:
5049 case OMPRTL_omp_in_final:
5050 case OMPRTL_omp_get_proc_bind:
5051 case OMPRTL_omp_get_num_places:
5052 case OMPRTL_omp_get_num_procs:
5053 case OMPRTL_omp_get_place_proc_ids:
5054 case OMPRTL_omp_get_place_num:
5055 case OMPRTL_omp_get_partition_num_places:
5056 case OMPRTL_omp_get_partition_place_nums:
5057 case OMPRTL_omp_get_wtime:
5059 case OMPRTL___kmpc_distribute_static_init_4:
5060 case OMPRTL___kmpc_distribute_static_init_4u:
5061 case OMPRTL___kmpc_distribute_static_init_8:
5062 case OMPRTL___kmpc_distribute_static_init_8u:
5063 case OMPRTL___kmpc_for_static_init_4:
5064 case OMPRTL___kmpc_for_static_init_4u:
5065 case OMPRTL___kmpc_for_static_init_8:
5066 case OMPRTL___kmpc_for_static_init_8u: {
5068 unsigned ScheduleArgOpNo = 2;
5069 auto *ScheduleTypeCI =
5071 unsigned ScheduleTypeVal =
5072 ScheduleTypeCI ? ScheduleTypeCI->getZExtValue() : 0;
5074 case OMPScheduleType::UnorderedStatic:
5075 case OMPScheduleType::UnorderedStaticChunked:
5076 case OMPScheduleType::OrderedDistribute:
5077 case OMPScheduleType::OrderedDistributeChunked:
5080 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5081 SPMDCompatibilityTracker.insert(&CB);
5085 case OMPRTL___kmpc_target_init:
5088 case OMPRTL___kmpc_target_deinit:
5089 KernelDeinitCB = &CB;
5091 case OMPRTL___kmpc_parallel_60:
5092 if (!handleParallel60(
A, CB))
5093 indicatePessimisticFixpoint();
5095 case OMPRTL___kmpc_omp_task:
5097 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5098 SPMDCompatibilityTracker.insert(&CB);
5099 ReachedUnknownParallelRegions.insert(&CB);
5101 case OMPRTL___kmpc_alloc_shared:
5102 case OMPRTL___kmpc_free_shared:
5105 case OMPRTL___kmpc_distribute_static_loop_4:
5106 case OMPRTL___kmpc_distribute_static_loop_4u:
5107 case OMPRTL___kmpc_distribute_static_loop_8:
5108 case OMPRTL___kmpc_distribute_static_loop_8u:
5109 case OMPRTL___kmpc_distribute_for_static_loop_4:
5110 case OMPRTL___kmpc_distribute_for_static_loop_4u:
5111 case OMPRTL___kmpc_distribute_for_static_loop_8:
5112 case OMPRTL___kmpc_distribute_for_static_loop_8u:
5113 case OMPRTL___kmpc_for_static_loop_4:
5114 case OMPRTL___kmpc_for_static_loop_4u:
5115 case OMPRTL___kmpc_for_static_loop_8:
5116 case OMPRTL___kmpc_for_static_loop_8u:
5120 ReachedUnknownParallelRegions.insert(&CB);
5125 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5126 SPMDCompatibilityTracker.insert(&CB);
5131 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5132 SPMDCompatibilityTracker.insert(&CB);
5138 indicateOptimisticFixpoint();
5142 A.getAAFor<AACallEdges>(*
this, getIRPosition(), DepClassTy::OPTIONAL);
5143 if (!AACE || !AACE->getState().isValidState() || AACE->hasUnknownCallee()) {
5144 CheckCallee(getAssociatedFunction(), 1);
5147 const auto &OptimisticEdges = AACE->getOptimisticEdges();
5148 for (
auto *Callee : OptimisticEdges) {
5149 CheckCallee(Callee, OptimisticEdges.size());
5160 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5161 KernelInfoState StateBefore = getState();
5163 auto CheckCallee = [&](
Function *
F,
int NumCallees) {
5164 const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(
F);
5168 if (It == OMPInfoCache.RuntimeFunctionIDMap.end()) {
5171 A.getAAFor<AAKernelInfo>(*
this, FnPos, DepClassTy::REQUIRED);
5173 return indicatePessimisticFixpoint();
5174 if (getState() == FnAA->getState())
5175 return ChangeStatus::UNCHANGED;
5176 getState() = FnAA->getState();
5177 return ChangeStatus::CHANGED;
5180 return indicatePessimisticFixpoint();
5183 if (It->getSecond() == OMPRTL___kmpc_parallel_60) {
5184 if (!handleParallel60(
A, CB))
5185 return indicatePessimisticFixpoint();
5186 return StateBefore == getState() ? ChangeStatus::UNCHANGED
5187 : ChangeStatus::CHANGED;
5193 (It->getSecond() == OMPRTL___kmpc_alloc_shared ||
5194 It->getSecond() == OMPRTL___kmpc_free_shared) &&
5195 "Expected a __kmpc_alloc_shared or __kmpc_free_shared runtime call");
5197 auto *HeapToStackAA =
A.getAAFor<AAHeapToStack>(
5199 auto *HeapToSharedAA =
A.getAAFor<AAHeapToShared>(
5207 case OMPRTL___kmpc_alloc_shared:
5208 if ((!HeapToStackAA || !HeapToStackAA->isAssumedHeapToStack(CB)) &&
5209 (!HeapToSharedAA || !HeapToSharedAA->isAssumedHeapToShared(CB)))
5210 SPMDCompatibilityTracker.insert(&CB);
5212 case OMPRTL___kmpc_free_shared:
5213 if ((!HeapToStackAA ||
5214 !HeapToStackAA->isAssumedHeapToStackRemovedFree(CB)) &&
5216 !HeapToSharedAA->isAssumedHeapToSharedRemovedFree(CB)))
5217 SPMDCompatibilityTracker.insert(&CB);
5220 SPMDCompatibilityTracker.indicatePessimisticFixpoint();
5221 SPMDCompatibilityTracker.insert(&CB);
5223 return ChangeStatus::CHANGED;
5227 A.getAAFor<AACallEdges>(*
this, getIRPosition(), DepClassTy::OPTIONAL);
5228 if (!AACE || !AACE->getState().isValidState() || AACE->hasUnknownCallee()) {
5229 if (
Function *
F = getAssociatedFunction())
5232 const auto &OptimisticEdges = AACE->getOptimisticEdges();
5233 for (
auto *Callee : OptimisticEdges) {
5234 CheckCallee(Callee, OptimisticEdges.size());
5240 return StateBefore == getState() ? ChangeStatus::UNCHANGED
5241 : ChangeStatus::CHANGED;
5246 bool handleParallel60(Attributor &
A, CallBase &CB) {
5247 const unsigned int NonWrapperFunctionArgNo = 5;
5248 const unsigned int WrapperFunctionArgNo = 6;
5249 auto ParallelRegionOpArgNo = SPMDCompatibilityTracker.isAssumed()
5250 ? NonWrapperFunctionArgNo
5251 : WrapperFunctionArgNo;
5255 if (!ParallelRegion)
5258 ReachedKnownParallelRegions.insert(&CB);
5260 auto *FnAA =
A.getAAFor<AAKernelInfo>(
5262 NestedParallelism |= !FnAA || !FnAA->getState().isValidState() ||
5263 !FnAA->ReachedKnownParallelRegions.empty() ||
5264 !FnAA->ReachedKnownParallelRegions.isValidState() ||
5265 !FnAA->ReachedUnknownParallelRegions.isValidState() ||
5266 !FnAA->ReachedUnknownParallelRegions.empty();
5271struct AAFoldRuntimeCall
5272 :
public StateWrapper<BooleanState, AbstractAttribute> {
5273 using Base = StateWrapper<BooleanState, AbstractAttribute>;
5275 AAFoldRuntimeCall(
const IRPosition &IRP, Attributor &
A) :
Base(IRP) {}
5278 void trackStatistics()
const override {}
5281 static AAFoldRuntimeCall &createForPosition(
const IRPosition &IRP,
5285 StringRef
getName()
const override {
return "AAFoldRuntimeCall"; }
5288 const char *getIdAddr()
const override {
return &ID; }
5292 static bool classof(
const AbstractAttribute *AA) {
5296 static const char ID;
5299struct AAFoldRuntimeCallCallSiteReturned : AAFoldRuntimeCall {
5300 AAFoldRuntimeCallCallSiteReturned(
const IRPosition &IRP, Attributor &
A)
5301 : AAFoldRuntimeCall(IRP,
A) {}
5304 const std::string getAsStr(Attributor *)
const override {
5305 if (!isValidState())
5308 std::string Str(
"simplified value: ");
5310 if (!SimplifiedValue)
5311 return Str + std::string(
"none");
5313 if (!*SimplifiedValue)
5314 return Str + std::string(
"nullptr");
5317 return Str + std::to_string(CI->getSExtValue());
5319 return Str + std::string(
"unknown");
5324 indicatePessimisticFixpoint();
5328 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5329 const auto &It = OMPInfoCache.RuntimeFunctionIDMap.find(Callee);
5330 assert(It != OMPInfoCache.RuntimeFunctionIDMap.end() &&
5331 "Expected a known OpenMP runtime function");
5333 RFKind = It->getSecond();
5336 A.registerSimplificationCallback(
5338 [&](
const IRPosition &IRP,
const AbstractAttribute *AA,
5339 bool &UsedAssumedInformation) -> std::optional<Value *> {
5340 assert((isValidState() || SimplifiedValue ==
nullptr) &&
5341 "Unexpected invalid state!");
5343 if (!isAtFixpoint()) {
5344 UsedAssumedInformation =
true;
5346 A.recordDependence(*
this, *AA, DepClassTy::OPTIONAL);
5348 return SimplifiedValue;
5355 case OMPRTL___kmpc_is_spmd_exec_mode:
5358 case OMPRTL___kmpc_parallel_level:
5361 case OMPRTL___kmpc_get_hardware_num_threads_in_block:
5362 Changed =
Changed | foldKernelFnAttribute(
A,
"omp_target_thread_limit");
5364 case OMPRTL___kmpc_get_hardware_num_blocks:
5377 if (SimplifiedValue && *SimplifiedValue) {
5380 A.deleteAfterManifest(
I);
5383 auto Remark = [&](OptimizationRemark
OR) {
5385 return OR <<
"Replacing OpenMP runtime call "
5387 <<
ore::NV(
"FoldedValue",
C->getZExtValue()) <<
".";
5388 return OR <<
"Replacing OpenMP runtime call "
5393 A.emitRemark<OptimizationRemark>(CB,
"OMP180",
Remark);
5396 << **SimplifiedValue <<
"\n");
5398 Changed = ChangeStatus::CHANGED;
5405 SimplifiedValue =
nullptr;
5406 return AAFoldRuntimeCall::indicatePessimisticFixpoint();
5412 std::optional<Value *> SimplifiedValueBefore = SimplifiedValue;
5414 unsigned AssumedSPMDCount = 0, KnownSPMDCount = 0;
5415 unsigned AssumedNonSPMDCount = 0, KnownNonSPMDCount = 0;
5416 auto *CallerKernelInfoAA =
A.getAAFor<AAKernelInfo>(
5419 if (!CallerKernelInfoAA ||
5420 !CallerKernelInfoAA->ReachingKernelEntries.isValidState())
5421 return indicatePessimisticFixpoint();
5423 for (
Kernel K : CallerKernelInfoAA->ReachingKernelEntries) {
5425 DepClassTy::REQUIRED);
5427 if (!AA || !AA->isValidState()) {
5428 SimplifiedValue =
nullptr;
5429 return indicatePessimisticFixpoint();
5432 if (AA->SPMDCompatibilityTracker.isAssumed()) {
5433 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5438 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5439 ++KnownNonSPMDCount;
5441 ++AssumedNonSPMDCount;
5445 if ((AssumedSPMDCount + KnownSPMDCount) &&
5446 (AssumedNonSPMDCount + KnownNonSPMDCount))
5447 return indicatePessimisticFixpoint();
5449 auto &Ctx = getAnchorValue().getContext();
5450 if (KnownSPMDCount || AssumedSPMDCount) {
5451 assert(KnownNonSPMDCount == 0 && AssumedNonSPMDCount == 0 &&
5452 "Expected only SPMD kernels!");
5455 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx),
true);
5456 }
else if (KnownNonSPMDCount || AssumedNonSPMDCount) {
5457 assert(KnownSPMDCount == 0 && AssumedSPMDCount == 0 &&
5458 "Expected only non-SPMD kernels!");
5461 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx),
false);
5466 assert(!SimplifiedValue &&
"SimplifiedValue should be none");
5469 return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED
5470 : ChangeStatus::CHANGED;
5475 std::optional<Value *> SimplifiedValueBefore = SimplifiedValue;
5477 auto *CallerKernelInfoAA =
A.getAAFor<AAKernelInfo>(
5480 if (!CallerKernelInfoAA ||
5481 !CallerKernelInfoAA->ParallelLevels.isValidState())
5482 return indicatePessimisticFixpoint();
5484 if (!CallerKernelInfoAA->ReachingKernelEntries.isValidState())
5485 return indicatePessimisticFixpoint();
5487 if (CallerKernelInfoAA->ReachingKernelEntries.empty()) {
5488 assert(!SimplifiedValue &&
5489 "SimplifiedValue should keep none at this point");
5490 return ChangeStatus::UNCHANGED;
5493 unsigned AssumedSPMDCount = 0, KnownSPMDCount = 0;
5494 unsigned AssumedNonSPMDCount = 0, KnownNonSPMDCount = 0;
5495 for (
Kernel K : CallerKernelInfoAA->ReachingKernelEntries) {
5497 DepClassTy::REQUIRED);
5498 if (!AA || !AA->SPMDCompatibilityTracker.isValidState())
5499 return indicatePessimisticFixpoint();
5501 if (AA->SPMDCompatibilityTracker.isAssumed()) {
5502 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5507 if (AA->SPMDCompatibilityTracker.isAtFixpoint())
5508 ++KnownNonSPMDCount;
5510 ++AssumedNonSPMDCount;
5514 if ((AssumedSPMDCount + KnownSPMDCount) &&
5515 (AssumedNonSPMDCount + KnownNonSPMDCount))
5516 return indicatePessimisticFixpoint();
5518 auto &Ctx = getAnchorValue().getContext();
5522 if (AssumedSPMDCount || KnownSPMDCount) {
5523 assert(KnownNonSPMDCount == 0 && AssumedNonSPMDCount == 0 &&
5524 "Expected only SPMD kernels!");
5525 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), 1);
5527 assert(KnownSPMDCount == 0 && AssumedSPMDCount == 0 &&
5528 "Expected only non-SPMD kernels!");
5529 SimplifiedValue = ConstantInt::get(Type::getInt8Ty(Ctx), 0);
5531 return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED
5532 : ChangeStatus::CHANGED;
5535 ChangeStatus foldKernelFnAttribute(Attributor &
A, llvm::StringRef Attr) {
5537 int32_t CurrentAttrValue = -1;
5538 std::optional<Value *> SimplifiedValueBefore = SimplifiedValue;
5540 auto *CallerKernelInfoAA =
A.getAAFor<AAKernelInfo>(
5543 if (!CallerKernelInfoAA ||
5544 !CallerKernelInfoAA->ReachingKernelEntries.isValidState())
5545 return indicatePessimisticFixpoint();
5548 for (
Kernel K : CallerKernelInfoAA->ReachingKernelEntries) {
5549 int32_t NextAttrVal =
K->getFnAttributeAsParsedInteger(Attr, -1);
5551 if (NextAttrVal == -1 ||
5552 (CurrentAttrValue != -1 && CurrentAttrValue != NextAttrVal))
5553 return indicatePessimisticFixpoint();
5554 CurrentAttrValue = NextAttrVal;
5557 if (CurrentAttrValue != -1) {
5558 auto &Ctx = getAnchorValue().getContext();
5560 ConstantInt::get(Type::getInt32Ty(Ctx), CurrentAttrValue);
5562 return SimplifiedValue == SimplifiedValueBefore ? ChangeStatus::UNCHANGED
5563 : ChangeStatus::CHANGED;
5569 std::optional<Value *> SimplifiedValue;
5579 auto &RFI = OMPInfoCache.RFIs[RF];
5580 RFI.foreachUse(SCC, [&](Use &U,
Function &
F) {
5581 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &RFI);
5584 A.getOrCreateAAFor<AAFoldRuntimeCall>(
5586 DepClassTy::NONE,
false,
5592void OpenMPOpt::registerAAs(
bool IsModulePass) {
5602 A.getOrCreateAAFor<AAKernelInfo>(
5604 DepClassTy::NONE,
false,
5608 OMPInformationCache::RuntimeFunctionInfo &InitRFI =
5609 OMPInfoCache.RFIs[OMPRTL___kmpc_target_init];
5610 InitRFI.foreachUse(SCC, CreateKernelInfoCB);
5612 registerFoldRuntimeCall(OMPRTL___kmpc_is_spmd_exec_mode);
5613 registerFoldRuntimeCall(OMPRTL___kmpc_parallel_level);
5614 registerFoldRuntimeCall(OMPRTL___kmpc_get_hardware_num_threads_in_block);
5615 registerFoldRuntimeCall(OMPRTL___kmpc_get_hardware_num_blocks);
5620 for (
int Idx = 0; Idx < OMPInfoCache.ICVs.size() - 1; ++Idx) {
5623 auto &GetterRFI = OMPInfoCache.RFIs[ICVInfo.Getter];
5626 CallInst *CI = OpenMPOpt::getCallIfRegularCall(U, &GetterRFI);
5633 A.getOrCreateAAFor<AAICVTracker>(CBPos);
5637 GetterRFI.foreachUse(SCC, CreateAA);
5646 for (
auto *
F : SCC) {
5647 if (
F->isDeclaration())
5653 if (
F->hasLocalLinkage()) {
5655 const auto *CB = dyn_cast<CallBase>(U.getUser());
5656 return CB && CB->isCallee(&U) &&
5657 A.isRunOn(const_cast<Function *>(CB->getCaller()));
5661 registerAAsForFunction(
A, *
F);
5665void OpenMPOpt::registerAAsForFunction(Attributor &
A,
const Function &
F) {
5666 auto &OMPInfoCache =
static_cast<OMPInformationCache &
>(
A.getInfoCache());
5669 A.getOrCreateAAFor<AAExecutionDomain>(FPos);
5670 if (
F.hasFnAttribute(Attribute::Convergent))
5671 A.getOrCreateAAFor<AANonConvergent>(FPos);
5673 bool FunctionUsesSharedAlloc =
false;
5675 const OMPInformationCache::RuntimeFunctionInfo::UseVector *SharedAllocUses =
5676 OMPInfoCache.RFIs[OMPRTL___kmpc_alloc_shared].getUseVector(
5678 FunctionUsesSharedAlloc = SharedAllocUses && !SharedAllocUses->
empty();
5680 bool HasHeapToStackCandidate =
false;
5681 const TargetLibraryInfo *TLI =
nullptr;
5685 bool UsedAssumedInformation =
false;
5688 A.getOrCreateAAFor<AAAddressSpace>(
5695 TLI =
A.getInfoCache().getTargetLibraryInfoForFunction(
F);
5696 HasHeapToStackCandidate =
5700 A.getOrCreateAAFor<AAIndirectCallInfo>(
5705 A.getOrCreateAAFor<AAAddressSpace>(
5714 if (
II->getIntrinsicID() == Intrinsic::assume) {
5715 A.getOrCreateAAFor<AAPotentialValues>(
5722 if (FunctionUsesSharedAlloc)
5723 A.getOrCreateAAFor<AAHeapToShared>(FPos);
5724 if (HasHeapToStackCandidate)
5725 A.getOrCreateAAFor<AAHeapToStack>(FPos);
5728const char AAICVTracker::ID = 0;
5729const char AAKernelInfo::ID = 0;
5731const char AAHeapToShared::ID = 0;
5732const char AAFoldRuntimeCall::ID = 0;
5734AAICVTracker &AAICVTracker::createForPosition(
const IRPosition &IRP,
5736 AAICVTracker *AA =
nullptr;
5744 AA =
new (
A.Allocator) AAICVTrackerFunctionReturned(IRP,
A);
5747 AA =
new (
A.Allocator) AAICVTrackerCallSiteReturned(IRP,
A);
5750 AA =
new (
A.Allocator) AAICVTrackerCallSite(IRP,
A);
5753 AA =
new (
A.Allocator) AAICVTrackerFunction(IRP,
A);
5762 AAExecutionDomainFunction *
AA =
nullptr;
5772 "AAExecutionDomain can only be created for function position!");
5774 AA =
new (
A.Allocator) AAExecutionDomainFunction(IRP,
A);
5781AAHeapToShared &AAHeapToShared::createForPosition(
const IRPosition &IRP,
5783 AAHeapToSharedFunction *
AA =
nullptr;
5793 "AAHeapToShared can only be created for function position!");
5795 AA =
new (
A.Allocator) AAHeapToSharedFunction(IRP,
A);
5802AAKernelInfo &AAKernelInfo::createForPosition(
const IRPosition &IRP,
5804 AAKernelInfo *AA =
nullptr;
5814 AA =
new (
A.Allocator) AAKernelInfoCallSite(IRP,
A);
5817 AA =
new (
A.Allocator) AAKernelInfoFunction(IRP,
A);
5824AAFoldRuntimeCall &AAFoldRuntimeCall::createForPosition(
const IRPosition &IRP,
5826 AAFoldRuntimeCall *AA =
nullptr;
5835 llvm_unreachable(
"KernelInfo can only be created for call site position!");
5837 AA =
new (
A.Allocator) AAFoldRuntimeCallCallSiteReturned(IRP,
A);
5857 unsigned NumAssumedCallees) {
5875 if (Kernels.contains(&
F))
5877 return !
F.use_empty();
5884 return ORA <<
"Could not internalize function. "
5885 <<
"Some optimizations may not be possible. [OMP140]";
5897 if (!
F.isDeclaration() && !Kernels.contains(&
F) && IsCalled(
F) &&
5901 }
else if (!
F.hasLocalLinkage() && !
F.hasFnAttribute(Attribute::Cold)) {
5914 if (!
F.isDeclaration() && !InternalizedMap.
lookup(&
F)) {
5916 Functions.insert(&
F);
5934 OMPInformationCache InfoCache(M, AG, Allocator,
nullptr, PostLink);
5936 unsigned MaxFixpointIterations =
5949 return F.hasFnAttribute(
"kernel");
5954 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache,
A);
5960 if (!
F.isDeclaration() && !Kernels.contains(&
F) &&
5961 !
F.hasFnAttribute(Attribute::NoInline))
5962 F.addFnAttr(Attribute::AlwaysInline);
5992 Module &M = *
C.begin()->getFunction().getParent();
6014 OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, Allocator,
6015 &Functions, PostLink);
6017 unsigned MaxFixpointIterations =
6032 OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache,
A);
6033 bool Changed = OMPOpt.run(
false);
6052 if (
F.hasKernelCallingConv()) {
6057 ++NumOpenMPTargetRegionKernels;
6060 ++NumNonOpenMPTargetRegionKernels;
6067 Metadata *MD = M.getModuleFlag(
"openmp");
6075 Metadata *MD = M.getModuleFlag(
"openmp-device");
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
amdgpu aa AMDGPU Address space based Alias Analysis Wrapper
amdgpu next use AMDGPU Next Use Analysis Printer
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Expand Atomic instructions
static cl::opt< unsigned > SetFixpointIterations("attributor-max-iterations", cl::Hidden, cl::desc("Maximal number of fixpoint iterations."), cl::init(32))
static const Function * getParent(const Value *V)
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file provides interfaces used to manipulate a call graph, regardless if it is a "old style" Call...
This file provides interfaces used to build and manipulate a call graph, which is a very useful tool ...
This file contains the declarations for the subclasses of Constant, which represent the different fla...
This file defines the DenseSet and SmallDenseSet classes.
This file defines an array type that can be indexed using scoped enum values.
static void emitRemark(const Function &F, OptimizationRemarkEmitter &ORE, bool Skip)
Loop::LoopBounds::Direction Direction
Machine Check Debug Module
This file provides utility analysis objects describing memory locations.
uint64_t IntrinsicInst * II
This file defines constans and helpers used when dealing with OpenMP.
This file defines constans that will be used by both host and device compilation.
static constexpr auto TAG
static cl::opt< bool > HideMemoryTransferLatency("openmp-hide-memory-transfer-latency", cl::desc("[WIP] Tries to hide the latency of host to device memory" " transfers"), cl::Hidden, cl::init(false))
static cl::opt< bool > DisableOpenMPOptStateMachineRewrite("openmp-opt-disable-state-machine-rewrite", cl::desc("Disable OpenMP optimizations that replace the state machine."), cl::Hidden, cl::init(false))
static cl::opt< bool > EnableParallelRegionMerging("openmp-opt-enable-merging", cl::desc("Enable the OpenMP region merging optimization."), cl::Hidden, cl::init(false))
static cl::opt< bool > PrintModuleAfterOptimizations("openmp-opt-print-module-after", cl::desc("Print the current module after OpenMP optimizations."), cl::Hidden, cl::init(false))
#define KERNEL_ENVIRONMENT_CONFIGURATION_GETTER(MEMBER)
#define KERNEL_ENVIRONMENT_CONFIGURATION_IDX(MEMBER, IDX)
#define KERNEL_ENVIRONMENT_CONFIGURATION_SETTER(MEMBER)
static cl::opt< bool > PrintOpenMPKernels("openmp-print-gpu-kernels", cl::init(false), cl::Hidden)
static cl::opt< bool > DisableOpenMPOptFolding("openmp-opt-disable-folding", cl::desc("Disable OpenMP optimizations involving folding."), cl::Hidden, cl::init(false))
static bool shouldSpecializeIndirectCallee(Attributor &, const AbstractAttribute &, CallBase &, Function &, unsigned NumAssumedCallees)
Bound the if-cascade AAIndirectCallInfo builds for an indirect call.
static cl::opt< bool > PrintModuleBeforeOptimizations("openmp-opt-print-module-before", cl::desc("Print the current module before OpenMP optimizations."), cl::Hidden, cl::init(false))
static cl::opt< unsigned > SetFixpointIterations("openmp-opt-max-iterations", cl::Hidden, cl::desc("Maximal number of attributor iterations."), cl::init(256))
static cl::opt< bool > DisableInternalization("openmp-opt-disable-internalization", cl::desc("Disable function internalization."), cl::Hidden, cl::init(false))
static cl::opt< bool > PrintICVValues("openmp-print-icv-values", cl::init(false), cl::Hidden)
static cl::opt< bool > DisableOpenMPOptimizations("openmp-opt-disable", cl::desc("Disable OpenMP specific optimizations."), cl::Hidden, cl::init(false))
static cl::opt< unsigned > SharedMemoryLimit("openmp-opt-shared-limit", cl::Hidden, cl::desc("Maximum amount of shared memory to use."), cl::init(std::numeric_limits< unsigned >::max()))
static cl::opt< bool > EnableVerboseRemarks("openmp-opt-verbose-remarks", cl::desc("Enables more verbose remarks."), cl::Hidden, cl::init(false))
static cl::opt< unsigned > MaxCalleesForSpecialization("openmp-opt-max-callees-for-specialization", cl::Hidden, cl::desc("Number of possible callees above which an indirect call site is " "left alone rather than specialized into an if-cascade."), cl::init(3))
static cl::opt< bool > DisableOpenMPOptDeglobalization("openmp-opt-disable-deglobalization", cl::desc("Disable OpenMP optimizations involving deglobalization."), cl::Hidden, cl::init(false))
static cl::opt< bool > DisableOpenMPOptBarrierElimination("openmp-opt-disable-barrier-elimination", cl::desc("Disable OpenMP optimizations that eliminate barriers."), cl::Hidden, cl::init(false))
static cl::opt< bool > DeduceICVValues("openmp-deduce-icv-values", cl::init(false), cl::Hidden)
#define KERNEL_ENVIRONMENT_IDX(MEMBER, IDX)
#define KERNEL_ENVIRONMENT_GETTER(MEMBER, RETURNTYPE)
static cl::opt< bool > DisableOpenMPOptSPMDization("openmp-opt-disable-spmdization", cl::desc("Disable OpenMP optimizations involving SPMD-ization."), cl::Hidden, cl::init(false))
static cl::opt< bool > AlwaysInlineDeviceFunctions("openmp-opt-inline-device", cl::desc("Inline all applicable functions on the device."), cl::Hidden, cl::init(false))
FunctionAnalysisManager FAM
This file builds on the ADT/GraphTraits.h file to build a generic graph post order iterator.
static StringRef getName(Value *V)
Remove Loads Into Fake Uses
std::pair< BasicBlock *, BasicBlock * > Edge
static bool contains(SmallPtrSetImpl< ConstantExpr * > &Cache, ConstantExpr *Expr, Constant *C)
This file implements a set that has insertion order iteration characteristics.
This file defines the SmallPtrSet class.
This file defines the SmallVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
static 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)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the 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.
std::function< bool(Attributor &A, const AbstractAttribute &AA, CallBase &CB, Function &AssumedCallee, unsigned NumAssumedCallees)> IndirectCalleeSpecializationCallback
Callback function to determine if an indirect call targets should be made direct call targets (with a...
bool DefaultInitializeLiveInternals
Flag to determine if we want to initialize all default AAs for an internal function marked live.
The fixpoint analysis framework that orchestrates the attribute deduction.
static LLVM_ABI bool isInternalizable(Function &F)
Returns true if the function F can be internalized.
std::function< std::optional< Value * >( const IRPosition &, const AbstractAttribute *, bool &)> SimplifictionCallbackTy
Register CB as a simplification callback.
std::function< std::optional< Constant * >( const GlobalVariable &, const AbstractAttribute *, bool &)> GlobalVariableSimplifictionCallbackTy
Register CB as a simplification callback.
std::function< bool(Attributor &, const AbstractAttribute *)> VirtualUseCallbackTy
static LLVM_ABI bool internalizeFunctions(SmallPtrSetImpl< Function * > &FnSet, DenseMap< Function *, Function * > &FnMap)
Make copies of each function in the set FnSet such that the copied version has internal linkage after...
Simple wrapper for a single bit (boolean) state.
Support structure for SCC passes to communicate updates the call graph back to the CGSCC pass manager...
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),...