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