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 | } // namespace llvm |
32 | |
33 | namespace { |
34 | |
35 | using Generation = AMDGPUSubtarget::Generation; |
36 | |
37 | class AMDGPURemoveIncompatibleFunctions : public ModulePass { |
38 | public: |
39 | static char ID; |
40 | |
41 | AMDGPURemoveIncompatibleFunctions(const TargetMachine *TM = nullptr) |
42 | : ModulePass(ID), TM(TM) { |
43 | assert(TM && "No TargetMachine!" ); |
44 | } |
45 | |
46 | StringRef getPassName() const override { |
47 | return "AMDGPU Remove Incompatible Functions" ; |
48 | } |
49 | |
50 | 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 | |
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(Elt: &F); |
62 | } |
63 | |
64 | for (Function *F : FnsToDelete) { |
65 | F->replaceAllUsesWith(V: ConstantPointerNull::get(T: F->getType())); |
66 | F->eraseFromParent(); |
67 | } |
68 | return !FnsToDelete.empty(); |
69 | } |
70 | |
71 | private: |
72 | const TargetMachine *TM = nullptr; |
73 | }; |
74 | |
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 | |
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 | |
111 | FeatureBitset expandImpliedFeatures(const FeatureBitset &Features) { |
112 | FeatureBitset Result = Features; |
113 | for (const SubtargetFeatureKV &FE : AMDGPUFeatureKV) { |
114 | if (Features.test(I: FE.Value) && FE.Implies.any()) |
115 | Result |= expandImpliedFeatures(Features: FE.Implies.getAsBitset()); |
116 | } |
117 | return Result; |
118 | } |
119 | |
120 | void reportFunctionRemoved(Function &F, unsigned Feature) { |
121 | OptimizationRemarkEmitter ORE(&F); |
122 | ORE.emit(RemarkBuilder: [&]() { |
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 | |
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 or generic-hsa. Generic is used for testing |
143 | // only and we don't want this pass to interfere with it. |
144 | StringRef GPUName = ST->getCPU(); |
145 | if (GPUName.empty() || GPUName.starts_with(Prefix: "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: *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(Features: 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(I: 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(Feature: AMDGPU::FeatureWavefrontSize32)) { |
180 | reportFunctionRemoved(F, Feature: 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 * |
192 | llvm::createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine *TM) { |
193 | return new AMDGPURemoveIncompatibleFunctions(TM); |
194 | } |
195 | |