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