https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/200965
>From 6e438402b83f6d4a8fed8fc0bbc2b7957021fadc Mon Sep 17 00:00:00 2001 From: Helena Kotas <[email protected]> Date: Mon, 1 Jun 2026 14:56:56 -0700 Subject: [PATCH] [DirectX] Add pass that removes unused resources-in-unused-function.ll Fixes #192524 --- llvm/lib/Target/DirectX/CMakeLists.txt | 1 + .../DirectX/DXILRemoveUnusedResources.cpp | 140 ++++++++++++++++++ .../DirectX/DXILRemoveUnusedResources.h | 29 ++++ llvm/lib/Target/DirectX/DirectX.h | 6 + .../Target/DirectX/DirectXPassRegistry.def | 1 + .../Target/DirectX/DirectXTargetMachine.cpp | 3 + .../DirectX/Binding/binding-overlap-3.ll | 2 +- .../DirectX/Binding/binding-overlap-4.ll | 2 +- .../DirectX/Binding/binding-overlap-5.ll | 2 +- .../DirectX/Binding/binding-overlap-7.ll | 2 +- .../ContainerData/PSVResources-order.ll | 2 +- .../DirectX/ContainerData/PSVResources.ll | 2 +- .../DirectX/Metadata/cbuffer-metadata.ll | 2 +- .../CodeGen/DirectX/Metadata/srv_metadata.ll | 2 +- .../CodeGen/DirectX/Metadata/uav_metadata.ll | 2 +- .../max-64-uavs-array-valver1.6.ll | 2 +- .../DirectX/ShaderFlags/max-64-uavs.ll | 2 +- .../ShaderFlags/overflow-uavs-array.ll | 2 +- .../uavs-at-every-stage-lib-valver1.7.ll | 2 +- .../ShaderFlags/unbounded-uavs-array.ll | 2 +- llvm/test/CodeGen/DirectX/llc-pipeline.ll | 1 + .../DirectX/resources-in-unused-function.ll | 81 ++++++++++ .../DirectX/unused-resources-impl-binding.ll | 109 ++++++++++++++ llvm/test/CodeGen/DirectX/unused-resources.ll | 68 +++++++++ 24 files changed, 453 insertions(+), 14 deletions(-) create mode 100644 llvm/lib/Target/DirectX/DXILRemoveUnusedResources.cpp create mode 100644 llvm/lib/Target/DirectX/DXILRemoveUnusedResources.h create mode 100644 llvm/test/CodeGen/DirectX/resources-in-unused-function.ll create mode 100644 llvm/test/CodeGen/DirectX/unused-resources-impl-binding.ll create mode 100644 llvm/test/CodeGen/DirectX/unused-resources.ll diff --git a/llvm/lib/Target/DirectX/CMakeLists.txt b/llvm/lib/Target/DirectX/CMakeLists.txt index 436b7499f8dca..fd730ed82cc51 100644 --- a/llvm/lib/Target/DirectX/CMakeLists.txt +++ b/llvm/lib/Target/DirectX/CMakeLists.txt @@ -32,6 +32,7 @@ add_llvm_target(DirectXCodeGen DXILPostOptimizationValidation.cpp DXILPrepare.cpp DXILPrettyPrinter.cpp + DXILRemoveUnusedResources.cpp DXILResourceAccess.cpp DXILResourceImplicitBinding.cpp DXILShaderFlags.cpp diff --git a/llvm/lib/Target/DirectX/DXILRemoveUnusedResources.cpp b/llvm/lib/Target/DirectX/DXILRemoveUnusedResources.cpp new file mode 100644 index 0000000000000..aa05f58336752 --- /dev/null +++ b/llvm/lib/Target/DirectX/DXILRemoveUnusedResources.cpp @@ -0,0 +1,140 @@ +//===- DXILResourceAccess.cpp - Resource access via load/store ------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "DXILRemoveUnusedResources.h" +#include "DirectX.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Analysis/VectorUtils.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicsDirectX.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/InitializePasses.h" +#include "llvm/Transforms/Utils/ValueMapper.h" + +#define DEBUG_TYPE "dxil-remove-unused-resources" + +// Hidden option to disable the pass to make it easier to test +// other passes related to DXIL resources using llc. +static llvm::cl::opt<bool> DisableDXILRemoveUnusedResources( + "disable-dxil-remove-unused-resources", + llvm::cl::desc("Disable dxil-remove-unused-resources pass"), + llvm::cl::init(false), llvm::cl::Hidden); + +using namespace llvm; + +// Removes all calls to intrinsics dx_resource_handlefrom{implicit}binding that +// either are not used, or their only use is in a store instruction, which +// stores the initialized handle into a global variable that does not have +// external linkage and that is not used anywhere else in the module. +static bool removeUnusedResources(Function &F) { + if (DisableDXILRemoveUnusedResources) + return false; + + SmallVector<Instruction *> DeadInstr; + SmallSetVector<GlobalVariable *, 4> DeadGlobals; + for (BasicBlock &BB : make_early_inc_range(F)) { + for (Instruction &I : BB) { + if (auto *II = dyn_cast<IntrinsicInst>(&I)) { + if (II->getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding && + II->getIntrinsicID() != + Intrinsic::dx_resource_handlefromimplicitbinding) + continue; + if (II->user_empty()) { + // Initialized handle is not used anywhere. + DeadInstr.push_back(II); + continue; + } + if (!II->hasOneUser()) + continue; + + // Initialized handle is only used in one store instruction, the store + // is into global variable, and that global variable is not used + // anywhere else and does not have external linkage. + auto *SI = dyn_cast<StoreInst>(*II->user_begin()); + if (!SI) + continue; + assert(SI->getValueOperand() == II && + "expected value operand to be the resource handle"); + + GlobalVariable *GV = dyn_cast<GlobalVariable>(SI->getPointerOperand()); + if (!GV || GV->hasExternalLinkage()) + continue; + + if (GV->hasOneUser()) { + assert(*GV->user_begin() == SI && + "expected single user to be the store instruction"); + DeadInstr.push_back(SI); + DeadInstr.push_back(II); + DeadGlobals.insert(GV); + } + } + } + } + + if (DeadInstr.empty()) + return false; + + for (auto *Instr : DeadInstr) { + if (auto *II = dyn_cast<IntrinsicInst>(Instr)) { + assert(II->getIntrinsicID() == Intrinsic::dx_resource_handlefrombinding || + II->getIntrinsicID() == + Intrinsic::dx_resource_handlefromimplicitbinding); + const unsigned ResourceNameOpIndex = 4; + GlobalVariable *ResourceName = dyn_cast_or_null<GlobalVariable>( + II->getArgOperand(ResourceNameOpIndex)); + if (ResourceName) + DeadGlobals.insert(ResourceName); + } + Instr->eraseFromParent(); + } + + for (auto *GV : DeadGlobals) + if (GV->use_empty()) + GV->eraseFromParent(); + + return true; +} + +PreservedAnalyses DXILRemoveUnusedResources::run(Function &F, + FunctionAnalysisManager &AM) { + removeUnusedResources(F); + return PreservedAnalyses::all(); +} + +namespace { +class DXILRemoveUnusedResourcesLegacy : public FunctionPass { +public: + bool runOnFunction(Function &F) override { return removeUnusedResources(F); } + StringRef getPassName() const override { + return "DXIL Remove Unused Resources"; + } + DXILRemoveUnusedResourcesLegacy() : FunctionPass(ID) {} + + static char ID; // Pass identification. + void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } +}; +char DXILRemoveUnusedResourcesLegacy::ID = 0; +} // end anonymous namespace + +INITIALIZE_PASS_BEGIN(DXILRemoveUnusedResourcesLegacy, DEBUG_TYPE, + "DXIL Remove Unused Resources", false, false) +INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass) +INITIALIZE_PASS_END(DXILRemoveUnusedResourcesLegacy, DEBUG_TYPE, + "DXIL Remove Unused Resources", false, false) + +FunctionPass *llvm::createDXILRemoveUnusedResourcesLegacyPass() { + return new DXILRemoveUnusedResourcesLegacy(); +} diff --git a/llvm/lib/Target/DirectX/DXILRemoveUnusedResources.h b/llvm/lib/Target/DirectX/DXILRemoveUnusedResources.h new file mode 100644 index 0000000000000..2b9f61c3f8a7a --- /dev/null +++ b/llvm/lib/Target/DirectX/DXILRemoveUnusedResources.h @@ -0,0 +1,29 @@ +//===- DXILRemoveUnusedResources.h - Remove unused resources ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// \file Pass for removing unused DXIL resources initialization calls and +// associated global variables. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_DIRECTX_DXILREMOVEUNUSEDRESOURCES_H +#define LLVM_LIB_TARGET_DIRECTX_DXILREMOVEUNUSEDRESOURCES_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class DXILRemoveUnusedResources + : public OptionalPassInfoMixin<DXILRemoveUnusedResources> { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_DIRECTX_DXILREMOVEUNUSEDRESOURCES_H diff --git a/llvm/lib/Target/DirectX/DirectX.h b/llvm/lib/Target/DirectX/DirectX.h index dfeb1ab12665d..eb2966ee9b4c4 100644 --- a/llvm/lib/Target/DirectX/DirectX.h +++ b/llvm/lib/Target/DirectX/DirectX.h @@ -78,6 +78,12 @@ void initializeDXILOpLoweringLegacyPass(PassRegistry &); /// Pass to lowering LLVM intrinsic call to DXIL op function call. ModulePass *createDXILOpLoweringLegacyPass(); +/// Initializer for DXILRemoveUnusedResources +void initializeDXILRemoveUnusedResourcesLegacyPass(PassRegistry &); + +/// Pass to update remove unsused resources. +FunctionPass *createDXILRemoveUnusedResourcesLegacyPass(); + /// Initializer for DXILResourceAccess void initializeDXILResourceAccessLegacyPass(PassRegistry &); diff --git a/llvm/lib/Target/DirectX/DirectXPassRegistry.def b/llvm/lib/Target/DirectX/DirectXPassRegistry.def index f594546f98901..027803c1181f5 100644 --- a/llvm/lib/Target/DirectX/DirectXPassRegistry.def +++ b/llvm/lib/Target/DirectX/DirectXPassRegistry.def @@ -43,6 +43,7 @@ MODULE_PASS("print<dxil-root-signature>", dxil::RootSignatureAnalysisPrinter(dbg #define FUNCTION_PASS(NAME, CREATE_PASS) #endif FUNCTION_PASS("dxil-forward-handle-accesses", DXILForwardHandleAccesses()) +FUNCTION_PASS("dxil-remove-unused-resources", DXILRemoveUnusedResources()) FUNCTION_PASS("dxil-resource-access", DXILResourceAccess()) FUNCTION_PASS("dxil-legalize", DXILLegalizePass()) #undef FUNCTION_PASS diff --git a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp index 234385828bd3d..c4172b748107a 100644 --- a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp +++ b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp @@ -23,6 +23,7 @@ #include "DXILOpLowering.h" #include "DXILPostOptimizationValidation.h" #include "DXILPrettyPrinter.h" +#include "DXILRemoveUnusedResources.h" #include "DXILResourceAccess.h" #include "DXILResourceImplicitBinding.h" #include "DXILRootSignature.h" @@ -71,6 +72,7 @@ LLVMInitializeDirectXTarget() { initializeDXContainerGlobalsPass(*PR); initializeGlobalDCELegacyPassPass(*PR); initializeDXILOpLoweringLegacyPass(*PR); + initializeDXILRemoveUnusedResourcesLegacyPass(*PR); initializeDXILResourceAccessLegacyPass(*PR); initializeDXILResourceImplicitBindingLegacyPass(*PR); initializeDXILTranslateMetadataLegacyPass(*PR); @@ -117,6 +119,7 @@ class DirectXPassConfig : public TargetPassConfig { addPass(createGlobalDCEPass()); addPass(createDXILMemIntrinsicsLegacyPass()); addPass(createDXILCBufferAccessLegacyPass()); + addPass(createDXILRemoveUnusedResourcesLegacyPass()); addPass(createDXILResourceAccessLegacyPass()); addPass(createDXILIntrinsicExpansionLegacyPass()); addPass(createDXILDataScalarizationLegacyPass()); diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll index 67365ee3b64f7..f41b3b4f91ad2 100644 --- a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll +++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll @@ -1,5 +1,5 @@ ; Use llc for this test so that we don't abort after the first error. -; RUN: not llc %s -o /dev/null 2>&1 | FileCheck %s +; RUN: not llc %s -disable-dxil-remove-unused-resources -o /dev/null 2>&1 | FileCheck %s ; Check multiple overlap errors. ; Also check different resource class with same binding values is ok (no error expected). diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-4.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-4.ll index bd8dda7858f9f..a0bb16a85d6ef 100644 --- a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-4.ll +++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-4.ll @@ -1,5 +1,5 @@ ; Use llc for this test so that we don't abort after the first error. -; RUN: not llc %s -o /dev/null 2>&1 | FileCheck %s +; RUN: not llc %s -disable-dxil-remove-unused-resources -o /dev/null 2>&1 | FileCheck %s ; Check multiple overlap errors. diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-5.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-5.ll index b047a22db5fb9..6e6ae67cdf786 100644 --- a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-5.ll +++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-5.ll @@ -1,5 +1,5 @@ ; Use llc for this test so that we don't abort after the first error. -; RUN: not llc %s -o /dev/null 2>&1 | FileCheck %s +; RUN: not llc %s -disable-dxil-remove-unused-resources -o /dev/null 2>&1 | FileCheck %s ; Check multiple overlap errors. diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-7.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-7.ll index 151c770489826..2e1983fd16fce 100644 --- a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-7.ll +++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-7.ll @@ -1,5 +1,5 @@ ; Use llc for this test so that we don't abort after the first error. -; RUN: not llc %s -o /dev/null 2>&1 | FileCheck %s +; RUN: not llc %s -disable-dxil-remove-unused-resources -o /dev/null 2>&1 | FileCheck %s ; Check that there is no overlap with unbounded array in different space diff --git a/llvm/test/CodeGen/DirectX/ContainerData/PSVResources-order.ll b/llvm/test/CodeGen/DirectX/ContainerData/PSVResources-order.ll index 5cd67be1a4a28..4a6f9267c2fea 100644 --- a/llvm/test/CodeGen/DirectX/ContainerData/PSVResources-order.ll +++ b/llvm/test/CodeGen/DirectX/ContainerData/PSVResources-order.ll @@ -1,4 +1,4 @@ -; RUN: llc %s --filetype=obj -o - | obj2yaml | FileCheck %s +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s ; Check that resources are emitted to the object in the order that matches what ; the DXIL validator expects: CBuffers, Samplers, SRVs, and then UAVs. diff --git a/llvm/test/CodeGen/DirectX/ContainerData/PSVResources.ll b/llvm/test/CodeGen/DirectX/ContainerData/PSVResources.ll index ea3a02245c1cc..ffd9886d27f41 100644 --- a/llvm/test/CodeGen/DirectX/ContainerData/PSVResources.ll +++ b/llvm/test/CodeGen/DirectX/ContainerData/PSVResources.ll @@ -1,4 +1,4 @@ -; RUN: llc %s --filetype=obj -o - | obj2yaml | FileCheck %s +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s ; Make sure resource table is created correctly. ; CHECK: Resources: diff --git a/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll index 6b90e17816991..f389e612e5560 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll @@ -1,6 +1,6 @@ ; RUN: opt -S -dxil-translate-metadata < %s | FileCheck %s ; RUN: opt -S --passes="dxil-pretty-printer" < %s 2>&1 | FileCheck %s --check-prefix=PRINT -; RUN: llc %s --filetype=asm -o - < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=asm -o - < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT target triple = "dxil-pc-shadermodel6.6-compute" diff --git a/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll index ea8f418d4dada..202f29ca2df9b 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll @@ -1,6 +1,6 @@ ; RUN: opt -S -dxil-translate-metadata < %s | FileCheck %s ; RUN: opt -S --passes="dxil-pretty-printer" < %s 2>&1 | FileCheck %s --check-prefix=PRINT -; RUN: llc %s --filetype=asm -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=asm -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" target triple = "dxil-pc-shadermodel6.6-compute" diff --git a/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll index 0f7d56fb1261e..3c7cdb981d87f 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll @@ -1,6 +1,6 @@ ; RUN: opt -S -dxil-translate-metadata < %s | FileCheck %s ; RUN: opt -S --passes="dxil-pretty-printer" < %s 2>&1 | FileCheck %s --check-prefix=PRINT -; RUN: llc %s --filetype=asm -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=asm -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" target triple = "dxil-pc-shadermodel6.6-compute" diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/max-64-uavs-array-valver1.6.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/max-64-uavs-array-valver1.6.ll index a397074650e46..c4bbfcf3c8ad3 100644 --- a/llvm/test/CodeGen/DirectX/ShaderFlags/max-64-uavs-array-valver1.6.ll +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/max-64-uavs-array-valver1.6.ll @@ -1,5 +1,5 @@ ; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s -; RUN: llc %s --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC ; This test makes sure that resource arrays sizes are accounted for when ; counting the number of UAVs for setting the shader flag '64 UAV slots' when diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/max-64-uavs.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/max-64-uavs.ll index 6ed267d05dae1..6ef00b1a21b62 100644 --- a/llvm/test/CodeGen/DirectX/ShaderFlags/max-64-uavs.ll +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/max-64-uavs.ll @@ -1,5 +1,5 @@ ; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s -; RUN: llc %s --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC ; This test makes sure that the shader flag '64 UAV slots' is set when there are ; more than 8 UAVs in the module. diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/overflow-uavs-array.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/overflow-uavs-array.ll index 6b08f62ff73ed..4f25c0cd69d3a 100644 --- a/llvm/test/CodeGen/DirectX/ShaderFlags/overflow-uavs-array.ll +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/overflow-uavs-array.ll @@ -1,5 +1,5 @@ ; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s -; RUN: llc %s --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=OBJ +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=OBJ ; This test makes sure that no overflow is happening when a resource is followed ; by an unbounded array of UAVs. diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/uavs-at-every-stage-lib-valver1.7.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/uavs-at-every-stage-lib-valver1.7.ll index ae2b35f80bde5..5ab0153af4d7c 100644 --- a/llvm/test/CodeGen/DirectX/ShaderFlags/uavs-at-every-stage-lib-valver1.7.ll +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/uavs-at-every-stage-lib-valver1.7.ll @@ -1,5 +1,5 @@ ; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s -; RUN: llc %s --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC ; This test ensures that a library shader with a UAV gets the module and ; shader feature flag UAVsAtEveryStage when the DXIL validator version is < 1.8 diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/unbounded-uavs-array.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/unbounded-uavs-array.ll index ba323d60fb530..6b7de13153df7 100644 --- a/llvm/test/CodeGen/DirectX/ShaderFlags/unbounded-uavs-array.ll +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/unbounded-uavs-array.ll @@ -1,5 +1,5 @@ ; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s -; RUN: llc %s --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=OBJ +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=OBJ ; This test makes sure that Max64UAVs is correctly set when using an ; unbounded array of UAVs; diff --git a/llvm/test/CodeGen/DirectX/llc-pipeline.ll b/llvm/test/CodeGen/DirectX/llc-pipeline.ll index 2474e08ac1549..021a12a4b9334 100644 --- a/llvm/test/CodeGen/DirectX/llc-pipeline.ll +++ b/llvm/test/CodeGen/DirectX/llc-pipeline.ll @@ -21,6 +21,7 @@ ; CHECK-NEXT: DXIL Memory Intrinsic Elimination ; CHECK-NEXT: DXIL CBuffer Access ; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: DXIL Remove Unused Resources ; CHECK-NEXT: DXIL Resource Access ; CHECK-NEXT: DXIL Intrinsic Expansion ; CHECK-NEXT: DXIL Data Scalarization diff --git a/llvm/test/CodeGen/DirectX/resources-in-unused-function.ll b/llvm/test/CodeGen/DirectX/resources-in-unused-function.ll new file mode 100644 index 0000000000000..3476832402517 --- /dev/null +++ b/llvm/test/CodeGen/DirectX/resources-in-unused-function.ll @@ -0,0 +1,81 @@ +; RUN: llc %s -stop-after=dxil-resource-access -o - | FileCheck %s +; RUN: llc %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-PRINT + +target triple = "dxil-pc-shadermodel6.6-compute" + +; This test makes sure that if resources are referenced in a function that +; is not used, then after the function is optimized out we also remove: +; - unused resource initialization calls +; - unused associated global resource variables +; - resource name strings + +%__cblayout_CB = type <{ i32, float }> +%"__cblayout_$Globals" = type <{ i32, float }> + [email protected] = internal global target("dx.CBuffer", %__cblayout_CB) poison +@c = external hidden local_unnamed_addr addrspace(2) global i32, align 4 [email protected] = private unnamed_addr constant [3 x i8] c"CB\00", align 1 +@_ZL3Buf.0 = internal unnamed_addr global target("dx.TypedBuffer", float, 1, 0, 0) poison, align 4 [email protected] = private unnamed_addr constant [4 x i8] c"Buf\00", align 1 +@a = external hidden local_unnamed_addr addrspace(2) global i32, align 4 +@"$Globals.cb" = internal global target("dx.CBuffer", %"__cblayout_$Globals") poison +@"$Globals.str" = private unnamed_addr constant [9 x i8] c"$Globals\00", align 1 + +; Unused function that references resources +define hidden void @_Z3foov() local_unnamed_addr #1 { +entry: + %0 = load i32, ptr addrspace(2) @a, align 4 + %1 = load i32, ptr addrspace(2) @c, align 4 + %add = add nsw i32 %1, %0 + %conv = sitofp i32 %add to float + %2 = load target("dx.TypedBuffer", float, 1, 0, 0), ptr @_ZL3Buf.0, align 4 + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_f32_1_0_0t.i32(target("dx.TypedBuffer", float, 1, 0, 0) %2, i32 0) + store float %conv, ptr %3, align 4 + ret void +} + +define void @main() { +entry: + +; CBuffers + %cb_handle0 = tail call target("dx.CBuffer", %__cblayout_CB) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_CBst(i32 0, i32 0, i32 1, i32 0, ptr nonnull @CB.str) + store target("dx.CBuffer", %__cblayout_CB) %cb_handle0, ptr @CB.cb, align 4 + + %uav_handle = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str) + store target("dx.TypedBuffer", float, 1, 0, 0) %uav_handle, ptr @_ZL3Buf.0, align 4 + + %cb_handle1 = tail call target("dx.CBuffer", %"__cblayout_$Globals") @"llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_$Globalsst"(i32 1, i32 0, i32 1, i32 0, ptr nonnull @"$Globals.str") + store target("dx.CBuffer", %"__cblayout_$Globals") %cb_handle1, ptr @"$Globals.cb", align 4 + + ret void +} + +!hlsl.cbs = !{!0, !1} + +; This is a list of all the constant buffers in the shader. +; The null values in the list indicate that the corresponding constant is not used in the shader. +!0 = distinct !{ptr @CB.cb, ptr addrspace(2) @c, null} +!1 = distinct !{ptr @"$Globals.cb", ptr addrspace(2) @a, null} + +; Make sure the resource globals and initialization calls are removed from the module +; CHECK-NOT: @CB.cb +; CHECK-NOT: @c +; CHECK-NOT: @CB.str +; CHECK-NOT: @_ZL3Buf.0 +; CHECK-NOT: @.str +; CHECK-NOT: @a +; CHECK-NOT: @"$Globals.cb" +; CHECK-NOT: @"$Globals.str" +; CHECK-NOT: call {{.*}} llvm.dx.resource.handlefrombinding +; CHECK-NOT: call {{.*}} llvm.dx.resource.handlefromimplicitbinding + +; Make sure the resource bindings table is empty +; CHECK-PRINT: ; Resource Bindings: +; CHECK-PRINT-NEXT: ; +; CHECK-PRINT-NEXT: ; Name Type Format Dim ID HLSL Bind Count +; CHECK-PRINT-NEXT: ; ------------------------------ ---------- ------- ----------- ------- -------------- --------- +; CHECK-PRINT-NEXT: ; +; CHECK-PRINT-NEXT: ; ModuleID +; CHECK-PRINT-NOT: ; Buf +; CHECK-PRINT-NOT: ; CB +; CHECK-PRINT-NOT: ; $Globals diff --git a/llvm/test/CodeGen/DirectX/unused-resources-impl-binding.ll b/llvm/test/CodeGen/DirectX/unused-resources-impl-binding.ll new file mode 100644 index 0000000000000..db74746f14434 --- /dev/null +++ b/llvm/test/CodeGen/DirectX/unused-resources-impl-binding.ll @@ -0,0 +1,109 @@ +; RUN: llc -stop-after=dxil-resource-access %s -o - | FileCheck %s +; RUN: llc %s -o - | FileCheck %s --check-prefix=CHECK-PRINT + +target triple = "dxil-pc-shadermodel6.6-compute" + +; This test makes sure that unused resource removed in dxil-resource-access +; pass do not affect implicit resource bindings. +; For constant buffers declared in the order {CB_Unused, CB, $Globals} where only +; CB is used, the CB should be the only resource that remains in the resource bindings +; table and should be assigned slot cb0. +; Similarly, for RWBuffer resources declared in order {Buf_Unused, Buf} where only Buf +; is used, the Buf resource should be the only UAV resource in the resource bindings table +; and should be assigned slot u0. + +%__cblayout_CB_Unused = type <{ i32 }> +%__cblayout_CB = type <{ i32 }> +%"__cblayout_$Globals" = type <{ i32 }> + +@CB_Unused.cb = internal global target("dx.CBuffer", %__cblayout_CB_Unused) poison +@CB_Unused.str = private unnamed_addr constant [10 x i8] c"CB_Unused\00", align 1 [email protected] = internal global target("dx.CBuffer", %__cblayout_CB) poison +@c = external hidden local_unnamed_addr addrspace(2) global i32, align 4 [email protected] = private unnamed_addr constant [3 x i8] c"CB\00", align 1 +@_ZL10Buf_Unused.0 = internal unnamed_addr global target("dx.TypedBuffer", i32, 1, 0, 1) poison, align 4 [email protected] = private unnamed_addr constant [11 x i8] c"Buf_Unused\00", align 1 +@_ZL3Buf.0 = internal unnamed_addr global target("dx.TypedBuffer", i32, 1, 0, 1) poison, align 4 [email protected] = private unnamed_addr constant [4 x i8] c"Buf\00", align 1 +@a = external hidden local_unnamed_addr addrspace(2) global i32, align 4 +@"$Globals.cb" = internal global target("dx.CBuffer", %"__cblayout_$Globals") poison +@"$Globals.str" = private unnamed_addr constant [9 x i8] c"$Globals\00", align 1 + +; Unused function foo +define hidden void @_Z3foov() { +entry: + ; Use constant @a from $Globals and buffer Buf_Unused + %0 = load i32, ptr addrspace(2) @a, align 4 + %1 = load target("dx.TypedBuffer", i32, 1, 0, 1), ptr @_ZL10Buf_Unused.0, align 4 + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %1, i32 0) + store i32 %0, ptr %2, align 4 + ret void +} + +define void @main() { +entry: + %cb_handle0 = tail call target("dx.CBuffer", %__cblayout_CB_Unused) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_CB_Unusedst(i32 0, i32 0, i32 1, i32 0, ptr nonnull @CB_Unused.str) + store target("dx.CBuffer", %__cblayout_CB_Unused) %cb_handle0, ptr @CB_Unused.cb, align 4 + + %cb_handle1 = tail call target("dx.CBuffer", %__cblayout_CB) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_CBst(i32 1, i32 0, i32 1, i32 0, ptr nonnull @CB.str) + store target("dx.CBuffer", %__cblayout_CB) %cb_handle1, ptr @CB.cb, align 4 + + %uav_handle0 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefromimplicitbinding.tdx.TypedBuffer_i32_1_0_1t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str) + store target("dx.TypedBuffer", i32, 1, 0, 1) %uav_handle0, ptr @_ZL10Buf_Unused.0, align 4 + + %uav_handle1 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefromimplicitbinding.tdx.TypedBuffer_i32_1_0_1t(i32 3, i32 0, i32 1, i32 0, ptr nonnull @.str.2) + store target("dx.TypedBuffer", i32, 1, 0, 1) %uav_handle1, ptr @_ZL3Buf.0, align 4 + + %cb_handle2 = tail call target("dx.CBuffer", %"__cblayout_$Globals") @"llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_$Globalsst"(i32 4, i32 0, i32 1, i32 0, ptr nonnull @"$Globals.str") + store target("dx.CBuffer", %"__cblayout_$Globals") %cb_handle2, ptr @"$Globals.cb", align 4 + + ; Use constant @c from CB and buffer Buf + %1 = load i32, ptr addrspace(2) @c, align 4 + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %uav_handle1, i32 0) + store i32 %1, ptr %2, align 4 + ret void +} + +!hlsl.cbs = !{!0, !1, !2} + +; This is a list of all the constant buffers in the shader. +; The null values in the list indicate that the corresponding constant is not used in the shader. +!0 = distinct !{ptr @CB_Unused.cb, null} +!1 = !{ptr @CB.cb, ptr addrspace(2) @c} +!2 = !{ptr @"$Globals.cb", ptr addrspace(2) @a} + +; Make sure the unused resource globals and initialization calls are removed from the module +; and the used ones are retained. +; CHECK-NOT: @CB_Unused.cb +; CHECK-NOT: @CB_Unused.str +; CHECK-NOT: @_ZL10Buf_Unused.0 +; CHECK-NOT: @.str +; CHECK-NOT: @"$Globals.cb" +; CHECK-NOT: @"$Globals.str" + +; CHECK: @CB.cb = internal global target("dx.CBuffer", %__cblayout_CB) poison +; CHECK: @CB.str = internal unnamed_addr constant [3 x i8] c"CB\00", align 1 +; CHECK: @_ZL3Buf.0 = internal unnamed_addr global target("dx.TypedBuffer", i32, 1, 0, 1) poison, align 4 +; CHECK: @.str.2 = internal unnamed_addr constant [4 x i8] c"Buf\00", align 1 + +; CHECK-NOT: {{.*}} call {{.*}} @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_CB_Unusedst({{.*}}, ptr nonnull @CB_unused.str) +; CHECK: call {{.*}} @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_CBst({{.*}}, ptr nonnull @CB.str) +; CHECK-NOT: call {{.*}} @"llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_$Globalsst"({{.*}}, ptr nonnull @"$Globals.str") + +; CHECK-NOT: call {{.*}} @llvm.dx.resource.handlefromimplicitbinding.tdx.TypedBuffer_i32_1_0_1t({{.*}}, ptr nonnull @.str) +; CHECK: call {{.*}} @llvm.dx.resource.handlefromimplicitbinding.tdx.TypedBuffer_i32_1_0_1t({{.*}}, ptr nonnull @.str.2) + +; Make sure the resource bindings table contain only the used resources with correct binding information. + +; CHECK-PRINT: ; Resource Bindings: +; CHECK-PRINT-NEXT: ; +; CHECK-PRINT-NEXT: ; Name Type Format Dim ID HLSL Bind Count +; CHECK-PRINT-NEXT: ; ------------------------------ ---------- ------- ----------- ------- -------------- --------- +; CHECK-PRINT-NEXT: ; Buf UAV i32 buf U0 u0 1 +; CHECK-PRINT-NEXT: ; CB cbuffer NA NA CB0 cb0 1 +; CHECK-PRINT-NEXT: ; +; CHECK-PRINT-NEXT: ; ModuleID + +; CHECK-PRINT-NOT: ; Buf_Unused +; CHECK-PRINT-NOT: ; CB_Unused +; CHECK-PRINT-NOT: ; $Globals diff --git a/llvm/test/CodeGen/DirectX/unused-resources.ll b/llvm/test/CodeGen/DirectX/unused-resources.ll new file mode 100644 index 0000000000000..b11638cec0f9a --- /dev/null +++ b/llvm/test/CodeGen/DirectX/unused-resources.ll @@ -0,0 +1,68 @@ +; RUN: opt -S -dxil-remove-unused-resources %s -o - | FileCheck %s +; RUN: llc %s -o - | FileCheck %s --check-prefix=CHECK-PRINT + +target triple = "dxil-pc-shadermodel6.6-compute" + +; This test makes sure that we remove: +; - unused resource initialization calls +; - unused cbuffers and associated global variables +; - resource name strings + +%__cblayout_CB = type <{ i32, float }> +%"__cblayout_$Globals" = type <{ i32, float }> + [email protected] = internal global target("dx.CBuffer", %__cblayout_CB) poison [email protected] = private unnamed_addr constant [3 x i8] c"CB\00", align 1 +@"$Globals.cb" = internal global target("dx.CBuffer", %"__cblayout_$Globals") poison +@"$Globals.str" = private unnamed_addr constant [9 x i8] c"$Globals\00", align 1 +@_ZL3Buf = internal unnamed_addr global target("dx.RawBuffer", i16, 1, 0) poison, align 4 [email protected] = private unnamed_addr constant [4 x i8] c"Buf\00", align 1 + +define void @main() { +entry: + +; CBuffers + %cb_handle0 = tail call target("dx.CBuffer", %__cblayout_CB) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_CBst(i32 0, i32 0, i32 1, i32 0, ptr nonnull @CB.str) + store target("dx.CBuffer", %__cblayout_CB) %cb_handle0, ptr @CB.cb, align 4 + + %cb_handle1 = tail call target("dx.CBuffer", %"__cblayout_$Globals") @"llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_$Globalsst"(i32 2, i32 0, i32 1, i32 0, ptr nonnull @"$Globals.str") + store target("dx.CBuffer", %"__cblayout_$Globals") %cb_handle1, ptr @"$Globals.cb", align 4 + +; SrvBuffer + %srv_handle = call target("dx.TypedBuffer", i64, 0, 0, 0) + @llvm.dx.resource.handlefrombinding(i32 5, i32 20, i32 1, i32 0, ptr null) + +; UavBuffer + %uav_handle = call target("dx.RawBuffer", i16, 1, 0) + @llvm.dx.resource.handlefrombinding(i32 0, i32 6, i32 1, i32 0, ptr @Buf.str) + store target("dx.RawBuffer", i16, 1, 0) %uav_handle, ptr @_ZL3Buf, align 4 + + ret void +} + +!hlsl.cbs = !{!0, !1} + +; This is a list of all the constant buffers in the shader. +; The null values in the list indicate that the corresponding constant is not used in the shader. +!0 = distinct !{ptr @CB.cb, null, null} +!1 = distinct !{ptr @"$Globals.cb", null, null} + +; CHECK-NOT: @CB.cb +; CHECK-NOT: @CB.str +; CHECK-NOT: @"$Globals.cb" +; CHECK-NOT: @"$Globals.str" +; CHECK-NOT: @_ZL3Buf +; CHECK-NOT: @Buf.str +; CHECK-NOT: call {{.*}} llvm.dx.resource.handlefrombinding +; CHECK-NOT: call {{.*}} llvm.dx.resource.handlefromimplicitbinding + +; Make sure the resource bindings table is empty +; CHECK-PRINT: ; Resource Bindings: +; CHECK-PRINT-NEXT: ; +; CHECK-PRINT-NEXT: ; Name Type Format Dim ID HLSL Bind Count +; CHECK-PRINT-NEXT: ; ------------------------------ ---------- ------- ----------- ------- -------------- --------- +; CHECK-PRINT-NEXT: ; +; CHECK-PRINT-NEXT: ; ModuleID +; CHECK-PRINT-NOT: ; Buf +; CHECK-PRINT-NOT: ; CB +; CHECK-PRINT-NOT: ; $Globals _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
