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(&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 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(FE.Value) && FE.Implies.any()) 115 Result |= expandImpliedFeatures(FE.Implies.getAsBitset()); 116 } 117 return Result; 118 } 119 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 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("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 * 192 llvm::createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine *TM) { 193 return new AMDGPURemoveIncompatibleFunctions(TM); 194 } 195