• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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