Skip to content

Commit 70d4b5d

Browse files
committed
[DirectX] Remove trivially dead functions at linkage finalize
Functions are not removed even when made internal by DXILFinalizeLinkage The removal code is called from alwaysinliner and globalopt, which are invoked too early to remove functions made internal by this pass. This adds a check similar to that in alwaysinliner that removes trivially dead functions after being marked internal. It refactors that code a bit to make it simpler including reversing what is stored in the work queue. Tests both the pass in isolation and the full inlining, linkage finalization, and function removal process. Fixes #106139
1 parent 13dd2fd commit 70d4b5d

File tree

3 files changed

+135
-8
lines changed

3 files changed

+135
-8
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %clang -target dxil-pc-shadermodel6.0-compute -S -o - %s | FileCheck %s
2+
// RUN: %clang -target dxil-pc-shadermodel6.3-library -S -o - %s | FileCheck %s
3+
4+
// Verify that internal linkage unused functions are removed
5+
6+
RWBuffer<unsigned> buf;
7+
8+
// Never called functions should be removed.
9+
// CHECK-NOT: define{{.*}}uncalledFor
10+
void uncalledFor() {
11+
buf[1] = 1;
12+
}
13+
14+
// Never called but exported functions should remain.
15+
// CHECK: define void @"?exported@@YAXXZ"()
16+
export void exported() {
17+
buf[1] = 1;
18+
}
19+
20+
// Never called but noinlined functions should remain.
21+
// CHECK: define internal void @"?noinlined@@YAXXZ"()
22+
__attribute__((noinline)) void noinlined() {
23+
buf[1] = 1;
24+
}
25+
26+
// Called functions marked noinline should remain.
27+
// CHECK: define internal void @"?calledAndNoinlined@@YAXXZ"()
28+
__attribute__((noinline)) void calledAndNoinlined() {
29+
buf[1] = 1;
30+
}
31+
32+
// Called functions that get inlined by default should be removed.
33+
// CHECK-NOT: define{{.*}}calledAndInlined
34+
void calledAndInlined() {
35+
buf[1] = 1;
36+
}
37+
38+
39+
// Entry point functions should remain.
40+
// CHECK: define{{.*}}main
41+
[numthreads(1,1,1)]
42+
[shader("compute")]
43+
void main() {
44+
calledAndInlined();
45+
calledAndNoinlined();
46+
buf[0] = 0;
47+
}

llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@
1919
using namespace llvm;
2020

2121
static bool finalizeLinkage(Module &M) {
22-
SmallPtrSet<Function *, 8> EntriesAndExports;
22+
SmallPtrSet<Function *, 8> Funcs;
2323

2424
// Find all entry points and export functions
2525
for (Function &EF : M.functions()) {
26-
if (!EF.hasFnAttribute("hlsl.shader") && !EF.hasFnAttribute("hlsl.export"))
26+
if (EF.hasFnAttribute("hlsl.shader") || EF.hasFnAttribute("hlsl.export"))
2727
continue;
28-
EntriesAndExports.insert(&EF);
28+
Funcs.insert(&EF);
2929
}
3030

31-
for (Function &F : M.functions()) {
32-
if (F.getLinkage() == GlobalValue::ExternalLinkage &&
33-
!EntriesAndExports.contains(&F)) {
34-
F.setLinkage(GlobalValue::InternalLinkage);
35-
}
31+
for (Function *F : Funcs) {
32+
if (F->getLinkage() == GlobalValue::ExternalLinkage)
33+
F->setLinkage(GlobalValue::InternalLinkage);
34+
if (F->hasFnAttribute(Attribute::AlwaysInline) && F->isDefTriviallyDead())
35+
M.getFunctionList().erase(F);
3636
}
3737

3838
return false;
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
; RUN: opt -S -dxil-finalize-linkage -mtriple=dxil-unknown-shadermodel6.5-compute %s | FileCheck %s
2+
; RUN: llc %s --filetype=asm -o - | FileCheck %s
3+
4+
target triple = "dxilv1.5-pc-shadermodel6.5-compute"
5+
6+
; Confirm that DXILFinalizeLinkage will remove functions that have compatible
7+
; linkage and are not called from anywhere. This should be any function that
8+
; is not explicitly marked noinline or export and is not an entry point.
9+
10+
; Not called nor marked with any linking or inlining attributes.
11+
; CHECK-NOT: define {{.*}}doNothingNothing
12+
define void @"?doNothingNothing@@YAXXZ"() #0 {
13+
entry:
14+
ret void
15+
}
16+
17+
; Marked internal, this should be removed.
18+
; CHECK-NOT: define {{.*}}doNothingInternally
19+
define internal void @"?doNothingInternally@@YAXXZ"() #0 {
20+
entry:
21+
ret void
22+
}
23+
24+
; Marked external, which should become internal and be removed.
25+
; CHECK-NOT: define {{.*}}doNothingExternally
26+
define external void @"?doNothingExternally@@YAXXZ"() #0 {
27+
entry:
28+
ret void
29+
}
30+
31+
; Not called nor marked with any linking or inlining attributes.
32+
; CHECK: define internal void @"?doSomethingSomething@@YAXXZ"() #0
33+
define void @"?doSomethingSomething@@YAXXZ"() #0 {
34+
entry:
35+
ret void
36+
}
37+
38+
; Marked internal, this should be removed.
39+
; CHECK: define internal void @"?doSomethingInternally@@YAXXZ"() #0
40+
define internal void @"?doSomethingInternally@@YAXXZ"() #0 {
41+
entry:
42+
ret void
43+
}
44+
45+
; Marked external, which should become internal and be removed.
46+
; CHECK: define internal void @"?doSomethingExternally@@YAXXZ"() #0
47+
define external void @"?doSomethingExternally@@YAXXZ"() #0 {
48+
entry:
49+
ret void
50+
}
51+
52+
; Lacks alwaysinline attribute. Should remain.
53+
; CHECK: define internal void @"?doNothingDefault@@YAXXZ"() #1
54+
define void @"?doNothingDefault@@YAXXZ"() #1 {
55+
entry:
56+
ret void
57+
}
58+
59+
; Has noinline attribute. Should remain.
60+
; CHECK: define {{.*}}doNothingNoinline
61+
define void @"?doNothingNoinline@@YAXXZ"() #2 {
62+
entry:
63+
ret void
64+
}
65+
66+
; Entry point function should stay.
67+
; CHECK: define void @main() #3
68+
define void @main() #3 {
69+
entry:
70+
call void @"?doSomethingSomething@@YAXXZ"() #4
71+
call void @"?doSomethingInternally@@YAXXZ"() #4
72+
call void @"?doSomethingExternally@@YAXXZ"() #4
73+
ret void
74+
}
75+
76+
attributes #0 = { alwaysinline convergent norecurse nounwind }
77+
attributes #1 = { convergent norecurse nounwind }
78+
attributes #2 = { convergent noinline norecurse nounwind }
79+
attributes #3 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
80+
attributes #4 = { convergent }

0 commit comments

Comments
 (0)