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
AMDGPURemoveIncompatibleFunctions(const TargetMachine * TM=nullptr)4106c3fb27SDimitry Andric AMDGPURemoveIncompatibleFunctions(const TargetMachine *TM = nullptr)
4206c3fb27SDimitry Andric : ModulePass(ID), TM(TM) {
4306c3fb27SDimitry Andric assert(TM && "No TargetMachine!");
4406c3fb27SDimitry Andric }
4506c3fb27SDimitry Andric
getPassName() const4606c3fb27SDimitry Andric StringRef getPassName() const override {
4706c3fb27SDimitry Andric return "AMDGPU Remove Incompatible Functions";
4806c3fb27SDimitry Andric }
4906c3fb27SDimitry Andric
getAnalysisUsage(AnalysisUsage & AU) const5006c3fb27SDimitry 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
runOnModule(Module & M)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
getFeatureName(unsigned Feature)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
getGPUInfo(const GCNSubtarget & ST,StringRef GPUName)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
expandImpliedFeatures(const FeatureBitset & Features)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
reportFunctionRemoved(Function & F,unsigned Feature)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
checkFunction(Function & F)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 *
createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine * TM)19206c3fb27SDimitry Andric llvm::createAMDGPURemoveIncompatibleFunctionsPass(const TargetMachine *TM) {
19306c3fb27SDimitry Andric return new AMDGPURemoveIncompatibleFunctions(TM);
19406c3fb27SDimitry Andric }
195