blob: 95b5f4f1509408b025cf0e4bb05a625b65adb098 [file] [log] [blame]
// 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/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
#include "source/opt/build_module.h"
#include "source/reduce/reduction_opportunity.h"
#include "test/reduce/reduce_test_util.h"
namespace spvtools {
namespace reduce {
namespace {
TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader1) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %13
%13 = OpLabel
%19 = OpLoad %6 %8
%21 = OpIAdd %6 %19 %20
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%20 = OpConstant %6 1
%22 = OpConstantTrue %17
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %22 %14 %12
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %12
%13 = OpLabel
%19 = OpLoad %6 %8
%21 = OpIAdd %6 %19 %20
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader2) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%28 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
%32 = OpVariable %7 Function
%40 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpStore %19 %9
OpBranch %20
%20 = OpLabel
OpLoopMerge %22 %23 None
OpBranch %24
%24 = OpLabel
%25 = OpLoad %6 %19
%26 = OpSLessThan %17 %25 %16
OpBranchConditional %26 %21 %22
%21 = OpLabel
OpBranch %23
%23 = OpLabel
%27 = OpLoad %6 %19
%29 = OpIAdd %6 %27 %28
OpStore %19 %29
OpBranch %20
%22 = OpLabel
OpBranch %13
%13 = OpLabel
%30 = OpLoad %6 %8
%31 = OpIAdd %6 %30 %28
OpStore %8 %31
OpBranch %10
%12 = OpLabel
OpStore %32 %9
OpBranch %33
%33 = OpLabel
OpLoopMerge %35 %36 None
OpBranch %37
%37 = OpLabel
%38 = OpLoad %6 %32
%39 = OpSLessThan %17 %38 %16
OpBranchConditional %39 %34 %35
%34 = OpLabel
OpStore %40 %9
OpBranch %41
%41 = OpLabel
OpLoopMerge %43 %44 None
OpBranch %45
%45 = OpLabel
%46 = OpLoad %6 %40
%47 = OpSLessThan %17 %46 %16
OpBranchConditional %47 %42 %43
%42 = OpLabel
OpBranch %44
%44 = OpLabel
%48 = OpLoad %6 %40
%49 = OpIAdd %6 %48 %28
OpStore %40 %49
OpBranch %41
%43 = OpLabel
OpBranch %36
%36 = OpLabel
%50 = OpLoad %6 %32
%51 = OpIAdd %6 %50 %28
OpStore %32 %51
OpBranch %33
%35 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(4, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%28 = OpConstant %6 1
%52 = OpConstantTrue %17
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
%32 = OpVariable %7 Function
%40 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %52 %14 %12
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpStore %19 %9
OpBranch %20
%20 = OpLabel
OpLoopMerge %22 %23 None
OpBranch %24
%24 = OpLabel
%25 = OpLoad %6 %19
%26 = OpSLessThan %17 %25 %16
OpBranchConditional %26 %21 %22
%21 = OpLabel
OpBranch %23
%23 = OpLabel
%27 = OpLoad %6 %19
%29 = OpIAdd %6 %27 %28
OpStore %19 %29
OpBranch %20
%22 = OpLabel
OpBranch %12
%13 = OpLabel
%30 = OpLoad %6 %8
%31 = OpIAdd %6 %30 %28
OpStore %8 %31
OpBranch %10
%12 = OpLabel
OpStore %32 %9
OpBranch %33
%33 = OpLabel
OpLoopMerge %35 %36 None
OpBranch %37
%37 = OpLabel
%38 = OpLoad %6 %32
%39 = OpSLessThan %17 %38 %16
OpBranchConditional %39 %34 %35
%34 = OpLabel
OpStore %40 %9
OpBranch %41
%41 = OpLabel
OpLoopMerge %43 %44 None
OpBranch %45
%45 = OpLabel
%46 = OpLoad %6 %40
%47 = OpSLessThan %17 %46 %16
OpBranchConditional %47 %42 %43
%42 = OpLabel
OpBranch %44
%44 = OpLabel
%48 = OpLoad %6 %40
%49 = OpIAdd %6 %48 %28
OpStore %40 %49
OpBranch %41
%43 = OpLabel
OpBranch %36
%36 = OpLabel
%50 = OpLoad %6 %32
%51 = OpIAdd %6 %50 %28
OpStore %32 %51
OpBranch %33
%35 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
ASSERT_TRUE(ops[1]->PreconditionHolds());
ops[1]->TryToApply();
CheckValid(env, context.get());
std::string after_op_1 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%28 = OpConstant %6 1
%52 = OpConstantTrue %17
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
%32 = OpVariable %7 Function
%40 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %52 %14 %12
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpStore %19 %9
OpBranch %20
%20 = OpLabel
OpSelectionMerge %22 None
OpBranchConditional %52 %24 %22
%24 = OpLabel
%25 = OpLoad %6 %19
%26 = OpSLessThan %17 %25 %16
OpBranchConditional %26 %21 %22
%21 = OpLabel
OpBranch %22
%23 = OpLabel
%27 = OpLoad %6 %19
%29 = OpIAdd %6 %27 %28
OpStore %19 %29
OpBranch %20
%22 = OpLabel
OpBranch %12
%13 = OpLabel
%30 = OpLoad %6 %8
%31 = OpIAdd %6 %30 %28
OpStore %8 %31
OpBranch %10
%12 = OpLabel
OpStore %32 %9
OpBranch %33
%33 = OpLabel
OpLoopMerge %35 %36 None
OpBranch %37
%37 = OpLabel
%38 = OpLoad %6 %32
%39 = OpSLessThan %17 %38 %16
OpBranchConditional %39 %34 %35
%34 = OpLabel
OpStore %40 %9
OpBranch %41
%41 = OpLabel
OpLoopMerge %43 %44 None
OpBranch %45
%45 = OpLabel
%46 = OpLoad %6 %40
%47 = OpSLessThan %17 %46 %16
OpBranchConditional %47 %42 %43
%42 = OpLabel
OpBranch %44
%44 = OpLabel
%48 = OpLoad %6 %40
%49 = OpIAdd %6 %48 %28
OpStore %40 %49
OpBranch %41
%43 = OpLabel
OpBranch %36
%36 = OpLabel
%50 = OpLoad %6 %32
%51 = OpIAdd %6 %50 %28
OpStore %32 %51
OpBranch %33
%35 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_1, context.get());
ASSERT_TRUE(ops[2]->PreconditionHolds());
ops[2]->TryToApply();
CheckValid(env, context.get());
std::string after_op_2 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%28 = OpConstant %6 1
%52 = OpConstantTrue %17
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
%32 = OpVariable %7 Function
%40 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %52 %14 %12
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpStore %19 %9
OpBranch %20
%20 = OpLabel
OpSelectionMerge %22 None
OpBranchConditional %52 %24 %22
%24 = OpLabel
%25 = OpLoad %6 %19
%26 = OpSLessThan %17 %25 %16
OpBranchConditional %26 %21 %22
%21 = OpLabel
OpBranch %22
%23 = OpLabel
%27 = OpLoad %6 %19
%29 = OpIAdd %6 %27 %28
OpStore %19 %29
OpBranch %20
%22 = OpLabel
OpBranch %12
%13 = OpLabel
%30 = OpLoad %6 %8
%31 = OpIAdd %6 %30 %28
OpStore %8 %31
OpBranch %10
%12 = OpLabel
OpStore %32 %9
OpBranch %33
%33 = OpLabel
OpSelectionMerge %35 None
OpBranchConditional %52 %37 %35
%37 = OpLabel
%38 = OpLoad %6 %32
%39 = OpSLessThan %17 %38 %16
OpBranchConditional %39 %34 %35
%34 = OpLabel
OpStore %40 %9
OpBranch %41
%41 = OpLabel
OpLoopMerge %43 %44 None
OpBranch %45
%45 = OpLabel
%46 = OpLoad %6 %40
%47 = OpSLessThan %17 %46 %16
OpBranchConditional %47 %42 %43
%42 = OpLabel
OpBranch %44
%44 = OpLabel
%48 = OpLoad %6 %40
%49 = OpIAdd %6 %48 %28
OpStore %40 %49
OpBranch %41
%43 = OpLabel
OpBranch %35
%36 = OpLabel
%50 = OpLoad %6 %32
%51 = OpIAdd %6 %50 %28
OpStore %32 %51
OpBranch %33
%35 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_2, context.get());
ASSERT_TRUE(ops[3]->PreconditionHolds());
ops[3]->TryToApply();
CheckValid(env, context.get());
std::string after_op_3 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%28 = OpConstant %6 1
%52 = OpConstantTrue %17
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
%32 = OpVariable %7 Function
%40 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %52 %14 %12
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpStore %19 %9
OpBranch %20
%20 = OpLabel
OpSelectionMerge %22 None
OpBranchConditional %52 %24 %22
%24 = OpLabel
%25 = OpLoad %6 %19
%26 = OpSLessThan %17 %25 %16
OpBranchConditional %26 %21 %22
%21 = OpLabel
OpBranch %22
%23 = OpLabel
%27 = OpLoad %6 %19
%29 = OpIAdd %6 %27 %28
OpStore %19 %29
OpBranch %20
%22 = OpLabel
OpBranch %12
%13 = OpLabel
%30 = OpLoad %6 %8
%31 = OpIAdd %6 %30 %28
OpStore %8 %31
OpBranch %10
%12 = OpLabel
OpStore %32 %9
OpBranch %33
%33 = OpLabel
OpSelectionMerge %35 None
OpBranchConditional %52 %37 %35
%37 = OpLabel
%38 = OpLoad %6 %32
%39 = OpSLessThan %17 %38 %16
OpBranchConditional %39 %34 %35
%34 = OpLabel
OpStore %40 %9
OpBranch %41
%41 = OpLabel
OpSelectionMerge %43 None
OpBranchConditional %52 %45 %43
%45 = OpLabel
%46 = OpLoad %6 %40
%47 = OpSLessThan %17 %46 %16
OpBranchConditional %47 %42 %43
%42 = OpLabel
OpBranch %43
%44 = OpLabel
%48 = OpLoad %6 %40
%49 = OpIAdd %6 %48 %28
OpStore %40 %49
OpBranch %41
%43 = OpLabel
OpBranch %35
%36 = OpLabel
%50 = OpLoad %6 %32
%51 = OpIAdd %6 %50 %28
OpStore %32 %51
OpBranch %33
%35 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_3, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader3) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 10
%16 = OpConstant %6 0
%17 = OpTypeBool
%20 = OpConstant %6 1
%23 = OpConstant %6 3
%40 = OpConstant %6 5
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSGreaterThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%19 = OpLoad %6 %8
%21 = OpISub %6 %19 %20
OpStore %8 %21
%22 = OpLoad %6 %8
%24 = OpSLessThan %17 %22 %23
OpSelectionMerge %26 None
OpBranchConditional %24 %25 %26
%25 = OpLabel
OpBranch %13
%26 = OpLabel
OpBranch %28
%28 = OpLabel
OpLoopMerge %30 %31 None
OpBranch %29
%29 = OpLabel
%32 = OpLoad %6 %8
%33 = OpISub %6 %32 %20
OpStore %8 %33
%34 = OpLoad %6 %8
%35 = OpIEqual %17 %34 %20
OpSelectionMerge %37 None
OpBranchConditional %35 %36 %37
%36 = OpLabel
OpReturn ; This return spoils everything: it means the merge does not post-dominate the header.
%37 = OpLabel
OpBranch %31
%31 = OpLabel
%39 = OpLoad %6 %8
%41 = OpSGreaterThan %17 %39 %40
OpBranchConditional %41 %28 %30
%30 = OpLabel
OpBranch %13
%13 = OpLabel
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(0, ops.size());
}
TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader4) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %6 %7
%13 = OpConstant %6 0
%22 = OpTypeBool
%25 = OpConstant %6 1
%39 = OpConstant %6 100
%4 = OpFunction %2 None %3
%5 = OpLabel
%45 = OpVariable %7 Function
%46 = OpVariable %7 Function
%47 = OpVariable %7 Function
%32 = OpVariable %7 Function
%42 = OpVariable %7 Function
OpStore %32 %13
OpBranch %33
%33 = OpLabel
OpLoopMerge %35 %36 None
OpBranch %37
%37 = OpLabel
%38 = OpLoad %6 %32
%40 = OpSLessThan %22 %38 %39
OpBranchConditional %40 %34 %35
%34 = OpLabel
OpBranch %36
%36 = OpLabel
%41 = OpLoad %6 %32
OpStore %42 %25
OpStore %45 %13
OpStore %46 %13
OpBranch %48
%48 = OpLabel
OpLoopMerge %49 %50 None
OpBranch %51
%51 = OpLabel
%52 = OpLoad %6 %46
%53 = OpLoad %6 %42
%54 = OpSLessThan %22 %52 %53
OpBranchConditional %54 %55 %49
%55 = OpLabel
%56 = OpLoad %6 %45
%57 = OpIAdd %6 %56 %25
OpStore %45 %57
OpBranch %50
%50 = OpLabel
%58 = OpLoad %6 %46
%59 = OpIAdd %6 %58 %25
OpStore %46 %59
OpBranch %48
%49 = OpLabel
%60 = OpLoad %6 %45
OpStore %47 %60
%43 = OpLoad %6 %47
%44 = OpIAdd %6 %41 %43
OpStore %32 %44
OpBranch %33
%35 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// Initially there are two opportunities.
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %6 %7
%13 = OpConstant %6 0
%22 = OpTypeBool
%25 = OpConstant %6 1
%39 = OpConstant %6 100
%61 = OpConstantTrue %22
%62 = OpUndef %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%45 = OpVariable %7 Function
%46 = OpVariable %7 Function
%47 = OpVariable %7 Function
%32 = OpVariable %7 Function
%42 = OpVariable %7 Function
OpStore %32 %13
OpBranch %33
%33 = OpLabel
OpSelectionMerge %35 None
OpBranchConditional %61 %37 %35
%37 = OpLabel
%38 = OpLoad %6 %32
%40 = OpSLessThan %22 %38 %39
OpBranchConditional %40 %34 %35
%34 = OpLabel
OpBranch %35
%36 = OpLabel
%41 = OpLoad %6 %32
OpStore %42 %25
OpStore %45 %13
OpStore %46 %13
OpBranch %48
%48 = OpLabel
OpLoopMerge %49 %50 None
OpBranch %51
%51 = OpLabel
%52 = OpLoad %6 %46
%53 = OpLoad %6 %42
%54 = OpSLessThan %22 %52 %53
OpBranchConditional %54 %55 %49
%55 = OpLabel
%56 = OpLoad %6 %45
%57 = OpIAdd %6 %56 %25
OpStore %45 %57
OpBranch %50
%50 = OpLabel
%58 = OpLoad %6 %46
%59 = OpIAdd %6 %58 %25
OpStore %46 %59
OpBranch %48
%49 = OpLabel
%60 = OpLoad %6 %45
OpStore %47 %60
%43 = OpLoad %6 %47
%44 = OpIAdd %6 %62 %43
OpStore %32 %44
OpBranch %33
%35 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
// Applying the first opportunity has killed the second opportunity, because
// there was a loop embedded in the continue target of the loop we have just
// eliminated; the continue-embedded loop is now unreachable.
ASSERT_FALSE(ops[1]->PreconditionHolds());
}
TEST(StructuredLoopToSelectionReductionPassTest, ConditionalBreak1) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeBool
%11 = OpConstantFalse %10
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpLoopMerge %8 %9 None
OpBranch %7
%7 = OpLabel
OpSelectionMerge %13 None
OpBranchConditional %11 %12 %13
%12 = OpLabel
OpBranch %8
%13 = OpLabel
OpBranch %9
%9 = OpLabel
OpBranchConditional %11 %6 %8
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeBool
%11 = OpConstantFalse %10
%14 = OpConstantTrue %10
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpSelectionMerge %8 None
OpBranchConditional %14 %7 %8
%7 = OpLabel
OpSelectionMerge %13 None
OpBranchConditional %11 %12 %13
%12 = OpLabel
OpBranch %13
%13 = OpLabel
OpBranch %8
%9 = OpLabel
OpBranchConditional %11 %6 %8
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, ConditionalBreak2) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeBool
%11 = OpConstantFalse %10
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpLoopMerge %8 %9 None
OpBranch %7
%7 = OpLabel
OpSelectionMerge %13 None
OpBranchConditional %11 %8 %13
%13 = OpLabel
OpBranch %9
%9 = OpLabel
OpBranchConditional %11 %6 %8
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeBool
%11 = OpConstantFalse %10
%14 = OpConstantTrue %10
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpSelectionMerge %8 None
OpBranchConditional %14 %7 %8
%7 = OpLabel
OpSelectionMerge %13 None
OpBranchConditional %11 %13 %13
%13 = OpLabel
OpBranch %8
%9 = OpLabel
OpBranchConditional %11 %6 %8
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, UnconditionalBreak) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpLoopMerge %8 %9 None
OpBranch %7
%7 = OpLabel
OpBranch %8
%9 = OpLabel
OpBranch %6
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeBool
%11 = OpConstantTrue %10
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpSelectionMerge %8 None
OpBranchConditional %11 %7 %8
%7 = OpLabel
OpBranch %8
%9 = OpLabel
OpBranch %6
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, Complex) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %4 0 Offset 0
OpMemberDecorate %4 1 Offset 4
OpMemberDecorate %4 2 Offset 8
OpMemberDecorate %4 3 Offset 12
OpDecorate %4 Block
OpDecorate %5 DescriptorSet 0
OpDecorate %5 Binding 0
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeBool
%9 = OpTypePointer Function %8
%10 = OpTypeInt 32 1
%4 = OpTypeStruct %10 %10 %10 %10
%11 = OpTypePointer Uniform %4
%5 = OpVariable %11 Uniform
%12 = OpConstant %10 0
%13 = OpTypePointer Uniform %10
%14 = OpTypeInt 32 0
%15 = OpConstant %14 0
%16 = OpConstant %10 1
%17 = OpConstant %10 2
%18 = OpConstant %10 3
%19 = OpTypePointer Function %10
%20 = OpConstantFalse %8
%21 = OpTypeFloat 32
%22 = OpTypeVector %21 4
%23 = OpTypePointer Output %22
%3 = OpVariable %23 Output
%2 = OpFunction %6 None %7
%24 = OpLabel
%25 = OpVariable %9 Function
%26 = OpVariable %9 Function
%27 = OpVariable %9 Function
%28 = OpVariable %9 Function
%29 = OpVariable %9 Function
%30 = OpVariable %19 Function
%31 = OpAccessChain %13 %5 %12
%32 = OpLoad %10 %31
%33 = OpINotEqual %8 %32 %15
OpStore %25 %33
%34 = OpAccessChain %13 %5 %16
%35 = OpLoad %10 %34
%36 = OpINotEqual %8 %35 %15
OpStore %26 %36
%37 = OpAccessChain %13 %5 %17
%38 = OpLoad %10 %37
%39 = OpINotEqual %8 %38 %15
OpStore %27 %39
%40 = OpAccessChain %13 %5 %18
%41 = OpLoad %10 %40
%42 = OpINotEqual %8 %41 %15
OpStore %28 %42
%43 = OpLoad %8 %25
OpStore %29 %43
OpStore %30 %12
OpBranch %44
%44 = OpLabel
OpLoopMerge %45 %46 None
OpBranch %47
%47 = OpLabel
%48 = OpLoad %8 %29
OpBranchConditional %48 %49 %45
%49 = OpLabel
%50 = OpLoad %8 %25
OpSelectionMerge %51 None
OpBranchConditional %50 %52 %51
%52 = OpLabel
%53 = OpLoad %8 %26
OpStore %29 %53
%54 = OpLoad %10 %30
%55 = OpIAdd %10 %54 %16
OpStore %30 %55
OpBranch %51
%51 = OpLabel
%56 = OpLoad %8 %26
OpSelectionMerge %57 None
OpBranchConditional %56 %58 %57
%58 = OpLabel
%59 = OpLoad %10 %30
%60 = OpIAdd %10 %59 %16
OpStore %30 %60
%61 = OpLoad %8 %29
%62 = OpLoad %8 %25
%63 = OpLogicalOr %8 %61 %62
OpStore %29 %63
%64 = OpLoad %8 %27
OpSelectionMerge %65 None
OpBranchConditional %64 %66 %65
%66 = OpLabel
%67 = OpLoad %10 %30
%68 = OpIAdd %10 %67 %17
OpStore %30 %68
%69 = OpLoad %8 %29
%70 = OpLogicalNot %8 %69
OpStore %29 %70
OpBranch %46
%65 = OpLabel
%71 = OpLoad %8 %29
%72 = OpLogicalOr %8 %71 %20
OpStore %29 %72
OpBranch %46
%57 = OpLabel
OpBranch %73
%73 = OpLabel
OpLoopMerge %74 %75 None
OpBranch %76
%76 = OpLabel
%77 = OpLoad %8 %28
OpSelectionMerge %78 None
OpBranchConditional %77 %79 %80
%79 = OpLabel
%81 = OpLoad %10 %30
OpSelectionMerge %82 None
OpSwitch %81 %83 1 %84 2 %85
%83 = OpLabel
OpBranch %82
%84 = OpLabel
%86 = OpLoad %8 %29
%87 = OpSelect %10 %86 %16 %17
%88 = OpLoad %10 %30
%89 = OpIAdd %10 %88 %87
OpStore %30 %89
OpBranch %82
%85 = OpLabel
OpBranch %75
%82 = OpLabel
%90 = OpLoad %8 %27
OpSelectionMerge %91 None
OpBranchConditional %90 %92 %91
%92 = OpLabel
OpBranch %75
%91 = OpLabel
OpBranch %78
%80 = OpLabel
OpBranch %74
%78 = OpLabel
OpBranch %75
%75 = OpLabel
%93 = OpLoad %8 %29
OpBranchConditional %93 %73 %74
%74 = OpLabel
OpBranch %46
%46 = OpLabel
OpBranch %44
%45 = OpLabel
%94 = OpLoad %10 %30
%95 = OpConvertSToF %21 %94
%96 = OpCompositeConstruct %22 %95 %95 %95 %95
OpStore %3 %96
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %4 0 Offset 0
OpMemberDecorate %4 1 Offset 4
OpMemberDecorate %4 2 Offset 8
OpMemberDecorate %4 3 Offset 12
OpDecorate %4 Block
OpDecorate %5 DescriptorSet 0
OpDecorate %5 Binding 0
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeBool
%9 = OpTypePointer Function %8
%10 = OpTypeInt 32 1
%4 = OpTypeStruct %10 %10 %10 %10
%11 = OpTypePointer Uniform %4
%5 = OpVariable %11 Uniform
%12 = OpConstant %10 0
%13 = OpTypePointer Uniform %10
%14 = OpTypeInt 32 0
%15 = OpConstant %14 0
%16 = OpConstant %10 1
%17 = OpConstant %10 2
%18 = OpConstant %10 3
%19 = OpTypePointer Function %10
%20 = OpConstantFalse %8
%21 = OpTypeFloat 32
%22 = OpTypeVector %21 4
%23 = OpTypePointer Output %22
%3 = OpVariable %23 Output
%97 = OpConstantTrue %8
%2 = OpFunction %6 None %7
%24 = OpLabel
%25 = OpVariable %9 Function
%26 = OpVariable %9 Function
%27 = OpVariable %9 Function
%28 = OpVariable %9 Function
%29 = OpVariable %9 Function
%30 = OpVariable %19 Function
%31 = OpAccessChain %13 %5 %12
%32 = OpLoad %10 %31
%33 = OpINotEqual %8 %32 %15
OpStore %25 %33
%34 = OpAccessChain %13 %5 %16
%35 = OpLoad %10 %34
%36 = OpINotEqual %8 %35 %15
OpStore %26 %36
%37 = OpAccessChain %13 %5 %17
%38 = OpLoad %10 %37
%39 = OpINotEqual %8 %38 %15
OpStore %27 %39
%40 = OpAccessChain %13 %5 %18
%41 = OpLoad %10 %40
%42 = OpINotEqual %8 %41 %15
OpStore %28 %42
%43 = OpLoad %8 %25
OpStore %29 %43
OpStore %30 %12
OpBranch %44
%44 = OpLabel
OpSelectionMerge %45 None ; Was OpLoopMerge %45 %46 None
OpBranchConditional %97 %47 %45 ; Was OpBranch %47
%47 = OpLabel
%48 = OpLoad %8 %29
OpBranchConditional %48 %49 %45
%49 = OpLabel
%50 = OpLoad %8 %25
OpSelectionMerge %51 None
OpBranchConditional %50 %52 %51
%52 = OpLabel
%53 = OpLoad %8 %26
OpStore %29 %53
%54 = OpLoad %10 %30
%55 = OpIAdd %10 %54 %16
OpStore %30 %55
OpBranch %51
%51 = OpLabel
%56 = OpLoad %8 %26
OpSelectionMerge %57 None
OpBranchConditional %56 %58 %57
%58 = OpLabel
%59 = OpLoad %10 %30
%60 = OpIAdd %10 %59 %16
OpStore %30 %60
%61 = OpLoad %8 %29
%62 = OpLoad %8 %25
%63 = OpLogicalOr %8 %61 %62
OpStore %29 %63
%64 = OpLoad %8 %27
OpSelectionMerge %65 None
OpBranchConditional %64 %66 %65
%66 = OpLabel
%67 = OpLoad %10 %30
%68 = OpIAdd %10 %67 %17
OpStore %30 %68
%69 = OpLoad %8 %29
%70 = OpLogicalNot %8 %69
OpStore %29 %70
OpBranch %65 ; Was OpBranch %46
%65 = OpLabel
%71 = OpLoad %8 %29
%72 = OpLogicalOr %8 %71 %20
OpStore %29 %72
OpBranch %57 ; Was OpBranch %46
%57 = OpLabel
OpBranch %73
%73 = OpLabel
OpLoopMerge %74 %75 None
OpBranch %76
%76 = OpLabel
%77 = OpLoad %8 %28
OpSelectionMerge %78 None
OpBranchConditional %77 %79 %80
%79 = OpLabel
%81 = OpLoad %10 %30
OpSelectionMerge %82 None
OpSwitch %81 %83 1 %84 2 %85
%83 = OpLabel
OpBranch %82
%84 = OpLabel
%86 = OpLoad %8 %29
%87 = OpSelect %10 %86 %16 %17
%88 = OpLoad %10 %30
%89 = OpIAdd %10 %88 %87
OpStore %30 %89
OpBranch %82
%85 = OpLabel
OpBranch %75
%82 = OpLabel
%90 = OpLoad %8 %27
OpSelectionMerge %91 None
OpBranchConditional %90 %92 %91
%92 = OpLabel
OpBranch %75
%91 = OpLabel
OpBranch %78
%80 = OpLabel
OpBranch %74
%78 = OpLabel
OpBranch %75
%75 = OpLabel
%93 = OpLoad %8 %29
OpBranchConditional %93 %73 %74
%74 = OpLabel
OpBranch %45 ; Was OpBranch %46
%46 = OpLabel
OpBranch %44
%45 = OpLabel
%94 = OpLoad %10 %30
%95 = OpConvertSToF %21 %94
%96 = OpCompositeConstruct %22 %95 %95 %95 %95
OpStore %3 %96
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
ASSERT_TRUE(ops[1]->PreconditionHolds());
ops[1]->TryToApply();
CheckValid(env, context.get());
std::string after_op_1 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %4 0 Offset 0
OpMemberDecorate %4 1 Offset 4
OpMemberDecorate %4 2 Offset 8
OpMemberDecorate %4 3 Offset 12
OpDecorate %4 Block
OpDecorate %5 DescriptorSet 0
OpDecorate %5 Binding 0
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeBool
%9 = OpTypePointer Function %8
%10 = OpTypeInt 32 1
%4 = OpTypeStruct %10 %10 %10 %10
%11 = OpTypePointer Uniform %4
%5 = OpVariable %11 Uniform
%12 = OpConstant %10 0
%13 = OpTypePointer Uniform %10
%14 = OpTypeInt 32 0
%15 = OpConstant %14 0
%16 = OpConstant %10 1
%17 = OpConstant %10 2
%18 = OpConstant %10 3
%19 = OpTypePointer Function %10
%20 = OpConstantFalse %8
%21 = OpTypeFloat 32
%22 = OpTypeVector %21 4
%23 = OpTypePointer Output %22
%3 = OpVariable %23 Output
%97 = OpConstantTrue %8
%2 = OpFunction %6 None %7
%24 = OpLabel
%25 = OpVariable %9 Function
%26 = OpVariable %9 Function
%27 = OpVariable %9 Function
%28 = OpVariable %9 Function
%29 = OpVariable %9 Function
%30 = OpVariable %19 Function
%31 = OpAccessChain %13 %5 %12
%32 = OpLoad %10 %31
%33 = OpINotEqual %8 %32 %15
OpStore %25 %33
%34 = OpAccessChain %13 %5 %16
%35 = OpLoad %10 %34
%36 = OpINotEqual %8 %35 %15
OpStore %26 %36
%37 = OpAccessChain %13 %5 %17
%38 = OpLoad %10 %37
%39 = OpINotEqual %8 %38 %15
OpStore %27 %39
%40 = OpAccessChain %13 %5 %18
%41 = OpLoad %10 %40
%42 = OpINotEqual %8 %41 %15
OpStore %28 %42
%43 = OpLoad %8 %25
OpStore %29 %43
OpStore %30 %12
OpBranch %44
%44 = OpLabel
OpSelectionMerge %45 None ; Was OpLoopMerge %45 %46 None
OpBranchConditional %97 %47 %45 ; Was OpBranch %47
%47 = OpLabel
%48 = OpLoad %8 %29
OpBranchConditional %48 %49 %45
%49 = OpLabel
%50 = OpLoad %8 %25
OpSelectionMerge %51 None
OpBranchConditional %50 %52 %51
%52 = OpLabel
%53 = OpLoad %8 %26
OpStore %29 %53
%54 = OpLoad %10 %30
%55 = OpIAdd %10 %54 %16
OpStore %30 %55
OpBranch %51
%51 = OpLabel
%56 = OpLoad %8 %26
OpSelectionMerge %57 None
OpBranchConditional %56 %58 %57
%58 = OpLabel
%59 = OpLoad %10 %30
%60 = OpIAdd %10 %59 %16
OpStore %30 %60
%61 = OpLoad %8 %29
%62 = OpLoad %8 %25
%63 = OpLogicalOr %8 %61 %62
OpStore %29 %63
%64 = OpLoad %8 %27
OpSelectionMerge %65 None
OpBranchConditional %64 %66 %65
%66 = OpLabel
%67 = OpLoad %10 %30
%68 = OpIAdd %10 %67 %17
OpStore %30 %68
%69 = OpLoad %8 %29
%70 = OpLogicalNot %8 %69
OpStore %29 %70
OpBranch %65 ; Was OpBranch %46
%65 = OpLabel
%71 = OpLoad %8 %29
%72 = OpLogicalOr %8 %71 %20
OpStore %29 %72
OpBranch %57 ; Was OpBranch %46
%57 = OpLabel
OpBranch %73
%73 = OpLabel
OpSelectionMerge %74 None ; Was OpLoopMerge %74 %75 None
OpBranchConditional %97 %76 %74 ; Was OpBranch %76
%76 = OpLabel
%77 = OpLoad %8 %28
OpSelectionMerge %78 None
OpBranchConditional %77 %79 %80
%79 = OpLabel
%81 = OpLoad %10 %30
OpSelectionMerge %82 None
OpSwitch %81 %83 1 %84 2 %85
%83 = OpLabel
OpBranch %82
%84 = OpLabel
%86 = OpLoad %8 %29
%87 = OpSelect %10 %86 %16 %17
%88 = OpLoad %10 %30
%89 = OpIAdd %10 %88 %87
OpStore %30 %89
OpBranch %82
%85 = OpLabel
OpBranch %82
%82 = OpLabel
%90 = OpLoad %8 %27
OpSelectionMerge %91 None
OpBranchConditional %90 %92 %91
%92 = OpLabel
OpBranch %91
%91 = OpLabel
OpBranch %78
%80 = OpLabel
OpBranch %78 ; Was OpBranch %74
%78 = OpLabel
OpBranch %74
%75 = OpLabel
%93 = OpLoad %8 %29
OpBranchConditional %93 %73 %74
%74 = OpLabel
OpBranch %45 ; Was OpBranch %46
%46 = OpLabel
OpBranch %44
%45 = OpLabel
%94 = OpLoad %10 %30
%95 = OpConvertSToF %21 %94
%96 = OpCompositeConstruct %22 %95 %95 %95 %95
OpStore %3 %96
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_1, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, ComplexOptimized) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %4 0 Offset 0
OpMemberDecorate %4 1 Offset 4
OpMemberDecorate %4 2 Offset 8
OpMemberDecorate %4 3 Offset 12
OpDecorate %4 Block
OpDecorate %5 DescriptorSet 0
OpDecorate %5 Binding 0
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeBool
%10 = OpTypeInt 32 1
%4 = OpTypeStruct %10 %10 %10 %10
%11 = OpTypePointer Uniform %4
%5 = OpVariable %11 Uniform
%12 = OpConstant %10 0
%13 = OpTypePointer Uniform %10
%14 = OpTypeInt 32 0
%15 = OpConstant %14 0
%16 = OpConstant %10 1
%17 = OpConstant %10 2
%18 = OpConstant %10 3
%20 = OpConstantFalse %8
%21 = OpTypeFloat 32
%22 = OpTypeVector %21 4
%23 = OpTypePointer Output %22
%3 = OpVariable %23 Output
%2 = OpFunction %6 None %7
%24 = OpLabel
%31 = OpAccessChain %13 %5 %12
%32 = OpLoad %10 %31
%33 = OpINotEqual %8 %32 %15
%34 = OpAccessChain %13 %5 %16
%35 = OpLoad %10 %34
%36 = OpINotEqual %8 %35 %15
%37 = OpAccessChain %13 %5 %17
%38 = OpLoad %10 %37
%39 = OpINotEqual %8 %38 %15
%40 = OpAccessChain %13 %5 %18
%41 = OpLoad %10 %40
%42 = OpINotEqual %8 %41 %15
OpBranch %44
%44 = OpLabel
%98 = OpPhi %10 %12 %24 %107 %46
%97 = OpPhi %8 %33 %24 %105 %46
OpLoopMerge %45 %46 None
OpBranchConditional %97 %49 %45
%49 = OpLabel
OpSelectionMerge %51 None
OpBranchConditional %33 %52 %51
%52 = OpLabel
%55 = OpIAdd %10 %98 %16
OpBranch %51
%51 = OpLabel
%100 = OpPhi %10 %98 %49 %55 %52
%113 = OpSelect %8 %33 %36 %97
OpSelectionMerge %57 None
OpBranchConditional %36 %58 %57
%58 = OpLabel
%60 = OpIAdd %10 %100 %16
%63 = OpLogicalOr %8 %113 %33
OpSelectionMerge %65 None
OpBranchConditional %39 %66 %65
%66 = OpLabel
%68 = OpIAdd %10 %100 %18
%70 = OpLogicalNot %8 %63
OpBranch %46
%65 = OpLabel
%72 = OpLogicalOr %8 %63 %20
OpBranch %46
%57 = OpLabel
OpBranch %73
%73 = OpLabel
%99 = OpPhi %10 %100 %57 %109 %75
OpLoopMerge %74 %75 None
OpBranch %76
%76 = OpLabel
OpSelectionMerge %78 None
OpBranchConditional %42 %79 %80
%79 = OpLabel
OpSelectionMerge %82 None
OpSwitch %99 %83 1 %84 2 %85
%83 = OpLabel
OpBranch %82
%84 = OpLabel
%87 = OpSelect %10 %113 %16 %17
%89 = OpIAdd %10 %99 %87
OpBranch %82
%85 = OpLabel
OpBranch %75
%82 = OpLabel
%110 = OpPhi %10 %99 %83 %89 %84
OpSelectionMerge %91 None
OpBranchConditional %39 %92 %91
%92 = OpLabel
OpBranch %75
%91 = OpLabel
OpBranch %78
%80 = OpLabel
OpBranch %74
%78 = OpLabel
OpBranch %75
%75 = OpLabel
%109 = OpPhi %10 %99 %85 %110 %92 %110 %78
OpBranchConditional %113 %73 %74
%74 = OpLabel
%108 = OpPhi %10 %99 %80 %109 %75
OpBranch %46
%46 = OpLabel
%107 = OpPhi %10 %68 %66 %60 %65 %108 %74
%105 = OpPhi %8 %70 %66 %72 %65 %113 %74
OpBranch %44
%45 = OpLabel
%95 = OpConvertSToF %21 %98
%96 = OpCompositeConstruct %22 %95 %95 %95 %95
OpStore %3 %96
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %4 0 Offset 0
OpMemberDecorate %4 1 Offset 4
OpMemberDecorate %4 2 Offset 8
OpMemberDecorate %4 3 Offset 12
OpDecorate %4 Block
OpDecorate %5 DescriptorSet 0
OpDecorate %5 Binding 0
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeBool
%10 = OpTypeInt 32 1
%4 = OpTypeStruct %10 %10 %10 %10
%11 = OpTypePointer Uniform %4
%5 = OpVariable %11 Uniform
%12 = OpConstant %10 0
%13 = OpTypePointer Uniform %10
%14 = OpTypeInt 32 0
%15 = OpConstant %14 0
%16 = OpConstant %10 1
%17 = OpConstant %10 2
%18 = OpConstant %10 3
%20 = OpConstantFalse %8
%21 = OpTypeFloat 32
%22 = OpTypeVector %21 4
%23 = OpTypePointer Output %22
%3 = OpVariable %23 Output
%114 = OpUndef %10
%115 = OpUndef %8
%2 = OpFunction %6 None %7
%24 = OpLabel
%31 = OpAccessChain %13 %5 %12
%32 = OpLoad %10 %31
%33 = OpINotEqual %8 %32 %15
%34 = OpAccessChain %13 %5 %16
%35 = OpLoad %10 %34
%36 = OpINotEqual %8 %35 %15
%37 = OpAccessChain %13 %5 %17
%38 = OpLoad %10 %37
%39 = OpINotEqual %8 %38 %15
%40 = OpAccessChain %13 %5 %18
%41 = OpLoad %10 %40
%42 = OpINotEqual %8 %41 %15
OpBranch %44
%44 = OpLabel
%98 = OpPhi %10 %12 %24 %114 %46
%97 = OpPhi %8 %33 %24 %115 %46
OpSelectionMerge %45 None ; Was OpLoopMerge %45 %46 None
OpBranchConditional %97 %49 %45
%49 = OpLabel
OpSelectionMerge %51 None
OpBranchConditional %33 %52 %51
%52 = OpLabel
%55 = OpIAdd %10 %98 %16
OpBranch %51
%51 = OpLabel
%100 = OpPhi %10 %98 %49 %55 %52
%113 = OpSelect %8 %33 %36 %97
OpSelectionMerge %57 None
OpBranchConditional %36 %58 %57
%58 = OpLabel
%60 = OpIAdd %10 %100 %16
%63 = OpLogicalOr %8 %113 %33
OpSelectionMerge %65 None
OpBranchConditional %39 %66 %65
%66 = OpLabel
%68 = OpIAdd %10 %100 %18
%70 = OpLogicalNot %8 %63
OpBranch %65 ; Was OpBranch %46
%65 = OpLabel
%72 = OpLogicalOr %8 %63 %20
OpBranch %57 ; Was OpBranch %46
%57 = OpLabel
OpBranch %73
%73 = OpLabel
%99 = OpPhi %10 %100 %57 %109 %75
OpLoopMerge %74 %75 None
OpBranch %76
%76 = OpLabel
OpSelectionMerge %78 None
OpBranchConditional %42 %79 %80
%79 = OpLabel
OpSelectionMerge %82 None
OpSwitch %99 %83 1 %84 2 %85
%83 = OpLabel
OpBranch %82
%84 = OpLabel
%87 = OpSelect %10 %113 %16 %17
%89 = OpIAdd %10 %99 %87
OpBranch %82
%85 = OpLabel
OpBranch %75
%82 = OpLabel
%110 = OpPhi %10 %99 %83 %89 %84
OpSelectionMerge %91 None
OpBranchConditional %39 %92 %91
%92 = OpLabel
OpBranch %75
%91 = OpLabel
OpBranch %78
%80 = OpLabel
OpBranch %74
%78 = OpLabel
OpBranch %75
%75 = OpLabel
%109 = OpPhi %10 %99 %85 %110 %92 %110 %78
OpBranchConditional %113 %73 %74
%74 = OpLabel
%108 = OpPhi %10 %99 %80 %109 %75
OpBranch %45 ; Was OpBranch %46
%46 = OpLabel
%107 = OpPhi %10 ; Was OpPhi %10 %68 %66 %60 %65 %108 %74
%105 = OpPhi %8 ; Was OpPhi %8 %70 %66 %72 %65 %113 %74
OpBranch %44
%45 = OpLabel
%95 = OpConvertSToF %21 %98
%96 = OpCompositeConstruct %22 %95 %95 %95 %95
OpStore %3 %96
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
ASSERT_TRUE(ops[1]->PreconditionHolds());
ops[1]->TryToApply();
CheckValid(env, context.get());
std::string after_op_1 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %4 0 Offset 0
OpMemberDecorate %4 1 Offset 4
OpMemberDecorate %4 2 Offset 8
OpMemberDecorate %4 3 Offset 12
OpDecorate %4 Block
OpDecorate %5 DescriptorSet 0
OpDecorate %5 Binding 0
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeBool
%10 = OpTypeInt 32 1
%4 = OpTypeStruct %10 %10 %10 %10
%11 = OpTypePointer Uniform %4
%5 = OpVariable %11 Uniform
%12 = OpConstant %10 0
%13 = OpTypePointer Uniform %10
%14 = OpTypeInt 32 0
%15 = OpConstant %14 0
%16 = OpConstant %10 1
%17 = OpConstant %10 2
%18 = OpConstant %10 3
%20 = OpConstantFalse %8
%21 = OpTypeFloat 32
%22 = OpTypeVector %21 4
%23 = OpTypePointer Output %22
%3 = OpVariable %23 Output
%114 = OpUndef %10
%115 = OpUndef %8
%116 = OpConstantTrue %8
%2 = OpFunction %6 None %7
%24 = OpLabel
%31 = OpAccessChain %13 %5 %12
%32 = OpLoad %10 %31
%33 = OpINotEqual %8 %32 %15
%34 = OpAccessChain %13 %5 %16
%35 = OpLoad %10 %34
%36 = OpINotEqual %8 %35 %15
%37 = OpAccessChain %13 %5 %17
%38 = OpLoad %10 %37
%39 = OpINotEqual %8 %38 %15
%40 = OpAccessChain %13 %5 %18
%41 = OpLoad %10 %40
%42 = OpINotEqual %8 %41 %15
OpBranch %44
%44 = OpLabel
%98 = OpPhi %10 %12 %24 %114 %46
%97 = OpPhi %8 %33 %24 %115 %46
OpSelectionMerge %45 None ; Was OpLoopMerge %45 %46 None
OpBranchConditional %97 %49 %45
%49 = OpLabel
OpSelectionMerge %51 None
OpBranchConditional %33 %52 %51
%52 = OpLabel
%55 = OpIAdd %10 %98 %16
OpBranch %51
%51 = OpLabel
%100 = OpPhi %10 %98 %49 %55 %52
%113 = OpSelect %8 %33 %36 %97
OpSelectionMerge %57 None
OpBranchConditional %36 %58 %57
%58 = OpLabel
%60 = OpIAdd %10 %100 %16
%63 = OpLogicalOr %8 %113 %33
OpSelectionMerge %65 None
OpBranchConditional %39 %66 %65
%66 = OpLabel
%68 = OpIAdd %10 %100 %18
%70 = OpLogicalNot %8 %63
OpBranch %65 ; Was OpBranch %46
%65 = OpLabel
%72 = OpLogicalOr %8 %63 %20
OpBranch %57 ; Was OpBranch %46
%57 = OpLabel
OpBranch %73
%73 = OpLabel
%99 = OpPhi %10 %100 %57 %114 %75
OpSelectionMerge %74 None ; Was OpLoopMerge %74 %75 None
OpBranchConditional %116 %76 %74
%76 = OpLabel
OpSelectionMerge %78 None
OpBranchConditional %42 %79 %80
%79 = OpLabel
OpSelectionMerge %82 None
OpSwitch %99 %83 1 %84 2 %85
%83 = OpLabel
OpBranch %82
%84 = OpLabel
%87 = OpSelect %10 %113 %16 %17
%89 = OpIAdd %10 %99 %87
OpBranch %82
%85 = OpLabel
OpBranch %82 ; Was OpBranch %75
%82 = OpLabel
%110 = OpPhi %10 %99 %83 %89 %84 %114 %85 ; Was OpPhi %10 %99 %83 %89 %84
OpSelectionMerge %91 None
OpBranchConditional %39 %92 %91
%92 = OpLabel
OpBranch %91 ; OpBranch %75
%91 = OpLabel
OpBranch %78
%80 = OpLabel
OpBranch %78 ; Was OpBranch %74
%78 = OpLabel
OpBranch %74 ; Was OpBranch %75
%75 = OpLabel
%109 = OpPhi %10 ; Was OpPhi %10 %99 %85 %110 %92 %110 %78
OpBranchConditional %115 %73 %74
%74 = OpLabel
%108 = OpPhi %10 %114 %75 %114 %78 %114 %73 ; Was OpPhi %10 %99 %80 %109 %75
OpBranch %45 ; Was OpBranch %46
%46 = OpLabel
%107 = OpPhi %10 ; Was OpPhi %10 %68 %66 %60 %65 %108 %74
%105 = OpPhi %8 ; Was OpPhi %8 %70 %66 %72 %65 %113 %74
OpBranch %44
%45 = OpLabel
%95 = OpConvertSToF %21 %98
%96 = OpCompositeConstruct %22 %95 %95 %95 %95
OpStore %3 %96
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_1, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, DominanceIssue) {
// Exposes a scenario where redirecting edges results in uses of ids being
// non-dominated. We replace such uses with OpUndef to account for this.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%5 = OpTypeInt 32 1
%7 = OpTypePointer Function %5
%6 = OpTypeBool
%8 = OpConstantTrue %6
%9 = OpConstant %5 10
%10 = OpConstant %5 20
%11 = OpConstant %5 30
%4 = OpFunction %2 None %3
%12 = OpLabel
OpBranch %13
%13 = OpLabel
OpLoopMerge %14 %15 None
OpBranch %16
%16 = OpLabel
OpSelectionMerge %17 None
OpBranchConditional %8 %18 %19
%18 = OpLabel
OpBranch %14
%19 = OpLabel
%20 = OpIAdd %5 %9 %10
OpBranch %17
%17 = OpLabel
%21 = OpIAdd %5 %20 %11
OpBranchConditional %8 %14 %15
%15 = OpLabel
OpBranch %13
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string expected = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%5 = OpTypeInt 32 1
%7 = OpTypePointer Function %5
%6 = OpTypeBool
%8 = OpConstantTrue %6
%9 = OpConstant %5 10
%10 = OpConstant %5 20
%11 = OpConstant %5 30
%22 = OpUndef %5
%4 = OpFunction %2 None %3
%12 = OpLabel
OpBranch %13
%13 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %8 %16 %14
%16 = OpLabel
OpSelectionMerge %17 None
OpBranchConditional %8 %18 %19
%18 = OpLabel
OpBranch %17
%19 = OpLabel
%20 = OpIAdd %5 %9 %10
OpBranch %17
%17 = OpLabel
%21 = OpIAdd %5 %22 %11
OpBranchConditional %8 %14 %14
%15 = OpLabel
OpBranch %13
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, AccessChainIssue) {
// Exposes a scenario where redirecting edges results in a use of an id
// generated by an access chain being non-dominated.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %56
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %28 0 Offset 0
OpDecorate %28 Block
OpDecorate %30 DescriptorSet 0
OpDecorate %30 Binding 0
OpDecorate %56 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%60 = OpTypePointer Private %7
%10 = OpConstant %6 0
%11 = OpConstantComposite %7 %10 %10
%12 = OpTypePointer Function %6
%59 = OpTypePointer Private %6
%14 = OpTypeInt 32 1
%15 = OpTypePointer Function %14
%17 = OpConstant %14 0
%24 = OpConstant %14 100
%25 = OpTypeBool
%28 = OpTypeStruct %6
%29 = OpTypePointer Uniform %28
%30 = OpVariable %29 Uniform
%31 = OpTypePointer Uniform %6
%39 = OpTypeInt 32 0
%40 = OpConstant %39 1
%45 = OpConstant %39 0
%52 = OpConstant %14 1
%54 = OpTypeVector %6 4
%55 = OpTypePointer Output %54
%56 = OpVariable %55 Output
%9 = OpVariable %60 Private
%4 = OpFunction %2 None %3
%5 = OpLabel
%13 = OpVariable %12 Function
%16 = OpVariable %15 Function
%38 = OpVariable %12 Function
OpStore %9 %11
OpStore %13 %10
OpStore %16 %17
OpBranch %18
%18 = OpLabel
OpLoopMerge %20 %21 None
OpBranch %22
%22 = OpLabel
%23 = OpLoad %14 %16
%26 = OpSLessThan %25 %23 %24
OpBranchConditional %26 %19 %20
%19 = OpLabel
%27 = OpLoad %14 %16
%32 = OpAccessChain %31 %30 %17
%33 = OpLoad %6 %32
%34 = OpConvertFToS %14 %33
%35 = OpSLessThan %25 %27 %34
OpSelectionMerge %37 None
OpBranchConditional %35 %36 %44
%36 = OpLabel
%41 = OpAccessChain %59 %9 %40
%42 = OpLoad %6 %41
OpStore %38 %42
OpBranch %20
%44 = OpLabel
%46 = OpAccessChain %59 %9 %45
OpBranch %37
%37 = OpLabel
%47 = OpLoad %6 %46
OpStore %38 %47
%48 = OpLoad %6 %38
%49 = OpLoad %6 %13
%50 = OpFAdd %6 %49 %48
OpStore %13 %50
OpBranch %21
%21 = OpLabel
%51 = OpLoad %14 %16
%53 = OpIAdd %14 %51 %52
OpStore %16 %53
OpBranch %18
%20 = OpLabel
%57 = OpLoad %6 %13
%58 = OpCompositeConstruct %54 %57 %57 %57 %57
OpStore %56 %58
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string expected = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %56
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %28 0 Offset 0
OpDecorate %28 Block
OpDecorate %30 DescriptorSet 0
OpDecorate %30 Binding 0
OpDecorate %56 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%60 = OpTypePointer Private %7
%10 = OpConstant %6 0
%11 = OpConstantComposite %7 %10 %10
%12 = OpTypePointer Function %6
%59 = OpTypePointer Private %6
%14 = OpTypeInt 32 1
%15 = OpTypePointer Function %14
%17 = OpConstant %14 0
%24 = OpConstant %14 100
%25 = OpTypeBool
%28 = OpTypeStruct %6
%29 = OpTypePointer Uniform %28
%30 = OpVariable %29 Uniform
%31 = OpTypePointer Uniform %6
%39 = OpTypeInt 32 0
%40 = OpConstant %39 1
%45 = OpConstant %39 0
%52 = OpConstant %14 1
%54 = OpTypeVector %6 4
%55 = OpTypePointer Output %54
%56 = OpVariable %55 Output
%9 = OpVariable %60 Private
%61 = OpConstantTrue %25
%62 = OpVariable %59 Private
%4 = OpFunction %2 None %3
%5 = OpLabel
%13 = OpVariable %12 Function
%16 = OpVariable %15 Function
%38 = OpVariable %12 Function
OpStore %9 %11
OpStore %13 %10
OpStore %16 %17
OpBranch %18
%18 = OpLabel
OpSelectionMerge %20 None
OpBranchConditional %61 %22 %20
%22 = OpLabel
%23 = OpLoad %14 %16
%26 = OpSLessThan %25 %23 %24
OpBranchConditional %26 %19 %20
%19 = OpLabel
%27 = OpLoad %14 %16
%32 = OpAccessChain %31 %30 %17
%33 = OpLoad %6 %32
%34 = OpConvertFToS %14 %33
%35 = OpSLessThan %25 %27 %34
OpSelectionMerge %37 None
OpBranchConditional %35 %36 %44
%36 = OpLabel
%41 = OpAccessChain %59 %9 %40
%42 = OpLoad %6 %41
OpStore %38 %42
OpBranch %37
%44 = OpLabel
%46 = OpAccessChain %59 %9 %45
OpBranch %37
%37 = OpLabel
%47 = OpLoad %6 %62
OpStore %38 %47
%48 = OpLoad %6 %38
%49 = OpLoad %6 %13
%50 = OpFAdd %6 %49 %48
OpStore %13 %50
OpBranch %20
%21 = OpLabel
%51 = OpLoad %14 %16
%53 = OpIAdd %14 %51 %52
OpStore %16 %53
OpBranch %18
%20 = OpLabel
%57 = OpLoad %6 %13
%58 = OpCompositeConstruct %54 %57 %57 %57 %57
OpStore %56 %58
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, DominanceAndPhiIssue) {
// Exposes an interesting scenario where a use in a phi stops being dominated
// by the block with which it is associated in the phi.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%17 = OpTypeBool
%18 = OpConstantTrue %17
%19 = OpConstantFalse %17
%20 = OpTypeInt 32 1
%21 = OpConstant %20 5
%22 = OpConstant %20 6
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpLoopMerge %16 %15 None
OpBranch %7
%7 = OpLabel
OpSelectionMerge %13 None
OpBranchConditional %18 %8 %9
%8 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %18 %10 %11
%9 = OpLabel
OpBranch %16
%10 = OpLabel
OpBranch %16
%11 = OpLabel
%23 = OpIAdd %20 %21 %22
OpBranch %12
%12 = OpLabel
OpBranch %13
%13 = OpLabel
OpBranch %14
%14 = OpLabel
%24 = OpPhi %20 %23 %13
OpBranchConditional %19 %15 %16
%15 = OpLabel
OpBranch %6
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string expected = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%17 = OpTypeBool
%18 = OpConstantTrue %17
%19 = OpConstantFalse %17
%20 = OpTypeInt 32 1
%21 = OpConstant %20 5
%22 = OpConstant %20 6
%25 = OpUndef %20
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpSelectionMerge %16 None
OpBranchConditional %18 %7 %16
%7 = OpLabel
OpSelectionMerge %13 None
OpBranchConditional %18 %8 %9
%8 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %18 %10 %11
%9 = OpLabel
OpBranch %13
%10 = OpLabel
OpBranch %12
%11 = OpLabel
%23 = OpIAdd %20 %21 %22
OpBranch %12
%12 = OpLabel
OpBranch %13
%13 = OpLabel
OpBranch %14
%14 = OpLabel
%24 = OpPhi %20 %25 %13
OpBranchConditional %19 %16 %16
%15 = OpLabel
OpBranch %6
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, OpLineBeforeOpPhi) {
// Test to ensure the pass knows OpLine and OpPhi instructions can be
// interleaved.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpString "somefile"
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpConstant %6 10
%8 = OpConstant %6 20
%9 = OpConstant %6 30
%10 = OpTypeBool
%11 = OpConstantTrue %10
%2 = OpFunction %4 None %5
%12 = OpLabel
OpBranch %13
%13 = OpLabel
OpLoopMerge %14 %15 None
OpBranch %16
%16 = OpLabel
OpSelectionMerge %17 None
OpBranchConditional %11 %18 %19
%18 = OpLabel
%20 = OpIAdd %6 %7 %8
%21 = OpIAdd %6 %7 %9
OpBranch %17
%19 = OpLabel
OpBranch %14
%17 = OpLabel
%22 = OpPhi %6 %20 %18
OpLine %3 0 0
%23 = OpPhi %6 %21 %18
OpBranch %15
%15 = OpLabel
OpBranch %13
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string expected = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpString "somefile"
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpTypeInt 32 1
%7 = OpConstant %6 10
%8 = OpConstant %6 20
%9 = OpConstant %6 30
%10 = OpTypeBool
%11 = OpConstantTrue %10
%24 = OpUndef %6
%2 = OpFunction %4 None %5
%12 = OpLabel
OpBranch %13
%13 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %11 %16 %14
%16 = OpLabel
OpSelectionMerge %17 None
OpBranchConditional %11 %18 %19
%18 = OpLabel
%20 = OpIAdd %6 %7 %8
%21 = OpIAdd %6 %7 %9
OpBranch %17
%19 = OpLabel
OpBranch %17
%17 = OpLabel
%22 = OpPhi %6 %20 %18 %24 %19
OpLine %3 0 0
%23 = OpPhi %6 %21 %18 %24 %19
OpBranch %14
%15 = OpLabel
OpBranch %13
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest,
SelectionMergeIsContinueTarget) {
// Example where a loop's continue target is also the target of a selection.
// In this scenario we cautiously do not apply the transformation.
std::string shader = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%3 = OpTypeBool
%4 = OpTypeFunction %2
%1 = OpFunction %2 None %4
%5 = OpLabel
%6 = OpUndef %3
OpBranch %7
%7 = OpLabel
%8 = OpPhi %3 %6 %5 %9 %10
OpLoopMerge %11 %10 None
OpBranch %12
%12 = OpLabel
%13 = OpUndef %3
OpSelectionMerge %10 None
OpBranchConditional %13 %14 %10
%14 = OpLabel
OpBranch %10
%10 = OpLabel
%9 = OpUndef %3
OpBranchConditional %9 %7 %11
%11 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// There should be no opportunities.
ASSERT_EQ(0, ops.size());
}
TEST(StructuredLoopToSelectionReductionPassTest,
SwitchSelectionMergeIsContinueTarget) {
// Another example where a loop's continue target is also the target of a
// selection; this time a selection associated with an OpSwitch. We
// cautiously do not apply the transformation.
std::string shader = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%3 = OpTypeBool
%5 = OpTypeInt 32 1
%4 = OpTypeFunction %2
%6 = OpConstant %5 2
%7 = OpConstantTrue %3
%1 = OpFunction %2 None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %14 %15 None
OpBranchConditional %7 %10 %14
%10 = OpLabel
OpSelectionMerge %15 None
OpSwitch %6 %12 1 %11 2 %11 3 %15
%11 = OpLabel
OpBranch %12
%12 = OpLabel
OpBranch %15
%15 = OpLabel
OpBranch %9
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// There should be no opportunities.
ASSERT_EQ(0, ops.size());
}
TEST(StructuredLoopToSelectionReductionPassTest, ContinueTargetIsSwitchTarget) {
std::string shader = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%3 = OpTypeBool
%5 = OpTypeInt 32 1
%4 = OpTypeFunction %2
%6 = OpConstant %5 2
%7 = OpConstantTrue %3
%1 = OpFunction %2 None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %14 %12 None
OpBranchConditional %7 %10 %14
%10 = OpLabel
OpSelectionMerge %15 None
OpSwitch %6 %12 1 %11 2 %11 3 %15
%11 = OpLabel
OpBranch %12
%12 = OpLabel
OpBranch %9
%15 = OpLabel
OpBranch %14
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string expected = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%3 = OpTypeBool
%5 = OpTypeInt 32 1
%4 = OpTypeFunction %2
%6 = OpConstant %5 2
%7 = OpConstantTrue %3
%1 = OpFunction %2 None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %7 %10 %14
%10 = OpLabel
OpSelectionMerge %15 None
OpSwitch %6 %15 1 %11 2 %11 3 %15
%11 = OpLabel
OpBranch %15
%12 = OpLabel
OpBranch %9
%15 = OpLabel
OpBranch %14
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest,
MultipleSwitchTargetsAreContinueTarget) {
std::string shader = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%3 = OpTypeBool
%5 = OpTypeInt 32 1
%4 = OpTypeFunction %2
%6 = OpConstant %5 2
%7 = OpConstantTrue %3
%1 = OpFunction %2 None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %14 %12 None
OpBranchConditional %7 %10 %14
%10 = OpLabel
OpSelectionMerge %15 None
OpSwitch %6 %11 1 %12 2 %12 3 %15
%11 = OpLabel
OpBranch %12
%12 = OpLabel
OpBranch %9
%15 = OpLabel
OpBranch %14
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string expected = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%3 = OpTypeBool
%5 = OpTypeInt 32 1
%4 = OpTypeFunction %2
%6 = OpConstant %5 2
%7 = OpConstantTrue %3
%1 = OpFunction %2 None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %7 %10 %14
%10 = OpLabel
OpSelectionMerge %15 None
OpSwitch %6 %11 1 %15 2 %15 3 %15
%11 = OpLabel
OpBranch %15
%12 = OpLabel
OpBranch %9
%15 = OpLabel
OpBranch %14
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, LoopBranchesStraightToMerge) {
std::string shader = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%4 = OpTypeFunction %2
%1 = OpFunction %2 None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %14 %12 None
OpBranch %14
%12 = OpLabel
OpBranch %9
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string expected = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%4 = OpTypeFunction %2
%15 = OpTypeBool
%16 = OpConstantTrue %15
%1 = OpFunction %2 None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %16 %14 %14
%12 = OpLabel
OpBranch %9
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest,
LoopConditionallyJumpsToMergeOrContinue) {
std::string shader = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%3 = OpTypeBool
%4 = OpTypeFunction %2
%7 = OpConstantTrue %3
%1 = OpFunction %2 None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %14 %12 None
OpBranchConditional %7 %14 %12
%12 = OpLabel
OpBranch %9
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string expected = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "main"
%2 = OpTypeVoid
%3 = OpTypeBool
%4 = OpTypeFunction %2
%7 = OpConstantTrue %3
%1 = OpFunction %2 None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %7 %14 %14
%12 = OpLabel
OpBranch %9
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, MultipleAccessChains) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeStruct %6
%8 = OpTypeStruct %7
%9 = OpTypePointer Function %8
%11 = OpConstant %6 3
%12 = OpConstantComposite %7 %11
%13 = OpConstantComposite %8 %12
%14 = OpTypePointer Function %7
%16 = OpConstant %6 0
%19 = OpTypePointer Function %6
%15 = OpTypeBool
%18 = OpConstantTrue %15
%4 = OpFunction %2 None %3
%5 = OpLabel
%10 = OpVariable %9 Function
%20 = OpVariable %19 Function
OpStore %10 %13
OpBranch %23
%23 = OpLabel
OpLoopMerge %25 %26 None
OpBranch %27
%27 = OpLabel
OpSelectionMerge %28 None
OpBranchConditional %18 %29 %25
%29 = OpLabel
%17 = OpAccessChain %14 %10 %16
OpBranch %28
%28 = OpLabel
%21 = OpAccessChain %19 %17 %16
%22 = OpLoad %6 %21
%24 = OpAccessChain %19 %10 %16 %16
OpStore %24 %22
OpBranch %25
%26 = OpLabel
OpBranch %23
%25 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string expected = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeStruct %6
%8 = OpTypeStruct %7
%9 = OpTypePointer Function %8
%11 = OpConstant %6 3
%12 = OpConstantComposite %7 %11
%13 = OpConstantComposite %8 %12
%14 = OpTypePointer Function %7
%16 = OpConstant %6 0
%19 = OpTypePointer Function %6
%15 = OpTypeBool
%18 = OpConstantTrue %15
%4 = OpFunction %2 None %3
%5 = OpLabel
%10 = OpVariable %9 Function
%20 = OpVariable %19 Function
%30 = OpVariable %14 Function
OpStore %10 %13
OpBranch %23
%23 = OpLabel
OpSelectionMerge %25 None
OpBranchConditional %18 %27 %25
%27 = OpLabel
OpSelectionMerge %28 None
OpBranchConditional %18 %29 %28
%29 = OpLabel
%17 = OpAccessChain %14 %10 %16
OpBranch %28
%28 = OpLabel
%21 = OpAccessChain %19 %30 %16
%22 = OpLoad %6 %21
%24 = OpAccessChain %19 %10 %16 %16
OpStore %24 %22
OpBranch %25
%26 = OpLabel
OpBranch %23
%25 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest,
UnreachableInnerLoopContinueBranchingToOuterLoopMerge) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%2 = OpFunction %3 None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpLoopMerge %9 %10 None
OpBranch %11
%11 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
OpBranchConditional %6 %9 %11
%12 = OpLabel
OpBranch %10
%10 = OpLabel
OpBranchConditional %6 %9 %8
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%2 = OpFunction %3 None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %6 %11 %9
%11 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
OpBranchConditional %6 %9 %11
%12 = OpLabel
OpBranch %9
%10 = OpLabel
OpBranchConditional %6 %9 %8
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
ASSERT_TRUE(ops[1]->PreconditionHolds());
ops[1]->TryToApply();
CheckValid(env, context.get());
std::string after_op_1 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%2 = OpFunction %3 None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %6 %11 %9
%11 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %6 %12 %12
%13 = OpLabel
OpBranchConditional %6 %9 %11
%12 = OpLabel
OpBranch %9
%10 = OpLabel
OpBranchConditional %6 %9 %8
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_1, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest,
UnreachableInnerLoopContinueBranchingToOuterLoopMerge2) {
// In this test, the branch to the outer loop merge from the inner loop's
// continue is part of a structured selection.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%2 = OpFunction %3 None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpLoopMerge %9 %10 None
OpBranch %11
%11 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %6 %9 %14
%14 = OpLabel
OpBranch %11
%12 = OpLabel
OpBranch %10
%10 = OpLabel
OpBranchConditional %6 %9 %8
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%2 = OpFunction %3 None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %6 %11 %9
%11 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %6 %9 %14
%14 = OpLabel
OpBranch %11
%12 = OpLabel
OpBranch %9
%10 = OpLabel
OpBranchConditional %6 %9 %8
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
ASSERT_TRUE(ops[1]->PreconditionHolds());
ops[1]->TryToApply();
CheckValid(env, context.get());
std::string after_op_1 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%2 = OpFunction %3 None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %6 %11 %9
%11 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %6 %12 %12
%13 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %6 %9 %14
%14 = OpLabel
OpBranch %11
%12 = OpLabel
OpBranch %9
%10 = OpLabel
OpBranchConditional %6 %9 %8
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_1, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest,
InnerLoopHeaderBranchesToOuterLoopMerge) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%2 = OpFunction %3 None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpLoopMerge %9 %10 None
OpBranch %11
%11 = OpLabel
OpLoopMerge %12 %13 None
OpBranchConditional %6 %9 %13
%13 = OpLabel
OpBranchConditional %6 %11 %12
%12 = OpLabel
OpBranch %10
%10 = OpLabel
OpBranchConditional %6 %9 %8
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// We cannot transform the inner loop due to its header jumping straight to
// the outer loop merge (the inner loop's merge does not post-dominate its
// header).
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%2 = OpFunction %3 None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %6 %11 %9
%11 = OpLabel
OpLoopMerge %12 %13 None
OpBranchConditional %6 %12 %13
%13 = OpLabel
OpBranchConditional %6 %11 %12
%12 = OpLabel
OpBranch %9
%10 = OpLabel
OpBranchConditional %6 %9 %8
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
// Now look again for more opportunities.
ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// What was the inner loop should now be transformable, as the jump to the
// outer loop's merge has been redirected.
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_another_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%2 = OpFunction %3 None %4
%7 = OpLabel
OpBranch %8
%8 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %6 %11 %9
%11 = OpLabel
OpSelectionMerge %12 None
OpBranchConditional %6 %12 %12
%13 = OpLabel
OpBranchConditional %6 %11 %12
%12 = OpLabel
OpBranch %9
%10 = OpLabel
OpBranchConditional %6 %9 %8
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_another_op_0, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, LongAccessChains) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeInt 32 1
%6 = OpTypeInt 32 0
%7 = OpConstant %6 5
%8 = OpTypeArray %5 %7
%9 = OpTypeStruct %8
%10 = OpTypeStruct %9 %9
%11 = OpConstant %6 2
%12 = OpTypeArray %10 %11
%13 = OpTypeStruct %12
%14 = OpTypePointer Function %13
%15 = OpConstant %5 0
%16 = OpConstant %5 1
%17 = OpConstant %5 2
%18 = OpConstant %5 3
%19 = OpConstant %5 4
%20 = OpConstantComposite %8 %15 %16 %17 %18 %19
%21 = OpConstantComposite %9 %20
%22 = OpConstant %5 5
%23 = OpConstant %5 6
%24 = OpConstant %5 7
%25 = OpConstant %5 8
%26 = OpConstant %5 9
%27 = OpConstantComposite %8 %22 %23 %24 %25 %26
%28 = OpConstantComposite %9 %27
%29 = OpConstantComposite %10 %21 %28
%30 = OpConstant %5 10
%31 = OpConstant %5 11
%32 = OpConstant %5 12
%33 = OpConstant %5 13
%34 = OpConstant %5 14
%35 = OpConstantComposite %8 %30 %31 %32 %33 %34
%36 = OpConstantComposite %9 %35
%37 = OpConstant %5 15
%38 = OpConstant %5 16
%39 = OpConstant %5 17
%40 = OpConstant %5 18
%41 = OpConstant %5 19
%42 = OpConstantComposite %8 %37 %38 %39 %40 %41
%43 = OpConstantComposite %9 %42
%44 = OpConstantComposite %10 %36 %43
%45 = OpConstantComposite %12 %29 %44
%46 = OpConstantComposite %13 %45
%47 = OpTypePointer Function %12
%48 = OpTypePointer Function %10
%49 = OpTypePointer Function %9
%50 = OpTypePointer Function %8
%51 = OpTypePointer Function %5
%52 = OpTypeBool
%53 = OpConstantTrue %52
%2 = OpFunction %3 None %4
%54 = OpLabel
%55 = OpVariable %14 Function
OpStore %55 %46
OpBranch %56
%56 = OpLabel
OpLoopMerge %57 %58 None
OpBranchConditional %53 %57 %59
%59 = OpLabel
OpSelectionMerge %60 None
OpBranchConditional %53 %61 %57
%61 = OpLabel
%62 = OpAccessChain %47 %55 %15
OpBranch %63
%63 = OpLabel
OpSelectionMerge %64 None
OpBranchConditional %53 %65 %57
%65 = OpLabel
%66 = OpAccessChain %48 %62 %16
OpBranch %67
%67 = OpLabel
OpSelectionMerge %68 None
OpBranchConditional %53 %69 %57
%69 = OpLabel
%70 = OpAccessChain %49 %66 %16
OpBranch %71
%71 = OpLabel
OpSelectionMerge %72 None
OpBranchConditional %53 %73 %57
%73 = OpLabel
%74 = OpAccessChain %50 %70 %15
OpBranch %75
%75 = OpLabel
OpSelectionMerge %76 None
OpBranchConditional %53 %77 %57
%77 = OpLabel
%78 = OpAccessChain %51 %74 %17
OpBranch %79
%79 = OpLabel
OpSelectionMerge %80 None
OpBranchConditional %53 %81 %57
%81 = OpLabel
%82 = OpLoad %5 %78
OpBranch %80
%80 = OpLabel
OpBranch %76
%76 = OpLabel
OpBranch %72
%72 = OpLabel
OpBranch %68
%68 = OpLabel
OpBranch %64
%64 = OpLabel
OpBranch %60
%60 = OpLabel
OpBranch %58
%58 = OpLabel
OpBranch %56
%57 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
// TODO(2183): When we have a more general solution for handling access
// chains, write an expected result for this test.
// std::string expected = R"(
// Expected text for transformed shader
//)";
// CheckEqual(env, expected, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest, LoopyShaderWithOpDecorate) {
// A shader containing a function that contains a loop and some definitions
// that are "used" in OpDecorate instructions (outside the function). These
// "uses" were causing segfaults because we try to calculate their dominance
// information, which doesn't make sense.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %9
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %9 "_GLF_color"
OpName %14 "buf0"
OpMemberName %14 0 "a"
OpName %16 ""
OpDecorate %9 RelaxedPrecision
OpDecorate %9 Location 0
OpMemberDecorate %14 0 RelaxedPrecision
OpMemberDecorate %14 0 Offset 0
OpDecorate %14 Block
OpDecorate %16 DescriptorSet 0
OpDecorate %16 Binding 0
OpDecorate %21 RelaxedPrecision
OpDecorate %35 RelaxedPrecision
OpDecorate %36 RelaxedPrecision
OpDecorate %39 RelaxedPrecision
OpDecorate %40 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypePointer Output %7
%9 = OpVariable %8 Output
%10 = OpConstant %6 1
%11 = OpConstantComposite %7 %10 %10 %10 %10
%14 = OpTypeStruct %6
%15 = OpTypePointer Uniform %14
%16 = OpVariable %15 Uniform
%17 = OpTypeInt 32 1
%18 = OpConstant %17 0
%19 = OpTypePointer Uniform %6
%28 = OpConstant %6 2
%29 = OpTypeBool
%31 = OpTypeInt 32 0
%32 = OpConstant %31 0
%33 = OpTypePointer Output %6
%4 = OpFunction %2 None %3
%5 = OpLabel
OpStore %9 %11
%20 = OpAccessChain %19 %16 %18
%21 = OpLoad %6 %20
OpBranch %22
%22 = OpLabel
%40 = OpPhi %6 %21 %5 %39 %23
%30 = OpFOrdLessThan %29 %40 %28
OpLoopMerge %24 %23 None
OpBranchConditional %30 %23 %24
%23 = OpLabel
%34 = OpAccessChain %33 %9 %32
%35 = OpLoad %6 %34
%36 = OpFAdd %6 %35 %10
OpStore %34 %36
%39 = OpFAdd %6 %40 %10
OpBranch %22
%24 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckValid(env, context.get());
std::string after_op_0 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %9
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %9 "_GLF_color"
OpName %14 "buf0"
OpMemberName %14 0 "a"
OpName %16 ""
OpDecorate %9 RelaxedPrecision
OpDecorate %9 Location 0
OpMemberDecorate %14 0 RelaxedPrecision
OpMemberDecorate %14 0 Offset 0
OpDecorate %14 Block
OpDecorate %16 DescriptorSet 0
OpDecorate %16 Binding 0
OpDecorate %21 RelaxedPrecision
OpDecorate %35 RelaxedPrecision
OpDecorate %36 RelaxedPrecision
OpDecorate %39 RelaxedPrecision
OpDecorate %40 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypePointer Output %7
%9 = OpVariable %8 Output
%10 = OpConstant %6 1
%11 = OpConstantComposite %7 %10 %10 %10 %10
%14 = OpTypeStruct %6
%15 = OpTypePointer Uniform %14
%16 = OpVariable %15 Uniform
%17 = OpTypeInt 32 1
%18 = OpConstant %17 0
%19 = OpTypePointer Uniform %6
%28 = OpConstant %6 2
%29 = OpTypeBool
%31 = OpTypeInt 32 0
%32 = OpConstant %31 0
%33 = OpTypePointer Output %6
%41 = OpUndef %6 ; Added
%4 = OpFunction %2 None %3
%5 = OpLabel
OpStore %9 %11
%20 = OpAccessChain %19 %16 %18
%21 = OpLoad %6 %20
OpBranch %22
%22 = OpLabel
%40 = OpPhi %6 %21 %5 %41 %23 ; Changed
%30 = OpFOrdLessThan %29 %40 %28
OpSelectionMerge %24 None ; Changed
OpBranchConditional %30 %24 %24
%23 = OpLabel
%34 = OpAccessChain %33 %9 %32
%35 = OpLoad %6 %34
%36 = OpFAdd %6 %35 %10
OpStore %34 %36
%39 = OpFAdd %6 %41 %10 ; Changed
OpBranch %22
%24 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(env, after_op_0, context.get());
}
TEST(StructuredLoopToSelectionReductionPassTest,
LoopWithCombinedHeaderAndContinue) {
// A shader containing a loop where the header is also the continue target.
// For now, we don't simplify such loops.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%30 = OpConstantFalse %6
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %10
%10 = OpLabel ; loop header and continue target
OpLoopMerge %12 %10 None
OpBranchConditional %30 %10 %12
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(0, ops.size());
}
} // namespace
} // namespace reduce
} // namespace spvtools