//===-- AMDGPURemoveIncompatibleFunctions.cpp -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // /// \file /// This pass replaces all uses of functions that use GPU features /// incompatible with the current GPU with null then deletes the function. // //===----------------------------------------------------------------------===// #include "AMDGPU.h" #include "GCNSubtarget.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Target/TargetMachine.h" #define DEBUG_TYPE "amdgpu-remove-incompatible-functions" using namespace llvm; namespace llvm { extern const SubtargetFeatureKV AMDGPUFeatureKV[AMDGPU::NumSubtargetFeatures - 1]; } namespace { using Generation = AMDGPUSubtarget::Generation; class AMDGPURemoveIncompatibleFunctions : public ModulePass { public: static char ID; AMDGPURemoveIncompatibleFunctions(const TargetMachine *TM = nullptr) : ModulePass(ID), TM(TM) { assert(TM && "No TargetMachine!"); } StringRef getPassName() const override { return "AMDGPU Remove Incompatible Functions"; } void getAnalysisUsage(AnalysisUsage &AU) const override {} /// Checks a single function, returns true if the function must be deleted. bool checkFunction(Function &F); bool runOnModule(Module &M) override { assert(TM->getTargetTriple().isAMDGCN()); SmallVector FnsToDelete; for (Function &F : M) { if (checkFunction(F)) FnsToDelete.push_back(&F); } for (Function *F : FnsToDelete) { F->replaceAllUsesWith(ConstantPointerNull::get(F->getType())); F->eraseFromParent(); } return !FnsToDelete.empty(); } private: const TargetMachine *TM = nullptr; }; StringRef getFeatureName(unsigned Feature) { for (const SubtargetFeatureKV &KV : AMDGPUFeatureKV) if (Feature == KV.Value) return KV.Key; llvm_unreachable("Unknown Target feature"); } const SubtargetSubTypeKV *getGPUInfo(const GCNSubtarget &ST, StringRef GPUName) { for (const SubtargetSubTypeKV &KV : ST.getAllProcessorDescriptions()) if (StringRef(KV.Key) == GPUName) return &KV; return nullptr; } constexpr unsigned FeaturesToCheck[] = {AMDGPU::FeatureGFX11Insts, AMDGPU::FeatureGFX10Insts, AMDGPU::FeatureGFX9Insts, AMDGPU::FeatureGFX8Insts, AMDGPU::FeatureDPP, AMDGPU::Feature16BitInsts, AMDGPU::FeatureDot1Insts, AMDGPU::FeatureDot2Insts, AMDGPU::FeatureDot3Insts, AMDGPU::FeatureDot4Insts, AMDGPU::FeatureDot5Insts, AMDGPU::FeatureDot6Insts, AMDGPU::FeatureDot7Insts, AMDGPU::FeatureDot8Insts, AMDGPU::FeatureExtendedImageInsts, AMDGPU::FeatureSMemRealTime, AMDGPU::FeatureSMemTimeInst, AMDGPU::FeatureGWS}; FeatureBitset expandImpliedFeatures(const FeatureBitset &Features) { FeatureBitset Result = Features; for (const SubtargetFeatureKV &FE : AMDGPUFeatureKV) { if (Features.test(FE.Value) && FE.Implies.any()) Result |= expandImpliedFeatures(FE.Implies.getAsBitset()); } return Result; } void reportFunctionRemoved(Function &F, unsigned Feature) { OptimizationRemarkEmitter ORE(&F); ORE.emit([&]() { // Note: we print the function name as part of the diagnostic because if // debug info is not present, users get ":0:0" as the debug // loc. If we didn't print the function name there would be no way to // tell which function got removed. return OptimizationRemark(DEBUG_TYPE, "AMDGPUIncompatibleFnRemoved", &F) << "removing function '" << F.getName() << "': +" << getFeatureName(Feature) << " is not supported on the current target"; }); } } // end anonymous namespace bool AMDGPURemoveIncompatibleFunctions::checkFunction(Function &F) { if (F.isDeclaration()) return false; const GCNSubtarget *ST = static_cast(TM->getSubtargetImpl(F)); // Check the GPU isn't generic. Generic is used for testing only // and we don't want this pass to interfere with it. StringRef GPUName = ST->getCPU(); if (GPUName.empty() || GPUName.contains("generic")) return false; // Try to fetch the GPU's info. If we can't, it's likely an unknown processor // so just bail out. const SubtargetSubTypeKV *GPUInfo = getGPUInfo(*ST, GPUName); if (!GPUInfo) return false; // Get all the features implied by the current GPU, and recursively expand // the features that imply other features. // // e.g. GFX90A implies FeatureGFX9, and FeatureGFX9 implies a whole set of // other features. const FeatureBitset GPUFeatureBits = expandImpliedFeatures(GPUInfo->Implies.getAsBitset()); // Now that the have a FeatureBitset containing all possible features for // the chosen GPU, check our list of "suspicious" features. // Check that the user didn't enable any features that aren't part of that // GPU's feature set. We only check a predetermined set of features. for (unsigned Feature : FeaturesToCheck) { if (ST->hasFeature(Feature) && !GPUFeatureBits.test(Feature)) { reportFunctionRemoved(F, Feature); return true; } } // Delete FeatureWavefrontSize32 functions for // gfx9 and below targets that don't support the mode. // gfx10+ is implied to support both wave32 and 64 features. // They are not in the feature set. So, we need a separate check if (ST->getGeneration() < AMDGPUSubtarget::GFX10 && ST->hasFeature(AMDGPU::FeatureWavefrontSize32)) { reportFunctionRemoved(F, AMDGPU::FeatureWavefrontSize32); return true; } return false; } INITIALIZE_PASS(AMDGPURemoveIncompatibleFunctions, DEBUG_TYPE, "AMDGPU Remove Incompatible Functions", false, false) char AMDGPURemoveIncompatibleFunctions::ID = 0; ModulePass * llvm::createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine *TM) { return new AMDGPURemoveIncompatibleFunctions(TM); }