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 } 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 110 FeatureBitset expandImpliedFeatures(const FeatureBitset &Features) { 111 FeatureBitset Result = Features; 112 for (const SubtargetFeatureKV &FE : AMDGPUFeatureKV) { 113 if (Features.test(FE.Value) && FE.Implies.any()) 114 Result |= expandImpliedFeatures(FE.Implies.getAsBitset()); 115 } 116 return Result; 117 } 118 119 void reportFunctionRemoved(Function &F, unsigned Feature) { 120 OptimizationRemarkEmitter ORE(&F); 121 ORE.emit([&]() { 122 // Note: we print the function name as part of the diagnostic because if 123 // debug info is not present, users get "<unknown>:0:0" as the debug 124 // loc. If we didn't print the function name there would be no way to 125 // tell which function got removed. 126 return OptimizationRemark(DEBUG_TYPE, "AMDGPUIncompatibleFnRemoved", &F) 127 << "removing function '" << F.getName() << "': +" 128 << getFeatureName(Feature) 129 << " is not supported on the current target"; 130 }); 131 } 132 } // end anonymous namespace 133 134 bool AMDGPURemoveIncompatibleFunctions::checkFunction(Function &F) { 135 if (F.isDeclaration()) 136 return false; 137 138 const GCNSubtarget *ST = 139 static_cast<const GCNSubtarget *>(TM->getSubtargetImpl(F)); 140 141 // Check the GPU isn't generic. Generic is used for testing only 142 // and we don't want this pass to interfere with it. 143 StringRef GPUName = ST->getCPU(); 144 if (GPUName.empty() || GPUName.contains("generic")) 145 return false; 146 147 // Try to fetch the GPU's info. If we can't, it's likely an unknown processor 148 // so just bail out. 149 const SubtargetSubTypeKV *GPUInfo = getGPUInfo(*ST, GPUName); 150 if (!GPUInfo) 151 return false; 152 153 // Get all the features implied by the current GPU, and recursively expand 154 // the features that imply other features. 155 // 156 // e.g. GFX90A implies FeatureGFX9, and FeatureGFX9 implies a whole set of 157 // other features. 158 const FeatureBitset GPUFeatureBits = 159 expandImpliedFeatures(GPUInfo->Implies.getAsBitset()); 160 161 // Now that the have a FeatureBitset containing all possible features for 162 // the chosen GPU, check our list of "suspicious" features. 163 164 // Check that the user didn't enable any features that aren't part of that 165 // GPU's feature set. We only check a predetermined set of features. 166 for (unsigned Feature : FeaturesToCheck) { 167 if (ST->hasFeature(Feature) && !GPUFeatureBits.test(Feature)) { 168 reportFunctionRemoved(F, Feature); 169 return true; 170 } 171 } 172 173 // Delete FeatureWavefrontSize32 functions for 174 // gfx9 and below targets that don't support the mode. 175 // gfx10+ is implied to support both wave32 and 64 features. 176 // They are not in the feature set. So, we need a separate check 177 if (ST->getGeneration() < AMDGPUSubtarget::GFX10 && 178 ST->hasFeature(AMDGPU::FeatureWavefrontSize32)) { 179 reportFunctionRemoved(F, AMDGPU::FeatureWavefrontSize32); 180 return true; 181 } 182 return false; 183 } 184 185 INITIALIZE_PASS(AMDGPURemoveIncompatibleFunctions, DEBUG_TYPE, 186 "AMDGPU Remove Incompatible Functions", false, false) 187 188 char AMDGPURemoveIncompatibleFunctions::ID = 0; 189 190 ModulePass * 191 llvm::createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine *TM) { 192 return new AMDGPURemoveIncompatibleFunctions(TM); 193 } 194