1 // Copyright (c) 2016 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 <algorithm>
16 #include <string>
17 #include <unordered_set>
18 #include <vector>
19 
20 #include "test/opt/assembly_builder.h"
21 #include "test/opt/pass_fixture.h"
22 #include "test/opt/pass_utils.h"
23 
24 namespace spvtools {
25 namespace opt {
26 namespace {
27 
28 using EliminateDeadConstantBasicTest = PassTest<::testing::Test>;
29 
TEST_F(EliminateDeadConstantBasicTest, BasicAllDeadConstants)30 TEST_F(EliminateDeadConstantBasicTest, BasicAllDeadConstants) {
31   const std::vector<const char*> text = {
32       // clang-format off
33                "OpCapability Shader",
34                "OpCapability Float64",
35           "%1 = OpExtInstImport \"GLSL.std.450\"",
36                "OpMemoryModel Logical GLSL450",
37                "OpEntryPoint Vertex %main \"main\"",
38                "OpName %main \"main\"",
39        "%void = OpTypeVoid",
40           "%4 = OpTypeFunction %void",
41        "%bool = OpTypeBool",
42        "%true = OpConstantTrue %bool",
43       "%false = OpConstantFalse %bool",
44         "%int = OpTypeInt 32 1",
45           "%9 = OpConstant %int 1",
46        "%uint = OpTypeInt 32 0",
47          "%11 = OpConstant %uint 2",
48       "%float = OpTypeFloat 32",
49          "%13 = OpConstant %float 3.1415",
50      "%double = OpTypeFloat 64",
51          "%15 = OpConstant %double 3.14159265358979",
52        "%main = OpFunction %void None %4",
53          "%16 = OpLabel",
54                "OpReturn",
55                "OpFunctionEnd",
56       // clang-format on
57   };
58   // None of the above constants is ever used, so all of them should be
59   // eliminated.
60   const char* const_decl_opcodes[] = {
61       " OpConstantTrue ",
62       " OpConstantFalse ",
63       " OpConstant ",
64   };
65   // Skip lines that have any one of const_decl_opcodes.
66   const std::string expected_disassembly =
67       SelectiveJoin(text, [&const_decl_opcodes](const char* line) {
68         return std::any_of(
69             std::begin(const_decl_opcodes), std::end(const_decl_opcodes),
70             [&line](const char* const_decl_op) {
71               return std::string(line).find(const_decl_op) != std::string::npos;
72             });
73       });
74 
75   SinglePassRunAndCheck<EliminateDeadConstantPass>(
76       JoinAllInsts(text), expected_disassembly, /* skip_nop = */ true);
77 }
78 
TEST_F(EliminateDeadConstantBasicTest, BasicNoneDeadConstants)79 TEST_F(EliminateDeadConstantBasicTest, BasicNoneDeadConstants) {
80   const std::vector<const char*> text = {
81       // clang-format off
82                 "OpCapability Shader",
83                 "OpCapability Float64",
84            "%1 = OpExtInstImport \"GLSL.std.450\"",
85                 "OpMemoryModel Logical GLSL450",
86                 "OpEntryPoint Vertex %main \"main\"",
87                 "OpName %main \"main\"",
88                 "OpName %btv \"btv\"",
89                 "OpName %bfv \"bfv\"",
90                 "OpName %iv \"iv\"",
91                 "OpName %uv \"uv\"",
92                 "OpName %fv \"fv\"",
93                 "OpName %dv \"dv\"",
94         "%void = OpTypeVoid",
95           "%10 = OpTypeFunction %void",
96         "%bool = OpTypeBool",
97  "%_ptr_Function_bool = OpTypePointer Function %bool",
98         "%true = OpConstantTrue %bool",
99        "%false = OpConstantFalse %bool",
100          "%int = OpTypeInt 32 1",
101  "%_ptr_Function_int = OpTypePointer Function %int",
102        "%int_1 = OpConstant %int 1",
103         "%uint = OpTypeInt 32 0",
104  "%_ptr_Function_uint = OpTypePointer Function %uint",
105       "%uint_2 = OpConstant %uint 2",
106        "%float = OpTypeFloat 32",
107  "%_ptr_Function_float = OpTypePointer Function %float",
108   "%float_3_1415 = OpConstant %float 3.1415",
109       "%double = OpTypeFloat 64",
110  "%_ptr_Function_double = OpTypePointer Function %double",
111  "%double_3_14159265358979 = OpConstant %double 3.14159265358979",
112         "%main = OpFunction %void None %10",
113           "%27 = OpLabel",
114          "%btv = OpVariable %_ptr_Function_bool Function",
115          "%bfv = OpVariable %_ptr_Function_bool Function",
116           "%iv = OpVariable %_ptr_Function_int Function",
117           "%uv = OpVariable %_ptr_Function_uint Function",
118           "%fv = OpVariable %_ptr_Function_float Function",
119           "%dv = OpVariable %_ptr_Function_double Function",
120                 "OpStore %btv %true",
121                 "OpStore %bfv %false",
122                 "OpStore %iv %int_1",
123                 "OpStore %uv %uint_2",
124                 "OpStore %fv %float_3_1415",
125                 "OpStore %dv %double_3_14159265358979",
126                 "OpReturn",
127                 "OpFunctionEnd",
128       // clang-format on
129   };
130   // All constants are used, so none of them should be eliminated.
131   SinglePassRunAndCheck<EliminateDeadConstantPass>(
132       JoinAllInsts(text), JoinAllInsts(text), /* skip_nop = */ true);
133 }
134 
135 struct EliminateDeadConstantTestCase {
136   // Type declarations and constants that should be kept.
137   std::vector<std::string> used_consts;
138   // Instructions that refer to constants, this is added to create uses for
139   // some constants so they won't be treated as dead constants.
140   std::vector<std::string> main_insts;
141   // Dead constants that should be removed.
142   std::vector<std::string> dead_consts;
143 };
144 
145 // All types that are potentially required in EliminateDeadConstantTest.
146 const std::vector<std::string> CommonTypes = {
147     // clang-format off
148     // scalar types
149     "%bool = OpTypeBool",
150     "%uint = OpTypeInt 32 0",
151     "%int = OpTypeInt 32 1",
152     "%float = OpTypeFloat 32",
153     "%double = OpTypeFloat 64",
154     // vector types
155     "%v2bool = OpTypeVector %bool 2",
156     "%v2uint = OpTypeVector %uint 2",
157     "%v2int = OpTypeVector %int 2",
158     "%v3int = OpTypeVector %int 3",
159     "%v4int = OpTypeVector %int 4",
160     "%v2float = OpTypeVector %float 2",
161     "%v3float = OpTypeVector %float 3",
162     "%v2double = OpTypeVector %double 2",
163     // variable pointer types
164     "%_pf_bool = OpTypePointer Function %bool",
165     "%_pf_uint = OpTypePointer Function %uint",
166     "%_pf_int = OpTypePointer Function %int",
167     "%_pf_float = OpTypePointer Function %float",
168     "%_pf_double = OpTypePointer Function %double",
169     "%_pf_v2int = OpTypePointer Function %v2int",
170     "%_pf_v3int = OpTypePointer Function %v3int",
171     "%_pf_v2float = OpTypePointer Function %v2float",
172     "%_pf_v3float = OpTypePointer Function %v3float",
173     "%_pf_v2double = OpTypePointer Function %v2double",
174     // struct types
175     "%inner_struct = OpTypeStruct %bool %int %float %double",
176     "%outer_struct = OpTypeStruct %inner_struct %int %double",
177     "%flat_struct = OpTypeStruct %bool %int %float %double",
178     // clang-format on
179 };
180 
181 using EliminateDeadConstantTest =
182     PassTest<::testing::TestWithParam<EliminateDeadConstantTestCase>>;
183 
TEST_P(EliminateDeadConstantTest, Custom)184 TEST_P(EliminateDeadConstantTest, Custom) {
185   auto& tc = GetParam();
186   AssemblyBuilder builder;
187   builder.AppendTypesConstantsGlobals(CommonTypes)
188       .AppendTypesConstantsGlobals(tc.used_consts)
189       .AppendInMain(tc.main_insts);
190   const std::string expected = builder.GetCode();
191   builder.AppendTypesConstantsGlobals(tc.dead_consts);
192   const std::string assembly_with_dead_const = builder.GetCode();
193   SinglePassRunAndCheck<EliminateDeadConstantPass>(
194       assembly_with_dead_const, expected, /*  skip_nop = */ true);
195 }
196 
197 INSTANTIATE_TEST_SUITE_P(
198     ScalarTypeConstants, EliminateDeadConstantTest,
199     ::testing::ValuesIn(std::vector<EliminateDeadConstantTestCase>({
200         // clang-format off
201         // Scalar type constants, one dead constant and one used constant.
202         {
203             /* .used_consts = */
204             {
205                 "%used_const_int = OpConstant %int 1",
206             },
207             /* .main_insts = */
208             {
209                 "%int_var = OpVariable %_pf_int Function",
210                 "OpStore %int_var %used_const_int",
211             },
212             /* .dead_consts = */
213             {
214                 "%dead_const_int = OpConstant %int 1",
215             },
216         },
217         {
218             /* .used_consts = */
219             {
220                 "%used_const_uint = OpConstant %uint 1",
221             },
222             /* .main_insts = */
223             {
224                 "%uint_var = OpVariable %_pf_uint Function",
225                 "OpStore %uint_var %used_const_uint",
226             },
227             /* .dead_consts = */
228             {
229                 "%dead_const_uint = OpConstant %uint 1",
230             },
231         },
232         {
233             /* .used_consts = */
234             {
235                 "%used_const_float = OpConstant %float 3.1415",
236             },
237             /* .main_insts = */
238             {
239                 "%float_var = OpVariable %_pf_float Function",
240                 "OpStore %float_var %used_const_float",
241             },
242             /* .dead_consts = */
243             {
244                 "%dead_const_float = OpConstant %float 3.1415",
245             },
246         },
247         {
248             /* .used_consts = */
249             {
250                 "%used_const_double = OpConstant %double 3.141592653",
251             },
252             /* .main_insts = */
253             {
254                 "%double_var = OpVariable %_pf_double Function",
255                 "OpStore %double_var %used_const_double",
256             },
257             /* .dead_consts = */
258             {
259                 "%dead_const_double = OpConstant %double 3.141592653",
260             },
261         },
262         // clang-format on
263     })));
264 
265 INSTANTIATE_TEST_SUITE_P(
266     VectorTypeConstants, EliminateDeadConstantTest,
267     ::testing::ValuesIn(std::vector<EliminateDeadConstantTestCase>({
268         // clang-format off
269         // Tests eliminating dead constant type ivec2. One dead constant vector
270         // and one used constant vector, each built from its own group of
271         // scalar constants.
272         {
273             /* .used_consts = */
274             {
275                 "%used_int_x = OpConstant %int 1",
276                 "%used_int_y = OpConstant %int 2",
277                 "%used_v2int = OpConstantComposite %v2int %used_int_x %used_int_y",
278             },
279             /* .main_insts = */
280             {
281                 "%v2int_var = OpVariable %_pf_v2int Function",
282                 "OpStore %v2int_var %used_v2int",
283             },
284             /* .dead_consts = */
285             {
286                 "%dead_int_x = OpConstant %int 1",
287                 "%dead_int_y = OpConstant %int 2",
288                 "%dead_v2int = OpConstantComposite %v2int %dead_int_x %dead_int_y",
289             },
290         },
291         // Tests eliminating dead constant ivec2. One dead constant vector and
292         // one used constant vector. But both built from a same group of
293         // scalar constants.
294         {
295             /* .used_consts = */
296             {
297                 "%used_int_x = OpConstant %int 1",
298                 "%used_int_y = OpConstant %int 2",
299                 "%used_int_z = OpConstant %int 3",
300                 "%used_v3int = OpConstantComposite %v3int %used_int_x %used_int_y %used_int_z",
301             },
302             /* .main_insts = */
303             {
304                 "%v3int_var = OpVariable %_pf_v3int Function",
305                 "OpStore %v3int_var %used_v3int",
306             },
307             /* .dead_consts = */
308             {
309                 "%dead_v3int = OpConstantComposite %v3int %used_int_x %used_int_y %used_int_z",
310             },
311         },
312         // Tests eliminating dead cosntant vec2. One dead constant vector and
313         // one used constant vector. Each built from its own group of scalar
314         // constants.
315         {
316             /* .used_consts = */
317             {
318                 "%used_float_x = OpConstant %float 3.1415",
319                 "%used_float_y = OpConstant %float 4.25",
320                 "%used_v2float = OpConstantComposite %v2float %used_float_x %used_float_y",
321             },
322             /* .main_insts = */
323             {
324                 "%v2float_var = OpVariable %_pf_v2float Function",
325                 "OpStore %v2float_var %used_v2float",
326             },
327             /* .dead_consts = */
328             {
329                 "%dead_float_x = OpConstant %float 3.1415",
330                 "%dead_float_y = OpConstant %float 4.25",
331                 "%dead_v2float = OpConstantComposite %v2float %dead_float_x %dead_float_y",
332             },
333         },
334         // Tests eliminating dead cosntant vec2. One dead constant vector and
335         // one used constant vector. Both built from a same group of scalar
336         // constants.
337         {
338             /* .used_consts = */
339             {
340                 "%used_float_x = OpConstant %float 3.1415",
341                 "%used_float_y = OpConstant %float 4.25",
342                 "%used_float_z = OpConstant %float 4.75",
343                 "%used_v3float = OpConstantComposite %v3float %used_float_x %used_float_y %used_float_z",
344             },
345             /* .main_insts = */
346             {
347                 "%v3float_var = OpVariable %_pf_v3float Function",
348                 "OpStore %v3float_var %used_v3float",
349             },
350             /* .dead_consts = */
351             {
352                 "%dead_v3float = OpConstantComposite %v3float %used_float_x %used_float_y %used_float_z",
353             },
354         },
355         // clang-format on
356     })));
357 
358 INSTANTIATE_TEST_SUITE_P(
359     StructTypeConstants, EliminateDeadConstantTest,
360     ::testing::ValuesIn(std::vector<EliminateDeadConstantTestCase>({
361         // clang-format off
362         // A plain struct type dead constants. All of its components are dead
363         // constants too.
364         {
365             /* .used_consts = */ {},
366             /* .main_insts = */ {},
367             /* .dead_consts = */
368             {
369                 "%dead_bool = OpConstantTrue %bool",
370                 "%dead_int = OpConstant %int 1",
371                 "%dead_float = OpConstant %float 2.5",
372                 "%dead_double = OpConstant %double 3.14159265358979",
373                 "%dead_struct = OpConstantComposite %flat_struct %dead_bool %dead_int %dead_float %dead_double",
374             },
375         },
376         // A plain struct type dead constants. Some of its components are dead
377         // constants while others are not.
378         {
379             /* .used_consts = */
380             {
381                 "%used_int = OpConstant %int 1",
382                 "%used_double = OpConstant %double 3.14159265358979",
383             },
384             /* .main_insts = */
385             {
386                 "%int_var = OpVariable %_pf_int Function",
387                 "OpStore %int_var %used_int",
388                 "%double_var = OpVariable %_pf_double Function",
389                 "OpStore %double_var %used_double",
390             },
391             /* .dead_consts = */
392             {
393                 "%dead_bool = OpConstantTrue %bool",
394                 "%dead_float = OpConstant %float 2.5",
395                 "%dead_struct = OpConstantComposite %flat_struct %dead_bool %used_int %dead_float %used_double",
396             },
397         },
398         // A nesting struct type dead constants. All components of both outer
399         // and inner structs are dead and should be removed after dead constant
400         // elimination.
401         {
402             /* .used_consts = */ {},
403             /* .main_insts = */ {},
404             /* .dead_consts = */
405             {
406                 "%dead_bool = OpConstantTrue %bool",
407                 "%dead_int = OpConstant %int 1",
408                 "%dead_float = OpConstant %float 2.5",
409                 "%dead_double = OpConstant %double 3.1415926535",
410                 "%dead_inner_struct = OpConstantComposite %inner_struct %dead_bool %dead_int %dead_float %dead_double",
411                 "%dead_int2 = OpConstant %int 2",
412                 "%dead_double2 = OpConstant %double 1.428571428514",
413                 "%dead_outer_struct = OpConstantComposite %outer_struct %dead_inner_struct %dead_int2 %dead_double2",
414             },
415         },
416         // A nesting struct type dead constants. Some of its components are
417         // dead constants while others are not.
418         {
419             /* .used_consts = */
420             {
421                 "%used_int = OpConstant %int 1",
422                 "%used_double = OpConstant %double 3.14159265358979",
423             },
424             /* .main_insts = */
425             {
426                 "%int_var = OpVariable %_pf_int Function",
427                 "OpStore %int_var %used_int",
428                 "%double_var = OpVariable %_pf_double Function",
429                 "OpStore %double_var %used_double",
430             },
431             /* .dead_consts = */
432             {
433                 "%dead_bool = OpConstantTrue %bool",
434                 "%dead_float = OpConstant %float 2.5",
435                 "%dead_inner_struct = OpConstantComposite %inner_struct %dead_bool %used_int %dead_float %used_double",
436                 "%dead_int = OpConstant %int 2",
437                 "%dead_outer_struct = OpConstantComposite %outer_struct %dead_inner_struct %dead_int %used_double",
438             },
439         },
440         // A nesting struct case. The inner struct is used while the outer struct is not
441         {
442           /* .used_const = */
443           {
444             "%used_bool = OpConstantTrue %bool",
445             "%used_int = OpConstant %int 1",
446             "%used_float = OpConstant %float 1.25",
447             "%used_double = OpConstant %double 1.23456789012345",
448             "%used_inner_struct = OpConstantComposite %inner_struct %used_bool %used_int %used_float %used_double",
449           },
450           /* .main_insts = */
451           {
452             "%bool_var = OpVariable %_pf_bool Function",
453             "%bool_from_inner_struct = OpCompositeExtract %bool %used_inner_struct 0",
454             "OpStore %bool_var %bool_from_inner_struct",
455           },
456           /* .dead_consts = */
457           {
458             "%dead_int = OpConstant %int 2",
459             "%dead_outer_struct = OpConstantComposite %outer_struct %used_inner_struct %dead_int %used_double"
460           },
461         },
462         // A nesting struct case. The outer struct is used, so the inner struct should not
463         // be removed even though it is not used anywhere.
464         {
465           /* .used_const = */
466           {
467             "%used_bool = OpConstantTrue %bool",
468             "%used_int = OpConstant %int 1",
469             "%used_float = OpConstant %float 1.25",
470             "%used_double = OpConstant %double 1.23456789012345",
471             "%used_inner_struct = OpConstantComposite %inner_struct %used_bool %used_int %used_float %used_double",
472             "%used_outer_struct = OpConstantComposite %outer_struct %used_inner_struct %used_int %used_double"
473           },
474           /* .main_insts = */
475           {
476             "%int_var = OpVariable %_pf_int Function",
477             "%int_from_outer_struct = OpCompositeExtract %int %used_outer_struct 1",
478             "OpStore %int_var %int_from_outer_struct",
479           },
480           /* .dead_consts = */ {},
481         },
482         // clang-format on
483     })));
484 
485 INSTANTIATE_TEST_SUITE_P(
486     ScalarTypeSpecConstants, EliminateDeadConstantTest,
487     ::testing::ValuesIn(std::vector<EliminateDeadConstantTestCase>({
488         // clang-format off
489         // All scalar type spec constants.
490         {
491             /* .used_consts = */
492             {
493                 "%used_bool = OpSpecConstantTrue %bool",
494                 "%used_uint = OpSpecConstant %uint 2",
495                 "%used_int = OpSpecConstant %int 2",
496                 "%used_float = OpSpecConstant %float 2.5",
497                 "%used_double = OpSpecConstant %double 1.42857142851",
498             },
499             /* .main_insts = */
500             {
501                 "%bool_var = OpVariable %_pf_bool Function",
502                 "%uint_var = OpVariable %_pf_uint Function",
503                 "%int_var = OpVariable %_pf_int Function",
504                 "%float_var = OpVariable %_pf_float Function",
505                 "%double_var = OpVariable %_pf_double Function",
506                 "OpStore %bool_var %used_bool", "OpStore %uint_var %used_uint",
507                 "OpStore %int_var %used_int", "OpStore %float_var %used_float",
508                 "OpStore %double_var %used_double",
509             },
510             /* .dead_consts = */
511             {
512                 "%dead_bool = OpSpecConstantTrue %bool",
513                 "%dead_uint = OpSpecConstant %uint 2",
514                 "%dead_int = OpSpecConstant %int 2",
515                 "%dead_float = OpSpecConstant %float 2.5",
516                 "%dead_double = OpSpecConstant %double 1.42857142851",
517             },
518         },
519         // clang-format on
520     })));
521 
522 INSTANTIATE_TEST_SUITE_P(
523     VectorTypeSpecConstants, EliminateDeadConstantTest,
524     ::testing::ValuesIn(std::vector<EliminateDeadConstantTestCase>({
525         // clang-format off
526         // Bool vector type spec constants. One vector has all component dead,
527         // another vector has one dead boolean and one used boolean.
528         {
529             /* .used_consts = */
530             {
531                 "%used_bool = OpSpecConstantTrue %bool",
532             },
533             /* .main_insts = */
534             {
535                 "%bool_var = OpVariable %_pf_bool Function",
536                 "OpStore %bool_var %used_bool",
537             },
538             /* .dead_consts = */
539             {
540                 "%dead_bool = OpSpecConstantFalse %bool",
541                 "%dead_bool_vec1 = OpSpecConstantComposite %v2bool %dead_bool %dead_bool",
542                 "%dead_bool_vec2 = OpSpecConstantComposite %v2bool %dead_bool %used_bool",
543             },
544         },
545 
546         // Uint vector type spec constants. One vector has all component dead,
547         // another vector has one dead unsigned integer and one used unsigned
548         // integer.
549         {
550             /* .used_consts = */
551             {
552                 "%used_uint = OpSpecConstant %uint 3",
553             },
554             /* .main_insts = */
555             {
556                 "%uint_var = OpVariable %_pf_uint Function",
557                 "OpStore %uint_var %used_uint",
558             },
559             /* .dead_consts = */
560             {
561                 "%dead_uint = OpSpecConstant %uint 1",
562                 "%dead_uint_vec1 = OpSpecConstantComposite %v2uint %dead_uint %dead_uint",
563                 "%dead_uint_vec2 = OpSpecConstantComposite %v2uint %dead_uint %used_uint",
564             },
565         },
566 
567         // Int vector type spec constants. One vector has all component dead,
568         // another vector has one dead integer and one used integer.
569         {
570             /* .used_consts = */
571             {
572                 "%used_int = OpSpecConstant %int 3",
573             },
574             /* .main_insts = */
575             {
576                 "%int_var = OpVariable %_pf_int Function",
577                 "OpStore %int_var %used_int",
578             },
579             /* .dead_consts = */
580             {
581                 "%dead_int = OpSpecConstant %int 1",
582                 "%dead_int_vec1 = OpSpecConstantComposite %v2int %dead_int %dead_int",
583                 "%dead_int_vec2 = OpSpecConstantComposite %v2int %dead_int %used_int",
584             },
585         },
586 
587         // Int vector type spec constants built with both spec constants and
588         // front-end constants.
589         {
590             /* .used_consts = */
591             {
592                 "%used_spec_int = OpSpecConstant %int 3",
593                 "%used_front_end_int = OpConstant %int 3",
594             },
595             /* .main_insts = */
596             {
597                 "%int_var1 = OpVariable %_pf_int Function",
598                 "OpStore %int_var1 %used_spec_int",
599                 "%int_var2 = OpVariable %_pf_int Function",
600                 "OpStore %int_var2 %used_front_end_int",
601             },
602             /* .dead_consts = */
603             {
604                 "%dead_spec_int = OpSpecConstant %int 1",
605                 "%dead_front_end_int = OpConstant %int 1",
606                 // Dead front-end and dead spec constants
607                 "%dead_int_vec1 = OpSpecConstantComposite %v2int %dead_spec_int %dead_front_end_int",
608                 // Used front-end and dead spec constants
609                 "%dead_int_vec2 = OpSpecConstantComposite %v2int %dead_spec_int %used_front_end_int",
610                 // Dead front-end and used spec constants
611                 "%dead_int_vec3 = OpSpecConstantComposite %v2int %dead_front_end_int %used_spec_int",
612             },
613         },
614         // clang-format on
615     })));
616 
617 INSTANTIATE_TEST_SUITE_P(
618     SpecConstantOp, EliminateDeadConstantTest,
619     ::testing::ValuesIn(std::vector<EliminateDeadConstantTestCase>({
620         // clang-format off
621         // Cast operations: uint <-> int <-> bool
622         {
623             /* .used_consts = */ {},
624             /* .main_insts = */ {},
625             /* .dead_consts = */
626             {
627                 // Assistant constants, only used in dead spec constant
628                 // operations.
629                 "%signed_zero = OpConstant %int 0",
630                 "%signed_zero_vec = OpConstantComposite %v2int %signed_zero %signed_zero",
631                 "%unsigned_zero = OpConstant %uint 0",
632                 "%unsigned_zero_vec = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero",
633                 "%signed_one = OpConstant %int 1",
634                 "%signed_one_vec = OpConstantComposite %v2int %signed_one %signed_one",
635                 "%unsigned_one = OpConstant %uint 1",
636                 "%unsigned_one_vec = OpConstantComposite %v2uint %unsigned_one %unsigned_one",
637 
638                 // Spec constants that support casting to each other.
639                 "%dead_bool = OpSpecConstantTrue %bool",
640                 "%dead_uint = OpSpecConstant %uint 1",
641                 "%dead_int = OpSpecConstant %int 2",
642                 "%dead_bool_vec = OpSpecConstantComposite %v2bool %dead_bool %dead_bool",
643                 "%dead_uint_vec = OpSpecConstantComposite %v2uint %dead_uint %dead_uint",
644                 "%dead_int_vec = OpSpecConstantComposite %v2int %dead_int %dead_int",
645 
646                 // Scalar cast to boolean spec constant.
647                 "%int_to_bool = OpSpecConstantOp %bool INotEqual %dead_int %signed_zero",
648                 "%uint_to_bool = OpSpecConstantOp %bool INotEqual %dead_uint %unsigned_zero",
649 
650                 // Vector cast to boolean spec constant.
651                 "%int_to_bool_vec = OpSpecConstantOp %v2bool INotEqual %dead_int_vec %signed_zero_vec",
652                 "%uint_to_bool_vec = OpSpecConstantOp %v2bool INotEqual %dead_uint_vec %unsigned_zero_vec",
653 
654                 // Scalar cast to int spec constant.
655                 "%bool_to_int = OpSpecConstantOp %int Select %dead_bool %signed_one %signed_zero",
656                 "%uint_to_int = OpSpecConstantOp %uint IAdd %dead_uint %unsigned_zero",
657 
658                 // Vector cast to int spec constant.
659                 "%bool_to_int_vec = OpSpecConstantOp %v2int Select %dead_bool_vec %signed_one_vec %signed_zero_vec",
660                 "%uint_to_int_vec = OpSpecConstantOp %v2uint IAdd %dead_uint_vec %unsigned_zero_vec",
661 
662                 // Scalar cast to uint spec constant.
663                 "%bool_to_uint = OpSpecConstantOp %uint Select %dead_bool %unsigned_one %unsigned_zero",
664                 "%int_to_uint_vec = OpSpecConstantOp %uint IAdd %dead_int %signed_zero",
665 
666                 // Vector cast to uint spec constant.
667                 "%bool_to_uint_vec = OpSpecConstantOp %v2uint Select %dead_bool_vec %unsigned_one_vec %unsigned_zero_vec",
668                 "%int_to_uint = OpSpecConstantOp %v2uint IAdd %dead_int_vec %signed_zero_vec",
669             },
670         },
671 
672         // Add, sub, mul, div, rem.
673         {
674             /* .used_consts = */ {},
675             /* .main_insts = */ {},
676             /* .dead_consts = */
677             {
678                 "%dead_spec_int_a = OpSpecConstant %int 1",
679                 "%dead_spec_int_a_vec = OpSpecConstantComposite %v2int %dead_spec_int_a %dead_spec_int_a",
680 
681                 "%dead_spec_int_b = OpSpecConstant %int 2",
682                 "%dead_spec_int_b_vec = OpSpecConstantComposite %v2int %dead_spec_int_b %dead_spec_int_b",
683 
684                 "%dead_const_int_c = OpConstant %int 3",
685                 "%dead_const_int_c_vec = OpConstantComposite %v2int %dead_const_int_c %dead_const_int_c",
686 
687                 // Add
688                 "%add_a_b = OpSpecConstantOp %int IAdd %dead_spec_int_a %dead_spec_int_b",
689                 "%add_a_b_vec = OpSpecConstantOp %v2int IAdd %dead_spec_int_a_vec %dead_spec_int_b_vec",
690 
691                 // Sub
692                 "%sub_a_b = OpSpecConstantOp %int ISub %dead_spec_int_a %dead_spec_int_b",
693                 "%sub_a_b_vec = OpSpecConstantOp %v2int ISub %dead_spec_int_a_vec %dead_spec_int_b_vec",
694 
695                 // Mul
696                 "%mul_a_b = OpSpecConstantOp %int IMul %dead_spec_int_a %dead_spec_int_b",
697                 "%mul_a_b_vec = OpSpecConstantOp %v2int IMul %dead_spec_int_a_vec %dead_spec_int_b_vec",
698 
699                 // Div
700                 "%div_a_b = OpSpecConstantOp %int SDiv %dead_spec_int_a %dead_spec_int_b",
701                 "%div_a_b_vec = OpSpecConstantOp %v2int SDiv %dead_spec_int_a_vec %dead_spec_int_b_vec",
702 
703                 // Bitwise Xor
704                 "%xor_a_b = OpSpecConstantOp %int BitwiseXor %dead_spec_int_a %dead_spec_int_b",
705                 "%xor_a_b_vec = OpSpecConstantOp %v2int BitwiseXor %dead_spec_int_a_vec %dead_spec_int_b_vec",
706 
707                 // Scalar Comparison
708                 "%less_a_b = OpSpecConstantOp %bool SLessThan %dead_spec_int_a %dead_spec_int_b",
709             },
710         },
711 
712         // Vectors without used swizzles should be removed.
713         {
714             /* .used_consts = */
715             {
716                 "%used_int = OpConstant %int 3",
717             },
718             /* .main_insts = */
719             {
720                 "%int_var = OpVariable %_pf_int Function",
721                 "OpStore %int_var %used_int",
722             },
723             /* .dead_consts = */
724             {
725                 "%dead_int = OpConstant %int 3",
726 
727                 "%dead_spec_int_a = OpSpecConstant %int 1",
728                 "%vec_a = OpSpecConstantComposite %v4int %dead_spec_int_a %dead_spec_int_a %dead_int %dead_int",
729 
730                 "%dead_spec_int_b = OpSpecConstant %int 2",
731                 "%vec_b = OpSpecConstantComposite %v4int %dead_spec_int_b %dead_spec_int_b %used_int %used_int",
732 
733                 // Extract scalar
734                 "%a_x = OpSpecConstantOp %int CompositeExtract %vec_a 0",
735                 "%b_x = OpSpecConstantOp %int CompositeExtract %vec_b 0",
736 
737                 // Extract vector
738                 "%a_xy = OpSpecConstantOp %v2int VectorShuffle %vec_a %vec_a 0 1",
739                 "%b_xy = OpSpecConstantOp %v2int VectorShuffle %vec_b %vec_b 0 1",
740             },
741         },
742         // Vectors with used swizzles should not be removed.
743         {
744             /* .used_consts = */
745             {
746                 "%used_int = OpConstant %int 3",
747                 "%used_spec_int_a = OpSpecConstant %int 1",
748                 "%used_spec_int_b = OpSpecConstant %int 2",
749                 // Create vectors
750                 "%vec_a = OpSpecConstantComposite %v4int %used_spec_int_a %used_spec_int_a %used_int %used_int",
751                 "%vec_b = OpSpecConstantComposite %v4int %used_spec_int_b %used_spec_int_b %used_int %used_int",
752                 // Extract vector
753                 "%a_xy = OpSpecConstantOp %v2int VectorShuffle %vec_a %vec_a 0 1",
754                 "%b_xy = OpSpecConstantOp %v2int VectorShuffle %vec_b %vec_b 0 1",
755             },
756             /* .main_insts = */
757             {
758                 "%v2int_var_a = OpVariable %_pf_v2int Function",
759                 "%v2int_var_b = OpVariable %_pf_v2int Function",
760                 "OpStore %v2int_var_a %a_xy",
761                 "OpStore %v2int_var_b %b_xy",
762             },
763             /* .dead_consts = */ {},
764         },
765         // clang-format on
766     })));
767 
768 INSTANTIATE_TEST_SUITE_P(
769     LongDefUseChain, EliminateDeadConstantTest,
770     ::testing::ValuesIn(std::vector<EliminateDeadConstantTestCase>({
771         // clang-format off
772         // Long Def-Use chain with binary operations.
773         {
774             /* .used_consts = */
775             {
776               "%array_size = OpConstant %int 4",
777               "%type_arr_int_4 = OpTypeArray %int %array_size",
778               "%used_int_0 = OpConstant %int 100",
779               "%used_int_1 = OpConstant %int 1",
780               "%used_int_2 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_1",
781               "%used_int_3 = OpSpecConstantOp %int ISub %used_int_0 %used_int_2",
782               "%used_int_4 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_3",
783               "%used_int_5 = OpSpecConstantOp %int ISub %used_int_0 %used_int_4",
784               "%used_int_6 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_5",
785               "%used_int_7 = OpSpecConstantOp %int ISub %used_int_0 %used_int_6",
786               "%used_int_8 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_7",
787               "%used_int_9 = OpSpecConstantOp %int ISub %used_int_0 %used_int_8",
788               "%used_int_10 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_9",
789               "%used_int_11 = OpSpecConstantOp %int ISub %used_int_0 %used_int_10",
790               "%used_int_12 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_11",
791               "%used_int_13 = OpSpecConstantOp %int ISub %used_int_0 %used_int_12",
792               "%used_int_14 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_13",
793               "%used_int_15 = OpSpecConstantOp %int ISub %used_int_0 %used_int_14",
794               "%used_int_16 = OpSpecConstantOp %int ISub %used_int_0 %used_int_15",
795               "%used_int_17 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_16",
796               "%used_int_18 = OpSpecConstantOp %int ISub %used_int_0 %used_int_17",
797               "%used_int_19 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_18",
798               "%used_int_20 = OpSpecConstantOp %int ISub %used_int_0 %used_int_19",
799               "%used_vec_a = OpSpecConstantComposite %v2int %used_int_18 %used_int_19",
800               "%used_vec_b = OpSpecConstantOp %v2int IMul %used_vec_a %used_vec_a",
801               "%used_int_21 = OpSpecConstantOp %int CompositeExtract %used_vec_b 0",
802               "%used_array = OpConstantComposite %type_arr_int_4 %used_int_20 %used_int_20 %used_int_21 %used_int_21",
803             },
804             /* .main_insts = */
805             {
806               "%int_var = OpVariable %_pf_int Function",
807               "%used_array_2 = OpCompositeExtract %int %used_array 2",
808               "OpStore %int_var %used_array_2",
809             },
810             /* .dead_consts = */
811             {
812               "%dead_int_1 = OpConstant %int 2",
813               "%dead_int_2 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_1",
814               "%dead_int_3 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_2",
815               "%dead_int_4 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_3",
816               "%dead_int_5 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_4",
817               "%dead_int_6 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_5",
818               "%dead_int_7 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_6",
819               "%dead_int_8 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_7",
820               "%dead_int_9 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_8",
821               "%dead_int_10 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_9",
822               "%dead_int_11 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_10",
823               "%dead_int_12 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_11",
824               "%dead_int_13 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_12",
825               "%dead_int_14 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_13",
826               "%dead_int_15 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_14",
827               "%dead_int_16 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_15",
828               "%dead_int_17 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_16",
829               "%dead_int_18 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_17",
830               "%dead_int_19 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_18",
831               "%dead_int_20 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_19",
832               "%dead_vec_a = OpSpecConstantComposite %v2int %dead_int_18 %dead_int_19",
833               "%dead_vec_b = OpSpecConstantOp %v2int IMul %dead_vec_a %dead_vec_a",
834               "%dead_int_21 = OpSpecConstantOp %int CompositeExtract %dead_vec_b 0",
835               "%dead_array = OpConstantComposite %type_arr_int_4 %dead_int_20 %used_int_20 %dead_int_19 %used_int_19",
836             },
837         },
838         // Long Def-Use chain with swizzle
839         // clang-format on
840     })));
841 
842 }  // namespace
843 }  // namespace opt
844 }  // namespace spvtools
845