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 
TEST_F(EliminateDeadFunctionsBasicTest,NonSemanticInfoRemoveDebugPrintf)442 TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoRemoveDebugPrintf) {
443   const std::string text = R"(
444 ; CHECK-NOT: %foo_ = OpFunction %void None % 3
445 ; CHECK-NOT: % 7 = OpLabel
446 ; CHECK-NOT: %c = OpVariable %_ptr_Function_v4float Function
447 ; CHECK-NOT: % 22 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0
448 ; CHECK-NOT: % 23 = OpLoad % 13 % 22
449 ; CHECK-NOT: % 27 = OpImageSampleExplicitLod %v4float % 23 % 26 Lod %float_0
450 ; CHECK-NOT: OpStore %c % 27
451 ; CHECK-NOT: % 31 = OpAccessChain %_ptr_Function_float %c %uint_0
452 ; CHECK-NOT: % 32 = OpLoad %float %31
453 ; CHECK-NOT: % 34 = OpExtInst %void %33 1 % 28 % 32
454 OpCapability RayTracingKHR
455 OpExtension "SPV_KHR_non_semantic_info"
456 OpExtension "SPV_KHR_ray_tracing"
457 %1 = OpExtInstImport "GLSL.std.450"
458 %33 = OpExtInstImport "NonSemantic.DebugPrintf"
459 OpMemoryModel Logical GLSL450
460 OpEntryPoint ClosestHitNV %main "main" %samplers
461 %28 = OpString "%f"
462 OpSource GLSL 460
463 OpSourceExtension "GL_EXT_debug_printf"
464 OpName %main "main"
465 OpName %foo_ "foo("
466 OpName %c "c"
467 OpName %samplers "samplers"
468 OpDecorate %samplers DescriptorSet 0
469 OpDecorate %samplers Binding 0
470 %void = OpTypeVoid
471 %3 = OpTypeFunction %void
472 %float = OpTypeFloat 32
473 %v4float = OpTypeVector %float 4
474 %_ptr_Function_v4float = OpTypePointer Function %v4float
475 %12 = OpTypeImage %float 3D 0 0 0 1 Unknown
476 %13 = OpTypeSampledImage %12
477 %uint = OpTypeInt 32 0
478 %uint_1 = OpConstant %uint 1
479 %_arr_13_uint_1 = OpTypeArray %13 %uint_1
480 %_ptr_UniformConstant__arr_13_uint_1 = OpTypePointer UniformConstant %_arr_13_uint_1
481 %samplers = OpVariable %_ptr_UniformConstant__arr_13_uint_1 UniformConstant
482 %int = OpTypeInt 32 1
483 %int_0 = OpConstant %int 0
484 %_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
485 %v3float = OpTypeVector %float 3
486 %float_0 = OpConstant %float 0
487 %26 = OpConstantComposite %v3float %float_0 %float_0 %float_0
488 %uint_0 = OpConstant %uint 0
489 %_ptr_Function_float = OpTypePointer Function %float
490 %main = OpFunction %void None %3
491 %5 = OpLabel
492 %36 = OpVariable %_ptr_Function_v4float Function
493 %38 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0
494 %39 = OpLoad %13 %38
495 %40 = OpImageSampleExplicitLod %v4float %39 %26 Lod %float_0
496 OpStore %36 %40
497 %41 = OpAccessChain %_ptr_Function_float %36 %uint_0
498 %42 = OpLoad %float %41
499 %43 = OpExtInst %void %33 1 %28 %42
500 OpReturn
501 OpFunctionEnd
502 %foo_ = OpFunction %void None %3
503 %7 = OpLabel
504 %c = OpVariable %_ptr_Function_v4float Function
505 %22 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0
506 %23 = OpLoad %13 %22
507 %27 = OpImageSampleExplicitLod %v4float %23 %26 Lod %float_0
508 OpStore %c %27
509 %31 = OpAccessChain %_ptr_Function_float %c %uint_0
510 %32 = OpLoad %float %31
511 %34 = OpExtInst %void %33 1 %28 %32
512 OpReturn
513 OpFunctionEnd
514 )";
515 
516   SetTargetEnv(SPV_ENV_VULKAN_1_2);
517   SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
518 }
519 
TEST_F(EliminateDeadFunctionsBasicTest,DependentNonSemanticChain)520 TEST_F(EliminateDeadFunctionsBasicTest, DependentNonSemanticChain) {
521   const std::string text = R"(
522 ; CHECK: OpEntryPoint GLCompute [[main:%\w+]]
523 ; CHECK: [[main]] = OpFunction
524 ; CHECK-NOT: = OpFunction
525 ; CHECK: [[ext1:%\w+]] = OpExtInst %void {{%\w+}} 1 [[main]]
526 ; CHECK: [[ext2:%\w+]] = OpExtInst %void {{%\w+}} 2 [[ext1]]
527 ; CHECK: [[ext3:%\w+]] = OpExtInst %void {{%\w+}} 3 [[ext1]] [[ext2]]
528 OpCapability Shader
529 OpExtension "SPV_KHR_non_semantic_info"
530 %1 = OpExtInstImport "NonSemantic.Test"
531 OpMemoryModel Logical GLSL450
532 OpEntryPoint GLCompute %main "main"
533 OpExecutionMode %main LocalSize 1 1 1
534 %void = OpTypeVoid
535 %void_fn = OpTypeFunction %void
536 %main = OpFunction %void None %void_fn
537 %main_entry = OpLabel
538 OpReturn
539 OpFunctionEnd
540 %dead = OpFunction %void None %void_fn
541 %dead_entry = OpLabel
542 OpReturn
543 OpFunctionEnd
544 %2 = OpExtInst %void %1 1 %main
545 %3 = OpExtInst %void %1 2 %2
546 %4 = OpExtInst %void %1 3 %2 %3
547 )";
548 
549   SetTargetEnv(SPV_ENV_VULKAN_1_0);
550   SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
551 }
552 
553 }  // namespace
554 }  // namespace opt
555 }  // namespace spvtools
556