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