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 <memory> 16 #include <string> 17 #include <vector> 18 19 #include "gmock/gmock.h" 20 #include "source/opt/loop_unroller.h" 21 #include "source/opt/loop_utils.h" 22 #include "source/opt/pass.h" 23 #include "test/opt/assembly_builder.h" 24 #include "test/opt/function_utils.h" 25 #include "test/opt/pass_fixture.h" 26 #include "test/opt/pass_utils.h" 27 28 namespace spvtools { 29 namespace opt { 30 namespace { 31 32 using ::testing::UnorderedElementsAre; 33 using PassClassTest = PassTest<::testing::Test>; 34 35 /* 36 Generated from the following GLSL 37 #version 330 core 38 layout(location = 0) out vec4 c; 39 void main() { 40 float x[4]; 41 for (int i = 0; i < 4; ++i) { 42 x[i] = 1.0f; 43 } 44 } 45 */ 46 TEST_F(PassClassTest, SimpleFullyUnrollTest) { 47 // With LocalMultiStoreElimPass 48 const std::string text = R"( 49 OpCapability Shader 50 %1 = OpExtInstImport "GLSL.std.450" 51 OpMemoryModel Logical GLSL450 52 OpEntryPoint Fragment %2 "main" %3 53 OpExecutionMode %2 OriginUpperLeft 54 OpSource GLSL 330 55 OpName %2 "main" 56 OpName %5 "x" 57 OpName %3 "c" 58 OpDecorate %3 Location 0 59 %6 = OpTypeVoid 60 %7 = OpTypeFunction %6 61 %8 = OpTypeInt 32 1 62 %9 = OpTypePointer Function %8 63 %10 = OpConstant %8 0 64 %11 = OpConstant %8 4 65 %12 = OpTypeBool 66 %13 = OpTypeFloat 32 67 %14 = OpTypeInt 32 0 68 %15 = OpConstant %14 4 69 %16 = OpTypeArray %13 %15 70 %17 = OpTypePointer Function %16 71 %18 = OpConstant %13 1 72 %19 = OpTypePointer Function %13 73 %20 = OpConstant %8 1 74 %21 = OpTypeVector %13 4 75 %22 = OpTypePointer Output %21 76 %3 = OpVariable %22 Output 77 %2 = OpFunction %6 None %7 78 %23 = OpLabel 79 %5 = OpVariable %17 Function 80 OpBranch %24 81 %24 = OpLabel 82 %35 = OpPhi %8 %10 %23 %34 %26 83 OpLoopMerge %25 %26 Unroll 84 OpBranch %27 85 %27 = OpLabel 86 %29 = OpSLessThan %12 %35 %11 87 OpBranchConditional %29 %30 %25 88 %30 = OpLabel 89 %32 = OpAccessChain %19 %5 %35 90 OpStore %32 %18 91 OpBranch %26 92 %26 = OpLabel 93 %34 = OpIAdd %8 %35 %20 94 OpBranch %24 95 %25 = OpLabel 96 OpReturn 97 OpFunctionEnd 98 )"; 99 100 const std::string output = R"(OpCapability Shader 101 %1 = OpExtInstImport "GLSL.std.450" 102 OpMemoryModel Logical GLSL450 103 OpEntryPoint Fragment %2 "main" %3 104 OpExecutionMode %2 OriginUpperLeft 105 OpSource GLSL 330 106 OpName %2 "main" 107 OpName %4 "x" 108 OpName %3 "c" 109 OpDecorate %3 Location 0 110 %5 = OpTypeVoid 111 %6 = OpTypeFunction %5 112 %7 = OpTypeInt 32 1 113 %8 = OpTypePointer Function %7 114 %9 = OpConstant %7 0 115 %10 = OpConstant %7 4 116 %11 = OpTypeBool 117 %12 = OpTypeFloat 32 118 %13 = OpTypeInt 32 0 119 %14 = OpConstant %13 4 120 %15 = OpTypeArray %12 %14 121 %16 = OpTypePointer Function %15 122 %17 = OpConstant %12 1 123 %18 = OpTypePointer Function %12 124 %19 = OpConstant %7 1 125 %20 = OpTypeVector %12 4 126 %21 = OpTypePointer Output %20 127 %3 = OpVariable %21 Output 128 %2 = OpFunction %5 None %6 129 %22 = OpLabel 130 %4 = OpVariable %16 Function 131 OpBranch %23 132 %23 = OpLabel 133 OpBranch %28 134 %28 = OpLabel 135 %29 = OpSLessThan %11 %9 %10 136 OpBranch %30 137 %30 = OpLabel 138 %31 = OpAccessChain %18 %4 %9 139 OpStore %31 %17 140 OpBranch %26 141 %26 = OpLabel 142 %25 = OpIAdd %7 %9 %19 143 OpBranch %32 144 %32 = OpLabel 145 OpBranch %34 146 %34 = OpLabel 147 %35 = OpSLessThan %11 %25 %10 148 OpBranch %36 149 %36 = OpLabel 150 %37 = OpAccessChain %18 %4 %25 151 OpStore %37 %17 152 OpBranch %38 153 %38 = OpLabel 154 %39 = OpIAdd %7 %25 %19 155 OpBranch %40 156 %40 = OpLabel 157 OpBranch %42 158 %42 = OpLabel 159 %43 = OpSLessThan %11 %39 %10 160 OpBranch %44 161 %44 = OpLabel 162 %45 = OpAccessChain %18 %4 %39 163 OpStore %45 %17 164 OpBranch %46 165 %46 = OpLabel 166 %47 = OpIAdd %7 %39 %19 167 OpBranch %48 168 %48 = OpLabel 169 OpBranch %50 170 %50 = OpLabel 171 %51 = OpSLessThan %11 %47 %10 172 OpBranch %52 173 %52 = OpLabel 174 %53 = OpAccessChain %18 %4 %47 175 OpStore %53 %17 176 OpBranch %54 177 %54 = OpLabel 178 %55 = OpIAdd %7 %47 %19 179 OpBranch %27 180 %27 = OpLabel 181 OpReturn 182 OpFunctionEnd 183 )"; 184 185 std::unique_ptr<IRContext> context = 186 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 187 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 188 Module* module = context->module(); 189 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 190 << text << std::endl; 191 192 LoopUnroller loop_unroller; 193 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 194 SinglePassRunAndCheck<LoopUnroller>(text, output, false); 195 } 196 197 template <int factor> 198 class PartialUnrollerTestPass : public Pass { 199 public: 200 PartialUnrollerTestPass() : Pass() {} 201 202 const char* name() const override { return "Loop unroller"; } 203 204 Status Process() override { 205 for (Function& f : *context()->module()) { 206 LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); 207 for (auto& loop : loop_descriptor) { 208 LoopUtils loop_utils{context(), &loop}; 209 loop_utils.PartiallyUnroll(factor); 210 } 211 } 212 213 return Pass::Status::SuccessWithChange; 214 } 215 }; 216 217 /* 218 Generated from the following GLSL 219 #version 330 core 220 layout(location = 0) out vec4 c; 221 void main() { 222 float x[10]; 223 for (int i = 0; i < 10; ++i) { 224 x[i] = 1.0f; 225 } 226 } 227 */ 228 TEST_F(PassClassTest, SimplePartialUnroll) { 229 // With LocalMultiStoreElimPass 230 const std::string text = R"( 231 OpCapability Shader 232 %1 = OpExtInstImport "GLSL.std.450" 233 OpMemoryModel Logical GLSL450 234 OpEntryPoint Fragment %2 "main" %3 235 OpExecutionMode %2 OriginUpperLeft 236 OpSource GLSL 330 237 OpName %2 "main" 238 OpName %5 "x" 239 OpName %3 "c" 240 OpDecorate %3 Location 0 241 %6 = OpTypeVoid 242 %7 = OpTypeFunction %6 243 %8 = OpTypeInt 32 1 244 %9 = OpTypePointer Function %8 245 %10 = OpConstant %8 0 246 %11 = OpConstant %8 10 247 %12 = OpTypeBool 248 %13 = OpTypeFloat 32 249 %14 = OpTypeInt 32 0 250 %15 = OpConstant %14 10 251 %16 = OpTypeArray %13 %15 252 %17 = OpTypePointer Function %16 253 %18 = OpConstant %13 1 254 %19 = OpTypePointer Function %13 255 %20 = OpConstant %8 1 256 %21 = OpTypeVector %13 4 257 %22 = OpTypePointer Output %21 258 %3 = OpVariable %22 Output 259 %2 = OpFunction %6 None %7 260 %23 = OpLabel 261 %5 = OpVariable %17 Function 262 OpBranch %24 263 %24 = OpLabel 264 %35 = OpPhi %8 %10 %23 %34 %26 265 OpLoopMerge %25 %26 Unroll 266 OpBranch %27 267 %27 = OpLabel 268 %29 = OpSLessThan %12 %35 %11 269 OpBranchConditional %29 %30 %25 270 %30 = OpLabel 271 %32 = OpAccessChain %19 %5 %35 272 OpStore %32 %18 273 OpBranch %26 274 %26 = OpLabel 275 %34 = OpIAdd %8 %35 %20 276 OpBranch %24 277 %25 = OpLabel 278 OpReturn 279 OpFunctionEnd 280 )"; 281 282 const std::string output = R"(OpCapability Shader 283 %1 = OpExtInstImport "GLSL.std.450" 284 OpMemoryModel Logical GLSL450 285 OpEntryPoint Fragment %2 "main" %3 286 OpExecutionMode %2 OriginUpperLeft 287 OpSource GLSL 330 288 OpName %2 "main" 289 OpName %4 "x" 290 OpName %3 "c" 291 OpDecorate %3 Location 0 292 %5 = OpTypeVoid 293 %6 = OpTypeFunction %5 294 %7 = OpTypeInt 32 1 295 %8 = OpTypePointer Function %7 296 %9 = OpConstant %7 0 297 %10 = OpConstant %7 10 298 %11 = OpTypeBool 299 %12 = OpTypeFloat 32 300 %13 = OpTypeInt 32 0 301 %14 = OpConstant %13 10 302 %15 = OpTypeArray %12 %14 303 %16 = OpTypePointer Function %15 304 %17 = OpConstant %12 1 305 %18 = OpTypePointer Function %12 306 %19 = OpConstant %7 1 307 %20 = OpTypeVector %12 4 308 %21 = OpTypePointer Output %20 309 %3 = OpVariable %21 Output 310 %2 = OpFunction %5 None %6 311 %22 = OpLabel 312 %4 = OpVariable %16 Function 313 OpBranch %23 314 %23 = OpLabel 315 %24 = OpPhi %7 %9 %22 %39 %38 316 OpLoopMerge %27 %38 DontUnroll 317 OpBranch %28 318 %28 = OpLabel 319 %29 = OpSLessThan %11 %24 %10 320 OpBranchConditional %29 %30 %27 321 %30 = OpLabel 322 %31 = OpAccessChain %18 %4 %24 323 OpStore %31 %17 324 OpBranch %26 325 %26 = OpLabel 326 %25 = OpIAdd %7 %24 %19 327 OpBranch %32 328 %32 = OpLabel 329 OpBranch %34 330 %34 = OpLabel 331 %35 = OpSLessThan %11 %25 %10 332 OpBranch %36 333 %36 = OpLabel 334 %37 = OpAccessChain %18 %4 %25 335 OpStore %37 %17 336 OpBranch %38 337 %38 = OpLabel 338 %39 = OpIAdd %7 %25 %19 339 OpBranch %23 340 %27 = OpLabel 341 OpReturn 342 OpFunctionEnd 343 )"; 344 345 std::unique_ptr<IRContext> context = 346 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 347 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 348 Module* module = context->module(); 349 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 350 << text << std::endl; 351 352 LoopUnroller loop_unroller; 353 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 354 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(text, output, false); 355 } 356 357 /* 358 Generated from the following GLSL 359 #version 330 core 360 layout(location = 0) out vec4 c; 361 void main() { 362 float x[10]; 363 for (int i = 0; i < 10; ++i) { 364 x[i] = 1.0f; 365 } 366 } 367 */ 368 TEST_F(PassClassTest, SimpleUnevenPartialUnroll) { 369 // With LocalMultiStoreElimPass 370 const std::string text = R"( 371 OpCapability Shader 372 %1 = OpExtInstImport "GLSL.std.450" 373 OpMemoryModel Logical GLSL450 374 OpEntryPoint Fragment %2 "main" %3 375 OpExecutionMode %2 OriginUpperLeft 376 OpSource GLSL 330 377 OpName %2 "main" 378 OpName %5 "x" 379 OpName %3 "c" 380 OpDecorate %3 Location 0 381 %6 = OpTypeVoid 382 %7 = OpTypeFunction %6 383 %8 = OpTypeInt 32 1 384 %9 = OpTypePointer Function %8 385 %10 = OpConstant %8 0 386 %11 = OpConstant %8 10 387 %12 = OpTypeBool 388 %13 = OpTypeFloat 32 389 %14 = OpTypeInt 32 0 390 %15 = OpConstant %14 10 391 %16 = OpTypeArray %13 %15 392 %17 = OpTypePointer Function %16 393 %18 = OpConstant %13 1 394 %19 = OpTypePointer Function %13 395 %20 = OpConstant %8 1 396 %21 = OpTypeVector %13 4 397 %22 = OpTypePointer Output %21 398 %3 = OpVariable %22 Output 399 %2 = OpFunction %6 None %7 400 %23 = OpLabel 401 %5 = OpVariable %17 Function 402 OpBranch %24 403 %24 = OpLabel 404 %35 = OpPhi %8 %10 %23 %34 %26 405 OpLoopMerge %25 %26 Unroll 406 OpBranch %27 407 %27 = OpLabel 408 %29 = OpSLessThan %12 %35 %11 409 OpBranchConditional %29 %30 %25 410 %30 = OpLabel 411 %32 = OpAccessChain %19 %5 %35 412 OpStore %32 %18 413 OpBranch %26 414 %26 = OpLabel 415 %34 = OpIAdd %8 %35 %20 416 OpBranch %24 417 %25 = OpLabel 418 OpReturn 419 OpFunctionEnd 420 )"; 421 422 const std::string output = R"(OpCapability Shader 423 %1 = OpExtInstImport "GLSL.std.450" 424 OpMemoryModel Logical GLSL450 425 OpEntryPoint Fragment %2 "main" %3 426 OpExecutionMode %2 OriginUpperLeft 427 OpSource GLSL 330 428 OpName %2 "main" 429 OpName %4 "x" 430 OpName %3 "c" 431 OpDecorate %3 Location 0 432 %5 = OpTypeVoid 433 %6 = OpTypeFunction %5 434 %7 = OpTypeInt 32 1 435 %8 = OpTypePointer Function %7 436 %9 = OpConstant %7 0 437 %10 = OpConstant %7 10 438 %11 = OpTypeBool 439 %12 = OpTypeFloat 32 440 %13 = OpTypeInt 32 0 441 %14 = OpConstant %13 10 442 %15 = OpTypeArray %12 %14 443 %16 = OpTypePointer Function %15 444 %17 = OpConstant %12 1 445 %18 = OpTypePointer Function %12 446 %19 = OpConstant %7 1 447 %20 = OpTypeVector %12 4 448 %21 = OpTypePointer Output %20 449 %3 = OpVariable %21 Output 450 %58 = OpConstant %13 1 451 %2 = OpFunction %5 None %6 452 %22 = OpLabel 453 %4 = OpVariable %16 Function 454 OpBranch %23 455 %23 = OpLabel 456 %24 = OpPhi %7 %9 %22 %25 %26 457 OpLoopMerge %32 %26 Unroll 458 OpBranch %28 459 %28 = OpLabel 460 %29 = OpSLessThan %11 %24 %58 461 OpBranchConditional %29 %30 %32 462 %30 = OpLabel 463 %31 = OpAccessChain %18 %4 %24 464 OpStore %31 %17 465 OpBranch %26 466 %26 = OpLabel 467 %25 = OpIAdd %7 %24 %19 468 OpBranch %23 469 %32 = OpLabel 470 OpBranch %33 471 %33 = OpLabel 472 %34 = OpPhi %7 %24 %32 %57 %56 473 OpLoopMerge %41 %56 DontUnroll 474 OpBranch %35 475 %35 = OpLabel 476 %36 = OpSLessThan %11 %34 %10 477 OpBranchConditional %36 %37 %41 478 %37 = OpLabel 479 %38 = OpAccessChain %18 %4 %34 480 OpStore %38 %17 481 OpBranch %39 482 %39 = OpLabel 483 %40 = OpIAdd %7 %34 %19 484 OpBranch %42 485 %42 = OpLabel 486 OpBranch %44 487 %44 = OpLabel 488 %45 = OpSLessThan %11 %40 %10 489 OpBranch %46 490 %46 = OpLabel 491 %47 = OpAccessChain %18 %4 %40 492 OpStore %47 %17 493 OpBranch %48 494 %48 = OpLabel 495 %49 = OpIAdd %7 %40 %19 496 OpBranch %50 497 %50 = OpLabel 498 OpBranch %52 499 %52 = OpLabel 500 %53 = OpSLessThan %11 %49 %10 501 OpBranch %54 502 %54 = OpLabel 503 %55 = OpAccessChain %18 %4 %49 504 OpStore %55 %17 505 OpBranch %56 506 %56 = OpLabel 507 %57 = OpIAdd %7 %49 %19 508 OpBranch %33 509 %41 = OpLabel 510 OpReturn 511 %27 = OpLabel 512 OpReturn 513 OpFunctionEnd 514 )"; 515 516 std::unique_ptr<IRContext> context = 517 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 518 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 519 Module* module = context->module(); 520 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 521 << text << std::endl; 522 523 LoopUnroller loop_unroller; 524 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 525 // By unrolling by a factor that doesn't divide evenly into the number of loop 526 // iterations we perfom an additional transform when partially unrolling to 527 // account for the remainder. 528 SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, output, false); 529 } 530 531 /* Generated from 532 #version 410 core 533 layout(location=0) flat in int upper_bound; 534 void main() { 535 float x[10]; 536 for (int i = 2; i < 8; i+=2) { 537 x[i] = i; 538 } 539 } 540 */ 541 TEST_F(PassClassTest, SimpleLoopIterationsCheck) { 542 // With LocalMultiStoreElimPass 543 const std::string text = R"( 544 OpCapability Shader 545 %1 = OpExtInstImport "GLSL.std.450" 546 OpMemoryModel Logical GLSL450 547 OpEntryPoint Fragment %2 "main" %3 548 OpExecutionMode %2 OriginUpperLeft 549 OpSource GLSL 410 550 OpName %2 "main" 551 OpName %5 "x" 552 OpName %3 "upper_bound" 553 OpDecorate %3 Flat 554 OpDecorate %3 Location 0 555 %6 = OpTypeVoid 556 %7 = OpTypeFunction %6 557 %8 = OpTypeInt 32 1 558 %9 = OpTypePointer Function %8 559 %10 = OpConstant %8 2 560 %11 = OpConstant %8 8 561 %12 = OpTypeBool 562 %13 = OpTypeFloat 32 563 %14 = OpTypeInt 32 0 564 %15 = OpConstant %14 10 565 %16 = OpTypeArray %13 %15 566 %17 = OpTypePointer Function %16 567 %18 = OpTypePointer Function %13 568 %19 = OpTypePointer Input %8 569 %3 = OpVariable %19 Input 570 %2 = OpFunction %6 None %7 571 %20 = OpLabel 572 %5 = OpVariable %17 Function 573 OpBranch %21 574 %21 = OpLabel 575 %34 = OpPhi %8 %10 %20 %33 %23 576 OpLoopMerge %22 %23 Unroll 577 OpBranch %24 578 %24 = OpLabel 579 %26 = OpSLessThan %12 %34 %11 580 OpBranchConditional %26 %27 %22 581 %27 = OpLabel 582 %30 = OpConvertSToF %13 %34 583 %31 = OpAccessChain %18 %5 %34 584 OpStore %31 %30 585 OpBranch %23 586 %23 = OpLabel 587 %33 = OpIAdd %8 %34 %10 588 OpBranch %21 589 %22 = OpLabel 590 OpReturn 591 OpFunctionEnd 592 )"; 593 594 std::unique_ptr<IRContext> context = 595 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 596 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 597 Module* module = context->module(); 598 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 599 << text << std::endl; 600 601 Function* f = spvtest::GetFunction(module, 2); 602 603 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); 604 EXPECT_EQ(loop_descriptor.NumLoops(), 1u); 605 606 Loop& loop = loop_descriptor.GetLoopByIndex(0); 607 608 EXPECT_TRUE(loop.HasUnrollLoopControl()); 609 610 BasicBlock* condition = loop.FindConditionBlock(); 611 EXPECT_EQ(condition->id(), 24u); 612 613 Instruction* induction = loop.FindConditionVariable(condition); 614 EXPECT_EQ(induction->result_id(), 34u); 615 616 LoopUtils loop_utils{context.get(), &loop}; 617 EXPECT_TRUE(loop_utils.CanPerformUnroll()); 618 619 size_t iterations = 0; 620 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(), 621 &iterations)); 622 EXPECT_EQ(iterations, 3u); 623 } 624 625 /* Generated from 626 #version 410 core 627 void main() { 628 float x[10]; 629 for (int i = -1; i < 6; i+=3) { 630 x[i] = i; 631 } 632 } 633 */ 634 TEST_F(PassClassTest, SimpleLoopIterationsCheckSignedInit) { 635 // With LocalMultiStoreElimPass 636 const std::string text = R"( 637 OpCapability Shader 638 %1 = OpExtInstImport "GLSL.std.450" 639 OpMemoryModel Logical GLSL450 640 OpEntryPoint Fragment %2 "main" %3 641 OpExecutionMode %2 OriginUpperLeft 642 OpSource GLSL 410 643 OpName %2 "main" 644 OpName %5 "x" 645 OpName %3 "upper_bound" 646 OpDecorate %3 Flat 647 OpDecorate %3 Location 0 648 %6 = OpTypeVoid 649 %7 = OpTypeFunction %6 650 %8 = OpTypeInt 32 1 651 %9 = OpTypePointer Function %8 652 %10 = OpConstant %8 -1 653 %11 = OpConstant %8 6 654 %12 = OpTypeBool 655 %13 = OpTypeFloat 32 656 %14 = OpTypeInt 32 0 657 %15 = OpConstant %14 10 658 %16 = OpTypeArray %13 %15 659 %17 = OpTypePointer Function %16 660 %18 = OpTypePointer Function %13 661 %19 = OpConstant %8 3 662 %20 = OpTypePointer Input %8 663 %3 = OpVariable %20 Input 664 %2 = OpFunction %6 None %7 665 %21 = OpLabel 666 %5 = OpVariable %17 Function 667 OpBranch %22 668 %22 = OpLabel 669 %35 = OpPhi %8 %10 %21 %34 %24 670 OpLoopMerge %23 %24 None 671 OpBranch %25 672 %25 = OpLabel 673 %27 = OpSLessThan %12 %35 %11 674 OpBranchConditional %27 %28 %23 675 %28 = OpLabel 676 %31 = OpConvertSToF %13 %35 677 %32 = OpAccessChain %18 %5 %35 678 OpStore %32 %31 679 OpBranch %24 680 %24 = OpLabel 681 %34 = OpIAdd %8 %35 %19 682 OpBranch %22 683 %23 = OpLabel 684 OpReturn 685 OpFunctionEnd 686 )"; 687 688 std::unique_ptr<IRContext> context = 689 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 690 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 691 Module* module = context->module(); 692 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 693 << text << std::endl; 694 695 Function* f = spvtest::GetFunction(module, 2); 696 697 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); 698 699 EXPECT_EQ(loop_descriptor.NumLoops(), 1u); 700 701 Loop& loop = loop_descriptor.GetLoopByIndex(0); 702 703 EXPECT_FALSE(loop.HasUnrollLoopControl()); 704 705 BasicBlock* condition = loop.FindConditionBlock(); 706 EXPECT_EQ(condition->id(), 25u); 707 708 Instruction* induction = loop.FindConditionVariable(condition); 709 EXPECT_EQ(induction->result_id(), 35u); 710 711 LoopUtils loop_utils{context.get(), &loop}; 712 EXPECT_TRUE(loop_utils.CanPerformUnroll()); 713 714 size_t iterations = 0; 715 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(), 716 &iterations)); 717 EXPECT_EQ(iterations, 3u); 718 } 719 720 /* 721 Generated from the following GLSL 722 #version 410 core 723 void main() { 724 float out_array[6]; 725 for (uint i = 0; i < 2; i++) { 726 for (int x = 0; x < 3; ++x) { 727 out_array[x + i*3] = i; 728 } 729 } 730 } 731 */ 732 TEST_F(PassClassTest, UnrollNestedLoops) { 733 // With LocalMultiStoreElimPass 734 const std::string text = R"( 735 OpCapability Shader 736 %1 = OpExtInstImport "GLSL.std.450" 737 OpMemoryModel Logical GLSL450 738 OpEntryPoint Fragment %4 "main" 739 OpExecutionMode %4 OriginUpperLeft 740 OpSource GLSL 410 741 OpName %4 "main" 742 OpName %35 "out_array" 743 %2 = OpTypeVoid 744 %3 = OpTypeFunction %2 745 %6 = OpTypeInt 32 0 746 %7 = OpTypePointer Function %6 747 %9 = OpConstant %6 0 748 %16 = OpConstant %6 2 749 %17 = OpTypeBool 750 %19 = OpTypeInt 32 1 751 %20 = OpTypePointer Function %19 752 %22 = OpConstant %19 0 753 %29 = OpConstant %19 3 754 %31 = OpTypeFloat 32 755 %32 = OpConstant %6 6 756 %33 = OpTypeArray %31 %32 757 %34 = OpTypePointer Function %33 758 %39 = OpConstant %6 3 759 %44 = OpTypePointer Function %31 760 %47 = OpConstant %19 1 761 %4 = OpFunction %2 None %3 762 %5 = OpLabel 763 %35 = OpVariable %34 Function 764 OpBranch %10 765 %10 = OpLabel 766 %51 = OpPhi %6 %9 %5 %50 %13 767 OpLoopMerge %12 %13 Unroll 768 OpBranch %14 769 %14 = OpLabel 770 %18 = OpULessThan %17 %51 %16 771 OpBranchConditional %18 %11 %12 772 %11 = OpLabel 773 OpBranch %23 774 %23 = OpLabel 775 %54 = OpPhi %19 %22 %11 %48 %26 776 OpLoopMerge %25 %26 Unroll 777 OpBranch %27 778 %27 = OpLabel 779 %30 = OpSLessThan %17 %54 %29 780 OpBranchConditional %30 %24 %25 781 %24 = OpLabel 782 %37 = OpBitcast %6 %54 783 %40 = OpIMul %6 %51 %39 784 %41 = OpIAdd %6 %37 %40 785 %43 = OpConvertUToF %31 %51 786 %45 = OpAccessChain %44 %35 %41 787 OpStore %45 %43 788 OpBranch %26 789 %26 = OpLabel 790 %48 = OpIAdd %19 %54 %47 791 OpBranch %23 792 %25 = OpLabel 793 OpBranch %13 794 %13 = OpLabel 795 %50 = OpIAdd %6 %51 %47 796 OpBranch %10 797 %12 = OpLabel 798 OpReturn 799 OpFunctionEnd 800 )"; 801 802 const std::string output = R"(OpCapability Shader 803 %1 = OpExtInstImport "GLSL.std.450" 804 OpMemoryModel Logical GLSL450 805 OpEntryPoint Fragment %2 "main" 806 OpExecutionMode %2 OriginUpperLeft 807 OpSource GLSL 410 808 OpName %2 "main" 809 OpName %3 "out_array" 810 %4 = OpTypeVoid 811 %5 = OpTypeFunction %4 812 %6 = OpTypeInt 32 0 813 %7 = OpTypePointer Function %6 814 %8 = OpConstant %6 0 815 %9 = OpConstant %6 2 816 %10 = OpTypeBool 817 %11 = OpTypeInt 32 1 818 %12 = OpTypePointer Function %11 819 %13 = OpConstant %11 0 820 %14 = OpConstant %11 3 821 %15 = OpTypeFloat 32 822 %16 = OpConstant %6 6 823 %17 = OpTypeArray %15 %16 824 %18 = OpTypePointer Function %17 825 %19 = OpConstant %6 3 826 %20 = OpTypePointer Function %15 827 %21 = OpConstant %11 1 828 %2 = OpFunction %4 None %5 829 %22 = OpLabel 830 %3 = OpVariable %18 Function 831 OpBranch %23 832 %23 = OpLabel 833 OpBranch %28 834 %28 = OpLabel 835 %29 = OpULessThan %10 %8 %9 836 OpBranch %30 837 %30 = OpLabel 838 OpBranch %31 839 %31 = OpLabel 840 OpBranch %36 841 %36 = OpLabel 842 %37 = OpSLessThan %10 %13 %14 843 OpBranch %38 844 %38 = OpLabel 845 %39 = OpBitcast %6 %13 846 %40 = OpIMul %6 %8 %19 847 %41 = OpIAdd %6 %39 %40 848 %42 = OpConvertUToF %15 %8 849 %43 = OpAccessChain %20 %3 %41 850 OpStore %43 %42 851 OpBranch %34 852 %34 = OpLabel 853 %33 = OpIAdd %11 %13 %21 854 OpBranch %44 855 %44 = OpLabel 856 OpBranch %46 857 %46 = OpLabel 858 %47 = OpSLessThan %10 %33 %14 859 OpBranch %48 860 %48 = OpLabel 861 %49 = OpBitcast %6 %33 862 %50 = OpIMul %6 %8 %19 863 %51 = OpIAdd %6 %49 %50 864 %52 = OpConvertUToF %15 %8 865 %53 = OpAccessChain %20 %3 %51 866 OpStore %53 %52 867 OpBranch %54 868 %54 = OpLabel 869 %55 = OpIAdd %11 %33 %21 870 OpBranch %56 871 %56 = OpLabel 872 OpBranch %58 873 %58 = OpLabel 874 %59 = OpSLessThan %10 %55 %14 875 OpBranch %60 876 %60 = OpLabel 877 %61 = OpBitcast %6 %55 878 %62 = OpIMul %6 %8 %19 879 %63 = OpIAdd %6 %61 %62 880 %64 = OpConvertUToF %15 %8 881 %65 = OpAccessChain %20 %3 %63 882 OpStore %65 %64 883 OpBranch %66 884 %66 = OpLabel 885 %67 = OpIAdd %11 %55 %21 886 OpBranch %35 887 %35 = OpLabel 888 OpBranch %26 889 %26 = OpLabel 890 %25 = OpIAdd %6 %8 %21 891 OpBranch %68 892 %68 = OpLabel 893 OpBranch %70 894 %70 = OpLabel 895 %71 = OpULessThan %10 %25 %9 896 OpBranch %72 897 %72 = OpLabel 898 OpBranch %73 899 %73 = OpLabel 900 OpBranch %74 901 %74 = OpLabel 902 %75 = OpSLessThan %10 %13 %14 903 OpBranch %76 904 %76 = OpLabel 905 %77 = OpBitcast %6 %13 906 %78 = OpIMul %6 %25 %19 907 %79 = OpIAdd %6 %77 %78 908 %80 = OpConvertUToF %15 %25 909 %81 = OpAccessChain %20 %3 %79 910 OpStore %81 %80 911 OpBranch %82 912 %82 = OpLabel 913 %83 = OpIAdd %11 %13 %21 914 OpBranch %84 915 %84 = OpLabel 916 OpBranch %85 917 %85 = OpLabel 918 %86 = OpSLessThan %10 %83 %14 919 OpBranch %87 920 %87 = OpLabel 921 %88 = OpBitcast %6 %83 922 %89 = OpIMul %6 %25 %19 923 %90 = OpIAdd %6 %88 %89 924 %91 = OpConvertUToF %15 %25 925 %92 = OpAccessChain %20 %3 %90 926 OpStore %92 %91 927 OpBranch %93 928 %93 = OpLabel 929 %94 = OpIAdd %11 %83 %21 930 OpBranch %95 931 %95 = OpLabel 932 OpBranch %96 933 %96 = OpLabel 934 %97 = OpSLessThan %10 %94 %14 935 OpBranch %98 936 %98 = OpLabel 937 %99 = OpBitcast %6 %94 938 %100 = OpIMul %6 %25 %19 939 %101 = OpIAdd %6 %99 %100 940 %102 = OpConvertUToF %15 %25 941 %103 = OpAccessChain %20 %3 %101 942 OpStore %103 %102 943 OpBranch %104 944 %104 = OpLabel 945 %105 = OpIAdd %11 %94 %21 946 OpBranch %106 947 %106 = OpLabel 948 OpBranch %107 949 %107 = OpLabel 950 %108 = OpIAdd %6 %25 %21 951 OpBranch %27 952 %27 = OpLabel 953 OpReturn 954 OpFunctionEnd 955 )"; 956 957 std::unique_ptr<IRContext> context = 958 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 959 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 960 Module* module = context->module(); 961 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 962 << text << std::endl; 963 LoopUnroller loop_unroller; 964 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 965 SinglePassRunAndCheck<LoopUnroller>(text, output, false); 966 } 967 968 /* 969 Generated from the following GLSL 970 #version 410 core 971 void main() { 972 float out_array[2]; 973 for (int i = -3; i < -1; i++) { 974 out_array[3 + i] = i; 975 } 976 } 977 */ 978 TEST_F(PassClassTest, NegativeConditionAndInit) { 979 // With LocalMultiStoreElimPass 980 const std::string text = R"( 981 OpCapability Shader 982 %1 = OpExtInstImport "GLSL.std.450" 983 OpMemoryModel Logical GLSL450 984 OpEntryPoint Fragment %4 "main" 985 OpExecutionMode %4 OriginUpperLeft 986 OpSource GLSL 410 987 OpName %4 "main" 988 OpName %23 "out_array" 989 %2 = OpTypeVoid 990 %3 = OpTypeFunction %2 991 %6 = OpTypeInt 32 1 992 %7 = OpTypePointer Function %6 993 %9 = OpConstant %6 -3 994 %16 = OpConstant %6 -1 995 %17 = OpTypeBool 996 %19 = OpTypeInt 32 0 997 %20 = OpConstant %19 2 998 %21 = OpTypeArray %6 %20 999 %22 = OpTypePointer Function %21 1000 %25 = OpConstant %6 3 1001 %30 = OpConstant %6 1 1002 %4 = OpFunction %2 None %3 1003 %5 = OpLabel 1004 %23 = OpVariable %22 Function 1005 OpBranch %10 1006 %10 = OpLabel 1007 %32 = OpPhi %6 %9 %5 %31 %13 1008 OpLoopMerge %12 %13 Unroll 1009 OpBranch %14 1010 %14 = OpLabel 1011 %18 = OpSLessThan %17 %32 %16 1012 OpBranchConditional %18 %11 %12 1013 %11 = OpLabel 1014 %26 = OpIAdd %6 %32 %25 1015 %28 = OpAccessChain %7 %23 %26 1016 OpStore %28 %32 1017 OpBranch %13 1018 %13 = OpLabel 1019 %31 = OpIAdd %6 %32 %30 1020 OpBranch %10 1021 %12 = OpLabel 1022 OpReturn 1023 OpFunctionEnd 1024 )"; 1025 1026 const std::string expected = R"(OpCapability Shader 1027 %1 = OpExtInstImport "GLSL.std.450" 1028 OpMemoryModel Logical GLSL450 1029 OpEntryPoint Fragment %2 "main" 1030 OpExecutionMode %2 OriginUpperLeft 1031 OpSource GLSL 410 1032 OpName %2 "main" 1033 OpName %3 "out_array" 1034 %4 = OpTypeVoid 1035 %5 = OpTypeFunction %4 1036 %6 = OpTypeInt 32 1 1037 %7 = OpTypePointer Function %6 1038 %8 = OpConstant %6 -3 1039 %9 = OpConstant %6 -1 1040 %10 = OpTypeBool 1041 %11 = OpTypeInt 32 0 1042 %12 = OpConstant %11 2 1043 %13 = OpTypeArray %6 %12 1044 %14 = OpTypePointer Function %13 1045 %15 = OpConstant %6 3 1046 %16 = OpConstant %6 1 1047 %2 = OpFunction %4 None %5 1048 %17 = OpLabel 1049 %3 = OpVariable %14 Function 1050 OpBranch %18 1051 %18 = OpLabel 1052 OpBranch %23 1053 %23 = OpLabel 1054 %24 = OpSLessThan %10 %8 %9 1055 OpBranch %25 1056 %25 = OpLabel 1057 %26 = OpIAdd %6 %8 %15 1058 %27 = OpAccessChain %7 %3 %26 1059 OpStore %27 %8 1060 OpBranch %21 1061 %21 = OpLabel 1062 %20 = OpIAdd %6 %8 %16 1063 OpBranch %28 1064 %28 = OpLabel 1065 OpBranch %30 1066 %30 = OpLabel 1067 %31 = OpSLessThan %10 %20 %9 1068 OpBranch %32 1069 %32 = OpLabel 1070 %33 = OpIAdd %6 %20 %15 1071 %34 = OpAccessChain %7 %3 %33 1072 OpStore %34 %20 1073 OpBranch %35 1074 %35 = OpLabel 1075 %36 = OpIAdd %6 %20 %16 1076 OpBranch %22 1077 %22 = OpLabel 1078 OpReturn 1079 OpFunctionEnd 1080 )"; 1081 1082 std::unique_ptr<IRContext> context = 1083 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1084 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1085 Module* module = context->module(); 1086 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 1087 << text << std::endl; 1088 1089 LoopUnroller loop_unroller; 1090 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 1091 // SinglePassRunAndCheck<LoopUnroller>(text, expected, false); 1092 1093 Function* f = spvtest::GetFunction(module, 4); 1094 1095 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); 1096 EXPECT_EQ(loop_descriptor.NumLoops(), 1u); 1097 1098 Loop& loop = loop_descriptor.GetLoopByIndex(0); 1099 1100 EXPECT_TRUE(loop.HasUnrollLoopControl()); 1101 1102 BasicBlock* condition = loop.FindConditionBlock(); 1103 EXPECT_EQ(condition->id(), 14u); 1104 1105 Instruction* induction = loop.FindConditionVariable(condition); 1106 EXPECT_EQ(induction->result_id(), 32u); 1107 1108 LoopUtils loop_utils{context.get(), &loop}; 1109 EXPECT_TRUE(loop_utils.CanPerformUnroll()); 1110 1111 size_t iterations = 0; 1112 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(), 1113 &iterations)); 1114 EXPECT_EQ(iterations, 2u); 1115 SinglePassRunAndCheck<LoopUnroller>(text, expected, false); 1116 } 1117 1118 /* 1119 Generated from the following GLSL 1120 #version 410 core 1121 void main() { 1122 float out_array[9]; 1123 for (int i = -10; i < -1; i++) { 1124 out_array[i] = i; 1125 } 1126 } 1127 */ 1128 TEST_F(PassClassTest, NegativeConditionAndInitResidualUnroll) { 1129 // With LocalMultiStoreElimPass 1130 const std::string text = R"( 1131 OpCapability Shader 1132 %1 = OpExtInstImport "GLSL.std.450" 1133 OpMemoryModel Logical GLSL450 1134 OpEntryPoint Fragment %4 "main" 1135 OpExecutionMode %4 OriginUpperLeft 1136 OpSource GLSL 410 1137 OpName %4 "main" 1138 OpName %23 "out_array" 1139 %2 = OpTypeVoid 1140 %3 = OpTypeFunction %2 1141 %6 = OpTypeInt 32 1 1142 %7 = OpTypePointer Function %6 1143 %9 = OpConstant %6 -10 1144 %16 = OpConstant %6 -1 1145 %17 = OpTypeBool 1146 %19 = OpTypeInt 32 0 1147 %20 = OpConstant %19 9 1148 %21 = OpTypeArray %6 %20 1149 %22 = OpTypePointer Function %21 1150 %25 = OpConstant %6 10 1151 %30 = OpConstant %6 1 1152 %4 = OpFunction %2 None %3 1153 %5 = OpLabel 1154 %23 = OpVariable %22 Function 1155 OpBranch %10 1156 %10 = OpLabel 1157 %32 = OpPhi %6 %9 %5 %31 %13 1158 OpLoopMerge %12 %13 Unroll 1159 OpBranch %14 1160 %14 = OpLabel 1161 %18 = OpSLessThan %17 %32 %16 1162 OpBranchConditional %18 %11 %12 1163 %11 = OpLabel 1164 %26 = OpIAdd %6 %32 %25 1165 %28 = OpAccessChain %7 %23 %26 1166 OpStore %28 %32 1167 OpBranch %13 1168 %13 = OpLabel 1169 %31 = OpIAdd %6 %32 %30 1170 OpBranch %10 1171 %12 = OpLabel 1172 OpReturn 1173 OpFunctionEnd 1174 )"; 1175 1176 const std::string expected = R"(OpCapability Shader 1177 %1 = OpExtInstImport "GLSL.std.450" 1178 OpMemoryModel Logical GLSL450 1179 OpEntryPoint Fragment %2 "main" 1180 OpExecutionMode %2 OriginUpperLeft 1181 OpSource GLSL 410 1182 OpName %2 "main" 1183 OpName %3 "out_array" 1184 %4 = OpTypeVoid 1185 %5 = OpTypeFunction %4 1186 %6 = OpTypeInt 32 1 1187 %7 = OpTypePointer Function %6 1188 %8 = OpConstant %6 -10 1189 %9 = OpConstant %6 -1 1190 %10 = OpTypeBool 1191 %11 = OpTypeInt 32 0 1192 %12 = OpConstant %11 9 1193 %13 = OpTypeArray %6 %12 1194 %14 = OpTypePointer Function %13 1195 %15 = OpConstant %6 10 1196 %16 = OpConstant %6 1 1197 %48 = OpConstant %6 -9 1198 %2 = OpFunction %4 None %5 1199 %17 = OpLabel 1200 %3 = OpVariable %14 Function 1201 OpBranch %18 1202 %18 = OpLabel 1203 %19 = OpPhi %6 %8 %17 %20 %21 1204 OpLoopMerge %28 %21 Unroll 1205 OpBranch %23 1206 %23 = OpLabel 1207 %24 = OpSLessThan %10 %19 %48 1208 OpBranchConditional %24 %25 %28 1209 %25 = OpLabel 1210 %26 = OpIAdd %6 %19 %15 1211 %27 = OpAccessChain %7 %3 %26 1212 OpStore %27 %19 1213 OpBranch %21 1214 %21 = OpLabel 1215 %20 = OpIAdd %6 %19 %16 1216 OpBranch %18 1217 %28 = OpLabel 1218 OpBranch %29 1219 %29 = OpLabel 1220 %30 = OpPhi %6 %19 %28 %47 %46 1221 OpLoopMerge %38 %46 DontUnroll 1222 OpBranch %31 1223 %31 = OpLabel 1224 %32 = OpSLessThan %10 %30 %9 1225 OpBranchConditional %32 %33 %38 1226 %33 = OpLabel 1227 %34 = OpIAdd %6 %30 %15 1228 %35 = OpAccessChain %7 %3 %34 1229 OpStore %35 %30 1230 OpBranch %36 1231 %36 = OpLabel 1232 %37 = OpIAdd %6 %30 %16 1233 OpBranch %39 1234 %39 = OpLabel 1235 OpBranch %41 1236 %41 = OpLabel 1237 %42 = OpSLessThan %10 %37 %9 1238 OpBranch %43 1239 %43 = OpLabel 1240 %44 = OpIAdd %6 %37 %15 1241 %45 = OpAccessChain %7 %3 %44 1242 OpStore %45 %37 1243 OpBranch %46 1244 %46 = OpLabel 1245 %47 = OpIAdd %6 %37 %16 1246 OpBranch %29 1247 %38 = OpLabel 1248 OpReturn 1249 %22 = OpLabel 1250 OpReturn 1251 OpFunctionEnd 1252 )"; 1253 1254 std::unique_ptr<IRContext> context = 1255 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1256 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1257 Module* module = context->module(); 1258 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 1259 << text << std::endl; 1260 1261 LoopUnroller loop_unroller; 1262 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 1263 1264 Function* f = spvtest::GetFunction(module, 4); 1265 1266 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); 1267 EXPECT_EQ(loop_descriptor.NumLoops(), 1u); 1268 1269 Loop& loop = loop_descriptor.GetLoopByIndex(0); 1270 1271 EXPECT_TRUE(loop.HasUnrollLoopControl()); 1272 1273 BasicBlock* condition = loop.FindConditionBlock(); 1274 EXPECT_EQ(condition->id(), 14u); 1275 1276 Instruction* induction = loop.FindConditionVariable(condition); 1277 EXPECT_EQ(induction->result_id(), 32u); 1278 1279 LoopUtils loop_utils{context.get(), &loop}; 1280 EXPECT_TRUE(loop_utils.CanPerformUnroll()); 1281 1282 size_t iterations = 0; 1283 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(), 1284 &iterations)); 1285 EXPECT_EQ(iterations, 9u); 1286 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(text, expected, false); 1287 } 1288 1289 /* 1290 Generated from the following GLSL 1291 #version 410 core 1292 void main() { 1293 float out_array[10]; 1294 for (uint i = 0; i < 2; i++) { 1295 for (int x = 0; x < 5; ++x) { 1296 out_array[x + i*5] = i; 1297 } 1298 } 1299 } 1300 */ 1301 TEST_F(PassClassTest, UnrollNestedLoopsValidateDescriptor) { 1302 // With LocalMultiStoreElimPass 1303 const std::string text = R"( 1304 OpCapability Shader 1305 %1 = OpExtInstImport "GLSL.std.450" 1306 OpMemoryModel Logical GLSL450 1307 OpEntryPoint Fragment %4 "main" 1308 OpExecutionMode %4 OriginUpperLeft 1309 OpSource GLSL 410 1310 OpName %4 "main" 1311 OpName %35 "out_array" 1312 %2 = OpTypeVoid 1313 %3 = OpTypeFunction %2 1314 %6 = OpTypeInt 32 0 1315 %7 = OpTypePointer Function %6 1316 %9 = OpConstant %6 0 1317 %16 = OpConstant %6 2 1318 %17 = OpTypeBool 1319 %19 = OpTypeInt 32 1 1320 %20 = OpTypePointer Function %19 1321 %22 = OpConstant %19 0 1322 %29 = OpConstant %19 5 1323 %31 = OpTypeFloat 32 1324 %32 = OpConstant %6 10 1325 %33 = OpTypeArray %31 %32 1326 %34 = OpTypePointer Function %33 1327 %39 = OpConstant %6 5 1328 %44 = OpTypePointer Function %31 1329 %47 = OpConstant %19 1 1330 %4 = OpFunction %2 None %3 1331 %5 = OpLabel 1332 %35 = OpVariable %34 Function 1333 OpBranch %10 1334 %10 = OpLabel 1335 %51 = OpPhi %6 %9 %5 %50 %13 1336 OpLoopMerge %12 %13 Unroll 1337 OpBranch %14 1338 %14 = OpLabel 1339 %18 = OpULessThan %17 %51 %16 1340 OpBranchConditional %18 %11 %12 1341 %11 = OpLabel 1342 OpBranch %23 1343 %23 = OpLabel 1344 %54 = OpPhi %19 %22 %11 %48 %26 1345 OpLoopMerge %25 %26 Unroll 1346 OpBranch %27 1347 %27 = OpLabel 1348 %30 = OpSLessThan %17 %54 %29 1349 OpBranchConditional %30 %24 %25 1350 %24 = OpLabel 1351 %37 = OpBitcast %6 %54 1352 %40 = OpIMul %6 %51 %39 1353 %41 = OpIAdd %6 %37 %40 1354 %43 = OpConvertUToF %31 %51 1355 %45 = OpAccessChain %44 %35 %41 1356 OpStore %45 %43 1357 OpBranch %26 1358 %26 = OpLabel 1359 %48 = OpIAdd %19 %54 %47 1360 OpBranch %23 1361 %25 = OpLabel 1362 OpBranch %13 1363 %13 = OpLabel 1364 %50 = OpIAdd %6 %51 %47 1365 OpBranch %10 1366 %12 = OpLabel 1367 OpReturn 1368 OpFunctionEnd 1369 )"; 1370 1371 { // Test fully unroll 1372 std::unique_ptr<IRContext> context = 1373 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1374 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1375 Module* module = context->module(); 1376 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 1377 << text << std::endl; 1378 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 1379 1380 Function* f = spvtest::GetFunction(module, 4); 1381 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); 1382 EXPECT_EQ(loop_descriptor.NumLoops(), 2u); 1383 1384 Loop& outer_loop = loop_descriptor.GetLoopByIndex(1); 1385 1386 EXPECT_TRUE(outer_loop.HasUnrollLoopControl()); 1387 1388 Loop& inner_loop = loop_descriptor.GetLoopByIndex(0); 1389 1390 EXPECT_TRUE(inner_loop.HasUnrollLoopControl()); 1391 1392 EXPECT_EQ(outer_loop.GetBlocks().size(), 9u); 1393 1394 EXPECT_EQ(inner_loop.GetBlocks().size(), 4u); 1395 EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u); 1396 EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u); 1397 1398 { 1399 LoopUtils loop_utils{context.get(), &inner_loop}; 1400 loop_utils.FullyUnroll(); 1401 loop_utils.Finalize(); 1402 } 1403 1404 EXPECT_EQ(loop_descriptor.NumLoops(), 1u); 1405 EXPECT_EQ(outer_loop.GetBlocks().size(), 25u); 1406 EXPECT_EQ(outer_loop.NumImmediateChildren(), 0u); 1407 { 1408 LoopUtils loop_utils{context.get(), &outer_loop}; 1409 loop_utils.FullyUnroll(); 1410 loop_utils.Finalize(); 1411 } 1412 EXPECT_EQ(loop_descriptor.NumLoops(), 0u); 1413 } 1414 1415 { // Test partially unroll 1416 std::unique_ptr<IRContext> context = 1417 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1418 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1419 Module* module = context->module(); 1420 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 1421 << text << std::endl; 1422 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 1423 1424 Function* f = spvtest::GetFunction(module, 4); 1425 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); 1426 EXPECT_EQ(loop_descriptor.NumLoops(), 2u); 1427 1428 Loop& outer_loop = loop_descriptor.GetLoopByIndex(1); 1429 1430 EXPECT_TRUE(outer_loop.HasUnrollLoopControl()); 1431 1432 Loop& inner_loop = loop_descriptor.GetLoopByIndex(0); 1433 1434 EXPECT_TRUE(inner_loop.HasUnrollLoopControl()); 1435 1436 EXPECT_EQ(outer_loop.GetBlocks().size(), 9u); 1437 1438 EXPECT_EQ(inner_loop.GetBlocks().size(), 4u); 1439 1440 EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u); 1441 EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u); 1442 1443 LoopUtils loop_utils{context.get(), &inner_loop}; 1444 loop_utils.PartiallyUnroll(2); 1445 loop_utils.Finalize(); 1446 1447 // The number of loops should actually grow. 1448 EXPECT_EQ(loop_descriptor.NumLoops(), 3u); 1449 EXPECT_EQ(outer_loop.GetBlocks().size(), 18u); 1450 EXPECT_EQ(outer_loop.NumImmediateChildren(), 2u); 1451 } 1452 } 1453 1454 /* 1455 Generated from the following GLSL 1456 #version 410 core 1457 void main() { 1458 float out_array[3]; 1459 for (int i = 3; i > 0; --i) { 1460 out_array[i] = i; 1461 } 1462 } 1463 */ 1464 TEST_F(PassClassTest, FullyUnrollNegativeStepLoopTest) { 1465 // With LocalMultiStoreElimPass 1466 const std::string text = R"( 1467 OpCapability Shader 1468 %1 = OpExtInstImport "GLSL.std.450" 1469 OpMemoryModel Logical GLSL450 1470 OpEntryPoint Fragment %4 "main" 1471 OpExecutionMode %4 OriginUpperLeft 1472 OpSource GLSL 410 1473 OpName %4 "main" 1474 OpName %24 "out_array" 1475 %2 = OpTypeVoid 1476 %3 = OpTypeFunction %2 1477 %6 = OpTypeInt 32 1 1478 %7 = OpTypePointer Function %6 1479 %9 = OpConstant %6 3 1480 %16 = OpConstant %6 0 1481 %17 = OpTypeBool 1482 %19 = OpTypeFloat 32 1483 %20 = OpTypeInt 32 0 1484 %21 = OpConstant %20 3 1485 %22 = OpTypeArray %19 %21 1486 %23 = OpTypePointer Function %22 1487 %28 = OpTypePointer Function %19 1488 %31 = OpConstant %6 1 1489 %4 = OpFunction %2 None %3 1490 %5 = OpLabel 1491 %24 = OpVariable %23 Function 1492 OpBranch %10 1493 %10 = OpLabel 1494 %33 = OpPhi %6 %9 %5 %32 %13 1495 OpLoopMerge %12 %13 Unroll 1496 OpBranch %14 1497 %14 = OpLabel 1498 %18 = OpSGreaterThan %17 %33 %16 1499 OpBranchConditional %18 %11 %12 1500 %11 = OpLabel 1501 %27 = OpConvertSToF %19 %33 1502 %29 = OpAccessChain %28 %24 %33 1503 OpStore %29 %27 1504 OpBranch %13 1505 %13 = OpLabel 1506 %32 = OpISub %6 %33 %31 1507 OpBranch %10 1508 %12 = OpLabel 1509 OpReturn 1510 OpFunctionEnd 1511 )"; 1512 1513 const std::string output = R"(OpCapability Shader 1514 %1 = OpExtInstImport "GLSL.std.450" 1515 OpMemoryModel Logical GLSL450 1516 OpEntryPoint Fragment %2 "main" 1517 OpExecutionMode %2 OriginUpperLeft 1518 OpSource GLSL 410 1519 OpName %2 "main" 1520 OpName %3 "out_array" 1521 %4 = OpTypeVoid 1522 %5 = OpTypeFunction %4 1523 %6 = OpTypeInt 32 1 1524 %7 = OpTypePointer Function %6 1525 %8 = OpConstant %6 3 1526 %9 = OpConstant %6 0 1527 %10 = OpTypeBool 1528 %11 = OpTypeFloat 32 1529 %12 = OpTypeInt 32 0 1530 %13 = OpConstant %12 3 1531 %14 = OpTypeArray %11 %13 1532 %15 = OpTypePointer Function %14 1533 %16 = OpTypePointer Function %11 1534 %17 = OpConstant %6 1 1535 %2 = OpFunction %4 None %5 1536 %18 = OpLabel 1537 %3 = OpVariable %15 Function 1538 OpBranch %19 1539 %19 = OpLabel 1540 OpBranch %24 1541 %24 = OpLabel 1542 %25 = OpSGreaterThan %10 %8 %9 1543 OpBranch %26 1544 %26 = OpLabel 1545 %27 = OpConvertSToF %11 %8 1546 %28 = OpAccessChain %16 %3 %8 1547 OpStore %28 %27 1548 OpBranch %22 1549 %22 = OpLabel 1550 %21 = OpISub %6 %8 %17 1551 OpBranch %29 1552 %29 = OpLabel 1553 OpBranch %31 1554 %31 = OpLabel 1555 %32 = OpSGreaterThan %10 %21 %9 1556 OpBranch %33 1557 %33 = OpLabel 1558 %34 = OpConvertSToF %11 %21 1559 %35 = OpAccessChain %16 %3 %21 1560 OpStore %35 %34 1561 OpBranch %36 1562 %36 = OpLabel 1563 %37 = OpISub %6 %21 %17 1564 OpBranch %38 1565 %38 = OpLabel 1566 OpBranch %40 1567 %40 = OpLabel 1568 %41 = OpSGreaterThan %10 %37 %9 1569 OpBranch %42 1570 %42 = OpLabel 1571 %43 = OpConvertSToF %11 %37 1572 %44 = OpAccessChain %16 %3 %37 1573 OpStore %44 %43 1574 OpBranch %45 1575 %45 = OpLabel 1576 %46 = OpISub %6 %37 %17 1577 OpBranch %23 1578 %23 = OpLabel 1579 OpReturn 1580 OpFunctionEnd 1581 )"; 1582 1583 std::unique_ptr<IRContext> context = 1584 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1585 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1586 Module* module = context->module(); 1587 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 1588 << text << std::endl; 1589 1590 LoopUnroller loop_unroller; 1591 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 1592 SinglePassRunAndCheck<LoopUnroller>(text, output, false); 1593 } 1594 1595 /* 1596 Generated from the following GLSL 1597 #version 410 core 1598 void main() { 1599 float out_array[3]; 1600 for (int i = 9; i > 0; i-=3) { 1601 out_array[i] = i; 1602 } 1603 } 1604 */ 1605 TEST_F(PassClassTest, FullyUnrollNegativeNonOneStepLoop) { 1606 // With LocalMultiStoreElimPass 1607 const std::string text = R"( 1608 OpCapability Shader 1609 %1 = OpExtInstImport "GLSL.std.450" 1610 OpMemoryModel Logical GLSL450 1611 OpEntryPoint Fragment %4 "main" 1612 OpExecutionMode %4 OriginUpperLeft 1613 OpSource GLSL 410 1614 OpName %4 "main" 1615 OpName %24 "out_array" 1616 %2 = OpTypeVoid 1617 %3 = OpTypeFunction %2 1618 %6 = OpTypeInt 32 1 1619 %7 = OpTypePointer Function %6 1620 %9 = OpConstant %6 9 1621 %16 = OpConstant %6 0 1622 %17 = OpTypeBool 1623 %19 = OpTypeFloat 32 1624 %20 = OpTypeInt 32 0 1625 %21 = OpConstant %20 3 1626 %22 = OpTypeArray %19 %21 1627 %23 = OpTypePointer Function %22 1628 %28 = OpTypePointer Function %19 1629 %30 = OpConstant %6 3 1630 %4 = OpFunction %2 None %3 1631 %5 = OpLabel 1632 %24 = OpVariable %23 Function 1633 OpBranch %10 1634 %10 = OpLabel 1635 %33 = OpPhi %6 %9 %5 %32 %13 1636 OpLoopMerge %12 %13 Unroll 1637 OpBranch %14 1638 %14 = OpLabel 1639 %18 = OpSGreaterThan %17 %33 %16 1640 OpBranchConditional %18 %11 %12 1641 %11 = OpLabel 1642 %27 = OpConvertSToF %19 %33 1643 %29 = OpAccessChain %28 %24 %33 1644 OpStore %29 %27 1645 OpBranch %13 1646 %13 = OpLabel 1647 %32 = OpISub %6 %33 %30 1648 OpBranch %10 1649 %12 = OpLabel 1650 OpReturn 1651 OpFunctionEnd 1652 )"; 1653 1654 const std::string output = R"(OpCapability Shader 1655 %1 = OpExtInstImport "GLSL.std.450" 1656 OpMemoryModel Logical GLSL450 1657 OpEntryPoint Fragment %2 "main" 1658 OpExecutionMode %2 OriginUpperLeft 1659 OpSource GLSL 410 1660 OpName %2 "main" 1661 OpName %3 "out_array" 1662 %4 = OpTypeVoid 1663 %5 = OpTypeFunction %4 1664 %6 = OpTypeInt 32 1 1665 %7 = OpTypePointer Function %6 1666 %8 = OpConstant %6 9 1667 %9 = OpConstant %6 0 1668 %10 = OpTypeBool 1669 %11 = OpTypeFloat 32 1670 %12 = OpTypeInt 32 0 1671 %13 = OpConstant %12 3 1672 %14 = OpTypeArray %11 %13 1673 %15 = OpTypePointer Function %14 1674 %16 = OpTypePointer Function %11 1675 %17 = OpConstant %6 3 1676 %2 = OpFunction %4 None %5 1677 %18 = OpLabel 1678 %3 = OpVariable %15 Function 1679 OpBranch %19 1680 %19 = OpLabel 1681 OpBranch %24 1682 %24 = OpLabel 1683 %25 = OpSGreaterThan %10 %8 %9 1684 OpBranch %26 1685 %26 = OpLabel 1686 %27 = OpConvertSToF %11 %8 1687 %28 = OpAccessChain %16 %3 %8 1688 OpStore %28 %27 1689 OpBranch %22 1690 %22 = OpLabel 1691 %21 = OpISub %6 %8 %17 1692 OpBranch %29 1693 %29 = OpLabel 1694 OpBranch %31 1695 %31 = OpLabel 1696 %32 = OpSGreaterThan %10 %21 %9 1697 OpBranch %33 1698 %33 = OpLabel 1699 %34 = OpConvertSToF %11 %21 1700 %35 = OpAccessChain %16 %3 %21 1701 OpStore %35 %34 1702 OpBranch %36 1703 %36 = OpLabel 1704 %37 = OpISub %6 %21 %17 1705 OpBranch %38 1706 %38 = OpLabel 1707 OpBranch %40 1708 %40 = OpLabel 1709 %41 = OpSGreaterThan %10 %37 %9 1710 OpBranch %42 1711 %42 = OpLabel 1712 %43 = OpConvertSToF %11 %37 1713 %44 = OpAccessChain %16 %3 %37 1714 OpStore %44 %43 1715 OpBranch %45 1716 %45 = OpLabel 1717 %46 = OpISub %6 %37 %17 1718 OpBranch %23 1719 %23 = OpLabel 1720 OpReturn 1721 OpFunctionEnd 1722 )"; 1723 1724 std::unique_ptr<IRContext> context = 1725 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1726 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1727 Module* module = context->module(); 1728 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 1729 << text << std::endl; 1730 1731 LoopUnroller loop_unroller; 1732 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 1733 SinglePassRunAndCheck<LoopUnroller>(text, output, false); 1734 } 1735 1736 /* 1737 Generated from the following GLSL 1738 #version 410 core 1739 void main() { 1740 float out_array[3]; 1741 for (int i = 0; i < 7; i+=3) { 1742 out_array[i] = i; 1743 } 1744 } 1745 */ 1746 TEST_F(PassClassTest, FullyUnrollNonDivisibleStepLoop) { 1747 // With LocalMultiStoreElimPass 1748 const std::string text = R"(OpCapability Shader 1749 %1 = OpExtInstImport "GLSL.std.450" 1750 OpMemoryModel Logical GLSL450 1751 OpEntryPoint Fragment %4 "main" 1752 OpExecutionMode %4 OriginUpperLeft 1753 OpSource GLSL 410 1754 OpName %4 "main" 1755 OpName %24 "out_array" 1756 %2 = OpTypeVoid 1757 %3 = OpTypeFunction %2 1758 %6 = OpTypeInt 32 1 1759 %7 = OpTypePointer Function %6 1760 %9 = OpConstant %6 0 1761 %16 = OpConstant %6 7 1762 %17 = OpTypeBool 1763 %19 = OpTypeFloat 32 1764 %20 = OpTypeInt 32 0 1765 %21 = OpConstant %20 3 1766 %22 = OpTypeArray %19 %21 1767 %23 = OpTypePointer Function %22 1768 %28 = OpTypePointer Function %19 1769 %30 = OpConstant %6 3 1770 %4 = OpFunction %2 None %3 1771 %5 = OpLabel 1772 %24 = OpVariable %23 Function 1773 OpBranch %10 1774 %10 = OpLabel 1775 %33 = OpPhi %6 %9 %5 %32 %13 1776 OpLoopMerge %12 %13 Unroll 1777 OpBranch %14 1778 %14 = OpLabel 1779 %18 = OpSLessThan %17 %33 %16 1780 OpBranchConditional %18 %11 %12 1781 %11 = OpLabel 1782 %27 = OpConvertSToF %19 %33 1783 %29 = OpAccessChain %28 %24 %33 1784 OpStore %29 %27 1785 OpBranch %13 1786 %13 = OpLabel 1787 %32 = OpIAdd %6 %33 %30 1788 OpBranch %10 1789 %12 = OpLabel 1790 OpReturn 1791 OpFunctionEnd 1792 )"; 1793 1794 const std::string output = R"(OpCapability Shader 1795 %1 = OpExtInstImport "GLSL.std.450" 1796 OpMemoryModel Logical GLSL450 1797 OpEntryPoint Fragment %2 "main" 1798 OpExecutionMode %2 OriginUpperLeft 1799 OpSource GLSL 410 1800 OpName %2 "main" 1801 OpName %3 "out_array" 1802 %4 = OpTypeVoid 1803 %5 = OpTypeFunction %4 1804 %6 = OpTypeInt 32 1 1805 %7 = OpTypePointer Function %6 1806 %8 = OpConstant %6 0 1807 %9 = OpConstant %6 7 1808 %10 = OpTypeBool 1809 %11 = OpTypeFloat 32 1810 %12 = OpTypeInt 32 0 1811 %13 = OpConstant %12 3 1812 %14 = OpTypeArray %11 %13 1813 %15 = OpTypePointer Function %14 1814 %16 = OpTypePointer Function %11 1815 %17 = OpConstant %6 3 1816 %2 = OpFunction %4 None %5 1817 %18 = OpLabel 1818 %3 = OpVariable %15 Function 1819 OpBranch %19 1820 %19 = OpLabel 1821 OpBranch %24 1822 %24 = OpLabel 1823 %25 = OpSLessThan %10 %8 %9 1824 OpBranch %26 1825 %26 = OpLabel 1826 %27 = OpConvertSToF %11 %8 1827 %28 = OpAccessChain %16 %3 %8 1828 OpStore %28 %27 1829 OpBranch %22 1830 %22 = OpLabel 1831 %21 = OpIAdd %6 %8 %17 1832 OpBranch %29 1833 %29 = OpLabel 1834 OpBranch %31 1835 %31 = OpLabel 1836 %32 = OpSLessThan %10 %21 %9 1837 OpBranch %33 1838 %33 = OpLabel 1839 %34 = OpConvertSToF %11 %21 1840 %35 = OpAccessChain %16 %3 %21 1841 OpStore %35 %34 1842 OpBranch %36 1843 %36 = OpLabel 1844 %37 = OpIAdd %6 %21 %17 1845 OpBranch %38 1846 %38 = OpLabel 1847 OpBranch %40 1848 %40 = OpLabel 1849 %41 = OpSLessThan %10 %37 %9 1850 OpBranch %42 1851 %42 = OpLabel 1852 %43 = OpConvertSToF %11 %37 1853 %44 = OpAccessChain %16 %3 %37 1854 OpStore %44 %43 1855 OpBranch %45 1856 %45 = OpLabel 1857 %46 = OpIAdd %6 %37 %17 1858 OpBranch %23 1859 %23 = OpLabel 1860 OpReturn 1861 OpFunctionEnd 1862 )"; 1863 1864 std::unique_ptr<IRContext> context = 1865 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1866 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1867 Module* module = context->module(); 1868 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 1869 << text << std::endl; 1870 1871 LoopUnroller loop_unroller; 1872 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 1873 SinglePassRunAndCheck<LoopUnroller>(text, output, false); 1874 } 1875 1876 /* 1877 Generated from the following GLSL 1878 #version 410 core 1879 void main() { 1880 float out_array[4]; 1881 for (int i = 11; i > 0; i-=3) { 1882 out_array[i] = i; 1883 } 1884 } 1885 */ 1886 TEST_F(PassClassTest, FullyUnrollNegativeNonDivisibleStepLoop) { 1887 // With LocalMultiStoreElimPass 1888 const std::string text = R"(OpCapability Shader 1889 %1 = OpExtInstImport "GLSL.std.450" 1890 OpMemoryModel Logical GLSL450 1891 OpEntryPoint Fragment %4 "main" 1892 OpExecutionMode %4 OriginUpperLeft 1893 OpSource GLSL 410 1894 OpName %4 "main" 1895 OpName %24 "out_array" 1896 %2 = OpTypeVoid 1897 %3 = OpTypeFunction %2 1898 %6 = OpTypeInt 32 1 1899 %7 = OpTypePointer Function %6 1900 %9 = OpConstant %6 11 1901 %16 = OpConstant %6 0 1902 %17 = OpTypeBool 1903 %19 = OpTypeFloat 32 1904 %20 = OpTypeInt 32 0 1905 %21 = OpConstant %20 4 1906 %22 = OpTypeArray %19 %21 1907 %23 = OpTypePointer Function %22 1908 %28 = OpTypePointer Function %19 1909 %30 = OpConstant %6 3 1910 %4 = OpFunction %2 None %3 1911 %5 = OpLabel 1912 %24 = OpVariable %23 Function 1913 OpBranch %10 1914 %10 = OpLabel 1915 %33 = OpPhi %6 %9 %5 %32 %13 1916 OpLoopMerge %12 %13 Unroll 1917 OpBranch %14 1918 %14 = OpLabel 1919 %18 = OpSGreaterThan %17 %33 %16 1920 OpBranchConditional %18 %11 %12 1921 %11 = OpLabel 1922 %27 = OpConvertSToF %19 %33 1923 %29 = OpAccessChain %28 %24 %33 1924 OpStore %29 %27 1925 OpBranch %13 1926 %13 = OpLabel 1927 %32 = OpISub %6 %33 %30 1928 OpBranch %10 1929 %12 = OpLabel 1930 OpReturn 1931 OpFunctionEnd 1932 )"; 1933 1934 const std::string output = R"(OpCapability Shader 1935 %1 = OpExtInstImport "GLSL.std.450" 1936 OpMemoryModel Logical GLSL450 1937 OpEntryPoint Fragment %2 "main" 1938 OpExecutionMode %2 OriginUpperLeft 1939 OpSource GLSL 410 1940 OpName %2 "main" 1941 OpName %3 "out_array" 1942 %4 = OpTypeVoid 1943 %5 = OpTypeFunction %4 1944 %6 = OpTypeInt 32 1 1945 %7 = OpTypePointer Function %6 1946 %8 = OpConstant %6 11 1947 %9 = OpConstant %6 0 1948 %10 = OpTypeBool 1949 %11 = OpTypeFloat 32 1950 %12 = OpTypeInt 32 0 1951 %13 = OpConstant %12 4 1952 %14 = OpTypeArray %11 %13 1953 %15 = OpTypePointer Function %14 1954 %16 = OpTypePointer Function %11 1955 %17 = OpConstant %6 3 1956 %2 = OpFunction %4 None %5 1957 %18 = OpLabel 1958 %3 = OpVariable %15 Function 1959 OpBranch %19 1960 %19 = OpLabel 1961 OpBranch %24 1962 %24 = OpLabel 1963 %25 = OpSGreaterThan %10 %8 %9 1964 OpBranch %26 1965 %26 = OpLabel 1966 %27 = OpConvertSToF %11 %8 1967 %28 = OpAccessChain %16 %3 %8 1968 OpStore %28 %27 1969 OpBranch %22 1970 %22 = OpLabel 1971 %21 = OpISub %6 %8 %17 1972 OpBranch %29 1973 %29 = OpLabel 1974 OpBranch %31 1975 %31 = OpLabel 1976 %32 = OpSGreaterThan %10 %21 %9 1977 OpBranch %33 1978 %33 = OpLabel 1979 %34 = OpConvertSToF %11 %21 1980 %35 = OpAccessChain %16 %3 %21 1981 OpStore %35 %34 1982 OpBranch %36 1983 %36 = OpLabel 1984 %37 = OpISub %6 %21 %17 1985 OpBranch %38 1986 %38 = OpLabel 1987 OpBranch %40 1988 %40 = OpLabel 1989 %41 = OpSGreaterThan %10 %37 %9 1990 OpBranch %42 1991 %42 = OpLabel 1992 %43 = OpConvertSToF %11 %37 1993 %44 = OpAccessChain %16 %3 %37 1994 OpStore %44 %43 1995 OpBranch %45 1996 %45 = OpLabel 1997 %46 = OpISub %6 %37 %17 1998 OpBranch %47 1999 %47 = OpLabel 2000 OpBranch %49 2001 %49 = OpLabel 2002 %50 = OpSGreaterThan %10 %46 %9 2003 OpBranch %51 2004 %51 = OpLabel 2005 %52 = OpConvertSToF %11 %46 2006 %53 = OpAccessChain %16 %3 %46 2007 OpStore %53 %52 2008 OpBranch %54 2009 %54 = OpLabel 2010 %55 = OpISub %6 %46 %17 2011 OpBranch %23 2012 %23 = OpLabel 2013 OpReturn 2014 OpFunctionEnd 2015 )"; 2016 2017 std::unique_ptr<IRContext> context = 2018 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 2019 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2020 Module* module = context->module(); 2021 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 2022 << text << std::endl; 2023 2024 LoopUnroller loop_unroller; 2025 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 2026 SinglePassRunAndCheck<LoopUnroller>(text, output, false); 2027 } 2028 2029 // With LocalMultiStoreElimPass 2030 static const std::string multiple_phi_shader = R"( 2031 OpCapability Shader 2032 %1 = OpExtInstImport "GLSL.std.450" 2033 OpMemoryModel Logical GLSL450 2034 OpEntryPoint Fragment %4 "main" 2035 OpExecutionMode %4 OriginUpperLeft 2036 OpSource GLSL 410 2037 OpName %4 "main" 2038 OpName %8 "foo(" 2039 %2 = OpTypeVoid 2040 %3 = OpTypeFunction %2 2041 %6 = OpTypeInt 32 1 2042 %7 = OpTypeFunction %6 2043 %10 = OpTypePointer Function %6 2044 %12 = OpConstant %6 0 2045 %14 = OpConstant %6 3 2046 %22 = OpConstant %6 6 2047 %23 = OpTypeBool 2048 %31 = OpConstant %6 1 2049 %4 = OpFunction %2 None %3 2050 %5 = OpLabel 2051 %40 = OpFunctionCall %6 %8 2052 OpReturn 2053 OpFunctionEnd 2054 %8 = OpFunction %6 None %7 2055 %9 = OpLabel 2056 OpBranch %16 2057 %16 = OpLabel 2058 %41 = OpPhi %6 %12 %9 %34 %19 2059 %42 = OpPhi %6 %14 %9 %29 %19 2060 %43 = OpPhi %6 %12 %9 %32 %19 2061 OpLoopMerge %18 %19 Unroll 2062 OpBranch %20 2063 %20 = OpLabel 2064 %24 = OpSLessThan %23 %43 %22 2065 OpBranchConditional %24 %17 %18 2066 %17 = OpLabel 2067 %27 = OpIMul %6 %43 %41 2068 %29 = OpIAdd %6 %42 %27 2069 OpBranch %19 2070 %19 = OpLabel 2071 %32 = OpIAdd %6 %43 %31 2072 %34 = OpISub %6 %41 %31 2073 OpBranch %16 2074 %18 = OpLabel 2075 %37 = OpIAdd %6 %42 %41 2076 OpReturnValue %37 2077 OpFunctionEnd 2078 )"; 2079 2080 TEST_F(PassClassTest, PartiallyUnrollResidualMultipleInductionVariables) { 2081 const std::string output = R"(OpCapability Shader 2082 %1 = OpExtInstImport "GLSL.std.450" 2083 OpMemoryModel Logical GLSL450 2084 OpEntryPoint Fragment %2 "main" 2085 OpExecutionMode %2 OriginUpperLeft 2086 OpSource GLSL 410 2087 OpName %2 "main" 2088 OpName %3 "foo(" 2089 %4 = OpTypeVoid 2090 %5 = OpTypeFunction %4 2091 %6 = OpTypeInt 32 1 2092 %7 = OpTypeFunction %6 2093 %8 = OpTypePointer Function %6 2094 %9 = OpConstant %6 0 2095 %10 = OpConstant %6 3 2096 %11 = OpConstant %6 6 2097 %12 = OpTypeBool 2098 %13 = OpConstant %6 1 2099 %82 = OpTypeInt 32 0 2100 %83 = OpConstant %82 2 2101 %2 = OpFunction %4 None %5 2102 %14 = OpLabel 2103 %15 = OpFunctionCall %6 %3 2104 OpReturn 2105 OpFunctionEnd 2106 %3 = OpFunction %6 None %7 2107 %16 = OpLabel 2108 OpBranch %17 2109 %17 = OpLabel 2110 %18 = OpPhi %6 %9 %16 %19 %20 2111 %21 = OpPhi %6 %10 %16 %22 %20 2112 %23 = OpPhi %6 %9 %16 %24 %20 2113 OpLoopMerge %31 %20 Unroll 2114 OpBranch %26 2115 %26 = OpLabel 2116 %27 = OpSLessThan %12 %23 %83 2117 OpBranchConditional %27 %28 %31 2118 %28 = OpLabel 2119 %29 = OpIMul %6 %23 %18 2120 %22 = OpIAdd %6 %21 %29 2121 OpBranch %20 2122 %20 = OpLabel 2123 %24 = OpIAdd %6 %23 %13 2124 %19 = OpISub %6 %18 %13 2125 OpBranch %17 2126 %31 = OpLabel 2127 OpBranch %32 2128 %32 = OpLabel 2129 %33 = OpPhi %6 %18 %31 %81 %79 2130 %34 = OpPhi %6 %21 %31 %78 %79 2131 %35 = OpPhi %6 %23 %31 %80 %79 2132 OpLoopMerge %44 %79 DontUnroll 2133 OpBranch %36 2134 %36 = OpLabel 2135 %37 = OpSLessThan %12 %35 %11 2136 OpBranchConditional %37 %38 %44 2137 %38 = OpLabel 2138 %39 = OpIMul %6 %35 %33 2139 %40 = OpIAdd %6 %34 %39 2140 OpBranch %41 2141 %41 = OpLabel 2142 %42 = OpIAdd %6 %35 %13 2143 %43 = OpISub %6 %33 %13 2144 OpBranch %46 2145 %46 = OpLabel 2146 OpBranch %50 2147 %50 = OpLabel 2148 %51 = OpSLessThan %12 %42 %11 2149 OpBranch %52 2150 %52 = OpLabel 2151 %53 = OpIMul %6 %42 %43 2152 %54 = OpIAdd %6 %40 %53 2153 OpBranch %55 2154 %55 = OpLabel 2155 %56 = OpIAdd %6 %42 %13 2156 %57 = OpISub %6 %43 %13 2157 OpBranch %58 2158 %58 = OpLabel 2159 OpBranch %62 2160 %62 = OpLabel 2161 %63 = OpSLessThan %12 %56 %11 2162 OpBranch %64 2163 %64 = OpLabel 2164 %65 = OpIMul %6 %56 %57 2165 %66 = OpIAdd %6 %54 %65 2166 OpBranch %67 2167 %67 = OpLabel 2168 %68 = OpIAdd %6 %56 %13 2169 %69 = OpISub %6 %57 %13 2170 OpBranch %70 2171 %70 = OpLabel 2172 OpBranch %74 2173 %74 = OpLabel 2174 %75 = OpSLessThan %12 %68 %11 2175 OpBranch %76 2176 %76 = OpLabel 2177 %77 = OpIMul %6 %68 %69 2178 %78 = OpIAdd %6 %66 %77 2179 OpBranch %79 2180 %79 = OpLabel 2181 %80 = OpIAdd %6 %68 %13 2182 %81 = OpISub %6 %69 %13 2183 OpBranch %32 2184 %44 = OpLabel 2185 %45 = OpIAdd %6 %34 %33 2186 OpReturnValue %45 2187 %25 = OpLabel 2188 %30 = OpIAdd %6 %34 %33 2189 OpReturnValue %30 2190 OpFunctionEnd 2191 )"; 2192 2193 std::unique_ptr<IRContext> context = 2194 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader, 2195 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2196 Module* module = context->module(); 2197 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 2198 << multiple_phi_shader << std::endl; 2199 2200 LoopUnroller loop_unroller; 2201 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 2202 SinglePassRunAndCheck<PartialUnrollerTestPass<4>>(multiple_phi_shader, output, 2203 false); 2204 } 2205 2206 TEST_F(PassClassTest, PartiallyUnrollMultipleInductionVariables) { 2207 const std::string output = R"(OpCapability Shader 2208 %1 = OpExtInstImport "GLSL.std.450" 2209 OpMemoryModel Logical GLSL450 2210 OpEntryPoint Fragment %2 "main" 2211 OpExecutionMode %2 OriginUpperLeft 2212 OpSource GLSL 410 2213 OpName %2 "main" 2214 OpName %3 "foo(" 2215 %4 = OpTypeVoid 2216 %5 = OpTypeFunction %4 2217 %6 = OpTypeInt 32 1 2218 %7 = OpTypeFunction %6 2219 %8 = OpTypePointer Function %6 2220 %9 = OpConstant %6 0 2221 %10 = OpConstant %6 3 2222 %11 = OpConstant %6 6 2223 %12 = OpTypeBool 2224 %13 = OpConstant %6 1 2225 %2 = OpFunction %4 None %5 2226 %14 = OpLabel 2227 %15 = OpFunctionCall %6 %3 2228 OpReturn 2229 OpFunctionEnd 2230 %3 = OpFunction %6 None %7 2231 %16 = OpLabel 2232 OpBranch %17 2233 %17 = OpLabel 2234 %18 = OpPhi %6 %9 %16 %42 %40 2235 %21 = OpPhi %6 %10 %16 %39 %40 2236 %23 = OpPhi %6 %9 %16 %41 %40 2237 OpLoopMerge %25 %40 DontUnroll 2238 OpBranch %26 2239 %26 = OpLabel 2240 %27 = OpSLessThan %12 %23 %11 2241 OpBranchConditional %27 %28 %25 2242 %28 = OpLabel 2243 %29 = OpIMul %6 %23 %18 2244 %22 = OpIAdd %6 %21 %29 2245 OpBranch %20 2246 %20 = OpLabel 2247 %24 = OpIAdd %6 %23 %13 2248 %19 = OpISub %6 %18 %13 2249 OpBranch %31 2250 %31 = OpLabel 2251 OpBranch %35 2252 %35 = OpLabel 2253 %36 = OpSLessThan %12 %24 %11 2254 OpBranch %37 2255 %37 = OpLabel 2256 %38 = OpIMul %6 %24 %19 2257 %39 = OpIAdd %6 %22 %38 2258 OpBranch %40 2259 %40 = OpLabel 2260 %41 = OpIAdd %6 %24 %13 2261 %42 = OpISub %6 %19 %13 2262 OpBranch %17 2263 %25 = OpLabel 2264 %30 = OpIAdd %6 %21 %18 2265 OpReturnValue %30 2266 OpFunctionEnd 2267 )"; 2268 2269 std::unique_ptr<IRContext> context = 2270 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader, 2271 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2272 Module* module = context->module(); 2273 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 2274 << multiple_phi_shader << std::endl; 2275 2276 LoopUnroller loop_unroller; 2277 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 2278 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(multiple_phi_shader, output, 2279 false); 2280 } 2281 2282 TEST_F(PassClassTest, FullyUnrollMultipleInductionVariables) { 2283 const std::string output = R"(OpCapability Shader 2284 %1 = OpExtInstImport "GLSL.std.450" 2285 OpMemoryModel Logical GLSL450 2286 OpEntryPoint Fragment %2 "main" 2287 OpExecutionMode %2 OriginUpperLeft 2288 OpSource GLSL 410 2289 OpName %2 "main" 2290 OpName %3 "foo(" 2291 %4 = OpTypeVoid 2292 %5 = OpTypeFunction %4 2293 %6 = OpTypeInt 32 1 2294 %7 = OpTypeFunction %6 2295 %8 = OpTypePointer Function %6 2296 %9 = OpConstant %6 0 2297 %10 = OpConstant %6 3 2298 %11 = OpConstant %6 6 2299 %12 = OpTypeBool 2300 %13 = OpConstant %6 1 2301 %2 = OpFunction %4 None %5 2302 %14 = OpLabel 2303 %15 = OpFunctionCall %6 %3 2304 OpReturn 2305 OpFunctionEnd 2306 %3 = OpFunction %6 None %7 2307 %16 = OpLabel 2308 OpBranch %17 2309 %17 = OpLabel 2310 OpBranch %26 2311 %26 = OpLabel 2312 %27 = OpSLessThan %12 %9 %11 2313 OpBranch %28 2314 %28 = OpLabel 2315 %29 = OpIMul %6 %9 %9 2316 %22 = OpIAdd %6 %10 %29 2317 OpBranch %20 2318 %20 = OpLabel 2319 %24 = OpIAdd %6 %9 %13 2320 %19 = OpISub %6 %9 %13 2321 OpBranch %31 2322 %31 = OpLabel 2323 OpBranch %35 2324 %35 = OpLabel 2325 %36 = OpSLessThan %12 %24 %11 2326 OpBranch %37 2327 %37 = OpLabel 2328 %38 = OpIMul %6 %24 %19 2329 %39 = OpIAdd %6 %22 %38 2330 OpBranch %40 2331 %40 = OpLabel 2332 %41 = OpIAdd %6 %24 %13 2333 %42 = OpISub %6 %19 %13 2334 OpBranch %43 2335 %43 = OpLabel 2336 OpBranch %47 2337 %47 = OpLabel 2338 %48 = OpSLessThan %12 %41 %11 2339 OpBranch %49 2340 %49 = OpLabel 2341 %50 = OpIMul %6 %41 %42 2342 %51 = OpIAdd %6 %39 %50 2343 OpBranch %52 2344 %52 = OpLabel 2345 %53 = OpIAdd %6 %41 %13 2346 %54 = OpISub %6 %42 %13 2347 OpBranch %55 2348 %55 = OpLabel 2349 OpBranch %59 2350 %59 = OpLabel 2351 %60 = OpSLessThan %12 %53 %11 2352 OpBranch %61 2353 %61 = OpLabel 2354 %62 = OpIMul %6 %53 %54 2355 %63 = OpIAdd %6 %51 %62 2356 OpBranch %64 2357 %64 = OpLabel 2358 %65 = OpIAdd %6 %53 %13 2359 %66 = OpISub %6 %54 %13 2360 OpBranch %67 2361 %67 = OpLabel 2362 OpBranch %71 2363 %71 = OpLabel 2364 %72 = OpSLessThan %12 %65 %11 2365 OpBranch %73 2366 %73 = OpLabel 2367 %74 = OpIMul %6 %65 %66 2368 %75 = OpIAdd %6 %63 %74 2369 OpBranch %76 2370 %76 = OpLabel 2371 %77 = OpIAdd %6 %65 %13 2372 %78 = OpISub %6 %66 %13 2373 OpBranch %79 2374 %79 = OpLabel 2375 OpBranch %83 2376 %83 = OpLabel 2377 %84 = OpSLessThan %12 %77 %11 2378 OpBranch %85 2379 %85 = OpLabel 2380 %86 = OpIMul %6 %77 %78 2381 %87 = OpIAdd %6 %75 %86 2382 OpBranch %88 2383 %88 = OpLabel 2384 %89 = OpIAdd %6 %77 %13 2385 %90 = OpISub %6 %78 %13 2386 OpBranch %25 2387 %25 = OpLabel 2388 %30 = OpIAdd %6 %87 %90 2389 OpReturnValue %30 2390 OpFunctionEnd 2391 )"; 2392 2393 std::unique_ptr<IRContext> context = 2394 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader, 2395 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2396 Module* module = context->module(); 2397 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 2398 << multiple_phi_shader << std::endl; 2399 2400 LoopUnroller loop_unroller; 2401 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 2402 SinglePassRunAndCheck<LoopUnroller>(multiple_phi_shader, output, false); 2403 } 2404 2405 /* 2406 Generated from the following GLSL 2407 #version 440 core 2408 void main() 2409 { 2410 int j = 0; 2411 for (int i = 0; i <= 2; ++i) 2412 ++j; 2413 2414 for (int i = 1; i >= 0; --i) 2415 ++j; 2416 } 2417 */ 2418 TEST_F(PassClassTest, FullyUnrollEqualToOperations) { 2419 // With LocalMultiStoreElimPass 2420 const std::string text = R"( 2421 OpCapability Shader 2422 %1 = OpExtInstImport "GLSL.std.450" 2423 OpMemoryModel Logical GLSL450 2424 OpEntryPoint Fragment %4 "main" 2425 OpExecutionMode %4 OriginUpperLeft 2426 OpSource GLSL 440 2427 OpName %4 "main" 2428 %2 = OpTypeVoid 2429 %3 = OpTypeFunction %2 2430 %6 = OpTypeInt 32 1 2431 %7 = OpTypePointer Function %6 2432 %9 = OpConstant %6 0 2433 %17 = OpConstant %6 2 2434 %18 = OpTypeBool 2435 %21 = OpConstant %6 1 2436 %4 = OpFunction %2 None %3 2437 %5 = OpLabel 2438 OpBranch %11 2439 %11 = OpLabel 2440 %37 = OpPhi %6 %9 %5 %22 %14 2441 %38 = OpPhi %6 %9 %5 %24 %14 2442 OpLoopMerge %13 %14 Unroll 2443 OpBranch %15 2444 %15 = OpLabel 2445 %19 = OpSLessThanEqual %18 %38 %17 2446 OpBranchConditional %19 %12 %13 2447 %12 = OpLabel 2448 %22 = OpIAdd %6 %37 %21 2449 OpBranch %14 2450 %14 = OpLabel 2451 %24 = OpIAdd %6 %38 %21 2452 OpBranch %11 2453 %13 = OpLabel 2454 OpBranch %26 2455 %26 = OpLabel 2456 %39 = OpPhi %6 %37 %13 %34 %29 2457 %40 = OpPhi %6 %21 %13 %36 %29 2458 OpLoopMerge %28 %29 Unroll 2459 OpBranch %30 2460 %30 = OpLabel 2461 %32 = OpSGreaterThanEqual %18 %40 %9 2462 OpBranchConditional %32 %27 %28 2463 %27 = OpLabel 2464 %34 = OpIAdd %6 %39 %21 2465 OpBranch %29 2466 %29 = OpLabel 2467 %36 = OpISub %6 %40 %21 2468 OpBranch %26 2469 %28 = OpLabel 2470 OpReturn 2471 OpFunctionEnd 2472 )"; 2473 2474 const std::string output = R"(OpCapability Shader 2475 %1 = OpExtInstImport "GLSL.std.450" 2476 OpMemoryModel Logical GLSL450 2477 OpEntryPoint Fragment %2 "main" 2478 OpExecutionMode %2 OriginUpperLeft 2479 OpSource GLSL 440 2480 OpName %2 "main" 2481 %3 = OpTypeVoid 2482 %4 = OpTypeFunction %3 2483 %5 = OpTypeInt 32 1 2484 %6 = OpTypePointer Function %5 2485 %7 = OpConstant %5 0 2486 %8 = OpConstant %5 2 2487 %9 = OpTypeBool 2488 %10 = OpConstant %5 1 2489 %2 = OpFunction %3 None %4 2490 %11 = OpLabel 2491 OpBranch %12 2492 %12 = OpLabel 2493 OpBranch %19 2494 %19 = OpLabel 2495 %20 = OpSLessThanEqual %9 %7 %8 2496 OpBranch %21 2497 %21 = OpLabel 2498 %14 = OpIAdd %5 %7 %10 2499 OpBranch %15 2500 %15 = OpLabel 2501 %17 = OpIAdd %5 %7 %10 2502 OpBranch %41 2503 %41 = OpLabel 2504 OpBranch %44 2505 %44 = OpLabel 2506 %45 = OpSLessThanEqual %9 %17 %8 2507 OpBranch %46 2508 %46 = OpLabel 2509 %47 = OpIAdd %5 %14 %10 2510 OpBranch %48 2511 %48 = OpLabel 2512 %49 = OpIAdd %5 %17 %10 2513 OpBranch %50 2514 %50 = OpLabel 2515 OpBranch %53 2516 %53 = OpLabel 2517 %54 = OpSLessThanEqual %9 %49 %8 2518 OpBranch %55 2519 %55 = OpLabel 2520 %56 = OpIAdd %5 %47 %10 2521 OpBranch %57 2522 %57 = OpLabel 2523 %58 = OpIAdd %5 %49 %10 2524 OpBranch %18 2525 %18 = OpLabel 2526 OpBranch %22 2527 %22 = OpLabel 2528 OpBranch %29 2529 %29 = OpLabel 2530 %30 = OpSGreaterThanEqual %9 %10 %7 2531 OpBranch %31 2532 %31 = OpLabel 2533 %24 = OpIAdd %5 %56 %10 2534 OpBranch %25 2535 %25 = OpLabel 2536 %27 = OpISub %5 %10 %10 2537 OpBranch %32 2538 %32 = OpLabel 2539 OpBranch %35 2540 %35 = OpLabel 2541 %36 = OpSGreaterThanEqual %9 %27 %7 2542 OpBranch %37 2543 %37 = OpLabel 2544 %38 = OpIAdd %5 %24 %10 2545 OpBranch %39 2546 %39 = OpLabel 2547 %40 = OpISub %5 %27 %10 2548 OpBranch %28 2549 %28 = OpLabel 2550 OpReturn 2551 OpFunctionEnd 2552 )"; 2553 2554 std::unique_ptr<IRContext> context = 2555 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 2556 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2557 Module* module = context->module(); 2558 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 2559 << text << std::endl; 2560 2561 LoopUnroller loop_unroller; 2562 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 2563 SinglePassRunAndCheck<LoopUnroller>(text, output, false); 2564 } 2565 2566 // With LocalMultiStoreElimPass 2567 const std::string condition_in_header = R"( 2568 OpCapability Shader 2569 OpMemoryModel Logical GLSL450 2570 OpEntryPoint Fragment %main "main" %o 2571 OpExecutionMode %main OriginUpperLeft 2572 OpSource GLSL 430 2573 OpDecorate %o Location 0 2574 %void = OpTypeVoid 2575 %6 = OpTypeFunction %void 2576 %int = OpTypeInt 32 1 2577 %int_n2 = OpConstant %int -2 2578 %int_2 = OpConstant %int 2 2579 %bool = OpTypeBool 2580 %float = OpTypeFloat 32 2581 %_ptr_Output_float = OpTypePointer Output %float 2582 %o = OpVariable %_ptr_Output_float Output 2583 %float_1 = OpConstant %float 1 2584 %main = OpFunction %void None %6 2585 %15 = OpLabel 2586 OpBranch %16 2587 %16 = OpLabel 2588 %27 = OpPhi %int %int_n2 %15 %26 %18 2589 %21 = OpSLessThanEqual %bool %27 %int_2 2590 OpLoopMerge %17 %18 Unroll 2591 OpBranchConditional %21 %22 %17 2592 %22 = OpLabel 2593 %23 = OpLoad %float %o 2594 %24 = OpFAdd %float %23 %float_1 2595 OpStore %o %24 2596 OpBranch %18 2597 %18 = OpLabel 2598 %26 = OpIAdd %int %27 %int_2 2599 OpBranch %16 2600 %17 = OpLabel 2601 OpReturn 2602 OpFunctionEnd 2603 )"; 2604 2605 TEST_F(PassClassTest, FullyUnrollConditionIsInHeaderBlock) { 2606 const std::string output = R"(OpCapability Shader 2607 OpMemoryModel Logical GLSL450 2608 OpEntryPoint Fragment %1 "main" %2 2609 OpExecutionMode %1 OriginUpperLeft 2610 OpSource GLSL 430 2611 OpDecorate %2 Location 0 2612 %3 = OpTypeVoid 2613 %4 = OpTypeFunction %3 2614 %5 = OpTypeInt 32 1 2615 %6 = OpConstant %5 -2 2616 %7 = OpConstant %5 2 2617 %8 = OpTypeBool 2618 %9 = OpTypeFloat 32 2619 %10 = OpTypePointer Output %9 2620 %2 = OpVariable %10 Output 2621 %11 = OpConstant %9 1 2622 %1 = OpFunction %3 None %4 2623 %12 = OpLabel 2624 OpBranch %13 2625 %13 = OpLabel 2626 %17 = OpSLessThanEqual %8 %6 %7 2627 OpBranch %19 2628 %19 = OpLabel 2629 %20 = OpLoad %9 %2 2630 %21 = OpFAdd %9 %20 %11 2631 OpStore %2 %21 2632 OpBranch %16 2633 %16 = OpLabel 2634 %15 = OpIAdd %5 %6 %7 2635 OpBranch %22 2636 %22 = OpLabel 2637 %24 = OpSLessThanEqual %8 %15 %7 2638 OpBranch %25 2639 %25 = OpLabel 2640 %26 = OpLoad %9 %2 2641 %27 = OpFAdd %9 %26 %11 2642 OpStore %2 %27 2643 OpBranch %28 2644 %28 = OpLabel 2645 %29 = OpIAdd %5 %15 %7 2646 OpBranch %30 2647 %30 = OpLabel 2648 %32 = OpSLessThanEqual %8 %29 %7 2649 OpBranch %33 2650 %33 = OpLabel 2651 %34 = OpLoad %9 %2 2652 %35 = OpFAdd %9 %34 %11 2653 OpStore %2 %35 2654 OpBranch %36 2655 %36 = OpLabel 2656 %37 = OpIAdd %5 %29 %7 2657 OpBranch %18 2658 %18 = OpLabel 2659 OpReturn 2660 OpFunctionEnd 2661 )"; 2662 2663 std::unique_ptr<IRContext> context = 2664 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header, 2665 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2666 Module* module = context->module(); 2667 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 2668 << condition_in_header << std::endl; 2669 2670 LoopUnroller loop_unroller; 2671 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 2672 SinglePassRunAndCheck<LoopUnroller>(condition_in_header, output, false); 2673 } 2674 2675 TEST_F(PassClassTest, PartiallyUnrollResidualConditionIsInHeaderBlock) { 2676 const std::string output = R"(OpCapability Shader 2677 OpMemoryModel Logical GLSL450 2678 OpEntryPoint Fragment %1 "main" %2 2679 OpExecutionMode %1 OriginUpperLeft 2680 OpSource GLSL 430 2681 OpDecorate %2 Location 0 2682 %3 = OpTypeVoid 2683 %4 = OpTypeFunction %3 2684 %5 = OpTypeInt 32 1 2685 %6 = OpConstant %5 -2 2686 %7 = OpConstant %5 2 2687 %8 = OpTypeBool 2688 %9 = OpTypeFloat 32 2689 %10 = OpTypePointer Output %9 2690 %2 = OpVariable %10 Output 2691 %11 = OpConstant %9 1 2692 %40 = OpTypeInt 32 0 2693 %41 = OpConstant %40 1 2694 %1 = OpFunction %3 None %4 2695 %12 = OpLabel 2696 OpBranch %13 2697 %13 = OpLabel 2698 %14 = OpPhi %5 %6 %12 %15 %16 2699 %17 = OpSLessThanEqual %8 %14 %41 2700 OpLoopMerge %22 %16 Unroll 2701 OpBranchConditional %17 %19 %22 2702 %19 = OpLabel 2703 %20 = OpLoad %9 %2 2704 %21 = OpFAdd %9 %20 %11 2705 OpStore %2 %21 2706 OpBranch %16 2707 %16 = OpLabel 2708 %15 = OpIAdd %5 %14 %7 2709 OpBranch %13 2710 %22 = OpLabel 2711 OpBranch %23 2712 %23 = OpLabel 2713 %24 = OpPhi %5 %14 %22 %39 %38 2714 %25 = OpSLessThanEqual %8 %24 %7 2715 OpLoopMerge %31 %38 DontUnroll 2716 OpBranchConditional %25 %26 %31 2717 %26 = OpLabel 2718 %27 = OpLoad %9 %2 2719 %28 = OpFAdd %9 %27 %11 2720 OpStore %2 %28 2721 OpBranch %29 2722 %29 = OpLabel 2723 %30 = OpIAdd %5 %24 %7 2724 OpBranch %32 2725 %32 = OpLabel 2726 %34 = OpSLessThanEqual %8 %30 %7 2727 OpBranch %35 2728 %35 = OpLabel 2729 %36 = OpLoad %9 %2 2730 %37 = OpFAdd %9 %36 %11 2731 OpStore %2 %37 2732 OpBranch %38 2733 %38 = OpLabel 2734 %39 = OpIAdd %5 %30 %7 2735 OpBranch %23 2736 %31 = OpLabel 2737 OpReturn 2738 %18 = OpLabel 2739 OpReturn 2740 OpFunctionEnd 2741 )"; 2742 2743 std::unique_ptr<IRContext> context = 2744 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header, 2745 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2746 Module* module = context->module(); 2747 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" 2748 << condition_in_header << std::endl; 2749 2750 LoopUnroller loop_unroller; 2751 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 2752 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(condition_in_header, output, 2753 false); 2754 } 2755 2756 /* 2757 Generated from following GLSL with latch block artificially inserted to be 2758 seperate from continue. 2759 #version 430 2760 void main(void) { 2761 float x[10]; 2762 for (int i = 0; i < 10; ++i) { 2763 x[i] = i; 2764 } 2765 } 2766 */ 2767 TEST_F(PassClassTest, PartiallyUnrollLatchNotContinue) { 2768 const std::string text = R"(OpCapability Shader 2769 %1 = OpExtInstImport "GLSL.std.450" 2770 OpMemoryModel Logical GLSL450 2771 OpEntryPoint Fragment %2 "main" 2772 OpExecutionMode %2 OriginUpperLeft 2773 OpSource GLSL 430 2774 OpName %2 "main" 2775 OpName %3 "i" 2776 OpName %4 "x" 2777 %5 = OpTypeVoid 2778 %6 = OpTypeFunction %5 2779 %7 = OpTypeInt 32 1 2780 %8 = OpTypePointer Function %7 2781 %9 = OpConstant %7 0 2782 %10 = OpConstant %7 10 2783 %11 = OpTypeBool 2784 %12 = OpTypeFloat 32 2785 %13 = OpTypeInt 32 0 2786 %14 = OpConstant %13 10 2787 %15 = OpTypeArray %12 %14 2788 %16 = OpTypePointer Function %15 2789 %17 = OpTypePointer Function %12 2790 %18 = OpConstant %7 1 2791 %2 = OpFunction %5 None %6 2792 %19 = OpLabel 2793 %3 = OpVariable %8 Function 2794 %4 = OpVariable %16 Function 2795 OpStore %3 %9 2796 OpBranch %20 2797 %20 = OpLabel 2798 %21 = OpPhi %7 %9 %19 %22 %30 2799 OpLoopMerge %24 %23 Unroll 2800 OpBranch %25 2801 %25 = OpLabel 2802 %26 = OpSLessThan %11 %21 %10 2803 OpBranchConditional %26 %27 %24 2804 %27 = OpLabel 2805 %28 = OpConvertSToF %12 %21 2806 %29 = OpAccessChain %17 %4 %21 2807 OpStore %29 %28 2808 OpBranch %23 2809 %23 = OpLabel 2810 %22 = OpIAdd %7 %21 %18 2811 OpStore %3 %22 2812 OpBranch %30 2813 %30 = OpLabel 2814 OpBranch %20 2815 %24 = OpLabel 2816 OpReturn 2817 OpFunctionEnd 2818 )"; 2819 2820 const std::string expected = R"(OpCapability Shader 2821 %1 = OpExtInstImport "GLSL.std.450" 2822 OpMemoryModel Logical GLSL450 2823 OpEntryPoint Fragment %2 "main" 2824 OpExecutionMode %2 OriginUpperLeft 2825 OpSource GLSL 430 2826 OpName %2 "main" 2827 OpName %3 "i" 2828 OpName %4 "x" 2829 %5 = OpTypeVoid 2830 %6 = OpTypeFunction %5 2831 %7 = OpTypeInt 32 1 2832 %8 = OpTypePointer Function %7 2833 %9 = OpConstant %7 0 2834 %10 = OpConstant %7 10 2835 %11 = OpTypeBool 2836 %12 = OpTypeFloat 32 2837 %13 = OpTypeInt 32 0 2838 %14 = OpConstant %13 10 2839 %15 = OpTypeArray %12 %14 2840 %16 = OpTypePointer Function %15 2841 %17 = OpTypePointer Function %12 2842 %18 = OpConstant %7 1 2843 %63 = OpConstant %13 1 2844 %2 = OpFunction %5 None %6 2845 %19 = OpLabel 2846 %3 = OpVariable %8 Function 2847 %4 = OpVariable %16 Function 2848 OpStore %3 %9 2849 OpBranch %20 2850 %20 = OpLabel 2851 %21 = OpPhi %7 %9 %19 %22 %23 2852 OpLoopMerge %31 %25 Unroll 2853 OpBranch %26 2854 %26 = OpLabel 2855 %27 = OpSLessThan %11 %21 %63 2856 OpBranchConditional %27 %28 %31 2857 %28 = OpLabel 2858 %29 = OpConvertSToF %12 %21 2859 %30 = OpAccessChain %17 %4 %21 2860 OpStore %30 %29 2861 OpBranch %25 2862 %25 = OpLabel 2863 %22 = OpIAdd %7 %21 %18 2864 OpStore %3 %22 2865 OpBranch %23 2866 %23 = OpLabel 2867 OpBranch %20 2868 %31 = OpLabel 2869 OpBranch %32 2870 %32 = OpLabel 2871 %33 = OpPhi %7 %21 %31 %61 %62 2872 OpLoopMerge %42 %60 DontUnroll 2873 OpBranch %34 2874 %34 = OpLabel 2875 %35 = OpSLessThan %11 %33 %10 2876 OpBranchConditional %35 %36 %42 2877 %36 = OpLabel 2878 %37 = OpConvertSToF %12 %33 2879 %38 = OpAccessChain %17 %4 %33 2880 OpStore %38 %37 2881 OpBranch %39 2882 %39 = OpLabel 2883 %40 = OpIAdd %7 %33 %18 2884 OpStore %3 %40 2885 OpBranch %41 2886 %41 = OpLabel 2887 OpBranch %43 2888 %43 = OpLabel 2889 OpBranch %45 2890 %45 = OpLabel 2891 %46 = OpSLessThan %11 %40 %10 2892 OpBranch %47 2893 %47 = OpLabel 2894 %48 = OpConvertSToF %12 %40 2895 %49 = OpAccessChain %17 %4 %40 2896 OpStore %49 %48 2897 OpBranch %50 2898 %50 = OpLabel 2899 %51 = OpIAdd %7 %40 %18 2900 OpStore %3 %51 2901 OpBranch %52 2902 %52 = OpLabel 2903 OpBranch %53 2904 %53 = OpLabel 2905 OpBranch %55 2906 %55 = OpLabel 2907 %56 = OpSLessThan %11 %51 %10 2908 OpBranch %57 2909 %57 = OpLabel 2910 %58 = OpConvertSToF %12 %51 2911 %59 = OpAccessChain %17 %4 %51 2912 OpStore %59 %58 2913 OpBranch %60 2914 %60 = OpLabel 2915 %61 = OpIAdd %7 %51 %18 2916 OpStore %3 %61 2917 OpBranch %62 2918 %62 = OpLabel 2919 OpBranch %32 2920 %42 = OpLabel 2921 OpReturn 2922 %24 = OpLabel 2923 OpReturn 2924 OpFunctionEnd 2925 )"; 2926 2927 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); 2928 SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, expected, true); 2929 2930 // Make sure the latch block information is preserved and propagated correctly 2931 // by the pass. 2932 std::unique_ptr<IRContext> context = 2933 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 2934 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2935 2936 PartialUnrollerTestPass<3> unroller; 2937 unroller.SetContextForTesting(context.get()); 2938 unroller.Process(); 2939 2940 Module* module = context->module(); 2941 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 2942 << text << std::endl; 2943 const Function* f = spvtest::GetFunction(module, 2); 2944 LoopDescriptor ld{context.get(), f}; 2945 2946 EXPECT_EQ(ld.NumLoops(), 2u); 2947 2948 Loop& loop_1 = ld.GetLoopByIndex(0u); 2949 EXPECT_NE(loop_1.GetLatchBlock(), loop_1.GetContinueBlock()); 2950 2951 Loop& loop_2 = ld.GetLoopByIndex(1u); 2952 EXPECT_NE(loop_2.GetLatchBlock(), loop_2.GetContinueBlock()); 2953 } 2954 2955 // Test that a loop with a self-referencing OpPhi instruction is handled 2956 // correctly. 2957 TEST_F(PassClassTest, OpPhiSelfReference) { 2958 const std::string text = R"( 2959 ; Find the two adds from the unrolled loop 2960 ; CHECK: OpIAdd 2961 ; CHECK: OpIAdd 2962 ; CHECK: OpIAdd %uint %uint_0 %uint_1 2963 ; CHECK-NEXT: OpReturn 2964 OpCapability Shader 2965 %1 = OpExtInstImport "GLSL.std.450" 2966 OpMemoryModel Logical GLSL450 2967 OpEntryPoint GLCompute %2 "main" 2968 OpExecutionMode %2 LocalSize 8 8 1 2969 OpSource HLSL 600 2970 %uint = OpTypeInt 32 0 2971 %void = OpTypeVoid 2972 %5 = OpTypeFunction %void 2973 %uint_0 = OpConstant %uint 0 2974 %uint_1 = OpConstant %uint 1 2975 %bool = OpTypeBool 2976 %true = OpConstantTrue %bool 2977 %2 = OpFunction %void None %5 2978 %10 = OpLabel 2979 OpBranch %19 2980 %19 = OpLabel 2981 %20 = OpPhi %uint %uint_0 %10 %20 %21 2982 %22 = OpPhi %uint %uint_0 %10 %23 %21 2983 %24 = OpULessThanEqual %bool %22 %uint_1 2984 OpLoopMerge %25 %21 Unroll 2985 OpBranchConditional %24 %21 %25 2986 %21 = OpLabel 2987 %23 = OpIAdd %uint %22 %uint_1 2988 OpBranch %19 2989 %25 = OpLabel 2990 %14 = OpIAdd %uint %20 %uint_1 2991 OpReturn 2992 OpFunctionEnd 2993 )"; 2994 2995 const bool kFullyUnroll = true; 2996 const uint32_t kUnrollFactor = 0; 2997 SinglePassRunAndMatch<opt::LoopUnroller>(text, true, kFullyUnroll, 2998 kUnrollFactor); 2999 } 3000 3001 } // namespace 3002 } // namespace opt 3003 } // namespace spvtools 3004