1*06c3fb27SDimitry Andric //===-- AMDGPURemoveIncompatibleFunctions.cpp -----------------------------===// 2*06c3fb27SDimitry Andric // 3*06c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*06c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*06c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*06c3fb27SDimitry Andric // 7*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 8*06c3fb27SDimitry Andric // 9*06c3fb27SDimitry Andric /// \file 10*06c3fb27SDimitry Andric /// This pass replaces all uses of functions that use GPU features 11*06c3fb27SDimitry Andric /// incompatible with the current GPU with null then deletes the function. 12*06c3fb27SDimitry Andric // 13*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 14*06c3fb27SDimitry Andric 15*06c3fb27SDimitry Andric #include "AMDGPU.h" 16*06c3fb27SDimitry Andric #include "GCNSubtarget.h" 17*06c3fb27SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h" 18*06c3fb27SDimitry Andric #include "llvm/IR/Function.h" 19*06c3fb27SDimitry Andric #include "llvm/IR/IRBuilder.h" 20*06c3fb27SDimitry Andric #include "llvm/IR/Module.h" 21*06c3fb27SDimitry Andric #include "llvm/Pass.h" 22*06c3fb27SDimitry Andric #include "llvm/Target/TargetMachine.h" 23*06c3fb27SDimitry Andric 24*06c3fb27SDimitry Andric #define DEBUG_TYPE "amdgpu-remove-incompatible-functions" 25*06c3fb27SDimitry Andric 26*06c3fb27SDimitry Andric using namespace llvm; 27*06c3fb27SDimitry Andric 28*06c3fb27SDimitry Andric namespace llvm { 29*06c3fb27SDimitry Andric extern const SubtargetFeatureKV 30*06c3fb27SDimitry Andric AMDGPUFeatureKV[AMDGPU::NumSubtargetFeatures - 1]; 31*06c3fb27SDimitry Andric } 32*06c3fb27SDimitry Andric 33*06c3fb27SDimitry Andric namespace { 34*06c3fb27SDimitry Andric 35*06c3fb27SDimitry Andric using Generation = AMDGPUSubtarget::Generation; 36*06c3fb27SDimitry Andric 37*06c3fb27SDimitry Andric class AMDGPURemoveIncompatibleFunctions : public ModulePass { 38*06c3fb27SDimitry Andric public: 39*06c3fb27SDimitry Andric static char ID; 40*06c3fb27SDimitry Andric 41*06c3fb27SDimitry Andric AMDGPURemoveIncompatibleFunctions(const TargetMachine *TM = nullptr) 42*06c3fb27SDimitry Andric : ModulePass(ID), TM(TM) { 43*06c3fb27SDimitry Andric assert(TM && "No TargetMachine!"); 44*06c3fb27SDimitry Andric } 45*06c3fb27SDimitry Andric 46*06c3fb27SDimitry Andric StringRef getPassName() const override { 47*06c3fb27SDimitry Andric return "AMDGPU Remove Incompatible Functions"; 48*06c3fb27SDimitry Andric } 49*06c3fb27SDimitry Andric 50*06c3fb27SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {} 51*06c3fb27SDimitry Andric 52*06c3fb27SDimitry Andric /// Checks a single function, returns true if the function must be deleted. 53*06c3fb27SDimitry Andric bool checkFunction(Function &F); 54*06c3fb27SDimitry Andric 55*06c3fb27SDimitry Andric bool runOnModule(Module &M) override { 56*06c3fb27SDimitry Andric assert(TM->getTargetTriple().isAMDGCN()); 57*06c3fb27SDimitry Andric 58*06c3fb27SDimitry Andric SmallVector<Function *, 4> FnsToDelete; 59*06c3fb27SDimitry Andric for (Function &F : M) { 60*06c3fb27SDimitry Andric if (checkFunction(F)) 61*06c3fb27SDimitry Andric FnsToDelete.push_back(&F); 62*06c3fb27SDimitry Andric } 63*06c3fb27SDimitry Andric 64*06c3fb27SDimitry Andric for (Function *F : FnsToDelete) { 65*06c3fb27SDimitry Andric F->replaceAllUsesWith(ConstantPointerNull::get(F->getType())); 66*06c3fb27SDimitry Andric F->eraseFromParent(); 67*06c3fb27SDimitry Andric } 68*06c3fb27SDimitry Andric return !FnsToDelete.empty(); 69*06c3fb27SDimitry Andric } 70*06c3fb27SDimitry Andric 71*06c3fb27SDimitry Andric private: 72*06c3fb27SDimitry Andric const TargetMachine *TM = nullptr; 73*06c3fb27SDimitry Andric }; 74*06c3fb27SDimitry Andric 75*06c3fb27SDimitry Andric StringRef getFeatureName(unsigned Feature) { 76*06c3fb27SDimitry Andric for (const SubtargetFeatureKV &KV : AMDGPUFeatureKV) 77*06c3fb27SDimitry Andric if (Feature == KV.Value) 78*06c3fb27SDimitry Andric return KV.Key; 79*06c3fb27SDimitry Andric 80*06c3fb27SDimitry Andric llvm_unreachable("Unknown Target feature"); 81*06c3fb27SDimitry Andric } 82*06c3fb27SDimitry Andric 83*06c3fb27SDimitry Andric const SubtargetSubTypeKV *getGPUInfo(const GCNSubtarget &ST, 84*06c3fb27SDimitry Andric StringRef GPUName) { 85*06c3fb27SDimitry Andric for (const SubtargetSubTypeKV &KV : ST.getAllProcessorDescriptions()) 86*06c3fb27SDimitry Andric if (StringRef(KV.Key) == GPUName) 87*06c3fb27SDimitry Andric return &KV; 88*06c3fb27SDimitry Andric 89*06c3fb27SDimitry Andric return nullptr; 90*06c3fb27SDimitry Andric } 91*06c3fb27SDimitry Andric 92*06c3fb27SDimitry Andric constexpr unsigned FeaturesToCheck[] = { 93*06c3fb27SDimitry Andric AMDGPU::FeatureGFX11Insts, AMDGPU::FeatureGFX10Insts, 94*06c3fb27SDimitry Andric AMDGPU::FeatureGFX9Insts, AMDGPU::FeatureGFX8Insts, 95*06c3fb27SDimitry Andric AMDGPU::FeatureDPP, AMDGPU::Feature16BitInsts, 96*06c3fb27SDimitry Andric AMDGPU::FeatureDot1Insts, AMDGPU::FeatureDot2Insts, 97*06c3fb27SDimitry Andric AMDGPU::FeatureDot3Insts, AMDGPU::FeatureDot4Insts, 98*06c3fb27SDimitry Andric AMDGPU::FeatureDot5Insts, AMDGPU::FeatureDot6Insts, 99*06c3fb27SDimitry Andric AMDGPU::FeatureDot7Insts, AMDGPU::FeatureDot8Insts, 100*06c3fb27SDimitry Andric }; 101*06c3fb27SDimitry Andric 102*06c3fb27SDimitry Andric FeatureBitset expandImpliedFeatures(const FeatureBitset &Features) { 103*06c3fb27SDimitry Andric FeatureBitset Result = Features; 104*06c3fb27SDimitry Andric for (const SubtargetFeatureKV &FE : AMDGPUFeatureKV) { 105*06c3fb27SDimitry Andric if (Features.test(FE.Value) && FE.Implies.any()) 106*06c3fb27SDimitry Andric Result |= expandImpliedFeatures(FE.Implies.getAsBitset()); 107*06c3fb27SDimitry Andric } 108*06c3fb27SDimitry Andric return Result; 109*06c3fb27SDimitry Andric } 110*06c3fb27SDimitry Andric 111*06c3fb27SDimitry Andric void reportFunctionRemoved(Function &F, unsigned Feature) { 112*06c3fb27SDimitry Andric OptimizationRemarkEmitter ORE(&F); 113*06c3fb27SDimitry Andric ORE.emit([&]() { 114*06c3fb27SDimitry Andric // Note: we print the function name as part of the diagnostic because if 115*06c3fb27SDimitry Andric // debug info is not present, users get "<unknown>:0:0" as the debug 116*06c3fb27SDimitry Andric // loc. If we didn't print the function name there would be no way to 117*06c3fb27SDimitry Andric // tell which function got removed. 118*06c3fb27SDimitry Andric return OptimizationRemark(DEBUG_TYPE, "AMDGPUIncompatibleFnRemoved", &F) 119*06c3fb27SDimitry Andric << "removing function '" << F.getName() << "': +" 120*06c3fb27SDimitry Andric << getFeatureName(Feature) 121*06c3fb27SDimitry Andric << " is not supported on the current target"; 122*06c3fb27SDimitry Andric }); 123*06c3fb27SDimitry Andric return; 124*06c3fb27SDimitry Andric } 125*06c3fb27SDimitry Andric } // end anonymous namespace 126*06c3fb27SDimitry Andric 127*06c3fb27SDimitry Andric bool AMDGPURemoveIncompatibleFunctions::checkFunction(Function &F) { 128*06c3fb27SDimitry Andric if (F.isDeclaration()) 129*06c3fb27SDimitry Andric return false; 130*06c3fb27SDimitry Andric 131*06c3fb27SDimitry Andric const GCNSubtarget *ST = 132*06c3fb27SDimitry Andric static_cast<const GCNSubtarget *>(TM->getSubtargetImpl(F)); 133*06c3fb27SDimitry Andric 134*06c3fb27SDimitry Andric // Check the GPU isn't generic. Generic is used for testing only 135*06c3fb27SDimitry Andric // and we don't want this pass to interfere with it. 136*06c3fb27SDimitry Andric StringRef GPUName = ST->getCPU(); 137*06c3fb27SDimitry Andric if (GPUName.empty() || GPUName.contains("generic")) 138*06c3fb27SDimitry Andric return false; 139*06c3fb27SDimitry Andric 140*06c3fb27SDimitry Andric // Try to fetch the GPU's info. If we can't, it's likely an unknown processor 141*06c3fb27SDimitry Andric // so just bail out. 142*06c3fb27SDimitry Andric const SubtargetSubTypeKV *GPUInfo = getGPUInfo(*ST, GPUName); 143*06c3fb27SDimitry Andric if (!GPUInfo) 144*06c3fb27SDimitry Andric return false; 145*06c3fb27SDimitry Andric 146*06c3fb27SDimitry Andric // Get all the features implied by the current GPU, and recursively expand 147*06c3fb27SDimitry Andric // the features that imply other features. 148*06c3fb27SDimitry Andric // 149*06c3fb27SDimitry Andric // e.g. GFX90A implies FeatureGFX9, and FeatureGFX9 implies a whole set of 150*06c3fb27SDimitry Andric // other features. 151*06c3fb27SDimitry Andric const FeatureBitset GPUFeatureBits = 152*06c3fb27SDimitry Andric expandImpliedFeatures(GPUInfo->Implies.getAsBitset()); 153*06c3fb27SDimitry Andric 154*06c3fb27SDimitry Andric // Now that the have a FeatureBitset containing all possible features for 155*06c3fb27SDimitry Andric // the chosen GPU, check our list of "suspicious" features. 156*06c3fb27SDimitry Andric 157*06c3fb27SDimitry Andric // Check that the user didn't enable any features that aren't part of that 158*06c3fb27SDimitry Andric // GPU's feature set. We only check a predetermined set of features. 159*06c3fb27SDimitry Andric for (unsigned Feature : FeaturesToCheck) { 160*06c3fb27SDimitry Andric if (ST->hasFeature(Feature) && !GPUFeatureBits.test(Feature)) { 161*06c3fb27SDimitry Andric reportFunctionRemoved(F, Feature); 162*06c3fb27SDimitry Andric return true; 163*06c3fb27SDimitry Andric } 164*06c3fb27SDimitry Andric } 165*06c3fb27SDimitry Andric 166*06c3fb27SDimitry Andric // Delete FeatureWavefrontSize32 functions for 167*06c3fb27SDimitry Andric // gfx9 and below targets that don't support the mode. 168*06c3fb27SDimitry Andric // gfx10+ is implied to support both wave32 and 64 features. 169*06c3fb27SDimitry Andric // They are not in the feature set. So, we need a separate check 170*06c3fb27SDimitry Andric if (ST->getGeneration() < AMDGPUSubtarget::GFX10 && 171*06c3fb27SDimitry Andric ST->hasFeature(AMDGPU::FeatureWavefrontSize32)) { 172*06c3fb27SDimitry Andric reportFunctionRemoved(F, AMDGPU::FeatureWavefrontSize32); 173*06c3fb27SDimitry Andric return true; 174*06c3fb27SDimitry Andric } 175*06c3fb27SDimitry Andric return false; 176*06c3fb27SDimitry Andric } 177*06c3fb27SDimitry Andric 178*06c3fb27SDimitry Andric INITIALIZE_PASS(AMDGPURemoveIncompatibleFunctions, DEBUG_TYPE, 179*06c3fb27SDimitry Andric "AMDGPU Remove Incompatible Functions", false, false) 180*06c3fb27SDimitry Andric 181*06c3fb27SDimitry Andric char AMDGPURemoveIncompatibleFunctions::ID = 0; 182*06c3fb27SDimitry Andric 183*06c3fb27SDimitry Andric ModulePass * 184*06c3fb27SDimitry Andric llvm::createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine *TM) { 185*06c3fb27SDimitry Andric return new AMDGPURemoveIncompatibleFunctions(TM); 186*06c3fb27SDimitry Andric } 187