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:
AMDGPURemoveIncompatibleFunctions(const TargetMachine * TM=nullptr)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
run(Module & M)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
AMDGPURemoveIncompatibleFunctionsLegacy(const TargetMachine * TM)70 AMDGPURemoveIncompatibleFunctionsLegacy(const TargetMachine *TM)
71 : ModulePass(ID), TM(TM) {}
72
runOnModule(Module & M)73 bool runOnModule(Module &M) override {
74 AMDGPURemoveIncompatibleFunctions Pass(TM);
75 return Pass.run(M);
76 }
77
getPassName() const78 StringRef getPassName() const override {
79 return "AMDGPU Remove Incompatible Functions";
80 }
81
getAnalysisUsage(AnalysisUsage & AU) const82 void getAnalysisUsage(AnalysisUsage &AU) const override {}
83
84 private:
85 const TargetMachine *TM = nullptr;
86 };
87
getFeatureName(unsigned Feature)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
getGPUInfo(const GCNSubtarget & ST,StringRef GPUName)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
expandImpliedFeatures(const FeatureBitset & Features)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
reportFunctionRemoved(Function & F,unsigned Feature)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
run(Module & M,ModuleAnalysisManager & MAM)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
checkFunction(Function & F)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 *
createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine * TM)214 llvm::createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine *TM) {
215 return new AMDGPURemoveIncompatibleFunctionsLegacy(TM);
216 }
217