xref: /llvm-project/llvm/lib/Target/AMDGPU/AMDGPURemoveIncompatibleFunctions.cpp (revision 7bf1cb702bdbec4b054a65cb58244596e8cd4138)
1 //===-- AMDGPURemoveIncompatibleFunctions.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 /// \file
10 /// This pass replaces all uses of functions that use GPU features
11 /// incompatible with the current GPU with null then deletes the function.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "AMDGPURemoveIncompatibleFunctions.h"
16 #include "AMDGPU.h"
17 #include "GCNSubtarget.h"
18 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
19 #include "llvm/IR/Function.h"
20 #include "llvm/IR/Module.h"
21 #include "llvm/Pass.h"
22 #include "llvm/Target/TargetMachine.h"
23 
24 #define DEBUG_TYPE "amdgpu-remove-incompatible-functions"
25 
26 using namespace llvm;
27 
28 namespace llvm {
29 extern const SubtargetFeatureKV
30     AMDGPUFeatureKV[AMDGPU::NumSubtargetFeatures - 1];
31 } // namespace llvm
32 
33 namespace {
34 
35 using Generation = AMDGPUSubtarget::Generation;
36 
37 class AMDGPURemoveIncompatibleFunctions {
38 public:
39   AMDGPURemoveIncompatibleFunctions(const TargetMachine *TM = nullptr)
40       : TM(TM) {
41     assert(TM && "No TargetMachine!");
42   }
43   /// Checks a single function, returns true if the function must be deleted.
44   bool checkFunction(Function &F);
45 
46   bool run(Module &M) {
47     assert(TM->getTargetTriple().isAMDGCN());
48 
49     SmallVector<Function *, 4> FnsToDelete;
50     for (Function &F : M) {
51       if (checkFunction(F))
52         FnsToDelete.push_back(&F);
53     }
54 
55     for (Function *F : FnsToDelete) {
56       F->replaceAllUsesWith(ConstantPointerNull::get(F->getType()));
57       F->eraseFromParent();
58     }
59     return !FnsToDelete.empty();
60   }
61 
62 private:
63   const TargetMachine *TM = nullptr;
64 };
65 
66 class AMDGPURemoveIncompatibleFunctionsLegacy : public ModulePass {
67 public:
68   static char ID;
69 
70   AMDGPURemoveIncompatibleFunctionsLegacy(const TargetMachine *TM)
71       : ModulePass(ID), TM(TM) {}
72 
73   bool runOnModule(Module &M) override {
74     AMDGPURemoveIncompatibleFunctions Pass(TM);
75     return Pass.run(M);
76   }
77 
78   StringRef getPassName() const override {
79     return "AMDGPU Remove Incompatible Functions";
80   }
81 
82   void getAnalysisUsage(AnalysisUsage &AU) const override {}
83 
84 private:
85   const TargetMachine *TM = nullptr;
86 };
87 
88 StringRef getFeatureName(unsigned Feature) {
89   for (const SubtargetFeatureKV &KV : AMDGPUFeatureKV)
90     if (Feature == KV.Value)
91       return KV.Key;
92 
93   llvm_unreachable("Unknown Target feature");
94 }
95 
96 const SubtargetSubTypeKV *getGPUInfo(const GCNSubtarget &ST,
97                                      StringRef GPUName) {
98   for (const SubtargetSubTypeKV &KV : ST.getAllProcessorDescriptions())
99     if (StringRef(KV.Key) == GPUName)
100       return &KV;
101 
102   return nullptr;
103 }
104 
105 constexpr unsigned FeaturesToCheck[] = {AMDGPU::FeatureGFX11Insts,
106                                         AMDGPU::FeatureGFX10Insts,
107                                         AMDGPU::FeatureGFX9Insts,
108                                         AMDGPU::FeatureGFX8Insts,
109                                         AMDGPU::FeatureDPP,
110                                         AMDGPU::Feature16BitInsts,
111                                         AMDGPU::FeatureDot1Insts,
112                                         AMDGPU::FeatureDot2Insts,
113                                         AMDGPU::FeatureDot3Insts,
114                                         AMDGPU::FeatureDot4Insts,
115                                         AMDGPU::FeatureDot5Insts,
116                                         AMDGPU::FeatureDot6Insts,
117                                         AMDGPU::FeatureDot7Insts,
118                                         AMDGPU::FeatureDot8Insts,
119                                         AMDGPU::FeatureExtendedImageInsts,
120                                         AMDGPU::FeatureSMemRealTime,
121                                         AMDGPU::FeatureSMemTimeInst,
122                                         AMDGPU::FeatureGWS};
123 
124 FeatureBitset expandImpliedFeatures(const FeatureBitset &Features) {
125   FeatureBitset Result = Features;
126   for (const SubtargetFeatureKV &FE : AMDGPUFeatureKV) {
127     if (Features.test(FE.Value) && FE.Implies.any())
128       Result |= expandImpliedFeatures(FE.Implies.getAsBitset());
129   }
130   return Result;
131 }
132 
133 void reportFunctionRemoved(Function &F, unsigned Feature) {
134   OptimizationRemarkEmitter ORE(&F);
135   ORE.emit([&]() {
136     // Note: we print the function name as part of the diagnostic because if
137     // debug info is not present, users get "<unknown>:0:0" as the debug
138     // loc. If we didn't print the function name there would be no way to
139     // tell which function got removed.
140     return OptimizationRemark(DEBUG_TYPE, "AMDGPUIncompatibleFnRemoved", &F)
141            << "removing function '" << F.getName() << "': +"
142            << getFeatureName(Feature)
143            << " is not supported on the current target";
144   });
145 }
146 } // end anonymous namespace
147 
148 PreservedAnalyses
149 AMDGPURemoveIncompatibleFunctionsPass::run(Module &M,
150                                            ModuleAnalysisManager &MAM) {
151   AMDGPURemoveIncompatibleFunctions Impl(TM);
152   if (Impl.run(M))
153     return PreservedAnalyses::none();
154   return PreservedAnalyses::all();
155 }
156 
157 bool AMDGPURemoveIncompatibleFunctions::checkFunction(Function &F) {
158   if (F.isDeclaration())
159     return false;
160 
161   const GCNSubtarget *ST =
162       static_cast<const GCNSubtarget *>(TM->getSubtargetImpl(F));
163 
164   // Check the GPU isn't generic or generic-hsa. Generic is used for testing
165   // only and we don't want this pass to interfere with it.
166   StringRef GPUName = ST->getCPU();
167   if (GPUName.empty() || GPUName.starts_with("generic"))
168     return false;
169 
170   // Try to fetch the GPU's info. If we can't, it's likely an unknown processor
171   // so just bail out.
172   const SubtargetSubTypeKV *GPUInfo = getGPUInfo(*ST, GPUName);
173   if (!GPUInfo)
174     return false;
175 
176   // Get all the features implied by the current GPU, and recursively expand
177   // the features that imply other features.
178   //
179   // e.g. GFX90A implies FeatureGFX9, and FeatureGFX9 implies a whole set of
180   // other features.
181   const FeatureBitset GPUFeatureBits =
182       expandImpliedFeatures(GPUInfo->Implies.getAsBitset());
183 
184   // Now that the have a FeatureBitset containing all possible features for
185   // the chosen GPU, check our list of "suspicious" features.
186 
187   // Check that the user didn't enable any features that aren't part of that
188   // GPU's feature set. We only check a predetermined set of features.
189   for (unsigned Feature : FeaturesToCheck) {
190     if (ST->hasFeature(Feature) && !GPUFeatureBits.test(Feature)) {
191       reportFunctionRemoved(F, Feature);
192       return true;
193     }
194   }
195 
196   // Delete FeatureWavefrontSize32 functions for
197   // gfx9 and below targets that don't support the mode.
198   // gfx10+ is implied to support both wave32 and 64 features.
199   // They are not in the feature set. So, we need a separate check
200   if (ST->getGeneration() < AMDGPUSubtarget::GFX10 &&
201       ST->hasFeature(AMDGPU::FeatureWavefrontSize32)) {
202     reportFunctionRemoved(F, AMDGPU::FeatureWavefrontSize32);
203     return true;
204   }
205   return false;
206 }
207 
208 INITIALIZE_PASS(AMDGPURemoveIncompatibleFunctionsLegacy, DEBUG_TYPE,
209                 "AMDGPU Remove Incompatible Functions", false, false)
210 
211 char AMDGPURemoveIncompatibleFunctionsLegacy::ID = 0;
212 
213 ModulePass *
214 llvm::createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine *TM) {
215   return new AMDGPURemoveIncompatibleFunctionsLegacy(TM);
216 }
217