| 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 | |