1 //===-- AMDGPULowerIntrinsics.cpp -----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "AMDGPU.h"
10 #include "AMDGPUSubtarget.h"
11 #include "llvm/Analysis/TargetTransformInfo.h"
12 #include "llvm/CodeGen/TargetPassConfig.h"
13 #include "llvm/IR/Constants.h"
14 #include "llvm/IR/Instructions.h"
15 #include "llvm/IR/IntrinsicInst.h"
16 #include "llvm/IR/IntrinsicsAMDGPU.h"
17 #include "llvm/IR/IntrinsicsR600.h"
18 #include "llvm/IR/Module.h"
19 #include "llvm/Support/CommandLine.h"
20 #include "llvm/Target/TargetMachine.h"
21 #include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
22 
23 #define DEBUG_TYPE "amdgpu-lower-intrinsics"
24 
25 using namespace llvm;
26 
27 namespace {
28 
29 static int MaxStaticSize;
30 
31 static cl::opt<int, true> MemIntrinsicExpandSizeThresholdOpt(
32   "amdgpu-mem-intrinsic-expand-size",
33   cl::desc("Set minimum mem intrinsic size to expand in IR"),
34   cl::location(MaxStaticSize),
35   cl::init(1024),
36   cl::Hidden);
37 
38 
39 class AMDGPULowerIntrinsics : public ModulePass {
40 private:
41   bool makeLIDRangeMetadata(Function &F) const;
42 
43 public:
44   static char ID;
45 
46   AMDGPULowerIntrinsics() : ModulePass(ID) {}
47 
48   bool runOnModule(Module &M) override;
49   bool expandMemIntrinsicUses(Function &F);
50   StringRef getPassName() const override {
51     return "AMDGPU Lower Intrinsics";
52   }
53 
54   void getAnalysisUsage(AnalysisUsage &AU) const override {
55     AU.addRequired<TargetTransformInfoWrapperPass>();
56   }
57 };
58 
59 }
60 
61 char AMDGPULowerIntrinsics::ID = 0;
62 
63 char &llvm::AMDGPULowerIntrinsicsID = AMDGPULowerIntrinsics::ID;
64 
65 INITIALIZE_PASS(AMDGPULowerIntrinsics, DEBUG_TYPE, "Lower intrinsics", false,
66                 false)
67 
68 // TODO: Should refine based on estimated number of accesses (e.g. does it
69 // require splitting based on alignment)
70 static bool shouldExpandOperationWithSize(Value *Size) {
71   ConstantInt *CI = dyn_cast<ConstantInt>(Size);
72   return !CI || (CI->getSExtValue() > MaxStaticSize);
73 }
74 
75 bool AMDGPULowerIntrinsics::expandMemIntrinsicUses(Function &F) {
76   Intrinsic::ID ID = F.getIntrinsicID();
77   bool Changed = false;
78 
79   for (User *U : llvm::make_early_inc_range(F.users())) {
80     Instruction *Inst = cast<Instruction>(U);
81 
82     switch (ID) {
83     case Intrinsic::memcpy: {
84       auto *Memcpy = cast<MemCpyInst>(Inst);
85       if (shouldExpandOperationWithSize(Memcpy->getLength())) {
86         Function *ParentFunc = Memcpy->getParent()->getParent();
87         const TargetTransformInfo &TTI =
88             getAnalysis<TargetTransformInfoWrapperPass>().getTTI(*ParentFunc);
89         expandMemCpyAsLoop(Memcpy, TTI);
90         Changed = true;
91         Memcpy->eraseFromParent();
92       }
93 
94       break;
95     }
96     case Intrinsic::memmove: {
97       auto *Memmove = cast<MemMoveInst>(Inst);
98       if (shouldExpandOperationWithSize(Memmove->getLength())) {
99         expandMemMoveAsLoop(Memmove);
100         Changed = true;
101         Memmove->eraseFromParent();
102       }
103 
104       break;
105     }
106     case Intrinsic::memset: {
107       auto *Memset = cast<MemSetInst>(Inst);
108       if (shouldExpandOperationWithSize(Memset->getLength())) {
109         expandMemSetAsLoop(Memset);
110         Changed = true;
111         Memset->eraseFromParent();
112       }
113 
114       break;
115     }
116     default:
117       break;
118     }
119   }
120 
121   return Changed;
122 }
123 
124 bool AMDGPULowerIntrinsics::makeLIDRangeMetadata(Function &F) const {
125   auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
126   if (!TPC)
127     return false;
128 
129   const TargetMachine &TM = TPC->getTM<TargetMachine>();
130   bool Changed = false;
131 
132   for (auto *U : F.users()) {
133     auto *CI = dyn_cast<CallInst>(U);
134     if (!CI)
135       continue;
136 
137     Function *Caller = CI->getParent()->getParent();
138     const AMDGPUSubtarget &ST = AMDGPUSubtarget::get(TM, *Caller);
139     Changed |= ST.makeLIDRangeMetadata(CI);
140   }
141   return Changed;
142 }
143 
144 bool AMDGPULowerIntrinsics::runOnModule(Module &M) {
145   bool Changed = false;
146 
147   for (Function &F : M) {
148     if (!F.isDeclaration())
149       continue;
150 
151     switch (F.getIntrinsicID()) {
152     case Intrinsic::memcpy:
153     case Intrinsic::memmove:
154     case Intrinsic::memset:
155       if (expandMemIntrinsicUses(F))
156         Changed = true;
157       break;
158 
159     case Intrinsic::amdgcn_workitem_id_x:
160     case Intrinsic::r600_read_tidig_x:
161     case Intrinsic::amdgcn_workitem_id_y:
162     case Intrinsic::r600_read_tidig_y:
163     case Intrinsic::amdgcn_workitem_id_z:
164     case Intrinsic::r600_read_tidig_z:
165     case Intrinsic::r600_read_local_size_x:
166     case Intrinsic::r600_read_local_size_y:
167     case Intrinsic::r600_read_local_size_z:
168       Changed |= makeLIDRangeMetadata(F);
169       break;
170 
171     default:
172       break;
173     }
174   }
175 
176   return Changed;
177 }
178 
179 ModulePass *llvm::createAMDGPULowerIntrinsicsPass() {
180   return new AMDGPULowerIntrinsics();
181 }
182