106f32e7eSjoerg //===- AMDGPUAnnotateKernelFeaturesPass.cpp -------------------------------===//
206f32e7eSjoerg //
306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information.
506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606f32e7eSjoerg //
706f32e7eSjoerg //===----------------------------------------------------------------------===//
806f32e7eSjoerg //
906f32e7eSjoerg /// \file This pass adds target attributes to functions which use intrinsics
1006f32e7eSjoerg /// which will impact calling convention lowering.
1106f32e7eSjoerg //
1206f32e7eSjoerg //===----------------------------------------------------------------------===//
1306f32e7eSjoerg 
1406f32e7eSjoerg #include "AMDGPU.h"
15*da58b97aSjoerg #include "GCNSubtarget.h"
1606f32e7eSjoerg #include "llvm/Analysis/CallGraph.h"
1706f32e7eSjoerg #include "llvm/Analysis/CallGraphSCCPass.h"
1806f32e7eSjoerg #include "llvm/CodeGen/TargetPassConfig.h"
19*da58b97aSjoerg #include "llvm/IR/IntrinsicsAMDGPU.h"
20*da58b97aSjoerg #include "llvm/IR/IntrinsicsR600.h"
2106f32e7eSjoerg #include "llvm/Target/TargetMachine.h"
2206f32e7eSjoerg 
2306f32e7eSjoerg #define DEBUG_TYPE "amdgpu-annotate-kernel-features"
2406f32e7eSjoerg 
2506f32e7eSjoerg using namespace llvm;
2606f32e7eSjoerg 
2706f32e7eSjoerg namespace {
28*da58b97aSjoerg static constexpr StringLiteral ImplicitAttrNames[] = {
29*da58b97aSjoerg     // X ids unnecessarily propagated to kernels.
30*da58b97aSjoerg     "amdgpu-work-item-id-x",  "amdgpu-work-item-id-y",
31*da58b97aSjoerg     "amdgpu-work-item-id-z",  "amdgpu-work-group-id-x",
32*da58b97aSjoerg     "amdgpu-work-group-id-y", "amdgpu-work-group-id-z",
33*da58b97aSjoerg     "amdgpu-dispatch-ptr",    "amdgpu-dispatch-id",
34*da58b97aSjoerg     "amdgpu-queue-ptr",       "amdgpu-implicitarg-ptr"};
3506f32e7eSjoerg 
3606f32e7eSjoerg class AMDGPUAnnotateKernelFeatures : public CallGraphSCCPass {
3706f32e7eSjoerg private:
3806f32e7eSjoerg   const TargetMachine *TM = nullptr;
3906f32e7eSjoerg   SmallVector<CallGraphNode*, 8> NodeList;
4006f32e7eSjoerg 
4106f32e7eSjoerg   bool addFeatureAttributes(Function &F);
4206f32e7eSjoerg   bool processUniformWorkGroupAttribute();
4306f32e7eSjoerg   bool propagateUniformWorkGroupAttribute(Function &Caller, Function &Callee);
4406f32e7eSjoerg 
4506f32e7eSjoerg public:
4606f32e7eSjoerg   static char ID;
4706f32e7eSjoerg 
AMDGPUAnnotateKernelFeatures()4806f32e7eSjoerg   AMDGPUAnnotateKernelFeatures() : CallGraphSCCPass(ID) {}
4906f32e7eSjoerg 
5006f32e7eSjoerg   bool doInitialization(CallGraph &CG) override;
5106f32e7eSjoerg   bool runOnSCC(CallGraphSCC &SCC) override;
5206f32e7eSjoerg 
getPassName() const5306f32e7eSjoerg   StringRef getPassName() const override {
5406f32e7eSjoerg     return "AMDGPU Annotate Kernel Features";
5506f32e7eSjoerg   }
5606f32e7eSjoerg 
getAnalysisUsage(AnalysisUsage & AU) const5706f32e7eSjoerg   void getAnalysisUsage(AnalysisUsage &AU) const override {
5806f32e7eSjoerg     AU.setPreservesAll();
5906f32e7eSjoerg     CallGraphSCCPass::getAnalysisUsage(AU);
6006f32e7eSjoerg   }
6106f32e7eSjoerg 
6206f32e7eSjoerg   static bool visitConstantExpr(const ConstantExpr *CE);
6306f32e7eSjoerg   static bool visitConstantExprsRecursively(
6406f32e7eSjoerg     const Constant *EntryC,
65*da58b97aSjoerg     SmallPtrSet<const Constant *, 8> &ConstantExprVisited, bool IsFunc,
66*da58b97aSjoerg     bool HasApertureRegs);
6706f32e7eSjoerg };
6806f32e7eSjoerg 
6906f32e7eSjoerg } // end anonymous namespace
7006f32e7eSjoerg 
7106f32e7eSjoerg char AMDGPUAnnotateKernelFeatures::ID = 0;
7206f32e7eSjoerg 
7306f32e7eSjoerg char &llvm::AMDGPUAnnotateKernelFeaturesID = AMDGPUAnnotateKernelFeatures::ID;
7406f32e7eSjoerg 
7506f32e7eSjoerg INITIALIZE_PASS(AMDGPUAnnotateKernelFeatures, DEBUG_TYPE,
7606f32e7eSjoerg                 "Add AMDGPU function attributes", false, false)
7706f32e7eSjoerg 
7806f32e7eSjoerg 
7906f32e7eSjoerg // The queue ptr is only needed when casting to flat, not from it.
castRequiresQueuePtr(unsigned SrcAS)8006f32e7eSjoerg static bool castRequiresQueuePtr(unsigned SrcAS) {
8106f32e7eSjoerg   return SrcAS == AMDGPUAS::LOCAL_ADDRESS || SrcAS == AMDGPUAS::PRIVATE_ADDRESS;
8206f32e7eSjoerg }
8306f32e7eSjoerg 
castRequiresQueuePtr(const AddrSpaceCastInst * ASC)8406f32e7eSjoerg static bool castRequiresQueuePtr(const AddrSpaceCastInst *ASC) {
8506f32e7eSjoerg   return castRequiresQueuePtr(ASC->getSrcAddressSpace());
8606f32e7eSjoerg }
8706f32e7eSjoerg 
isDSAddress(const Constant * C)88*da58b97aSjoerg static bool isDSAddress(const Constant *C) {
89*da58b97aSjoerg   const GlobalValue *GV = dyn_cast<GlobalValue>(C);
90*da58b97aSjoerg   if (!GV)
91*da58b97aSjoerg     return false;
92*da58b97aSjoerg   unsigned AS = GV->getAddressSpace();
93*da58b97aSjoerg   return AS == AMDGPUAS::LOCAL_ADDRESS || AS == AMDGPUAS::REGION_ADDRESS;
94*da58b97aSjoerg }
95*da58b97aSjoerg 
visitConstantExpr(const ConstantExpr * CE)9606f32e7eSjoerg bool AMDGPUAnnotateKernelFeatures::visitConstantExpr(const ConstantExpr *CE) {
9706f32e7eSjoerg   if (CE->getOpcode() == Instruction::AddrSpaceCast) {
9806f32e7eSjoerg     unsigned SrcAS = CE->getOperand(0)->getType()->getPointerAddressSpace();
9906f32e7eSjoerg     return castRequiresQueuePtr(SrcAS);
10006f32e7eSjoerg   }
10106f32e7eSjoerg 
10206f32e7eSjoerg   return false;
10306f32e7eSjoerg }
10406f32e7eSjoerg 
visitConstantExprsRecursively(const Constant * EntryC,SmallPtrSet<const Constant *,8> & ConstantExprVisited,bool IsFunc,bool HasApertureRegs)10506f32e7eSjoerg bool AMDGPUAnnotateKernelFeatures::visitConstantExprsRecursively(
10606f32e7eSjoerg   const Constant *EntryC,
107*da58b97aSjoerg   SmallPtrSet<const Constant *, 8> &ConstantExprVisited,
108*da58b97aSjoerg   bool IsFunc, bool HasApertureRegs) {
10906f32e7eSjoerg 
11006f32e7eSjoerg   if (!ConstantExprVisited.insert(EntryC).second)
11106f32e7eSjoerg     return false;
11206f32e7eSjoerg 
11306f32e7eSjoerg   SmallVector<const Constant *, 16> Stack;
11406f32e7eSjoerg   Stack.push_back(EntryC);
11506f32e7eSjoerg 
11606f32e7eSjoerg   while (!Stack.empty()) {
11706f32e7eSjoerg     const Constant *C = Stack.pop_back_val();
11806f32e7eSjoerg 
119*da58b97aSjoerg     // We need to trap on DS globals in non-entry functions.
120*da58b97aSjoerg     if (IsFunc && isDSAddress(C))
121*da58b97aSjoerg       return true;
122*da58b97aSjoerg 
12306f32e7eSjoerg     // Check this constant expression.
12406f32e7eSjoerg     if (const auto *CE = dyn_cast<ConstantExpr>(C)) {
125*da58b97aSjoerg       if (!HasApertureRegs && visitConstantExpr(CE))
12606f32e7eSjoerg         return true;
12706f32e7eSjoerg     }
12806f32e7eSjoerg 
12906f32e7eSjoerg     // Visit all sub-expressions.
13006f32e7eSjoerg     for (const Use &U : C->operands()) {
13106f32e7eSjoerg       const auto *OpC = dyn_cast<Constant>(U);
13206f32e7eSjoerg       if (!OpC)
13306f32e7eSjoerg         continue;
13406f32e7eSjoerg 
13506f32e7eSjoerg       if (!ConstantExprVisited.insert(OpC).second)
13606f32e7eSjoerg         continue;
13706f32e7eSjoerg 
13806f32e7eSjoerg       Stack.push_back(OpC);
13906f32e7eSjoerg     }
14006f32e7eSjoerg   }
14106f32e7eSjoerg 
14206f32e7eSjoerg   return false;
14306f32e7eSjoerg }
14406f32e7eSjoerg 
14506f32e7eSjoerg // We do not need to note the x workitem or workgroup id because they are always
14606f32e7eSjoerg // initialized.
14706f32e7eSjoerg //
14806f32e7eSjoerg // TODO: We should not add the attributes if the known compile time workgroup
14906f32e7eSjoerg // size is 1 for y/z.
intrinsicToAttrName(Intrinsic::ID ID,bool & NonKernelOnly,bool & IsQueuePtr)15006f32e7eSjoerg static StringRef intrinsicToAttrName(Intrinsic::ID ID,
15106f32e7eSjoerg                                      bool &NonKernelOnly,
15206f32e7eSjoerg                                      bool &IsQueuePtr) {
15306f32e7eSjoerg   switch (ID) {
15406f32e7eSjoerg   case Intrinsic::amdgcn_workitem_id_x:
15506f32e7eSjoerg     NonKernelOnly = true;
15606f32e7eSjoerg     return "amdgpu-work-item-id-x";
15706f32e7eSjoerg   case Intrinsic::amdgcn_workgroup_id_x:
15806f32e7eSjoerg     NonKernelOnly = true;
15906f32e7eSjoerg     return "amdgpu-work-group-id-x";
16006f32e7eSjoerg   case Intrinsic::amdgcn_workitem_id_y:
16106f32e7eSjoerg   case Intrinsic::r600_read_tidig_y:
16206f32e7eSjoerg     return "amdgpu-work-item-id-y";
16306f32e7eSjoerg   case Intrinsic::amdgcn_workitem_id_z:
16406f32e7eSjoerg   case Intrinsic::r600_read_tidig_z:
16506f32e7eSjoerg     return "amdgpu-work-item-id-z";
16606f32e7eSjoerg   case Intrinsic::amdgcn_workgroup_id_y:
16706f32e7eSjoerg   case Intrinsic::r600_read_tgid_y:
16806f32e7eSjoerg     return "amdgpu-work-group-id-y";
16906f32e7eSjoerg   case Intrinsic::amdgcn_workgroup_id_z:
17006f32e7eSjoerg   case Intrinsic::r600_read_tgid_z:
17106f32e7eSjoerg     return "amdgpu-work-group-id-z";
17206f32e7eSjoerg   case Intrinsic::amdgcn_dispatch_ptr:
17306f32e7eSjoerg     return "amdgpu-dispatch-ptr";
17406f32e7eSjoerg   case Intrinsic::amdgcn_dispatch_id:
17506f32e7eSjoerg     return "amdgpu-dispatch-id";
17606f32e7eSjoerg   case Intrinsic::amdgcn_kernarg_segment_ptr:
17706f32e7eSjoerg     return "amdgpu-kernarg-segment-ptr";
17806f32e7eSjoerg   case Intrinsic::amdgcn_implicitarg_ptr:
17906f32e7eSjoerg     return "amdgpu-implicitarg-ptr";
18006f32e7eSjoerg   case Intrinsic::amdgcn_queue_ptr:
18106f32e7eSjoerg   case Intrinsic::amdgcn_is_shared:
18206f32e7eSjoerg   case Intrinsic::amdgcn_is_private:
18306f32e7eSjoerg     // TODO: Does not require queue ptr on gfx9+
18406f32e7eSjoerg   case Intrinsic::trap:
18506f32e7eSjoerg   case Intrinsic::debugtrap:
18606f32e7eSjoerg     IsQueuePtr = true;
18706f32e7eSjoerg     return "amdgpu-queue-ptr";
18806f32e7eSjoerg   default:
18906f32e7eSjoerg     return "";
19006f32e7eSjoerg   }
19106f32e7eSjoerg }
19206f32e7eSjoerg 
handleAttr(Function & Parent,const Function & Callee,StringRef Name)19306f32e7eSjoerg static bool handleAttr(Function &Parent, const Function &Callee,
19406f32e7eSjoerg                        StringRef Name) {
19506f32e7eSjoerg   if (Callee.hasFnAttribute(Name)) {
19606f32e7eSjoerg     Parent.addFnAttr(Name);
19706f32e7eSjoerg     return true;
19806f32e7eSjoerg   }
19906f32e7eSjoerg   return false;
20006f32e7eSjoerg }
20106f32e7eSjoerg 
copyFeaturesToFunction(Function & Parent,const Function & Callee,bool & NeedQueuePtr)20206f32e7eSjoerg static void copyFeaturesToFunction(Function &Parent, const Function &Callee,
20306f32e7eSjoerg                                    bool &NeedQueuePtr) {
20406f32e7eSjoerg   if (handleAttr(Parent, Callee, "amdgpu-queue-ptr"))
20506f32e7eSjoerg     NeedQueuePtr = true;
20606f32e7eSjoerg 
207*da58b97aSjoerg   for (StringRef AttrName : ImplicitAttrNames)
20806f32e7eSjoerg     handleAttr(Parent, Callee, AttrName);
20906f32e7eSjoerg }
21006f32e7eSjoerg 
processUniformWorkGroupAttribute()21106f32e7eSjoerg bool AMDGPUAnnotateKernelFeatures::processUniformWorkGroupAttribute() {
21206f32e7eSjoerg   bool Changed = false;
21306f32e7eSjoerg 
21406f32e7eSjoerg   for (auto *Node : reverse(NodeList)) {
21506f32e7eSjoerg     Function *Caller = Node->getFunction();
21606f32e7eSjoerg 
21706f32e7eSjoerg     for (auto I : *Node) {
21806f32e7eSjoerg       Function *Callee = std::get<1>(I)->getFunction();
21906f32e7eSjoerg       if (Callee)
22006f32e7eSjoerg         Changed = propagateUniformWorkGroupAttribute(*Caller, *Callee);
22106f32e7eSjoerg     }
22206f32e7eSjoerg   }
22306f32e7eSjoerg 
22406f32e7eSjoerg   return Changed;
22506f32e7eSjoerg }
22606f32e7eSjoerg 
propagateUniformWorkGroupAttribute(Function & Caller,Function & Callee)22706f32e7eSjoerg bool AMDGPUAnnotateKernelFeatures::propagateUniformWorkGroupAttribute(
22806f32e7eSjoerg        Function &Caller, Function &Callee) {
22906f32e7eSjoerg 
23006f32e7eSjoerg   // Check for externally defined function
23106f32e7eSjoerg   if (!Callee.hasExactDefinition()) {
23206f32e7eSjoerg     Callee.addFnAttr("uniform-work-group-size", "false");
23306f32e7eSjoerg     if (!Caller.hasFnAttribute("uniform-work-group-size"))
23406f32e7eSjoerg       Caller.addFnAttr("uniform-work-group-size", "false");
23506f32e7eSjoerg 
23606f32e7eSjoerg     return true;
23706f32e7eSjoerg   }
23806f32e7eSjoerg   // Check if the Caller has the attribute
23906f32e7eSjoerg   if (Caller.hasFnAttribute("uniform-work-group-size")) {
24006f32e7eSjoerg     // Check if the value of the attribute is true
24106f32e7eSjoerg     if (Caller.getFnAttribute("uniform-work-group-size")
24206f32e7eSjoerg         .getValueAsString().equals("true")) {
24306f32e7eSjoerg       // Propagate the attribute to the Callee, if it does not have it
24406f32e7eSjoerg       if (!Callee.hasFnAttribute("uniform-work-group-size")) {
24506f32e7eSjoerg         Callee.addFnAttr("uniform-work-group-size", "true");
24606f32e7eSjoerg         return true;
24706f32e7eSjoerg       }
24806f32e7eSjoerg     } else {
24906f32e7eSjoerg       Callee.addFnAttr("uniform-work-group-size", "false");
25006f32e7eSjoerg       return true;
25106f32e7eSjoerg     }
25206f32e7eSjoerg   } else {
25306f32e7eSjoerg     // If the attribute is absent, set it as false
25406f32e7eSjoerg     Caller.addFnAttr("uniform-work-group-size", "false");
25506f32e7eSjoerg     Callee.addFnAttr("uniform-work-group-size", "false");
25606f32e7eSjoerg     return true;
25706f32e7eSjoerg   }
25806f32e7eSjoerg   return false;
25906f32e7eSjoerg }
26006f32e7eSjoerg 
addFeatureAttributes(Function & F)26106f32e7eSjoerg bool AMDGPUAnnotateKernelFeatures::addFeatureAttributes(Function &F) {
26206f32e7eSjoerg   const GCNSubtarget &ST = TM->getSubtarget<GCNSubtarget>(F);
26306f32e7eSjoerg   bool HasApertureRegs = ST.hasApertureRegs();
26406f32e7eSjoerg   SmallPtrSet<const Constant *, 8> ConstantExprVisited;
26506f32e7eSjoerg 
266*da58b97aSjoerg   bool HaveStackObjects = false;
26706f32e7eSjoerg   bool Changed = false;
26806f32e7eSjoerg   bool NeedQueuePtr = false;
26906f32e7eSjoerg   bool HaveCall = false;
270*da58b97aSjoerg   bool HasIndirectCall = false;
27106f32e7eSjoerg   bool IsFunc = !AMDGPU::isEntryFunctionCC(F.getCallingConv());
272*da58b97aSjoerg   CallingConv::ID CC = F.getCallingConv();
273*da58b97aSjoerg   bool CallingConvSupportsAllImplicits = (CC != CallingConv::AMDGPU_Gfx);
274*da58b97aSjoerg 
275*da58b97aSjoerg   // If this function hasAddressTaken() = true
276*da58b97aSjoerg   // then add all attributes corresponding to the implicit args.
277*da58b97aSjoerg   if (CallingConvSupportsAllImplicits &&
278*da58b97aSjoerg       F.hasAddressTaken(nullptr, true, true, true)) {
279*da58b97aSjoerg     for (StringRef AttrName : ImplicitAttrNames) {
280*da58b97aSjoerg       F.addFnAttr(AttrName);
281*da58b97aSjoerg     }
282*da58b97aSjoerg     Changed = true;
283*da58b97aSjoerg   }
28406f32e7eSjoerg 
28506f32e7eSjoerg   for (BasicBlock &BB : F) {
28606f32e7eSjoerg     for (Instruction &I : BB) {
287*da58b97aSjoerg       if (isa<AllocaInst>(I)) {
288*da58b97aSjoerg         HaveStackObjects = true;
289*da58b97aSjoerg         continue;
290*da58b97aSjoerg       }
29106f32e7eSjoerg 
292*da58b97aSjoerg       if (auto *CB = dyn_cast<CallBase>(&I)) {
293*da58b97aSjoerg         const Function *Callee =
294*da58b97aSjoerg             dyn_cast<Function>(CB->getCalledOperand()->stripPointerCasts());
295*da58b97aSjoerg 
296*da58b97aSjoerg         // Note the occurence of indirect call.
29706f32e7eSjoerg         if (!Callee) {
298*da58b97aSjoerg           if (!CB->isInlineAsm()) {
299*da58b97aSjoerg             HasIndirectCall = true;
30006f32e7eSjoerg             HaveCall = true;
301*da58b97aSjoerg           }
30206f32e7eSjoerg           continue;
30306f32e7eSjoerg         }
30406f32e7eSjoerg 
30506f32e7eSjoerg         Intrinsic::ID IID = Callee->getIntrinsicID();
30606f32e7eSjoerg         if (IID == Intrinsic::not_intrinsic) {
30706f32e7eSjoerg           HaveCall = true;
30806f32e7eSjoerg           copyFeaturesToFunction(F, *Callee, NeedQueuePtr);
30906f32e7eSjoerg           Changed = true;
31006f32e7eSjoerg         } else {
31106f32e7eSjoerg           bool NonKernelOnly = false;
312*da58b97aSjoerg 
313*da58b97aSjoerg           if (!IsFunc && IID == Intrinsic::amdgcn_kernarg_segment_ptr) {
314*da58b97aSjoerg             F.addFnAttr("amdgpu-kernarg-segment-ptr");
315*da58b97aSjoerg           } else {
316*da58b97aSjoerg             StringRef AttrName = intrinsicToAttrName(IID, NonKernelOnly,
317*da58b97aSjoerg                                                      NeedQueuePtr);
31806f32e7eSjoerg             if (!AttrName.empty() && (IsFunc || !NonKernelOnly)) {
31906f32e7eSjoerg               F.addFnAttr(AttrName);
32006f32e7eSjoerg               Changed = true;
32106f32e7eSjoerg             }
32206f32e7eSjoerg           }
32306f32e7eSjoerg         }
324*da58b97aSjoerg       }
32506f32e7eSjoerg 
326*da58b97aSjoerg       if (NeedQueuePtr || (!IsFunc && HasApertureRegs))
32706f32e7eSjoerg         continue;
32806f32e7eSjoerg 
32906f32e7eSjoerg       if (const AddrSpaceCastInst *ASC = dyn_cast<AddrSpaceCastInst>(&I)) {
330*da58b97aSjoerg         if (!HasApertureRegs && castRequiresQueuePtr(ASC)) {
33106f32e7eSjoerg           NeedQueuePtr = true;
33206f32e7eSjoerg           continue;
33306f32e7eSjoerg         }
33406f32e7eSjoerg       }
33506f32e7eSjoerg 
33606f32e7eSjoerg       for (const Use &U : I.operands()) {
33706f32e7eSjoerg         const auto *OpC = dyn_cast<Constant>(U);
33806f32e7eSjoerg         if (!OpC)
33906f32e7eSjoerg           continue;
34006f32e7eSjoerg 
341*da58b97aSjoerg         if (visitConstantExprsRecursively(OpC, ConstantExprVisited, IsFunc,
342*da58b97aSjoerg                                           HasApertureRegs)) {
34306f32e7eSjoerg           NeedQueuePtr = true;
34406f32e7eSjoerg           break;
34506f32e7eSjoerg         }
34606f32e7eSjoerg       }
34706f32e7eSjoerg     }
34806f32e7eSjoerg   }
34906f32e7eSjoerg 
35006f32e7eSjoerg   if (NeedQueuePtr) {
35106f32e7eSjoerg     F.addFnAttr("amdgpu-queue-ptr");
35206f32e7eSjoerg     Changed = true;
35306f32e7eSjoerg   }
35406f32e7eSjoerg 
35506f32e7eSjoerg   // TODO: We could refine this to captured pointers that could possibly be
35606f32e7eSjoerg   // accessed by flat instructions. For now this is mostly a poor way of
35706f32e7eSjoerg   // estimating whether there are calls before argument lowering.
358*da58b97aSjoerg   if (!IsFunc && HaveCall) {
359*da58b97aSjoerg     F.addFnAttr("amdgpu-calls");
360*da58b97aSjoerg     Changed = true;
361*da58b97aSjoerg   }
362*da58b97aSjoerg 
363*da58b97aSjoerg   if (HaveStackObjects) {
364*da58b97aSjoerg     F.addFnAttr("amdgpu-stack-objects");
365*da58b97aSjoerg     Changed = true;
366*da58b97aSjoerg   }
367*da58b97aSjoerg 
368*da58b97aSjoerg   // This pass cannot copy attributes from callees to callers
369*da58b97aSjoerg   // if there is an indirect call and in thus such cases,
370*da58b97aSjoerg   // hasAddressTaken() would be false for kernels and functions
371*da58b97aSjoerg   // making an indirect call (if they are themselves not indirectly called).
372*da58b97aSjoerg   // We must tag all such kernels/functions with all implicits attributes
373*da58b97aSjoerg   // for correctness.
374*da58b97aSjoerg   // e.g.
375*da58b97aSjoerg   // 1. Kernel K1 makes an indirect call to function F1.
376*da58b97aSjoerg   //    Without detecting an indirect call in K1, this pass will not
377*da58b97aSjoerg   //    add all implicit args to K1 (which is incorrect).
378*da58b97aSjoerg   // 2. Kernel K1 makes direct call to F1 which makes indirect call to function
379*da58b97aSjoerg   // F2.
380*da58b97aSjoerg   //    Without detecting an indirect call in F1 (whose hasAddressTaken() is
381*da58b97aSjoerg   //    false), the pass will not add all implicit args to F1 (which is
382*da58b97aSjoerg   //    essential for correctness).
383*da58b97aSjoerg   if (CallingConvSupportsAllImplicits && HasIndirectCall) {
384*da58b97aSjoerg     for (StringRef AttrName : ImplicitAttrNames) {
385*da58b97aSjoerg       F.addFnAttr(AttrName);
386*da58b97aSjoerg     }
38706f32e7eSjoerg     Changed = true;
38806f32e7eSjoerg   }
38906f32e7eSjoerg 
39006f32e7eSjoerg   return Changed;
39106f32e7eSjoerg }
39206f32e7eSjoerg 
runOnSCC(CallGraphSCC & SCC)39306f32e7eSjoerg bool AMDGPUAnnotateKernelFeatures::runOnSCC(CallGraphSCC &SCC) {
39406f32e7eSjoerg   bool Changed = false;
39506f32e7eSjoerg 
39606f32e7eSjoerg   for (CallGraphNode *I : SCC) {
39706f32e7eSjoerg     // Build a list of CallGraphNodes from most number of uses to least
39806f32e7eSjoerg     if (I->getNumReferences())
39906f32e7eSjoerg       NodeList.push_back(I);
40006f32e7eSjoerg     else {
40106f32e7eSjoerg       processUniformWorkGroupAttribute();
40206f32e7eSjoerg       NodeList.clear();
40306f32e7eSjoerg     }
40406f32e7eSjoerg 
40506f32e7eSjoerg     Function *F = I->getFunction();
406*da58b97aSjoerg     // Ignore functions with graphics calling conventions, these are currently
407*da58b97aSjoerg     // not allowed to have kernel arguments.
408*da58b97aSjoerg     if (!F || F->isDeclaration() || AMDGPU::isGraphics(F->getCallingConv()))
40906f32e7eSjoerg       continue;
410*da58b97aSjoerg     // Add feature attributes
41106f32e7eSjoerg     Changed |= addFeatureAttributes(*F);
41206f32e7eSjoerg   }
41306f32e7eSjoerg 
41406f32e7eSjoerg   return Changed;
41506f32e7eSjoerg }
41606f32e7eSjoerg 
doInitialization(CallGraph & CG)41706f32e7eSjoerg bool AMDGPUAnnotateKernelFeatures::doInitialization(CallGraph &CG) {
41806f32e7eSjoerg   auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
41906f32e7eSjoerg   if (!TPC)
42006f32e7eSjoerg     report_fatal_error("TargetMachine is required");
42106f32e7eSjoerg 
42206f32e7eSjoerg   TM = &TPC->getTM<TargetMachine>();
42306f32e7eSjoerg   return false;
42406f32e7eSjoerg }
42506f32e7eSjoerg 
createAMDGPUAnnotateKernelFeaturesPass()42606f32e7eSjoerg Pass *llvm::createAMDGPUAnnotateKernelFeaturesPass() {
42706f32e7eSjoerg   return new AMDGPUAnnotateKernelFeatures();
42806f32e7eSjoerg }
429