1// Copyright (c) 2018 Google LLC. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include <string> 16 17#include "effcee/effcee.h" 18#include "gmock/gmock.h" 19#include "test/opt/pass_fixture.h" 20 21namespace spvtools { 22namespace opt { 23namespace { 24 25using UnswitchTest = PassTest<::testing::Test>; 26 27/* 28Generated from the following GLSL + --eliminate-local-multi-store 29 30#version 450 core 31uniform vec4 c; 32void main() { 33 int i = 0; 34 int j = 0; 35 bool cond = c[0] == 0; 36 for (; i < 10; i++, j++) { 37 if (cond) { 38 i++; 39 } 40 else { 41 j++; 42 } 43 } 44} 45*/ 46TEST_F(UnswitchTest, SimpleUnswitch) { 47 const std::string text = R"( 48; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual 49; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None 50; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] 51 52; Loop specialized for false. 53; CHECK: [[loop_f]] = OpLabel 54; CHECK-NEXT: OpBranch [[loop:%\w+]] 55; CHECK: [[loop]] = OpLabel 56; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]] 57; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_j:%\w+]] [[continue]] 58; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 59; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 60; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] 61; [[loop_body]] = OpLabel 62; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None 63; CHECK: OpBranchConditional %false [[bb1:%\w+]] [[bb2:%\w+]] 64; CHECK: [[bb2]] = OpLabel 65; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1 66; CHECK-NEXT: OpBranch [[sel_merge]] 67; CHECK: [[bb1]] = OpLabel 68; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1 69; CHECK-NEXT: OpBranch [[sel_merge]] 70; CHECK: [[sel_merge]] = OpLabel 71; CHECK: OpBranch [[if_merge]] 72 73; Loop specialized for true. 74; CHECK: [[loop_t]] = OpLabel 75; CHECK-NEXT: OpBranch [[loop:%\w+]] 76; CHECK: [[loop]] = OpLabel 77; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]] 78; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_j:%\w+]] [[continue]] 79; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 80; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 81; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] 82; [[loop_body]] = OpLabel 83; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None 84; CHECK: OpBranchConditional %true [[bb1:%\w+]] [[bb2:%\w+]] 85; CHECK: [[bb1]] = OpLabel 86; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1 87; CHECK-NEXT: OpBranch [[sel_merge]] 88; CHECK: [[bb2]] = OpLabel 89; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1 90; CHECK-NEXT: OpBranch [[sel_merge]] 91; CHECK: [[sel_merge]] = OpLabel 92; CHECK: OpBranch [[if_merge]] 93 94; CHECK: [[if_merge]] = OpLabel 95; CHECK-NEXT: OpReturn 96 97 OpCapability Shader 98 %1 = OpExtInstImport "GLSL.std.450" 99 OpMemoryModel Logical GLSL450 100 OpEntryPoint Fragment %main "main" 101 OpExecutionMode %main OriginLowerLeft 102 OpSource GLSL 450 103 OpName %main "main" 104 OpName %c "c" 105 OpDecorate %c Location 0 106 OpDecorate %c DescriptorSet 0 107 %void = OpTypeVoid 108 %3 = OpTypeFunction %void 109 %int = OpTypeInt 32 1 110%_ptr_Function_int = OpTypePointer Function %int 111 %int_0 = OpConstant %int 0 112 %bool = OpTypeBool 113%_ptr_Function_bool = OpTypePointer Function %bool 114 %float = OpTypeFloat 32 115 %v4float = OpTypeVector %float 4 116%_ptr_UniformConstant_v4float = OpTypePointer UniformConstant %v4float 117 %c = OpVariable %_ptr_UniformConstant_v4float UniformConstant 118 %uint = OpTypeInt 32 0 119 %uint_0 = OpConstant %uint 0 120%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float 121 %float_0 = OpConstant %float 0 122 %int_10 = OpConstant %int 10 123 %int_1 = OpConstant %int 1 124 %main = OpFunction %void None %3 125 %5 = OpLabel 126 %21 = OpAccessChain %_ptr_UniformConstant_float %c %uint_0 127 %22 = OpLoad %float %21 128 %24 = OpFOrdEqual %bool %22 %float_0 129 OpBranch %25 130 %25 = OpLabel 131 %46 = OpPhi %int %int_0 %5 %43 %28 132 %47 = OpPhi %int %int_0 %5 %45 %28 133 OpLoopMerge %27 %28 None 134 OpBranch %29 135 %29 = OpLabel 136 %32 = OpSLessThan %bool %46 %int_10 137 OpBranchConditional %32 %26 %27 138 %26 = OpLabel 139 OpSelectionMerge %35 None 140 OpBranchConditional %24 %34 %39 141 %34 = OpLabel 142 %38 = OpIAdd %int %46 %int_1 143 OpBranch %35 144 %39 = OpLabel 145 %41 = OpIAdd %int %47 %int_1 146 OpBranch %35 147 %35 = OpLabel 148 %48 = OpPhi %int %38 %34 %46 %39 149 %49 = OpPhi %int %47 %34 %41 %39 150 OpBranch %28 151 %28 = OpLabel 152 %43 = OpIAdd %int %48 %int_1 153 %45 = OpIAdd %int %49 %int_1 154 OpBranch %25 155 %27 = OpLabel 156 OpReturn 157 OpFunctionEnd 158 )"; 159 160 SinglePassRunAndMatch<LoopUnswitchPass>(text, true); 161} 162 163/* 164Generated from the following GLSL + --eliminate-local-multi-store 165 166#version 330 core 167in vec4 c; 168void main() { 169 int i = 0; 170 bool cond = c[0] == 0; 171 for (; i < 10; i++) { 172 if (cond) { 173 i++; 174 } 175 else { 176 return; 177 } 178 } 179} 180*/ 181TEST_F(UnswitchTest, UnswitchExit) { 182 const std::string text = R"( 183; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual 184; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None 185; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] 186 187; Loop specialized for false. 188; CHECK: [[loop_f]] = OpLabel 189; CHECK: OpReturn 190 191; Loop specialized for true. 192; CHECK: [[loop_t]] = OpLabel 193; CHECK-NEXT: OpBranch [[loop:%\w+]] 194; CHECK: [[loop]] = OpLabel 195; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]] 196; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 197; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 198; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]] 199; Check that we have i+=2. 200; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1 201; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1 202; CHECK: [[merge]] = OpLabel 203; CHECK-NEXT: OpBranch [[if_merge]] 204 205; CHECK: [[if_merge]] = OpLabel 206; CHECK-NEXT: OpReturn 207 208 OpCapability Shader 209 %1 = OpExtInstImport "GLSL.std.450" 210 OpMemoryModel Logical GLSL450 211 OpEntryPoint Fragment %main "main" %c 212 OpExecutionMode %main OriginUpperLeft 213 OpSource GLSL 330 214 OpName %main "main" 215 OpName %c "c" 216 OpDecorate %c Location 0 217 OpDecorate %23 Uniform 218 %void = OpTypeVoid 219 %3 = OpTypeFunction %void 220 %int = OpTypeInt 32 1 221%_ptr_Function_int = OpTypePointer Function %int 222 %int_0 = OpConstant %int 0 223 %bool = OpTypeBool 224%_ptr_Function_bool = OpTypePointer Function %bool 225 %float = OpTypeFloat 32 226 %v4float = OpTypeVector %float 4 227%_ptr_Input_v4float = OpTypePointer Input %v4float 228 %c = OpVariable %_ptr_Input_v4float Input 229 %uint = OpTypeInt 32 0 230 %uint_0 = OpConstant %uint 0 231%_ptr_Input_float = OpTypePointer Input %float 232 %float_0 = OpConstant %float 0 233 %int_10 = OpConstant %int 10 234 %int_1 = OpConstant %int 1 235 %main = OpFunction %void None %3 236 %5 = OpLabel 237 %20 = OpAccessChain %_ptr_Input_float %c %uint_0 238 %21 = OpLoad %float %20 239 %23 = OpFOrdEqual %bool %21 %float_0 240 OpBranch %24 241 %24 = OpLabel 242 %42 = OpPhi %int %int_0 %5 %41 %27 243 OpLoopMerge %26 %27 None 244 OpBranch %28 245 %28 = OpLabel 246 %31 = OpSLessThan %bool %42 %int_10 247 OpBranchConditional %31 %25 %26 248 %25 = OpLabel 249 OpSelectionMerge %34 None 250 OpBranchConditional %23 %33 %38 251 %33 = OpLabel 252 %37 = OpIAdd %int %42 %int_1 253 OpBranch %34 254 %38 = OpLabel 255 OpReturn 256 %34 = OpLabel 257 OpBranch %27 258 %27 = OpLabel 259 %41 = OpIAdd %int %37 %int_1 260 OpBranch %24 261 %26 = OpLabel 262 OpReturn 263 OpFunctionEnd 264 )"; 265 266 SinglePassRunAndMatch<LoopUnswitchPass>(text, true); 267} 268 269/* 270Generated from the following GLSL + --eliminate-local-multi-store 271 272#version 330 core 273in vec4 c; 274void main() { 275 int i = 0; 276 bool cond = c[0] == 0; 277 for (; i < 10; i++) { 278 if (cond) { 279 continue; 280 } 281 else { 282 i++; 283 } 284 } 285} 286*/ 287TEST_F(UnswitchTest, UnswitchContinue) { 288 const std::string text = R"( 289; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual 290; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None 291; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] 292 293; Loop specialized for false. 294; CHECK: [[loop_f]] = OpLabel 295; CHECK-NEXT: OpBranch [[loop:%\w+]] 296; CHECK: [[loop]] = OpLabel 297; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]] 298; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 299; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 300; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] 301; CHECK: [[loop_body:%\w+]] = OpLabel 302; CHECK-NEXT: OpSelectionMerge 303; CHECK-NEXT: OpBranchConditional %false 304; CHECK: [[merge]] = OpLabel 305; CHECK-NEXT: OpBranch [[if_merge]] 306 307; Loop specialized for true. 308; CHECK: [[loop_t]] = OpLabel 309; CHECK-NEXT: OpBranch [[loop:%\w+]] 310; CHECK: [[loop]] = OpLabel 311; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]] 312; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 313; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 314; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] 315; CHECK: [[loop_body:%\w+]] = OpLabel 316; CHECK-NEXT: OpSelectionMerge 317; CHECK-NEXT: OpBranchConditional %true 318; CHECK: [[merge]] = OpLabel 319; CHECK-NEXT: OpBranch [[if_merge]] 320 321; CHECK: [[if_merge]] = OpLabel 322; CHECK-NEXT: OpReturn 323 324 OpCapability Shader 325 %1 = OpExtInstImport "GLSL.std.450" 326 OpMemoryModel Logical GLSL450 327 OpEntryPoint Fragment %main "main" %c 328 OpExecutionMode %main OriginUpperLeft 329 OpSource GLSL 330 330 OpName %main "main" 331 OpName %c "c" 332 OpDecorate %c Location 0 333 OpDecorate %23 Uniform 334 %void = OpTypeVoid 335 %3 = OpTypeFunction %void 336 %int = OpTypeInt 32 1 337%_ptr_Function_int = OpTypePointer Function %int 338 %int_0 = OpConstant %int 0 339 %bool = OpTypeBool 340%_ptr_Function_bool = OpTypePointer Function %bool 341 %float = OpTypeFloat 32 342 %v4float = OpTypeVector %float 4 343%_ptr_Input_v4float = OpTypePointer Input %v4float 344 %c = OpVariable %_ptr_Input_v4float Input 345 %uint = OpTypeInt 32 0 346 %uint_0 = OpConstant %uint 0 347%_ptr_Input_float = OpTypePointer Input %float 348 %float_0 = OpConstant %float 0 349 %int_10 = OpConstant %int 10 350 %int_1 = OpConstant %int 1 351 %main = OpFunction %void None %3 352 %5 = OpLabel 353 %20 = OpAccessChain %_ptr_Input_float %c %uint_0 354 %21 = OpLoad %float %20 355 %23 = OpFOrdEqual %bool %21 %float_0 356 OpBranch %24 357 %24 = OpLabel 358 %42 = OpPhi %int %int_0 %5 %41 %27 359 OpLoopMerge %26 %27 None 360 OpBranch %28 361 %28 = OpLabel 362 %31 = OpSLessThan %bool %42 %int_10 363 OpBranchConditional %31 %25 %26 364 %25 = OpLabel 365 OpSelectionMerge %34 None 366 OpBranchConditional %23 %33 %36 367 %33 = OpLabel 368 OpBranch %27 369 %36 = OpLabel 370 %39 = OpIAdd %int %42 %int_1 371 OpBranch %34 372 %34 = OpLabel 373 OpBranch %27 374 %27 = OpLabel 375 %43 = OpPhi %int %42 %33 %39 %34 376 %41 = OpIAdd %int %43 %int_1 377 OpBranch %24 378 %26 = OpLabel 379 OpReturn 380 OpFunctionEnd 381 )"; 382 383 SinglePassRunAndMatch<LoopUnswitchPass>(text, true); 384} 385 386/* 387Generated from the following GLSL + --eliminate-local-multi-store 388 389#version 330 core 390in vec4 c; 391void main() { 392 int i = 0; 393 bool cond = c[0] == 0; 394 for (; i < 10; i++) { 395 if (cond) { 396 i++; 397 } 398 else { 399 break; 400 } 401 } 402} 403*/ 404TEST_F(UnswitchTest, UnswitchKillLoop) { 405 const std::string text = R"( 406; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual 407; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None 408; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] 409 410; Loop specialized for false. 411; CHECK: [[loop_f]] = OpLabel 412; CHECK: OpBranch [[if_merge]] 413 414; Loop specialized for true. 415; CHECK: [[loop_t]] = OpLabel 416; CHECK-NEXT: OpBranch [[loop:%\w+]] 417; CHECK: [[loop]] = OpLabel 418; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]] 419; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 420; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 421; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]] 422; Check that we have i+=2. 423; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1 424; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1 425; CHECK: [[merge]] = OpLabel 426; CHECK-NEXT: OpBranch [[if_merge]] 427 428; CHECK: [[if_merge]] = OpLabel 429; CHECK-NEXT: OpReturn 430 431 OpCapability Shader 432 %1 = OpExtInstImport "GLSL.std.450" 433 OpMemoryModel Logical GLSL450 434 OpEntryPoint Fragment %main "main" %c 435 OpExecutionMode %main OriginUpperLeft 436 OpSource GLSL 330 437 OpName %main "main" 438 OpName %c "c" 439 OpDecorate %c Location 0 440 OpDecorate %23 Uniform 441 %void = OpTypeVoid 442 %3 = OpTypeFunction %void 443 %int = OpTypeInt 32 1 444%_ptr_Function_int = OpTypePointer Function %int 445 %int_0 = OpConstant %int 0 446 %bool = OpTypeBool 447%_ptr_Function_bool = OpTypePointer Function %bool 448 %float = OpTypeFloat 32 449 %v4float = OpTypeVector %float 4 450%_ptr_Input_v4float = OpTypePointer Input %v4float 451 %c = OpVariable %_ptr_Input_v4float Input 452 %uint = OpTypeInt 32 0 453 %uint_0 = OpConstant %uint 0 454%_ptr_Input_float = OpTypePointer Input %float 455 %float_0 = OpConstant %float 0 456 %int_10 = OpConstant %int 10 457 %int_1 = OpConstant %int 1 458 %main = OpFunction %void None %3 459 %5 = OpLabel 460 %20 = OpAccessChain %_ptr_Input_float %c %uint_0 461 %21 = OpLoad %float %20 462 %23 = OpFOrdEqual %bool %21 %float_0 463 OpBranch %24 464 %24 = OpLabel 465 %42 = OpPhi %int %int_0 %5 %41 %27 466 OpLoopMerge %26 %27 None 467 OpBranch %28 468 %28 = OpLabel 469 %31 = OpSLessThan %bool %42 %int_10 470 OpBranchConditional %31 %25 %26 471 %25 = OpLabel 472 OpSelectionMerge %34 None 473 OpBranchConditional %23 %33 %38 474 %33 = OpLabel 475 %37 = OpIAdd %int %42 %int_1 476 OpBranch %34 477 %38 = OpLabel 478 OpBranch %26 479 %34 = OpLabel 480 OpBranch %27 481 %27 = OpLabel 482 %41 = OpIAdd %int %37 %int_1 483 OpBranch %24 484 %26 = OpLabel 485 OpReturn 486 OpFunctionEnd 487 )"; 488 489 SinglePassRunAndMatch<LoopUnswitchPass>(text, true); 490} 491 492/* 493Generated from the following GLSL + --eliminate-local-multi-store 494 495#version 330 core 496in vec4 c; 497void main() { 498 int i = 0; 499 int cond = int(c[0]); 500 for (; i < 10; i++) { 501 switch (cond) { 502 case 0: 503 return; 504 case 1: 505 discard; 506 case 2: 507 break; 508 default: 509 break; 510 } 511 } 512 bool cond2 = i == 9; 513} 514*/ 515TEST_F(UnswitchTest, UnswitchSwitch) { 516 const std::string text = R"( 517; CHECK: [[cst_cond:%\w+]] = OpConvertFToS 518; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None 519; CHECK-NEXT: OpSwitch [[cst_cond]] [[default:%\w+]] 0 [[loop_0:%\w+]] 1 [[loop_1:%\w+]] 2 [[loop_2:%\w+]] 520 521; Loop specialized for 2. 522; CHECK: [[loop_2]] = OpLabel 523; CHECK-NEXT: OpBranch [[loop:%\w+]] 524; CHECK: [[loop]] = OpLabel 525; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_2]] [[iv_i:%\w+]] [[continue:%\w+]] 526; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 527; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 528; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] 529; CHECK: [[loop_body]] = OpLabel 530; CHECK-NEXT: OpSelectionMerge 531; CHECK-NEXT: OpSwitch %int_2 532; CHECK: [[merge]] = OpLabel 533; CHECK-NEXT: OpBranch [[if_merge]] 534 535; Loop specialized for 1. 536; CHECK: [[loop_1]] = OpLabel 537; CHECK-NEXT: OpBranch [[loop:%\w+]] 538; CHECK: [[loop]] = OpLabel 539; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_1]] [[iv_i:%\w+]] [[continue:%\w+]] 540; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 541; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 542; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] 543; CHECK: [[loop_body]] = OpLabel 544; CHECK-NEXT: OpSelectionMerge 545; CHECK-NEXT: OpSwitch %int_1 546; CHECK: [[merge]] = OpLabel 547; CHECK-NEXT: OpBranch [[if_merge]] 548 549; Loop specialized for 0. 550; CHECK: [[loop_0]] = OpLabel 551; CHECK-NEXT: OpBranch [[loop:%\w+]] 552; CHECK: [[loop]] = OpLabel 553; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_0]] [[iv_i:%\w+]] [[continue:%\w+]] 554; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 555; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 556; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] 557; CHECK: [[loop_body]] = OpLabel 558; CHECK-NEXT: OpSelectionMerge 559; CHECK-NEXT: OpSwitch %int_0 560; CHECK: [[merge]] = OpLabel 561; CHECK-NEXT: OpBranch [[if_merge]] 562 563; Loop specialized for the default case. 564; CHECK: [[default]] = OpLabel 565; CHECK-NEXT: OpBranch [[loop:%\w+]] 566; CHECK: [[loop]] = OpLabel 567; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[default]] [[iv_i:%\w+]] [[continue:%\w+]] 568; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 569; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} 570; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] 571; CHECK: [[loop_body]] = OpLabel 572; CHECK-NEXT: OpSelectionMerge 573; CHECK-NEXT: OpSwitch %uint_3 574; CHECK: [[merge]] = OpLabel 575; CHECK-NEXT: OpBranch [[if_merge]] 576 577; CHECK: [[if_merge]] = OpLabel 578; CHECK-NEXT: OpReturn 579 OpCapability Shader 580 %1 = OpExtInstImport "GLSL.std.450" 581 OpMemoryModel Logical GLSL450 582 OpEntryPoint Fragment %main "main" %c 583 OpExecutionMode %main OriginUpperLeft 584 OpSource GLSL 330 585 OpName %main "main" 586 OpName %c "c" 587 OpDecorate %c Location 0 588 OpDecorate %20 Uniform 589 %void = OpTypeVoid 590 %3 = OpTypeFunction %void 591 %int = OpTypeInt 32 1 592%_ptr_Function_int = OpTypePointer Function %int 593 %int_0 = OpConstant %int 0 594 %float = OpTypeFloat 32 595 %v4float = OpTypeVector %float 4 596%_ptr_Input_v4float = OpTypePointer Input %v4float 597 %c = OpVariable %_ptr_Input_v4float Input 598 %uint = OpTypeInt 32 0 599 %uint_0 = OpConstant %uint 0 600%_ptr_Input_float = OpTypePointer Input %float 601 %int_10 = OpConstant %int 10 602 %bool = OpTypeBool 603 %int_1 = OpConstant %int 1 604%_ptr_Function_bool = OpTypePointer Function %bool 605 %main = OpFunction %void None %3 606 %5 = OpLabel 607 %18 = OpAccessChain %_ptr_Input_float %c %uint_0 608 %19 = OpLoad %float %18 609 %20 = OpConvertFToS %int %19 610 OpBranch %21 611 %21 = OpLabel 612 %49 = OpPhi %int %int_0 %5 %43 %24 613 OpLoopMerge %23 %24 None 614 OpBranch %25 615 %25 = OpLabel 616 %29 = OpSLessThan %bool %49 %int_10 617 OpBranchConditional %29 %22 %23 618 %22 = OpLabel 619 OpSelectionMerge %35 None 620 OpSwitch %20 %34 0 %31 1 %32 2 %33 621 %34 = OpLabel 622 OpBranch %35 623 %31 = OpLabel 624 OpReturn 625 %32 = OpLabel 626 OpKill 627 %33 = OpLabel 628 OpBranch %35 629 %35 = OpLabel 630 OpBranch %24 631 %24 = OpLabel 632 %43 = OpIAdd %int %49 %int_1 633 OpBranch %21 634 %23 = OpLabel 635 OpReturn 636 OpFunctionEnd 637 )"; 638 639 SinglePassRunAndMatch<LoopUnswitchPass>(text, true); 640} 641 642/* 643Generated from the following GLSL + --eliminate-local-multi-store 644 645#version 440 core 646layout(location = 0)in vec4 c; 647void main() { 648 int i = 0; 649 int j = 0; 650 int k = 0; 651 bool cond = c[0] == 0; 652 for (; i < 10; i++) { 653 for (; j < 10; j++) { 654 if (cond) { 655 i++; 656 } else { 657 j++; 658 } 659 } 660 } 661} 662*/ 663TEST_F(UnswitchTest, UnSwitchNested) { 664 // Test that an branch can be unswitched out of two nested loops. 665 const std::string text = R"( 666; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual 667; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None 668; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] 669 670; Loop specialized for false 671; CHECK: [[loop_f]] = OpLabel 672; CHECK-NEXT: OpBranch [[loop:%\w+]] 673; CHECK: [[loop]] = OpLabel 674; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue:%\w+]] 675; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue]] 676; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 677; CHECK-NOT: [[merge]] = OpLabel 678; CHECK: OpLoopMerge 679; CHECK-NEXT: OpBranch [[bb1:%\w+]] 680; CHECK: [[bb1]] = OpLabel 681; CHECK-NEXT: OpSLessThan 682; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]] 683; CHECK: [[bb2]] = OpLabel 684; CHECK-NEXT: OpSelectionMerge 685; CHECK-NEXT: OpBranchConditional %false 686; CHECK: [[merge]] = OpLabel 687 688; Loop specialized for true. Same as first loop except the branch condition is true. 689; CHECK: [[loop_t]] = OpLabel 690; CHECK-NEXT: OpBranch [[loop:%\w+]] 691; CHECK: [[loop]] = OpLabel 692; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue:%\w+]] 693; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue]] 694; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None 695; CHECK-NOT: [[merge]] = OpLabel 696; CHECK: OpLoopMerge 697; CHECK-NEXT: OpBranch [[bb1:%\w+]] 698; CHECK: [[bb1]] = OpLabel 699; CHECK-NEXT: OpSLessThan 700; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]] 701; CHECK: [[bb2]] = OpLabel 702; CHECK-NEXT: OpSelectionMerge 703; CHECK-NEXT: OpBranchConditional %true 704; CHECK: [[merge]] = OpLabel 705 706 OpCapability Shader 707 %1 = OpExtInstImport "GLSL.std.450" 708 OpMemoryModel Logical GLSL450 709 OpEntryPoint Fragment %main "main" %c 710 OpExecutionMode %main OriginUpperLeft 711 OpSource GLSL 440 712 OpName %main "main" 713 OpName %c "c" 714 OpDecorate %c Location 0 715 OpDecorate %25 Uniform 716 %void = OpTypeVoid 717 %3 = OpTypeFunction %void 718 %int = OpTypeInt 32 1 719%_ptr_Function_int = OpTypePointer Function %int 720 %int_0 = OpConstant %int 0 721 %bool = OpTypeBool 722%_ptr_Function_bool = OpTypePointer Function %bool 723 %float = OpTypeFloat 32 724 %v4float = OpTypeVector %float 4 725%_ptr_Input_v4float = OpTypePointer Input %v4float 726 %c = OpVariable %_ptr_Input_v4float Input 727 %uint = OpTypeInt 32 0 728 %uint_0 = OpConstant %uint 0 729%_ptr_Input_float = OpTypePointer Input %float 730 %float_0 = OpConstant %float 0 731 %int_10 = OpConstant %int 10 732 %int_1 = OpConstant %int 1 733 %main = OpFunction %void None %3 734 %5 = OpLabel 735 %22 = OpAccessChain %_ptr_Input_float %c %uint_0 736 %23 = OpLoad %float %22 737 %25 = OpFOrdEqual %bool %23 %float_0 738 OpBranch %26 739 %26 = OpLabel 740 %67 = OpPhi %int %int_0 %5 %52 %29 741 %68 = OpPhi %int %int_0 %5 %70 %29 742 OpLoopMerge %28 %29 None 743 OpBranch %30 744 %30 = OpLabel 745 %33 = OpSLessThan %bool %67 %int_10 746 OpBranchConditional %33 %27 %28 747 %27 = OpLabel 748 OpBranch %34 749 %34 = OpLabel 750 %69 = OpPhi %int %67 %27 %46 %37 751 %70 = OpPhi %int %68 %27 %50 %37 752 OpLoopMerge %36 %37 None 753 OpBranch %38 754 %38 = OpLabel 755 %40 = OpSLessThan %bool %70 %int_10 756 OpBranchConditional %40 %35 %36 757 %35 = OpLabel 758 OpSelectionMerge %43 None 759 OpBranchConditional %25 %42 %47 760 %42 = OpLabel 761 %46 = OpIAdd %int %69 %int_1 762 OpBranch %43 763 %47 = OpLabel 764 OpReturn 765 %43 = OpLabel 766 OpBranch %37 767 %37 = OpLabel 768 %50 = OpIAdd %int %70 %int_1 769 OpBranch %34 770 %36 = OpLabel 771 OpBranch %29 772 %29 = OpLabel 773 %52 = OpIAdd %int %69 %int_1 774 OpBranch %26 775 %28 = OpLabel 776 OpReturn 777 OpFunctionEnd 778)"; 779 780 SinglePassRunAndMatch<LoopUnswitchPass>(text, true); 781} 782 783/* 784Generated from the following GLSL + --eliminate-local-multi-store 785 786#version 330 core 787in vec4 c; 788void main() { 789 bool cond = false; 790 if (c[0] == 0) { 791 cond = c[1] == 0; 792 } else { 793 cond = c[2] == 0; 794 } 795 for (int i = 0; i < 10; i++) { 796 if (cond) { 797 i++; 798 } 799 } 800} 801*/ 802TEST_F(UnswitchTest, UnswitchNotUniform) { 803 // Check that the unswitch is not triggered (condition loop invariant but not 804 // uniform) 805 const std::string text = R"( 806 OpCapability Shader 807 %1 = OpExtInstImport "GLSL.std.450" 808 OpMemoryModel Logical GLSL450 809 OpEntryPoint Fragment %main "main" %c 810 OpExecutionMode %main OriginUpperLeft 811 OpSource GLSL 330 812 OpName %main "main" 813 OpName %c "c" 814 OpDecorate %c Location 0 815 %void = OpTypeVoid 816 %3 = OpTypeFunction %void 817 %bool = OpTypeBool 818%_ptr_Function_bool = OpTypePointer Function %bool 819 %float = OpTypeFloat 32 820 %v4float = OpTypeVector %float 4 821%_ptr_Input_v4float = OpTypePointer Input %v4float 822 %c = OpVariable %_ptr_Input_v4float Input 823 %uint = OpTypeInt 32 0 824 %uint_0 = OpConstant %uint 0 825%_ptr_Input_float = OpTypePointer Input %float 826 %float_0 = OpConstant %float 0 827 %uint_1 = OpConstant %uint 1 828 %uint_2 = OpConstant %uint 2 829 %int = OpTypeInt 32 1 830%_ptr_Function_int = OpTypePointer Function %int 831 %int_0 = OpConstant %int 0 832 %int_10 = OpConstant %int 10 833 %int_1 = OpConstant %int 1 834 %main = OpFunction %void None %3 835 %5 = OpLabel 836 %17 = OpAccessChain %_ptr_Input_float %c %uint_0 837 %18 = OpLoad %float %17 838 %20 = OpFOrdEqual %bool %18 %float_0 839 OpSelectionMerge %22 None 840 OpBranchConditional %20 %21 %27 841 %21 = OpLabel 842 %24 = OpAccessChain %_ptr_Input_float %c %uint_1 843 %25 = OpLoad %float %24 844 %26 = OpFOrdEqual %bool %25 %float_0 845 OpBranch %22 846 %27 = OpLabel 847 %29 = OpAccessChain %_ptr_Input_float %c %uint_2 848 %30 = OpLoad %float %29 849 %31 = OpFOrdEqual %bool %30 %float_0 850 OpBranch %22 851 %22 = OpLabel 852 %52 = OpPhi %bool %26 %21 %31 %27 853 OpBranch %36 854 %36 = OpLabel 855 %53 = OpPhi %int %int_0 %22 %51 %39 856 OpLoopMerge %38 %39 None 857 OpBranch %40 858 %40 = OpLabel 859 %43 = OpSLessThan %bool %53 %int_10 860 OpBranchConditional %43 %37 %38 861 %37 = OpLabel 862 OpSelectionMerge %46 None 863 OpBranchConditional %52 %45 %46 864 %45 = OpLabel 865 %49 = OpIAdd %int %53 %int_1 866 OpBranch %46 867 %46 = OpLabel 868 %54 = OpPhi %int %53 %37 %49 %45 869 OpBranch %39 870 %39 = OpLabel 871 %51 = OpIAdd %int %54 %int_1 872 OpBranch %36 873 %38 = OpLabel 874 OpReturn 875 OpFunctionEnd 876 )"; 877 878 auto result = 879 SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false); 880 881 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); 882} 883 884TEST_F(UnswitchTest, DontUnswitchLatch) { 885 // Check that the unswitch is not triggered for the latch branch. 886 const std::string text = R"( 887 OpCapability Shader 888 %1 = OpExtInstImport "GLSL.std.450" 889 OpMemoryModel Logical GLSL450 890 OpEntryPoint Fragment %4 "main" 891 OpExecutionMode %4 OriginUpperLeft 892 OpSource ESSL 310 893 %void = OpTypeVoid 894 %3 = OpTypeFunction %void 895 %bool = OpTypeBool 896%false = OpConstantFalse %bool 897 %4 = OpFunction %void None %3 898 %5 = OpLabel 899 OpBranch %6 900 %6 = OpLabel 901 OpLoopMerge %8 %9 None 902 OpBranch %7 903 %7 = OpLabel 904 OpBranch %9 905 %9 = OpLabel 906 OpBranchConditional %false %6 %8 907 %8 = OpLabel 908 OpReturn 909 OpFunctionEnd 910 )"; 911 912 auto result = 913 SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false); 914 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); 915} 916 917TEST_F(UnswitchTest, DontUnswitchConstantCondition) { 918 const std::string text = R"( 919 OpCapability Shader 920 %1 = OpExtInstImport "GLSL.std.450" 921 OpMemoryModel Logical GLSL450 922 OpEntryPoint Fragment %main "main" 923 OpExecutionMode %main OriginLowerLeft 924 OpSource GLSL 450 925 OpName %main "main" 926 %void = OpTypeVoid 927 %4 = OpTypeFunction %void 928 %int = OpTypeInt 32 1 929 %int_0 = OpConstant %int 0 930 %bool = OpTypeBool 931 %true = OpConstantTrue %bool 932 %int_1 = OpConstant %int 1 933 %main = OpFunction %void None %4 934 %10 = OpLabel 935 OpBranch %11 936 %11 = OpLabel 937 %12 = OpPhi %int %int_0 %10 %13 %14 938 OpLoopMerge %15 %14 None 939 OpBranch %16 940 %16 = OpLabel 941 %17 = OpSLessThan %bool %12 %int_1 942 OpBranchConditional %17 %18 %15 943 %18 = OpLabel 944 OpSelectionMerge %19 None 945 OpBranchConditional %true %20 %19 946 %20 = OpLabel 947 %21 = OpIAdd %int %12 %int_1 948 OpBranch %19 949 %19 = OpLabel 950 %22 = OpPhi %int %21 %20 %12 %18 951 OpBranch %14 952 %14 = OpLabel 953 %13 = OpIAdd %int %22 %int_1 954 OpBranch %11 955 %15 = OpLabel 956 OpReturn 957 OpFunctionEnd 958 )"; 959 960 auto result = 961 SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false); 962 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); 963} 964 965} // namespace 966} // namespace opt 967} // namespace spvtools 968