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 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 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 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_CASE_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_CASE_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_CASE_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_CASE_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_CASE_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 unsigend 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_CASE_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_CASE_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