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(Elt: &F); |
53 | } |
54 | |
55 | for (Function *F : FnsToDelete) { |
56 | F->replaceAllUsesWith(V: ConstantPointerNull::get(T: 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(I: FE.Value) && FE.Implies.any()) |
128 | Result |= expandImpliedFeatures(Features: FE.Implies.getAsBitset()); |
129 | } |
130 | return Result; |
131 | } |
132 | |
133 | void reportFunctionRemoved(Function &F, unsigned Feature) { |
134 | OptimizationRemarkEmitter ORE(&F); |
135 | ORE.emit(RemarkBuilder: [&]() { |
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(Prefix: "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: *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(Features: 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(I: 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(Feature: AMDGPU::FeatureWavefrontSize32)) { |
202 | reportFunctionRemoved(F, Feature: 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 | |