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 <algorithm> 16 #include <iterator> 17 #include <memory> 18 #include <string> 19 #include <vector> 20 21 #include "effcee/effcee.h" 22 #include "gmock/gmock.h" 23 #include "source/opt/loop_descriptor.h" 24 #include "source/opt/loop_fusion.h" 25 #include "test/opt/pass_fixture.h" 26 27 namespace spvtools { 28 namespace opt { 29 namespace { 30 31 using FusionLegalTest = PassTest<::testing::Test>; 32 33 bool Validate(const std::vector<uint32_t>& bin) { 34 spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; 35 spv_context spvContext = spvContextCreate(target_env); 36 spv_diagnostic diagnostic = nullptr; 37 spv_const_binary_t binary = {bin.data(), bin.size()}; 38 spv_result_t error = spvValidate(spvContext, &binary, &diagnostic); 39 if (error != 0) spvDiagnosticPrint(diagnostic); 40 spvDiagnosticDestroy(diagnostic); 41 spvContextDestroy(spvContext); 42 return error == 0; 43 } 44 45 void Match(const std::string& checks, IRContext* context) { 46 // Silence unused warnings with !defined(SPIRV_EFFCE) 47 (void)checks; 48 49 std::vector<uint32_t> bin; 50 context->module()->ToBinary(&bin, true); 51 EXPECT_TRUE(Validate(bin)); 52 std::string assembly; 53 SpirvTools tools(SPV_ENV_UNIVERSAL_1_2); 54 EXPECT_TRUE( 55 tools.Disassemble(bin, &assembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)) 56 << "Disassembling failed for shader:\n" 57 << assembly << std::endl; 58 auto match_result = effcee::Match(assembly, checks); 59 EXPECT_EQ(effcee::Result::Status::Ok, match_result.status()) 60 << match_result.message() << "\nChecking result:\n" 61 << assembly; 62 } 63 64 /* 65 Generated from the following GLSL + --eliminate-local-multi-store 66 67 #version 440 core 68 void main() { 69 int[10] a; 70 int[10] b; 71 // No dependence, legal 72 for (int i = 0; i < 10; i++) { 73 a[i] = a[i]*2; 74 } 75 for (int i = 0; i < 10; i++) { 76 b[i] = b[i]+2; 77 } 78 } 79 80 */ 81 TEST_F(FusionLegalTest, DifferentArraysInLoops) { 82 std::string text = R"( 83 OpCapability Shader 84 %1 = OpExtInstImport "GLSL.std.450" 85 OpMemoryModel Logical GLSL450 86 OpEntryPoint Fragment %4 "main" 87 OpExecutionMode %4 OriginUpperLeft 88 OpSource GLSL 440 89 OpName %4 "main" 90 OpName %8 "i" 91 OpName %23 "a" 92 OpName %34 "i" 93 OpName %42 "b" 94 %2 = OpTypeVoid 95 %3 = OpTypeFunction %2 96 %6 = OpTypeInt 32 1 97 %7 = OpTypePointer Function %6 98 %9 = OpConstant %6 0 99 %16 = OpConstant %6 10 100 %17 = OpTypeBool 101 %19 = OpTypeInt 32 0 102 %20 = OpConstant %19 10 103 %21 = OpTypeArray %6 %20 104 %22 = OpTypePointer Function %21 105 %28 = OpConstant %6 2 106 %32 = OpConstant %6 1 107 %4 = OpFunction %2 None %3 108 %5 = OpLabel 109 %8 = OpVariable %7 Function 110 %23 = OpVariable %22 Function 111 %34 = OpVariable %7 Function 112 %42 = OpVariable %22 Function 113 OpStore %8 %9 114 OpBranch %10 115 %10 = OpLabel 116 %51 = OpPhi %6 %9 %5 %33 %13 117 OpLoopMerge %12 %13 None 118 OpBranch %14 119 %14 = OpLabel 120 %18 = OpSLessThan %17 %51 %16 121 OpBranchConditional %18 %11 %12 122 %11 = OpLabel 123 %26 = OpAccessChain %7 %23 %51 124 %27 = OpLoad %6 %26 125 %29 = OpIMul %6 %27 %28 126 %30 = OpAccessChain %7 %23 %51 127 OpStore %30 %29 128 OpBranch %13 129 %13 = OpLabel 130 %33 = OpIAdd %6 %51 %32 131 OpStore %8 %33 132 OpBranch %10 133 %12 = OpLabel 134 OpStore %34 %9 135 OpBranch %35 136 %35 = OpLabel 137 %52 = OpPhi %6 %9 %12 %50 %38 138 OpLoopMerge %37 %38 None 139 OpBranch %39 140 %39 = OpLabel 141 %41 = OpSLessThan %17 %52 %16 142 OpBranchConditional %41 %36 %37 143 %36 = OpLabel 144 %45 = OpAccessChain %7 %42 %52 145 %46 = OpLoad %6 %45 146 %47 = OpIAdd %6 %46 %28 147 %48 = OpAccessChain %7 %42 %52 148 OpStore %48 %47 149 OpBranch %38 150 %38 = OpLabel 151 %50 = OpIAdd %6 %52 %32 152 OpStore %34 %50 153 OpBranch %35 154 %37 = OpLabel 155 OpReturn 156 OpFunctionEnd 157 )"; 158 159 std::unique_ptr<IRContext> context = 160 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 161 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 162 Module* module = context->module(); 163 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 164 << text << std::endl; 165 Function& f = *module->begin(); 166 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 167 EXPECT_EQ(ld.NumLoops(), 2u); 168 169 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 170 171 LoopFusion fusion(context.get(), loops[0], loops[1]); 172 173 EXPECT_TRUE(fusion.AreCompatible()); 174 EXPECT_TRUE(fusion.IsLegal()); 175 176 fusion.Fuse(); 177 178 std::string checks = R"( 179 CHECK: [[PHI:%\w+]] = OpPhi 180 CHECK-NEXT: OpLoopMerge 181 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 182 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 183 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 184 CHECK-NEXT: OpStore [[STORE_0]] 185 CHECK-NOT: OpPhi 186 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 187 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 188 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 189 CHECK-NEXT: OpStore [[STORE_1]] 190 )"; 191 192 Match(checks, context.get()); 193 auto& ld_final = *context->GetLoopDescriptor(&f); 194 EXPECT_EQ(ld_final.NumLoops(), 1u); 195 } 196 197 /* 198 Generated from the following GLSL + --eliminate-local-multi-store 199 200 #version 440 core 201 void main() { 202 int[10] a; 203 int[10] b; 204 int[10] c; 205 // Only loads to the same array, legal 206 for (int i = 0; i < 10; i++) { 207 b[i] = a[i]*2; 208 } 209 for (int i = 0; i < 10; i++) { 210 c[i] = a[i]+2; 211 } 212 } 213 214 */ 215 TEST_F(FusionLegalTest, OnlyLoadsToSameArray) { 216 std::string text = R"( 217 OpCapability Shader 218 %1 = OpExtInstImport "GLSL.std.450" 219 OpMemoryModel Logical GLSL450 220 OpEntryPoint Fragment %4 "main" 221 OpExecutionMode %4 OriginUpperLeft 222 OpSource GLSL 440 223 OpName %4 "main" 224 OpName %8 "i" 225 OpName %23 "b" 226 OpName %25 "a" 227 OpName %35 "i" 228 OpName %43 "c" 229 %2 = OpTypeVoid 230 %3 = OpTypeFunction %2 231 %6 = OpTypeInt 32 1 232 %7 = OpTypePointer Function %6 233 %9 = OpConstant %6 0 234 %16 = OpConstant %6 10 235 %17 = OpTypeBool 236 %19 = OpTypeInt 32 0 237 %20 = OpConstant %19 10 238 %21 = OpTypeArray %6 %20 239 %22 = OpTypePointer Function %21 240 %29 = OpConstant %6 2 241 %33 = OpConstant %6 1 242 %4 = OpFunction %2 None %3 243 %5 = OpLabel 244 %8 = OpVariable %7 Function 245 %23 = OpVariable %22 Function 246 %25 = OpVariable %22 Function 247 %35 = OpVariable %7 Function 248 %43 = OpVariable %22 Function 249 OpStore %8 %9 250 OpBranch %10 251 %10 = OpLabel 252 %52 = OpPhi %6 %9 %5 %34 %13 253 OpLoopMerge %12 %13 None 254 OpBranch %14 255 %14 = OpLabel 256 %18 = OpSLessThan %17 %52 %16 257 OpBranchConditional %18 %11 %12 258 %11 = OpLabel 259 %27 = OpAccessChain %7 %25 %52 260 %28 = OpLoad %6 %27 261 %30 = OpIMul %6 %28 %29 262 %31 = OpAccessChain %7 %23 %52 263 OpStore %31 %30 264 OpBranch %13 265 %13 = OpLabel 266 %34 = OpIAdd %6 %52 %33 267 OpStore %8 %34 268 OpBranch %10 269 %12 = OpLabel 270 OpStore %35 %9 271 OpBranch %36 272 %36 = OpLabel 273 %53 = OpPhi %6 %9 %12 %51 %39 274 OpLoopMerge %38 %39 None 275 OpBranch %40 276 %40 = OpLabel 277 %42 = OpSLessThan %17 %53 %16 278 OpBranchConditional %42 %37 %38 279 %37 = OpLabel 280 %46 = OpAccessChain %7 %25 %53 281 %47 = OpLoad %6 %46 282 %48 = OpIAdd %6 %47 %29 283 %49 = OpAccessChain %7 %43 %53 284 OpStore %49 %48 285 OpBranch %39 286 %39 = OpLabel 287 %51 = OpIAdd %6 %53 %33 288 OpStore %35 %51 289 OpBranch %36 290 %38 = OpLabel 291 OpReturn 292 OpFunctionEnd 293 )"; 294 295 std::unique_ptr<IRContext> context = 296 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 297 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 298 Module* module = context->module(); 299 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 300 << text << std::endl; 301 Function& f = *module->begin(); 302 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 303 EXPECT_EQ(ld.NumLoops(), 2u); 304 305 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 306 307 LoopFusion fusion(context.get(), loops[0], loops[1]); 308 309 EXPECT_TRUE(fusion.AreCompatible()); 310 EXPECT_TRUE(fusion.IsLegal()); 311 312 fusion.Fuse(); 313 314 std::string checks = R"( 315 CHECK: [[PHI:%\w+]] = OpPhi 316 CHECK-NEXT: OpLoopMerge 317 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 318 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 319 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 320 CHECK-NEXT: OpStore [[STORE_0]] 321 CHECK-NOT: OpPhi 322 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 323 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 324 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 325 CHECK-NEXT: OpStore [[STORE_1]] 326 )"; 327 328 Match(checks, context.get()); 329 auto& ld_final = *context->GetLoopDescriptor(&f); 330 EXPECT_EQ(ld_final.NumLoops(), 1u); 331 } 332 333 /* 334 Generated from the following GLSL + --eliminate-local-multi-store 335 336 #version 440 core 337 void main() { 338 int[10] a; 339 int[10] b; 340 // No loop-carried dependences, legal 341 for (int i = 0; i < 10; i++) { 342 a[i] = a[i]*2; 343 } 344 for (int i = 0; i < 10; i++) { 345 b[i] = a[i]+2; 346 } 347 } 348 349 */ 350 TEST_F(FusionLegalTest, NoLoopCarriedDependences) { 351 std::string text = R"( 352 OpCapability Shader 353 %1 = OpExtInstImport "GLSL.std.450" 354 OpMemoryModel Logical GLSL450 355 OpEntryPoint Fragment %4 "main" 356 OpExecutionMode %4 OriginUpperLeft 357 OpSource GLSL 440 358 OpName %4 "main" 359 OpName %8 "i" 360 OpName %23 "a" 361 OpName %34 "i" 362 OpName %42 "b" 363 %2 = OpTypeVoid 364 %3 = OpTypeFunction %2 365 %6 = OpTypeInt 32 1 366 %7 = OpTypePointer Function %6 367 %9 = OpConstant %6 0 368 %16 = OpConstant %6 10 369 %17 = OpTypeBool 370 %19 = OpTypeInt 32 0 371 %20 = OpConstant %19 10 372 %21 = OpTypeArray %6 %20 373 %22 = OpTypePointer Function %21 374 %28 = OpConstant %6 2 375 %32 = OpConstant %6 1 376 %4 = OpFunction %2 None %3 377 %5 = OpLabel 378 %8 = OpVariable %7 Function 379 %23 = OpVariable %22 Function 380 %34 = OpVariable %7 Function 381 %42 = OpVariable %22 Function 382 OpStore %8 %9 383 OpBranch %10 384 %10 = OpLabel 385 %51 = OpPhi %6 %9 %5 %33 %13 386 OpLoopMerge %12 %13 None 387 OpBranch %14 388 %14 = OpLabel 389 %18 = OpSLessThan %17 %51 %16 390 OpBranchConditional %18 %11 %12 391 %11 = OpLabel 392 %26 = OpAccessChain %7 %23 %51 393 %27 = OpLoad %6 %26 394 %29 = OpIMul %6 %27 %28 395 %30 = OpAccessChain %7 %23 %51 396 OpStore %30 %29 397 OpBranch %13 398 %13 = OpLabel 399 %33 = OpIAdd %6 %51 %32 400 OpStore %8 %33 401 OpBranch %10 402 %12 = OpLabel 403 OpStore %34 %9 404 OpBranch %35 405 %35 = OpLabel 406 %52 = OpPhi %6 %9 %12 %50 %38 407 OpLoopMerge %37 %38 None 408 OpBranch %39 409 %39 = OpLabel 410 %41 = OpSLessThan %17 %52 %16 411 OpBranchConditional %41 %36 %37 412 %36 = OpLabel 413 %45 = OpAccessChain %7 %23 %52 414 %46 = OpLoad %6 %45 415 %47 = OpIAdd %6 %46 %28 416 %48 = OpAccessChain %7 %42 %52 417 OpStore %48 %47 418 OpBranch %38 419 %38 = OpLabel 420 %50 = OpIAdd %6 %52 %32 421 OpStore %34 %50 422 OpBranch %35 423 %37 = OpLabel 424 OpReturn 425 OpFunctionEnd 426 )"; 427 428 std::unique_ptr<IRContext> context = 429 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 430 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 431 Module* module = context->module(); 432 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 433 << text << std::endl; 434 Function& f = *module->begin(); 435 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 436 EXPECT_EQ(ld.NumLoops(), 2u); 437 438 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 439 440 LoopFusion fusion(context.get(), loops[0], loops[1]); 441 442 EXPECT_TRUE(fusion.AreCompatible()); 443 EXPECT_TRUE(fusion.IsLegal()); 444 445 fusion.Fuse(); 446 447 std::string checks = R"( 448 CHECK: [[PHI:%\w+]] = OpPhi 449 CHECK-NEXT: OpLoopMerge 450 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 451 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 452 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 453 CHECK-NEXT: OpStore [[STORE_0]] 454 CHECK-NOT: OpPhi 455 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 456 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 457 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 458 CHECK-NEXT: OpStore [[STORE_1]] 459 )"; 460 461 Match(checks, context.get()); 462 auto& ld_final = *context->GetLoopDescriptor(&f); 463 EXPECT_EQ(ld_final.NumLoops(), 1u); 464 } 465 466 /* 467 Generated from the following GLSL + --eliminate-local-multi-store 468 469 #version 440 core 470 void main() { 471 int[10] a; 472 int[10] b; 473 int[10] c; 474 // Parallelism inhibiting, but legal. 475 for (int i = 0; i < 10; i++) { 476 a[i] = b[i] + 1; 477 } 478 for (int i = 0; i < 10; i++) { 479 c[i] = a[i] + c[i-1]; 480 } 481 } 482 483 */ 484 TEST_F(FusionLegalTest, ExistingLoopCarriedDependence) { 485 std::string text = R"( 486 OpCapability Shader 487 %1 = OpExtInstImport "GLSL.std.450" 488 OpMemoryModel Logical GLSL450 489 OpEntryPoint Fragment %4 "main" 490 OpExecutionMode %4 OriginUpperLeft 491 OpSource GLSL 440 492 OpName %4 "main" 493 OpName %8 "i" 494 OpName %23 "a" 495 OpName %25 "b" 496 OpName %34 "i" 497 OpName %42 "c" 498 %2 = OpTypeVoid 499 %3 = OpTypeFunction %2 500 %6 = OpTypeInt 32 1 501 %7 = OpTypePointer Function %6 502 %9 = OpConstant %6 0 503 %16 = OpConstant %6 10 504 %17 = OpTypeBool 505 %19 = OpTypeInt 32 0 506 %20 = OpConstant %19 10 507 %21 = OpTypeArray %6 %20 508 %22 = OpTypePointer Function %21 509 %29 = OpConstant %6 1 510 %4 = OpFunction %2 None %3 511 %5 = OpLabel 512 %8 = OpVariable %7 Function 513 %23 = OpVariable %22 Function 514 %25 = OpVariable %22 Function 515 %34 = OpVariable %7 Function 516 %42 = OpVariable %22 Function 517 OpStore %8 %9 518 OpBranch %10 519 %10 = OpLabel 520 %55 = OpPhi %6 %9 %5 %33 %13 521 OpLoopMerge %12 %13 None 522 OpBranch %14 523 %14 = OpLabel 524 %18 = OpSLessThan %17 %55 %16 525 OpBranchConditional %18 %11 %12 526 %11 = OpLabel 527 %27 = OpAccessChain %7 %25 %55 528 %28 = OpLoad %6 %27 529 %30 = OpIAdd %6 %28 %29 530 %31 = OpAccessChain %7 %23 %55 531 OpStore %31 %30 532 OpBranch %13 533 %13 = OpLabel 534 %33 = OpIAdd %6 %55 %29 535 OpStore %8 %33 536 OpBranch %10 537 %12 = OpLabel 538 OpStore %34 %9 539 OpBranch %35 540 %35 = OpLabel 541 %56 = OpPhi %6 %9 %12 %54 %38 542 OpLoopMerge %37 %38 None 543 OpBranch %39 544 %39 = OpLabel 545 %41 = OpSLessThan %17 %56 %16 546 OpBranchConditional %41 %36 %37 547 %36 = OpLabel 548 %45 = OpAccessChain %7 %23 %56 549 %46 = OpLoad %6 %45 550 %48 = OpISub %6 %56 %29 551 %49 = OpAccessChain %7 %42 %48 552 %50 = OpLoad %6 %49 553 %51 = OpIAdd %6 %46 %50 554 %52 = OpAccessChain %7 %42 %56 555 OpStore %52 %51 556 OpBranch %38 557 %38 = OpLabel 558 %54 = OpIAdd %6 %56 %29 559 OpStore %34 %54 560 OpBranch %35 561 %37 = OpLabel 562 OpReturn 563 OpFunctionEnd 564 )"; 565 566 std::unique_ptr<IRContext> context = 567 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 568 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 569 Module* module = context->module(); 570 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 571 << text << std::endl; 572 Function& f = *module->begin(); 573 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 574 EXPECT_EQ(ld.NumLoops(), 2u); 575 576 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 577 578 LoopFusion fusion(context.get(), loops[0], loops[1]); 579 580 EXPECT_TRUE(fusion.AreCompatible()); 581 EXPECT_TRUE(fusion.IsLegal()); 582 583 fusion.Fuse(); 584 585 std::string checks = R"( 586 CHECK: [[PHI:%\w+]] = OpPhi 587 CHECK-NEXT: OpLoopMerge 588 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 589 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 590 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 591 CHECK-NEXT: OpStore [[STORE_0]] 592 CHECK-NOT: OpPhi 593 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 594 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 595 CHECK: [[I_1:%\w+]] = OpISub {{%\w+}} [[PHI]] {{%\w+}} 596 CHECK-NEXT: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] 597 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] 598 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 599 CHECK-NEXT: OpStore [[STORE_1]] 600 )"; 601 602 Match(checks, context.get()); 603 auto& ld_final = *context->GetLoopDescriptor(&f); 604 EXPECT_EQ(ld_final.NumLoops(), 1u); 605 } 606 607 /* 608 Generated from the following GLSL + --eliminate-local-multi-store 609 610 #version 440 core 611 void main() { 612 int[10] a; 613 int[10] b; 614 int[10] c; 615 // Creates a loop-carried dependence, but negative, so legal 616 for (int i = 0; i < 10; i++) { 617 a[i+1] = b[i] + 1; 618 } 619 for (int i = 0; i < 10; i++) { 620 c[i] = a[i] + 2; 621 } 622 } 623 624 */ 625 TEST_F(FusionLegalTest, NegativeDistanceCreatedRAW) { 626 std::string text = R"( 627 OpCapability Shader 628 %1 = OpExtInstImport "GLSL.std.450" 629 OpMemoryModel Logical GLSL450 630 OpEntryPoint Fragment %4 "main" 631 OpExecutionMode %4 OriginUpperLeft 632 OpSource GLSL 440 633 OpName %4 "main" 634 OpName %8 "i" 635 OpName %23 "a" 636 OpName %27 "b" 637 OpName %35 "i" 638 OpName %43 "c" 639 %2 = OpTypeVoid 640 %3 = OpTypeFunction %2 641 %6 = OpTypeInt 32 1 642 %7 = OpTypePointer Function %6 643 %9 = OpConstant %6 0 644 %16 = OpConstant %6 10 645 %17 = OpTypeBool 646 %19 = OpTypeInt 32 0 647 %20 = OpConstant %19 10 648 %21 = OpTypeArray %6 %20 649 %22 = OpTypePointer Function %21 650 %25 = OpConstant %6 1 651 %48 = OpConstant %6 2 652 %4 = OpFunction %2 None %3 653 %5 = OpLabel 654 %8 = OpVariable %7 Function 655 %23 = OpVariable %22 Function 656 %27 = OpVariable %22 Function 657 %35 = OpVariable %7 Function 658 %43 = OpVariable %22 Function 659 OpStore %8 %9 660 OpBranch %10 661 %10 = OpLabel 662 %53 = OpPhi %6 %9 %5 %34 %13 663 OpLoopMerge %12 %13 None 664 OpBranch %14 665 %14 = OpLabel 666 %18 = OpSLessThan %17 %53 %16 667 OpBranchConditional %18 %11 %12 668 %11 = OpLabel 669 %26 = OpIAdd %6 %53 %25 670 %29 = OpAccessChain %7 %27 %53 671 %30 = OpLoad %6 %29 672 %31 = OpIAdd %6 %30 %25 673 %32 = OpAccessChain %7 %23 %26 674 OpStore %32 %31 675 OpBranch %13 676 %13 = OpLabel 677 %34 = OpIAdd %6 %53 %25 678 OpStore %8 %34 679 OpBranch %10 680 %12 = OpLabel 681 OpStore %35 %9 682 OpBranch %36 683 %36 = OpLabel 684 %54 = OpPhi %6 %9 %12 %52 %39 685 OpLoopMerge %38 %39 None 686 OpBranch %40 687 %40 = OpLabel 688 %42 = OpSLessThan %17 %54 %16 689 OpBranchConditional %42 %37 %38 690 %37 = OpLabel 691 %46 = OpAccessChain %7 %23 %54 692 %47 = OpLoad %6 %46 693 %49 = OpIAdd %6 %47 %48 694 %50 = OpAccessChain %7 %43 %54 695 OpStore %50 %49 696 OpBranch %39 697 %39 = OpLabel 698 %52 = OpIAdd %6 %54 %25 699 OpStore %35 %52 700 OpBranch %36 701 %38 = OpLabel 702 OpReturn 703 OpFunctionEnd 704 )"; 705 706 std::unique_ptr<IRContext> context = 707 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 708 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 709 Module* module = context->module(); 710 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 711 << text << std::endl; 712 Function& f = *module->begin(); 713 714 { 715 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 716 EXPECT_EQ(ld.NumLoops(), 2u); 717 718 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 719 720 LoopFusion fusion(context.get(), loops[0], loops[1]); 721 722 EXPECT_TRUE(fusion.AreCompatible()); 723 EXPECT_TRUE(fusion.IsLegal()); 724 725 fusion.Fuse(); 726 727 std::string checks = R"( 728 CHECK: [[PHI:%\w+]] = OpPhi 729 CHECK-NEXT: OpLoopMerge 730 CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} 731 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 732 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 733 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] 734 CHECK-NEXT: OpStore [[STORE_0]] 735 CHECK-NOT: OpPhi 736 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 737 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 738 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 739 CHECK-NEXT: OpStore [[STORE_1]] 740 )"; 741 742 Match(checks, context.get()); 743 } 744 745 { 746 auto& ld = *context->GetLoopDescriptor(&f); 747 EXPECT_EQ(ld.NumLoops(), 1u); 748 } 749 } 750 751 /* 752 Generated from the following GLSL + --eliminate-local-multi-store 753 754 #version 440 core 755 void main() { 756 int[10] a; 757 int[10] b; 758 int[10] c; 759 // Legal 760 for (int i = 0; i < 10; i++) { 761 a[i+1] = b[i] + 1; 762 } 763 for (int i = 0; i < 10; i++) { 764 c[i] = a[i+1] + 2; 765 } 766 } 767 768 */ 769 TEST_F(FusionLegalTest, NoLoopCarriedDependencesAdjustedIndex) { 770 std::string text = R"( 771 OpCapability Shader 772 %1 = OpExtInstImport "GLSL.std.450" 773 OpMemoryModel Logical GLSL450 774 OpEntryPoint Fragment %4 "main" 775 OpExecutionMode %4 OriginUpperLeft 776 OpSource GLSL 440 777 OpName %4 "main" 778 OpName %8 "i" 779 OpName %23 "a" 780 OpName %27 "b" 781 OpName %35 "i" 782 OpName %43 "c" 783 %2 = OpTypeVoid 784 %3 = OpTypeFunction %2 785 %6 = OpTypeInt 32 1 786 %7 = OpTypePointer Function %6 787 %9 = OpConstant %6 0 788 %16 = OpConstant %6 10 789 %17 = OpTypeBool 790 %19 = OpTypeInt 32 0 791 %20 = OpConstant %19 10 792 %21 = OpTypeArray %6 %20 793 %22 = OpTypePointer Function %21 794 %25 = OpConstant %6 1 795 %49 = OpConstant %6 2 796 %4 = OpFunction %2 None %3 797 %5 = OpLabel 798 %8 = OpVariable %7 Function 799 %23 = OpVariable %22 Function 800 %27 = OpVariable %22 Function 801 %35 = OpVariable %7 Function 802 %43 = OpVariable %22 Function 803 OpStore %8 %9 804 OpBranch %10 805 %10 = OpLabel 806 %54 = OpPhi %6 %9 %5 %34 %13 807 OpLoopMerge %12 %13 None 808 OpBranch %14 809 %14 = OpLabel 810 %18 = OpSLessThan %17 %54 %16 811 OpBranchConditional %18 %11 %12 812 %11 = OpLabel 813 %26 = OpIAdd %6 %54 %25 814 %29 = OpAccessChain %7 %27 %54 815 %30 = OpLoad %6 %29 816 %31 = OpIAdd %6 %30 %25 817 %32 = OpAccessChain %7 %23 %26 818 OpStore %32 %31 819 OpBranch %13 820 %13 = OpLabel 821 %34 = OpIAdd %6 %54 %25 822 OpStore %8 %34 823 OpBranch %10 824 %12 = OpLabel 825 OpStore %35 %9 826 OpBranch %36 827 %36 = OpLabel 828 %55 = OpPhi %6 %9 %12 %53 %39 829 OpLoopMerge %38 %39 None 830 OpBranch %40 831 %40 = OpLabel 832 %42 = OpSLessThan %17 %55 %16 833 OpBranchConditional %42 %37 %38 834 %37 = OpLabel 835 %46 = OpIAdd %6 %55 %25 836 %47 = OpAccessChain %7 %23 %46 837 %48 = OpLoad %6 %47 838 %50 = OpIAdd %6 %48 %49 839 %51 = OpAccessChain %7 %43 %55 840 OpStore %51 %50 841 OpBranch %39 842 %39 = OpLabel 843 %53 = OpIAdd %6 %55 %25 844 OpStore %35 %53 845 OpBranch %36 846 %38 = OpLabel 847 OpReturn 848 OpFunctionEnd 849 )"; 850 851 std::unique_ptr<IRContext> context = 852 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 853 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 854 Module* module = context->module(); 855 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 856 << text << std::endl; 857 Function& f = *module->begin(); 858 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 859 EXPECT_EQ(ld.NumLoops(), 2u); 860 861 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 862 863 LoopFusion fusion(context.get(), loops[0], loops[1]); 864 865 EXPECT_TRUE(fusion.AreCompatible()); 866 EXPECT_TRUE(fusion.IsLegal()); 867 868 fusion.Fuse(); 869 870 std::string checks = R"( 871 CHECK: [[PHI:%\w+]] = OpPhi 872 CHECK-NEXT: OpLoopMerge 873 CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} 874 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 875 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 876 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] 877 CHECK-NEXT: OpStore [[STORE_0]] 878 CHECK-NOT: OpPhi 879 CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} 880 CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] 881 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 882 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 883 CHECK-NEXT: OpStore [[STORE_1]] 884 )"; 885 886 Match(checks, context.get()); 887 auto& ld_final = *context->GetLoopDescriptor(&f); 888 EXPECT_EQ(ld_final.NumLoops(), 1u); 889 } 890 891 /* 892 Generated from the following GLSL + --eliminate-local-multi-store 893 894 #version 440 core 895 void main() { 896 int[10] a; 897 int[10] b; 898 int[10] c; 899 // Legal, independent locations in |a|, SIV 900 for (int i = 0; i < 10; i++) { 901 a[2*i+1] = b[i] + 1; 902 } 903 for (int i = 0; i < 10; i++) { 904 c[i] = a[2*i] + 2; 905 } 906 } 907 908 */ 909 TEST_F(FusionLegalTest, IndependentSIV) { 910 std::string text = R"( 911 OpCapability Shader 912 %1 = OpExtInstImport "GLSL.std.450" 913 OpMemoryModel Logical GLSL450 914 OpEntryPoint Fragment %4 "main" 915 OpExecutionMode %4 OriginUpperLeft 916 OpSource GLSL 440 917 OpName %4 "main" 918 OpName %8 "i" 919 OpName %23 "a" 920 OpName %29 "b" 921 OpName %37 "i" 922 OpName %45 "c" 923 %2 = OpTypeVoid 924 %3 = OpTypeFunction %2 925 %6 = OpTypeInt 32 1 926 %7 = OpTypePointer Function %6 927 %9 = OpConstant %6 0 928 %16 = OpConstant %6 10 929 %17 = OpTypeBool 930 %19 = OpTypeInt 32 0 931 %20 = OpConstant %19 10 932 %21 = OpTypeArray %6 %20 933 %22 = OpTypePointer Function %21 934 %24 = OpConstant %6 2 935 %27 = OpConstant %6 1 936 %4 = OpFunction %2 None %3 937 %5 = OpLabel 938 %8 = OpVariable %7 Function 939 %23 = OpVariable %22 Function 940 %29 = OpVariable %22 Function 941 %37 = OpVariable %7 Function 942 %45 = OpVariable %22 Function 943 OpStore %8 %9 944 OpBranch %10 945 %10 = OpLabel 946 %55 = OpPhi %6 %9 %5 %36 %13 947 OpLoopMerge %12 %13 None 948 OpBranch %14 949 %14 = OpLabel 950 %18 = OpSLessThan %17 %55 %16 951 OpBranchConditional %18 %11 %12 952 %11 = OpLabel 953 %26 = OpIMul %6 %24 %55 954 %28 = OpIAdd %6 %26 %27 955 %31 = OpAccessChain %7 %29 %55 956 %32 = OpLoad %6 %31 957 %33 = OpIAdd %6 %32 %27 958 %34 = OpAccessChain %7 %23 %28 959 OpStore %34 %33 960 OpBranch %13 961 %13 = OpLabel 962 %36 = OpIAdd %6 %55 %27 963 OpStore %8 %36 964 OpBranch %10 965 %12 = OpLabel 966 OpStore %37 %9 967 OpBranch %38 968 %38 = OpLabel 969 %56 = OpPhi %6 %9 %12 %54 %41 970 OpLoopMerge %40 %41 None 971 OpBranch %42 972 %42 = OpLabel 973 %44 = OpSLessThan %17 %56 %16 974 OpBranchConditional %44 %39 %40 975 %39 = OpLabel 976 %48 = OpIMul %6 %24 %56 977 %49 = OpAccessChain %7 %23 %48 978 %50 = OpLoad %6 %49 979 %51 = OpIAdd %6 %50 %24 980 %52 = OpAccessChain %7 %45 %56 981 OpStore %52 %51 982 OpBranch %41 983 %41 = OpLabel 984 %54 = OpIAdd %6 %56 %27 985 OpStore %37 %54 986 OpBranch %38 987 %40 = OpLabel 988 OpReturn 989 OpFunctionEnd 990 )"; 991 992 std::unique_ptr<IRContext> context = 993 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 994 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 995 Module* module = context->module(); 996 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 997 << text << std::endl; 998 Function& f = *module->begin(); 999 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 1000 EXPECT_EQ(ld.NumLoops(), 2u); 1001 1002 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 1003 1004 LoopFusion fusion(context.get(), loops[0], loops[1]); 1005 1006 EXPECT_TRUE(fusion.AreCompatible()); 1007 EXPECT_TRUE(fusion.IsLegal()); 1008 1009 fusion.Fuse(); 1010 1011 std::string checks = R"( 1012 CHECK: [[PHI:%\w+]] = OpPhi 1013 CHECK-NEXT: OpLoopMerge 1014 CHECK: [[I_2:%\w+]] = OpIMul {{%\w+}} {{%\w+}} [[PHI]] 1015 CHECK-NEXT: [[I_2_1:%\w+]] = OpIAdd {{%\w+}} [[I_2]] {{%\w+}} 1016 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1017 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 1018 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_2_1]] 1019 CHECK-NEXT: OpStore [[STORE_0]] 1020 CHECK-NOT: OpPhi 1021 CHECK: [[I_2:%\w+]] = OpIMul {{%\w+}} {{%\w+}} [[PHI]] 1022 CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_2]] 1023 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 1024 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1025 CHECK-NEXT: OpStore [[STORE_1]] 1026 )"; 1027 1028 Match(checks, context.get()); 1029 auto& ld_final = *context->GetLoopDescriptor(&f); 1030 EXPECT_EQ(ld_final.NumLoops(), 1u); 1031 } 1032 1033 /* 1034 Generated from the following GLSL + --eliminate-local-multi-store 1035 1036 #version 440 core 1037 void main() { 1038 int[10] a; 1039 int[10] b; 1040 int[10] c; 1041 // Legal, independent locations in |a|, ZIV 1042 for (int i = 0; i < 10; i++) { 1043 a[1] = b[i] + 1; 1044 } 1045 for (int i = 0; i < 10; i++) { 1046 c[i] = a[9] + 2; 1047 } 1048 } 1049 1050 */ 1051 TEST_F(FusionLegalTest, IndependentZIV) { 1052 std::string text = R"( 1053 OpCapability Shader 1054 %1 = OpExtInstImport "GLSL.std.450" 1055 OpMemoryModel Logical GLSL450 1056 OpEntryPoint Fragment %4 "main" 1057 OpExecutionMode %4 OriginUpperLeft 1058 OpSource GLSL 440 1059 OpName %4 "main" 1060 OpName %8 "i" 1061 OpName %23 "a" 1062 OpName %25 "b" 1063 OpName %33 "i" 1064 OpName %41 "c" 1065 %2 = OpTypeVoid 1066 %3 = OpTypeFunction %2 1067 %6 = OpTypeInt 32 1 1068 %7 = OpTypePointer Function %6 1069 %9 = OpConstant %6 0 1070 %16 = OpConstant %6 10 1071 %17 = OpTypeBool 1072 %19 = OpTypeInt 32 0 1073 %20 = OpConstant %19 10 1074 %21 = OpTypeArray %6 %20 1075 %22 = OpTypePointer Function %21 1076 %24 = OpConstant %6 1 1077 %43 = OpConstant %6 9 1078 %46 = OpConstant %6 2 1079 %4 = OpFunction %2 None %3 1080 %5 = OpLabel 1081 %8 = OpVariable %7 Function 1082 %23 = OpVariable %22 Function 1083 %25 = OpVariable %22 Function 1084 %33 = OpVariable %7 Function 1085 %41 = OpVariable %22 Function 1086 OpStore %8 %9 1087 OpBranch %10 1088 %10 = OpLabel 1089 %51 = OpPhi %6 %9 %5 %32 %13 1090 OpLoopMerge %12 %13 None 1091 OpBranch %14 1092 %14 = OpLabel 1093 %18 = OpSLessThan %17 %51 %16 1094 OpBranchConditional %18 %11 %12 1095 %11 = OpLabel 1096 %27 = OpAccessChain %7 %25 %51 1097 %28 = OpLoad %6 %27 1098 %29 = OpIAdd %6 %28 %24 1099 %30 = OpAccessChain %7 %23 %24 1100 OpStore %30 %29 1101 OpBranch %13 1102 %13 = OpLabel 1103 %32 = OpIAdd %6 %51 %24 1104 OpStore %8 %32 1105 OpBranch %10 1106 %12 = OpLabel 1107 OpStore %33 %9 1108 OpBranch %34 1109 %34 = OpLabel 1110 %52 = OpPhi %6 %9 %12 %50 %37 1111 OpLoopMerge %36 %37 None 1112 OpBranch %38 1113 %38 = OpLabel 1114 %40 = OpSLessThan %17 %52 %16 1115 OpBranchConditional %40 %35 %36 1116 %35 = OpLabel 1117 %44 = OpAccessChain %7 %23 %43 1118 %45 = OpLoad %6 %44 1119 %47 = OpIAdd %6 %45 %46 1120 %48 = OpAccessChain %7 %41 %52 1121 OpStore %48 %47 1122 OpBranch %37 1123 %37 = OpLabel 1124 %50 = OpIAdd %6 %52 %24 1125 OpStore %33 %50 1126 OpBranch %34 1127 %36 = OpLabel 1128 OpReturn 1129 OpFunctionEnd 1130 )"; 1131 1132 std::unique_ptr<IRContext> context = 1133 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1134 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1135 Module* module = context->module(); 1136 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 1137 << text << std::endl; 1138 Function& f = *module->begin(); 1139 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 1140 EXPECT_EQ(ld.NumLoops(), 2u); 1141 1142 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 1143 1144 LoopFusion fusion(context.get(), loops[0], loops[1]); 1145 1146 EXPECT_TRUE(fusion.AreCompatible()); 1147 EXPECT_TRUE(fusion.IsLegal()); 1148 1149 fusion.Fuse(); 1150 1151 std::string checks = R"( 1152 CHECK: [[PHI:%\w+]] = OpPhi 1153 CHECK-NEXT: OpLoopMerge 1154 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1155 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 1156 CHECK-NOT: OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1157 CHECK: OpStore 1158 CHECK-NOT: OpPhi 1159 CHECK-NOT: OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1160 CHECK: OpLoad 1161 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1162 CHECK-NEXT: OpStore [[STORE_1]] 1163 )"; 1164 1165 Match(checks, context.get()); 1166 auto& ld_final = *context->GetLoopDescriptor(&f); 1167 EXPECT_EQ(ld_final.NumLoops(), 1u); 1168 } 1169 1170 /* 1171 Generated from the following GLSL + --eliminate-local-multi-store 1172 1173 #version 440 core 1174 void main() { 1175 int[20] a; 1176 int[10] b; 1177 int[10] c; 1178 // Legal, non-overlapping sections in |a| 1179 for (int i = 0; i < 10; i++) { 1180 a[i] = b[i] + 1; 1181 } 1182 for (int i = 0; i < 10; i++) { 1183 c[i] = a[i+10] + 2; 1184 } 1185 } 1186 1187 */ 1188 TEST_F(FusionLegalTest, NonOverlappingAccesses) { 1189 std::string text = R"( 1190 OpCapability Shader 1191 %1 = OpExtInstImport "GLSL.std.450" 1192 OpMemoryModel Logical GLSL450 1193 OpEntryPoint Fragment %4 "main" 1194 OpExecutionMode %4 OriginUpperLeft 1195 OpSource GLSL 440 1196 OpName %4 "main" 1197 OpName %8 "i" 1198 OpName %23 "a" 1199 OpName %28 "b" 1200 OpName %37 "i" 1201 OpName %45 "c" 1202 %2 = OpTypeVoid 1203 %3 = OpTypeFunction %2 1204 %6 = OpTypeInt 32 1 1205 %7 = OpTypePointer Function %6 1206 %9 = OpConstant %6 0 1207 %16 = OpConstant %6 10 1208 %17 = OpTypeBool 1209 %19 = OpTypeInt 32 0 1210 %20 = OpConstant %19 20 1211 %21 = OpTypeArray %6 %20 1212 %22 = OpTypePointer Function %21 1213 %25 = OpConstant %19 10 1214 %26 = OpTypeArray %6 %25 1215 %27 = OpTypePointer Function %26 1216 %32 = OpConstant %6 1 1217 %51 = OpConstant %6 2 1218 %4 = OpFunction %2 None %3 1219 %5 = OpLabel 1220 %8 = OpVariable %7 Function 1221 %23 = OpVariable %22 Function 1222 %28 = OpVariable %27 Function 1223 %37 = OpVariable %7 Function 1224 %45 = OpVariable %27 Function 1225 OpStore %8 %9 1226 OpBranch %10 1227 %10 = OpLabel 1228 %56 = OpPhi %6 %9 %5 %36 %13 1229 OpLoopMerge %12 %13 None 1230 OpBranch %14 1231 %14 = OpLabel 1232 %18 = OpSLessThan %17 %56 %16 1233 OpBranchConditional %18 %11 %12 1234 %11 = OpLabel 1235 %30 = OpAccessChain %7 %28 %56 1236 %31 = OpLoad %6 %30 1237 %33 = OpIAdd %6 %31 %32 1238 %34 = OpAccessChain %7 %23 %56 1239 OpStore %34 %33 1240 OpBranch %13 1241 %13 = OpLabel 1242 %36 = OpIAdd %6 %56 %32 1243 OpStore %8 %36 1244 OpBranch %10 1245 %12 = OpLabel 1246 OpStore %37 %9 1247 OpBranch %38 1248 %38 = OpLabel 1249 %57 = OpPhi %6 %9 %12 %55 %41 1250 OpLoopMerge %40 %41 None 1251 OpBranch %42 1252 %42 = OpLabel 1253 %44 = OpSLessThan %17 %57 %16 1254 OpBranchConditional %44 %39 %40 1255 %39 = OpLabel 1256 %48 = OpIAdd %6 %57 %16 1257 %49 = OpAccessChain %7 %23 %48 1258 %50 = OpLoad %6 %49 1259 %52 = OpIAdd %6 %50 %51 1260 %53 = OpAccessChain %7 %45 %57 1261 OpStore %53 %52 1262 OpBranch %41 1263 %41 = OpLabel 1264 %55 = OpIAdd %6 %57 %32 1265 OpStore %37 %55 1266 OpBranch %38 1267 %40 = OpLabel 1268 OpReturn 1269 OpFunctionEnd 1270 )"; 1271 1272 std::unique_ptr<IRContext> context = 1273 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1274 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1275 Module* module = context->module(); 1276 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 1277 << text << std::endl; 1278 Function& f = *module->begin(); 1279 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 1280 EXPECT_EQ(ld.NumLoops(), 2u); 1281 1282 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 1283 1284 LoopFusion fusion(context.get(), loops[0], loops[1]); 1285 1286 EXPECT_TRUE(fusion.AreCompatible()); 1287 EXPECT_TRUE(fusion.IsLegal()); 1288 1289 fusion.Fuse(); 1290 1291 std::string checks = R"( 1292 CHECK: [[PHI:%\w+]] = OpPhi 1293 CHECK-NEXT: OpLoopMerge 1294 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1295 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 1296 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1297 CHECK-NOT: OpPhi 1298 CHECK: [[I_10:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} 1299 CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_10]] 1300 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 1301 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1302 CHECK-NEXT: OpStore [[STORE_1]] 1303 )"; 1304 1305 Match(checks, context.get()); 1306 1307 auto& ld_final = *context->GetLoopDescriptor(&f); 1308 EXPECT_EQ(ld_final.NumLoops(), 1u); 1309 } 1310 1311 /* 1312 Generated from the following GLSL + --eliminate-local-multi-store 1313 1314 #version 440 core 1315 void main() { 1316 int[10] a; 1317 int[10] b; 1318 int[10] c; 1319 // Legal, 3 adjacent loops 1320 for (int i = 0; i < 10; i++) { 1321 a[i] = b[i] + 1; 1322 } 1323 for (int i = 0; i < 10; i++) { 1324 c[i] = a[i] + 2; 1325 } 1326 for (int i = 0; i < 10; i++) { 1327 b[i] = c[i] + 10; 1328 } 1329 } 1330 1331 */ 1332 TEST_F(FusionLegalTest, AdjacentLoops) { 1333 std::string text = R"( 1334 OpCapability Shader 1335 %1 = OpExtInstImport "GLSL.std.450" 1336 OpMemoryModel Logical GLSL450 1337 OpEntryPoint Fragment %4 "main" 1338 OpExecutionMode %4 OriginUpperLeft 1339 OpSource GLSL 440 1340 OpName %4 "main" 1341 OpName %8 "i" 1342 OpName %23 "a" 1343 OpName %25 "b" 1344 OpName %34 "i" 1345 OpName %42 "c" 1346 OpName %52 "i" 1347 %2 = OpTypeVoid 1348 %3 = OpTypeFunction %2 1349 %6 = OpTypeInt 32 1 1350 %7 = OpTypePointer Function %6 1351 %9 = OpConstant %6 0 1352 %16 = OpConstant %6 10 1353 %17 = OpTypeBool 1354 %19 = OpTypeInt 32 0 1355 %20 = OpConstant %19 10 1356 %21 = OpTypeArray %6 %20 1357 %22 = OpTypePointer Function %21 1358 %29 = OpConstant %6 1 1359 %47 = OpConstant %6 2 1360 %4 = OpFunction %2 None %3 1361 %5 = OpLabel 1362 %8 = OpVariable %7 Function 1363 %23 = OpVariable %22 Function 1364 %25 = OpVariable %22 Function 1365 %34 = OpVariable %7 Function 1366 %42 = OpVariable %22 Function 1367 %52 = OpVariable %7 Function 1368 OpStore %8 %9 1369 OpBranch %10 1370 %10 = OpLabel 1371 %68 = OpPhi %6 %9 %5 %33 %13 1372 OpLoopMerge %12 %13 None 1373 OpBranch %14 1374 %14 = OpLabel 1375 %18 = OpSLessThan %17 %68 %16 1376 OpBranchConditional %18 %11 %12 1377 %11 = OpLabel 1378 %27 = OpAccessChain %7 %25 %68 1379 %28 = OpLoad %6 %27 1380 %30 = OpIAdd %6 %28 %29 1381 %31 = OpAccessChain %7 %23 %68 1382 OpStore %31 %30 1383 OpBranch %13 1384 %13 = OpLabel 1385 %33 = OpIAdd %6 %68 %29 1386 OpStore %8 %33 1387 OpBranch %10 1388 %12 = OpLabel 1389 OpStore %34 %9 1390 OpBranch %35 1391 %35 = OpLabel 1392 %69 = OpPhi %6 %9 %12 %51 %38 1393 OpLoopMerge %37 %38 None 1394 OpBranch %39 1395 %39 = OpLabel 1396 %41 = OpSLessThan %17 %69 %16 1397 OpBranchConditional %41 %36 %37 1398 %36 = OpLabel 1399 %45 = OpAccessChain %7 %23 %69 1400 %46 = OpLoad %6 %45 1401 %48 = OpIAdd %6 %46 %47 1402 %49 = OpAccessChain %7 %42 %69 1403 OpStore %49 %48 1404 OpBranch %38 1405 %38 = OpLabel 1406 %51 = OpIAdd %6 %69 %29 1407 OpStore %34 %51 1408 OpBranch %35 1409 %37 = OpLabel 1410 OpStore %52 %9 1411 OpBranch %53 1412 %53 = OpLabel 1413 %70 = OpPhi %6 %9 %37 %67 %56 1414 OpLoopMerge %55 %56 None 1415 OpBranch %57 1416 %57 = OpLabel 1417 %59 = OpSLessThan %17 %70 %16 1418 OpBranchConditional %59 %54 %55 1419 %54 = OpLabel 1420 %62 = OpAccessChain %7 %42 %70 1421 %63 = OpLoad %6 %62 1422 %64 = OpIAdd %6 %63 %16 1423 %65 = OpAccessChain %7 %25 %70 1424 OpStore %65 %64 1425 OpBranch %56 1426 %56 = OpLabel 1427 %67 = OpIAdd %6 %70 %29 1428 OpStore %52 %67 1429 OpBranch %53 1430 %55 = OpLabel 1431 OpReturn 1432 OpFunctionEnd 1433 )"; 1434 1435 std::unique_ptr<IRContext> context = 1436 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1437 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1438 Module* module = context->module(); 1439 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 1440 << text << std::endl; 1441 Function& f = *module->begin(); 1442 1443 { 1444 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 1445 EXPECT_EQ(ld.NumLoops(), 3u); 1446 1447 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 1448 1449 LoopFusion fusion(context.get(), loops[1], loops[2]); 1450 1451 EXPECT_TRUE(fusion.AreCompatible()); 1452 EXPECT_TRUE(fusion.IsLegal()); 1453 1454 fusion.Fuse(); 1455 } 1456 1457 std::string checks = R"( 1458 CHECK: [[PHI_0:%\w+]] = OpPhi 1459 CHECK-NEXT: OpLoopMerge 1460 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 1461 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 1462 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 1463 CHECK-NEXT: OpStore [[STORE_0]] 1464 CHECK: [[PHI_1:%\w+]] = OpPhi 1465 CHECK-NEXT: OpLoopMerge 1466 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] 1467 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 1468 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] 1469 CHECK-NEXT: OpStore [[STORE_1]] 1470 CHECK-NOT: OpPhi 1471 CHECK: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] 1472 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] 1473 CHECK: [[STORE_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] 1474 CHECK-NEXT: OpStore [[STORE_2]] 1475 )"; 1476 1477 Match(checks, context.get()); 1478 1479 { 1480 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 1481 EXPECT_EQ(ld.NumLoops(), 2u); 1482 1483 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 1484 1485 LoopFusion fusion(context.get(), loops[0], loops[1]); 1486 1487 EXPECT_TRUE(fusion.AreCompatible()); 1488 EXPECT_TRUE(fusion.IsLegal()); 1489 1490 fusion.Fuse(); 1491 } 1492 1493 std::string checks_ = R"( 1494 CHECK: [[PHI:%\w+]] = OpPhi 1495 CHECK-NEXT: OpLoopMerge 1496 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1497 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 1498 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1499 CHECK-NEXT: OpStore [[STORE_0]] 1500 CHECK-NOT: OpPhi 1501 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1502 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 1503 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1504 CHECK-NEXT: OpStore [[STORE_1]] 1505 CHECK-NOT: OpPhi 1506 CHECK: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1507 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] 1508 CHECK: [[STORE_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 1509 CHECK-NEXT: OpStore [[STORE_2]] 1510 )"; 1511 1512 Match(checks_, context.get()); 1513 1514 auto& ld_final = *context->GetLoopDescriptor(&f); 1515 EXPECT_EQ(ld_final.NumLoops(), 1u); 1516 } 1517 1518 /* 1519 Generated from the following GLSL + --eliminate-local-multi-store 1520 1521 #version 440 core 1522 void main() { 1523 int[10][10] a; 1524 int[10][10] b; 1525 int[10][10] c; 1526 // Legal inner loop fusion 1527 for (int i = 0; i < 10; i++) { 1528 for (int j = 0; j < 10; j++) { 1529 c[i][j] = a[i][j] + 2; 1530 } 1531 for (int j = 0; j < 10; j++) { 1532 b[i][j] = c[i][j] + 10; 1533 } 1534 } 1535 } 1536 1537 */ 1538 TEST_F(FusionLegalTest, InnerLoopFusion) { 1539 std::string text = R"( 1540 OpCapability Shader 1541 %1 = OpExtInstImport "GLSL.std.450" 1542 OpMemoryModel Logical GLSL450 1543 OpEntryPoint Fragment %4 "main" 1544 OpExecutionMode %4 OriginUpperLeft 1545 OpSource GLSL 440 1546 OpName %4 "main" 1547 OpName %8 "i" 1548 OpName %19 "j" 1549 OpName %32 "c" 1550 OpName %35 "a" 1551 OpName %46 "j" 1552 OpName %54 "b" 1553 %2 = OpTypeVoid 1554 %3 = OpTypeFunction %2 1555 %6 = OpTypeInt 32 1 1556 %7 = OpTypePointer Function %6 1557 %9 = OpConstant %6 0 1558 %16 = OpConstant %6 10 1559 %17 = OpTypeBool 1560 %27 = OpTypeInt 32 0 1561 %28 = OpConstant %27 10 1562 %29 = OpTypeArray %6 %28 1563 %30 = OpTypeArray %29 %28 1564 %31 = OpTypePointer Function %30 1565 %40 = OpConstant %6 2 1566 %44 = OpConstant %6 1 1567 %4 = OpFunction %2 None %3 1568 %5 = OpLabel 1569 %8 = OpVariable %7 Function 1570 %19 = OpVariable %7 Function 1571 %32 = OpVariable %31 Function 1572 %35 = OpVariable %31 Function 1573 %46 = OpVariable %7 Function 1574 %54 = OpVariable %31 Function 1575 OpStore %8 %9 1576 OpBranch %10 1577 %10 = OpLabel 1578 %67 = OpPhi %6 %9 %5 %66 %13 1579 OpLoopMerge %12 %13 None 1580 OpBranch %14 1581 %14 = OpLabel 1582 %18 = OpSLessThan %17 %67 %16 1583 OpBranchConditional %18 %11 %12 1584 %11 = OpLabel 1585 OpStore %19 %9 1586 OpBranch %20 1587 %20 = OpLabel 1588 %68 = OpPhi %6 %9 %11 %45 %23 1589 OpLoopMerge %22 %23 None 1590 OpBranch %24 1591 %24 = OpLabel 1592 %26 = OpSLessThan %17 %68 %16 1593 OpBranchConditional %26 %21 %22 1594 %21 = OpLabel 1595 %38 = OpAccessChain %7 %35 %67 %68 1596 %39 = OpLoad %6 %38 1597 %41 = OpIAdd %6 %39 %40 1598 %42 = OpAccessChain %7 %32 %67 %68 1599 OpStore %42 %41 1600 OpBranch %23 1601 %23 = OpLabel 1602 %45 = OpIAdd %6 %68 %44 1603 OpStore %19 %45 1604 OpBranch %20 1605 %22 = OpLabel 1606 OpStore %46 %9 1607 OpBranch %47 1608 %47 = OpLabel 1609 %69 = OpPhi %6 %9 %22 %64 %50 1610 OpLoopMerge %49 %50 None 1611 OpBranch %51 1612 %51 = OpLabel 1613 %53 = OpSLessThan %17 %69 %16 1614 OpBranchConditional %53 %48 %49 1615 %48 = OpLabel 1616 %59 = OpAccessChain %7 %32 %67 %69 1617 %60 = OpLoad %6 %59 1618 %61 = OpIAdd %6 %60 %16 1619 %62 = OpAccessChain %7 %54 %67 %69 1620 OpStore %62 %61 1621 OpBranch %50 1622 %50 = OpLabel 1623 %64 = OpIAdd %6 %69 %44 1624 OpStore %46 %64 1625 OpBranch %47 1626 %49 = OpLabel 1627 OpBranch %13 1628 %13 = OpLabel 1629 %66 = OpIAdd %6 %67 %44 1630 OpStore %8 %66 1631 OpBranch %10 1632 %12 = OpLabel 1633 OpReturn 1634 OpFunctionEnd 1635 )"; 1636 1637 std::unique_ptr<IRContext> context = 1638 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1639 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1640 Module* module = context->module(); 1641 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 1642 << text << std::endl; 1643 Function& f = *module->begin(); 1644 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 1645 EXPECT_EQ(ld.NumLoops(), 3u); 1646 1647 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 1648 1649 auto loop_0 = loops[0]; 1650 auto loop_1 = loops[1]; 1651 auto loop_2 = loops[2]; 1652 1653 { 1654 LoopFusion fusion(context.get(), loop_0, loop_1); 1655 EXPECT_FALSE(fusion.AreCompatible()); 1656 } 1657 1658 { 1659 LoopFusion fusion(context.get(), loop_0, loop_2); 1660 EXPECT_FALSE(fusion.AreCompatible()); 1661 } 1662 1663 { 1664 LoopFusion fusion(context.get(), loop_1, loop_2); 1665 EXPECT_TRUE(fusion.AreCompatible()); 1666 EXPECT_TRUE(fusion.IsLegal()); 1667 1668 fusion.Fuse(); 1669 } 1670 1671 std::string checks = R"( 1672 CHECK: [[PHI_0:%\w+]] = OpPhi 1673 CHECK-NEXT: OpLoopMerge 1674 CHECK: [[PHI_1:%\w+]] = OpPhi 1675 CHECK-NEXT: OpLoopMerge 1676 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1677 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 1678 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1679 CHECK-NEXT: OpStore [[STORE_0]] 1680 CHECK-NOT: OpPhi 1681 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1682 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 1683 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1684 CHECK-NEXT: OpStore [[STORE_1]] 1685 )"; 1686 1687 Match(checks, context.get()); 1688 1689 auto& ld_final = *context->GetLoopDescriptor(&f); 1690 EXPECT_EQ(ld_final.NumLoops(), 2u); 1691 } 1692 1693 /* 1694 Generated from the following GLSL + --eliminate-local-multi-store 1695 1696 // 12 1697 #version 440 core 1698 void main() { 1699 int[10][10] a; 1700 int[10][10] b; 1701 int[10][10] c; 1702 // Legal both 1703 for (int i = 0; i < 10; i++) { 1704 for (int j = 0; j < 10; j++) { 1705 c[i][j] = a[i][j] + 2; 1706 } 1707 } 1708 for (int i = 0; i < 10; i++) { 1709 for (int j = 0; j < 10; j++) { 1710 b[i][j] = c[i][j] + 10; 1711 } 1712 } 1713 } 1714 1715 */ 1716 TEST_F(FusionLegalTest, OuterAndInnerLoop) { 1717 std::string text = R"( 1718 OpCapability Shader 1719 %1 = OpExtInstImport "GLSL.std.450" 1720 OpMemoryModel Logical GLSL450 1721 OpEntryPoint Fragment %4 "main" 1722 OpExecutionMode %4 OriginUpperLeft 1723 OpSource GLSL 440 1724 OpName %4 "main" 1725 OpName %8 "i" 1726 OpName %19 "j" 1727 OpName %32 "c" 1728 OpName %35 "a" 1729 OpName %48 "i" 1730 OpName %56 "j" 1731 OpName %64 "b" 1732 %2 = OpTypeVoid 1733 %3 = OpTypeFunction %2 1734 %6 = OpTypeInt 32 1 1735 %7 = OpTypePointer Function %6 1736 %9 = OpConstant %6 0 1737 %16 = OpConstant %6 10 1738 %17 = OpTypeBool 1739 %27 = OpTypeInt 32 0 1740 %28 = OpConstant %27 10 1741 %29 = OpTypeArray %6 %28 1742 %30 = OpTypeArray %29 %28 1743 %31 = OpTypePointer Function %30 1744 %40 = OpConstant %6 2 1745 %44 = OpConstant %6 1 1746 %4 = OpFunction %2 None %3 1747 %5 = OpLabel 1748 %8 = OpVariable %7 Function 1749 %19 = OpVariable %7 Function 1750 %32 = OpVariable %31 Function 1751 %35 = OpVariable %31 Function 1752 %48 = OpVariable %7 Function 1753 %56 = OpVariable %7 Function 1754 %64 = OpVariable %31 Function 1755 OpStore %8 %9 1756 OpBranch %10 1757 %10 = OpLabel 1758 %77 = OpPhi %6 %9 %5 %47 %13 1759 OpLoopMerge %12 %13 None 1760 OpBranch %14 1761 %14 = OpLabel 1762 %18 = OpSLessThan %17 %77 %16 1763 OpBranchConditional %18 %11 %12 1764 %11 = OpLabel 1765 OpStore %19 %9 1766 OpBranch %20 1767 %20 = OpLabel 1768 %81 = OpPhi %6 %9 %11 %45 %23 1769 OpLoopMerge %22 %23 None 1770 OpBranch %24 1771 %24 = OpLabel 1772 %26 = OpSLessThan %17 %81 %16 1773 OpBranchConditional %26 %21 %22 1774 %21 = OpLabel 1775 %38 = OpAccessChain %7 %35 %77 %81 1776 %39 = OpLoad %6 %38 1777 %41 = OpIAdd %6 %39 %40 1778 %42 = OpAccessChain %7 %32 %77 %81 1779 OpStore %42 %41 1780 OpBranch %23 1781 %23 = OpLabel 1782 %45 = OpIAdd %6 %81 %44 1783 OpStore %19 %45 1784 OpBranch %20 1785 %22 = OpLabel 1786 OpBranch %13 1787 %13 = OpLabel 1788 %47 = OpIAdd %6 %77 %44 1789 OpStore %8 %47 1790 OpBranch %10 1791 %12 = OpLabel 1792 OpStore %48 %9 1793 OpBranch %49 1794 %49 = OpLabel 1795 %78 = OpPhi %6 %9 %12 %76 %52 1796 OpLoopMerge %51 %52 None 1797 OpBranch %53 1798 %53 = OpLabel 1799 %55 = OpSLessThan %17 %78 %16 1800 OpBranchConditional %55 %50 %51 1801 %50 = OpLabel 1802 OpStore %56 %9 1803 OpBranch %57 1804 %57 = OpLabel 1805 %79 = OpPhi %6 %9 %50 %74 %60 1806 OpLoopMerge %59 %60 None 1807 OpBranch %61 1808 %61 = OpLabel 1809 %63 = OpSLessThan %17 %79 %16 1810 OpBranchConditional %63 %58 %59 1811 %58 = OpLabel 1812 %69 = OpAccessChain %7 %32 %78 %79 1813 %70 = OpLoad %6 %69 1814 %71 = OpIAdd %6 %70 %16 1815 %72 = OpAccessChain %7 %64 %78 %79 1816 OpStore %72 %71 1817 OpBranch %60 1818 %60 = OpLabel 1819 %74 = OpIAdd %6 %79 %44 1820 OpStore %56 %74 1821 OpBranch %57 1822 %59 = OpLabel 1823 OpBranch %52 1824 %52 = OpLabel 1825 %76 = OpIAdd %6 %78 %44 1826 OpStore %48 %76 1827 OpBranch %49 1828 %51 = OpLabel 1829 OpReturn 1830 OpFunctionEnd 1831 )"; 1832 1833 std::unique_ptr<IRContext> context = 1834 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 1835 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 1836 Module* module = context->module(); 1837 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 1838 << text << std::endl; 1839 Function& f = *module->begin(); 1840 1841 { 1842 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 1843 EXPECT_EQ(ld.NumLoops(), 4u); 1844 1845 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 1846 1847 auto loop_0 = loops[0]; 1848 auto loop_1 = loops[1]; 1849 auto loop_2 = loops[2]; 1850 auto loop_3 = loops[3]; 1851 1852 { 1853 LoopFusion fusion(context.get(), loop_0, loop_1); 1854 EXPECT_FALSE(fusion.AreCompatible()); 1855 } 1856 1857 { 1858 LoopFusion fusion(context.get(), loop_1, loop_2); 1859 EXPECT_FALSE(fusion.AreCompatible()); 1860 } 1861 1862 { 1863 LoopFusion fusion(context.get(), loop_2, loop_3); 1864 EXPECT_FALSE(fusion.AreCompatible()); 1865 } 1866 1867 { 1868 LoopFusion fusion(context.get(), loop_1, loop_3); 1869 EXPECT_FALSE(fusion.AreCompatible()); 1870 } 1871 1872 { 1873 LoopFusion fusion(context.get(), loop_0, loop_2); 1874 EXPECT_TRUE(fusion.AreCompatible()); 1875 EXPECT_TRUE(fusion.IsLegal()); 1876 fusion.Fuse(); 1877 } 1878 1879 std::string checks = R"( 1880 CHECK: [[PHI_0:%\w+]] = OpPhi 1881 CHECK-NEXT: OpLoopMerge 1882 CHECK: [[PHI_1:%\w+]] = OpPhi 1883 CHECK-NEXT: OpLoopMerge 1884 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1885 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 1886 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1887 CHECK-NEXT: OpStore [[STORE_0]] 1888 CHECK: [[PHI_2:%\w+]] = OpPhi 1889 CHECK-NEXT: OpLoopMerge 1890 CHECK-NOT: OpPhi 1891 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] 1892 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 1893 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] 1894 CHECK-NEXT: OpStore [[STORE_1]] 1895 )"; 1896 1897 Match(checks, context.get()); 1898 } 1899 1900 { 1901 auto& ld = *context->GetLoopDescriptor(&f); 1902 EXPECT_EQ(ld.NumLoops(), 3u); 1903 1904 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 1905 auto loop_0 = loops[0]; 1906 auto loop_1 = loops[1]; 1907 auto loop_2 = loops[2]; 1908 1909 { 1910 LoopFusion fusion(context.get(), loop_0, loop_1); 1911 EXPECT_FALSE(fusion.AreCompatible()); 1912 } 1913 1914 { 1915 LoopFusion fusion(context.get(), loop_0, loop_2); 1916 EXPECT_FALSE(fusion.AreCompatible()); 1917 } 1918 1919 { 1920 LoopFusion fusion(context.get(), loop_1, loop_2); 1921 EXPECT_TRUE(fusion.AreCompatible()); 1922 EXPECT_TRUE(fusion.IsLegal()); 1923 fusion.Fuse(); 1924 } 1925 1926 std::string checks = R"( 1927 CHECK: [[PHI_0:%\w+]] = OpPhi 1928 CHECK-NEXT: OpLoopMerge 1929 CHECK: [[PHI_1:%\w+]] = OpPhi 1930 CHECK-NEXT: OpLoopMerge 1931 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1932 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 1933 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1934 CHECK-NEXT: OpStore [[STORE_0]] 1935 CHECK-NOT: OpPhi 1936 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1937 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 1938 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 1939 CHECK-NEXT: OpStore [[STORE_1]] 1940 )"; 1941 1942 Match(checks, context.get()); 1943 } 1944 1945 { 1946 auto& ld = *context->GetLoopDescriptor(&f); 1947 EXPECT_EQ(ld.NumLoops(), 2u); 1948 } 1949 } 1950 1951 /* 1952 Generated from the following GLSL + --eliminate-local-multi-store 1953 1954 #version 440 core 1955 void main() { 1956 int[10][10] a; 1957 int[10][10] b; 1958 int[10][10] c; 1959 // Legal both, more complex 1960 for (int i = 0; i < 10; i++) { 1961 for (int j = 0; j < 10; j++) { 1962 if (i % 2 == 0 && j % 2 == 0) { 1963 c[i][j] = a[i][j] + 2; 1964 } 1965 } 1966 } 1967 for (int i = 0; i < 10; i++) { 1968 for (int j = 0; j < 10; j++) { 1969 b[i][j] = c[i][j] + 10; 1970 } 1971 } 1972 } 1973 1974 */ 1975 TEST_F(FusionLegalTest, OuterAndInnerLoopMoreComplex) { 1976 std::string text = R"( 1977 OpCapability Shader 1978 %1 = OpExtInstImport "GLSL.std.450" 1979 OpMemoryModel Logical GLSL450 1980 OpEntryPoint Fragment %4 "main" 1981 OpExecutionMode %4 OriginUpperLeft 1982 OpSource GLSL 440 1983 OpName %4 "main" 1984 OpName %8 "i" 1985 OpName %19 "j" 1986 OpName %44 "c" 1987 OpName %47 "a" 1988 OpName %59 "i" 1989 OpName %67 "j" 1990 OpName %75 "b" 1991 %2 = OpTypeVoid 1992 %3 = OpTypeFunction %2 1993 %6 = OpTypeInt 32 1 1994 %7 = OpTypePointer Function %6 1995 %9 = OpConstant %6 0 1996 %16 = OpConstant %6 10 1997 %17 = OpTypeBool 1998 %28 = OpConstant %6 2 1999 %39 = OpTypeInt 32 0 2000 %40 = OpConstant %39 10 2001 %41 = OpTypeArray %6 %40 2002 %42 = OpTypeArray %41 %40 2003 %43 = OpTypePointer Function %42 2004 %55 = OpConstant %6 1 2005 %4 = OpFunction %2 None %3 2006 %5 = OpLabel 2007 %8 = OpVariable %7 Function 2008 %19 = OpVariable %7 Function 2009 %44 = OpVariable %43 Function 2010 %47 = OpVariable %43 Function 2011 %59 = OpVariable %7 Function 2012 %67 = OpVariable %7 Function 2013 %75 = OpVariable %43 Function 2014 OpStore %8 %9 2015 OpBranch %10 2016 %10 = OpLabel 2017 %88 = OpPhi %6 %9 %5 %58 %13 2018 OpLoopMerge %12 %13 None 2019 OpBranch %14 2020 %14 = OpLabel 2021 %18 = OpSLessThan %17 %88 %16 2022 OpBranchConditional %18 %11 %12 2023 %11 = OpLabel 2024 OpStore %19 %9 2025 OpBranch %20 2026 %20 = OpLabel 2027 %92 = OpPhi %6 %9 %11 %56 %23 2028 OpLoopMerge %22 %23 None 2029 OpBranch %24 2030 %24 = OpLabel 2031 %26 = OpSLessThan %17 %92 %16 2032 OpBranchConditional %26 %21 %22 2033 %21 = OpLabel 2034 %29 = OpSMod %6 %88 %28 2035 %30 = OpIEqual %17 %29 %9 2036 OpSelectionMerge %32 None 2037 OpBranchConditional %30 %31 %32 2038 %31 = OpLabel 2039 %34 = OpSMod %6 %92 %28 2040 %35 = OpIEqual %17 %34 %9 2041 OpBranch %32 2042 %32 = OpLabel 2043 %36 = OpPhi %17 %30 %21 %35 %31 2044 OpSelectionMerge %38 None 2045 OpBranchConditional %36 %37 %38 2046 %37 = OpLabel 2047 %50 = OpAccessChain %7 %47 %88 %92 2048 %51 = OpLoad %6 %50 2049 %52 = OpIAdd %6 %51 %28 2050 %53 = OpAccessChain %7 %44 %88 %92 2051 OpStore %53 %52 2052 OpBranch %38 2053 %38 = OpLabel 2054 OpBranch %23 2055 %23 = OpLabel 2056 %56 = OpIAdd %6 %92 %55 2057 OpStore %19 %56 2058 OpBranch %20 2059 %22 = OpLabel 2060 OpBranch %13 2061 %13 = OpLabel 2062 %58 = OpIAdd %6 %88 %55 2063 OpStore %8 %58 2064 OpBranch %10 2065 %12 = OpLabel 2066 OpStore %59 %9 2067 OpBranch %60 2068 %60 = OpLabel 2069 %89 = OpPhi %6 %9 %12 %87 %63 2070 OpLoopMerge %62 %63 None 2071 OpBranch %64 2072 %64 = OpLabel 2073 %66 = OpSLessThan %17 %89 %16 2074 OpBranchConditional %66 %61 %62 2075 %61 = OpLabel 2076 OpStore %67 %9 2077 OpBranch %68 2078 %68 = OpLabel 2079 %90 = OpPhi %6 %9 %61 %85 %71 2080 OpLoopMerge %70 %71 None 2081 OpBranch %72 2082 %72 = OpLabel 2083 %74 = OpSLessThan %17 %90 %16 2084 OpBranchConditional %74 %69 %70 2085 %69 = OpLabel 2086 %80 = OpAccessChain %7 %44 %89 %90 2087 %81 = OpLoad %6 %80 2088 %82 = OpIAdd %6 %81 %16 2089 %83 = OpAccessChain %7 %75 %89 %90 2090 OpStore %83 %82 2091 OpBranch %71 2092 %71 = OpLabel 2093 %85 = OpIAdd %6 %90 %55 2094 OpStore %67 %85 2095 OpBranch %68 2096 %70 = OpLabel 2097 OpBranch %63 2098 %63 = OpLabel 2099 %87 = OpIAdd %6 %89 %55 2100 OpStore %59 %87 2101 OpBranch %60 2102 %62 = OpLabel 2103 OpReturn 2104 OpFunctionEnd 2105 )"; 2106 2107 std::unique_ptr<IRContext> context = 2108 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 2109 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2110 Module* module = context->module(); 2111 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 2112 << text << std::endl; 2113 Function& f = *module->begin(); 2114 2115 { 2116 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2117 EXPECT_EQ(ld.NumLoops(), 4u); 2118 2119 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 2120 2121 auto loop_0 = loops[0]; 2122 auto loop_1 = loops[1]; 2123 auto loop_2 = loops[2]; 2124 auto loop_3 = loops[3]; 2125 2126 { 2127 LoopFusion fusion(context.get(), loop_0, loop_1); 2128 EXPECT_FALSE(fusion.AreCompatible()); 2129 } 2130 2131 { 2132 LoopFusion fusion(context.get(), loop_1, loop_2); 2133 EXPECT_FALSE(fusion.AreCompatible()); 2134 } 2135 2136 { 2137 LoopFusion fusion(context.get(), loop_2, loop_3); 2138 EXPECT_FALSE(fusion.AreCompatible()); 2139 } 2140 2141 { 2142 LoopFusion fusion(context.get(), loop_1, loop_3); 2143 EXPECT_FALSE(fusion.AreCompatible()); 2144 } 2145 2146 { 2147 LoopFusion fusion(context.get(), loop_0, loop_2); 2148 EXPECT_TRUE(fusion.AreCompatible()); 2149 EXPECT_TRUE(fusion.IsLegal()); 2150 fusion.Fuse(); 2151 } 2152 2153 std::string checks = R"( 2154 CHECK: [[PHI_0:%\w+]] = OpPhi 2155 CHECK-NEXT: OpLoopMerge 2156 CHECK: [[PHI_1:%\w+]] = OpPhi 2157 CHECK-NEXT: OpLoopMerge 2158 CHECK: OpPhi 2159 CHECK-NEXT: OpSelectionMerge 2160 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 2161 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 2162 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 2163 CHECK-NEXT: OpStore [[STORE_0]] 2164 CHECK: [[PHI_2:%\w+]] = OpPhi 2165 CHECK-NEXT: OpLoopMerge 2166 CHECK-NOT: OpPhi 2167 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] 2168 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 2169 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] 2170 CHECK-NEXT: OpStore [[STORE_1]] 2171 )"; 2172 2173 Match(checks, context.get()); 2174 } 2175 2176 { 2177 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2178 EXPECT_EQ(ld.NumLoops(), 3u); 2179 2180 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 2181 2182 auto loop_0 = loops[0]; 2183 auto loop_1 = loops[1]; 2184 auto loop_2 = loops[2]; 2185 2186 { 2187 LoopFusion fusion(context.get(), loop_0, loop_1); 2188 EXPECT_FALSE(fusion.AreCompatible()); 2189 } 2190 2191 { 2192 LoopFusion fusion(context.get(), loop_0, loop_2); 2193 EXPECT_FALSE(fusion.AreCompatible()); 2194 } 2195 2196 { 2197 LoopFusion fusion(context.get(), loop_1, loop_2); 2198 EXPECT_TRUE(fusion.AreCompatible()); 2199 EXPECT_TRUE(fusion.IsLegal()); 2200 fusion.Fuse(); 2201 } 2202 2203 std::string checks = R"( 2204 CHECK: [[PHI_0:%\w+]] = OpPhi 2205 CHECK-NEXT: OpLoopMerge 2206 CHECK: [[PHI_1:%\w+]] = OpPhi 2207 CHECK-NEXT: OpLoopMerge 2208 CHECK: OpPhi 2209 CHECK-NEXT: OpSelectionMerge 2210 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 2211 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 2212 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 2213 CHECK-NEXT: OpStore [[STORE_0]] 2214 CHECK-NOT: OpPhi 2215 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 2216 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 2217 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 2218 CHECK-NEXT: OpStore [[STORE_1]] 2219 )"; 2220 2221 Match(checks, context.get()); 2222 } 2223 2224 { 2225 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2226 EXPECT_EQ(ld.NumLoops(), 2u); 2227 } 2228 } 2229 2230 /* 2231 Generated from the following GLSL + --eliminate-local-multi-store 2232 2233 #version 440 core 2234 void main() { 2235 int[10][10] a; 2236 int[10][10] b; 2237 int[10][10] c; 2238 // Outer would have been illegal to fuse, but since written 2239 // like this, inner loop fusion is legal. 2240 for (int i = 0; i < 10; i++) { 2241 for (int j = 0; j < 10; j++) { 2242 c[i][j] = a[i][j] + 2; 2243 } 2244 for (int j = 0; j < 10; j++) { 2245 b[i][j] = c[i+1][j] + 10; 2246 } 2247 } 2248 } 2249 2250 */ 2251 TEST_F(FusionLegalTest, InnerWithExistingDependenceOnOuter) { 2252 std::string text = R"( 2253 OpCapability Shader 2254 %1 = OpExtInstImport "GLSL.std.450" 2255 OpMemoryModel Logical GLSL450 2256 OpEntryPoint Fragment %4 "main" 2257 OpExecutionMode %4 OriginUpperLeft 2258 OpSource GLSL 440 2259 OpName %4 "main" 2260 OpName %8 "i" 2261 OpName %19 "j" 2262 OpName %32 "c" 2263 OpName %35 "a" 2264 OpName %46 "j" 2265 OpName %54 "b" 2266 %2 = OpTypeVoid 2267 %3 = OpTypeFunction %2 2268 %6 = OpTypeInt 32 1 2269 %7 = OpTypePointer Function %6 2270 %9 = OpConstant %6 0 2271 %16 = OpConstant %6 10 2272 %17 = OpTypeBool 2273 %27 = OpTypeInt 32 0 2274 %28 = OpConstant %27 10 2275 %29 = OpTypeArray %6 %28 2276 %30 = OpTypeArray %29 %28 2277 %31 = OpTypePointer Function %30 2278 %40 = OpConstant %6 2 2279 %44 = OpConstant %6 1 2280 %4 = OpFunction %2 None %3 2281 %5 = OpLabel 2282 %8 = OpVariable %7 Function 2283 %19 = OpVariable %7 Function 2284 %32 = OpVariable %31 Function 2285 %35 = OpVariable %31 Function 2286 %46 = OpVariable %7 Function 2287 %54 = OpVariable %31 Function 2288 OpStore %8 %9 2289 OpBranch %10 2290 %10 = OpLabel 2291 %68 = OpPhi %6 %9 %5 %67 %13 2292 OpLoopMerge %12 %13 None 2293 OpBranch %14 2294 %14 = OpLabel 2295 %18 = OpSLessThan %17 %68 %16 2296 OpBranchConditional %18 %11 %12 2297 %11 = OpLabel 2298 OpStore %19 %9 2299 OpBranch %20 2300 %20 = OpLabel 2301 %69 = OpPhi %6 %9 %11 %45 %23 2302 OpLoopMerge %22 %23 None 2303 OpBranch %24 2304 %24 = OpLabel 2305 %26 = OpSLessThan %17 %69 %16 2306 OpBranchConditional %26 %21 %22 2307 %21 = OpLabel 2308 %38 = OpAccessChain %7 %35 %68 %69 2309 %39 = OpLoad %6 %38 2310 %41 = OpIAdd %6 %39 %40 2311 %42 = OpAccessChain %7 %32 %68 %69 2312 OpStore %42 %41 2313 OpBranch %23 2314 %23 = OpLabel 2315 %45 = OpIAdd %6 %69 %44 2316 OpStore %19 %45 2317 OpBranch %20 2318 %22 = OpLabel 2319 OpStore %46 %9 2320 OpBranch %47 2321 %47 = OpLabel 2322 %70 = OpPhi %6 %9 %22 %65 %50 2323 OpLoopMerge %49 %50 None 2324 OpBranch %51 2325 %51 = OpLabel 2326 %53 = OpSLessThan %17 %70 %16 2327 OpBranchConditional %53 %48 %49 2328 %48 = OpLabel 2329 %58 = OpIAdd %6 %68 %44 2330 %60 = OpAccessChain %7 %32 %58 %70 2331 %61 = OpLoad %6 %60 2332 %62 = OpIAdd %6 %61 %16 2333 %63 = OpAccessChain %7 %54 %68 %70 2334 OpStore %63 %62 2335 OpBranch %50 2336 %50 = OpLabel 2337 %65 = OpIAdd %6 %70 %44 2338 OpStore %46 %65 2339 OpBranch %47 2340 %49 = OpLabel 2341 OpBranch %13 2342 %13 = OpLabel 2343 %67 = OpIAdd %6 %68 %44 2344 OpStore %8 %67 2345 OpBranch %10 2346 %12 = OpLabel 2347 OpReturn 2348 OpFunctionEnd 2349 )"; 2350 2351 std::unique_ptr<IRContext> context = 2352 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 2353 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2354 Module* module = context->module(); 2355 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 2356 << text << std::endl; 2357 Function& f = *module->begin(); 2358 2359 { 2360 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2361 EXPECT_EQ(ld.NumLoops(), 3u); 2362 2363 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 2364 2365 auto loop_0 = loops[0]; 2366 auto loop_1 = loops[1]; 2367 auto loop_2 = loops[2]; 2368 2369 { 2370 LoopFusion fusion(context.get(), loop_0, loop_1); 2371 EXPECT_FALSE(fusion.AreCompatible()); 2372 } 2373 2374 { 2375 LoopFusion fusion(context.get(), loop_0, loop_2); 2376 EXPECT_FALSE(fusion.AreCompatible()); 2377 } 2378 2379 { 2380 LoopFusion fusion(context.get(), loop_1, loop_2); 2381 EXPECT_TRUE(fusion.AreCompatible()); 2382 EXPECT_TRUE(fusion.IsLegal()); 2383 2384 fusion.Fuse(); 2385 } 2386 } 2387 2388 { 2389 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2390 EXPECT_EQ(ld.NumLoops(), 2u); 2391 2392 std::string checks = R"( 2393 CHECK: [[PHI_0:%\w+]] = OpPhi 2394 CHECK-NEXT: OpLoopMerge 2395 CHECK: [[PHI_1:%\w+]] = OpPhi 2396 CHECK-NEXT: OpLoopMerge 2397 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 2398 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 2399 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 2400 CHECK-NEXT: OpStore [[STORE_0]] 2401 CHECK-NOT: OpPhi 2402 CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI_0]] {{%\w+}} 2403 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] [[PHI_1]] 2404 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 2405 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 2406 CHECK-NEXT: OpStore [[STORE_1]] 2407 )"; 2408 2409 Match(checks, context.get()); 2410 } 2411 } 2412 2413 /* 2414 Generated from the following GLSL + --eliminate-local-multi-store 2415 2416 #version 440 core 2417 void main() { 2418 int[10] a; 2419 int[10] b; 2420 int[10] c; 2421 // One dimensional arrays. Legal, outer dist 0, inner independent. 2422 for (int i = 0; i < 10; i++) { 2423 for (int j = 0; j < 10; j++) { 2424 c[i] = a[j] + 2; 2425 } 2426 } 2427 for (int i = 0; i < 10; i++) { 2428 for (int j = 0; j < 10; j++) { 2429 b[j] = c[i] + 10; 2430 } 2431 } 2432 } 2433 2434 */ 2435 TEST_F(FusionLegalTest, OuterAndInnerLoopOneDimArrays) { 2436 std::string text = R"( 2437 OpCapability Shader 2438 %1 = OpExtInstImport "GLSL.std.450" 2439 OpMemoryModel Logical GLSL450 2440 OpEntryPoint Fragment %4 "main" 2441 OpExecutionMode %4 OriginUpperLeft 2442 OpSource GLSL 440 2443 OpName %4 "main" 2444 OpName %8 "i" 2445 OpName %19 "j" 2446 OpName %31 "c" 2447 OpName %33 "a" 2448 OpName %45 "i" 2449 OpName %53 "j" 2450 OpName %61 "b" 2451 %2 = OpTypeVoid 2452 %3 = OpTypeFunction %2 2453 %6 = OpTypeInt 32 1 2454 %7 = OpTypePointer Function %6 2455 %9 = OpConstant %6 0 2456 %16 = OpConstant %6 10 2457 %17 = OpTypeBool 2458 %27 = OpTypeInt 32 0 2459 %28 = OpConstant %27 10 2460 %29 = OpTypeArray %6 %28 2461 %30 = OpTypePointer Function %29 2462 %37 = OpConstant %6 2 2463 %41 = OpConstant %6 1 2464 %4 = OpFunction %2 None %3 2465 %5 = OpLabel 2466 %8 = OpVariable %7 Function 2467 %19 = OpVariable %7 Function 2468 %31 = OpVariable %30 Function 2469 %33 = OpVariable %30 Function 2470 %45 = OpVariable %7 Function 2471 %53 = OpVariable %7 Function 2472 %61 = OpVariable %30 Function 2473 OpStore %8 %9 2474 OpBranch %10 2475 %10 = OpLabel 2476 %72 = OpPhi %6 %9 %5 %44 %13 2477 OpLoopMerge %12 %13 None 2478 OpBranch %14 2479 %14 = OpLabel 2480 %18 = OpSLessThan %17 %72 %16 2481 OpBranchConditional %18 %11 %12 2482 %11 = OpLabel 2483 OpStore %19 %9 2484 OpBranch %20 2485 %20 = OpLabel 2486 %76 = OpPhi %6 %9 %11 %42 %23 2487 OpLoopMerge %22 %23 None 2488 OpBranch %24 2489 %24 = OpLabel 2490 %26 = OpSLessThan %17 %76 %16 2491 OpBranchConditional %26 %21 %22 2492 %21 = OpLabel 2493 %35 = OpAccessChain %7 %33 %76 2494 %36 = OpLoad %6 %35 2495 %38 = OpIAdd %6 %36 %37 2496 %39 = OpAccessChain %7 %31 %72 2497 OpStore %39 %38 2498 OpBranch %23 2499 %23 = OpLabel 2500 %42 = OpIAdd %6 %76 %41 2501 OpStore %19 %42 2502 OpBranch %20 2503 %22 = OpLabel 2504 OpBranch %13 2505 %13 = OpLabel 2506 %44 = OpIAdd %6 %72 %41 2507 OpStore %8 %44 2508 OpBranch %10 2509 %12 = OpLabel 2510 OpStore %45 %9 2511 OpBranch %46 2512 %46 = OpLabel 2513 %73 = OpPhi %6 %9 %12 %71 %49 2514 OpLoopMerge %48 %49 None 2515 OpBranch %50 2516 %50 = OpLabel 2517 %52 = OpSLessThan %17 %73 %16 2518 OpBranchConditional %52 %47 %48 2519 %47 = OpLabel 2520 OpStore %53 %9 2521 OpBranch %54 2522 %54 = OpLabel 2523 %74 = OpPhi %6 %9 %47 %69 %57 2524 OpLoopMerge %56 %57 None 2525 OpBranch %58 2526 %58 = OpLabel 2527 %60 = OpSLessThan %17 %74 %16 2528 OpBranchConditional %60 %55 %56 2529 %55 = OpLabel 2530 %64 = OpAccessChain %7 %31 %73 2531 %65 = OpLoad %6 %64 2532 %66 = OpIAdd %6 %65 %16 2533 %67 = OpAccessChain %7 %61 %74 2534 OpStore %67 %66 2535 OpBranch %57 2536 %57 = OpLabel 2537 %69 = OpIAdd %6 %74 %41 2538 OpStore %53 %69 2539 OpBranch %54 2540 %56 = OpLabel 2541 OpBranch %49 2542 %49 = OpLabel 2543 %71 = OpIAdd %6 %73 %41 2544 OpStore %45 %71 2545 OpBranch %46 2546 %48 = OpLabel 2547 OpReturn 2548 OpFunctionEnd 2549 )"; 2550 2551 std::unique_ptr<IRContext> context = 2552 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 2553 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2554 Module* module = context->module(); 2555 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 2556 << text << std::endl; 2557 Function& f = *module->begin(); 2558 2559 { 2560 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2561 EXPECT_EQ(ld.NumLoops(), 4u); 2562 2563 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 2564 2565 auto loop_0 = loops[0]; 2566 auto loop_1 = loops[1]; 2567 auto loop_2 = loops[2]; 2568 auto loop_3 = loops[3]; 2569 2570 { 2571 LoopFusion fusion(context.get(), loop_0, loop_1); 2572 EXPECT_FALSE(fusion.AreCompatible()); 2573 } 2574 2575 { 2576 LoopFusion fusion(context.get(), loop_1, loop_2); 2577 EXPECT_FALSE(fusion.AreCompatible()); 2578 } 2579 2580 { 2581 LoopFusion fusion(context.get(), loop_2, loop_3); 2582 EXPECT_FALSE(fusion.AreCompatible()); 2583 } 2584 2585 { 2586 LoopFusion fusion(context.get(), loop_0, loop_2); 2587 EXPECT_TRUE(fusion.AreCompatible()); 2588 EXPECT_TRUE(fusion.IsLegal()); 2589 fusion.Fuse(); 2590 } 2591 2592 std::string checks = R"( 2593 CHECK: [[PHI_0:%\w+]] = OpPhi 2594 CHECK-NEXT: OpLoopMerge 2595 CHECK: [[PHI_1:%\w+]] = OpPhi 2596 CHECK-NEXT: OpLoopMerge 2597 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] 2598 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 2599 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 2600 CHECK-NEXT: OpStore [[STORE_0]] 2601 CHECK: [[PHI_2:%\w+]] = OpPhi 2602 CHECK-NEXT: OpLoopMerge 2603 CHECK-NOT: OpPhi 2604 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 2605 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 2606 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_2]] 2607 CHECK-NEXT: OpStore [[STORE_1]] 2608 )"; 2609 2610 Match(checks, context.get()); 2611 } 2612 2613 { 2614 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2615 EXPECT_EQ(ld.NumLoops(), 3u); 2616 2617 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 2618 2619 auto loop_0 = loops[0]; 2620 auto loop_1 = loops[1]; 2621 auto loop_2 = loops[2]; 2622 2623 { 2624 LoopFusion fusion(context.get(), loop_0, loop_1); 2625 EXPECT_FALSE(fusion.AreCompatible()); 2626 } 2627 2628 { 2629 LoopFusion fusion(context.get(), loop_0, loop_2); 2630 EXPECT_FALSE(fusion.AreCompatible()); 2631 } 2632 2633 { 2634 LoopFusion fusion(context.get(), loop_1, loop_2); 2635 EXPECT_TRUE(fusion.AreCompatible()); 2636 EXPECT_TRUE(fusion.IsLegal()); 2637 2638 fusion.Fuse(); 2639 } 2640 2641 std::string checks = R"( 2642 CHECK: [[PHI_0:%\w+]] = OpPhi 2643 CHECK-NEXT: OpLoopMerge 2644 CHECK: [[PHI_1:%\w+]] = OpPhi 2645 CHECK-NEXT: OpLoopMerge 2646 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] 2647 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 2648 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 2649 CHECK-NEXT: OpStore [[STORE_0]] 2650 CHECK-NOT: OpPhi 2651 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 2652 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 2653 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] 2654 CHECK-NEXT: OpStore [[STORE_1]] 2655 )"; 2656 2657 Match(checks, context.get()); 2658 } 2659 } 2660 2661 /* 2662 Generated from the following GLSL + --eliminate-local-multi-store 2663 2664 #version 440 core 2665 void main() { 2666 int[10] a; 2667 int[10] b; 2668 int[10] c; 2669 // Legal, creates a loop-carried dependence, but has negative distance 2670 for (int i = 0; i < 10; i++) { 2671 c[i] = a[i+1] + 1; 2672 } 2673 for (int i = 0; i < 10; i++) { 2674 a[i] = c[i] + 2; 2675 } 2676 } 2677 2678 */ 2679 TEST_F(FusionLegalTest, NegativeDistanceCreatedWAR) { 2680 std::string text = R"( 2681 OpCapability Shader 2682 %1 = OpExtInstImport "GLSL.std.450" 2683 OpMemoryModel Logical GLSL450 2684 OpEntryPoint Fragment %4 "main" 2685 OpExecutionMode %4 OriginUpperLeft 2686 OpSource GLSL 440 2687 OpName %4 "main" 2688 OpName %8 "i" 2689 OpName %23 "c" 2690 OpName %25 "a" 2691 OpName %35 "i" 2692 %2 = OpTypeVoid 2693 %3 = OpTypeFunction %2 2694 %6 = OpTypeInt 32 1 2695 %7 = OpTypePointer Function %6 2696 %9 = OpConstant %6 0 2697 %16 = OpConstant %6 10 2698 %17 = OpTypeBool 2699 %19 = OpTypeInt 32 0 2700 %20 = OpConstant %19 10 2701 %21 = OpTypeArray %6 %20 2702 %22 = OpTypePointer Function %21 2703 %27 = OpConstant %6 1 2704 %47 = OpConstant %6 2 2705 %4 = OpFunction %2 None %3 2706 %5 = OpLabel 2707 %8 = OpVariable %7 Function 2708 %23 = OpVariable %22 Function 2709 %25 = OpVariable %22 Function 2710 %35 = OpVariable %7 Function 2711 OpStore %8 %9 2712 OpBranch %10 2713 %10 = OpLabel 2714 %52 = OpPhi %6 %9 %5 %34 %13 2715 OpLoopMerge %12 %13 None 2716 OpBranch %14 2717 %14 = OpLabel 2718 %18 = OpSLessThan %17 %52 %16 2719 OpBranchConditional %18 %11 %12 2720 %11 = OpLabel 2721 %28 = OpIAdd %6 %52 %27 2722 %29 = OpAccessChain %7 %25 %28 2723 %30 = OpLoad %6 %29 2724 %31 = OpIAdd %6 %30 %27 2725 %32 = OpAccessChain %7 %23 %52 2726 OpStore %32 %31 2727 OpBranch %13 2728 %13 = OpLabel 2729 %34 = OpIAdd %6 %52 %27 2730 OpStore %8 %34 2731 OpBranch %10 2732 %12 = OpLabel 2733 OpStore %35 %9 2734 OpBranch %36 2735 %36 = OpLabel 2736 %53 = OpPhi %6 %9 %12 %51 %39 2737 OpLoopMerge %38 %39 None 2738 OpBranch %40 2739 %40 = OpLabel 2740 %42 = OpSLessThan %17 %53 %16 2741 OpBranchConditional %42 %37 %38 2742 %37 = OpLabel 2743 %45 = OpAccessChain %7 %23 %53 2744 %46 = OpLoad %6 %45 2745 %48 = OpIAdd %6 %46 %47 2746 %49 = OpAccessChain %7 %25 %53 2747 OpStore %49 %48 2748 OpBranch %39 2749 %39 = OpLabel 2750 %51 = OpIAdd %6 %53 %27 2751 OpStore %35 %51 2752 OpBranch %36 2753 %38 = OpLabel 2754 OpReturn 2755 OpFunctionEnd 2756 )"; 2757 2758 std::unique_ptr<IRContext> context = 2759 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 2760 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2761 Module* module = context->module(); 2762 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 2763 << text << std::endl; 2764 Function& f = *module->begin(); 2765 2766 { 2767 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2768 EXPECT_EQ(ld.NumLoops(), 2u); 2769 2770 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 2771 2772 LoopFusion fusion(context.get(), loops[0], loops[1]); 2773 EXPECT_TRUE(fusion.AreCompatible()); 2774 EXPECT_TRUE(fusion.IsLegal()); 2775 2776 fusion.Fuse(); 2777 2778 std::string checks = R"( 2779 CHECK: [[PHI:%\w+]] = OpPhi 2780 CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} 2781 CHECK-NEXT: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] 2782 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 2783 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 2784 CHECK-NEXT: OpStore [[STORE_0]] 2785 CHECK-NOT: OpPhi 2786 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 2787 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 2788 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 2789 CHECK-NEXT: OpStore [[STORE_1]] 2790 )"; 2791 2792 Match(checks, context.get()); 2793 } 2794 2795 { 2796 auto& ld = *context->GetLoopDescriptor(&f); 2797 EXPECT_EQ(ld.NumLoops(), 1u); 2798 } 2799 } 2800 2801 /* 2802 Generated from the following GLSL + --eliminate-local-multi-store 2803 2804 #version 440 core 2805 void main() { 2806 int[10] a; 2807 int[10] b; 2808 int[10] c; 2809 // Legal, creates a loop-carried dependence, but has negative distance 2810 for (int i = 0; i < 10; i++) { 2811 a[i+1] = b[i] + 1; 2812 } 2813 for (int i = 0; i < 10; i++) { 2814 a[i] = c[i+1] + 2; 2815 } 2816 } 2817 2818 */ 2819 TEST_F(FusionLegalTest, NegativeDistanceCreatedWAW) { 2820 std::string text = R"( 2821 OpCapability Shader 2822 %1 = OpExtInstImport "GLSL.std.450" 2823 OpMemoryModel Logical GLSL450 2824 OpEntryPoint Fragment %4 "main" 2825 OpExecutionMode %4 OriginUpperLeft 2826 OpSource GLSL 440 2827 OpName %4 "main" 2828 OpName %8 "i" 2829 OpName %23 "a" 2830 OpName %27 "b" 2831 OpName %35 "i" 2832 OpName %44 "c" 2833 %2 = OpTypeVoid 2834 %3 = OpTypeFunction %2 2835 %6 = OpTypeInt 32 1 2836 %7 = OpTypePointer Function %6 2837 %9 = OpConstant %6 0 2838 %16 = OpConstant %6 10 2839 %17 = OpTypeBool 2840 %19 = OpTypeInt 32 0 2841 %20 = OpConstant %19 10 2842 %21 = OpTypeArray %6 %20 2843 %22 = OpTypePointer Function %21 2844 %25 = OpConstant %6 1 2845 %49 = OpConstant %6 2 2846 %4 = OpFunction %2 None %3 2847 %5 = OpLabel 2848 %8 = OpVariable %7 Function 2849 %23 = OpVariable %22 Function 2850 %27 = OpVariable %22 Function 2851 %35 = OpVariable %7 Function 2852 %44 = OpVariable %22 Function 2853 OpStore %8 %9 2854 OpBranch %10 2855 %10 = OpLabel 2856 %54 = OpPhi %6 %9 %5 %34 %13 2857 OpLoopMerge %12 %13 None 2858 OpBranch %14 2859 %14 = OpLabel 2860 %18 = OpSLessThan %17 %54 %16 2861 OpBranchConditional %18 %11 %12 2862 %11 = OpLabel 2863 %26 = OpIAdd %6 %54 %25 2864 %29 = OpAccessChain %7 %27 %54 2865 %30 = OpLoad %6 %29 2866 %31 = OpIAdd %6 %30 %25 2867 %32 = OpAccessChain %7 %23 %26 2868 OpStore %32 %31 2869 OpBranch %13 2870 %13 = OpLabel 2871 %34 = OpIAdd %6 %54 %25 2872 OpStore %8 %34 2873 OpBranch %10 2874 %12 = OpLabel 2875 OpStore %35 %9 2876 OpBranch %36 2877 %36 = OpLabel 2878 %55 = OpPhi %6 %9 %12 %53 %39 2879 OpLoopMerge %38 %39 None 2880 OpBranch %40 2881 %40 = OpLabel 2882 %42 = OpSLessThan %17 %55 %16 2883 OpBranchConditional %42 %37 %38 2884 %37 = OpLabel 2885 %46 = OpIAdd %6 %55 %25 2886 %47 = OpAccessChain %7 %44 %46 2887 %48 = OpLoad %6 %47 2888 %50 = OpIAdd %6 %48 %49 2889 %51 = OpAccessChain %7 %23 %55 2890 OpStore %51 %50 2891 OpBranch %39 2892 %39 = OpLabel 2893 %53 = OpIAdd %6 %55 %25 2894 OpStore %35 %53 2895 OpBranch %36 2896 %38 = OpLabel 2897 OpReturn 2898 OpFunctionEnd 2899 )"; 2900 2901 std::unique_ptr<IRContext> context = 2902 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 2903 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 2904 Module* module = context->module(); 2905 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 2906 << text << std::endl; 2907 Function& f = *module->begin(); 2908 2909 { 2910 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2911 EXPECT_EQ(ld.NumLoops(), 2u); 2912 2913 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 2914 2915 LoopFusion fusion(context.get(), loops[0], loops[1]); 2916 EXPECT_TRUE(fusion.AreCompatible()); 2917 EXPECT_TRUE(fusion.IsLegal()); 2918 2919 fusion.Fuse(); 2920 } 2921 2922 { 2923 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 2924 EXPECT_EQ(ld.NumLoops(), 1u); 2925 2926 std::string checks = R"( 2927 CHECK: [[PHI:%\w+]] = OpPhi 2928 CHECK-NEXT: OpLoopMerge 2929 CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} 2930 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 2931 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 2932 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] 2933 CHECK-NEXT: OpStore 2934 CHECK-NOT: OpPhi 2935 CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} 2936 CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] 2937 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 2938 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 2939 CHECK-NEXT: OpStore [[STORE_1]] 2940 )"; 2941 2942 Match(checks, context.get()); 2943 } 2944 } 2945 2946 /* 2947 Generated from the following GLSL + --eliminate-local-multi-store 2948 2949 #version 440 core 2950 void main() { 2951 int[10] a; 2952 int[10] b; 2953 int[10] c; 2954 // Legal, no loop-carried dependence 2955 for (int i = 0; i < 10; i++) { 2956 a[i] = b[i] + 1; 2957 } 2958 for (int i = 0; i < 10; i++) { 2959 a[i] = c[i+1] + 2; 2960 } 2961 } 2962 2963 */ 2964 TEST_F(FusionLegalTest, NoLoopCarriedDependencesWAW) { 2965 std::string text = R"( 2966 OpCapability Shader 2967 %1 = OpExtInstImport "GLSL.std.450" 2968 OpMemoryModel Logical GLSL450 2969 OpEntryPoint Fragment %4 "main" 2970 OpExecutionMode %4 OriginUpperLeft 2971 OpSource GLSL 440 2972 OpName %4 "main" 2973 OpName %8 "i" 2974 OpName %23 "a" 2975 OpName %25 "b" 2976 OpName %34 "i" 2977 OpName %43 "c" 2978 %2 = OpTypeVoid 2979 %3 = OpTypeFunction %2 2980 %6 = OpTypeInt 32 1 2981 %7 = OpTypePointer Function %6 2982 %9 = OpConstant %6 0 2983 %16 = OpConstant %6 10 2984 %17 = OpTypeBool 2985 %19 = OpTypeInt 32 0 2986 %20 = OpConstant %19 10 2987 %21 = OpTypeArray %6 %20 2988 %22 = OpTypePointer Function %21 2989 %29 = OpConstant %6 1 2990 %48 = OpConstant %6 2 2991 %4 = OpFunction %2 None %3 2992 %5 = OpLabel 2993 %8 = OpVariable %7 Function 2994 %23 = OpVariable %22 Function 2995 %25 = OpVariable %22 Function 2996 %34 = OpVariable %7 Function 2997 %43 = OpVariable %22 Function 2998 OpStore %8 %9 2999 OpBranch %10 3000 %10 = OpLabel 3001 %53 = OpPhi %6 %9 %5 %33 %13 3002 OpLoopMerge %12 %13 None 3003 OpBranch %14 3004 %14 = OpLabel 3005 %18 = OpSLessThan %17 %53 %16 3006 OpBranchConditional %18 %11 %12 3007 %11 = OpLabel 3008 %27 = OpAccessChain %7 %25 %53 3009 %28 = OpLoad %6 %27 3010 %30 = OpIAdd %6 %28 %29 3011 %31 = OpAccessChain %7 %23 %53 3012 OpStore %31 %30 3013 OpBranch %13 3014 %13 = OpLabel 3015 %33 = OpIAdd %6 %53 %29 3016 OpStore %8 %33 3017 OpBranch %10 3018 %12 = OpLabel 3019 OpStore %34 %9 3020 OpBranch %35 3021 %35 = OpLabel 3022 %54 = OpPhi %6 %9 %12 %52 %38 3023 OpLoopMerge %37 %38 None 3024 OpBranch %39 3025 %39 = OpLabel 3026 %41 = OpSLessThan %17 %54 %16 3027 OpBranchConditional %41 %36 %37 3028 %36 = OpLabel 3029 %45 = OpIAdd %6 %54 %29 3030 %46 = OpAccessChain %7 %43 %45 3031 %47 = OpLoad %6 %46 3032 %49 = OpIAdd %6 %47 %48 3033 %50 = OpAccessChain %7 %23 %54 3034 OpStore %50 %49 3035 OpBranch %38 3036 %38 = OpLabel 3037 %52 = OpIAdd %6 %54 %29 3038 OpStore %34 %52 3039 OpBranch %35 3040 %37 = OpLabel 3041 OpReturn 3042 OpFunctionEnd 3043 )"; 3044 3045 std::unique_ptr<IRContext> context = 3046 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 3047 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 3048 Module* module = context->module(); 3049 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 3050 << text << std::endl; 3051 Function& f = *module->begin(); 3052 3053 { 3054 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3055 EXPECT_EQ(ld.NumLoops(), 2u); 3056 3057 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 3058 3059 LoopFusion fusion(context.get(), loops[0], loops[1]); 3060 EXPECT_TRUE(fusion.AreCompatible()); 3061 EXPECT_TRUE(fusion.IsLegal()); 3062 3063 fusion.Fuse(); 3064 } 3065 3066 { 3067 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3068 EXPECT_EQ(ld.NumLoops(), 1u); 3069 3070 std::string checks = R"( 3071 CHECK: [[PHI:%\w+]] = OpPhi 3072 CHECK-NEXT: OpLoopMerge 3073 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3074 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 3075 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3076 CHECK-NEXT: OpStore [[STORE_0]] 3077 CHECK-NOT: OpPhi 3078 CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} 3079 CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] 3080 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 3081 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3082 CHECK-NEXT: OpStore [[STORE_1]] 3083 )"; 3084 3085 Match(checks, context.get()); 3086 } 3087 } 3088 3089 /* 3090 Generated from the following GLSL + --eliminate-local-multi-store 3091 3092 #version 440 core 3093 void main() { 3094 int[10][10] a; 3095 int[10][10] b; 3096 int[10][10] c; 3097 // Legal outer. Continue and break are fine if nested in inner loops 3098 for (int i = 0; i < 10; i++) { 3099 for (int j = 0; j < 10; j++) { 3100 if (j % 2 == 0) { 3101 c[i][j] = a[i][j] + 2; 3102 } else { 3103 continue; 3104 } 3105 } 3106 } 3107 for (int i = 0; i < 10; i++) { 3108 for (int j = 0; j < 10; j++) { 3109 if (j % 2 == 0) { 3110 b[i][j] = c[i][j] + 10; 3111 } else { 3112 break; 3113 } 3114 } 3115 } 3116 } 3117 3118 */ 3119 TEST_F(FusionLegalTest, OuterloopWithBreakContinueInInner) { 3120 std::string text = R"( 3121 OpCapability Shader 3122 %1 = OpExtInstImport "GLSL.std.450" 3123 OpMemoryModel Logical GLSL450 3124 OpEntryPoint Fragment %4 "main" 3125 OpExecutionMode %4 OriginUpperLeft 3126 OpSource GLSL 440 3127 OpName %4 "main" 3128 OpName %8 "i" 3129 OpName %19 "j" 3130 OpName %38 "c" 3131 OpName %41 "a" 3132 OpName %55 "i" 3133 OpName %63 "j" 3134 OpName %76 "b" 3135 %2 = OpTypeVoid 3136 %3 = OpTypeFunction %2 3137 %6 = OpTypeInt 32 1 3138 %7 = OpTypePointer Function %6 3139 %9 = OpConstant %6 0 3140 %16 = OpConstant %6 10 3141 %17 = OpTypeBool 3142 %28 = OpConstant %6 2 3143 %33 = OpTypeInt 32 0 3144 %34 = OpConstant %33 10 3145 %35 = OpTypeArray %6 %34 3146 %36 = OpTypeArray %35 %34 3147 %37 = OpTypePointer Function %36 3148 %51 = OpConstant %6 1 3149 %4 = OpFunction %2 None %3 3150 %5 = OpLabel 3151 %8 = OpVariable %7 Function 3152 %19 = OpVariable %7 Function 3153 %38 = OpVariable %37 Function 3154 %41 = OpVariable %37 Function 3155 %55 = OpVariable %7 Function 3156 %63 = OpVariable %7 Function 3157 %76 = OpVariable %37 Function 3158 OpStore %8 %9 3159 OpBranch %10 3160 %10 = OpLabel 3161 %91 = OpPhi %6 %9 %5 %54 %13 3162 OpLoopMerge %12 %13 None 3163 OpBranch %14 3164 %14 = OpLabel 3165 %18 = OpSLessThan %17 %91 %16 3166 OpBranchConditional %18 %11 %12 3167 %11 = OpLabel 3168 OpStore %19 %9 3169 OpBranch %20 3170 %20 = OpLabel 3171 %96 = OpPhi %6 %9 %11 %52 %23 3172 OpLoopMerge %22 %23 None 3173 OpBranch %24 3174 %24 = OpLabel 3175 %26 = OpSLessThan %17 %96 %16 3176 OpBranchConditional %26 %21 %22 3177 %21 = OpLabel 3178 %29 = OpSMod %6 %96 %28 3179 %30 = OpIEqual %17 %29 %9 3180 OpSelectionMerge %23 None 3181 OpBranchConditional %30 %31 %48 3182 %31 = OpLabel 3183 %44 = OpAccessChain %7 %41 %91 %96 3184 %45 = OpLoad %6 %44 3185 %46 = OpIAdd %6 %45 %28 3186 %47 = OpAccessChain %7 %38 %91 %96 3187 OpStore %47 %46 3188 OpBranch %32 3189 %48 = OpLabel 3190 OpBranch %23 3191 %32 = OpLabel 3192 OpBranch %23 3193 %23 = OpLabel 3194 %52 = OpIAdd %6 %96 %51 3195 OpStore %19 %52 3196 OpBranch %20 3197 %22 = OpLabel 3198 OpBranch %13 3199 %13 = OpLabel 3200 %54 = OpIAdd %6 %91 %51 3201 OpStore %8 %54 3202 OpBranch %10 3203 %12 = OpLabel 3204 OpStore %55 %9 3205 OpBranch %56 3206 %56 = OpLabel 3207 %92 = OpPhi %6 %9 %12 %90 %59 3208 OpLoopMerge %58 %59 None 3209 OpBranch %60 3210 %60 = OpLabel 3211 %62 = OpSLessThan %17 %92 %16 3212 OpBranchConditional %62 %57 %58 3213 %57 = OpLabel 3214 OpStore %63 %9 3215 OpBranch %64 3216 %64 = OpLabel 3217 %93 = OpPhi %6 %9 %57 %88 %67 3218 OpLoopMerge %66 %67 None 3219 OpBranch %68 3220 %68 = OpLabel 3221 %70 = OpSLessThan %17 %93 %16 3222 OpBranchConditional %70 %65 %66 3223 %65 = OpLabel 3224 %72 = OpSMod %6 %93 %28 3225 %73 = OpIEqual %17 %72 %9 3226 OpSelectionMerge %75 None 3227 OpBranchConditional %73 %74 %66 3228 %74 = OpLabel 3229 %81 = OpAccessChain %7 %38 %92 %93 3230 %82 = OpLoad %6 %81 3231 %83 = OpIAdd %6 %82 %16 3232 %84 = OpAccessChain %7 %76 %92 %93 3233 OpStore %84 %83 3234 OpBranch %75 3235 %75 = OpLabel 3236 OpBranch %67 3237 %67 = OpLabel 3238 %88 = OpIAdd %6 %93 %51 3239 OpStore %63 %88 3240 OpBranch %64 3241 %66 = OpLabel 3242 OpBranch %59 3243 %59 = OpLabel 3244 %90 = OpIAdd %6 %92 %51 3245 OpStore %55 %90 3246 OpBranch %56 3247 %58 = OpLabel 3248 OpReturn 3249 OpFunctionEnd 3250 )"; 3251 3252 std::unique_ptr<IRContext> context = 3253 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 3254 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 3255 Module* module = context->module(); 3256 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 3257 << text << std::endl; 3258 Function& f = *module->begin(); 3259 3260 { 3261 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3262 EXPECT_EQ(ld.NumLoops(), 4u); 3263 3264 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 3265 3266 LoopFusion fusion(context.get(), loops[0], loops[2]); 3267 EXPECT_TRUE(fusion.AreCompatible()); 3268 EXPECT_TRUE(fusion.IsLegal()); 3269 3270 fusion.Fuse(); 3271 } 3272 3273 { 3274 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3275 EXPECT_EQ(ld.NumLoops(), 3u); 3276 3277 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 3278 3279 LoopFusion fusion(context.get(), loops[1], loops[2]); 3280 EXPECT_FALSE(fusion.AreCompatible()); 3281 3282 std::string checks = R"( 3283 CHECK: [[PHI_0:%\w+]] = OpPhi 3284 CHECK-NEXT: OpLoopMerge 3285 CHECK: [[PHI_1:%\w+]] = OpPhi 3286 CHECK-NEXT: OpLoopMerge 3287 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 3288 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 3289 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] 3290 CHECK-NEXT: OpStore [[STORE_0]] 3291 CHECK: [[PHI_2:%\w+]] = OpPhi 3292 CHECK-NEXT: OpLoopMerge 3293 CHECK-NOT: OpPhi 3294 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] 3295 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 3296 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] 3297 CHECK-NEXT: OpStore [[STORE_1]] 3298 )"; 3299 3300 Match(checks, context.get()); 3301 } 3302 } 3303 3304 /* 3305 Generated from the following GLSL + --eliminate-local-multi-store 3306 3307 // j loop preheader removed manually 3308 #version 440 core 3309 void main() { 3310 int[10] a; 3311 int[10] b; 3312 int i = 0; 3313 int j = 0; 3314 // No loop-carried dependences, legal 3315 for (; i < 10; i++) { 3316 a[i] = a[i]*2; 3317 } 3318 for (; j < 10; j++) { 3319 b[j] = a[j]+2; 3320 } 3321 } 3322 3323 */ 3324 TEST_F(FusionLegalTest, DifferentArraysInLoopsNoPreheader) { 3325 std::string text = R"( 3326 OpCapability Shader 3327 %1 = OpExtInstImport "GLSL.std.450" 3328 OpMemoryModel Logical GLSL450 3329 OpEntryPoint Fragment %4 "main" 3330 OpExecutionMode %4 OriginUpperLeft 3331 OpSource GLSL 440 3332 OpName %4 "main" 3333 OpName %8 "i" 3334 OpName %10 "j" 3335 OpName %24 "a" 3336 OpName %42 "b" 3337 %2 = OpTypeVoid 3338 %3 = OpTypeFunction %2 3339 %6 = OpTypeInt 32 1 3340 %7 = OpTypePointer Function %6 3341 %9 = OpConstant %6 0 3342 %17 = OpConstant %6 10 3343 %18 = OpTypeBool 3344 %20 = OpTypeInt 32 0 3345 %21 = OpConstant %20 10 3346 %22 = OpTypeArray %6 %21 3347 %23 = OpTypePointer Function %22 3348 %29 = OpConstant %6 2 3349 %33 = OpConstant %6 1 3350 %4 = OpFunction %2 None %3 3351 %5 = OpLabel 3352 %8 = OpVariable %7 Function 3353 %10 = OpVariable %7 Function 3354 %24 = OpVariable %23 Function 3355 %42 = OpVariable %23 Function 3356 OpStore %8 %9 3357 OpStore %10 %9 3358 OpBranch %11 3359 %11 = OpLabel 3360 %51 = OpPhi %6 %9 %5 %34 %14 3361 OpLoopMerge %35 %14 None 3362 OpBranch %15 3363 %15 = OpLabel 3364 %19 = OpSLessThan %18 %51 %17 3365 OpBranchConditional %19 %12 %35 3366 %12 = OpLabel 3367 %27 = OpAccessChain %7 %24 %51 3368 %28 = OpLoad %6 %27 3369 %30 = OpIMul %6 %28 %29 3370 %31 = OpAccessChain %7 %24 %51 3371 OpStore %31 %30 3372 OpBranch %14 3373 %14 = OpLabel 3374 %34 = OpIAdd %6 %51 %33 3375 OpStore %8 %34 3376 OpBranch %11 3377 %35 = OpLabel 3378 %52 = OpPhi %6 %9 %15 %50 %38 3379 OpLoopMerge %37 %38 None 3380 OpBranch %39 3381 %39 = OpLabel 3382 %41 = OpSLessThan %18 %52 %17 3383 OpBranchConditional %41 %36 %37 3384 %36 = OpLabel 3385 %45 = OpAccessChain %7 %24 %52 3386 %46 = OpLoad %6 %45 3387 %47 = OpIAdd %6 %46 %29 3388 %48 = OpAccessChain %7 %42 %52 3389 OpStore %48 %47 3390 OpBranch %38 3391 %38 = OpLabel 3392 %50 = OpIAdd %6 %52 %33 3393 OpStore %10 %50 3394 OpBranch %35 3395 %37 = OpLabel 3396 OpReturn 3397 OpFunctionEnd 3398 )"; 3399 3400 std::unique_ptr<IRContext> context = 3401 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 3402 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 3403 Module* module = context->module(); 3404 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 3405 << text << std::endl; 3406 Function& f = *module->begin(); 3407 3408 { 3409 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3410 EXPECT_EQ(ld.NumLoops(), 2u); 3411 3412 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 3413 3414 { 3415 LoopFusion fusion(context.get(), loops[0], loops[1]); 3416 EXPECT_FALSE(fusion.AreCompatible()); 3417 } 3418 3419 ld.CreatePreHeaderBlocksIfMissing(); 3420 3421 { 3422 LoopFusion fusion(context.get(), loops[0], loops[1]); 3423 EXPECT_TRUE(fusion.AreCompatible()); 3424 EXPECT_TRUE(fusion.IsLegal()); 3425 3426 fusion.Fuse(); 3427 } 3428 } 3429 3430 { 3431 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3432 EXPECT_EQ(ld.NumLoops(), 1u); 3433 3434 std::string checks = R"( 3435 CHECK: [[PHI:%\w+]] = OpPhi 3436 CHECK-NEXT: OpLoopMerge 3437 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3438 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 3439 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3440 CHECK-NEXT: OpStore [[STORE_0]] 3441 CHECK-NOT: OpPhi 3442 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3443 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 3444 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3445 CHECK-NEXT: OpStore [[STORE_1]] 3446 )"; 3447 3448 Match(checks, context.get()); 3449 } 3450 } 3451 3452 /* 3453 Generated from the following GLSL + --eliminate-local-multi-store 3454 3455 // j & k loop preheaders removed manually 3456 #version 440 core 3457 void main() { 3458 int[10] a; 3459 int[10] b; 3460 int i = 0; 3461 int j = 0; 3462 int k = 0; 3463 // No loop-carried dependences, legal 3464 for (; i < 10; i++) { 3465 a[i] = a[i]*2; 3466 } 3467 for (; j < 10; j++) { 3468 b[j] = a[j]+2; 3469 } 3470 for (; k < 10; k++) { 3471 a[k] = a[k]*2; 3472 } 3473 } 3474 3475 */ 3476 TEST_F(FusionLegalTest, AdjacentLoopsNoPreheaders) { 3477 std::string text = R"( 3478 OpCapability Shader 3479 %1 = OpExtInstImport "GLSL.std.450" 3480 OpMemoryModel Logical GLSL450 3481 OpEntryPoint Fragment %4 "main" 3482 OpExecutionMode %4 OriginUpperLeft 3483 OpSource GLSL 440 3484 OpName %4 "main" 3485 OpName %8 "i" 3486 OpName %10 "j" 3487 OpName %11 "k" 3488 OpName %25 "a" 3489 OpName %43 "b" 3490 %2 = OpTypeVoid 3491 %3 = OpTypeFunction %2 3492 %6 = OpTypeInt 32 1 3493 %7 = OpTypePointer Function %6 3494 %9 = OpConstant %6 0 3495 %18 = OpConstant %6 10 3496 %19 = OpTypeBool 3497 %21 = OpTypeInt 32 0 3498 %22 = OpConstant %21 10 3499 %23 = OpTypeArray %6 %22 3500 %24 = OpTypePointer Function %23 3501 %30 = OpConstant %6 2 3502 %34 = OpConstant %6 1 3503 %4 = OpFunction %2 None %3 3504 %5 = OpLabel 3505 %8 = OpVariable %7 Function 3506 %10 = OpVariable %7 Function 3507 %11 = OpVariable %7 Function 3508 %25 = OpVariable %24 Function 3509 %43 = OpVariable %24 Function 3510 OpStore %8 %9 3511 OpStore %10 %9 3512 OpStore %11 %9 3513 OpBranch %12 3514 %12 = OpLabel 3515 %67 = OpPhi %6 %9 %5 %35 %15 3516 OpLoopMerge %36 %15 None 3517 OpBranch %16 3518 %16 = OpLabel 3519 %20 = OpSLessThan %19 %67 %18 3520 OpBranchConditional %20 %13 %36 3521 %13 = OpLabel 3522 %28 = OpAccessChain %7 %25 %67 3523 %29 = OpLoad %6 %28 3524 %31 = OpIMul %6 %29 %30 3525 %32 = OpAccessChain %7 %25 %67 3526 OpStore %32 %31 3527 OpBranch %15 3528 %15 = OpLabel 3529 %35 = OpIAdd %6 %67 %34 3530 OpStore %8 %35 3531 OpBranch %12 3532 %36 = OpLabel 3533 %68 = OpPhi %6 %9 %16 %51 %39 3534 OpLoopMerge %52 %39 None 3535 OpBranch %40 3536 %40 = OpLabel 3537 %42 = OpSLessThan %19 %68 %18 3538 OpBranchConditional %42 %37 %52 3539 %37 = OpLabel 3540 %46 = OpAccessChain %7 %25 %68 3541 %47 = OpLoad %6 %46 3542 %48 = OpIAdd %6 %47 %30 3543 %49 = OpAccessChain %7 %43 %68 3544 OpStore %49 %48 3545 OpBranch %39 3546 %39 = OpLabel 3547 %51 = OpIAdd %6 %68 %34 3548 OpStore %10 %51 3549 OpBranch %36 3550 %52 = OpLabel 3551 %70 = OpPhi %6 %9 %40 %66 %55 3552 OpLoopMerge %54 %55 None 3553 OpBranch %56 3554 %56 = OpLabel 3555 %58 = OpSLessThan %19 %70 %18 3556 OpBranchConditional %58 %53 %54 3557 %53 = OpLabel 3558 %61 = OpAccessChain %7 %25 %70 3559 %62 = OpLoad %6 %61 3560 %63 = OpIMul %6 %62 %30 3561 %64 = OpAccessChain %7 %25 %70 3562 OpStore %64 %63 3563 OpBranch %55 3564 %55 = OpLabel 3565 %66 = OpIAdd %6 %70 %34 3566 OpStore %11 %66 3567 OpBranch %52 3568 %54 = OpLabel 3569 OpReturn 3570 OpFunctionEnd 3571 )"; 3572 3573 std::unique_ptr<IRContext> context = 3574 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 3575 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 3576 Module* module = context->module(); 3577 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 3578 << text << std::endl; 3579 Function& f = *module->begin(); 3580 3581 { 3582 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3583 EXPECT_EQ(ld.NumLoops(), 3u); 3584 3585 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 3586 3587 { 3588 LoopFusion fusion(context.get(), loops[0], loops[1]); 3589 EXPECT_FALSE(fusion.AreCompatible()); 3590 } 3591 3592 ld.CreatePreHeaderBlocksIfMissing(); 3593 3594 { 3595 LoopFusion fusion(context.get(), loops[0], loops[1]); 3596 EXPECT_TRUE(fusion.AreCompatible()); 3597 EXPECT_TRUE(fusion.IsLegal()); 3598 3599 fusion.Fuse(); 3600 } 3601 } 3602 3603 { 3604 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3605 EXPECT_EQ(ld.NumLoops(), 2u); 3606 3607 std::string checks = R"( 3608 CHECK: [[PHI_0:%\w+]] = OpPhi 3609 CHECK-NEXT: OpLoopMerge 3610 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 3611 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 3612 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 3613 CHECK-NEXT: OpStore [[STORE_0]] 3614 CHECK-NOT: OpPhi 3615 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 3616 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 3617 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] 3618 CHECK-NEXT: OpStore [[STORE_1]] 3619 CHECK: [[PHI_1:%\w+]] = OpPhi 3620 CHECK-NEXT: OpLoopMerge 3621 CHECK: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] 3622 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] 3623 CHECK: [[STORE_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] 3624 CHECK-NEXT: OpStore [[STORE_2]] 3625 )"; 3626 3627 Match(checks, context.get()); 3628 3629 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 3630 3631 LoopFusion fusion(context.get(), loops[0], loops[1]); 3632 EXPECT_TRUE(fusion.AreCompatible()); 3633 EXPECT_TRUE(fusion.IsLegal()); 3634 3635 fusion.Fuse(); 3636 } 3637 3638 { 3639 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3640 EXPECT_EQ(ld.NumLoops(), 1u); 3641 3642 std::string checks = R"( 3643 CHECK: [[PHI:%\w+]] = OpPhi 3644 CHECK-NEXT: OpLoopMerge 3645 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3646 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] 3647 CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3648 CHECK-NEXT: OpStore [[STORE_0]] 3649 CHECK-NOT: OpPhi 3650 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3651 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] 3652 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3653 CHECK-NEXT: OpStore [[STORE_1]] 3654 CHECK-NOT: OpPhi 3655 CHECK: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3656 CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] 3657 CHECK: [[STORE_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3658 CHECK-NEXT: OpStore [[STORE_2]] 3659 )"; 3660 3661 Match(checks, context.get()); 3662 } 3663 } 3664 3665 /* 3666 Generated from the following GLSL + --eliminate-local-multi-store 3667 3668 #version 440 core 3669 void main() { 3670 int[10] a; 3671 int[10] b; 3672 3673 int sum_0 = 0; 3674 int sum_1 = 0; 3675 3676 // No loop-carried dependences, legal 3677 for (int i = 0; i < 10; i++) { 3678 sum_0 += a[i]; 3679 } 3680 for (int j = 0; j < 10; j++) { 3681 sum_1 += b[j]; 3682 } 3683 3684 int total = sum_0 + sum_1; 3685 } 3686 3687 */ 3688 TEST_F(FusionLegalTest, IndependentReductions) { 3689 std::string text = R"( 3690 OpCapability Shader 3691 %1 = OpExtInstImport "GLSL.std.450" 3692 OpMemoryModel Logical GLSL450 3693 OpEntryPoint Fragment %4 "main" 3694 OpExecutionMode %4 OriginUpperLeft 3695 OpSource GLSL 440 3696 OpName %4 "main" 3697 OpName %8 "sum_0" 3698 OpName %10 "sum_1" 3699 OpName %11 "i" 3700 OpName %25 "a" 3701 OpName %34 "j" 3702 OpName %42 "b" 3703 OpName %50 "total" 3704 %2 = OpTypeVoid 3705 %3 = OpTypeFunction %2 3706 %6 = OpTypeInt 32 1 3707 %7 = OpTypePointer Function %6 3708 %9 = OpConstant %6 0 3709 %18 = OpConstant %6 10 3710 %19 = OpTypeBool 3711 %21 = OpTypeInt 32 0 3712 %22 = OpConstant %21 10 3713 %23 = OpTypeArray %6 %22 3714 %24 = OpTypePointer Function %23 3715 %32 = OpConstant %6 1 3716 %4 = OpFunction %2 None %3 3717 %5 = OpLabel 3718 %8 = OpVariable %7 Function 3719 %10 = OpVariable %7 Function 3720 %11 = OpVariable %7 Function 3721 %25 = OpVariable %24 Function 3722 %34 = OpVariable %7 Function 3723 %42 = OpVariable %24 Function 3724 %50 = OpVariable %7 Function 3725 OpStore %8 %9 3726 OpStore %10 %9 3727 OpStore %11 %9 3728 OpBranch %12 3729 %12 = OpLabel 3730 %57 = OpPhi %6 %9 %5 %30 %15 3731 %54 = OpPhi %6 %9 %5 %33 %15 3732 OpLoopMerge %14 %15 None 3733 OpBranch %16 3734 %16 = OpLabel 3735 %20 = OpSLessThan %19 %54 %18 3736 OpBranchConditional %20 %13 %14 3737 %13 = OpLabel 3738 %27 = OpAccessChain %7 %25 %54 3739 %28 = OpLoad %6 %27 3740 %30 = OpIAdd %6 %57 %28 3741 OpStore %8 %30 3742 OpBranch %15 3743 %15 = OpLabel 3744 %33 = OpIAdd %6 %54 %32 3745 OpStore %11 %33 3746 OpBranch %12 3747 %14 = OpLabel 3748 OpStore %34 %9 3749 OpBranch %35 3750 %35 = OpLabel 3751 %58 = OpPhi %6 %9 %14 %47 %38 3752 %55 = OpPhi %6 %9 %14 %49 %38 3753 OpLoopMerge %37 %38 None 3754 OpBranch %39 3755 %39 = OpLabel 3756 %41 = OpSLessThan %19 %55 %18 3757 OpBranchConditional %41 %36 %37 3758 %36 = OpLabel 3759 %44 = OpAccessChain %7 %42 %55 3760 %45 = OpLoad %6 %44 3761 %47 = OpIAdd %6 %58 %45 3762 OpStore %10 %47 3763 OpBranch %38 3764 %38 = OpLabel 3765 %49 = OpIAdd %6 %55 %32 3766 OpStore %34 %49 3767 OpBranch %35 3768 %37 = OpLabel 3769 %53 = OpIAdd %6 %57 %58 3770 OpStore %50 %53 3771 OpReturn 3772 OpFunctionEnd 3773 )"; 3774 3775 std::unique_ptr<IRContext> context = 3776 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 3777 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 3778 Module* module = context->module(); 3779 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 3780 << text << std::endl; 3781 Function& f = *module->begin(); 3782 3783 { 3784 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3785 EXPECT_EQ(ld.NumLoops(), 2u); 3786 3787 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 3788 3789 LoopFusion fusion(context.get(), loops[0], loops[1]); 3790 EXPECT_TRUE(fusion.AreCompatible()); 3791 EXPECT_TRUE(fusion.IsLegal()); 3792 3793 fusion.Fuse(); 3794 } 3795 3796 { 3797 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3798 EXPECT_EQ(ld.NumLoops(), 1u); 3799 3800 std::string checks = R"( 3801 CHECK: [[SUM_0:%\w+]] = OpPhi 3802 CHECK-NEXT: [[SUM_1:%\w+]] = OpPhi 3803 CHECK-NEXT: [[PHI:%\w+]] = OpPhi 3804 CHECK-NEXT: OpLoopMerge 3805 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3806 CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] 3807 CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[SUM_0]] [[LOAD_RES_0]] 3808 CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_0]] 3809 CHECK-NOT: OpPhi 3810 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3811 CHECK-NEXT: [[LOAD_RES_1:%\w+]] = OpLoad {{%\w+}} [[LOAD_1]] 3812 CHECK-NEXT: [[ADD_RES_1:%\w+]] = OpIAdd {{%\w+}} [[SUM_1]] [[LOAD_RES_1]] 3813 CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_1]] 3814 )"; 3815 3816 Match(checks, context.get()); 3817 } 3818 } 3819 3820 /* 3821 Generated from the following GLSL + --eliminate-local-multi-store 3822 3823 #version 440 core 3824 void main() { 3825 int[10] a; 3826 int[10] b; 3827 3828 int sum_0 = 0; 3829 int sum_1 = 0; 3830 3831 // No loop-carried dependences, legal 3832 for (int i = 0; i < 10; i++) { 3833 sum_0 += a[i]; 3834 } 3835 for (int j = 0; j < 10; j++) { 3836 sum_1 += b[j]; 3837 } 3838 3839 int total = sum_0 + sum_1; 3840 } 3841 3842 */ 3843 TEST_F(FusionLegalTest, IndependentReductionsOneLCSSA) { 3844 std::string text = R"( 3845 OpCapability Shader 3846 %1 = OpExtInstImport "GLSL.std.450" 3847 OpMemoryModel Logical GLSL450 3848 OpEntryPoint Fragment %4 "main" 3849 OpExecutionMode %4 OriginUpperLeft 3850 OpSource GLSL 440 3851 OpName %4 "main" 3852 OpName %8 "sum_0" 3853 OpName %10 "sum_1" 3854 OpName %11 "i" 3855 OpName %25 "a" 3856 OpName %34 "j" 3857 OpName %42 "b" 3858 OpName %50 "total" 3859 %2 = OpTypeVoid 3860 %3 = OpTypeFunction %2 3861 %6 = OpTypeInt 32 1 3862 %7 = OpTypePointer Function %6 3863 %9 = OpConstant %6 0 3864 %18 = OpConstant %6 10 3865 %19 = OpTypeBool 3866 %21 = OpTypeInt 32 0 3867 %22 = OpConstant %21 10 3868 %23 = OpTypeArray %6 %22 3869 %24 = OpTypePointer Function %23 3870 %32 = OpConstant %6 1 3871 %4 = OpFunction %2 None %3 3872 %5 = OpLabel 3873 %8 = OpVariable %7 Function 3874 %10 = OpVariable %7 Function 3875 %11 = OpVariable %7 Function 3876 %25 = OpVariable %24 Function 3877 %34 = OpVariable %7 Function 3878 %42 = OpVariable %24 Function 3879 %50 = OpVariable %7 Function 3880 OpStore %8 %9 3881 OpStore %10 %9 3882 OpStore %11 %9 3883 OpBranch %12 3884 %12 = OpLabel 3885 %57 = OpPhi %6 %9 %5 %30 %15 3886 %54 = OpPhi %6 %9 %5 %33 %15 3887 OpLoopMerge %14 %15 None 3888 OpBranch %16 3889 %16 = OpLabel 3890 %20 = OpSLessThan %19 %54 %18 3891 OpBranchConditional %20 %13 %14 3892 %13 = OpLabel 3893 %27 = OpAccessChain %7 %25 %54 3894 %28 = OpLoad %6 %27 3895 %30 = OpIAdd %6 %57 %28 3896 OpStore %8 %30 3897 OpBranch %15 3898 %15 = OpLabel 3899 %33 = OpIAdd %6 %54 %32 3900 OpStore %11 %33 3901 OpBranch %12 3902 %14 = OpLabel 3903 OpStore %34 %9 3904 OpBranch %35 3905 %35 = OpLabel 3906 %58 = OpPhi %6 %9 %14 %47 %38 3907 %55 = OpPhi %6 %9 %14 %49 %38 3908 OpLoopMerge %37 %38 None 3909 OpBranch %39 3910 %39 = OpLabel 3911 %41 = OpSLessThan %19 %55 %18 3912 OpBranchConditional %41 %36 %37 3913 %36 = OpLabel 3914 %44 = OpAccessChain %7 %42 %55 3915 %45 = OpLoad %6 %44 3916 %47 = OpIAdd %6 %58 %45 3917 OpStore %10 %47 3918 OpBranch %38 3919 %38 = OpLabel 3920 %49 = OpIAdd %6 %55 %32 3921 OpStore %34 %49 3922 OpBranch %35 3923 %37 = OpLabel 3924 %53 = OpIAdd %6 %57 %58 3925 OpStore %50 %53 3926 OpReturn 3927 OpFunctionEnd 3928 )"; 3929 3930 std::unique_ptr<IRContext> context = 3931 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 3932 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 3933 Module* module = context->module(); 3934 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 3935 << text << std::endl; 3936 Function& f = *module->begin(); 3937 3938 { 3939 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3940 EXPECT_EQ(ld.NumLoops(), 2u); 3941 3942 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 3943 3944 LoopUtils utils_0(context.get(), loops[0]); 3945 utils_0.MakeLoopClosedSSA(); 3946 3947 LoopFusion fusion(context.get(), loops[0], loops[1]); 3948 EXPECT_TRUE(fusion.AreCompatible()); 3949 EXPECT_TRUE(fusion.IsLegal()); 3950 3951 fusion.Fuse(); 3952 } 3953 3954 { 3955 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 3956 EXPECT_EQ(ld.NumLoops(), 1u); 3957 3958 std::string checks = R"( 3959 CHECK: [[SUM_0:%\w+]] = OpPhi 3960 CHECK-NEXT: [[SUM_1:%\w+]] = OpPhi 3961 CHECK-NEXT: [[PHI:%\w+]] = OpPhi 3962 CHECK-NEXT: OpLoopMerge 3963 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3964 CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] 3965 CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[SUM_0]] [[LOAD_RES_0]] 3966 CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_0]] 3967 CHECK-NOT: OpPhi 3968 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 3969 CHECK-NEXT: [[LOAD_RES_1:%\w+]] = OpLoad {{%\w+}} [[LOAD_1]] 3970 CHECK-NEXT: [[ADD_RES_1:%\w+]] = OpIAdd {{%\w+}} [[SUM_1]] [[LOAD_RES_1]] 3971 CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_1]] 3972 )"; 3973 3974 Match(checks, context.get()); 3975 } 3976 } 3977 3978 /* 3979 Generated from the following GLSL + --eliminate-local-multi-store 3980 3981 #version 440 core 3982 void main() { 3983 int[10] a; 3984 int[10] b; 3985 3986 int sum_0 = 0; 3987 int sum_1 = 0; 3988 3989 // No loop-carried dependences, legal 3990 for (int i = 0; i < 10; i++) { 3991 sum_0 += a[i]; 3992 } 3993 for (int j = 0; j < 10; j++) { 3994 sum_1 += b[j]; 3995 } 3996 3997 int total = sum_0 + sum_1; 3998 } 3999 4000 */ 4001 TEST_F(FusionLegalTest, IndependentReductionsBothLCSSA) { 4002 std::string text = R"( 4003 OpCapability Shader 4004 %1 = OpExtInstImport "GLSL.std.450" 4005 OpMemoryModel Logical GLSL450 4006 OpEntryPoint Fragment %4 "main" 4007 OpExecutionMode %4 OriginUpperLeft 4008 OpSource GLSL 440 4009 OpName %4 "main" 4010 OpName %8 "sum_0" 4011 OpName %10 "sum_1" 4012 OpName %11 "i" 4013 OpName %25 "a" 4014 OpName %34 "j" 4015 OpName %42 "b" 4016 OpName %50 "total" 4017 %2 = OpTypeVoid 4018 %3 = OpTypeFunction %2 4019 %6 = OpTypeInt 32 1 4020 %7 = OpTypePointer Function %6 4021 %9 = OpConstant %6 0 4022 %18 = OpConstant %6 10 4023 %19 = OpTypeBool 4024 %21 = OpTypeInt 32 0 4025 %22 = OpConstant %21 10 4026 %23 = OpTypeArray %6 %22 4027 %24 = OpTypePointer Function %23 4028 %32 = OpConstant %6 1 4029 %4 = OpFunction %2 None %3 4030 %5 = OpLabel 4031 %8 = OpVariable %7 Function 4032 %10 = OpVariable %7 Function 4033 %11 = OpVariable %7 Function 4034 %25 = OpVariable %24 Function 4035 %34 = OpVariable %7 Function 4036 %42 = OpVariable %24 Function 4037 %50 = OpVariable %7 Function 4038 OpStore %8 %9 4039 OpStore %10 %9 4040 OpStore %11 %9 4041 OpBranch %12 4042 %12 = OpLabel 4043 %57 = OpPhi %6 %9 %5 %30 %15 4044 %54 = OpPhi %6 %9 %5 %33 %15 4045 OpLoopMerge %14 %15 None 4046 OpBranch %16 4047 %16 = OpLabel 4048 %20 = OpSLessThan %19 %54 %18 4049 OpBranchConditional %20 %13 %14 4050 %13 = OpLabel 4051 %27 = OpAccessChain %7 %25 %54 4052 %28 = OpLoad %6 %27 4053 %30 = OpIAdd %6 %57 %28 4054 OpStore %8 %30 4055 OpBranch %15 4056 %15 = OpLabel 4057 %33 = OpIAdd %6 %54 %32 4058 OpStore %11 %33 4059 OpBranch %12 4060 %14 = OpLabel 4061 OpStore %34 %9 4062 OpBranch %35 4063 %35 = OpLabel 4064 %58 = OpPhi %6 %9 %14 %47 %38 4065 %55 = OpPhi %6 %9 %14 %49 %38 4066 OpLoopMerge %37 %38 None 4067 OpBranch %39 4068 %39 = OpLabel 4069 %41 = OpSLessThan %19 %55 %18 4070 OpBranchConditional %41 %36 %37 4071 %36 = OpLabel 4072 %44 = OpAccessChain %7 %42 %55 4073 %45 = OpLoad %6 %44 4074 %47 = OpIAdd %6 %58 %45 4075 OpStore %10 %47 4076 OpBranch %38 4077 %38 = OpLabel 4078 %49 = OpIAdd %6 %55 %32 4079 OpStore %34 %49 4080 OpBranch %35 4081 %37 = OpLabel 4082 %53 = OpIAdd %6 %57 %58 4083 OpStore %50 %53 4084 OpReturn 4085 OpFunctionEnd 4086 )"; 4087 4088 std::unique_ptr<IRContext> context = 4089 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 4090 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 4091 Module* module = context->module(); 4092 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 4093 << text << std::endl; 4094 Function& f = *module->begin(); 4095 4096 { 4097 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 4098 EXPECT_EQ(ld.NumLoops(), 2u); 4099 4100 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 4101 4102 LoopUtils utils_0(context.get(), loops[0]); 4103 utils_0.MakeLoopClosedSSA(); 4104 LoopUtils utils_1(context.get(), loops[1]); 4105 utils_1.MakeLoopClosedSSA(); 4106 4107 LoopFusion fusion(context.get(), loops[0], loops[1]); 4108 EXPECT_TRUE(fusion.AreCompatible()); 4109 EXPECT_TRUE(fusion.IsLegal()); 4110 4111 fusion.Fuse(); 4112 } 4113 4114 { 4115 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 4116 EXPECT_EQ(ld.NumLoops(), 1u); 4117 4118 std::string checks = R"( 4119 CHECK: [[SUM_0:%\w+]] = OpPhi 4120 CHECK-NEXT: [[SUM_1:%\w+]] = OpPhi 4121 CHECK-NEXT: [[PHI:%\w+]] = OpPhi 4122 CHECK-NEXT: OpLoopMerge 4123 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 4124 CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] 4125 CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[SUM_0]] [[LOAD_RES_0]] 4126 CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_0]] 4127 CHECK-NOT: OpPhi 4128 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 4129 CHECK-NEXT: [[LOAD_RES_1:%\w+]] = OpLoad {{%\w+}} [[LOAD_1]] 4130 CHECK-NEXT: [[ADD_RES_1:%\w+]] = OpIAdd {{%\w+}} [[SUM_1]] [[LOAD_RES_1]] 4131 CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_1]] 4132 )"; 4133 4134 Match(checks, context.get()); 4135 } 4136 } 4137 4138 /* 4139 Generated from the following GLSL + --eliminate-local-multi-store 4140 4141 #version 440 core 4142 void main() { 4143 int[10] a; 4144 int[10] b; 4145 4146 int sum_0 = 0; 4147 4148 // No loop-carried dependences, legal 4149 for (int i = 0; i < 10; i++) { 4150 sum_0 += a[i]; 4151 } 4152 for (int j = 0; j < 10; j++) { 4153 a[j] = b[j]; 4154 } 4155 } 4156 4157 */ 4158 TEST_F(FusionLegalTest, LoadStoreReductionAndNonLoopCarriedDependence) { 4159 std::string text = R"( 4160 OpCapability Shader 4161 %1 = OpExtInstImport "GLSL.std.450" 4162 OpMemoryModel Logical GLSL450 4163 OpEntryPoint Fragment %4 "main" 4164 OpExecutionMode %4 OriginUpperLeft 4165 OpSource GLSL 440 4166 OpName %4 "main" 4167 OpName %8 "sum_0" 4168 OpName %10 "i" 4169 OpName %24 "a" 4170 OpName %33 "j" 4171 OpName %42 "b" 4172 %2 = OpTypeVoid 4173 %3 = OpTypeFunction %2 4174 %6 = OpTypeInt 32 1 4175 %7 = OpTypePointer Function %6 4176 %9 = OpConstant %6 0 4177 %17 = OpConstant %6 10 4178 %18 = OpTypeBool 4179 %20 = OpTypeInt 32 0 4180 %21 = OpConstant %20 10 4181 %22 = OpTypeArray %6 %21 4182 %23 = OpTypePointer Function %22 4183 %31 = OpConstant %6 1 4184 %4 = OpFunction %2 None %3 4185 %5 = OpLabel 4186 %8 = OpVariable %7 Function 4187 %10 = OpVariable %7 Function 4188 %24 = OpVariable %23 Function 4189 %33 = OpVariable %7 Function 4190 %42 = OpVariable %23 Function 4191 OpStore %8 %9 4192 OpStore %10 %9 4193 OpBranch %11 4194 %11 = OpLabel 4195 %51 = OpPhi %6 %9 %5 %29 %14 4196 %49 = OpPhi %6 %9 %5 %32 %14 4197 OpLoopMerge %13 %14 None 4198 OpBranch %15 4199 %15 = OpLabel 4200 %19 = OpSLessThan %18 %49 %17 4201 OpBranchConditional %19 %12 %13 4202 %12 = OpLabel 4203 %26 = OpAccessChain %7 %24 %49 4204 %27 = OpLoad %6 %26 4205 %29 = OpIAdd %6 %51 %27 4206 OpStore %8 %29 4207 OpBranch %14 4208 %14 = OpLabel 4209 %32 = OpIAdd %6 %49 %31 4210 OpStore %10 %32 4211 OpBranch %11 4212 %13 = OpLabel 4213 OpStore %33 %9 4214 OpBranch %34 4215 %34 = OpLabel 4216 %50 = OpPhi %6 %9 %13 %48 %37 4217 OpLoopMerge %36 %37 None 4218 OpBranch %38 4219 %38 = OpLabel 4220 %40 = OpSLessThan %18 %50 %17 4221 OpBranchConditional %40 %35 %36 4222 %35 = OpLabel 4223 %44 = OpAccessChain %7 %42 %50 4224 %45 = OpLoad %6 %44 4225 %46 = OpAccessChain %7 %24 %50 4226 OpStore %46 %45 4227 OpBranch %37 4228 %37 = OpLabel 4229 %48 = OpIAdd %6 %50 %31 4230 OpStore %33 %48 4231 OpBranch %34 4232 %36 = OpLabel 4233 OpReturn 4234 OpFunctionEnd 4235 )"; 4236 4237 std::unique_ptr<IRContext> context = 4238 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 4239 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 4240 Module* module = context->module(); 4241 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 4242 << text << std::endl; 4243 Function& f = *module->begin(); 4244 4245 { 4246 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 4247 EXPECT_EQ(ld.NumLoops(), 2u); 4248 4249 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 4250 4251 LoopFusion fusion(context.get(), loops[0], loops[1]); 4252 EXPECT_TRUE(fusion.AreCompatible()); 4253 // TODO: Loop descriptor doesn't return induction variables but all OpPhi 4254 // in the header and LoopDependenceAnalysis falls over. 4255 // EXPECT_TRUE(fusion.IsLegal()); 4256 4257 // fusion.Fuse(); 4258 } 4259 4260 { 4261 // LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 4262 // EXPECT_EQ(ld.NumLoops(), 1u); 4263 4264 // std::string checks = R"( 4265 // CHECK: [[SUM_0:%\w+]] = OpPhi 4266 // CHECK-NEXT: [[PHI:%\w+]] = OpPhi 4267 // CHECK-NEXT: OpLoopMerge 4268 // CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 4269 // CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] 4270 // CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[SUM_0]] [[LOAD_RES_0]] 4271 // CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_0]] 4272 // CHECK-NOT: OpPhi 4273 // CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 4274 // CHECK-NEXT: [[LOAD_RES_1:%\w+]] = OpLoad {{%\w+}} [[LOAD_1]] 4275 // CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 4276 // CHECK-NEXT: OpStore [[STORE_1]] [[LOAD_RES_1]] 4277 // )"; 4278 4279 // Match(checks, context.get()); 4280 } 4281 } 4282 4283 /* 4284 Generated from the following GLSL + --eliminate-local-multi-store 4285 4286 #version 440 core 4287 int x; 4288 void main() { 4289 int[10] a; 4290 int[10] b; 4291 4292 // Legal. 4293 for (int i = 0; i < 10; i++) { 4294 x += a[i]; 4295 } 4296 for (int j = 0; j < 10; j++) { 4297 b[j] = b[j]+1; 4298 } 4299 } 4300 4301 */ 4302 TEST_F(FusionLegalTest, ReductionAndNonLoopCarriedDependence) { 4303 std::string text = R"( 4304 OpCapability Shader 4305 %1 = OpExtInstImport "GLSL.std.450" 4306 OpMemoryModel Logical GLSL450 4307 OpEntryPoint Fragment %4 "main" 4308 OpExecutionMode %4 OriginUpperLeft 4309 OpSource GLSL 440 4310 OpName %4 "main" 4311 OpName %8 "i" 4312 OpName %20 "x" 4313 OpName %25 "a" 4314 OpName %34 "j" 4315 OpName %42 "b" 4316 %2 = OpTypeVoid 4317 %3 = OpTypeFunction %2 4318 %6 = OpTypeInt 32 1 4319 %7 = OpTypePointer Function %6 4320 %9 = OpConstant %6 0 4321 %16 = OpConstant %6 10 4322 %17 = OpTypeBool 4323 %19 = OpTypePointer Private %6 4324 %20 = OpVariable %19 Private 4325 %21 = OpTypeInt 32 0 4326 %22 = OpConstant %21 10 4327 %23 = OpTypeArray %6 %22 4328 %24 = OpTypePointer Function %23 4329 %32 = OpConstant %6 1 4330 %4 = OpFunction %2 None %3 4331 %5 = OpLabel 4332 %8 = OpVariable %7 Function 4333 %25 = OpVariable %24 Function 4334 %34 = OpVariable %7 Function 4335 %42 = OpVariable %24 Function 4336 OpStore %8 %9 4337 OpBranch %10 4338 %10 = OpLabel 4339 %51 = OpPhi %6 %9 %5 %33 %13 4340 OpLoopMerge %12 %13 None 4341 OpBranch %14 4342 %14 = OpLabel 4343 %18 = OpSLessThan %17 %51 %16 4344 OpBranchConditional %18 %11 %12 4345 %11 = OpLabel 4346 %27 = OpAccessChain %7 %25 %51 4347 %28 = OpLoad %6 %27 4348 %29 = OpLoad %6 %20 4349 %30 = OpIAdd %6 %29 %28 4350 OpStore %20 %30 4351 OpBranch %13 4352 %13 = OpLabel 4353 %33 = OpIAdd %6 %51 %32 4354 OpStore %8 %33 4355 OpBranch %10 4356 %12 = OpLabel 4357 OpStore %34 %9 4358 OpBranch %35 4359 %35 = OpLabel 4360 %52 = OpPhi %6 %9 %12 %50 %38 4361 OpLoopMerge %37 %38 None 4362 OpBranch %39 4363 %39 = OpLabel 4364 %41 = OpSLessThan %17 %52 %16 4365 OpBranchConditional %41 %36 %37 4366 %36 = OpLabel 4367 %45 = OpAccessChain %7 %42 %52 4368 %46 = OpLoad %6 %45 4369 %47 = OpIAdd %6 %46 %32 4370 %48 = OpAccessChain %7 %42 %52 4371 OpStore %48 %47 4372 OpBranch %38 4373 %38 = OpLabel 4374 %50 = OpIAdd %6 %52 %32 4375 OpStore %34 %50 4376 OpBranch %35 4377 %37 = OpLabel 4378 OpReturn 4379 OpFunctionEnd 4380 )"; 4381 4382 std::unique_ptr<IRContext> context = 4383 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 4384 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 4385 Module* module = context->module(); 4386 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 4387 << text << std::endl; 4388 Function& f = *module->begin(); 4389 4390 { 4391 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 4392 EXPECT_EQ(ld.NumLoops(), 2u); 4393 4394 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 4395 4396 LoopFusion fusion(context.get(), loops[0], loops[1]); 4397 EXPECT_TRUE(fusion.AreCompatible()); 4398 EXPECT_TRUE(fusion.IsLegal()); 4399 4400 fusion.Fuse(); 4401 } 4402 4403 { 4404 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 4405 EXPECT_EQ(ld.NumLoops(), 1u); 4406 4407 std::string checks = R"( 4408 CHECK: OpName [[X:%\w+]] "x" 4409 CHECK: [[PHI:%\w+]] = OpPhi 4410 CHECK-NEXT: OpLoopMerge 4411 CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 4412 CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] 4413 CHECK-NEXT: [[X_LOAD:%\w+]] = OpLoad {{%\w+}} [[X]] 4414 CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[X_LOAD]] [[LOAD_RES_0]] 4415 CHECK-NEXT: OpStore [[X]] [[ADD_RES_0]] 4416 CHECK-NOT: OpPhi 4417 CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 4418 CHECK-NEXT: {{%\w+}} = OpLoad {{%\w+}} [[LOAD_1]] 4419 CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] 4420 CHECK-NEXT: OpStore [[STORE_1]] 4421 )"; 4422 4423 Match(checks, context.get()); 4424 } 4425 } 4426 4427 /* 4428 Generated from the following GLSL + --eliminate-local-multi-store 4429 4430 #version 440 core 4431 struct TestStruct { 4432 int[10] a; 4433 int b; 4434 }; 4435 4436 void main() { 4437 TestStruct test_0; 4438 TestStruct test_1; 4439 TestStruct test_2; 4440 4441 test_1.b = 2; 4442 4443 for (int i = 0; i < 10; i++) { 4444 test_0.a[i] = i; 4445 } 4446 for (int j = 0; j < 10; j++) { 4447 test_2 = test_1; 4448 } 4449 } 4450 4451 */ 4452 TEST_F(FusionLegalTest, ArrayInStruct) { 4453 std::string text = R"( 4454 OpCapability Shader 4455 %1 = OpExtInstImport "GLSL.std.450" 4456 OpMemoryModel Logical GLSL450 4457 OpEntryPoint Fragment %4 "main" 4458 OpExecutionMode %4 OriginUpperLeft 4459 OpSource GLSL 440 4460 OpName %4 "main" 4461 OpName %10 "TestStruct" 4462 OpMemberName %10 0 "a" 4463 OpMemberName %10 1 "b" 4464 OpName %12 "test_1" 4465 OpName %17 "i" 4466 OpName %28 "test_0" 4467 OpName %34 "j" 4468 OpName %42 "test_2" 4469 %2 = OpTypeVoid 4470 %3 = OpTypeFunction %2 4471 %6 = OpTypeInt 32 1 4472 %7 = OpTypeInt 32 0 4473 %8 = OpConstant %7 10 4474 %9 = OpTypeArray %6 %8 4475 %10 = OpTypeStruct %9 %6 4476 %11 = OpTypePointer Function %10 4477 %13 = OpConstant %6 1 4478 %14 = OpConstant %6 2 4479 %15 = OpTypePointer Function %6 4480 %18 = OpConstant %6 0 4481 %25 = OpConstant %6 10 4482 %26 = OpTypeBool 4483 %4 = OpFunction %2 None %3 4484 %5 = OpLabel 4485 %12 = OpVariable %11 Function 4486 %17 = OpVariable %15 Function 4487 %28 = OpVariable %11 Function 4488 %34 = OpVariable %15 Function 4489 %42 = OpVariable %11 Function 4490 %16 = OpAccessChain %15 %12 %13 4491 OpStore %16 %14 4492 OpStore %17 %18 4493 OpBranch %19 4494 %19 = OpLabel 4495 %46 = OpPhi %6 %18 %5 %33 %22 4496 OpLoopMerge %21 %22 None 4497 OpBranch %23 4498 %23 = OpLabel 4499 %27 = OpSLessThan %26 %46 %25 4500 OpBranchConditional %27 %20 %21 4501 %20 = OpLabel 4502 %31 = OpAccessChain %15 %28 %18 %46 4503 OpStore %31 %46 4504 OpBranch %22 4505 %22 = OpLabel 4506 %33 = OpIAdd %6 %46 %13 4507 OpStore %17 %33 4508 OpBranch %19 4509 %21 = OpLabel 4510 OpStore %34 %18 4511 OpBranch %35 4512 %35 = OpLabel 4513 %47 = OpPhi %6 %18 %21 %45 %38 4514 OpLoopMerge %37 %38 None 4515 OpBranch %39 4516 %39 = OpLabel 4517 %41 = OpSLessThan %26 %47 %25 4518 OpBranchConditional %41 %36 %37 4519 %36 = OpLabel 4520 %43 = OpLoad %10 %12 4521 OpStore %42 %43 4522 OpBranch %38 4523 %38 = OpLabel 4524 %45 = OpIAdd %6 %47 %13 4525 OpStore %34 %45 4526 OpBranch %35 4527 %37 = OpLabel 4528 OpReturn 4529 OpFunctionEnd 4530 )"; 4531 4532 std::unique_ptr<IRContext> context = 4533 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, 4534 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); 4535 Module* module = context->module(); 4536 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" 4537 << text << std::endl; 4538 Function& f = *module->begin(); 4539 4540 { 4541 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 4542 EXPECT_EQ(ld.NumLoops(), 2u); 4543 4544 auto loops = ld.GetLoopsInBinaryLayoutOrder(); 4545 4546 LoopFusion fusion(context.get(), loops[0], loops[1]); 4547 EXPECT_TRUE(fusion.AreCompatible()); 4548 EXPECT_TRUE(fusion.IsLegal()); 4549 4550 fusion.Fuse(); 4551 } 4552 4553 { 4554 LoopDescriptor& ld = *context->GetLoopDescriptor(&f); 4555 EXPECT_EQ(ld.NumLoops(), 1u); 4556 4557 // clang-format off 4558 std::string checks = R"( 4559 CHECK: OpName [[TEST_1:%\w+]] "test_1" 4560 CHECK: OpName [[TEST_0:%\w+]] "test_0" 4561 CHECK: OpName [[TEST_2:%\w+]] "test_2" 4562 CHECK: [[PHI:%\w+]] = OpPhi 4563 CHECK-NEXT: OpLoopMerge 4564 CHECK: [[TEST_0_STORE:%\w+]] = OpAccessChain {{%\w+}} [[TEST_0]] {{%\w+}} {{%\w+}} 4565 CHECK-NEXT: OpStore [[TEST_0_STORE]] [[PHI]] 4566 CHECK-NOT: OpPhi 4567 CHECK: [[TEST_1_LOAD:%\w+]] = OpLoad {{%\w+}} [[TEST_1]] 4568 CHECK: OpStore [[TEST_2]] [[TEST_1_LOAD]] 4569 )"; 4570 // clang-format on 4571 4572 Match(checks, context.get()); 4573 } 4574 } 4575 4576 } // namespace 4577 } // namespace opt 4578 } // namespace spvtools 4579