Skip to content

Commit ab75180

Browse files
authored
[DirectX] Remove trivially dead functions at linkage finalize (llvm#106146)
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 int he work queue. Tests both the pass in isolation and the full i0nlining, linkage finalization and function removal steps. Fixes llvm#106139
1 parent ed3d051 commit ab75180

13 files changed

+425
-32
lines changed

llvm/lib/Target/DirectX/DXILFinalizeLinkage.cpp

Lines changed: 9 additions & 9 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

24-
// Find all entry points and export functions
24+
// Collect non-entry and non-exported functions to set to internal linkage.
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->isDefTriviallyDead())
35+
M.getFunctionList().erase(F);
3636
}
3737

3838
return false;

llvm/test/CodeGen/DirectX/ShaderFlags/double-extensions.ll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ target triple = "dxil-pc-shadermodel6.7-library"
99
; CHECK-NEXT: ; Double-precision extensions for 11.1
1010
; CHECK-NEXT: ; Note: extra DXIL module flags:
1111
; CHECK-NEXT: {{^;$}}
12-
define double @div(double %a, double %b) {
12+
define double @div(double %a, double %b) #0 {
1313
%res = fdiv double %a, %b
1414
ret double %res
1515
}
1616

17+
attributes #0 = { convergent norecurse nounwind "hlsl.export"}
1718

1819
; DXC: - Name: SFI0
1920
; DXC-NEXT: Size: 8

llvm/test/CodeGen/DirectX/ShaderFlags/doubles.ll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ target triple = "dxil-pc-shadermodel6.7-library"
99
; CHECK-NEXT: ; Note: extra DXIL module flags:
1010
; CHECK-NEXT: {{^;$}}
1111

12-
define double @add(double %a, double %b) {
12+
define double @add(double %a, double %b) #0 {
1313
%sum = fadd double %a, %b
1414
ret double %sum
1515
}
1616

17+
attributes #0 = { convergent norecurse nounwind "hlsl.export"}
18+
1719
; DXC: - Name: SFI0
1820
; DXC-NEXT: Size: 8
1921
; DXC-NEXT: Flags:

llvm/test/CodeGen/DirectX/conflicting-bitcast-insert.ll

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
; RUN: llc --filetype=asm %s -o - | FileCheck %s
22
target triple = "dxil-unknown-shadermodel6.7-library"
33

4-
define i64 @test(ptr %p) {
4+
define i64 @test(ptr %p) #0 {
55
store i32 0, ptr %p
66
%v = load i64, ptr %p
77
ret i64 %v
88
}
99

10-
; CHECK: define internal i64 @test(ptr %p) {
10+
; CHECK: define i64 @test(ptr %p) #0 {
1111
; CHECK-NEXT: %1 = bitcast ptr %p to ptr
1212
; CHECK-NEXT: store i32 0, ptr %1, align 4
1313
; CHECK-NEXT: %2 = bitcast ptr %p to ptr
1414
; CHECK-NEXT: %3 = load i64, ptr %2, align 8
1515

16-
define i64 @testGEP(ptr %p) {
16+
define i64 @testGEP(ptr %p) #0 {
1717
%ptr = getelementptr i32, ptr %p, i32 4
1818
%val = load i64, ptr %p
1919
ret i64 %val
2020
}
2121

22-
; CHECK: define internal i64 @testGEP(ptr %p) {
22+
attributes #0 = { convergent norecurse nounwind "hlsl.export"}
23+
24+
; CHECK: define i64 @testGEP(ptr %p) #0 {
2325
; CHECK-NEXT: %1 = bitcast ptr %p to ptr
2426
; CHECK-NEXT: %ptr = getelementptr i32, ptr %1, i32 4
2527
; CHECK-NEXT: %2 = bitcast ptr %p to ptr
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
; RUN: opt -S -dxil-finalize-linkage -mtriple=dxil-unknown-shadermodel6.5-library %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 export and is not an entry point.
9+
10+
; Has no specified inlining/linking behavior and is uncalled, this should be removed.
11+
; CHECK-NOT: define {{.*}}doNothingUncalled
12+
define void @"?doNothingUncalled@@YAXXZ"() #2 {
13+
entry:
14+
ret void
15+
}
16+
17+
; Alwaysinline and uncalled, this should be removed.
18+
; CHECK-NOT: define {{.*}}doAlwaysInlineUncalled
19+
define void @"?doAlwaysInlineUncalled@@YAXXZ"() #0 {
20+
entry:
21+
ret void
22+
}
23+
24+
; Noinline and uncalled, this should be removed.
25+
; CHECK-NOT: define {{.*}}doNoinlineUncalled
26+
define void @"?doNoinlineUncalled@@YAXXZ"() #4 {
27+
entry:
28+
ret void
29+
}
30+
31+
; No inlining attribute, internal, and uncalled; this should be removed.
32+
; CHECK-NOT: define {{.*}}doInternalUncalled
33+
define internal void @"?doInternalUncalled@@YAXXZ"() #2 {
34+
entry:
35+
ret void
36+
}
37+
38+
; Alwaysinline, internal, and uncalled; this should be removed.
39+
; CHECK-NOT: define {{.*}}doAlwaysInlineInternalUncalled
40+
define internal void @"?doAlwaysInlineInternalUncalled@@YAXXZ"() #0 {
41+
entry:
42+
ret void
43+
}
44+
45+
; Noinline, internal, and uncalled; this should be removed.
46+
; CHECK-NOT: define {{.*}}doNoinlineInternalUncalled
47+
define internal void @"?doNoinlineInternalUncalled@@YAXXZ"() #4 {
48+
entry:
49+
ret void
50+
}
51+
52+
; Marked external and uncalled, this should become internal and be removed.
53+
; CHECK-NOT: define {{.*}}doExternalUncalled
54+
define external void @"?doExternalUncalled@@YAXXZ"() #2 {
55+
entry:
56+
ret void
57+
}
58+
59+
; Alwaysinline, external and uncalled, this should become internal and be removed.
60+
; CHECK-NOT: define {{.*}}doAlwaysInlineExternalUncalled
61+
define external void @"?doAlwaysInlineExternalUncalled@@YAXXZ"() #0 {
62+
entry:
63+
ret void
64+
}
65+
66+
; Noinline, external and uncalled, this should become internal and be removed.
67+
; CHECK-NOT: define {{.*}}doNoinlineExternalUncalled
68+
define external void @"?doNoinlineExternalUncalled@@YAXXZ"() #4 {
69+
entry:
70+
ret void
71+
}
72+
73+
; No inlining attribute and called, this should stay.
74+
; CHECK: define {{.*}}doNothingCalled
75+
define void @"?doNothingCalled@@YAXXZ"() #2 {
76+
entry:
77+
ret void
78+
}
79+
80+
; Alwaysinline and called, this should stay.
81+
; CHECK: define {{.*}}doAlwaysInlineCalled
82+
define void @"?doAlwaysInlineCalled@@YAXXZ"() #0 {
83+
entry:
84+
ret void
85+
}
86+
87+
; Noinline and called, this should stay.
88+
; CHECK: define {{.*}}doNoinlineCalled
89+
define void @"?doNoinlineCalled@@YAXXZ"() #4 {
90+
entry:
91+
ret void
92+
}
93+
94+
; No inlining attribute, internal, and called; this should stay.
95+
; CHECK: define {{.*}}doInternalCalled
96+
define internal void @"?doInternalCalled@@YAXXZ"() #2 {
97+
entry:
98+
ret void
99+
}
100+
101+
; Alwaysinline, internal, and called; this should stay.
102+
; CHECK: define {{.*}}doAlwaysInlineInternalCalled
103+
define internal void @"?doAlwaysInlineInternalCalled@@YAXXZ"() #0 {
104+
entry:
105+
ret void
106+
}
107+
108+
; Noinline, internal, and called; this should stay.
109+
; CHECK: define {{.*}}doNoinlineInternalCalled
110+
define internal void @"?doNoinlineInternalCalled@@YAXXZ"() #4 {
111+
entry:
112+
ret void
113+
}
114+
115+
; Marked external and called, this should become internal and stay.
116+
; CHECK: define {{.*}}doExternalCalled
117+
define external void @"?doExternalCalled@@YAXXZ"() #2 {
118+
entry:
119+
ret void
120+
}
121+
122+
; Always inlined, external and called, this should become internal and stay.
123+
; CHECK: define {{.*}}doAlwaysInlineExternalCalled
124+
define external void @"?doAlwaysInlineExternalCalled@@YAXXZ"() #0 {
125+
entry:
126+
ret void
127+
}
128+
129+
; Noinline, external and called, this should become internal and stay.
130+
; CHECK: define {{.*}}doNoinlineExternalCalled
131+
define external void @"?doNoinlineExternalCalled@@YAXXZ"() #4 {
132+
entry:
133+
ret void
134+
}
135+
136+
; No inlining attribute and exported, this should stay.
137+
; CHECK: define {{.*}}doNothingExported
138+
define void @"?doNothingExported@@YAXXZ"() #3 {
139+
entry:
140+
ret void
141+
}
142+
143+
; Alwaysinline and exported, this should stay.
144+
; CHECK: define {{.*}}doAlwaysInlineExported
145+
define void @"?doAlwaysInlineExported@@YAXXZ"() #1 {
146+
entry:
147+
ret void
148+
}
149+
150+
; Noinline attribute and exported, this should stay.
151+
; CHECK: define {{.*}}doNoinlineExported
152+
define void @"?doNoinlineExported@@YAXXZ"() #5 {
153+
entry:
154+
ret void
155+
}
156+
157+
; No inlining attribute, internal, and exported; this should stay.
158+
; CHECK: define {{.*}}doInternalExported
159+
define internal void @"?doInternalExported@@YAXXZ"() #3 {
160+
entry:
161+
ret void
162+
}
163+
164+
; Alwaysinline, internal, and exported; this should stay.
165+
; CHECK: define {{.*}}doAlwaysInlineInternalExported
166+
define internal void @"?doAlwaysInlineInternalExported@@YAXXZ"() #1 {
167+
entry:
168+
ret void
169+
}
170+
171+
; Noinline, internal, and exported; this should stay.
172+
; CHECK: define {{.*}}doNoinlineInternalExported
173+
define internal void @"?doNoinlineInternalExported@@YAXXZ"() #5 {
174+
entry:
175+
ret void
176+
}
177+
178+
; Marked external and exported, this should stay.
179+
; CHECK: define {{.*}}doExternalExported
180+
define external void @"?doExternalExported@@YAXXZ"() #3 {
181+
entry:
182+
ret void
183+
}
184+
185+
; Alwaysinline, external and exported, this should stay.
186+
; CHECK: define {{.*}}doAlwaysInlineExternalExported
187+
define external void @"?doAlwaysInlineExternalExported@@YAXXZ"() #1 {
188+
entry:
189+
ret void
190+
}
191+
192+
; Noinline, external and exported, this should stay.
193+
; CHECK: define {{.*}}doNoinlineExternalExported
194+
define external void @"?doNoinlineExternalExported@@YAXXZ"() #5 {
195+
entry:
196+
ret void
197+
}
198+
199+
; Entry point function, this should stay.
200+
; CHECK: define void @main()
201+
define void @main() #6 {
202+
entry:
203+
call void @"?doNothingCalled@@YAXXZ"() #7
204+
call void @"?doAlwaysInlineCalled@@YAXXZ"() #7
205+
call void @"?doNoinlineCalled@@YAXXZ"() #7
206+
call void @"?doInternalCalled@@YAXXZ"() #7
207+
call void @"?doAlwaysInlineInternalCalled@@YAXXZ"() #7
208+
call void @"?doNoinlineInternalCalled@@YAXXZ"() #7
209+
call void @"?doExternalCalled@@YAXXZ"() #7
210+
call void @"?doAlwaysInlineExternalCalled@@YAXXZ"() #7
211+
call void @"?doNoinlineExternalCalled@@YAXXZ"() #7
212+
ret void
213+
}
214+
215+
attributes #0 = { alwaysinline convergent norecurse nounwind }
216+
attributes #1 = { alwaysinline convergent norecurse nounwind "hlsl.export"}
217+
attributes #2 = { convergent norecurse nounwind }
218+
attributes #3 = { convergent norecurse nounwind "hlsl.export"}
219+
attributes #4 = { convergent noinline norecurse nounwind }
220+
attributes #5 = { convergent noinline norecurse nounwind "hlsl.export"}
221+
attributes #6 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
222+
attributes #7 = { convergent }

0 commit comments

Comments
 (0)