1 // Copyright (c) 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <string>
16 #include <vector>
17
18 #include "gmock/gmock.h"
19 #include "test/opt/assembly_builder.h"
20 #include "test/opt/pass_fixture.h"
21 #include "test/opt/pass_utils.h"
22
23 namespace spvtools {
24 namespace opt {
25 namespace {
26
27 using ::testing::HasSubstr;
28 using EliminateDeadFunctionsBasicTest = PassTest<::testing::Test>;
29
TEST_F(EliminateDeadFunctionsBasicTest,BasicDeleteDeadFunction)30 TEST_F(EliminateDeadFunctionsBasicTest, BasicDeleteDeadFunction) {
31 // The function Dead should be removed because it is never called.
32 const std::vector<const char*> common_code = {
33 // clang-format off
34 "OpCapability Shader",
35 "OpMemoryModel Logical GLSL450",
36 "OpEntryPoint Fragment %main \"main\"",
37 "OpName %main \"main\"",
38 "OpName %Live \"Live\"",
39 "%void = OpTypeVoid",
40 "%7 = OpTypeFunction %void",
41 "%main = OpFunction %void None %7",
42 "%15 = OpLabel",
43 "%16 = OpFunctionCall %void %Live",
44 "%17 = OpFunctionCall %void %Live",
45 "OpReturn",
46 "OpFunctionEnd",
47 "%Live = OpFunction %void None %7",
48 "%20 = OpLabel",
49 "OpReturn",
50 "OpFunctionEnd"
51 // clang-format on
52 };
53
54 const std::vector<const char*> dead_function = {
55 // clang-format off
56 "%Dead = OpFunction %void None %7",
57 "%19 = OpLabel",
58 "OpReturn",
59 "OpFunctionEnd",
60 // clang-format on
61 };
62
63 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
64 SinglePassRunAndCheck<EliminateDeadFunctionsPass>(
65 JoinAllInsts(Concat(common_code, dead_function)),
66 JoinAllInsts(common_code), /* skip_nop = */ true);
67 }
68
TEST_F(EliminateDeadFunctionsBasicTest,BasicKeepLiveFunction)69 TEST_F(EliminateDeadFunctionsBasicTest, BasicKeepLiveFunction) {
70 // Everything is reachable from an entry point, so no functions should be
71 // deleted.
72 const std::vector<const char*> text = {
73 // clang-format off
74 "OpCapability Shader",
75 "OpMemoryModel Logical GLSL450",
76 "OpEntryPoint Fragment %main \"main\"",
77 "OpName %main \"main\"",
78 "OpName %Live1 \"Live1\"",
79 "OpName %Live2 \"Live2\"",
80 "%void = OpTypeVoid",
81 "%7 = OpTypeFunction %void",
82 "%main = OpFunction %void None %7",
83 "%15 = OpLabel",
84 "%16 = OpFunctionCall %void %Live2",
85 "%17 = OpFunctionCall %void %Live1",
86 "OpReturn",
87 "OpFunctionEnd",
88 "%Live1 = OpFunction %void None %7",
89 "%19 = OpLabel",
90 "OpReturn",
91 "OpFunctionEnd",
92 "%Live2 = OpFunction %void None %7",
93 "%20 = OpLabel",
94 "OpReturn",
95 "OpFunctionEnd"
96 // clang-format on
97 };
98
99 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
100 std::string assembly = JoinAllInsts(text);
101 auto result = SinglePassRunAndDisassemble<EliminateDeadFunctionsPass>(
102 assembly, /* skip_nop = */ true, /* do_validation = */ false);
103 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
104 EXPECT_EQ(assembly, std::get<0>(result));
105 }
106
TEST_F(EliminateDeadFunctionsBasicTest,BasicKeepExportFunctions)107 TEST_F(EliminateDeadFunctionsBasicTest, BasicKeepExportFunctions) {
108 // All functions are reachable. In particular, ExportedFunc and Constant are
109 // reachable because ExportedFunc is exported. Nothing should be removed.
110 const std::vector<const char*> text = {
111 // clang-format off
112 "OpCapability Shader",
113 "OpCapability Linkage",
114 "OpMemoryModel Logical GLSL450",
115 "OpEntryPoint Fragment %main \"main\"",
116 "OpName %main \"main\"",
117 "OpName %ExportedFunc \"ExportedFunc\"",
118 "OpName %Live \"Live\"",
119 "OpDecorate %ExportedFunc LinkageAttributes \"ExportedFunc\" Export",
120 "%void = OpTypeVoid",
121 "%7 = OpTypeFunction %void",
122 "%main = OpFunction %void None %7",
123 "%15 = OpLabel",
124 "OpReturn",
125 "OpFunctionEnd",
126 "%ExportedFunc = OpFunction %void None %7",
127 "%19 = OpLabel",
128 "%16 = OpFunctionCall %void %Live",
129 "OpReturn",
130 "OpFunctionEnd",
131 "%Live = OpFunction %void None %7",
132 "%20 = OpLabel",
133 "OpReturn",
134 "OpFunctionEnd"
135 // clang-format on
136 };
137
138 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
139 std::string assembly = JoinAllInsts(text);
140 auto result = SinglePassRunAndDisassemble<EliminateDeadFunctionsPass>(
141 assembly, /* skip_nop = */ true, /* do_validation = */ false);
142 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
143 EXPECT_EQ(assembly, std::get<0>(result));
144 }
145
TEST_F(EliminateDeadFunctionsBasicTest,BasicRemoveDecorationsAndNames)146 TEST_F(EliminateDeadFunctionsBasicTest, BasicRemoveDecorationsAndNames) {
147 // We want to remove the names and decorations associated with results that
148 // are removed. This test will check for that.
149 const std::string text = R"(
150 OpCapability Shader
151 OpMemoryModel Logical GLSL450
152 OpEntryPoint Vertex %main "main"
153 OpName %main "main"
154 OpName %Dead "Dead"
155 OpName %x "x"
156 OpName %y "y"
157 OpName %z "z"
158 OpDecorate %x RelaxedPrecision
159 OpDecorate %y RelaxedPrecision
160 OpDecorate %z RelaxedPrecision
161 OpDecorate %6 RelaxedPrecision
162 OpDecorate %7 RelaxedPrecision
163 OpDecorate %8 RelaxedPrecision
164 %void = OpTypeVoid
165 %10 = OpTypeFunction %void
166 %float = OpTypeFloat 32
167 %_ptr_Function_float = OpTypePointer Function %float
168 %float_1 = OpConstant %float 1
169 %main = OpFunction %void None %10
170 %14 = OpLabel
171 OpReturn
172 OpFunctionEnd
173 %Dead = OpFunction %void None %10
174 %15 = OpLabel
175 %x = OpVariable %_ptr_Function_float Function
176 %y = OpVariable %_ptr_Function_float Function
177 %z = OpVariable %_ptr_Function_float Function
178 OpStore %x %float_1
179 OpStore %y %float_1
180 %6 = OpLoad %float %x
181 %7 = OpLoad %float %y
182 %8 = OpFAdd %float %6 %7
183 OpStore %z %8
184 OpReturn
185 OpFunctionEnd)";
186
187 const std::string expected_output = R"(OpCapability Shader
188 OpMemoryModel Logical GLSL450
189 OpEntryPoint Vertex %main "main"
190 OpName %main "main"
191 %void = OpTypeVoid
192 %10 = OpTypeFunction %void
193 %float = OpTypeFloat 32
194 %_ptr_Function_float = OpTypePointer Function %float
195 %float_1 = OpConstant %float 1
196 %main = OpFunction %void None %10
197 %14 = OpLabel
198 OpReturn
199 OpFunctionEnd
200 )";
201
202 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
203 SinglePassRunAndCheck<EliminateDeadFunctionsPass>(text, expected_output,
204 /* skip_nop = */ true);
205 }
206
TEST_F(EliminateDeadFunctionsBasicTest,DebugRemoveFunctionFromDebugFunction)207 TEST_F(EliminateDeadFunctionsBasicTest, DebugRemoveFunctionFromDebugFunction) {
208 // We want to remove id of OpFunction from DebugFunction.
209 const std::string text = R"(OpCapability Shader
210 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
211 OpMemoryModel Logical GLSL450
212 OpEntryPoint Fragment %2 "main" %3 %4
213 OpExecutionMode %2 OriginUpperLeft
214 %5 = OpString "ps.hlsl"
215 OpSource HLSL 600 %5 "float4 foo() {
216 return 1;
217 }
218 float4 main(float4 color : COLOR) : SV_TARGET {
219 return foo() + color;
220 }
221 "
222 %6 = OpString "float"
223 %7 = OpString "main"
224 %8 = OpString "foo"
225 ; CHECK: [[foo:%\d+]] = OpString "foo"
226 OpDecorate %3 Location 0
227 OpDecorate %4 Location 0
228 %uint = OpTypeInt 32 0
229 %uint_32 = OpConstant %uint 32
230 %float = OpTypeFloat 32
231 %float_1 = OpConstant %float 1
232 %v4float = OpTypeVector %float 4
233 %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
234 %_ptr_Input_v4float = OpTypePointer Input %v4float
235 %_ptr_Output_v4float = OpTypePointer Output %v4float
236 %void = OpTypeVoid
237 %18 = OpTypeFunction %void
238 %19 = OpTypeFunction %v4float
239 %3 = OpVariable %_ptr_Input_v4float Input
240 %4 = OpVariable %_ptr_Output_v4float Output
241 %_ptr_Function_v4float = OpTypePointer Function %v4float
242 ; CHECK: [[info_none:%\d+]] = OpExtInst %void %1 DebugInfoNone
243 %20 = OpExtInst %void %1 DebugSource %5
244 %21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL
245 %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float
246 %23 = OpExtInst %void %1 DebugTypeVector %22 4
247 %24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23
248 %25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23
249 %26 = OpExtInst %void %1 DebugFunction %7 %24 %20 4 1 %21 %7 FlagIsProtected|FlagIsPrivate 4 %2
250 %27 = OpExtInst %void %1 DebugFunction %8 %25 %20 1 1 %21 %8 FlagIsProtected|FlagIsPrivate 1 %28
251 ; CHECK: {{%\d+}} = OpExtInst %void %1 DebugFunction [[foo]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[info_none]]
252 %29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %27
253 %40 = OpExtInst %void %1 DebugInlinedAt 4 %26
254 %2 = OpFunction %void None %18
255 %30 = OpLabel
256 %39 = OpVariable %_ptr_Function_v4float Function
257 %41 = OpExtInst %void %1 DebugScope %27 %40
258 OpStore %39 %14
259 %32 = OpLoad %v4float %39
260 %42 = OpExtInst %void %1 DebugScope %26
261 %33 = OpLoad %v4float %3
262 %34 = OpFAdd %v4float %32 %33
263 OpStore %4 %34
264 %43 = OpExtInst %void %1 DebugNoScope
265 OpReturn
266 OpFunctionEnd
267 %28 = OpFunction %v4float None %19
268 %36 = OpLabel
269 OpReturnValue %14
270 OpFunctionEnd
271 )";
272
273 SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, false);
274 }
275
TEST_F(EliminateDeadFunctionsBasicTest,DebugRemoveFunctionUsingExistingDebugInfoNone)276 TEST_F(EliminateDeadFunctionsBasicTest,
277 DebugRemoveFunctionUsingExistingDebugInfoNone) {
278 // We want to remove id of OpFunction from DebugFunction.
279 const std::string text = R"(OpCapability Shader
280 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
281 OpMemoryModel Logical GLSL450
282 OpEntryPoint Fragment %2 "main" %3 %4
283 OpExecutionMode %2 OriginUpperLeft
284 %5 = OpString "ps.hlsl"
285 OpSource HLSL 600 %5 "float4 foo() {
286 return 1;
287 }
288 float4 main(float4 color : COLOR) : SV_TARGET {
289 return foo() + color;
290 }
291 "
292 %6 = OpString "float"
293 %7 = OpString "main"
294 %8 = OpString "foo"
295 ; CHECK: [[foo:%\d+]] = OpString "foo"
296 OpDecorate %3 Location 0
297 OpDecorate %4 Location 0
298 %uint = OpTypeInt 32 0
299 %uint_32 = OpConstant %uint 32
300 %float = OpTypeFloat 32
301 %float_1 = OpConstant %float 1
302 %v4float = OpTypeVector %float 4
303 %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
304 %_ptr_Input_v4float = OpTypePointer Input %v4float
305 %_ptr_Output_v4float = OpTypePointer Output %v4float
306 %void = OpTypeVoid
307 %18 = OpTypeFunction %void
308 %19 = OpTypeFunction %v4float
309 %3 = OpVariable %_ptr_Input_v4float Input
310 %4 = OpVariable %_ptr_Output_v4float Output
311 %_ptr_Function_v4float = OpTypePointer Function %v4float
312 ; CHECK: [[info_none:%\d+]] = OpExtInst %void %1 DebugInfoNone
313 %20 = OpExtInst %void %1 DebugSource %5
314 %21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL
315 %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float
316 %23 = OpExtInst %void %1 DebugTypeVector %22 4
317 %24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23
318 %25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23
319 %26 = OpExtInst %void %1 DebugFunction %7 %24 %20 4 1 %21 %7 FlagIsProtected|FlagIsPrivate 4 %2
320 %27 = OpExtInst %void %1 DebugFunction %8 %25 %20 1 1 %21 %8 FlagIsProtected|FlagIsPrivate 1 %28
321 ; CHECK: {{%\d+}} = OpExtInst %void %1 DebugFunction [[foo]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[info_none]]
322 %29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %27
323 %35 = OpExtInst %void %1 DebugInfoNone
324 %40 = OpExtInst %void %1 DebugInlinedAt 4 %26
325 %2 = OpFunction %void None %18
326 %30 = OpLabel
327 %39 = OpVariable %_ptr_Function_v4float Function
328 %41 = OpExtInst %void %1 DebugScope %27 %40
329 OpStore %39 %14
330 %32 = OpLoad %v4float %39
331 %42 = OpExtInst %void %1 DebugScope %26
332 %33 = OpLoad %v4float %3
333 %34 = OpFAdd %v4float %32 %33
334 OpStore %4 %34
335 %43 = OpExtInst %void %1 DebugNoScope
336 OpReturn
337 OpFunctionEnd
338 %28 = OpFunction %v4float None %19
339 %36 = OpLabel
340 OpReturnValue %14
341 OpFunctionEnd
342 )";
343
344 SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, false);
345 }
346
TEST_F(EliminateDeadFunctionsBasicTest,NonSemanticInfoPersists)347 TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoPersists) {
348 const std::string text = R"(
349 ; CHECK: [[import:%\w+]] = OpExtInstImport
350 ; CHECK: [[void:%\w+]] = OpTypeVoid
351 ; CHECK-NOT: OpExtInst [[void]] [[import]] 1
352 ; CHECK: OpExtInst [[void]] [[import]] 2
353 OpCapability Shader
354 OpExtension "SPV_KHR_non_semantic_info"
355 %ext = OpExtInstImport "NonSemantic.Test"
356 OpMemoryModel Logical GLSL450
357 OpEntryPoint GLCompute %main "main"
358 OpExecutionMode %main LocalSize 1 1 1
359 %void = OpTypeVoid
360 %void_fn = OpTypeFunction %void
361 %main = OpFunction %void None %void_fn
362 %entry = OpLabel
363 OpReturn
364 OpFunctionEnd
365 %foo = OpFunction %void None %void_fn
366 %foo_entry = OpLabel
367 %non_semantic1 = OpExtInst %void %ext 1
368 OpReturn
369 OpFunctionEnd
370 %non_semantic2 = OpExtInst %void %ext 2
371 )";
372
373 SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
374 }
375
TEST_F(EliminateDeadFunctionsBasicTest,NonSemanticInfoRemoveDependent)376 TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoRemoveDependent) {
377 const std::string text = R"(
378 ; CHECK: [[import:%\w+]] = OpExtInstImport
379 ; CHECK: [[void:%\w+]] = OpTypeVoid
380 ; CHECK-NOT: OpExtInst [[void]] [[import]] 1
381 ; CHECK-NOT: OpExtInst [[void]] [[import]] 2
382 ; CHECK: OpExtInst [[void]] [[import]] 3
383 OpCapability Shader
384 OpExtension "SPV_KHR_non_semantic_info"
385 %ext = OpExtInstImport "NonSemantic.Test"
386 OpMemoryModel Logical GLSL450
387 OpEntryPoint GLCompute %main "main"
388 OpExecutionMode %main LocalSize 1 1 1
389 %void = OpTypeVoid
390 %void_fn = OpTypeFunction %void
391 %main = OpFunction %void None %void_fn
392 %entry = OpLabel
393 OpReturn
394 OpFunctionEnd
395 %foo = OpFunction %void None %void_fn
396 %foo_entry = OpLabel
397 %non_semantic1 = OpExtInst %void %ext 1
398 OpReturn
399 OpFunctionEnd
400 %non_semantic2 = OpExtInst %void %ext 2 %foo
401 %non_semantic3 = OpExtInst %void %ext 3
402 )";
403
404 SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
405 }
406
TEST_F(EliminateDeadFunctionsBasicTest,NonSemanticInfoRemoveDependentTree)407 TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoRemoveDependentTree) {
408 const std::string text = R"(
409 ; CHECK: [[import:%\w+]] = OpExtInstImport
410 ; CHECK: [[void:%\w+]] = OpTypeVoid
411 ; CHECK-NOT: OpExtInst [[void]] [[import]] 1
412 ; CHECK-NOT: OpExtInst [[void]] [[import]] 2
413 ; CHECK: OpExtInst [[void]] [[import]] 3
414 ; CHECK-NOT: OpExtInst [[void]] [[import]] 4
415 ; CHECK-NOT: OpExtInst [[void]] [[import]] 5
416 OpCapability Shader
417 OpExtension "SPV_KHR_non_semantic_info"
418 %ext = OpExtInstImport "NonSemantic.Test"
419 OpMemoryModel Logical GLSL450
420 OpEntryPoint GLCompute %main "main"
421 OpExecutionMode %main LocalSize 1 1 1
422 %void = OpTypeVoid
423 %void_fn = OpTypeFunction %void
424 %main = OpFunction %void None %void_fn
425 %entry = OpLabel
426 OpReturn
427 OpFunctionEnd
428 %foo = OpFunction %void None %void_fn
429 %foo_entry = OpLabel
430 %non_semantic1 = OpExtInst %void %ext 1
431 OpReturn
432 OpFunctionEnd
433 %non_semantic2 = OpExtInst %void %ext 2 %foo
434 %non_semantic3 = OpExtInst %void %ext 3
435 %non_semantic4 = OpExtInst %void %ext 4 %non_semantic2
436 %non_semantic5 = OpExtInst %void %ext 5 %non_semantic4
437 )";
438
439 SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
440 }
441
442 } // namespace
443 } // namespace opt
444 } // namespace spvtools
445