xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/AMDGPU/AMDGPURemoveIncompatibleFunctions.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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