1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
//===-- 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<Function *, 4> 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,
};
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 "<unknown>: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";
});
return;
}
} // end anonymous namespace
bool AMDGPURemoveIncompatibleFunctions::checkFunction(Function &F) {
if (F.isDeclaration())
return false;
const GCNSubtarget *ST =
static_cast<const GCNSubtarget *>(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);
}
|