106c3fb27SDimitry Andric //===-- AMDGPURemoveIncompatibleFunctions.cpp -----------------------------===// 206c3fb27SDimitry Andric // 306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606c3fb27SDimitry Andric // 706c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 806c3fb27SDimitry Andric // 906c3fb27SDimitry Andric /// \file 1006c3fb27SDimitry Andric /// This pass replaces all uses of functions that use GPU features 1106c3fb27SDimitry Andric /// incompatible with the current GPU with null then deletes the function. 1206c3fb27SDimitry Andric // 1306c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 1406c3fb27SDimitry Andric 1506c3fb27SDimitry Andric #include "AMDGPU.h" 1606c3fb27SDimitry Andric #include "GCNSubtarget.h" 1706c3fb27SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h" 1806c3fb27SDimitry Andric #include "llvm/IR/Function.h" 1906c3fb27SDimitry Andric #include "llvm/IR/IRBuilder.h" 2006c3fb27SDimitry Andric #include "llvm/IR/Module.h" 2106c3fb27SDimitry Andric #include "llvm/Pass.h" 2206c3fb27SDimitry Andric #include "llvm/Target/TargetMachine.h" 2306c3fb27SDimitry Andric 2406c3fb27SDimitry Andric #define DEBUG_TYPE "amdgpu-remove-incompatible-functions" 2506c3fb27SDimitry Andric 2606c3fb27SDimitry Andric using namespace llvm; 2706c3fb27SDimitry Andric 2806c3fb27SDimitry Andric namespace llvm { 2906c3fb27SDimitry Andric extern const SubtargetFeatureKV 3006c3fb27SDimitry Andric AMDGPUFeatureKV[AMDGPU::NumSubtargetFeatures - 1]; 31*0fca6ea1SDimitry Andric } // namespace llvm 3206c3fb27SDimitry Andric 3306c3fb27SDimitry Andric namespace { 3406c3fb27SDimitry Andric 3506c3fb27SDimitry Andric using Generation = AMDGPUSubtarget::Generation; 3606c3fb27SDimitry Andric 3706c3fb27SDimitry Andric class AMDGPURemoveIncompatibleFunctions : public ModulePass { 3806c3fb27SDimitry Andric public: 3906c3fb27SDimitry Andric static char ID; 4006c3fb27SDimitry Andric 4106c3fb27SDimitry Andric AMDGPURemoveIncompatibleFunctions(const TargetMachine *TM = nullptr) 4206c3fb27SDimitry Andric : ModulePass(ID), TM(TM) { 4306c3fb27SDimitry Andric assert(TM && "No TargetMachine!"); 4406c3fb27SDimitry Andric } 4506c3fb27SDimitry Andric 4606c3fb27SDimitry Andric StringRef getPassName() const override { 4706c3fb27SDimitry Andric return "AMDGPU Remove Incompatible Functions"; 4806c3fb27SDimitry Andric } 4906c3fb27SDimitry Andric 5006c3fb27SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {} 5106c3fb27SDimitry Andric 5206c3fb27SDimitry Andric /// Checks a single function, returns true if the function must be deleted. 5306c3fb27SDimitry Andric bool checkFunction(Function &F); 5406c3fb27SDimitry Andric 5506c3fb27SDimitry Andric bool runOnModule(Module &M) override { 5606c3fb27SDimitry Andric assert(TM->getTargetTriple().isAMDGCN()); 5706c3fb27SDimitry Andric 5806c3fb27SDimitry Andric SmallVector<Function *, 4> FnsToDelete; 5906c3fb27SDimitry Andric for (Function &F : M) { 6006c3fb27SDimitry Andric if (checkFunction(F)) 6106c3fb27SDimitry Andric FnsToDelete.push_back(&F); 6206c3fb27SDimitry Andric } 6306c3fb27SDimitry Andric 6406c3fb27SDimitry Andric for (Function *F : FnsToDelete) { 6506c3fb27SDimitry Andric F->replaceAllUsesWith(ConstantPointerNull::get(F->getType())); 6606c3fb27SDimitry Andric F->eraseFromParent(); 6706c3fb27SDimitry Andric } 6806c3fb27SDimitry Andric return !FnsToDelete.empty(); 6906c3fb27SDimitry Andric } 7006c3fb27SDimitry Andric 7106c3fb27SDimitry Andric private: 7206c3fb27SDimitry Andric const TargetMachine *TM = nullptr; 7306c3fb27SDimitry Andric }; 7406c3fb27SDimitry Andric 7506c3fb27SDimitry Andric StringRef getFeatureName(unsigned Feature) { 7606c3fb27SDimitry Andric for (const SubtargetFeatureKV &KV : AMDGPUFeatureKV) 7706c3fb27SDimitry Andric if (Feature == KV.Value) 7806c3fb27SDimitry Andric return KV.Key; 7906c3fb27SDimitry Andric 8006c3fb27SDimitry Andric llvm_unreachable("Unknown Target feature"); 8106c3fb27SDimitry Andric } 8206c3fb27SDimitry Andric 8306c3fb27SDimitry Andric const SubtargetSubTypeKV *getGPUInfo(const GCNSubtarget &ST, 8406c3fb27SDimitry Andric StringRef GPUName) { 8506c3fb27SDimitry Andric for (const SubtargetSubTypeKV &KV : ST.getAllProcessorDescriptions()) 8606c3fb27SDimitry Andric if (StringRef(KV.Key) == GPUName) 8706c3fb27SDimitry Andric return &KV; 8806c3fb27SDimitry Andric 8906c3fb27SDimitry Andric return nullptr; 9006c3fb27SDimitry Andric } 9106c3fb27SDimitry Andric 925f757f3fSDimitry Andric constexpr unsigned FeaturesToCheck[] = {AMDGPU::FeatureGFX11Insts, 935f757f3fSDimitry Andric AMDGPU::FeatureGFX10Insts, 945f757f3fSDimitry Andric AMDGPU::FeatureGFX9Insts, 955f757f3fSDimitry Andric AMDGPU::FeatureGFX8Insts, 965f757f3fSDimitry Andric AMDGPU::FeatureDPP, 975f757f3fSDimitry Andric AMDGPU::Feature16BitInsts, 985f757f3fSDimitry Andric AMDGPU::FeatureDot1Insts, 995f757f3fSDimitry Andric AMDGPU::FeatureDot2Insts, 1005f757f3fSDimitry Andric AMDGPU::FeatureDot3Insts, 1015f757f3fSDimitry Andric AMDGPU::FeatureDot4Insts, 1025f757f3fSDimitry Andric AMDGPU::FeatureDot5Insts, 1035f757f3fSDimitry Andric AMDGPU::FeatureDot6Insts, 1045f757f3fSDimitry Andric AMDGPU::FeatureDot7Insts, 1055f757f3fSDimitry Andric AMDGPU::FeatureDot8Insts, 1065f757f3fSDimitry Andric AMDGPU::FeatureExtendedImageInsts, 1075f757f3fSDimitry Andric AMDGPU::FeatureSMemRealTime, 1087a6dacacSDimitry Andric AMDGPU::FeatureSMemTimeInst, 1097a6dacacSDimitry Andric AMDGPU::FeatureGWS}; 11006c3fb27SDimitry Andric 11106c3fb27SDimitry Andric FeatureBitset expandImpliedFeatures(const FeatureBitset &Features) { 11206c3fb27SDimitry Andric FeatureBitset Result = Features; 11306c3fb27SDimitry Andric for (const SubtargetFeatureKV &FE : AMDGPUFeatureKV) { 11406c3fb27SDimitry Andric if (Features.test(FE.Value) && FE.Implies.any()) 11506c3fb27SDimitry Andric Result |= expandImpliedFeatures(FE.Implies.getAsBitset()); 11606c3fb27SDimitry Andric } 11706c3fb27SDimitry Andric return Result; 11806c3fb27SDimitry Andric } 11906c3fb27SDimitry Andric 12006c3fb27SDimitry Andric void reportFunctionRemoved(Function &F, unsigned Feature) { 12106c3fb27SDimitry Andric OptimizationRemarkEmitter ORE(&F); 12206c3fb27SDimitry Andric ORE.emit([&]() { 12306c3fb27SDimitry Andric // Note: we print the function name as part of the diagnostic because if 12406c3fb27SDimitry Andric // debug info is not present, users get "<unknown>:0:0" as the debug 12506c3fb27SDimitry Andric // loc. If we didn't print the function name there would be no way to 12606c3fb27SDimitry Andric // tell which function got removed. 12706c3fb27SDimitry Andric return OptimizationRemark(DEBUG_TYPE, "AMDGPUIncompatibleFnRemoved", &F) 12806c3fb27SDimitry Andric << "removing function '" << F.getName() << "': +" 12906c3fb27SDimitry Andric << getFeatureName(Feature) 13006c3fb27SDimitry Andric << " is not supported on the current target"; 13106c3fb27SDimitry Andric }); 13206c3fb27SDimitry Andric } 13306c3fb27SDimitry Andric } // end anonymous namespace 13406c3fb27SDimitry Andric 13506c3fb27SDimitry Andric bool AMDGPURemoveIncompatibleFunctions::checkFunction(Function &F) { 13606c3fb27SDimitry Andric if (F.isDeclaration()) 13706c3fb27SDimitry Andric return false; 13806c3fb27SDimitry Andric 13906c3fb27SDimitry Andric const GCNSubtarget *ST = 14006c3fb27SDimitry Andric static_cast<const GCNSubtarget *>(TM->getSubtargetImpl(F)); 14106c3fb27SDimitry Andric 142*0fca6ea1SDimitry Andric // Check the GPU isn't generic or generic-hsa. Generic is used for testing 143*0fca6ea1SDimitry Andric // only and we don't want this pass to interfere with it. 14406c3fb27SDimitry Andric StringRef GPUName = ST->getCPU(); 145*0fca6ea1SDimitry Andric if (GPUName.empty() || GPUName.starts_with("generic")) 14606c3fb27SDimitry Andric return false; 14706c3fb27SDimitry Andric 14806c3fb27SDimitry Andric // Try to fetch the GPU's info. If we can't, it's likely an unknown processor 14906c3fb27SDimitry Andric // so just bail out. 15006c3fb27SDimitry Andric const SubtargetSubTypeKV *GPUInfo = getGPUInfo(*ST, GPUName); 15106c3fb27SDimitry Andric if (!GPUInfo) 15206c3fb27SDimitry Andric return false; 15306c3fb27SDimitry Andric 15406c3fb27SDimitry Andric // Get all the features implied by the current GPU, and recursively expand 15506c3fb27SDimitry Andric // the features that imply other features. 15606c3fb27SDimitry Andric // 15706c3fb27SDimitry Andric // e.g. GFX90A implies FeatureGFX9, and FeatureGFX9 implies a whole set of 15806c3fb27SDimitry Andric // other features. 15906c3fb27SDimitry Andric const FeatureBitset GPUFeatureBits = 16006c3fb27SDimitry Andric expandImpliedFeatures(GPUInfo->Implies.getAsBitset()); 16106c3fb27SDimitry Andric 16206c3fb27SDimitry Andric // Now that the have a FeatureBitset containing all possible features for 16306c3fb27SDimitry Andric // the chosen GPU, check our list of "suspicious" features. 16406c3fb27SDimitry Andric 16506c3fb27SDimitry Andric // Check that the user didn't enable any features that aren't part of that 16606c3fb27SDimitry Andric // GPU's feature set. We only check a predetermined set of features. 16706c3fb27SDimitry Andric for (unsigned Feature : FeaturesToCheck) { 16806c3fb27SDimitry Andric if (ST->hasFeature(Feature) && !GPUFeatureBits.test(Feature)) { 16906c3fb27SDimitry Andric reportFunctionRemoved(F, Feature); 17006c3fb27SDimitry Andric return true; 17106c3fb27SDimitry Andric } 17206c3fb27SDimitry Andric } 17306c3fb27SDimitry Andric 17406c3fb27SDimitry Andric // Delete FeatureWavefrontSize32 functions for 17506c3fb27SDimitry Andric // gfx9 and below targets that don't support the mode. 17606c3fb27SDimitry Andric // gfx10+ is implied to support both wave32 and 64 features. 17706c3fb27SDimitry Andric // They are not in the feature set. So, we need a separate check 17806c3fb27SDimitry Andric if (ST->getGeneration() < AMDGPUSubtarget::GFX10 && 17906c3fb27SDimitry Andric ST->hasFeature(AMDGPU::FeatureWavefrontSize32)) { 18006c3fb27SDimitry Andric reportFunctionRemoved(F, AMDGPU::FeatureWavefrontSize32); 18106c3fb27SDimitry Andric return true; 18206c3fb27SDimitry Andric } 18306c3fb27SDimitry Andric return false; 18406c3fb27SDimitry Andric } 18506c3fb27SDimitry Andric 18606c3fb27SDimitry Andric INITIALIZE_PASS(AMDGPURemoveIncompatibleFunctions, DEBUG_TYPE, 18706c3fb27SDimitry Andric "AMDGPU Remove Incompatible Functions", false, false) 18806c3fb27SDimitry Andric 18906c3fb27SDimitry Andric char AMDGPURemoveIncompatibleFunctions::ID = 0; 19006c3fb27SDimitry Andric 19106c3fb27SDimitry Andric ModulePass * 19206c3fb27SDimitry Andric llvm::createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine *TM) { 19306c3fb27SDimitry Andric return new AMDGPURemoveIncompatibleFunctions(TM); 19406c3fb27SDimitry Andric } 195