SPIRV-Tools/test/opt/loop_optimizations/unswitch.cpp
Steven Perron 5ec2d1a8cd
Don't fold specialized branches in loop unswitch (#2245)
* Don't fold specialized branchs in loop unswitch

Folding branches can have a lot of special cases, and can be a little
error prone.  So I only want it in one place.  That will be in dead
branch elimination.  I will change loop unswitching to set the branches
that were being folded to have a constant condition.  Then subsequent
pass of dead branch elimination will be able to remove the code.

At the same time, I added a check that loop unswitching will not
unswitch a branch with a constant condition.  It is not useful to do it
because dead branch elimination will simple fold the branch anyway.
Also it avoid an infinite loop that would other wise be introduced by my
first change.

Fixes #2203.
2018-12-19 04:40:30 +00:00

968 lines
31 KiB
C++

// 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 <string>
#include "effcee/effcee.h"
#include "gmock/gmock.h"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using UnswitchTest = PassTest<::testing::Test>;
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 450 core
uniform vec4 c;
void main() {
int i = 0;
int j = 0;
bool cond = c[0] == 0;
for (; i < 10; i++, j++) {
if (cond) {
i++;
}
else {
j++;
}
}
}
*/
TEST_F(UnswitchTest, SimpleUnswitch) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false.
; CHECK: [[loop_f]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_j:%\w+]] [[continue]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; [[loop_body]] = OpLabel
; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None
; CHECK: OpBranchConditional %false [[bb1:%\w+]] [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[bb1]] = OpLabel
; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[sel_merge]] = OpLabel
; CHECK: OpBranch [[if_merge]]
; Loop specialized for true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_j:%\w+]] [[continue]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; [[loop_body]] = OpLabel
; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None
; CHECK: OpBranchConditional %true [[bb1:%\w+]] [[bb2:%\w+]]
; CHECK: [[bb1]] = OpLabel
; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[sel_merge]] = OpLabel
; CHECK: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginLowerLeft
OpSource GLSL 450
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %c DescriptorSet 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_UniformConstant_v4float = OpTypePointer UniformConstant %v4float
%c = OpVariable %_ptr_UniformConstant_v4float UniformConstant
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%21 = OpAccessChain %_ptr_UniformConstant_float %c %uint_0
%22 = OpLoad %float %21
%24 = OpFOrdEqual %bool %22 %float_0
OpBranch %25
%25 = OpLabel
%46 = OpPhi %int %int_0 %5 %43 %28
%47 = OpPhi %int %int_0 %5 %45 %28
OpLoopMerge %27 %28 None
OpBranch %29
%29 = OpLabel
%32 = OpSLessThan %bool %46 %int_10
OpBranchConditional %32 %26 %27
%26 = OpLabel
OpSelectionMerge %35 None
OpBranchConditional %24 %34 %39
%34 = OpLabel
%38 = OpIAdd %int %46 %int_1
OpBranch %35
%39 = OpLabel
%41 = OpIAdd %int %47 %int_1
OpBranch %35
%35 = OpLabel
%48 = OpPhi %int %38 %34 %46 %39
%49 = OpPhi %int %47 %34 %41 %39
OpBranch %28
%28 = OpLabel
%43 = OpIAdd %int %48 %int_1
%45 = OpIAdd %int %49 %int_1
OpBranch %25
%27 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
int i = 0;
bool cond = c[0] == 0;
for (; i < 10; i++) {
if (cond) {
i++;
}
else {
return;
}
}
}
*/
TEST_F(UnswitchTest, UnswitchExit) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false.
; CHECK: [[loop_f]] = OpLabel
; CHECK: OpReturn
; Loop specialized for true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]]
; Check that we have i+=2.
; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %23 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_float %c %uint_0
%21 = OpLoad %float %20
%23 = OpFOrdEqual %bool %21 %float_0
OpBranch %24
%24 = OpLabel
%42 = OpPhi %int %int_0 %5 %41 %27
OpLoopMerge %26 %27 None
OpBranch %28
%28 = OpLabel
%31 = OpSLessThan %bool %42 %int_10
OpBranchConditional %31 %25 %26
%25 = OpLabel
OpSelectionMerge %34 None
OpBranchConditional %23 %33 %38
%33 = OpLabel
%37 = OpIAdd %int %42 %int_1
OpBranch %34
%38 = OpLabel
OpReturn
%34 = OpLabel
OpBranch %27
%27 = OpLabel
%41 = OpIAdd %int %37 %int_1
OpBranch %24
%26 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
int i = 0;
bool cond = c[0] == 0;
for (; i < 10; i++) {
if (cond) {
continue;
}
else {
i++;
}
}
}
*/
TEST_F(UnswitchTest, UnswitchContinue) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false.
; CHECK: [[loop_f]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body:%\w+]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpBranchConditional %false
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; Loop specialized for true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body:%\w+]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpBranchConditional %true
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %23 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_float %c %uint_0
%21 = OpLoad %float %20
%23 = OpFOrdEqual %bool %21 %float_0
OpBranch %24
%24 = OpLabel
%42 = OpPhi %int %int_0 %5 %41 %27
OpLoopMerge %26 %27 None
OpBranch %28
%28 = OpLabel
%31 = OpSLessThan %bool %42 %int_10
OpBranchConditional %31 %25 %26
%25 = OpLabel
OpSelectionMerge %34 None
OpBranchConditional %23 %33 %36
%33 = OpLabel
OpBranch %27
%36 = OpLabel
%39 = OpIAdd %int %42 %int_1
OpBranch %34
%34 = OpLabel
OpBranch %27
%27 = OpLabel
%43 = OpPhi %int %42 %33 %39 %34
%41 = OpIAdd %int %43 %int_1
OpBranch %24
%26 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
int i = 0;
bool cond = c[0] == 0;
for (; i < 10; i++) {
if (cond) {
i++;
}
else {
break;
}
}
}
*/
TEST_F(UnswitchTest, UnswitchKillLoop) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false.
; CHECK: [[loop_f]] = OpLabel
; CHECK: OpBranch [[if_merge]]
; Loop specialized for true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]]
; Check that we have i+=2.
; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %23 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_float %c %uint_0
%21 = OpLoad %float %20
%23 = OpFOrdEqual %bool %21 %float_0
OpBranch %24
%24 = OpLabel
%42 = OpPhi %int %int_0 %5 %41 %27
OpLoopMerge %26 %27 None
OpBranch %28
%28 = OpLabel
%31 = OpSLessThan %bool %42 %int_10
OpBranchConditional %31 %25 %26
%25 = OpLabel
OpSelectionMerge %34 None
OpBranchConditional %23 %33 %38
%33 = OpLabel
%37 = OpIAdd %int %42 %int_1
OpBranch %34
%38 = OpLabel
OpBranch %26
%34 = OpLabel
OpBranch %27
%27 = OpLabel
%41 = OpIAdd %int %37 %int_1
OpBranch %24
%26 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
int i = 0;
int cond = int(c[0]);
for (; i < 10; i++) {
switch (cond) {
case 0:
return;
case 1:
discard;
case 2:
break;
default:
break;
}
}
bool cond2 = i == 9;
}
*/
TEST_F(UnswitchTest, UnswitchSwitch) {
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpConvertFToS
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpSwitch [[cst_cond]] [[default:%\w+]] 0 [[loop_0:%\w+]] 1 [[loop_1:%\w+]] 2 [[loop_2:%\w+]]
; Loop specialized for 2.
; CHECK: [[loop_2]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_2]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpSwitch %int_2
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; Loop specialized for 1.
; CHECK: [[loop_1]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_1]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpSwitch %int_1
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; Loop specialized for 0.
; CHECK: [[loop_0]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_0]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpSwitch %int_0
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; Loop specialized for the default case.
; CHECK: [[default]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[default]] [[iv_i:%\w+]] [[continue:%\w+]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
; CHECK: [[loop_body]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpSwitch %uint_3
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpBranch [[if_merge]]
; CHECK: [[if_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %20 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%_ptr_Function_bool = OpTypePointer Function %bool
%main = OpFunction %void None %3
%5 = OpLabel
%18 = OpAccessChain %_ptr_Input_float %c %uint_0
%19 = OpLoad %float %18
%20 = OpConvertFToS %int %19
OpBranch %21
%21 = OpLabel
%49 = OpPhi %int %int_0 %5 %43 %24
OpLoopMerge %23 %24 None
OpBranch %25
%25 = OpLabel
%29 = OpSLessThan %bool %49 %int_10
OpBranchConditional %29 %22 %23
%22 = OpLabel
OpSelectionMerge %35 None
OpSwitch %20 %34 0 %31 1 %32 2 %33
%34 = OpLabel
OpBranch %35
%31 = OpLabel
OpReturn
%32 = OpLabel
OpKill
%33 = OpLabel
OpBranch %35
%35 = OpLabel
OpBranch %24
%24 = OpLabel
%43 = OpIAdd %int %49 %int_1
OpBranch %21
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 440 core
layout(location = 0)in vec4 c;
void main() {
int i = 0;
int j = 0;
int k = 0;
bool cond = c[0] == 0;
for (; i < 10; i++) {
for (; j < 10; j++) {
if (cond) {
i++;
} else {
j++;
}
}
}
}
*/
TEST_F(UnswitchTest, UnSwitchNested) {
// Test that an branch can be unswitched out of two nested loops.
const std::string text = R"(
; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
; Loop specialized for false
; CHECK: [[loop_f]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue:%\w+]]
; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK-NOT: [[merge]] = OpLabel
; CHECK: OpLoopMerge
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
; CHECK: [[bb1]] = OpLabel
; CHECK-NEXT: OpSLessThan
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpBranchConditional %false
; CHECK: [[merge]] = OpLabel
; Loop specialized for true. Same as first loop except the branch condition is true.
; CHECK: [[loop_t]] = OpLabel
; CHECK-NEXT: OpBranch [[loop:%\w+]]
; CHECK: [[loop]] = OpLabel
; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue:%\w+]]
; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue]]
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
; CHECK-NOT: [[merge]] = OpLabel
; CHECK: OpLoopMerge
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
; CHECK: [[bb1]] = OpLabel
; CHECK-NEXT: OpSLessThan
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK-NEXT: OpBranchConditional %true
; CHECK: [[merge]] = OpLabel
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 440
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
OpDecorate %25 Uniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%22 = OpAccessChain %_ptr_Input_float %c %uint_0
%23 = OpLoad %float %22
%25 = OpFOrdEqual %bool %23 %float_0
OpBranch %26
%26 = OpLabel
%67 = OpPhi %int %int_0 %5 %52 %29
%68 = OpPhi %int %int_0 %5 %70 %29
OpLoopMerge %28 %29 None
OpBranch %30
%30 = OpLabel
%33 = OpSLessThan %bool %67 %int_10
OpBranchConditional %33 %27 %28
%27 = OpLabel
OpBranch %34
%34 = OpLabel
%69 = OpPhi %int %67 %27 %46 %37
%70 = OpPhi %int %68 %27 %50 %37
OpLoopMerge %36 %37 None
OpBranch %38
%38 = OpLabel
%40 = OpSLessThan %bool %70 %int_10
OpBranchConditional %40 %35 %36
%35 = OpLabel
OpSelectionMerge %43 None
OpBranchConditional %25 %42 %47
%42 = OpLabel
%46 = OpIAdd %int %69 %int_1
OpBranch %43
%47 = OpLabel
OpReturn
%43 = OpLabel
OpBranch %37
%37 = OpLabel
%50 = OpIAdd %int %70 %int_1
OpBranch %34
%36 = OpLabel
OpBranch %29
%29 = OpLabel
%52 = OpIAdd %int %69 %int_1
OpBranch %26
%28 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
}
/*
Generated from the following GLSL + --eliminate-local-multi-store
#version 330 core
in vec4 c;
void main() {
bool cond = false;
if (c[0] == 0) {
cond = c[1] == 0;
} else {
cond = c[2] == 0;
}
for (int i = 0; i < 10; i++) {
if (cond) {
i++;
}
}
}
*/
TEST_F(UnswitchTest, UnswitchNotUniform) {
// Check that the unswitch is not triggered (condition loop invariant but not
// uniform)
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %c
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 330
OpName %main "main"
OpName %c "c"
OpDecorate %c Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Input_v4float = OpTypePointer Input %v4float
%c = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Input_float = OpTypePointer Input %float
%float_0 = OpConstant %float 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%17 = OpAccessChain %_ptr_Input_float %c %uint_0
%18 = OpLoad %float %17
%20 = OpFOrdEqual %bool %18 %float_0
OpSelectionMerge %22 None
OpBranchConditional %20 %21 %27
%21 = OpLabel
%24 = OpAccessChain %_ptr_Input_float %c %uint_1
%25 = OpLoad %float %24
%26 = OpFOrdEqual %bool %25 %float_0
OpBranch %22
%27 = OpLabel
%29 = OpAccessChain %_ptr_Input_float %c %uint_2
%30 = OpLoad %float %29
%31 = OpFOrdEqual %bool %30 %float_0
OpBranch %22
%22 = OpLabel
%52 = OpPhi %bool %26 %21 %31 %27
OpBranch %36
%36 = OpLabel
%53 = OpPhi %int %int_0 %22 %51 %39
OpLoopMerge %38 %39 None
OpBranch %40
%40 = OpLabel
%43 = OpSLessThan %bool %53 %int_10
OpBranchConditional %43 %37 %38
%37 = OpLabel
OpSelectionMerge %46 None
OpBranchConditional %52 %45 %46
%45 = OpLabel
%49 = OpIAdd %int %53 %int_1
OpBranch %46
%46 = OpLabel
%54 = OpPhi %int %53 %37 %49 %45
OpBranch %39
%39 = OpLabel
%51 = OpIAdd %int %54 %int_1
OpBranch %36
%38 = OpLabel
OpReturn
OpFunctionEnd
)";
auto result =
SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false);
EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
TEST_F(UnswitchTest, DontUnswitchLatch) {
// Check that the unswitch is not triggered for the latch branch.
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%void = OpTypeVoid
%3 = OpTypeFunction %void
%bool = OpTypeBool
%false = OpConstantFalse %bool
%4 = OpFunction %void None %3
%5 = OpLabel
OpBranch %6
%6 = OpLabel
OpLoopMerge %8 %9 None
OpBranch %7
%7 = OpLabel
OpBranch %9
%9 = OpLabel
OpBranchConditional %false %6 %8
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
auto result =
SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false);
EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
TEST_F(UnswitchTest, DontUnswitchConstantCondition) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginLowerLeft
OpSource GLSL 450
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%bool = OpTypeBool
%true = OpConstantTrue %bool
%int_1 = OpConstant %int 1
%main = OpFunction %void None %4
%10 = OpLabel
OpBranch %11
%11 = OpLabel
%12 = OpPhi %int %int_0 %10 %13 %14
OpLoopMerge %15 %14 None
OpBranch %16
%16 = OpLabel
%17 = OpSLessThan %bool %12 %int_1
OpBranchConditional %17 %18 %15
%18 = OpLabel
OpSelectionMerge %19 None
OpBranchConditional %true %20 %19
%20 = OpLabel
%21 = OpIAdd %int %12 %int_1
OpBranch %19
%19 = OpLabel
%22 = OpPhi %int %21 %20 %12 %18
OpBranch %14
%14 = OpLabel
%13 = OpIAdd %int %22 %int_1
OpBranch %11
%15 = OpLabel
OpReturn
OpFunctionEnd
)";
auto result =
SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false);
EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
} // namespace
} // namespace opt
} // namespace spvtools