| // Copyright (c) 2018 Google LLC. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "source/opt/loop_fission.h" | |
| #include <memory> | |
| #include <vector> | |
| #include "gmock/gmock.h" | |
| #include "source/opt/loop_utils.h" | |
| #include "test/opt/assembly_builder.h" | |
| #include "test/opt/function_utils.h" | |
| #include "test/opt/pass_fixture.h" | |
| #include "test/opt/pass_utils.h" | |
| namespace spvtools { | |
| namespace opt { | |
| namespace { | |
| using ::testing::UnorderedElementsAre; | |
| using FissionClassTest = PassTest<::testing::Test>; | |
| /* | |
| Generated from the following GLSL | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| A[i] = B[i]; | |
| B[i] = A[i]; | |
| } | |
| } | |
| Result should be equivalent to: | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| A[i] = B[i]; | |
| } | |
| for (int i = 0; i < 10; i++) { | |
| B[i] = A[i]; | |
| } | |
| } | |
| */ | |
| TEST_F(FissionClassTest, SimpleFission) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "A" | |
| OpName %5 "B" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpTypeFloat 32 | |
| %14 = OpTypeInt 32 0 | |
| %15 = OpConstant %14 10 | |
| %16 = OpTypeArray %13 %15 | |
| %17 = OpTypePointer Function %16 | |
| %18 = OpTypePointer Function %13 | |
| %19 = OpConstant %8 1 | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %17 Function | |
| %5 = OpVariable %17 Function | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %20 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpSLessThan %12 %22 %11 | |
| OpBranchConditional %27 %28 %25 | |
| %28 = OpLabel | |
| %29 = OpAccessChain %18 %5 %22 | |
| %30 = OpLoad %13 %29 | |
| %31 = OpAccessChain %18 %4 %22 | |
| OpStore %31 %30 | |
| %32 = OpAccessChain %18 %4 %22 | |
| %33 = OpLoad %13 %32 | |
| %34 = OpAccessChain %18 %5 %22 | |
| OpStore %34 %33 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %23 = OpIAdd %8 %22 %19 | |
| OpBranch %21 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "A" | |
| OpName %5 "B" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpTypeFloat 32 | |
| %14 = OpTypeInt 32 0 | |
| %15 = OpConstant %14 10 | |
| %16 = OpTypeArray %13 %15 | |
| %17 = OpTypePointer Function %16 | |
| %18 = OpTypePointer Function %13 | |
| %19 = OpConstant %8 1 | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %17 Function | |
| %5 = OpVariable %17 Function | |
| OpBranch %35 | |
| %35 = OpLabel | |
| %36 = OpPhi %8 %10 %20 %47 %46 | |
| OpLoopMerge %48 %46 None | |
| OpBranch %37 | |
| %37 = OpLabel | |
| %38 = OpSLessThan %12 %36 %11 | |
| OpBranchConditional %38 %39 %48 | |
| %39 = OpLabel | |
| %40 = OpAccessChain %18 %5 %36 | |
| %41 = OpLoad %13 %40 | |
| %42 = OpAccessChain %18 %4 %36 | |
| OpStore %42 %41 | |
| OpBranch %46 | |
| %46 = OpLabel | |
| %47 = OpIAdd %8 %36 %19 | |
| OpBranch %35 | |
| %48 = OpLabel | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %48 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpSLessThan %12 %22 %11 | |
| OpBranchConditional %27 %28 %25 | |
| %28 = OpLabel | |
| %32 = OpAccessChain %18 %4 %22 | |
| %33 = OpLoad %13 %32 | |
| %34 = OpAccessChain %18 %5 %22 | |
| OpStore %34 %33 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %23 = OpIAdd %8 %22 %19 | |
| OpBranch %21 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| // Check that the loop will NOT be split when provided with a pass-through | |
| // register pressure functor which just returns false. | |
| SinglePassRunAndCheck<LoopFissionPass>( | |
| source, source, true, | |
| [](const RegisterLiveness::RegionRegisterLiveness&) { return false; }); | |
| } | |
| /* | |
| Generated from the following GLSL | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| A[i] = B[i]; | |
| B[i] = A[i+1]; | |
| } | |
| } | |
| This loop should not be split, as the i+1 dependence would be broken by | |
| splitting the loop. | |
| */ | |
| TEST_F(FissionClassTest, FissionInterdependency) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "A" | |
| OpName %5 "B" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpTypeFloat 32 | |
| %14 = OpTypeInt 32 0 | |
| %15 = OpConstant %14 10 | |
| %16 = OpTypeArray %13 %15 | |
| %17 = OpTypePointer Function %16 | |
| %18 = OpTypePointer Function %13 | |
| %19 = OpConstant %8 1 | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %17 Function | |
| %5 = OpVariable %17 Function | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %20 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpSLessThan %12 %22 %11 | |
| OpBranchConditional %27 %28 %25 | |
| %28 = OpLabel | |
| %29 = OpAccessChain %18 %5 %22 | |
| %30 = OpLoad %13 %29 | |
| %31 = OpAccessChain %18 %4 %22 | |
| OpStore %31 %30 | |
| %32 = OpIAdd %8 %22 %19 | |
| %33 = OpAccessChain %18 %4 %32 | |
| %34 = OpLoad %13 %33 | |
| %35 = OpAccessChain %18 %5 %22 | |
| OpStore %35 %34 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %23 = OpIAdd %8 %22 %19 | |
| OpBranch %21 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, source, true); | |
| } | |
| /* | |
| Generated from the following GLSL | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| A[i] = B[i]; | |
| B[i+1] = A[i]; | |
| } | |
| } | |
| This should not be split as the load B[i] is dependent on the store B[i+1] | |
| */ | |
| TEST_F(FissionClassTest, FissionInterdependency2) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "A" | |
| OpName %5 "B" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpTypeFloat 32 | |
| %14 = OpTypeInt 32 0 | |
| %15 = OpConstant %14 10 | |
| %16 = OpTypeArray %13 %15 | |
| %17 = OpTypePointer Function %16 | |
| %18 = OpTypePointer Function %13 | |
| %19 = OpConstant %8 1 | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %17 Function | |
| %5 = OpVariable %17 Function | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %20 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpSLessThan %12 %22 %11 | |
| OpBranchConditional %27 %28 %25 | |
| %28 = OpLabel | |
| %29 = OpAccessChain %18 %5 %22 | |
| %30 = OpLoad %13 %29 | |
| %31 = OpAccessChain %18 %4 %22 | |
| OpStore %31 %30 | |
| %32 = OpIAdd %8 %22 %19 | |
| %33 = OpAccessChain %18 %4 %22 | |
| %34 = OpLoad %13 %33 | |
| %35 = OpAccessChain %18 %5 %32 | |
| OpStore %35 %34 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %23 = OpIAdd %8 %22 %19 | |
| OpBranch %21 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, source, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| float C[10] | |
| float D[10] | |
| for (int i = 0; i < 10; i++) { | |
| A[i] = B[i]; | |
| B[i] = A[i]; | |
| C[i] = D[i]; | |
| D[i] = C[i]; | |
| } | |
| } | |
| This should be split into the equivalent of: | |
| for (int i = 0; i < 10; i++) { | |
| A[i] = B[i]; | |
| B[i] = A[i]; | |
| } | |
| for (int i = 0; i < 10; i++) { | |
| C[i] = D[i]; | |
| D[i] = C[i]; | |
| } | |
| We then check that the loop is broken into four for loops like so, if the pass | |
| is run twice: | |
| for (int i = 0; i < 10; i++) | |
| A[i] = B[i]; | |
| for (int i = 0; i < 10; i++) | |
| B[i] = A[i]; | |
| for (int i = 0; i < 10; i++) | |
| C[i] = D[i]; | |
| for (int i = 0; i < 10; i++) | |
| D[i] = C[i]; | |
| */ | |
| TEST_F(FissionClassTest, FissionMultipleLoadStores) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "A" | |
| OpName %5 "B" | |
| OpName %6 "C" | |
| OpName %7 "D" | |
| %8 = OpTypeVoid | |
| %9 = OpTypeFunction %8 | |
| %10 = OpTypeInt 32 1 | |
| %11 = OpTypePointer Function %10 | |
| %12 = OpConstant %10 0 | |
| %13 = OpConstant %10 10 | |
| %14 = OpTypeBool | |
| %15 = OpTypeFloat 32 | |
| %16 = OpTypeInt 32 0 | |
| %17 = OpConstant %16 10 | |
| %18 = OpTypeArray %15 %17 | |
| %19 = OpTypePointer Function %18 | |
| %20 = OpTypePointer Function %15 | |
| %21 = OpConstant %10 1 | |
| %2 = OpFunction %8 None %9 | |
| %22 = OpLabel | |
| %3 = OpVariable %11 Function | |
| %4 = OpVariable %19 Function | |
| %5 = OpVariable %19 Function | |
| %6 = OpVariable %19 Function | |
| %7 = OpVariable %19 Function | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %10 %12 %22 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %14 %24 %13 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| %31 = OpAccessChain %20 %5 %24 | |
| %32 = OpLoad %15 %31 | |
| %33 = OpAccessChain %20 %4 %24 | |
| OpStore %33 %32 | |
| %34 = OpAccessChain %20 %4 %24 | |
| %35 = OpLoad %15 %34 | |
| %36 = OpAccessChain %20 %5 %24 | |
| OpStore %36 %35 | |
| %37 = OpAccessChain %20 %7 %24 | |
| %38 = OpLoad %15 %37 | |
| %39 = OpAccessChain %20 %6 %24 | |
| OpStore %39 %38 | |
| %40 = OpAccessChain %20 %6 %24 | |
| %41 = OpLoad %15 %40 | |
| %42 = OpAccessChain %20 %7 %24 | |
| OpStore %42 %41 | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %10 %24 %21 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "A" | |
| OpName %5 "B" | |
| OpName %6 "C" | |
| OpName %7 "D" | |
| %8 = OpTypeVoid | |
| %9 = OpTypeFunction %8 | |
| %10 = OpTypeInt 32 1 | |
| %11 = OpTypePointer Function %10 | |
| %12 = OpConstant %10 0 | |
| %13 = OpConstant %10 10 | |
| %14 = OpTypeBool | |
| %15 = OpTypeFloat 32 | |
| %16 = OpTypeInt 32 0 | |
| %17 = OpConstant %16 10 | |
| %18 = OpTypeArray %15 %17 | |
| %19 = OpTypePointer Function %18 | |
| %20 = OpTypePointer Function %15 | |
| %21 = OpConstant %10 1 | |
| %2 = OpFunction %8 None %9 | |
| %22 = OpLabel | |
| %3 = OpVariable %11 Function | |
| %4 = OpVariable %19 Function | |
| %5 = OpVariable %19 Function | |
| %6 = OpVariable %19 Function | |
| %7 = OpVariable %19 Function | |
| OpBranch %43 | |
| %43 = OpLabel | |
| %44 = OpPhi %10 %12 %22 %61 %60 | |
| OpLoopMerge %62 %60 None | |
| OpBranch %45 | |
| %45 = OpLabel | |
| %46 = OpSLessThan %14 %44 %13 | |
| OpBranchConditional %46 %47 %62 | |
| %47 = OpLabel | |
| %48 = OpAccessChain %20 %5 %44 | |
| %49 = OpLoad %15 %48 | |
| %50 = OpAccessChain %20 %4 %44 | |
| OpStore %50 %49 | |
| %51 = OpAccessChain %20 %4 %44 | |
| %52 = OpLoad %15 %51 | |
| %53 = OpAccessChain %20 %5 %44 | |
| OpStore %53 %52 | |
| OpBranch %60 | |
| %60 = OpLabel | |
| %61 = OpIAdd %10 %44 %21 | |
| OpBranch %43 | |
| %62 = OpLabel | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %10 %12 %62 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %14 %24 %13 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| %37 = OpAccessChain %20 %7 %24 | |
| %38 = OpLoad %15 %37 | |
| %39 = OpAccessChain %20 %6 %24 | |
| OpStore %39 %38 | |
| %40 = OpAccessChain %20 %6 %24 | |
| %41 = OpLoad %15 %40 | |
| %42 = OpAccessChain %20 %7 %24 | |
| OpStore %42 %41 | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %10 %24 %21 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected_multiple_passes = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "A" | |
| OpName %5 "B" | |
| OpName %6 "C" | |
| OpName %7 "D" | |
| %8 = OpTypeVoid | |
| %9 = OpTypeFunction %8 | |
| %10 = OpTypeInt 32 1 | |
| %11 = OpTypePointer Function %10 | |
| %12 = OpConstant %10 0 | |
| %13 = OpConstant %10 10 | |
| %14 = OpTypeBool | |
| %15 = OpTypeFloat 32 | |
| %16 = OpTypeInt 32 0 | |
| %17 = OpConstant %16 10 | |
| %18 = OpTypeArray %15 %17 | |
| %19 = OpTypePointer Function %18 | |
| %20 = OpTypePointer Function %15 | |
| %21 = OpConstant %10 1 | |
| %2 = OpFunction %8 None %9 | |
| %22 = OpLabel | |
| %3 = OpVariable %11 Function | |
| %4 = OpVariable %19 Function | |
| %5 = OpVariable %19 Function | |
| %6 = OpVariable %19 Function | |
| %7 = OpVariable %19 Function | |
| OpBranch %63 | |
| %63 = OpLabel | |
| %64 = OpPhi %10 %12 %22 %75 %74 | |
| OpLoopMerge %76 %74 None | |
| OpBranch %65 | |
| %65 = OpLabel | |
| %66 = OpSLessThan %14 %64 %13 | |
| OpBranchConditional %66 %67 %76 | |
| %67 = OpLabel | |
| %68 = OpAccessChain %20 %5 %64 | |
| %69 = OpLoad %15 %68 | |
| %70 = OpAccessChain %20 %4 %64 | |
| OpStore %70 %69 | |
| OpBranch %74 | |
| %74 = OpLabel | |
| %75 = OpIAdd %10 %64 %21 | |
| OpBranch %63 | |
| %76 = OpLabel | |
| OpBranch %43 | |
| %43 = OpLabel | |
| %44 = OpPhi %10 %12 %76 %61 %60 | |
| OpLoopMerge %62 %60 None | |
| OpBranch %45 | |
| %45 = OpLabel | |
| %46 = OpSLessThan %14 %44 %13 | |
| OpBranchConditional %46 %47 %62 | |
| %47 = OpLabel | |
| %51 = OpAccessChain %20 %4 %44 | |
| %52 = OpLoad %15 %51 | |
| %53 = OpAccessChain %20 %5 %44 | |
| OpStore %53 %52 | |
| OpBranch %60 | |
| %60 = OpLabel | |
| %61 = OpIAdd %10 %44 %21 | |
| OpBranch %43 | |
| %62 = OpLabel | |
| OpBranch %77 | |
| %77 = OpLabel | |
| %78 = OpPhi %10 %12 %62 %89 %88 | |
| OpLoopMerge %90 %88 None | |
| OpBranch %79 | |
| %79 = OpLabel | |
| %80 = OpSLessThan %14 %78 %13 | |
| OpBranchConditional %80 %81 %90 | |
| %81 = OpLabel | |
| %82 = OpAccessChain %20 %7 %78 | |
| %83 = OpLoad %15 %82 | |
| %84 = OpAccessChain %20 %6 %78 | |
| OpStore %84 %83 | |
| OpBranch %88 | |
| %88 = OpLabel | |
| %89 = OpIAdd %10 %78 %21 | |
| OpBranch %77 | |
| %90 = OpLabel | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %10 %12 %90 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %14 %24 %13 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| %40 = OpAccessChain %20 %6 %24 | |
| %41 = OpLoad %15 %40 | |
| %42 = OpAccessChain %20 %7 %24 | |
| OpStore %42 %41 | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %10 %24 %21 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| // By passing 1 as argument we are using the constructor which makes the | |
| // criteria to split the loop be if the registers in the loop exceede 1. By | |
| // using this constructor we are also enabling multiple passes (disabled by | |
| // default). | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected_multiple_passes, true, | |
| 1); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| int accumulator = 0; | |
| float X[10]; | |
| float Y[10]; | |
| for (int i = 0; i < 10; i++) { | |
| X[i] = Y[i]; | |
| Y[i] = X[i]; | |
| accumulator += i; | |
| } | |
| } | |
| This should be split into the equivalent of: | |
| #version 430 | |
| void main(void) { | |
| int accumulator = 0; | |
| float X[10]; | |
| float Y[10]; | |
| for (int i = 0; i < 10; i++) { | |
| X[i] = Y[i]; | |
| } | |
| for (int i = 0; i < 10; i++) { | |
| Y[i] = X[i]; | |
| accumulator += i; | |
| } | |
| } | |
| */ | |
| TEST_F(FissionClassTest, FissionWithAccumulator) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "accumulator" | |
| OpName %4 "i" | |
| OpName %5 "X" | |
| OpName %6 "Y" | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypePointer Function %17 | |
| %19 = OpTypePointer Function %14 | |
| %20 = OpConstant %9 1 | |
| %2 = OpFunction %7 None %8 | |
| %21 = OpLabel | |
| %3 = OpVariable %10 Function | |
| %4 = OpVariable %10 Function | |
| %5 = OpVariable %18 Function | |
| %6 = OpVariable %18 Function | |
| OpBranch %22 | |
| %22 = OpLabel | |
| %23 = OpPhi %9 %11 %21 %24 %25 | |
| %26 = OpPhi %9 %11 %21 %27 %25 | |
| OpLoopMerge %28 %25 None | |
| OpBranch %29 | |
| %29 = OpLabel | |
| %30 = OpSLessThan %13 %26 %12 | |
| OpBranchConditional %30 %31 %28 | |
| %31 = OpLabel | |
| %32 = OpAccessChain %19 %6 %26 | |
| %33 = OpLoad %14 %32 | |
| %34 = OpAccessChain %19 %5 %26 | |
| OpStore %34 %33 | |
| %35 = OpAccessChain %19 %5 %26 | |
| %36 = OpLoad %14 %35 | |
| %37 = OpAccessChain %19 %6 %26 | |
| OpStore %37 %36 | |
| %24 = OpIAdd %9 %23 %26 | |
| OpBranch %25 | |
| %25 = OpLabel | |
| %27 = OpIAdd %9 %26 %20 | |
| OpBranch %22 | |
| %28 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "accumulator" | |
| OpName %4 "i" | |
| OpName %5 "X" | |
| OpName %6 "Y" | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypePointer Function %17 | |
| %19 = OpTypePointer Function %14 | |
| %20 = OpConstant %9 1 | |
| %2 = OpFunction %7 None %8 | |
| %21 = OpLabel | |
| %3 = OpVariable %10 Function | |
| %4 = OpVariable %10 Function | |
| %5 = OpVariable %18 Function | |
| %6 = OpVariable %18 Function | |
| OpBranch %38 | |
| %38 = OpLabel | |
| %40 = OpPhi %9 %11 %21 %52 %51 | |
| OpLoopMerge %53 %51 None | |
| OpBranch %41 | |
| %41 = OpLabel | |
| %42 = OpSLessThan %13 %40 %12 | |
| OpBranchConditional %42 %43 %53 | |
| %43 = OpLabel | |
| %44 = OpAccessChain %19 %6 %40 | |
| %45 = OpLoad %14 %44 | |
| %46 = OpAccessChain %19 %5 %40 | |
| OpStore %46 %45 | |
| OpBranch %51 | |
| %51 = OpLabel | |
| %52 = OpIAdd %9 %40 %20 | |
| OpBranch %38 | |
| %53 = OpLabel | |
| OpBranch %22 | |
| %22 = OpLabel | |
| %23 = OpPhi %9 %11 %53 %24 %25 | |
| %26 = OpPhi %9 %11 %53 %27 %25 | |
| OpLoopMerge %28 %25 None | |
| OpBranch %29 | |
| %29 = OpLabel | |
| %30 = OpSLessThan %13 %26 %12 | |
| OpBranchConditional %30 %31 %28 | |
| %31 = OpLabel | |
| %35 = OpAccessChain %19 %5 %26 | |
| %36 = OpLoad %14 %35 | |
| %37 = OpAccessChain %19 %6 %26 | |
| OpStore %37 %36 | |
| %24 = OpIAdd %9 %23 %26 | |
| OpBranch %25 | |
| %25 = OpLabel | |
| %27 = OpIAdd %9 %26 %20 | |
| OpBranch %22 | |
| %28 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| } | |
| /* | |
| Generated from the following glsl: | |
| #version 430 | |
| layout(location=0) out float x; | |
| layout(location=1) out float y; | |
| void main(void) { | |
| float accumulator_1 = 0; | |
| float accumulator_2 = 0; | |
| for (int i = 0; i < 10; i++) { | |
| accumulator_1 += i; | |
| accumulator_2 += i; | |
| } | |
| x = accumulator_1; | |
| y = accumulator_2; | |
| } | |
| Should be split into equivalent of: | |
| void main(void) { | |
| float accumulator_1 = 0; | |
| float accumulator_2 = 0; | |
| for (int i = 0; i < 10; i++) { | |
| accumulator_1 += i; | |
| } | |
| for (int i = 0; i < 10; i++) { | |
| accumulator_2 += i; | |
| } | |
| x = accumulator_1; | |
| y = accumulator_2; | |
| } | |
| */ | |
| TEST_F(FissionClassTest, FissionWithPhisUsedOutwithLoop) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" %3 %4 | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %5 "accumulator_1" | |
| OpName %6 "accumulator_2" | |
| OpName %7 "i" | |
| OpName %3 "x" | |
| OpName %4 "y" | |
| OpDecorate %3 Location 0 | |
| OpDecorate %4 Location 1 | |
| %8 = OpTypeVoid | |
| %9 = OpTypeFunction %8 | |
| %10 = OpTypeFloat 32 | |
| %11 = OpTypePointer Function %10 | |
| %12 = OpConstant %10 0 | |
| %13 = OpTypeInt 32 1 | |
| %14 = OpTypePointer Function %13 | |
| %15 = OpConstant %13 0 | |
| %16 = OpConstant %13 10 | |
| %17 = OpTypeBool | |
| %18 = OpConstant %13 1 | |
| %19 = OpTypePointer Output %10 | |
| %3 = OpVariable %19 Output | |
| %4 = OpVariable %19 Output | |
| %2 = OpFunction %8 None %9 | |
| %20 = OpLabel | |
| %5 = OpVariable %11 Function | |
| %6 = OpVariable %11 Function | |
| %7 = OpVariable %14 Function | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %10 %12 %20 %23 %24 | |
| %25 = OpPhi %10 %12 %20 %26 %24 | |
| %27 = OpPhi %13 %15 %20 %28 %24 | |
| OpLoopMerge %29 %24 None | |
| OpBranch %30 | |
| %30 = OpLabel | |
| %31 = OpSLessThan %17 %27 %16 | |
| OpBranchConditional %31 %32 %29 | |
| %32 = OpLabel | |
| %33 = OpConvertSToF %10 %27 | |
| %26 = OpFAdd %10 %25 %33 | |
| %34 = OpConvertSToF %10 %27 | |
| %23 = OpFAdd %10 %22 %34 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %28 = OpIAdd %13 %27 %18 | |
| OpStore %7 %28 | |
| OpBranch %21 | |
| %29 = OpLabel | |
| OpStore %3 %25 | |
| OpStore %4 %22 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" %3 %4 | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %5 "accumulator_1" | |
| OpName %6 "accumulator_2" | |
| OpName %7 "i" | |
| OpName %3 "x" | |
| OpName %4 "y" | |
| OpDecorate %3 Location 0 | |
| OpDecorate %4 Location 1 | |
| %8 = OpTypeVoid | |
| %9 = OpTypeFunction %8 | |
| %10 = OpTypeFloat 32 | |
| %11 = OpTypePointer Function %10 | |
| %12 = OpConstant %10 0 | |
| %13 = OpTypeInt 32 1 | |
| %14 = OpTypePointer Function %13 | |
| %15 = OpConstant %13 0 | |
| %16 = OpConstant %13 10 | |
| %17 = OpTypeBool | |
| %18 = OpConstant %13 1 | |
| %19 = OpTypePointer Output %10 | |
| %3 = OpVariable %19 Output | |
| %4 = OpVariable %19 Output | |
| %2 = OpFunction %8 None %9 | |
| %20 = OpLabel | |
| %5 = OpVariable %11 Function | |
| %6 = OpVariable %11 Function | |
| %7 = OpVariable %14 Function | |
| OpBranch %35 | |
| %35 = OpLabel | |
| %37 = OpPhi %10 %12 %20 %43 %46 | |
| %38 = OpPhi %13 %15 %20 %47 %46 | |
| OpLoopMerge %48 %46 None | |
| OpBranch %39 | |
| %39 = OpLabel | |
| %40 = OpSLessThan %17 %38 %16 | |
| OpBranchConditional %40 %41 %48 | |
| %41 = OpLabel | |
| %42 = OpConvertSToF %10 %38 | |
| %43 = OpFAdd %10 %37 %42 | |
| OpBranch %46 | |
| %46 = OpLabel | |
| %47 = OpIAdd %13 %38 %18 | |
| OpStore %7 %47 | |
| OpBranch %35 | |
| %48 = OpLabel | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %10 %12 %48 %23 %24 | |
| %27 = OpPhi %13 %15 %48 %28 %24 | |
| OpLoopMerge %29 %24 None | |
| OpBranch %30 | |
| %30 = OpLabel | |
| %31 = OpSLessThan %17 %27 %16 | |
| OpBranchConditional %31 %32 %29 | |
| %32 = OpLabel | |
| %34 = OpConvertSToF %10 %27 | |
| %23 = OpFAdd %10 %22 %34 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %28 = OpIAdd %13 %27 %18 | |
| OpStore %7 %28 | |
| OpBranch %21 | |
| %29 = OpLabel | |
| OpStore %3 %37 | |
| OpStore %4 %22 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10][10]; | |
| float B[10][10]; | |
| for (int i = 0; i < 10; i++) { | |
| for (int j = 0; j < 10; j++) { | |
| A[i][j] = B[i][j]; | |
| B[i][j] = A[i][j]; | |
| } | |
| } | |
| } | |
| Should be split into equivalent of: | |
| #version 430 | |
| void main(void) { | |
| float A[10][10]; | |
| float B[10][10]; | |
| for (int i = 0; i < 10; i++) { | |
| for (int j = 0; j < 10; j++) { | |
| A[i][j] = B[i][j]; | |
| } | |
| for (int j = 0; j < 10; j++) { | |
| B[i][j] = A[i][j]; | |
| } | |
| } | |
| } | |
| */ | |
| TEST_F(FissionClassTest, FissionNested) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "j" | |
| OpName %5 "A" | |
| OpName %6 "B" | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypeArray %17 %16 | |
| %19 = OpTypePointer Function %18 | |
| %20 = OpTypePointer Function %14 | |
| %21 = OpConstant %9 1 | |
| %2 = OpFunction %7 None %8 | |
| %22 = OpLabel | |
| %3 = OpVariable %10 Function | |
| %4 = OpVariable %10 Function | |
| %5 = OpVariable %19 Function | |
| %6 = OpVariable %19 Function | |
| OpStore %3 %11 | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %9 %11 %22 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %13 %24 %12 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| OpStore %4 %11 | |
| OpBranch %31 | |
| %31 = OpLabel | |
| %32 = OpPhi %9 %11 %30 %33 %34 | |
| OpLoopMerge %35 %34 None | |
| OpBranch %36 | |
| %36 = OpLabel | |
| %37 = OpSLessThan %13 %32 %12 | |
| OpBranchConditional %37 %38 %35 | |
| %38 = OpLabel | |
| %39 = OpAccessChain %20 %6 %24 %32 | |
| %40 = OpLoad %14 %39 | |
| %41 = OpAccessChain %20 %5 %24 %32 | |
| OpStore %41 %40 | |
| %42 = OpAccessChain %20 %5 %24 %32 | |
| %43 = OpLoad %14 %42 | |
| %44 = OpAccessChain %20 %6 %24 %32 | |
| OpStore %44 %43 | |
| OpBranch %34 | |
| %34 = OpLabel | |
| %33 = OpIAdd %9 %32 %21 | |
| OpStore %4 %33 | |
| OpBranch %31 | |
| %35 = OpLabel | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %9 %24 %21 | |
| OpStore %3 %25 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "j" | |
| OpName %5 "A" | |
| OpName %6 "B" | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypeArray %17 %16 | |
| %19 = OpTypePointer Function %18 | |
| %20 = OpTypePointer Function %14 | |
| %21 = OpConstant %9 1 | |
| %2 = OpFunction %7 None %8 | |
| %22 = OpLabel | |
| %3 = OpVariable %10 Function | |
| %4 = OpVariable %10 Function | |
| %5 = OpVariable %19 Function | |
| %6 = OpVariable %19 Function | |
| OpStore %3 %11 | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %9 %11 %22 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %13 %24 %12 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| OpStore %4 %11 | |
| OpBranch %45 | |
| %45 = OpLabel | |
| %46 = OpPhi %9 %11 %30 %57 %56 | |
| OpLoopMerge %58 %56 None | |
| OpBranch %47 | |
| %47 = OpLabel | |
| %48 = OpSLessThan %13 %46 %12 | |
| OpBranchConditional %48 %49 %58 | |
| %49 = OpLabel | |
| %50 = OpAccessChain %20 %6 %24 %46 | |
| %51 = OpLoad %14 %50 | |
| %52 = OpAccessChain %20 %5 %24 %46 | |
| OpStore %52 %51 | |
| OpBranch %56 | |
| %56 = OpLabel | |
| %57 = OpIAdd %9 %46 %21 | |
| OpStore %4 %57 | |
| OpBranch %45 | |
| %58 = OpLabel | |
| OpBranch %31 | |
| %31 = OpLabel | |
| %32 = OpPhi %9 %11 %58 %33 %34 | |
| OpLoopMerge %35 %34 None | |
| OpBranch %36 | |
| %36 = OpLabel | |
| %37 = OpSLessThan %13 %32 %12 | |
| OpBranchConditional %37 %38 %35 | |
| %38 = OpLabel | |
| %42 = OpAccessChain %20 %5 %24 %32 | |
| %43 = OpLoad %14 %42 | |
| %44 = OpAccessChain %20 %6 %24 %32 | |
| OpStore %44 %43 | |
| OpBranch %34 | |
| %34 = OpLabel | |
| %33 = OpIAdd %9 %32 %21 | |
| OpStore %4 %33 | |
| OpBranch %31 | |
| %35 = OpLabel | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %9 %24 %21 | |
| OpStore %3 %25 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| int accumulator = 0; | |
| float A[10]; | |
| float B[10]; | |
| float C[10]; | |
| for (int i = 0; i < 10; i++) { | |
| int c = C[i]; | |
| A[i] = B[i]; | |
| B[i] = A[i] + c; | |
| } | |
| } | |
| This loop should not be split as we would have to break the order of the loads | |
| to do so. It would be grouped into two sets: | |
| 1 | |
| int c = C[i]; | |
| B[i] = A[i] + c; | |
| 2 | |
| A[i] = B[i]; | |
| To keep the load C[i] in the same order we would need to put B[i] ahead of that | |
| */ | |
| TEST_F(FissionClassTest, FissionLoad) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "c" | |
| OpName %5 "C" | |
| OpName %6 "A" | |
| OpName %7 "B" | |
| %8 = OpTypeVoid | |
| %9 = OpTypeFunction %8 | |
| %10 = OpTypeInt 32 1 | |
| %11 = OpTypePointer Function %10 | |
| %12 = OpConstant %10 0 | |
| %13 = OpConstant %10 10 | |
| %14 = OpTypeBool | |
| %15 = OpTypeFloat 32 | |
| %16 = OpTypePointer Function %15 | |
| %17 = OpTypeInt 32 0 | |
| %18 = OpConstant %17 10 | |
| %19 = OpTypeArray %15 %18 | |
| %20 = OpTypePointer Function %19 | |
| %21 = OpConstant %10 1 | |
| %2 = OpFunction %8 None %9 | |
| %22 = OpLabel | |
| %3 = OpVariable %11 Function | |
| %4 = OpVariable %16 Function | |
| %5 = OpVariable %20 Function | |
| %6 = OpVariable %20 Function | |
| %7 = OpVariable %20 Function | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %10 %12 %22 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %14 %24 %13 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| %31 = OpAccessChain %16 %5 %24 | |
| %32 = OpLoad %15 %31 | |
| OpStore %4 %32 | |
| %33 = OpAccessChain %16 %7 %24 | |
| %34 = OpLoad %15 %33 | |
| %35 = OpAccessChain %16 %6 %24 | |
| OpStore %35 %34 | |
| %36 = OpAccessChain %16 %6 %24 | |
| %37 = OpLoad %15 %36 | |
| %38 = OpFAdd %15 %37 %32 | |
| %39 = OpAccessChain %16 %7 %24 | |
| OpStore %39 %38 | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %10 %24 %21 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, source, true); | |
| } | |
| /* | |
| #version 430 | |
| layout(location=0) flat in int condition; | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| if (condition == 1) | |
| A[i] = B[i]; | |
| else | |
| B[i] = A[i]; | |
| } | |
| } | |
| When this is split we leave the condition check and control flow inplace and | |
| leave its removal for dead code elimination. | |
| #version 430 | |
| layout(location=0) flat in int condition; | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| if (condition == 1) | |
| A[i] = B[i]; | |
| else | |
| ; | |
| } | |
| for (int i = 0; i < 10; i++) { | |
| if (condition == 1) | |
| ; | |
| else | |
| B[i] = A[i]; | |
| } | |
| } | |
| */ | |
| TEST_F(FissionClassTest, FissionControlFlow) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" %3 | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %4 "i" | |
| OpName %3 "condition" | |
| OpName %5 "A" | |
| OpName %6 "B" | |
| OpDecorate %3 Flat | |
| OpDecorate %3 Location 0 | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypePointer Input %9 | |
| %3 = OpVariable %14 Input | |
| %15 = OpConstant %9 1 | |
| %16 = OpTypeFloat 32 | |
| %17 = OpTypeInt 32 0 | |
| %18 = OpConstant %17 10 | |
| %19 = OpTypeArray %16 %18 | |
| %20 = OpTypePointer Function %19 | |
| %21 = OpTypePointer Function %16 | |
| %2 = OpFunction %7 None %8 | |
| %22 = OpLabel | |
| %4 = OpVariable %10 Function | |
| %5 = OpVariable %20 Function | |
| %6 = OpVariable %20 Function | |
| %31 = OpLoad %9 %3 | |
| OpStore %4 %11 | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %9 %11 %22 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %13 %24 %12 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| %32 = OpIEqual %13 %31 %15 | |
| OpSelectionMerge %33 None | |
| OpBranchConditional %32 %34 %35 | |
| %34 = OpLabel | |
| %36 = OpAccessChain %21 %6 %24 | |
| %37 = OpLoad %16 %36 | |
| %38 = OpAccessChain %21 %5 %24 | |
| OpStore %38 %37 | |
| OpBranch %33 | |
| %35 = OpLabel | |
| %39 = OpAccessChain %21 %5 %24 | |
| %40 = OpLoad %16 %39 | |
| %41 = OpAccessChain %21 %6 %24 | |
| OpStore %41 %40 | |
| OpBranch %33 | |
| %33 = OpLabel | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %9 %24 %15 | |
| OpStore %4 %25 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" %3 | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %4 "i" | |
| OpName %3 "condition" | |
| OpName %5 "A" | |
| OpName %6 "B" | |
| OpDecorate %3 Flat | |
| OpDecorate %3 Location 0 | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypePointer Input %9 | |
| %3 = OpVariable %14 Input | |
| %15 = OpConstant %9 1 | |
| %16 = OpTypeFloat 32 | |
| %17 = OpTypeInt 32 0 | |
| %18 = OpConstant %17 10 | |
| %19 = OpTypeArray %16 %18 | |
| %20 = OpTypePointer Function %19 | |
| %21 = OpTypePointer Function %16 | |
| %2 = OpFunction %7 None %8 | |
| %22 = OpLabel | |
| %4 = OpVariable %10 Function | |
| %5 = OpVariable %20 Function | |
| %6 = OpVariable %20 Function | |
| %23 = OpLoad %9 %3 | |
| OpStore %4 %11 | |
| OpBranch %42 | |
| %42 = OpLabel | |
| %43 = OpPhi %9 %11 %22 %58 %57 | |
| OpLoopMerge %59 %57 None | |
| OpBranch %44 | |
| %44 = OpLabel | |
| %45 = OpSLessThan %13 %43 %12 | |
| OpBranchConditional %45 %46 %59 | |
| %46 = OpLabel | |
| %47 = OpIEqual %13 %23 %15 | |
| OpSelectionMerge %56 None | |
| OpBranchConditional %47 %52 %48 | |
| %48 = OpLabel | |
| OpBranch %56 | |
| %52 = OpLabel | |
| %53 = OpAccessChain %21 %6 %43 | |
| %54 = OpLoad %16 %53 | |
| %55 = OpAccessChain %21 %5 %43 | |
| OpStore %55 %54 | |
| OpBranch %56 | |
| %56 = OpLabel | |
| OpBranch %57 | |
| %57 = OpLabel | |
| %58 = OpIAdd %9 %43 %15 | |
| OpStore %4 %58 | |
| OpBranch %42 | |
| %59 = OpLabel | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %25 = OpPhi %9 %11 %59 %26 %27 | |
| OpLoopMerge %28 %27 None | |
| OpBranch %29 | |
| %29 = OpLabel | |
| %30 = OpSLessThan %13 %25 %12 | |
| OpBranchConditional %30 %31 %28 | |
| %31 = OpLabel | |
| %32 = OpIEqual %13 %23 %15 | |
| OpSelectionMerge %33 None | |
| OpBranchConditional %32 %34 %35 | |
| %34 = OpLabel | |
| OpBranch %33 | |
| %35 = OpLabel | |
| %39 = OpAccessChain %21 %5 %25 | |
| %40 = OpLoad %16 %39 | |
| %41 = OpAccessChain %21 %6 %25 | |
| OpStore %41 %40 | |
| OpBranch %33 | |
| %33 = OpLabel | |
| OpBranch %27 | |
| %27 = OpLabel | |
| %26 = OpIAdd %9 %25 %15 | |
| OpStore %4 %26 | |
| OpBranch %24 | |
| %28 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| if (i == 1) | |
| B[i] = A[i]; | |
| else if (i == 2) | |
| A[i] = B[i]; | |
| else | |
| A[i] = 0; | |
| } | |
| } | |
| After running the pass with multiple splits enabled (via register threshold of | |
| 1) we expect the equivalent of: | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| if (i == 1) | |
| B[i] = A[i]; | |
| else if (i == 2) | |
| else | |
| } | |
| for (int i = 0; i < 10; i++) { | |
| if (i == 1) | |
| else if (i == 2) | |
| A[i] = B[i]; | |
| else | |
| } | |
| for (int i = 0; i < 10; i++) { | |
| if (i == 1) | |
| else if (i == 2) | |
| else | |
| A[i] = 0; | |
| } | |
| } | |
| */ | |
| TEST_F(FissionClassTest, FissionControlFlow2) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpConstant %8 1 | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypePointer Function %17 | |
| %19 = OpTypePointer Function %14 | |
| %20 = OpConstant %8 2 | |
| %21 = OpConstant %14 0 | |
| %2 = OpFunction %6 None %7 | |
| %22 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %18 Function | |
| %5 = OpVariable %18 Function | |
| OpStore %3 %10 | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %8 %10 %22 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %12 %24 %11 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| %31 = OpIEqual %12 %24 %13 | |
| OpSelectionMerge %32 None | |
| OpBranchConditional %31 %33 %34 | |
| %33 = OpLabel | |
| %35 = OpAccessChain %19 %5 %24 | |
| %36 = OpLoad %14 %35 | |
| %37 = OpAccessChain %19 %4 %24 | |
| OpStore %37 %36 | |
| OpBranch %32 | |
| %34 = OpLabel | |
| %38 = OpIEqual %12 %24 %20 | |
| OpSelectionMerge %39 None | |
| OpBranchConditional %38 %40 %41 | |
| %40 = OpLabel | |
| %42 = OpAccessChain %19 %4 %24 | |
| %43 = OpLoad %14 %42 | |
| %44 = OpAccessChain %19 %5 %24 | |
| OpStore %44 %43 | |
| OpBranch %39 | |
| %41 = OpLabel | |
| %45 = OpAccessChain %19 %5 %24 | |
| OpStore %45 %21 | |
| OpBranch %39 | |
| %39 = OpLabel | |
| OpBranch %32 | |
| %32 = OpLabel | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %8 %24 %13 | |
| OpStore %3 %25 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpConstant %8 1 | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypePointer Function %17 | |
| %19 = OpTypePointer Function %14 | |
| %20 = OpConstant %8 2 | |
| %21 = OpConstant %14 0 | |
| %2 = OpFunction %6 None %7 | |
| %22 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %18 Function | |
| %5 = OpVariable %18 Function | |
| OpStore %3 %10 | |
| OpBranch %46 | |
| %46 = OpLabel | |
| %47 = OpPhi %8 %10 %22 %67 %66 | |
| OpLoopMerge %68 %66 None | |
| OpBranch %48 | |
| %48 = OpLabel | |
| %49 = OpSLessThan %12 %47 %11 | |
| OpBranchConditional %49 %50 %68 | |
| %50 = OpLabel | |
| %51 = OpIEqual %12 %47 %13 | |
| OpSelectionMerge %65 None | |
| OpBranchConditional %51 %61 %52 | |
| %52 = OpLabel | |
| %53 = OpIEqual %12 %47 %20 | |
| OpSelectionMerge %60 None | |
| OpBranchConditional %53 %56 %54 | |
| %54 = OpLabel | |
| OpBranch %60 | |
| %56 = OpLabel | |
| OpBranch %60 | |
| %60 = OpLabel | |
| OpBranch %65 | |
| %61 = OpLabel | |
| %62 = OpAccessChain %19 %5 %47 | |
| %63 = OpLoad %14 %62 | |
| %64 = OpAccessChain %19 %4 %47 | |
| OpStore %64 %63 | |
| OpBranch %65 | |
| %65 = OpLabel | |
| OpBranch %66 | |
| %66 = OpLabel | |
| %67 = OpIAdd %8 %47 %13 | |
| OpStore %3 %67 | |
| OpBranch %46 | |
| %68 = OpLabel | |
| OpBranch %69 | |
| %69 = OpLabel | |
| %70 = OpPhi %8 %10 %68 %87 %86 | |
| OpLoopMerge %88 %86 None | |
| OpBranch %71 | |
| %71 = OpLabel | |
| %72 = OpSLessThan %12 %70 %11 | |
| OpBranchConditional %72 %73 %88 | |
| %73 = OpLabel | |
| %74 = OpIEqual %12 %70 %13 | |
| OpSelectionMerge %85 None | |
| OpBranchConditional %74 %84 %75 | |
| %75 = OpLabel | |
| %76 = OpIEqual %12 %70 %20 | |
| OpSelectionMerge %83 None | |
| OpBranchConditional %76 %79 %77 | |
| %77 = OpLabel | |
| OpBranch %83 | |
| %79 = OpLabel | |
| %80 = OpAccessChain %19 %4 %70 | |
| %81 = OpLoad %14 %80 | |
| %82 = OpAccessChain %19 %5 %70 | |
| OpStore %82 %81 | |
| OpBranch %83 | |
| %83 = OpLabel | |
| OpBranch %85 | |
| %84 = OpLabel | |
| OpBranch %85 | |
| %85 = OpLabel | |
| OpBranch %86 | |
| %86 = OpLabel | |
| %87 = OpIAdd %8 %70 %13 | |
| OpStore %3 %87 | |
| OpBranch %69 | |
| %88 = OpLabel | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %8 %10 %88 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %12 %24 %11 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| %31 = OpIEqual %12 %24 %13 | |
| OpSelectionMerge %32 None | |
| OpBranchConditional %31 %33 %34 | |
| %33 = OpLabel | |
| OpBranch %32 | |
| %34 = OpLabel | |
| %38 = OpIEqual %12 %24 %20 | |
| OpSelectionMerge %39 None | |
| OpBranchConditional %38 %40 %41 | |
| %40 = OpLabel | |
| OpBranch %39 | |
| %41 = OpLabel | |
| %45 = OpAccessChain %19 %5 %24 | |
| OpStore %45 %21 | |
| OpBranch %39 | |
| %39 = OpLabel | |
| OpBranch %32 | |
| %32 = OpLabel | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %8 %24 %13 | |
| OpStore %3 %25 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true, 1); | |
| } | |
| /* | |
| #version 430 | |
| layout(location=0) flat in int condition; | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| B[i] = A[i]; | |
| memoryBarrier(); | |
| A[i] = B[i]; | |
| } | |
| } | |
| This should not be split due to the memory barrier. | |
| */ | |
| TEST_F(FissionClassTest, FissionBarrier) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" %3 | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %4 "i" | |
| OpName %5 "B" | |
| OpName %6 "A" | |
| OpName %3 "condition" | |
| OpDecorate %3 Flat | |
| OpDecorate %3 Location 0 | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypePointer Function %17 | |
| %19 = OpTypePointer Function %14 | |
| %20 = OpConstant %15 1 | |
| %21 = OpConstant %15 4048 | |
| %22 = OpConstant %9 1 | |
| %23 = OpTypePointer Input %9 | |
| %3 = OpVariable %23 Input | |
| %2 = OpFunction %7 None %8 | |
| %24 = OpLabel | |
| %4 = OpVariable %10 Function | |
| %5 = OpVariable %18 Function | |
| %6 = OpVariable %18 Function | |
| OpStore %4 %11 | |
| OpBranch %25 | |
| %25 = OpLabel | |
| %26 = OpPhi %9 %11 %24 %27 %28 | |
| OpLoopMerge %29 %28 None | |
| OpBranch %30 | |
| %30 = OpLabel | |
| %31 = OpSLessThan %13 %26 %12 | |
| OpBranchConditional %31 %32 %29 | |
| %32 = OpLabel | |
| %33 = OpAccessChain %19 %6 %26 | |
| %34 = OpLoad %14 %33 | |
| %35 = OpAccessChain %19 %5 %26 | |
| OpStore %35 %34 | |
| OpMemoryBarrier %20 %21 | |
| %36 = OpAccessChain %19 %5 %26 | |
| %37 = OpLoad %14 %36 | |
| %38 = OpAccessChain %19 %6 %26 | |
| OpStore %38 %37 | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %27 = OpIAdd %9 %26 %22 | |
| OpStore %4 %27 | |
| OpBranch %25 | |
| %29 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, source, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| B[i] = A[i]; | |
| if ( i== 1) | |
| break; | |
| A[i] = B[i]; | |
| } | |
| } | |
| This should not be split due to the break. | |
| */ | |
| TEST_F(FissionClassTest, FissionBreak) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpTypeFloat 32 | |
| %14 = OpTypeInt 32 0 | |
| %15 = OpConstant %14 10 | |
| %16 = OpTypeArray %13 %15 | |
| %17 = OpTypePointer Function %16 | |
| %18 = OpTypePointer Function %13 | |
| %19 = OpConstant %8 1 | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %17 Function | |
| %5 = OpVariable %17 Function | |
| OpStore %3 %10 | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %20 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpSLessThan %12 %22 %11 | |
| OpBranchConditional %27 %28 %25 | |
| %28 = OpLabel | |
| %29 = OpAccessChain %18 %5 %22 | |
| %30 = OpLoad %13 %29 | |
| %31 = OpAccessChain %18 %4 %22 | |
| OpStore %31 %30 | |
| %32 = OpIEqual %12 %22 %19 | |
| OpSelectionMerge %33 None | |
| OpBranchConditional %32 %34 %33 | |
| %34 = OpLabel | |
| OpBranch %25 | |
| %33 = OpLabel | |
| %35 = OpAccessChain %18 %4 %22 | |
| %36 = OpLoad %13 %35 | |
| %37 = OpAccessChain %18 %5 %22 | |
| OpStore %37 %36 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %23 = OpIAdd %8 %22 %19 | |
| OpStore %3 %23 | |
| OpBranch %21 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, source, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; i++) { | |
| B[i] = A[i]; | |
| if ( i== 1) | |
| continue; | |
| A[i] = B[i]; | |
| } | |
| } | |
| This loop should be split into: | |
| for (int i = 0; i < 10; i++) { | |
| B[i] = A[i]; | |
| if ( i== 1) | |
| continue; | |
| } | |
| for (int i = 0; i < 10; i++) { | |
| if ( i== 1) | |
| continue; | |
| A[i] = B[i]; | |
| } | |
| The continue block in the first loop is left to DCE. | |
| } | |
| */ | |
| TEST_F(FissionClassTest, FissionContinue) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpTypeFloat 32 | |
| %14 = OpTypeInt 32 0 | |
| %15 = OpConstant %14 10 | |
| %16 = OpTypeArray %13 %15 | |
| %17 = OpTypePointer Function %16 | |
| %18 = OpTypePointer Function %13 | |
| %19 = OpConstant %8 1 | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %17 Function | |
| %5 = OpVariable %17 Function | |
| OpStore %3 %10 | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %20 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpSLessThan %12 %22 %11 | |
| OpBranchConditional %27 %28 %25 | |
| %28 = OpLabel | |
| %29 = OpAccessChain %18 %5 %22 | |
| %30 = OpLoad %13 %29 | |
| %31 = OpAccessChain %18 %4 %22 | |
| OpStore %31 %30 | |
| %32 = OpIEqual %12 %22 %19 | |
| OpSelectionMerge %33 None | |
| OpBranchConditional %32 %34 %33 | |
| %34 = OpLabel | |
| OpBranch %24 | |
| %33 = OpLabel | |
| %35 = OpAccessChain %18 %4 %22 | |
| %36 = OpLoad %13 %35 | |
| %37 = OpAccessChain %18 %5 %22 | |
| OpStore %37 %36 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %23 = OpIAdd %8 %22 %19 | |
| OpStore %3 %23 | |
| OpBranch %21 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpTypeFloat 32 | |
| %14 = OpTypeInt 32 0 | |
| %15 = OpConstant %14 10 | |
| %16 = OpTypeArray %13 %15 | |
| %17 = OpTypePointer Function %16 | |
| %18 = OpTypePointer Function %13 | |
| %19 = OpConstant %8 1 | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %17 Function | |
| %5 = OpVariable %17 Function | |
| OpStore %3 %10 | |
| OpBranch %38 | |
| %38 = OpLabel | |
| %39 = OpPhi %8 %10 %20 %53 %52 | |
| OpLoopMerge %54 %52 None | |
| OpBranch %40 | |
| %40 = OpLabel | |
| %41 = OpSLessThan %12 %39 %11 | |
| OpBranchConditional %41 %42 %54 | |
| %42 = OpLabel | |
| %43 = OpAccessChain %18 %5 %39 | |
| %44 = OpLoad %13 %43 | |
| %45 = OpAccessChain %18 %4 %39 | |
| OpStore %45 %44 | |
| %46 = OpIEqual %12 %39 %19 | |
| OpSelectionMerge %48 None | |
| OpBranchConditional %46 %47 %48 | |
| %47 = OpLabel | |
| OpBranch %52 | |
| %48 = OpLabel | |
| OpBranch %52 | |
| %52 = OpLabel | |
| %53 = OpIAdd %8 %39 %19 | |
| OpStore %3 %53 | |
| OpBranch %38 | |
| %54 = OpLabel | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %54 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpSLessThan %12 %22 %11 | |
| OpBranchConditional %27 %28 %25 | |
| %28 = OpLabel | |
| %32 = OpIEqual %12 %22 %19 | |
| OpSelectionMerge %33 None | |
| OpBranchConditional %32 %34 %33 | |
| %34 = OpLabel | |
| OpBranch %24 | |
| %33 = OpLabel | |
| %35 = OpAccessChain %18 %4 %22 | |
| %36 = OpLoad %13 %35 | |
| %37 = OpAccessChain %18 %5 %22 | |
| OpStore %37 %36 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %23 = OpIAdd %8 %22 %19 | |
| OpStore %3 %23 | |
| OpBranch %21 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| int i = 0; | |
| do { | |
| B[i] = A[i]; | |
| A[i] = B[i]; | |
| ++i; | |
| } while (i < 10); | |
| } | |
| Check that this is split into: | |
| int i = 0; | |
| do { | |
| B[i] = A[i]; | |
| ++i; | |
| } while (i < 10); | |
| i = 0; | |
| do { | |
| A[i] = B[i]; | |
| ++i; | |
| } while (i < 10); | |
| */ | |
| TEST_F(FissionClassTest, FissionDoWhile) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpTypeFloat 32 | |
| %12 = OpTypeInt 32 0 | |
| %13 = OpConstant %12 10 | |
| %14 = OpTypeArray %11 %13 | |
| %15 = OpTypePointer Function %14 | |
| %16 = OpTypePointer Function %11 | |
| %17 = OpConstant %8 1 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %15 Function | |
| %5 = OpVariable %15 Function | |
| OpStore %3 %10 | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %20 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpAccessChain %16 %5 %22 | |
| %28 = OpLoad %11 %27 | |
| %29 = OpAccessChain %16 %4 %22 | |
| OpStore %29 %28 | |
| %30 = OpAccessChain %16 %4 %22 | |
| %31 = OpLoad %11 %30 | |
| %32 = OpAccessChain %16 %5 %22 | |
| OpStore %32 %31 | |
| %23 = OpIAdd %8 %22 %17 | |
| OpStore %3 %23 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %33 = OpSLessThan %19 %23 %18 | |
| OpBranchConditional %33 %21 %25 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpTypeFloat 32 | |
| %12 = OpTypeInt 32 0 | |
| %13 = OpConstant %12 10 | |
| %14 = OpTypeArray %11 %13 | |
| %15 = OpTypePointer Function %14 | |
| %16 = OpTypePointer Function %11 | |
| %17 = OpConstant %8 1 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %15 Function | |
| %5 = OpVariable %15 Function | |
| OpStore %3 %10 | |
| OpBranch %34 | |
| %34 = OpLabel | |
| %35 = OpPhi %8 %10 %20 %43 %44 | |
| OpLoopMerge %46 %44 None | |
| OpBranch %36 | |
| %36 = OpLabel | |
| %37 = OpAccessChain %16 %5 %35 | |
| %38 = OpLoad %11 %37 | |
| %39 = OpAccessChain %16 %4 %35 | |
| OpStore %39 %38 | |
| %43 = OpIAdd %8 %35 %17 | |
| OpStore %3 %43 | |
| OpBranch %44 | |
| %44 = OpLabel | |
| %45 = OpSLessThan %19 %43 %18 | |
| OpBranchConditional %45 %34 %46 | |
| %46 = OpLabel | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %46 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %30 = OpAccessChain %16 %4 %22 | |
| %31 = OpLoad %11 %30 | |
| %32 = OpAccessChain %16 %5 %22 | |
| OpStore %32 %31 | |
| %23 = OpIAdd %8 %22 %17 | |
| OpStore %3 %23 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %33 = OpSLessThan %19 %23 %18 | |
| OpBranchConditional %33 %21 %25 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10][10]; | |
| float B[10][10]; | |
| for (int j = 0; j < 10; ++j) { | |
| for (int i = 0; i < 10; ++i) { | |
| B[i][j] = A[i][i]; | |
| A[i][i] = B[i][j + 1]; | |
| } | |
| } | |
| } | |
| This loop can't be split because the load B[i][j + 1] is dependent on the store | |
| B[i][j]. | |
| */ | |
| TEST_F(FissionClassTest, FissionNestedDependency) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "j" | |
| OpName %4 "i" | |
| OpName %5 "B" | |
| OpName %6 "A" | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypeArray %17 %16 | |
| %19 = OpTypePointer Function %18 | |
| %20 = OpTypePointer Function %14 | |
| %21 = OpConstant %9 1 | |
| %2 = OpFunction %7 None %8 | |
| %22 = OpLabel | |
| %3 = OpVariable %10 Function | |
| %4 = OpVariable %10 Function | |
| %5 = OpVariable %19 Function | |
| %6 = OpVariable %19 Function | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %9 %11 %22 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %13 %24 %12 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| OpBranch %31 | |
| %31 = OpLabel | |
| %32 = OpPhi %9 %11 %30 %33 %34 | |
| OpLoopMerge %35 %34 None | |
| OpBranch %36 | |
| %36 = OpLabel | |
| %37 = OpSLessThan %13 %32 %12 | |
| OpBranchConditional %37 %38 %35 | |
| %38 = OpLabel | |
| %39 = OpAccessChain %20 %6 %32 %32 | |
| %40 = OpLoad %14 %39 | |
| %41 = OpAccessChain %20 %5 %32 %24 | |
| OpStore %41 %40 | |
| %42 = OpIAdd %9 %24 %21 | |
| %43 = OpAccessChain %20 %5 %32 %42 | |
| %44 = OpLoad %14 %43 | |
| %45 = OpAccessChain %20 %6 %32 %32 | |
| OpStore %45 %44 | |
| OpBranch %34 | |
| %34 = OpLabel | |
| %33 = OpIAdd %9 %32 %21 | |
| OpBranch %31 | |
| %35 = OpLabel | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %9 %24 %21 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, source, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10][10]; | |
| float B[10][10]; | |
| for (int j = 0; j < 10; ++j) { | |
| for (int i = 0; i < 10; ++i) { | |
| B[i][i] = A[i][j]; | |
| A[i][j+1] = B[i][i]; | |
| } | |
| } | |
| } | |
| This loop should not be split as the load A[i][j+1] would be reading a value | |
| written in the store A[i][j] which would be hit before A[i][j+1] if the loops | |
| where split but would not get hit before the read currently. | |
| */ | |
| TEST_F(FissionClassTest, FissionNestedDependency2) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "j" | |
| OpName %4 "i" | |
| OpName %5 "B" | |
| OpName %6 "A" | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypeArray %17 %16 | |
| %19 = OpTypePointer Function %18 | |
| %20 = OpTypePointer Function %14 | |
| %21 = OpConstant %9 1 | |
| %2 = OpFunction %7 None %8 | |
| %22 = OpLabel | |
| %3 = OpVariable %10 Function | |
| %4 = OpVariable %10 Function | |
| %5 = OpVariable %19 Function | |
| %6 = OpVariable %19 Function | |
| OpStore %3 %11 | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %9 %11 %22 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %13 %24 %12 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| OpStore %4 %11 | |
| OpBranch %31 | |
| %31 = OpLabel | |
| %32 = OpPhi %9 %11 %30 %33 %34 | |
| OpLoopMerge %35 %34 None | |
| OpBranch %36 | |
| %36 = OpLabel | |
| %37 = OpSLessThan %13 %32 %12 | |
| OpBranchConditional %37 %38 %35 | |
| %38 = OpLabel | |
| %39 = OpAccessChain %20 %6 %32 %24 | |
| %40 = OpLoad %14 %39 | |
| %41 = OpAccessChain %20 %5 %32 %32 | |
| OpStore %41 %40 | |
| %42 = OpIAdd %9 %24 %21 | |
| %43 = OpAccessChain %20 %5 %32 %32 | |
| %44 = OpLoad %14 %43 | |
| %45 = OpAccessChain %20 %6 %32 %42 | |
| OpStore %45 %44 | |
| OpBranch %34 | |
| %34 = OpLabel | |
| %33 = OpIAdd %9 %32 %21 | |
| OpStore %4 %33 | |
| OpBranch %31 | |
| %35 = OpLabel | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %9 %24 %21 | |
| OpStore %3 %25 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, source, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10][10]; | |
| float B[10][10]; | |
| for (int j = 0; j < 10; ++j) { | |
| for (int i = 0; i < 10; ++i) { | |
| B[i][j] = A[i][j]; | |
| A[i][j] = B[i][j]; | |
| } | |
| for (int i = 0; i < 10; ++i) { | |
| B[i][j] = A[i][j]; | |
| A[i][j] = B[i][j]; | |
| } | |
| } | |
| } | |
| Should be split into: | |
| for (int j = 0; j < 10; ++j) { | |
| for (int i = 0; i < 10; ++i) | |
| B[i][j] = A[i][j]; | |
| for (int i = 0; i < 10; ++i) | |
| A[i][j] = B[i][j]; | |
| for (int i = 0; i < 10; ++i) | |
| B[i][j] = A[i][j]; | |
| for (int i = 0; i < 10; ++i) | |
| A[i][j] = B[i][j]; | |
| */ | |
| TEST_F(FissionClassTest, FissionMultipleLoopsNested) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "j" | |
| OpName %4 "i" | |
| OpName %5 "B" | |
| OpName %6 "A" | |
| OpName %7 "i" | |
| %8 = OpTypeVoid | |
| %9 = OpTypeFunction %8 | |
| %10 = OpTypeInt 32 1 | |
| %11 = OpTypePointer Function %10 | |
| %12 = OpConstant %10 0 | |
| %13 = OpConstant %10 10 | |
| %14 = OpTypeBool | |
| %15 = OpTypeFloat 32 | |
| %16 = OpTypeInt 32 0 | |
| %17 = OpConstant %16 10 | |
| %18 = OpTypeArray %15 %17 | |
| %19 = OpTypeArray %18 %17 | |
| %20 = OpTypePointer Function %19 | |
| %21 = OpTypePointer Function %15 | |
| %22 = OpConstant %10 1 | |
| %2 = OpFunction %8 None %9 | |
| %23 = OpLabel | |
| %3 = OpVariable %11 Function | |
| %4 = OpVariable %11 Function | |
| %5 = OpVariable %20 Function | |
| %6 = OpVariable %20 Function | |
| %7 = OpVariable %11 Function | |
| OpStore %3 %12 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %25 = OpPhi %10 %12 %23 %26 %27 | |
| OpLoopMerge %28 %27 None | |
| OpBranch %29 | |
| %29 = OpLabel | |
| %30 = OpSLessThan %14 %25 %13 | |
| OpBranchConditional %30 %31 %28 | |
| %31 = OpLabel | |
| OpStore %4 %12 | |
| OpBranch %32 | |
| %32 = OpLabel | |
| %33 = OpPhi %10 %12 %31 %34 %35 | |
| OpLoopMerge %36 %35 None | |
| OpBranch %37 | |
| %37 = OpLabel | |
| %38 = OpSLessThan %14 %33 %13 | |
| OpBranchConditional %38 %39 %36 | |
| %39 = OpLabel | |
| %40 = OpAccessChain %21 %6 %33 %25 | |
| %41 = OpLoad %15 %40 | |
| %42 = OpAccessChain %21 %5 %33 %25 | |
| OpStore %42 %41 | |
| %43 = OpAccessChain %21 %5 %33 %25 | |
| %44 = OpLoad %15 %43 | |
| %45 = OpAccessChain %21 %6 %33 %25 | |
| OpStore %45 %44 | |
| OpBranch %35 | |
| %35 = OpLabel | |
| %34 = OpIAdd %10 %33 %22 | |
| OpStore %4 %34 | |
| OpBranch %32 | |
| %36 = OpLabel | |
| OpStore %7 %12 | |
| OpBranch %46 | |
| %46 = OpLabel | |
| %47 = OpPhi %10 %12 %36 %48 %49 | |
| OpLoopMerge %50 %49 None | |
| OpBranch %51 | |
| %51 = OpLabel | |
| %52 = OpSLessThan %14 %47 %13 | |
| OpBranchConditional %52 %53 %50 | |
| %53 = OpLabel | |
| %54 = OpAccessChain %21 %6 %47 %25 | |
| %55 = OpLoad %15 %54 | |
| %56 = OpAccessChain %21 %5 %47 %25 | |
| OpStore %56 %55 | |
| %57 = OpAccessChain %21 %5 %47 %25 | |
| %58 = OpLoad %15 %57 | |
| %59 = OpAccessChain %21 %6 %47 %25 | |
| OpStore %59 %58 | |
| OpBranch %49 | |
| %49 = OpLabel | |
| %48 = OpIAdd %10 %47 %22 | |
| OpStore %7 %48 | |
| OpBranch %46 | |
| %50 = OpLabel | |
| OpBranch %27 | |
| %27 = OpLabel | |
| %26 = OpIAdd %10 %25 %22 | |
| OpStore %3 %26 | |
| OpBranch %24 | |
| %28 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "j" | |
| OpName %4 "i" | |
| OpName %5 "B" | |
| OpName %6 "A" | |
| OpName %7 "i" | |
| %8 = OpTypeVoid | |
| %9 = OpTypeFunction %8 | |
| %10 = OpTypeInt 32 1 | |
| %11 = OpTypePointer Function %10 | |
| %12 = OpConstant %10 0 | |
| %13 = OpConstant %10 10 | |
| %14 = OpTypeBool | |
| %15 = OpTypeFloat 32 | |
| %16 = OpTypeInt 32 0 | |
| %17 = OpConstant %16 10 | |
| %18 = OpTypeArray %15 %17 | |
| %19 = OpTypeArray %18 %17 | |
| %20 = OpTypePointer Function %19 | |
| %21 = OpTypePointer Function %15 | |
| %22 = OpConstant %10 1 | |
| %2 = OpFunction %8 None %9 | |
| %23 = OpLabel | |
| %3 = OpVariable %11 Function | |
| %4 = OpVariable %11 Function | |
| %5 = OpVariable %20 Function | |
| %6 = OpVariable %20 Function | |
| %7 = OpVariable %11 Function | |
| OpStore %3 %12 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %25 = OpPhi %10 %12 %23 %26 %27 | |
| OpLoopMerge %28 %27 None | |
| OpBranch %29 | |
| %29 = OpLabel | |
| %30 = OpSLessThan %14 %25 %13 | |
| OpBranchConditional %30 %31 %28 | |
| %31 = OpLabel | |
| OpStore %4 %12 | |
| OpBranch %60 | |
| %60 = OpLabel | |
| %61 = OpPhi %10 %12 %31 %72 %71 | |
| OpLoopMerge %73 %71 None | |
| OpBranch %62 | |
| %62 = OpLabel | |
| %63 = OpSLessThan %14 %61 %13 | |
| OpBranchConditional %63 %64 %73 | |
| %64 = OpLabel | |
| %65 = OpAccessChain %21 %6 %61 %25 | |
| %66 = OpLoad %15 %65 | |
| %67 = OpAccessChain %21 %5 %61 %25 | |
| OpStore %67 %66 | |
| OpBranch %71 | |
| %71 = OpLabel | |
| %72 = OpIAdd %10 %61 %22 | |
| OpStore %4 %72 | |
| OpBranch %60 | |
| %73 = OpLabel | |
| OpBranch %32 | |
| %32 = OpLabel | |
| %33 = OpPhi %10 %12 %73 %34 %35 | |
| OpLoopMerge %36 %35 None | |
| OpBranch %37 | |
| %37 = OpLabel | |
| %38 = OpSLessThan %14 %33 %13 | |
| OpBranchConditional %38 %39 %36 | |
| %39 = OpLabel | |
| %43 = OpAccessChain %21 %5 %33 %25 | |
| %44 = OpLoad %15 %43 | |
| %45 = OpAccessChain %21 %6 %33 %25 | |
| OpStore %45 %44 | |
| OpBranch %35 | |
| %35 = OpLabel | |
| %34 = OpIAdd %10 %33 %22 | |
| OpStore %4 %34 | |
| OpBranch %32 | |
| %36 = OpLabel | |
| OpStore %7 %12 | |
| OpBranch %74 | |
| %74 = OpLabel | |
| %75 = OpPhi %10 %12 %36 %86 %85 | |
| OpLoopMerge %87 %85 None | |
| OpBranch %76 | |
| %76 = OpLabel | |
| %77 = OpSLessThan %14 %75 %13 | |
| OpBranchConditional %77 %78 %87 | |
| %78 = OpLabel | |
| %79 = OpAccessChain %21 %6 %75 %25 | |
| %80 = OpLoad %15 %79 | |
| %81 = OpAccessChain %21 %5 %75 %25 | |
| OpStore %81 %80 | |
| OpBranch %85 | |
| %85 = OpLabel | |
| %86 = OpIAdd %10 %75 %22 | |
| OpStore %7 %86 | |
| OpBranch %74 | |
| %87 = OpLabel | |
| OpBranch %46 | |
| %46 = OpLabel | |
| %47 = OpPhi %10 %12 %87 %48 %49 | |
| OpLoopMerge %50 %49 None | |
| OpBranch %51 | |
| %51 = OpLabel | |
| %52 = OpSLessThan %14 %47 %13 | |
| OpBranchConditional %52 %53 %50 | |
| %53 = OpLabel | |
| %57 = OpAccessChain %21 %5 %47 %25 | |
| %58 = OpLoad %15 %57 | |
| %59 = OpAccessChain %21 %6 %47 %25 | |
| OpStore %59 %58 | |
| OpBranch %49 | |
| %49 = OpLabel | |
| %48 = OpIAdd %10 %47 %22 | |
| OpStore %7 %48 | |
| OpBranch %46 | |
| %50 = OpLabel | |
| OpBranch %27 | |
| %27 = OpLabel | |
| %26 = OpIAdd %10 %25 %22 | |
| OpStore %3 %26 | |
| OpBranch %24 | |
| %28 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| const Function* function = spvtest::GetFunction(module, 2); | |
| LoopDescriptor& pre_pass_descriptor = *context->GetLoopDescriptor(function); | |
| EXPECT_EQ(pre_pass_descriptor.NumLoops(), 3u); | |
| EXPECT_EQ(pre_pass_descriptor.pre_begin()->NumImmediateChildren(), 2u); | |
| // Test that the pass transforms the ir into the expected output. | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| // Test that the loop descriptor is correctly maintained and updated by the | |
| // pass. | |
| LoopFissionPass loop_fission; | |
| loop_fission.SetContextForTesting(context.get()); | |
| loop_fission.Process(); | |
| function = spvtest::GetFunction(module, 2); | |
| LoopDescriptor& post_pass_descriptor = *context->GetLoopDescriptor(function); | |
| EXPECT_EQ(post_pass_descriptor.NumLoops(), 5u); | |
| EXPECT_EQ(post_pass_descriptor.pre_begin()->NumImmediateChildren(), 4u); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10][10]; | |
| float B[10][10]; | |
| for (int i = 0; i < 10; ++i) { | |
| B[i][i] = A[i][i]; | |
| A[i][i] = B[i][i]; | |
| } | |
| for (int i = 0; i < 10; ++i) { | |
| B[i][i] = A[i][i]; | |
| A[i][i] = B[i][i] | |
| } | |
| } | |
| Should be split into: | |
| for (int i = 0; i < 10; ++i) | |
| B[i][i] = A[i][i]; | |
| for (int i = 0; i < 10; ++i) | |
| A[i][i] = B[i][i]; | |
| for (int i = 0; i < 10; ++i) | |
| B[i][i] = A[i][i]; | |
| for (int i = 0; i < 10; ++i) | |
| A[i][i] = B[i][i]; | |
| */ | |
| TEST_F(FissionClassTest, FissionMultipleLoops) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| OpName %6 "i" | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypePointer Function %17 | |
| %19 = OpTypePointer Function %14 | |
| %20 = OpConstant %9 1 | |
| %2 = OpFunction %7 None %8 | |
| %21 = OpLabel | |
| %3 = OpVariable %10 Function | |
| %4 = OpVariable %18 Function | |
| %5 = OpVariable %18 Function | |
| %6 = OpVariable %10 Function | |
| OpStore %3 %11 | |
| OpBranch %22 | |
| %22 = OpLabel | |
| %23 = OpPhi %9 %11 %21 %24 %25 | |
| OpLoopMerge %26 %25 None | |
| OpBranch %27 | |
| %27 = OpLabel | |
| %28 = OpSLessThan %13 %23 %12 | |
| OpBranchConditional %28 %29 %26 | |
| %29 = OpLabel | |
| %30 = OpAccessChain %19 %5 %23 | |
| %31 = OpLoad %14 %30 | |
| %32 = OpAccessChain %19 %4 %23 | |
| OpStore %32 %31 | |
| %33 = OpAccessChain %19 %4 %23 | |
| %34 = OpLoad %14 %33 | |
| %35 = OpAccessChain %19 %5 %23 | |
| OpStore %35 %34 | |
| OpBranch %25 | |
| %25 = OpLabel | |
| %24 = OpIAdd %9 %23 %20 | |
| OpStore %3 %24 | |
| OpBranch %22 | |
| %26 = OpLabel | |
| OpStore %6 %11 | |
| OpBranch %36 | |
| %36 = OpLabel | |
| %37 = OpPhi %9 %11 %26 %38 %39 | |
| OpLoopMerge %40 %39 None | |
| OpBranch %41 | |
| %41 = OpLabel | |
| %42 = OpSLessThan %13 %37 %12 | |
| OpBranchConditional %42 %43 %40 | |
| %43 = OpLabel | |
| %44 = OpAccessChain %19 %5 %37 | |
| %45 = OpLoad %14 %44 | |
| %46 = OpAccessChain %19 %4 %37 | |
| OpStore %46 %45 | |
| %47 = OpAccessChain %19 %4 %37 | |
| %48 = OpLoad %14 %47 | |
| %49 = OpAccessChain %19 %5 %37 | |
| OpStore %49 %48 | |
| OpBranch %39 | |
| %39 = OpLabel | |
| %38 = OpIAdd %9 %37 %20 | |
| OpStore %6 %38 | |
| OpBranch %36 | |
| %40 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| OpName %6 "i" | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypePointer Function %9 | |
| %11 = OpConstant %9 0 | |
| %12 = OpConstant %9 10 | |
| %13 = OpTypeBool | |
| %14 = OpTypeFloat 32 | |
| %15 = OpTypeInt 32 0 | |
| %16 = OpConstant %15 10 | |
| %17 = OpTypeArray %14 %16 | |
| %18 = OpTypePointer Function %17 | |
| %19 = OpTypePointer Function %14 | |
| %20 = OpConstant %9 1 | |
| %2 = OpFunction %7 None %8 | |
| %21 = OpLabel | |
| %3 = OpVariable %10 Function | |
| %4 = OpVariable %18 Function | |
| %5 = OpVariable %18 Function | |
| %6 = OpVariable %10 Function | |
| OpStore %3 %11 | |
| OpBranch %64 | |
| %64 = OpLabel | |
| %65 = OpPhi %9 %11 %21 %76 %75 | |
| OpLoopMerge %77 %75 None | |
| OpBranch %66 | |
| %66 = OpLabel | |
| %67 = OpSLessThan %13 %65 %12 | |
| OpBranchConditional %67 %68 %77 | |
| %68 = OpLabel | |
| %69 = OpAccessChain %19 %5 %65 | |
| %70 = OpLoad %14 %69 | |
| %71 = OpAccessChain %19 %4 %65 | |
| OpStore %71 %70 | |
| OpBranch %75 | |
| %75 = OpLabel | |
| %76 = OpIAdd %9 %65 %20 | |
| OpStore %3 %76 | |
| OpBranch %64 | |
| %77 = OpLabel | |
| OpBranch %22 | |
| %22 = OpLabel | |
| %23 = OpPhi %9 %11 %77 %24 %25 | |
| OpLoopMerge %26 %25 None | |
| OpBranch %27 | |
| %27 = OpLabel | |
| %28 = OpSLessThan %13 %23 %12 | |
| OpBranchConditional %28 %29 %26 | |
| %29 = OpLabel | |
| %33 = OpAccessChain %19 %4 %23 | |
| %34 = OpLoad %14 %33 | |
| %35 = OpAccessChain %19 %5 %23 | |
| OpStore %35 %34 | |
| OpBranch %25 | |
| %25 = OpLabel | |
| %24 = OpIAdd %9 %23 %20 | |
| OpStore %3 %24 | |
| OpBranch %22 | |
| %26 = OpLabel | |
| OpStore %6 %11 | |
| OpBranch %50 | |
| %50 = OpLabel | |
| %51 = OpPhi %9 %11 %26 %62 %61 | |
| OpLoopMerge %63 %61 None | |
| OpBranch %52 | |
| %52 = OpLabel | |
| %53 = OpSLessThan %13 %51 %12 | |
| OpBranchConditional %53 %54 %63 | |
| %54 = OpLabel | |
| %55 = OpAccessChain %19 %5 %51 | |
| %56 = OpLoad %14 %55 | |
| %57 = OpAccessChain %19 %4 %51 | |
| OpStore %57 %56 | |
| OpBranch %61 | |
| %61 = OpLabel | |
| %62 = OpIAdd %9 %51 %20 | |
| OpStore %6 %62 | |
| OpBranch %50 | |
| %63 = OpLabel | |
| OpBranch %36 | |
| %36 = OpLabel | |
| %37 = OpPhi %9 %11 %63 %38 %39 | |
| OpLoopMerge %40 %39 None | |
| OpBranch %41 | |
| %41 = OpLabel | |
| %42 = OpSLessThan %13 %37 %12 | |
| OpBranchConditional %42 %43 %40 | |
| %43 = OpLabel | |
| %47 = OpAccessChain %19 %4 %37 | |
| %48 = OpLoad %14 %47 | |
| %49 = OpAccessChain %19 %5 %37 | |
| OpStore %49 %48 | |
| OpBranch %39 | |
| %39 = OpLabel | |
| %38 = OpIAdd %9 %37 %20 | |
| OpStore %6 %38 | |
| OpBranch %36 | |
| %40 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| const Function* function = spvtest::GetFunction(module, 2); | |
| LoopDescriptor& pre_pass_descriptor = *context->GetLoopDescriptor(function); | |
| EXPECT_EQ(pre_pass_descriptor.NumLoops(), 2u); | |
| EXPECT_EQ(pre_pass_descriptor.pre_begin()->NumImmediateChildren(), 0u); | |
| // Test that the pass transforms the ir into the expected output. | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| // Test that the loop descriptor is correctly maintained and updated by the | |
| // pass. | |
| LoopFissionPass loop_fission; | |
| loop_fission.SetContextForTesting(context.get()); | |
| loop_fission.Process(); | |
| function = spvtest::GetFunction(module, 2); | |
| LoopDescriptor& post_pass_descriptor = *context->GetLoopDescriptor(function); | |
| EXPECT_EQ(post_pass_descriptor.NumLoops(), 4u); | |
| EXPECT_EQ(post_pass_descriptor.pre_begin()->NumImmediateChildren(), 0u); | |
| } | |
| /* | |
| #version 430 | |
| int foo() { return 1; } | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; ++i) { | |
| B[i] = A[i]; | |
| foo(); | |
| A[i] = B[i]; | |
| } | |
| } | |
| This should not be split as it has a function call in it so we can't determine | |
| if it has side effects. | |
| */ | |
| TEST_F(FissionClassTest, FissionFunctionCall) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "foo(" | |
| OpName %4 "i" | |
| OpName %5 "B" | |
| OpName %6 "A" | |
| %7 = OpTypeVoid | |
| %8 = OpTypeFunction %7 | |
| %9 = OpTypeInt 32 1 | |
| %10 = OpTypeFunction %9 | |
| %11 = OpConstant %9 1 | |
| %12 = OpTypePointer Function %9 | |
| %13 = OpConstant %9 0 | |
| %14 = OpConstant %9 10 | |
| %15 = OpTypeBool | |
| %16 = OpTypeFloat 32 | |
| %17 = OpTypeInt 32 0 | |
| %18 = OpConstant %17 10 | |
| %19 = OpTypeArray %16 %18 | |
| %20 = OpTypePointer Function %19 | |
| %21 = OpTypePointer Function %16 | |
| %2 = OpFunction %7 None %8 | |
| %22 = OpLabel | |
| %4 = OpVariable %12 Function | |
| %5 = OpVariable %20 Function | |
| %6 = OpVariable %20 Function | |
| OpStore %4 %13 | |
| OpBranch %23 | |
| %23 = OpLabel | |
| %24 = OpPhi %9 %13 %22 %25 %26 | |
| OpLoopMerge %27 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| %29 = OpSLessThan %15 %24 %14 | |
| OpBranchConditional %29 %30 %27 | |
| %30 = OpLabel | |
| %31 = OpAccessChain %21 %6 %24 | |
| %32 = OpLoad %16 %31 | |
| %33 = OpAccessChain %21 %5 %24 | |
| OpStore %33 %32 | |
| %34 = OpFunctionCall %9 %3 | |
| %35 = OpAccessChain %21 %5 %24 | |
| %36 = OpLoad %16 %35 | |
| %37 = OpAccessChain %21 %6 %24 | |
| OpStore %37 %36 | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %25 = OpIAdd %9 %24 %11 | |
| OpStore %4 %25 | |
| OpBranch %23 | |
| %27 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %3 = OpFunction %9 None %10 | |
| %38 = OpLabel | |
| OpReturnValue %11 | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, source, true); | |
| } | |
| /* | |
| #version 430 | |
| void main(void) { | |
| float A[10]; | |
| float B[10]; | |
| for (int i = 0; i < 10; ++i) { | |
| switch (i) { | |
| case 1: | |
| B[i] = A[i]; | |
| break; | |
| default: | |
| A[i] = B[i]; | |
| } | |
| } | |
| } | |
| This should be split into: | |
| for (int i = 0; i < 10; ++i) { | |
| switch (i) { | |
| case 1: | |
| break; | |
| default: | |
| A[i] = B[i]; | |
| } | |
| } | |
| for (int i = 0; i < 10; ++i) { | |
| switch (i) { | |
| case 1: | |
| B[i] = A[i]; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| */ | |
| TEST_F(FissionClassTest, FissionSwitchStatement) { | |
| // clang-format off | |
| // With LocalMultiStoreElimPass | |
| const std::string source = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpTypeFloat 32 | |
| %14 = OpTypeInt 32 0 | |
| %15 = OpConstant %14 10 | |
| %16 = OpTypeArray %13 %15 | |
| %17 = OpTypePointer Function %16 | |
| %18 = OpTypePointer Function %13 | |
| %19 = OpConstant %8 1 | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %17 Function | |
| %5 = OpVariable %17 Function | |
| OpStore %3 %10 | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %20 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpSLessThan %12 %22 %11 | |
| OpBranchConditional %27 %28 %25 | |
| %28 = OpLabel | |
| OpSelectionMerge %29 None | |
| OpSwitch %22 %30 1 %31 | |
| %30 = OpLabel | |
| %32 = OpAccessChain %18 %4 %22 | |
| %33 = OpLoad %13 %32 | |
| %34 = OpAccessChain %18 %5 %22 | |
| OpStore %34 %33 | |
| OpBranch %29 | |
| %31 = OpLabel | |
| %35 = OpAccessChain %18 %5 %22 | |
| %36 = OpLoad %13 %35 | |
| %37 = OpAccessChain %18 %4 %22 | |
| OpStore %37 %36 | |
| OpBranch %29 | |
| %29 = OpLabel | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %23 = OpIAdd %8 %22 %19 | |
| OpStore %3 %23 | |
| OpBranch %21 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const std::string expected = R"(OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %2 "main" | |
| OpExecutionMode %2 OriginUpperLeft | |
| OpSource GLSL 430 | |
| OpName %2 "main" | |
| OpName %3 "i" | |
| OpName %4 "B" | |
| OpName %5 "A" | |
| %6 = OpTypeVoid | |
| %7 = OpTypeFunction %6 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpConstant %8 0 | |
| %11 = OpConstant %8 10 | |
| %12 = OpTypeBool | |
| %13 = OpTypeFloat 32 | |
| %14 = OpTypeInt 32 0 | |
| %15 = OpConstant %14 10 | |
| %16 = OpTypeArray %13 %15 | |
| %17 = OpTypePointer Function %16 | |
| %18 = OpTypePointer Function %13 | |
| %19 = OpConstant %8 1 | |
| %2 = OpFunction %6 None %7 | |
| %20 = OpLabel | |
| %3 = OpVariable %9 Function | |
| %4 = OpVariable %17 Function | |
| %5 = OpVariable %17 Function | |
| OpStore %3 %10 | |
| OpBranch %38 | |
| %38 = OpLabel | |
| %39 = OpPhi %8 %10 %20 %53 %52 | |
| OpLoopMerge %54 %52 None | |
| OpBranch %40 | |
| %40 = OpLabel | |
| %41 = OpSLessThan %12 %39 %11 | |
| OpBranchConditional %41 %42 %54 | |
| %42 = OpLabel | |
| OpSelectionMerge %51 None | |
| OpSwitch %39 %47 1 %43 | |
| %43 = OpLabel | |
| OpBranch %51 | |
| %47 = OpLabel | |
| %48 = OpAccessChain %18 %4 %39 | |
| %49 = OpLoad %13 %48 | |
| %50 = OpAccessChain %18 %5 %39 | |
| OpStore %50 %49 | |
| OpBranch %51 | |
| %51 = OpLabel | |
| OpBranch %52 | |
| %52 = OpLabel | |
| %53 = OpIAdd %8 %39 %19 | |
| OpStore %3 %53 | |
| OpBranch %38 | |
| %54 = OpLabel | |
| OpBranch %21 | |
| %21 = OpLabel | |
| %22 = OpPhi %8 %10 %54 %23 %24 | |
| OpLoopMerge %25 %24 None | |
| OpBranch %26 | |
| %26 = OpLabel | |
| %27 = OpSLessThan %12 %22 %11 | |
| OpBranchConditional %27 %28 %25 | |
| %28 = OpLabel | |
| OpSelectionMerge %29 None | |
| OpSwitch %22 %30 1 %31 | |
| %30 = OpLabel | |
| OpBranch %29 | |
| %31 = OpLabel | |
| %35 = OpAccessChain %18 %5 %22 | |
| %36 = OpLoad %13 %35 | |
| %37 = OpAccessChain %18 %4 %22 | |
| OpStore %37 %36 | |
| OpBranch %29 | |
| %29 = OpLabel | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %23 = OpIAdd %8 %22 %19 | |
| OpStore %3 %23 | |
| OpBranch %21 | |
| %25 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| // clang-format on | |
| std::unique_ptr<IRContext> context = | |
| BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, | |
| SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); | |
| Module* module = context->module(); | |
| EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" | |
| << source << std::endl; | |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); | |
| SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); | |
| } | |
| } // namespace | |
| } // namespace opt | |
| } // namespace spvtools |