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