// 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 #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(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(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(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(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(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(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(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(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(text, true, false); EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); } } // namespace } // namespace opt } // namespace spvtools