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