[SPIR-V] Erase dead functions in shader modules (#209672)
The LLVM offload test suite is current failing layout keyword orientation tests. While initially appearing to be a layout problem in the SPIR-V legalizer caused by matrix vector sizes exceeding 4, the root cause is actually persistent dead function definitions in the SPIR-V backend. This behavior is standard for OpenCL, which relies on a linker to clean up unused definitions; however, since shaders lack a linker and require full inlining, we must handle this in a special shader only pass. This change adds SPIRVFinalizeShaderLinkage (a shader-only analogue of DXILFinalizeLinkage): internalize non-entry, non-exported helpers and erase the dead ones. Globals are left alone since unreferenced externals become interface OpVariables. Fixes #201712 Assisted with Claude Opus 4.8
This commit is contained in:
parent
db4d75e13d
commit
aace063fda
7 changed files with 166 additions and 4 deletions
|
|
@ -46,6 +46,7 @@ add_llvm_target(SPIRVCodeGen
|
|||
SPIRVRegisterBankInfo.cpp
|
||||
SPIRVRegisterInfo.cpp
|
||||
SPIRVRegularizer.cpp
|
||||
SPIRVFinalizeShaderLinkage.cpp
|
||||
SPIRVSubtarget.cpp
|
||||
SPIRVTargetMachine.cpp
|
||||
SPIRVTargetTransformInfo.cpp
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ ModulePass *createSPIRVPushConstantAccessLegacyPass(SPIRVTargetMachine *TM);
|
|||
FunctionPass *createSPIRVMergeRegionExitTargetsPass();
|
||||
ModulePass *createSPIRVLegalizeImplicitBindingPass();
|
||||
ModulePass *createSPIRVLegalizeZeroSizeArraysPass(const SPIRVTargetMachine &TM);
|
||||
ModulePass *createSPIRVFinalizeShaderLinkagePass(const SPIRVTargetMachine &TM);
|
||||
FunctionPass *createSPIRVLegalizePointerCastPass(SPIRVTargetMachine *TM);
|
||||
FunctionPass *createSPIRVRegularizerPass();
|
||||
FunctionPass *createSPIRVPreLegalizerCombiner();
|
||||
|
|
@ -57,6 +58,7 @@ void initializeSPIRVPrepareFunctionsLegacyPass(PassRegistry &);
|
|||
void initializeSPIRVPrepareGlobalsLegacyPass(PassRegistry &);
|
||||
void initializeSPIRVLegalizeImplicitBindingLegacyPass(PassRegistry &);
|
||||
void initializeSPIRVLegalizeZeroSizeArraysLegacyPass(PassRegistry &);
|
||||
void initializeSPIRVFinalizeShaderLinkageLegacyPass(PassRegistry &);
|
||||
void initializeSPIRVCtorDtorLoweringLegacyPass(PassRegistry &);
|
||||
} // namespace llvm
|
||||
|
||||
|
|
|
|||
94
llvm/lib/Target/SPIRV/SPIRVFinalizeShaderLinkage.cpp
Normal file
94
llvm/lib/Target/SPIRV/SPIRVFinalizeShaderLinkage.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
//===- SPIRVFinalizeShaderLinkage.cpp - Finalize shader linkage ----------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Shader-only analogue of DXILFinalizeLinkage: internalizes non-entry,
|
||||
// non-exported HLSL helper functions and erases the resulting dead ones.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "SPIRVFinalizeShaderLinkage.h"
|
||||
#include "SPIRV.h"
|
||||
#include "SPIRVSubtarget.h"
|
||||
#include "SPIRVTargetMachine.h"
|
||||
#include "SPIRVUtils.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/GlobalValue.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Pass.h"
|
||||
|
||||
#define DEBUG_TYPE "spirv-finalize-shader-linkage"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
bool finalizeShaderLinkage(const SPIRVTargetMachine &TM, Module &M) {
|
||||
// This pass only applies to shader targets because shaders don't have
|
||||
// linkers.
|
||||
if (!TM.getSubtargetImpl()->isShader())
|
||||
return false;
|
||||
|
||||
bool Changed = false;
|
||||
|
||||
for (Function &F : M) {
|
||||
if (F.isIntrinsic() || F.isDeclaration() || isEntryPoint(F))
|
||||
continue;
|
||||
if (F.hasExternalLinkage() && !F.hasHiddenVisibility())
|
||||
continue;
|
||||
if (!F.hasLocalLinkage()) {
|
||||
F.setLinkage(GlobalValue::InternalLinkage);
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Erase dead helpers, iterating to a fixpoint for helper-calls-helper chains.
|
||||
bool LocalChange = true;
|
||||
while (LocalChange) {
|
||||
LocalChange = false;
|
||||
for (Function &F : make_early_inc_range(M))
|
||||
if (F.isDefTriviallyDead()) {
|
||||
F.eraseFromParent();
|
||||
LocalChange = Changed = true;
|
||||
}
|
||||
}
|
||||
return Changed;
|
||||
}
|
||||
|
||||
class SPIRVFinalizeShaderLinkageLegacy : public ModulePass {
|
||||
public:
|
||||
static char ID;
|
||||
SPIRVFinalizeShaderLinkageLegacy(const SPIRVTargetMachine &TM)
|
||||
: ModulePass(ID), TM(TM) {}
|
||||
StringRef getPassName() const override {
|
||||
return "SPIRV Finalize Shader Linkage";
|
||||
}
|
||||
bool runOnModule(Module &M) override { return finalizeShaderLinkage(TM, M); }
|
||||
|
||||
private:
|
||||
const SPIRVTargetMachine &TM;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
PreservedAnalyses SPIRVFinalizeShaderLinkage::run(Module &M,
|
||||
ModuleAnalysisManager &AM) {
|
||||
return finalizeShaderLinkage(TM, M) ? PreservedAnalyses::none()
|
||||
: PreservedAnalyses::all();
|
||||
}
|
||||
|
||||
char SPIRVFinalizeShaderLinkageLegacy::ID = 0;
|
||||
|
||||
INITIALIZE_PASS(SPIRVFinalizeShaderLinkageLegacy,
|
||||
"spirv-finalize-shader-linkage",
|
||||
"Finalize SPIR-V shader linkage", false, false)
|
||||
|
||||
ModulePass *
|
||||
llvm::createSPIRVFinalizeShaderLinkagePass(const SPIRVTargetMachine &TM) {
|
||||
return new SPIRVFinalizeShaderLinkageLegacy(TM);
|
||||
}
|
||||
34
llvm/lib/Target/SPIRV/SPIRVFinalizeShaderLinkage.h
Normal file
34
llvm/lib/Target/SPIRV/SPIRVFinalizeShaderLinkage.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//===- SPIRVFinalizeShaderLinkage.h - Finalize shader linkage --*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// Shader-only analogue of DXILFinalizeLinkage: internalizes non-entry,
|
||||
/// non-exported HLSL helper functions and erases the resulting dead ones.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVFINALIZESHADERLINKAGE_H
|
||||
#define LLVM_LIB_TARGET_SPIRV_SPIRVFINALIZESHADERLINKAGE_H
|
||||
|
||||
#include "llvm/IR/PassManager.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class SPIRVTargetMachine;
|
||||
|
||||
class SPIRVFinalizeShaderLinkage
|
||||
: public OptionalPassInfoMixin<SPIRVFinalizeShaderLinkage> {
|
||||
const SPIRVTargetMachine &TM;
|
||||
|
||||
public:
|
||||
SPIRVFinalizeShaderLinkage(const SPIRVTargetMachine &TM) : TM(TM) {}
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_LIB_TARGET_SPIRV_SPIRVFINALIZESHADERLINKAGE_H
|
||||
|
|
@ -23,6 +23,7 @@ MODULE_PASS("spirv-lower-ctor-dtor", SPIRVCtorDtorLoweringPass())
|
|||
MODULE_PASS("spirv-prepare-functions", SPIRVPrepareFunctions(*static_cast<const SPIRVTargetMachine *>(this)))
|
||||
MODULE_PASS("spirv-prepare-globals", SPIRVPrepareGlobals())
|
||||
MODULE_PASS("spirv-pushconstant-access", SPIRVPushConstantAccess(*static_cast<const SPIRVTargetMachine *>(this)))
|
||||
MODULE_PASS("spirv-finalize-shader-linkage", SPIRVFinalizeShaderLinkage(*static_cast<const SPIRVTargetMachine *>(this)))
|
||||
MODULE_PASS("spirv-emit-intrinsics", SPIRVEmitIntrinsicsPass(*static_cast<const SPIRVTargetMachine *>(this)))
|
||||
#undef MODULE_PASS
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "SPIRVCBufferAccess.h"
|
||||
#include "SPIRVCtorDtorLowering.h"
|
||||
#include "SPIRVEmitIntrinsics.h"
|
||||
#include "SPIRVFinalizeShaderLinkage.h"
|
||||
#include "SPIRVGlobalRegistry.h"
|
||||
#include "SPIRVLegalizeImplicitBinding.h"
|
||||
#include "SPIRVLegalizePointerCast.h"
|
||||
|
|
@ -75,6 +76,7 @@ extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
|
|||
initializeSPIRVPrepareGlobalsLegacyPass(PR);
|
||||
initializeSPIRVLegalizeImplicitBindingLegacyPass(PR);
|
||||
initializeSPIRVCtorDtorLoweringLegacyPass(PR);
|
||||
initializeSPIRVFinalizeShaderLinkageLegacyPass(PR);
|
||||
}
|
||||
|
||||
static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) {
|
||||
|
|
@ -186,10 +188,13 @@ void SPIRVPassConfig::addIRPasses() {
|
|||
|
||||
TargetPassConfig::addIRPasses();
|
||||
|
||||
// Variadic function calls aren't supported in shader code.
|
||||
// This needs to come before SPIRVPrepareFunctions because this
|
||||
// may introduce intrinsic calls.
|
||||
if (!TM.getSubtargetImpl()->isShader()) {
|
||||
if (TM.getSubtargetImpl()->isShader()) {
|
||||
if (getOptLevel() != CodeGenOptLevel::None)
|
||||
addPass(createSPIRVFinalizeShaderLinkagePass(TM));
|
||||
} else {
|
||||
// Variadic function calls aren't supported in shader code.
|
||||
// This needs to come before SPIRVPrepareFunctions because this
|
||||
// may introduce intrinsic calls.
|
||||
addPass(createExpandVariadicsPass(ExpandVariadicsMode::Lowering));
|
||||
}
|
||||
|
||||
|
|
|
|||
25
llvm/test/CodeGen/SPIRV/finalize-shader-linkage.ll
Normal file
25
llvm/test/CodeGen/SPIRV/finalize-shader-linkage.ll
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
; RUN: opt -mtriple=spirv-unknown-vulkan1.3-compute -passes=spirv-finalize-shader-linkage -S %s | FileCheck %s --check-prefix=OPT
|
||||
; RUN: llc -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s
|
||||
; RUN: %if spirv-tools %{ llc -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
|
||||
|
||||
; The pass erases the dead helper and keeps the entry point.
|
||||
; OPT-NOT: @dead_matrix_helper
|
||||
; OPT: define void @main()
|
||||
|
||||
; CHECK-DAG: OpEntryPoint GLCompute %[[#entry:]] "main"
|
||||
; The dead helper (and its illegal wide-vector parameter) must not be emitted.
|
||||
; CHECK-NOT: OpFunctionParameter
|
||||
; CHECK-NOT: dead_matrix_helper
|
||||
|
||||
define hidden spir_func <6 x float> @dead_matrix_helper(<6 x float> %m) #1 {
|
||||
%r = fadd <6 x float> %m, %m
|
||||
ret <6 x float> %r
|
||||
}
|
||||
|
||||
define void @main() #0 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
|
||||
attributes #1 = { alwaysinline }
|
||||
Loading…
Reference in a new issue