[llvm] [DirectX] Add GlobalDCE pass after finalize linkage pass in DirectX backend (PR #151071)

Kaitlin Peng via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 7 11:23:13 PDT 2025


https://github.com/kmpeng updated https://github.com/llvm/llvm-project/pull/151071

>From bced13de3806a509729a50ea87f2e6bc71b30c7d Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Fri, 28 Mar 2025 10:38:45 -0400
Subject: [PATCH 1/4] [LegacyPM] Restore the GlobalDCE Legacy pass and add it
 to the DirectX backend

---
 llvm/include/llvm/InitializePasses.h          |  1 +
 llvm/include/llvm/LinkAllPasses.h             |  2 ++
 llvm/include/llvm/Transforms/IPO/GlobalDCE.h  |  2 ++
 llvm/lib/Target/DirectX/CMakeLists.txt        |  1 +
 .../Target/DirectX/DirectXTargetMachine.cpp   |  3 ++
 llvm/lib/Transforms/IPO/GlobalDCE.cpp         | 32 +++++++++++++++++++
 llvm/test/CodeGen/DirectX/llc-pipeline.ll     |  1 +
 7 files changed, 42 insertions(+)

diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index 31801daa126ad..0e48d80936270 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -127,6 +127,7 @@ LLVM_ABI void initializeGCEmptyBasicBlocksPass(PassRegistry &);
 LLVM_ABI void initializeGCMachineCodeAnalysisPass(PassRegistry &);
 LLVM_ABI void initializeGCModuleInfoPass(PassRegistry &);
 LLVM_ABI void initializeGVNLegacyPassPass(PassRegistry &);
+LLVM_ABI void initializeGlobalDCELegacyPassPass(PassRegistry &);
 LLVM_ABI void initializeGlobalMergeFuncPassWrapperPass(PassRegistry &);
 LLVM_ABI void initializeGlobalMergePass(PassRegistry &);
 LLVM_ABI void initializeGlobalsAAWrapperPassPass(PassRegistry &);
diff --git a/llvm/include/llvm/LinkAllPasses.h b/llvm/include/llvm/LinkAllPasses.h
index f82a43967e67a..7b33ec7f09124 100644
--- a/llvm/include/llvm/LinkAllPasses.h
+++ b/llvm/include/llvm/LinkAllPasses.h
@@ -38,6 +38,7 @@
 #include "llvm/Support/Valgrind.h"
 #include "llvm/Transforms/IPO.h"
 #include "llvm/Transforms/IPO/AlwaysInliner.h"
+#include "llvm/Transforms/IPO/GlobalDCE.h"
 #include "llvm/Transforms/InstCombine/InstCombine.h"
 #include "llvm/Transforms/ObjCARC.h"
 #include "llvm/Transforms/Scalar.h"
@@ -83,6 +84,7 @@ struct ForcePassLinking {
     (void)llvm::createDomOnlyViewerWrapperPassPass();
     (void)llvm::createDomViewerWrapperPassPass();
     (void)llvm::createAlwaysInlinerLegacyPass();
+    (void)llvm::createGlobalDCEPass();
     (void)llvm::createGlobalMergeFuncPass();
     (void)llvm::createGlobalsAAWrapperPass();
     (void)llvm::createInstSimplifyLegacyPass();
diff --git a/llvm/include/llvm/Transforms/IPO/GlobalDCE.h b/llvm/include/llvm/Transforms/IPO/GlobalDCE.h
index f8f775b8ea96e..55dff64889270 100644
--- a/llvm/include/llvm/Transforms/IPO/GlobalDCE.h
+++ b/llvm/include/llvm/Transforms/IPO/GlobalDCE.h
@@ -32,6 +32,7 @@ class GlobalVariable;
 class Metadata;
 class Module;
 class Value;
+class ModulePass;
 
 /// Pass to remove unused function declarations.
 class GlobalDCEPass : public PassInfoMixin<GlobalDCEPass> {
@@ -80,6 +81,7 @@ class GlobalDCEPass : public PassInfoMixin<GlobalDCEPass> {
   void ComputeDependencies(Value *V, SmallPtrSetImpl<GlobalValue *> &U);
 };
 
+ModulePass *createGlobalDCEPass();
 }
 
 #endif // LLVM_TRANSFORMS_IPO_GLOBALDCE_H
diff --git a/llvm/lib/Target/DirectX/CMakeLists.txt b/llvm/lib/Target/DirectX/CMakeLists.txt
index c7c09caf43667..8100f941c8d94 100644
--- a/llvm/lib/Target/DirectX/CMakeLists.txt
+++ b/llvm/lib/Target/DirectX/CMakeLists.txt
@@ -49,6 +49,7 @@ add_llvm_target(DirectXCodeGen
   DirectXInfo
   DirectXPointerTypeAnalysis
   FrontendHLSL
+  IPO
   MC
   ScalarOpts
   SelectionDAG
diff --git a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
index 84751d2db2266..4601200f2c897 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
@@ -45,6 +45,7 @@
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Target/TargetLoweringObjectFile.h"
+#include "llvm/Transforms/IPO/GlobalDCE.h"
 #include "llvm/Transforms/Scalar/Scalarizer.h"
 #include <optional>
 
@@ -62,6 +63,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeDirectXTarget() {
   initializeEmbedDXILPassPass(*PR);
   initializeWriteDXILPassPass(*PR);
   initializeDXContainerGlobalsPass(*PR);
+  initializeGlobalDCELegacyPassPass(*PR);
   initializeDXILOpLoweringLegacyPass(*PR);
   initializeDXILResourceAccessLegacyPass(*PR);
   initializeDXILResourceImplicitBindingLegacyPass(*PR);
@@ -103,6 +105,7 @@ class DirectXPassConfig : public TargetPassConfig {
   FunctionPass *createTargetRegisterAllocator(bool) override { return nullptr; }
   void addCodeGenPrepare() override {
     addPass(createDXILFinalizeLinkageLegacyPass());
+    addPass(createGlobalDCEPass());
     addPass(createDXILResourceAccessLegacyPass());
     addPass(createDXILIntrinsicExpansionLegacyPass());
     addPass(createDXILCBufferAccessLegacyPass());
diff --git a/llvm/lib/Transforms/IPO/GlobalDCE.cpp b/llvm/lib/Transforms/IPO/GlobalDCE.cpp
index 45fb1f5212b70..3812032488035 100644
--- a/llvm/lib/Transforms/IPO/GlobalDCE.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalDCE.cpp
@@ -21,6 +21,8 @@
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Module.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Transforms/IPO.h"
 #include "llvm/Transforms/Utils/CtorUtils.h"
@@ -30,6 +32,36 @@ using namespace llvm;
 
 #define DEBUG_TYPE "globaldce"
 
+namespace {
+class GlobalDCELegacyPass : public ModulePass {
+public:
+  static char ID; // Pass identification, replacement for typeid
+  GlobalDCELegacyPass() : ModulePass(ID) {
+    initializeGlobalDCELegacyPassPass(*PassRegistry::getPassRegistry());
+  }
+  bool runOnModule(Module &M) override {
+    if (skipModule(M))
+      return false;
+    // Note: GlobalDCEPass does not use MAM. That
+    // means we can get away with init and pass
+    // as arg.
+    ModuleAnalysisManager MAM;
+    auto PA = Impl.run(M, MAM);
+    return !PA.areAllPreserved();
+  }
+
+private:
+  GlobalDCEPass Impl;
+};
+} // namespace
+
+char GlobalDCELegacyPass::ID = 0;
+INITIALIZE_PASS(GlobalDCELegacyPass, "globaldce", "Dead Global Elimination",
+                false, false)
+
+// Public interface to the GlobalDCEPass.
+ModulePass *llvm::createGlobalDCEPass() { return new GlobalDCELegacyPass(); }
+
 static cl::opt<bool>
     ClEnableVFE("enable-vfe", cl::Hidden, cl::init(true),
                 cl::desc("Enable virtual function elimination"));
diff --git a/llvm/test/CodeGen/DirectX/llc-pipeline.ll b/llvm/test/CodeGen/DirectX/llc-pipeline.ll
index 151603a7161c5..a3a214338536f 100644
--- a/llvm/test/CodeGen/DirectX/llc-pipeline.ll
+++ b/llvm/test/CodeGen/DirectX/llc-pipeline.ll
@@ -14,6 +14,7 @@
 
 ; CHECK-NEXT: ModulePass Manager
 ; CHECK-NEXT:   DXIL Finalize Linkage
+; CHECK-NEXT:   Dead Global Elimination
 ; CHECK-NEXT:   FunctionPass Manager
 ; CHECK-NEXT:     DXIL Resource Access
 ; CHECK-NEXT:   DXIL Intrinsic Expansion

>From 3bca2c85444f257320e124f1edbd94c0c3c8e0d4 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Mon, 28 Jul 2025 16:36:34 -0400
Subject: [PATCH 2/4] Make the dxil-finalize-linkage pass usable using the new
 pass manager flag syntax

---
 llvm/lib/Target/DirectX/DirectXPassRegistry.def  | 1 +
 llvm/lib/Target/DirectX/DirectXTargetMachine.cpp | 1 +
 llvm/test/CodeGen/DirectX/finalize_linkage.ll    | 2 +-
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/DirectX/DirectXPassRegistry.def b/llvm/lib/Target/DirectX/DirectXPassRegistry.def
index d5069541642b5..b4b48a166800e 100644
--- a/llvm/lib/Target/DirectX/DirectXPassRegistry.def
+++ b/llvm/lib/Target/DirectX/DirectXPassRegistry.def
@@ -24,6 +24,7 @@ MODULE_ANALYSIS("dxil-root-signature-analysis", dxil::RootSignatureAnalysis())
 #define MODULE_PASS(NAME, CREATE_PASS)
 #endif
 MODULE_PASS("dxil-cbuffer-access", DXILCBufferAccess())
+MODULE_PASS("dxil-finalize-linkage", DXILFinalizeLinkage())
 MODULE_PASS("dxil-data-scalarization", DXILDataScalarization())
 MODULE_PASS("dxil-flatten-arrays", DXILFlattenArrays())
 MODULE_PASS("dxil-intrinsic-expansion", DXILIntrinsicExpansion())
diff --git a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
index 4601200f2c897..d7609d596e444 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
@@ -14,6 +14,7 @@
 #include "DirectXTargetMachine.h"
 #include "DXILCBufferAccess.h"
 #include "DXILDataScalarization.h"
+#include "DXILFinalizeLinkage.h"
 #include "DXILFlattenArrays.h"
 #include "DXILForwardHandleAccesses.h"
 #include "DXILIntrinsicExpansion.h"
diff --git a/llvm/test/CodeGen/DirectX/finalize_linkage.ll b/llvm/test/CodeGen/DirectX/finalize_linkage.ll
index dc1140f1c9160..ac97a62ef1160 100644
--- a/llvm/test/CodeGen/DirectX/finalize_linkage.ll
+++ b/llvm/test/CodeGen/DirectX/finalize_linkage.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S -dxil-finalize-linkage -mtriple=dxil-unknown-shadermodel6.5-compute %s | FileCheck %s
+; RUN: opt -S -passes='dxil-finalize-linkage,globaldce' -mtriple=dxil-unknown-shadermodel6.5-compute %s | FileCheck %s
 ; RUN: llc %s --filetype=asm -o - | FileCheck %s --check-prefixes=CHECK-LLC
 
 target triple = "dxilv1.5-pc-shadermodel6.5-compute"

>From 08da645a3eb7a448fb748e4b2ce8ce6721ccdb75 Mon Sep 17 00:00:00 2001
From: kmpeng <kaitlinpeng at microsoft.com>
Date: Wed, 16 Jul 2025 13:33:28 -0700
Subject: [PATCH 3/4] convert external globals with no usage to internal
 linkage to be removed by globaldce, fix tests

---
 .../Target/DirectX/DXILFinalizeLinkage.cpp    |  8 ++++----
 llvm/test/CodeGen/DirectX/finalize_linkage.ll | 20 ++++++++++++++++++-
 llvm/test/CodeGen/DirectX/scalar-data.ll      |  2 +-
 .../dxil-dis/opaque-value_as_metadata.ll      |  1 +
 4 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp b/llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp
index 5f331dbd2d826..13e3408815bba 100644
--- a/llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp
+++ b/llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp
@@ -20,13 +20,13 @@ using namespace llvm;
 static bool finalizeLinkage(Module &M) {
   bool MadeChange = false;
 
-  // Convert private global variables to internal linkage.
-  for (GlobalVariable &GV : M.globals()) {
-    if (GV.hasPrivateLinkage()) {
+  // Convert private globals and external globals with no usage to internal
+  // linkage.
+  for (GlobalVariable &GV : M.globals())
+    if (GV.hasPrivateLinkage() || (GV.hasExternalLinkage() && GV.use_empty())) {
       GV.setLinkage(GlobalValue::InternalLinkage);
       MadeChange = true;
     }
-  }
 
   SmallVector<Function *> Funcs;
 
diff --git a/llvm/test/CodeGen/DirectX/finalize_linkage.ll b/llvm/test/CodeGen/DirectX/finalize_linkage.ll
index ac97a62ef1160..0e45257ba1824 100644
--- a/llvm/test/CodeGen/DirectX/finalize_linkage.ll
+++ b/llvm/test/CodeGen/DirectX/finalize_linkage.ll
@@ -4,7 +4,14 @@
 target triple = "dxilv1.5-pc-shadermodel6.5-compute"
 
 ; DXILFinalizeLinkage changes linkage of all functions that are hidden to
-; internal, and converts private global variables to internal linkage.
+; internal, converts private global variables to internal linkage, and removes
+; unused global variables.
+
+; CHECK-NOT: @aTile
+ at aTile = hidden addrspace(3) global [4 x [1 x i32]] zeroinitializer, align 4
+
+; CHECK-NOT: @bTile
+ at bTile = hidden addrspace(3) global [1 x <1 x i32>] zeroinitializer, align 4
 
 ; CHECK: @switch.table = internal unnamed_addr constant [4 x i32]
 @switch.table = private unnamed_addr constant [4 x i32] [i32 1, i32 257, i32 65793, i32 16843009], align 4
@@ -27,6 +34,17 @@ target triple = "dxilv1.5-pc-shadermodel6.5-compute"
 ; CHECK: @hidden_var = hidden global i32
 @hidden_var = hidden global i32 1, align 4
 
+define void @anchor_function() #0 {
+entry:
+  %0 = load i32, ptr @switch.table, align 4
+  %1 = load [3 x float], ptr @private_array, align 4
+  %2 = load i32, ptr @private_var, align 4
+  %3 = load i32, ptr @internal_var, align 4
+  %4 = load i32, ptr @external_var, align 4
+  %5 = load i32, ptr @hidden_var, align 4
+  ret void
+}
+
 ; CHECK-NOT: define internal void @"?f1@@YAXXZ"()
 define void @"?f1@@YAXXZ"() #0 {
 entry:
diff --git a/llvm/test/CodeGen/DirectX/scalar-data.ll b/llvm/test/CodeGen/DirectX/scalar-data.ll
index 4861a0890f136..d9c8df9bc169c 100644
--- a/llvm/test/CodeGen/DirectX/scalar-data.ll
+++ b/llvm/test/CodeGen/DirectX/scalar-data.ll
@@ -1,4 +1,4 @@
-; RUN: llc %s -mtriple=dxil-pc-shadermodel6.3-library --filetype=asm -o - | FileCheck %s
+; RUN: opt -S -passes='dxil-data-scalarization,dxil-flatten-arrays' -mtriple=dxil-unknown-shadermodel6.5-compute %s | FileCheck %s
 
 ; Make sure we don't touch arrays without vectors and that can recurse and flatten multiple-dimension arrays of vectors
 
diff --git a/llvm/test/tools/dxil-dis/opaque-value_as_metadata.ll b/llvm/test/tools/dxil-dis/opaque-value_as_metadata.ll
index 165d5d2e520df..c53bb2127ebcc 100644
--- a/llvm/test/tools/dxil-dis/opaque-value_as_metadata.ll
+++ b/llvm/test/tools/dxil-dis/opaque-value_as_metadata.ll
@@ -7,6 +7,7 @@ target triple = "dxil-unknown-shadermodel6.7-library"
 @CBV = external constant %"$Globals"
 
 define void @main() #0 {
+  %1 = load float, ptr @CBV, align 4
   ret void
 }
 

>From d1b498e5f28c07f20b42b9cb2f863b13557a9a66 Mon Sep 17 00:00:00 2001
From: kmpeng <kaitlinpeng at microsoft.com>
Date: Thu, 7 Aug 2025 11:21:38 -0700
Subject: [PATCH 4/4] update finalize_linkage test, update GlobalDCE comment

---
 llvm/lib/Transforms/IPO/GlobalDCE.cpp         |  5 ++---
 llvm/test/CodeGen/DirectX/finalize_linkage.ll | 22 +++++++++++--------
 2 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/GlobalDCE.cpp b/llvm/lib/Transforms/IPO/GlobalDCE.cpp
index 3812032488035..c576fbc92f709 100644
--- a/llvm/lib/Transforms/IPO/GlobalDCE.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalDCE.cpp
@@ -42,9 +42,8 @@ class GlobalDCELegacyPass : public ModulePass {
   bool runOnModule(Module &M) override {
     if (skipModule(M))
       return false;
-    // Note: GlobalDCEPass does not use MAM. That
-    // means we can get away with init and pass
-    // as arg.
+    // Note: GlobalDCEPass does not use any analyses, so we're safe to call the
+    // new-pm style pass with a default-initialized analysis manager here
     ModuleAnalysisManager MAM;
     auto PA = Impl.run(M, MAM);
     return !PA.areAllPreserved();
diff --git a/llvm/test/CodeGen/DirectX/finalize_linkage.ll b/llvm/test/CodeGen/DirectX/finalize_linkage.ll
index 0e45257ba1824..f6e54cf907a64 100644
--- a/llvm/test/CodeGen/DirectX/finalize_linkage.ll
+++ b/llvm/test/CodeGen/DirectX/finalize_linkage.ll
@@ -1,17 +1,11 @@
-; RUN: opt -S -passes='dxil-finalize-linkage,globaldce' -mtriple=dxil-unknown-shadermodel6.5-compute %s | FileCheck %s
+; RUN: opt -S -dxil-finalize-linkage -mtriple=dxil-unknown-shadermodel6.5-compute %s | FileCheck %s
 ; RUN: llc %s --filetype=asm -o - | FileCheck %s --check-prefixes=CHECK-LLC
 
 target triple = "dxilv1.5-pc-shadermodel6.5-compute"
 
 ; DXILFinalizeLinkage changes linkage of all functions that are hidden to
-; internal, converts private global variables to internal linkage, and removes
-; unused global variables.
-
-; CHECK-NOT: @aTile
- at aTile = hidden addrspace(3) global [4 x [1 x i32]] zeroinitializer, align 4
-
-; CHECK-NOT: @bTile
- at bTile = hidden addrspace(3) global [1 x <1 x i32>] zeroinitializer, align 4
+; internal, converts private globals to internal linkage, and converts external globals 
+; with no usage to internal linkage.
 
 ; CHECK: @switch.table = internal unnamed_addr constant [4 x i32]
 @switch.table = private unnamed_addr constant [4 x i32] [i32 1, i32 257, i32 65793, i32 16843009], align 4
@@ -34,6 +28,16 @@ target triple = "dxilv1.5-pc-shadermodel6.5-compute"
 ; CHECK: @hidden_var = hidden global i32
 @hidden_var = hidden global i32 1, align 4
 
+; Running the whole pipeline should remove unused global variables
+
+; CHECK: @aTile = internal addrspace(3) global
+; CHECK-LLC-NOT: @aTile
+ at aTile = hidden addrspace(3) global [4 x [1 x i32]] zeroinitializer, align 4
+
+; CHECK: @bTile = internal addrspace(3) global
+; CHECK-LLC-NOT: @bTile
+ at bTile = hidden addrspace(3) global [1 x [1 x i32]] zeroinitializer, align 4
+
 define void @anchor_function() #0 {
 entry:
   %0 = load i32, ptr @switch.table, align 4



More information about the llvm-commits mailing list