// Copyright (c) 2017 Google Inc. // // 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 "gmock/gmock.h" #include "spirv-tools/libspirv.hpp" #include "spirv-tools/optimizer.hpp" #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" namespace spvtools { namespace opt { namespace { using MergeReturnPassTest = PassTest<::testing::Test>; TEST_F(MergeReturnPassTest, OneReturn) { const std::string before = R"(OpCapability Addresses OpCapability Kernel OpCapability GenericPointer OpCapability Linkage OpMemoryModel Physical32 OpenCL OpEntryPoint Kernel %1 "simple_kernel" %2 = OpTypeVoid %3 = OpTypeFunction %2 %1 = OpFunction %2 None %3 %4 = OpLabel OpReturn OpFunctionEnd )"; const std::string after = before; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck(before, after, false, true); } TEST_F(MergeReturnPassTest, TwoReturnsNoValue) { const std::string before = R"(OpCapability Addresses OpCapability Kernel OpCapability GenericPointer OpCapability Linkage OpMemoryModel Physical32 OpenCL OpEntryPoint Kernel %6 "simple_kernel" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpBranchConditional %4 %8 %9 %8 = OpLabel OpReturn %9 = OpLabel OpReturn OpFunctionEnd )"; const std::string after = R"(OpCapability Addresses OpCapability Kernel OpCapability GenericPointer OpCapability Linkage OpMemoryModel Physical32 OpenCL OpEntryPoint Kernel %6 "simple_kernel" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpBranchConditional %4 %8 %9 %8 = OpLabel OpBranch %10 %9 = OpLabel OpBranch %10 %10 = OpLabel OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck(before, after, false, true); } TEST_F(MergeReturnPassTest, DebugTwoReturnsNoValue) { const std::string before = R"(OpCapability Addresses OpCapability Kernel OpCapability GenericPointer OpCapability Linkage %10 = OpExtInstImport "OpenCL.DebugInfo.100" OpMemoryModel Physical32 OpenCL OpEntryPoint Kernel %6 "simple_kernel" %11 = OpString "test" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %12 = OpExtInst %2 %10 DebugSource %11 %13 = OpExtInst %2 %10 DebugCompilationUnit 1 4 %12 HLSL %14 = OpExtInst %2 %10 DebugTypeFunction FlagIsProtected|FlagIsPrivate %2 %15 = OpExtInst %2 %10 DebugFunction %11 %14 %12 0 0 %13 %11 FlagIsProtected|FlagIsPrivate 0 %6 %6 = OpFunction %2 None %1 %7 = OpLabel OpBranchConditional %4 %8 %9 %8 = OpLabel %16 = OpExtInst %2 %10 DebugScope %15 OpLine %11 100 0 OpReturn %9 = OpLabel %17 = OpExtInst %2 %10 DebugScope %13 OpLine %11 200 0 OpReturn OpFunctionEnd )"; const std::string after = R"(OpCapability Addresses OpCapability Kernel OpCapability GenericPointer OpCapability Linkage %10 = OpExtInstImport "OpenCL.DebugInfo.100" OpMemoryModel Physical32 OpenCL OpEntryPoint Kernel %6 "simple_kernel" %11 = OpString "test" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %12 = OpExtInst %2 %10 DebugSource %11 %13 = OpExtInst %2 %10 DebugCompilationUnit 1 4 %12 HLSL %14 = OpExtInst %2 %10 DebugTypeFunction FlagIsProtected|FlagIsPrivate %2 %15 = OpExtInst %2 %10 DebugFunction %11 %14 %12 0 0 %13 %11 FlagIsProtected|FlagIsPrivate 0 %6 %6 = OpFunction %2 None %1 %7 = OpLabel OpBranchConditional %4 %8 %9 %8 = OpLabel %19 = OpExtInst %2 %10 DebugScope %15 OpLine %11 100 0 OpBranch %18 %20 = OpExtInst %2 %10 DebugNoScope %9 = OpLabel %21 = OpExtInst %2 %10 DebugScope %13 OpLine %11 200 0 OpBranch %18 %22 = OpExtInst %2 %10 DebugNoScope %18 = OpLabel OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck(before, after, false, true); } TEST_F(MergeReturnPassTest, TwoReturnsWithValues) { const std::string before = R"(OpCapability Linkage OpCapability Kernel OpMemoryModel Logical OpenCL OpDecorate %7 LinkageAttributes "simple_kernel" Export %1 = OpTypeInt 32 0 %2 = OpTypeBool %3 = OpConstantFalse %2 %4 = OpConstant %1 0 %5 = OpConstant %1 1 %6 = OpTypeFunction %1 %7 = OpFunction %1 None %6 %8 = OpLabel OpBranchConditional %3 %9 %10 %9 = OpLabel OpReturnValue %4 %10 = OpLabel OpReturnValue %5 OpFunctionEnd )"; const std::string after = R"(OpCapability Linkage OpCapability Kernel OpMemoryModel Logical OpenCL OpDecorate %7 LinkageAttributes "simple_kernel" Export %1 = OpTypeInt 32 0 %2 = OpTypeBool %3 = OpConstantFalse %2 %4 = OpConstant %1 0 %5 = OpConstant %1 1 %6 = OpTypeFunction %1 %7 = OpFunction %1 None %6 %8 = OpLabel OpBranchConditional %3 %9 %10 %9 = OpLabel OpBranch %11 %10 = OpLabel OpBranch %11 %11 = OpLabel %12 = OpPhi %1 %4 %9 %5 %10 OpReturnValue %12 OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck(before, after, false, true); } TEST_F(MergeReturnPassTest, UnreachableReturnsNoValue) { const std::string before = R"(OpCapability Addresses OpCapability Kernel OpCapability GenericPointer OpCapability Linkage OpMemoryModel Physical32 OpenCL OpEntryPoint Kernel %6 "simple_kernel" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpReturn %8 = OpLabel OpBranchConditional %4 %9 %10 %9 = OpLabel OpReturn %10 = OpLabel OpReturn OpFunctionEnd )"; const std::string after = R"(OpCapability Addresses OpCapability Kernel OpCapability GenericPointer OpCapability Linkage OpMemoryModel Physical32 OpenCL OpEntryPoint Kernel %6 "simple_kernel" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpBranch %11 %8 = OpLabel OpBranchConditional %4 %9 %10 %9 = OpLabel OpBranch %11 %10 = OpLabel OpBranch %11 %11 = OpLabel OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck(before, after, false, true); } TEST_F(MergeReturnPassTest, UnreachableReturnsWithValues) { const std::string before = R"(OpCapability Linkage OpCapability Kernel OpMemoryModel Logical OpenCL OpDecorate %7 LinkageAttributes "simple_kernel" Export %1 = OpTypeInt 32 0 %2 = OpTypeBool %3 = OpConstantFalse %2 %4 = OpConstant %1 0 %5 = OpConstant %1 1 %6 = OpTypeFunction %1 %7 = OpFunction %1 None %6 %8 = OpLabel %9 = OpIAdd %1 %4 %5 OpReturnValue %9 %10 = OpLabel OpBranchConditional %3 %11 %12 %11 = OpLabel OpReturnValue %4 %12 = OpLabel OpReturnValue %5 OpFunctionEnd )"; const std::string after = R"(OpCapability Linkage OpCapability Kernel OpMemoryModel Logical OpenCL OpDecorate %7 LinkageAttributes "simple_kernel" Export %1 = OpTypeInt 32 0 %2 = OpTypeBool %3 = OpConstantFalse %2 %4 = OpConstant %1 0 %5 = OpConstant %1 1 %6 = OpTypeFunction %1 %7 = OpFunction %1 None %6 %8 = OpLabel %9 = OpIAdd %1 %4 %5 OpBranch %13 %10 = OpLabel OpBranchConditional %3 %11 %12 %11 = OpLabel OpBranch %13 %12 = OpLabel OpBranch %13 %13 = OpLabel %14 = OpPhi %1 %9 %8 %4 %11 %5 %12 OpReturnValue %14 OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck(before, after, false, true); } TEST_F(MergeReturnPassTest, DebugUnreachableReturnsWithValues) { const std::string before = R"(OpCapability Linkage OpCapability Kernel %13 = OpExtInstImport "OpenCL.DebugInfo.100" OpMemoryModel Logical OpenCL %14 = OpString "test" OpDecorate %7 LinkageAttributes "simple_kernel" Export %1 = OpTypeInt 32 0 %20 = OpTypeVoid %2 = OpTypeBool %3 = OpConstantFalse %2 %4 = OpConstant %1 0 %5 = OpConstant %1 1 %6 = OpTypeFunction %1 %15 = OpExtInst %20 %13 DebugSource %14 %16 = OpExtInst %20 %13 DebugCompilationUnit 1 4 %15 HLSL %17 = OpExtInst %20 %13 DebugTypeFunction FlagIsProtected|FlagIsPrivate %20 %18 = OpExtInst %20 %13 DebugFunction %14 %17 %15 0 0 %16 %14 FlagIsProtected|FlagIsPrivate 0 %7 %7 = OpFunction %1 None %6 %8 = OpLabel %9 = OpIAdd %1 %4 %5 %19 = OpExtInst %20 %13 DebugScope %18 OpLine %14 100 0 OpReturnValue %9 %10 = OpLabel OpBranchConditional %3 %11 %12 %11 = OpLabel %21 = OpExtInst %20 %13 DebugScope %16 OpLine %14 200 0 OpReturnValue %4 %12 = OpLabel %22 = OpExtInst %20 %13 DebugScope %18 OpLine %14 300 0 OpReturnValue %5 OpFunctionEnd )"; const std::string after = R"(OpCapability Linkage OpCapability Kernel %13 = OpExtInstImport "OpenCL.DebugInfo.100" OpMemoryModel Logical OpenCL %14 = OpString "test" OpDecorate %7 LinkageAttributes "simple_kernel" Export %1 = OpTypeInt 32 0 %20 = OpTypeVoid %2 = OpTypeBool %3 = OpConstantFalse %2 %4 = OpConstant %1 0 %5 = OpConstant %1 1 %6 = OpTypeFunction %1 %15 = OpExtInst %20 %13 DebugSource %14 %16 = OpExtInst %20 %13 DebugCompilationUnit 1 4 %15 HLSL %17 = OpExtInst %20 %13 DebugTypeFunction FlagIsProtected|FlagIsPrivate %20 %18 = OpExtInst %20 %13 DebugFunction %14 %17 %15 0 0 %16 %14 FlagIsProtected|FlagIsPrivate 0 %7 %7 = OpFunction %1 None %6 %8 = OpLabel %9 = OpIAdd %1 %4 %5 %25 = OpExtInst %20 %13 DebugScope %18 OpLine %14 100 0 OpBranch %23 %26 = OpExtInst %20 %13 DebugNoScope %10 = OpLabel OpBranchConditional %3 %11 %12 %11 = OpLabel %27 = OpExtInst %20 %13 DebugScope %16 OpLine %14 200 0 OpBranch %23 %28 = OpExtInst %20 %13 DebugNoScope %12 = OpLabel %29 = OpExtInst %20 %13 DebugScope %18 OpLine %14 300 0 OpBranch %23 %30 = OpExtInst %20 %13 DebugNoScope %23 = OpLabel %24 = OpPhi %1 %9 %8 %4 %11 %5 %12 OpReturnValue %24 OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck(before, after, false, true); } TEST_F(MergeReturnPassTest, StructuredControlFlowWithUnreachableMerge) { const std::string before = R"( ; CHECK: [[false:%\w+]] = OpConstantFalse ; CHECK: [[true:%\w+]] = OpConstantTrue ; CHECK: OpFunction ; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]] ; CHECK: OpSelectionMerge [[return_block:%\w+]] ; CHECK: OpSelectionMerge [[merge_lab:%\w+]] ; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]] ; CHECK: [[if_lab]] = OpLabel ; CHECK-NEXT: OpStore [[var]] [[true]] ; CHECK-NEXT: OpBranch [[return_block]] ; CHECK: [[then_lab]] = OpLabel ; CHECK-NEXT: OpStore [[var]] [[true]] ; CHECK-NEXT: OpBranch [[return_block]] ; CHECK: [[merge_lab]] = OpLabel ; CHECK-NEXT: OpBranch [[return_block]] ; CHECK: [[return_block]] = OpLabel ; CHECK-NEXT: OpReturn OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %6 "simple_shader" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpSelectionMerge %10 None OpBranchConditional %4 %8 %9 %8 = OpLabel OpReturn %9 = OpLabel OpReturn %10 = OpLabel OpUnreachable OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, DebugStructuredControlFlowWithUnreachableMerge) { const std::string before = R"( ; CHECK: [[false:%\w+]] = OpConstantFalse ; CHECK: [[true:%\w+]] = OpConstantTrue ; CHECK: OpFunction ; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]] ; CHECK: OpSelectionMerge [[return_block:%\w+]] ; CHECK: OpSelectionMerge [[merge_lab:%\w+]] ; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]] ; CHECK: [[if_lab]] = OpLabel ; CHECK-NEXT: OpStore [[var]] [[true]] ; CHECK-NEXT: DebugScope ; CHECK-NEXT: OpLine {{%\d+}} 100 0 ; CHECK-NEXT: OpBranch [[return_block]] ; CHECK: [[then_lab]] = OpLabel ; CHECK-NEXT: OpStore [[var]] [[true]] ; CHECK-NEXT: DebugScope ; CHECK-NEXT: OpLine {{%\d+}} 200 0 ; CHECK-NEXT: OpBranch [[return_block]] ; CHECK: [[merge_lab]] = OpLabel ; CHECK-NEXT: DebugScope ; CHECK-NEXT: OpLine {{%\d+}} 300 0 ; CHECK-NEXT: OpBranch [[return_block]] ; CHECK: [[return_block]] = OpLabel ; CHECK-NEXT: OpReturn OpCapability Addresses OpCapability Shader OpCapability Linkage %12 = OpExtInstImport "OpenCL.DebugInfo.100" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %6 "simple_shader" %11 = OpString "test" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %13 = OpExtInst %2 %12 DebugSource %11 %14 = OpExtInst %2 %12 DebugCompilationUnit 1 4 %13 HLSL %6 = OpFunction %2 None %1 %7 = OpLabel OpSelectionMerge %10 None OpBranchConditional %4 %8 %9 %8 = OpLabel %15 = OpExtInst %2 %12 DebugScope %14 OpLine %11 100 0 OpReturn %9 = OpLabel %16 = OpExtInst %2 %12 DebugScope %14 OpLine %11 200 0 OpReturn %10 = OpLabel %17 = OpExtInst %2 %12 DebugScope %14 OpLine %11 300 0 OpUnreachable OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, StructuredControlFlowAddPhi) { const std::string before = R"( ; CHECK: [[false:%\w+]] = OpConstantFalse ; CHECK: [[true:%\w+]] = OpConstantTrue ; CHECK: OpFunction ; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]] ; CHECK: OpSelectionMerge [[single_case_switch_merge:%\w+]] ; CHECK: OpSelectionMerge [[merge_lab:%\w+]] ; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]] ; CHECK: [[if_lab]] = OpLabel ; CHECK-NEXT: [[add:%\w+]] = OpIAdd [[type:%\w+]] ; CHECK-NEXT: OpBranch ; CHECK: [[then_lab]] = OpLabel ; CHECK-NEXT: OpStore [[var]] [[true]] ; CHECK-NEXT: OpBranch [[single_case_switch_merge]] ; CHECK: [[merge_lab]] = OpLabel ; CHECK: [[single_case_switch_merge]] = OpLabel ; CHECK-NEXT: OpReturn OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %6 "simple_shader" %2 = OpTypeVoid %3 = OpTypeBool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpSelectionMerge %10 None OpBranchConditional %4 %8 %9 %8 = OpLabel %11 = OpIAdd %int %int_0 %int_0 OpBranch %10 %9 = OpLabel OpReturn %10 = OpLabel %12 = OpIAdd %int %11 %11 OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, StructuredControlDecoration) { const std::string before = R"( ; CHECK: OpDecorate [[dec_id:%\w+]] RelaxedPrecision ; CHECK: [[false:%\w+]] = OpConstantFalse ; CHECK: [[true:%\w+]] = OpConstantTrue ; CHECK: OpFunction ; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]] ; CHECK: OpSelectionMerge [[return_block:%\w+]] ; CHECK: OpSelectionMerge [[merge_lab:%\w+]] ; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]] ; CHECK: [[if_lab]] = OpLabel ; CHECK-NEXT: [[dec_id]] = OpIAdd [[type:%\w+]] ; CHECK-NEXT: OpBranch ; CHECK: [[then_lab]] = OpLabel ; CHECK-NEXT: OpStore [[var]] [[true]] ; CHECK-NEXT: OpBranch [[return_block]] ; CHECK: [[merge_lab]] = OpLabel ; CHECK-NEXT: OpStore [[var]] [[true]] ; CHECK-NEXT: OpBranch [[return_block]] ; CHECK: [[return_block]] = OpLabel ; CHECK-NEXT: OpReturn OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %6 "simple_shader" OpDecorate %11 RelaxedPrecision %2 = OpTypeVoid %3 = OpTypeBool %int = OpTypeInt 32 0 %int_0 = OpConstant %int 0 %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpSelectionMerge %10 None OpBranchConditional %4 %8 %9 %8 = OpLabel %11 = OpIAdd %int %int_0 %int_0 OpBranch %10 %9 = OpLabel OpReturn %10 = OpLabel OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, SplitBlockUsedInPhi) { const std::string before = R"( ; CHECK: OpFunction ; CHECK: OpSelectionMerge [[single_case_switch_merge:%\w+]] ; CHECK: OpLoopMerge [[loop_merge:%\w+]] ; CHECK: [[loop_merge]] = OpLabel ; CHECK: OpBranchConditional {{%\w+}} [[single_case_switch_merge]] [[old_code_path:%\w+]] ; CHECK: [[old_code_path:%\w+]] = OpLabel ; CHECK: OpBranchConditional {{%\w+}} [[side_node:%\w+]] [[phi_block:%\w+]] ; CHECK: [[phi_block]] = OpLabel ; CHECK-NEXT: OpPhi %bool %false [[side_node]] %true [[old_code_path]] OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %void = OpTypeVoid %bool = OpTypeBool %false = OpConstantFalse %bool %true = OpConstantTrue %bool %6 = OpTypeFunction %void %1 = OpFunction %void None %6 %7 = OpLabel OpLoopMerge %merge %cont None OpBranchConditional %false %9 %merge %9 = OpLabel OpReturn %cont = OpLabel OpBranch %7 %merge = OpLabel OpSelectionMerge %merge2 None OpBranchConditional %false %if %merge2 %if = OpLabel OpBranch %merge2 %merge2 = OpLabel %12 = OpPhi %bool %false %if %true %merge OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, DebugSplitBlockUsedInPhi) { const std::string before = R"( ; CHECK: DebugScope ; CHECK-NEXT: OpLine {{%\d+}} 100 0 ; CHECK: OpLoopMerge ; CHECK: OpStore [[return_in_loop:%\w+]] %true ; CHECK-NEXT: DebugScope ; CHECK-NEXT: OpLine {{%\d+}} 200 0 ; CHECK-NEXT: OpBranch [[check_early_return:%\w+]] ; CHECK: [[check_early_return]] = OpLabel ; CHECK-NEXT: [[early_return:%\w+]] = OpLoad %bool [[return_in_loop]] ; CHECK-NEXT: OpSelectionMerge [[not_early_return:%\w+]] None ; CHECK-NEXT: OpBranchConditional [[early_return]] {{%\d+}} [[not_early_return]] ; CHECK: [[not_early_return]] = OpLabel ; CHECK-NEXT: DebugScope ; CHECK-NEXT: OpLine {{%\d+}} 400 0 ; CHECK: OpSelectionMerge [[merge2:%\w+]] None ; CHECK: [[merge2]] = OpLabel ; CHECK-NEXT: DebugScope ; CHECK-NEXT: OpLine {{%\d+}} 600 0 ; CHECK-NEXT: [[phi:%\w+]] = OpPhi %bool %false {{%\d+}} %true [[not_early_return]] ; CHECK-NEXT: DebugValue {{%\d+}} [[phi]] OpCapability Addresses OpCapability Shader OpCapability Linkage %ext = OpExtInstImport "OpenCL.DebugInfo.100" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %tn = OpString "test" %void = OpTypeVoid %bool = OpTypeBool %uint = OpTypeInt 32 0 %uint_8 = OpConstant %uint 8 %false = OpConstantFalse %bool %true = OpConstantTrue %bool %6 = OpTypeFunction %void %src = OpExtInst %void %ext DebugSource %tn %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL %ty = OpExtInst %void %ext DebugTypeBasic %tn %uint_8 Boolean %v = OpExtInst %void %ext DebugLocalVariable %tn %ty %src 0 0 %cu FlagIsLocal %expr = OpExtInst %void %ext DebugExpression %1 = OpFunction %void None %6 %7 = OpLabel %s0 = OpExtInst %void %ext DebugScope %cu OpLine %tn 100 0 OpLoopMerge %merge %cont None OpBranchConditional %false %9 %merge %9 = OpLabel %s1 = OpExtInst %void %ext DebugScope %cu OpLine %tn 200 0 OpReturn %cont = OpLabel %s2 = OpExtInst %void %ext DebugScope %cu OpLine %tn 300 0 OpBranch %7 %merge = OpLabel %s3 = OpExtInst %void %ext DebugScope %cu OpLine %tn 400 0 OpSelectionMerge %merge2 None OpBranchConditional %false %if %merge2 %if = OpLabel %s4 = OpExtInst %void %ext DebugScope %cu OpLine %tn 500 0 OpBranch %merge2 %merge2 = OpLabel %s5 = OpExtInst %void %ext DebugScope %cu OpLine %tn 600 0 %12 = OpPhi %bool %false %if %true %merge %dv = OpExtInst %void %ext DebugValue %v %12 %expr OpLine %tn 900 0 OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(before, false); } // TODO(#1861): Reenable these test when the breaks from selection constructs // are reenabled. /* TEST_F(MergeReturnPassTest, UpdateOrderWhenPredicating) { const std::string before = R"( ; CHECK: OpFunction ; CHECK: OpFunction ; CHECK: OpSelectionMerge [[m1:%\w+]] None ; CHECK-NOT: OpReturn ; CHECK: [[m1]] = OpLabel ; CHECK: OpSelectionMerge [[m2:%\w+]] None ; CHECK: OpSelectionMerge [[m3:%\w+]] None ; CHECK: OpSelectionMerge [[m4:%\w+]] None ; CHECK: OpLabel ; CHECK-NEXT: OpStore ; CHECK-NEXT: OpBranch [[m4]] ; CHECK: [[m4]] = OpLabel ; CHECK-NEXT: [[ld4:%\w+]] = OpLoad %bool ; CHECK-NEXT: OpBranchConditional [[ld4]] [[m3]] ; CHECK: [[m3]] = OpLabel ; CHECK-NEXT: [[ld3:%\w+]] = OpLoad %bool ; CHECK-NEXT: OpBranchConditional [[ld3]] [[m2]] ; CHECK: [[m2]] = OpLabel OpCapability SampledBuffer OpCapability StorageImageExtendedFormats OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "PS_DebugTiles" OpExecutionMode %1 OriginUpperLeft OpSource HLSL 600 %void = OpTypeVoid %3 = OpTypeFunction %void %bool = OpTypeBool %1 = OpFunction %void None %3 %5 = OpLabel %6 = OpFunctionCall %void %7 OpReturn OpFunctionEnd %7 = OpFunction %void None %3 %8 = OpLabel %9 = OpUndef %bool OpSelectionMerge %10 None OpBranchConditional %9 %11 %10 %11 = OpLabel OpReturn %10 = OpLabel %12 = OpUndef %bool OpSelectionMerge %13 None OpBranchConditional %12 %14 %15 %15 = OpLabel %16 = OpUndef %bool OpSelectionMerge %17 None OpBranchConditional %16 %18 %17 %18 = OpLabel OpReturn %17 = OpLabel OpBranch %13 %14 = OpLabel OpReturn %13 = OpLabel OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(before, false); } */ TEST_F(MergeReturnPassTest, StructuredControlFlowBothMergeAndHeader) { const std::string test = R"( ; CHECK: OpFunction ; CHECK: [[ret_flag:%\w+]] = OpVariable %_ptr_Function_bool Function %false ; CHECK: OpSelectionMerge [[single_case_switch_merge:%\w+]] ; CHECK: OpLoopMerge [[loop1_merge:%\w+]] {{%\w+}} ; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_lab:%\w+]] {{%\w+}} ; CHECK: [[if_lab]] = OpLabel ; CHECK: OpStore [[ret_flag]] %true ; CHECK-NEXT: OpBranch [[loop1_merge]] ; CHECK: [[loop1_merge]] = OpLabel ; CHECK-NEXT: [[ld:%\w+]] = OpLoad %bool [[ret_flag]] ; CHECK-NOT: OpLabel ; CHECK: OpBranchConditional [[ld]] [[single_case_switch_merge]] [[empty_block:%\w+]] ; CHECK: [[empty_block]] = OpLabel ; CHECK-NEXT: OpBranch [[loop2:%\w+]] ; CHECK: [[loop2]] = OpLabel ; CHECK-NOT: OpLabel ; CHECK: OpLoopMerge OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %void = OpTypeVoid %bool = OpTypeBool %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %false = OpConstantFalse %bool %7 = OpTypeFunction %void %1 = OpFunction %void None %7 %8 = OpLabel OpBranch %9 %9 = OpLabel OpLoopMerge %10 %11 None OpBranchConditional %false %12 %13 %12 = OpLabel OpReturn %13 = OpLabel OpBranch %10 %11 = OpLabel OpBranch %9 %10 = OpLabel OpLoopMerge %14 %15 None OpBranch %15 %15 = OpLabel %16 = OpIAdd %uint %uint_0 %uint_0 OpBranchConditional %false %10 %14 %14 = OpLabel %17 = OpIAdd %uint %16 %16 OpReturn OpFunctionEnd )"; const std::string after = R"(OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %void = OpTypeVoid %bool = OpTypeBool %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %false = OpConstantFalse %bool %7 = OpTypeFunction %void %_ptr_Function_bool = OpTypePointer Function %bool %true = OpConstantTrue %bool %1 = OpFunction %void None %7 %8 = OpLabel %18 = OpVariable %_ptr_Function_bool Function %false OpSelectionMerge %9 None OpBranchConditional %false %10 %11 %10 = OpLabel OpStore %18 %true OpBranch %9 %11 = OpLabel OpBranch %9 %9 = OpLabel %23 = OpLoad %bool %18 OpSelectionMerge %22 None OpBranchConditional %23 %22 %21 %21 = OpLabel OpBranch %20 %20 = OpLabel OpLoopMerge %12 %13 None OpBranch %13 %13 = OpLabel %14 = OpIAdd %uint %uint_0 %uint_0 OpBranchConditional %false %20 %12 %12 = OpLabel %15 = OpIAdd %uint %14 %14 OpStore %18 %true OpBranch %22 %22 = OpLabel OpBranch %16 %16 = OpLabel OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(test, false); } // TODO(#1861): Reenable these test when the breaks from selection constructs // are reenabled. /* TEST_F(MergeReturnPassTest, NestedSelectionMerge) { const std::string before = R"( OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %void = OpTypeVoid %bool = OpTypeBool %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %false = OpConstantFalse %bool %7 = OpTypeFunction %void %1 = OpFunction %void None %7 %8 = OpLabel OpSelectionMerge %9 None OpBranchConditional %false %10 %11 %10 = OpLabel OpReturn %11 = OpLabel OpSelectionMerge %12 None OpBranchConditional %false %13 %14 %13 = OpLabel %15 = OpIAdd %uint %uint_0 %uint_0 OpBranch %12 %14 = OpLabel OpReturn %12 = OpLabel OpBranch %9 %9 = OpLabel %16 = OpIAdd %uint %15 %15 OpReturn OpFunctionEnd )"; const std::string after = R"(OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %void = OpTypeVoid %bool = OpTypeBool %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %false = OpConstantFalse %bool %7 = OpTypeFunction %void %_ptr_Function_bool = OpTypePointer Function %bool %true = OpConstantTrue %bool %26 = OpUndef %uint %1 = OpFunction %void None %7 %8 = OpLabel %19 = OpVariable %_ptr_Function_bool Function %false OpSelectionMerge %9 None OpBranchConditional %false %10 %11 %10 = OpLabel OpStore %19 %true OpBranch %9 %11 = OpLabel OpSelectionMerge %12 None OpBranchConditional %false %13 %14 %13 = OpLabel %15 = OpIAdd %uint %uint_0 %uint_0 OpBranch %12 %14 = OpLabel OpStore %19 %true OpBranch %12 %12 = OpLabel %27 = OpPhi %uint %15 %13 %26 %14 %22 = OpLoad %bool %19 OpBranchConditional %22 %9 %21 %21 = OpLabel OpBranch %9 %9 = OpLabel %28 = OpPhi %uint %27 %21 %26 %10 %26 %12 %25 = OpLoad %bool %19 OpSelectionMerge %24 None OpBranchConditional %25 %24 %23 %23 = OpLabel %16 = OpIAdd %uint %28 %28 OpStore %19 %true OpBranch %24 %24 = OpLabel OpBranch %17 %17 = OpLabel OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck(before, after, false, true); } // This is essentially the same as NestedSelectionMerge, except // the order of the first branch is changed. This is to make sure things // work even if the order of the traversals change. TEST_F(MergeReturnPassTest, NestedSelectionMerge2) { const std::string before = R"( OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %void = OpTypeVoid %bool = OpTypeBool %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %false = OpConstantFalse %bool %7 = OpTypeFunction %void %1 = OpFunction %void None %7 %8 = OpLabel OpSelectionMerge %9 None OpBranchConditional %false %10 %11 %11 = OpLabel OpReturn %10 = OpLabel OpSelectionMerge %12 None OpBranchConditional %false %13 %14 %13 = OpLabel %15 = OpIAdd %uint %uint_0 %uint_0 OpBranch %12 %14 = OpLabel OpReturn %12 = OpLabel OpBranch %9 %9 = OpLabel %16 = OpIAdd %uint %15 %15 OpReturn OpFunctionEnd )"; const std::string after = R"(OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %void = OpTypeVoid %bool = OpTypeBool %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %false = OpConstantFalse %bool %7 = OpTypeFunction %void %_ptr_Function_bool = OpTypePointer Function %bool %true = OpConstantTrue %bool %26 = OpUndef %uint %1 = OpFunction %void None %7 %8 = OpLabel %19 = OpVariable %_ptr_Function_bool Function %false OpSelectionMerge %9 None OpBranchConditional %false %10 %11 %11 = OpLabel OpStore %19 %true OpBranch %9 %10 = OpLabel OpSelectionMerge %12 None OpBranchConditional %false %13 %14 %13 = OpLabel %15 = OpIAdd %uint %uint_0 %uint_0 OpBranch %12 %14 = OpLabel OpStore %19 %true OpBranch %12 %12 = OpLabel %27 = OpPhi %uint %15 %13 %26 %14 %25 = OpLoad %bool %19 OpBranchConditional %25 %9 %24 %24 = OpLabel OpBranch %9 %9 = OpLabel %28 = OpPhi %uint %27 %24 %26 %11 %26 %12 %23 = OpLoad %bool %19 OpSelectionMerge %22 None OpBranchConditional %23 %22 %21 %21 = OpLabel %16 = OpIAdd %uint %28 %28 OpStore %19 %true OpBranch %22 %22 = OpLabel OpBranch %17 %17 = OpLabel OpReturn OpFunctionEnd )"; SinglePassRunAndCheck(before, after, false, true); } TEST_F(MergeReturnPassTest, NestedSelectionMerge3) { const std::string before = R"( OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %void = OpTypeVoid %bool = OpTypeBool %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %false = OpConstantFalse %bool %7 = OpTypeFunction %void %1 = OpFunction %void None %7 %8 = OpLabel OpSelectionMerge %9 None OpBranchConditional %false %10 %11 %11 = OpLabel OpReturn %10 = OpLabel %12 = OpIAdd %uint %uint_0 %uint_0 OpSelectionMerge %13 None OpBranchConditional %false %14 %15 %14 = OpLabel OpBranch %13 %15 = OpLabel OpReturn %13 = OpLabel OpBranch %9 %9 = OpLabel %16 = OpIAdd %uint %12 %12 OpReturn OpFunctionEnd )"; const std::string after = R"(OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "simple_shader" %void = OpTypeVoid %bool = OpTypeBool %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %false = OpConstantFalse %bool %7 = OpTypeFunction %void %_ptr_Function_bool = OpTypePointer Function %bool %true = OpConstantTrue %bool %26 = OpUndef %uint %1 = OpFunction %void None %7 %8 = OpLabel %19 = OpVariable %_ptr_Function_bool Function %false OpSelectionMerge %9 None OpBranchConditional %false %10 %11 %11 = OpLabel OpStore %19 %true OpBranch %9 %10 = OpLabel %12 = OpIAdd %uint %uint_0 %uint_0 OpSelectionMerge %13 None OpBranchConditional %false %14 %15 %14 = OpLabel OpBranch %13 %15 = OpLabel OpStore %19 %true OpBranch %13 %13 = OpLabel %25 = OpLoad %bool %19 OpBranchConditional %25 %9 %24 %24 = OpLabel OpBranch %9 %9 = OpLabel %27 = OpPhi %uint %12 %24 %26 %11 %26 %13 %23 = OpLoad %bool %19 OpSelectionMerge %22 None OpBranchConditional %23 %22 %21 %21 = OpLabel %16 = OpIAdd %uint %27 %27 OpStore %19 %true OpBranch %22 %22 = OpLabel OpBranch %17 %17 = OpLabel OpReturn OpFunctionEnd )"; SinglePassRunAndCheck(before, after, false, true); } */ TEST_F(MergeReturnPassTest, NestedLoopMerge) { const std::string test = R"( ; CHECK: OpFunction ; CHECK: OpSelectionMerge [[single_case_switch_merge:%\w+]] ; CHECK: OpLoopMerge [[outer_loop_merge:%\w+]] ; CHECK: OpLoopMerge [[inner_loop_merge:%\w+]] ; CHECK: OpSelectionMerge ; CHECK-NEXT: OpBranchConditional %true [[early_exit_block:%\w+]] ; CHECK: [[early_exit_block]] = OpLabel ; CHECK-NOT: OpLabel ; CHECK: OpBranch [[inner_loop_merge]] ; CHECK: [[inner_loop_merge]] = OpLabel ; CHECK-NOT: OpLabel ; CHECK: OpBranchConditional {{%\w+}} [[outer_loop_merge]] ; CHECK: [[outer_loop_merge]] = OpLabel ; CHECK-NOT: OpLabel ; CHECK: OpBranchConditional {{%\w+}} [[single_case_switch_merge]] ; CHECK: [[single_case_switch_merge]] = OpLabel ; CHECK-NOT: OpLabel ; CHECK: OpReturn OpCapability SampledBuffer OpCapability StorageImageExtendedFormats OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %2 "CS" OpExecutionMode %2 LocalSize 8 8 1 OpSource HLSL 600 %uint = OpTypeInt 32 0 %void = OpTypeVoid %6 = OpTypeFunction %void %uint_0 = OpConstant %uint 0 %uint_1 = OpConstant %uint 1 %v3uint = OpTypeVector %uint 3 %bool = OpTypeBool %true = OpConstantTrue %bool %_ptr_Function_uint = OpTypePointer Function %uint %2 = OpFunction %void None %6 %14 = OpLabel OpBranch %19 %19 = OpLabel %20 = OpPhi %uint %uint_0 %2 %34 %23 %21 = OpULessThan %bool %20 %uint_1 OpLoopMerge %22 %23 DontUnroll OpBranchConditional %21 %24 %22 %24 = OpLabel OpBranch %25 %25 = OpLabel %27 = OpINotEqual %bool %uint_1 %uint_0 OpLoopMerge %28 %29 DontUnroll OpBranchConditional %27 %30 %28 %30 = OpLabel OpSelectionMerge %31 None OpBranchConditional %true %32 %31 %32 = OpLabel OpReturn %31 = OpLabel OpBranch %29 %29 = OpLabel OpBranch %25 %28 = OpLabel OpBranch %23 %23 = OpLabel %34 = OpIAdd %uint %20 %uint_1 OpBranch %19 %22 = OpLabel OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(test, false); } TEST_F(MergeReturnPassTest, ReturnValueDecoration) { const std::string test = R"( ; CHECK: OpDecorate [[func:%\w+]] RelaxedPrecision ; CHECK: OpDecorate [[ret_val:%\w+]] RelaxedPrecision ; CHECK: [[func]] = OpFunction ; CHECK-NEXT: OpLabel ; CHECK-NOT: OpLabel ; CHECK: [[ret_val]] = OpVariable OpCapability Linkage OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %11 "simple_shader" OpDecorate %7 RelaxedPrecision %12 = OpTypeVoid %1 = OpTypeInt 32 0 %2 = OpTypeBool %3 = OpConstantFalse %2 %4 = OpConstant %1 0 %5 = OpConstant %1 1 %6 = OpTypeFunction %1 %13 = OpTypeFunction %12 %11 = OpFunction %12 None %13 %l1 = OpLabel %fc = OpFunctionCall %1 %7 OpReturn OpFunctionEnd %7 = OpFunction %1 None %6 %8 = OpLabel OpBranchConditional %3 %9 %10 %9 = OpLabel OpReturnValue %4 %10 = OpLabel OpReturnValue %5 OpFunctionEnd )"; SinglePassRunAndMatch(test, false); } TEST_F(MergeReturnPassTest, StructuredControlFlowWithNonTrivialUnreachableMerge) { const std::string before = R"( OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %6 "simple_shader" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpSelectionMerge %10 None OpBranchConditional %4 %8 %9 %8 = OpLabel OpReturn %9 = OpLabel OpReturn %10 = OpLabel %11 = OpUndef %3 OpUnreachable OpFunctionEnd )"; std::vector messages = { {SPV_MSG_ERROR, nullptr, 0, 0, "Module contains unreachable blocks during merge return. Run dead " "branch elimination before merge return."}}; SetMessageConsumer(GetTestMessageConsumer(messages)); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); auto result = SinglePassRunToBinary(before, false); EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); EXPECT_TRUE(messages.empty()); } TEST_F(MergeReturnPassTest, StructuredControlFlowWithNonTrivialUnreachableContinue) { const std::string before = R"( OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %6 "simple_shader" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpBranch %header %header = OpLabel OpLoopMerge %merge %continue None OpBranchConditional %4 %8 %merge %8 = OpLabel OpReturn %continue = OpLabel %11 = OpUndef %3 OpBranch %header %merge = OpLabel OpReturn OpFunctionEnd )"; std::vector messages = { {SPV_MSG_ERROR, nullptr, 0, 0, "Module contains unreachable blocks during merge return. Run dead " "branch elimination before merge return."}}; SetMessageConsumer(GetTestMessageConsumer(messages)); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); auto result = SinglePassRunToBinary(before, false); EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); EXPECT_TRUE(messages.empty()); } TEST_F(MergeReturnPassTest, StructuredControlFlowWithUnreachableBlock) { const std::string before = R"( OpCapability Addresses OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %6 "simple_shader" %2 = OpTypeVoid %3 = OpTypeBool %4 = OpConstantFalse %3 %1 = OpTypeFunction %2 %6 = OpFunction %2 None %1 %7 = OpLabel OpBranch %header %header = OpLabel OpLoopMerge %merge %continue None OpBranchConditional %4 %8 %merge %8 = OpLabel OpReturn %continue = OpLabel OpBranch %header %merge = OpLabel OpReturn %unreachable = OpLabel OpUnreachable OpFunctionEnd )"; std::vector messages = { {SPV_MSG_ERROR, nullptr, 0, 0, "Module contains unreachable blocks during merge return. Run dead " "branch elimination before merge return."}}; SetMessageConsumer(GetTestMessageConsumer(messages)); SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); auto result = SinglePassRunToBinary(before, false); EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); EXPECT_TRUE(messages.empty()); } TEST_F(MergeReturnPassTest, StructuredControlFlowDontChangeEntryPhi) { const std::string before = R"( ; CHECK: OpFunction %void ; CHECK: OpLabel ; CHECK: [[pre_header:%\w+]] = OpLabel ; CHECK: [[header:%\w+]] = OpLabel ; CHECK-NEXT: OpPhi %bool {{%\w+}} [[pre_header]] [[iv:%\w+]] [[continue:%\w+]] ; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] ; CHECK: [[continue]] = OpLabel ; CHECK-NEXT: [[iv]] = Op ; CHECK: [[merge]] = OpLabel OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %1 "main" %void = OpTypeVoid %bool = OpTypeBool %4 = OpTypeFunction %void %1 = OpFunction %void None %4 %5 = OpLabel %6 = OpUndef %bool OpBranch %7 %7 = OpLabel %8 = OpPhi %bool %6 %5 %9 %10 OpLoopMerge %11 %10 None OpBranch %12 %12 = OpLabel %13 = OpUndef %bool OpSelectionMerge %10 DontFlatten OpBranchConditional %13 %10 %14 %14 = OpLabel OpReturn %10 = OpLabel %9 = OpUndef %bool OpBranchConditional %13 %7 %11 %11 = OpLabel OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, StructuredControlFlowPartialReplacePhi) { const std::string before = R"( ; CHECK: OpFunction %void ; CHECK: OpLabel ; CHECK: [[pre_header:%\w+]] = OpLabel ; CHECK: [[header:%\w+]] = OpLabel ; CHECK-NEXT: OpPhi ; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] ; CHECK: OpLabel ; CHECK: [[old_ret_block:%\w+]] = OpLabel ; CHECK: [[bb:%\w+]] = OpLabel ; CHECK-NEXT: [[val:%\w+]] = OpUndef %bool ; CHECK: [[merge]] = OpLabel ; CHECK-NEXT: [[phi1:%\w+]] = OpPhi %bool {{%\w+}} [[old_ret_block]] [[val]] [[bb]] ; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} [[bb2:%\w+]] ; CHECK: [[bb2]] = OpLabel ; CHECK: OpBranch [[header2:%\w+]] ; CHECK: [[header2]] = OpLabel ; CHECK-NEXT: [[phi2:%\w+]] = OpPhi %bool [[phi1]] [[continue2:%\w+]] [[phi1]] [[bb2]] ; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue2]] ; CHECK: [[continue2]] = OpLabel ; CHECK-NEXT: OpBranch [[header2]] OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %1 "main" %void = OpTypeVoid %bool = OpTypeBool %4 = OpTypeFunction %void %1 = OpFunction %void None %4 %5 = OpLabel %6 = OpUndef %bool OpBranch %7 %7 = OpLabel %8 = OpPhi %bool %6 %5 %9 %10 OpLoopMerge %11 %10 None OpBranch %12 %12 = OpLabel %13 = OpUndef %bool OpSelectionMerge %10 DontFlatten OpBranchConditional %13 %10 %14 %14 = OpLabel OpReturn %10 = OpLabel %9 = OpUndef %bool OpBranchConditional %13 %7 %11 %11 = OpLabel %phi = OpPhi %bool %9 %10 %9 %cont OpLoopMerge %ret %cont None OpBranch %bb %bb = OpLabel OpBranchConditional %13 %ret %cont %cont = OpLabel OpBranch %11 %ret = OpLabel OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, GeneratePhiInOuterLoop) { const std::string before = R"( ; CHECK: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] ; CHECK-NEXT: [[def_bb1]] = OpLabel ; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] ; CHECK: [[continue]] = OpLabel ; CHECK-NEXT: [[undef:%\w+]] = OpUndef ; CHECK: [[merge]] = OpLabel ; CHECK-NEXT: [[phi:%\w+]] = OpPhi %bool {{%\w+}} {{%\w+}} [[undef]] [[continue]] ; CHECK: OpCopyObject %bool [[phi]] 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 %8 = OpTypeFunction %bool %false = OpConstantFalse %bool %4 = OpFunction %void None %3 %5 = OpLabel %63 = OpFunctionCall %bool %9 OpReturn OpFunctionEnd %9 = OpFunction %bool None %8 %10 = OpLabel OpBranch %31 %31 = OpLabel OpLoopMerge %33 %34 None OpBranch %32 %32 = OpLabel OpSelectionMerge %34 None OpBranchConditional %false %46 %34 %46 = OpLabel OpLoopMerge %51 %52 None OpBranch %53 %53 = OpLabel OpBranchConditional %false %50 %51 %50 = OpLabel OpReturnValue %false %52 = OpLabel OpBranch %46 %51 = OpLabel OpBranch %34 %34 = OpLabel %64 = OpUndef %bool OpBranchConditional %false %31 %33 %33 = OpLabel OpBranch %28 %28 = OpLabel %60 = OpCopyObject %bool %64 OpBranch %17 %17 = OpLabel OpReturnValue %false OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) { // #2455: This test case triggers phi insertions that use previously inserted // phis. Without the fix, it fails to validate. const std::string spirv = R"( ; CHECK: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] ; CHECK-NEXT: [[def_bb1]] = OpLabel ; CHECK: OpLoopMerge ; CHECK: OpLoopMerge ; CHECK: OpLoopMerge [[merge:%\w+]] ; CHECK: [[def:%\w+]] = OpFOrdLessThan ; CHECK: [[merge]] = OpLabel ; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[def]] ; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] ; CHECK: [[cont]] = OpLabel ; CHECK-NEXT: OpBranchConditional [[phi]] ; CHECK-NOT: [[def]] OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %53 OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpDecorate %20 RelaxedPrecision OpDecorate %27 RelaxedPrecision OpDecorate %53 BuiltIn FragCoord %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypeVector %6 3 %8 = OpTypeFunction %7 %11 = OpTypeBool %12 = OpConstantFalse %11 %15 = OpConstant %6 1 %16 = OpConstantComposite %7 %15 %15 %15 %18 = OpTypeInt 32 1 %19 = OpTypePointer Function %18 %21 = OpConstant %18 1 %28 = OpConstant %18 0 %31 = OpTypePointer Function %11 %33 = OpConstantTrue %11 %51 = OpTypeVector %6 4 %52 = OpTypePointer Input %51 %53 = OpVariable %52 Input %54 = OpTypeInt 32 0 %55 = OpConstant %54 0 %56 = OpTypePointer Input %6 %59 = OpConstant %6 0 %76 = OpUndef %18 %77 = OpUndef %11 %78 = OpUndef %6 %4 = OpFunction %2 None %3 %5 = OpLabel %75 = OpFunctionCall %7 %9 OpReturn OpFunctionEnd %9 = OpFunction %7 None %8 %10 = OpLabel %20 = OpVariable %19 Function OpBranch %14 %14 = OpLabel OpBranch %22 %22 = OpLabel %27 = OpLoad %18 %20 OpLoopMerge %24 %25 None OpBranch %24 %25 = OpLabel OpBranch %22 %24 = OpLabel OpBranch %34 %34 = OpLabel OpLoopMerge %36 %40 None OpBranch %35 %35 = OpLabel OpBranchConditional %77 %39 %40 %39 = OpLabel OpReturnValue %16 %40 = OpLabel OpBranchConditional %12 %34 %36 %36 = OpLabel OpBranch %43 %43 = OpLabel OpLoopMerge %45 %49 None OpBranch %44 %44 = OpLabel OpBranchConditional %77 %48 %49 %48 = OpLabel OpReturnValue %16 %49 = OpLabel %60 = OpFOrdLessThan %11 %15 %59 OpBranchConditional %12 %43 %45 %45 = OpLabel OpBranch %62 %62 = OpLabel OpLoopMerge %64 %68 None OpBranch %63 %63 = OpLabel OpBranchConditional %77 %67 %68 %67 = OpLabel OpReturnValue %16 %68 = OpLabel OpBranchConditional %60 %62 %64 %64 = OpLabel OpReturnValue %16 OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(spirv, true); } TEST_F(MergeReturnPassTest, InnerLoopMergeIsOuterLoopContinue) { const std::string before = R"( ; CHECK: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] ; CHECK-NEXT: [[def_bb1]] = OpLabel ; CHECK-NEXT: OpBranch [[outer_loop_header:%\w+]] ; CHECK: [[outer_loop_header]] = OpLabel ; CHECK-NEXT: OpLoopMerge [[outer_loop_merge:%\w+]] [[outer_loop_continue:%\w+]] None ; CHECK: [[outer_loop_continue]] = OpLabel ; CHECK-NEXT: OpBranch [[outer_loop_header]] OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 %void = OpTypeVoid %4 = OpTypeFunction %void %bool = OpTypeBool %6 = OpTypeFunction %bool %true = OpConstantTrue %bool %2 = OpFunction %void None %4 %8 = OpLabel %9 = OpFunctionCall %bool %10 OpReturn OpFunctionEnd %10 = OpFunction %bool None %6 %11 = OpLabel OpBranch %12 %12 = OpLabel OpLoopMerge %13 %14 None OpBranchConditional %true %15 %13 %15 = OpLabel OpLoopMerge %14 %16 None OpBranchConditional %true %17 %14 %17 = OpLabel OpReturnValue %true %16 = OpLabel OpBranch %15 %14 = OpLabel OpBranch %12 %13 = OpLabel OpReturnValue %true OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, BreakFromLoopUseNoLongerDominated) { const std::string spirv = R"( ; CHECK: [[undef:%\w+]] = OpUndef ; CHECK: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] ; CHECK-NEXT: [[def_bb1]] = OpLabel ; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] ; CHECK-NEXT: OpBranch [[body:%\w+]] ; CHECK: [[body]] = OpLabel ; CHECK-NEXT: OpSelectionMerge [[non_ret:%\w+]] ; CHECK-NEXT: OpBranchConditional {{%\w+}} [[ret:%\w+]] [[non_ret]] ; CHECK: [[ret]] = OpLabel ; CHECK-NEXT: OpStore ; CHECK-NEXT: OpBranch [[merge]] ; CHECK: [[non_ret]] = OpLabel ; CHECK-NEXT: [[def:%\w+]] = OpLogicalNot ; CHECK-NEXT: OpBranchConditional {{%\w+}} [[break:%\w+]] [[cont]] ; CHECK: [[break]] = OpLabel ; CHECK-NEXT: OpBranch [[merge]] ; CHECK: [[cont]] = OpLabel ; CHECK-NEXT: OpBranchConditional {{%\w+}} {{%\w+}} [[merge]] ; CHECK: [[merge]] = OpLabel ; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} [[undef]] [[ret]] [[def]] [[break]] [[def]] [[cont]] ; CHECK: OpLogicalNot {{%\w+}} [[phi]] OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %func "func" OpExecutionMode %func LocalSize 1 1 1 %void = OpTypeVoid %void_fn = OpTypeFunction %void %bool = OpTypeBool %true = OpConstantTrue %bool %func = OpFunction %void None %void_fn %1 = OpLabel OpBranch %2 %2 = OpLabel OpLoopMerge %8 %7 None OpBranch %3 %3 = OpLabel OpSelectionMerge %5 None OpBranchConditional %true %4 %5 %4 = OpLabel OpReturn %5 = OpLabel %def = OpLogicalNot %bool %true OpBranchConditional %true %6 %7 %6 = OpLabel OpBranch %8 %7 = OpLabel OpBranchConditional %true %2 %8 %8 = OpLabel OpBranch %9 %9 = OpLabel %use = OpLogicalNot %bool %def OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(spirv, true); } TEST_F(MergeReturnPassTest, TwoBreaksFromLoopUsesNoLongerDominated) { const std::string spirv = R"( ; CHECK: [[undef:%\w+]] = OpUndef ; CHECK: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] ; CHECK-NEXT: [[def_bb1]] = OpLabel ; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] ; CHECK-NEXT: OpBranch [[body:%\w+]] ; CHECK: [[body]] = OpLabel ; CHECK-NEXT: OpSelectionMerge [[body2:%\w+]] ; CHECK-NEXT: OpBranchConditional {{%\w+}} [[ret1:%\w+]] [[body2]] ; CHECK: [[ret1]] = OpLabel ; CHECK-NEXT: OpStore ; CHECK-NEXT: OpBranch [[merge]] ; CHECK: [[body2]] = OpLabel ; CHECK-NEXT: [[def1:%\w+]] = OpLogicalNot ; CHECK-NEXT: OpSelectionMerge [[body3:%\w+]] ; CHECK-NEXT: OpBranchConditional {{%\w+}} [[ret2:%\w+]] [[body3:%\w+]] ; CHECK: [[ret2]] = OpLabel ; CHECK-NEXT: OpStore ; CHECK-NEXT: OpBranch [[merge]] ; CHECK: [[body3]] = OpLabel ; CHECK-NEXT: [[def2:%\w+]] = OpLogicalAnd ; CHECK-NEXT: OpBranchConditional {{%\w+}} [[break:%\w+]] [[cont]] ; CHECK: [[break]] = OpLabel ; CHECK-NEXT: OpBranch [[merge]] ; CHECK: [[cont]] = OpLabel ; CHECK-NEXT: OpBranchConditional {{%\w+}} {{%\w+}} [[merge]] ; CHECK: [[merge]] = OpLabel ; CHECK-NEXT: [[phi1:%\w+]] = OpPhi {{%\w+}} [[undef]] [[ret1]] [[undef]] [[ret2]] [[def1]] [[break]] [[def1]] [[cont]] ; CHECK-NEXT: [[phi2:%\w+]] = OpPhi {{%\w+}} [[undef]] [[ret1]] [[undef]] [[ret2]] [[def2]] [[break]] [[def2]] [[cont]] ; CHECK: OpLogicalNot {{%\w+}} [[phi1]] ; CHECK: OpLogicalAnd {{%\w+}} [[phi2]] OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %func "func" OpExecutionMode %func LocalSize 1 1 1 %void = OpTypeVoid %void_fn = OpTypeFunction %void %bool = OpTypeBool %true = OpConstantTrue %bool %func = OpFunction %void None %void_fn %1 = OpLabel OpBranch %2 %2 = OpLabel OpLoopMerge %10 %9 None OpBranch %3 %3 = OpLabel OpSelectionMerge %5 None OpBranchConditional %true %4 %5 %4 = OpLabel OpReturn %5 = OpLabel %def1 = OpLogicalNot %bool %true OpSelectionMerge %7 None OpBranchConditional %true %6 %7 %6 = OpLabel OpReturn %7 = OpLabel %def2 = OpLogicalAnd %bool %true %true OpBranchConditional %true %8 %9 %8 = OpLabel OpBranch %10 %9 = OpLabel OpBranchConditional %true %2 %10 %10 = OpLabel OpBranch %11 %11 = OpLabel %use1 = OpLogicalNot %bool %def1 %use2 = OpLogicalAnd %bool %def2 %true OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(spirv, true); } TEST_F(MergeReturnPassTest, PredicateBreakBlock) { const std::string spirv = R"( ; IDs are being preserved so we can rely on basic block labels. ; CHECK: [[undef:%\w+]] = OpUndef ; CHECK: [[undef:%\w+]] = OpUndef ; CHECK: %13 = OpLabel ; CHECK-NEXT: [[def:%\w+]] = OpLogicalNot ; CHECK: %8 = OpLabel ; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} [[undef]] {{%\w+}} [[undef]] {{%\w+}} [[def]] %13 [[undef]] {{%\w+}} ; CHECK: OpLogicalAnd {{%\w+}} [[phi]] OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %1 "func" OpExecutionMode %1 LocalSize 1 1 1 %void = OpTypeVoid %3 = OpTypeFunction %void %bool = OpTypeBool %true = OpUndef %bool %1 = OpFunction %void None %3 %6 = OpLabel OpBranch %7 %7 = OpLabel OpLoopMerge %8 %9 None OpBranch %10 %10 = OpLabel OpSelectionMerge %11 None OpBranchConditional %true %12 %13 %12 = OpLabel OpLoopMerge %14 %15 None OpBranch %16 %16 = OpLabel OpReturn %15 = OpLabel OpBranch %12 %14 = OpLabel OpUnreachable %13 = OpLabel %17 = OpLogicalNot %bool %true OpBranch %8 %11 = OpLabel OpUnreachable %9 = OpLabel OpBranch %7 %8 = OpLabel OpBranch %18 %18 = OpLabel %19 = OpLogicalAnd %bool %17 %true OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(spirv, true); } TEST_F(MergeReturnPassTest, SingleReturnInLoop) { const std::string predefs = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpSource ESSL 310 %void = OpTypeVoid %7 = OpTypeFunction %void %float = OpTypeFloat 32 %9 = OpTypeFunction %float %float_1 = OpConstant %float 1 )"; const std::string caller = R"( ; CHECK: OpFunction ; CHECK: OpFunctionEnd %main = OpFunction %void None %7 %22 = OpLabel %30 = OpFunctionCall %float %f_ OpReturn OpFunctionEnd )"; const std::string callee = R"( ; CHECK: OpFunction ; CHECK: OpLoopMerge [[merge:%\w+]] ; CHECK: [[merge]] = OpLabel ; CHECK: OpReturnValue ; CHECK-NEXT: OpFunctionEnd %f_ = OpFunction %float None %9 %33 = OpLabel OpBranch %34 %34 = OpLabel OpLoopMerge %35 %36 None OpBranch %37 %37 = OpLabel OpReturnValue %float_1 %36 = OpLabel OpBranch %34 %35 = OpLabel OpUnreachable OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(predefs + caller + callee, true); } TEST_F(MergeReturnPassTest, MergeToMergeBranch) { const std::string text = R"( ; CHECK: [[new_undef:%\w+]] = OpUndef %uint ; CHECK: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] ; CHECK-NEXT: [[def_bb1]] = OpLabel ; CHECK: OpLoopMerge [[merge1:%\w+]] ; CHECK: OpLoopMerge [[merge2:%\w+]] ; CHECK: [[merge1]] = OpLabel ; CHECK-NEXT: OpPhi %uint [[new_undef]] [[merge2]] OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %2 "main" OpExecutionMode %2 LocalSize 100 1 1 OpSource ESSL 310 %void = OpTypeVoid %4 = OpTypeFunction %void %uint = OpTypeInt 32 0 %uint_1 = OpConstant %uint 1 %bool = OpTypeBool %false = OpConstantFalse %bool %uint_0 = OpConstant %uint 0 %int = OpTypeInt 32 1 %int_0 = OpConstant %int 0 %int_1 = OpConstant %int 1 %13 = OpUndef %bool %2 = OpFunction %void None %4 %14 = OpLabel OpBranch %15 %15 = OpLabel OpLoopMerge %16 %17 None OpBranch %18 %18 = OpLabel OpLoopMerge %19 %20 None OpBranchConditional %13 %21 %19 %21 = OpLabel OpReturn %20 = OpLabel OpBranch %18 %19 = OpLabel %22 = OpUndef %uint OpBranch %23 %23 = OpLabel OpBranch %16 %17 = OpLabel OpBranch %15 %16 = OpLabel %24 = OpCopyObject %uint %22 OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(text, true); } TEST_F(MergeReturnPassTest, PhiInSecondMerge) { // Add and use a phi in the second merge block from the return. const std::string text = R"( ; CHECK: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] ; CHECK-NEXT: [[def_bb1]] = OpLabel ; CHECK: OpLoopMerge [[merge_bb:%\w+]] [[continue_bb:%\w+]] ; CHECK: [[continue_bb]] = OpLabel ; CHECK-NEXT: [[val:%\w+]] = OpUndef %float ; CHECK: [[merge_bb]] = OpLabel ; CHECK-NEXT: [[phi:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} [[val]] [[continue_bb]] ; CHECK-NOT: OpLabel ; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} [[old_merge:%\w+]] ; CHECK: [[old_merge]] = OpLabel ; CHECK-NEXT: OpConvertFToS %int [[phi]] OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 %void = OpTypeVoid %4 = OpTypeFunction %void %int = OpTypeInt 32 1 %float = OpTypeFloat 32 %bool = OpTypeBool %8 = OpUndef %bool %2 = OpFunction %void None %4 %9 = OpLabel OpBranch %10 %10 = OpLabel OpLoopMerge %11 %12 None OpBranch %13 %13 = OpLabel OpLoopMerge %18 %14 None OpBranchConditional %8 %15 %18 %15 = OpLabel OpReturn %14 = OpLabel OpBranch %13 %18 = OpLabel OpBranch %12 %12 = OpLabel %16 = OpUndef %float OpBranchConditional %8 %10 %11 %11 = OpLabel %17 = OpConvertFToS %int %16 OpReturn OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(text, true); } TEST_F(MergeReturnPassTest, ReturnsInSwitch) { // Cannot branch directly to single case switch merge block from original // switch. Must branch to merge block of original switch and then do // predicated branch to merge block of single case switch. const std::string text = R"( ; CHECK: OpSelectionMerge [[single_case_switch_merge_bb:%\w+]] ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] ; CHECK-NEXT: [[def_bb1]] = OpLabel ; CHECK: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[inner_merge_bb:%\w+]] 0 {{%\w+}} 1 {{%\w+}} ; CHECK: OpBranch [[inner_merge_bb]] ; CHECK: OpBranch [[inner_merge_bb]] ; CHECK-NEXT: [[inner_merge_bb]] = OpLabel ; CHECK: OpBranchConditional {{%\w+}} [[single_case_switch_merge_bb]] {{%\w+}} OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %PSMain "PSMain" %_entryPointOutput_color OpExecutionMode %PSMain OriginUpperLeft OpSource HLSL 500 OpMemberDecorate %cb 0 Offset 0 OpMemberDecorate %cb 1 Offset 16 OpMemberDecorate %cb 2 Offset 32 OpMemberDecorate %cb 3 Offset 48 OpDecorate %cb Block OpDecorate %_ DescriptorSet 0 OpDecorate %_ Binding 0 OpDecorate %_entryPointOutput_color Location 0 %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %8 = OpTypeFunction %v4float %int = OpTypeInt 32 1 %cb = OpTypeStruct %v4float %v4float %v4float %int %_ptr_Uniform_cb = OpTypePointer Uniform %cb %_ = OpVariable %_ptr_Uniform_cb Uniform %int_3 = OpConstant %int 3 %_ptr_Uniform_int = OpTypePointer Uniform %int %int_0 = OpConstant %int 0 %_ptr_Uniform_v4float = OpTypePointer Uniform %v4float %int_1 = OpConstant %int 1 %int_2 = OpConstant %int 2 %float_0 = OpConstant %float 0 %float_1 = OpConstant %float 1 %45 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 %_ptr_Function_v4float = OpTypePointer Function %v4float %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput_color = OpVariable %_ptr_Output_v4float Output %PSMain = OpFunction %void None %3 %5 = OpLabel %50 = OpFunctionCall %v4float %BlendValue_ OpStore %_entryPointOutput_color %50 OpReturn OpFunctionEnd %BlendValue_ = OpFunction %v4float None %8 %10 = OpLabel %21 = OpAccessChain %_ptr_Uniform_int %_ %int_3 %22 = OpLoad %int %21 OpSelectionMerge %25 None OpSwitch %22 %25 0 %23 1 %24 %23 = OpLabel %28 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %29 = OpLoad %v4float %28 OpReturnValue %29 %24 = OpLabel %31 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %32 = OpLoad %v4float %31 %34 = OpAccessChain %_ptr_Uniform_v4float %_ %int_1 %35 = OpLoad %v4float %34 %37 = OpAccessChain %_ptr_Uniform_v4float %_ %int_2 %38 = OpLoad %v4float %37 %39 = OpFMul %v4float %35 %38 %40 = OpFAdd %v4float %32 %39 OpReturnValue %40 %25 = OpLabel OpReturnValue %45 OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndMatch(text, true); } TEST_F(MergeReturnPassTest, UnreachableMergeAndContinue) { // Make sure that the pass can handle a single block that is both a merge and // a continue. const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 %void = OpTypeVoid %4 = OpTypeFunction %void %bool = OpTypeBool %true = OpConstantTrue %bool %2 = OpFunction %void None %4 %7 = OpLabel OpBranch %8 %8 = OpLabel OpLoopMerge %9 %10 None OpBranch %11 %11 = OpLabel OpSelectionMerge %10 None OpBranchConditional %true %12 %13 %12 = OpLabel OpReturn %13 = OpLabel OpReturn %10 = OpLabel OpBranch %8 %9 = OpLabel OpUnreachable OpFunctionEnd )"; SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); auto result = SinglePassRunAndDisassemble(text, true, true); // Not looking for any particular output. Other tests do that. // Just want to make sure the check for unreachable blocks does not emit an // error. EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); } TEST_F(MergeReturnPassTest, SingleReturnInMiddle) { const std::string before = R"( ; CHECK: OpFunction ; CHECK: OpReturn ; CHECK-NEXT: OpFunctionEnd OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" OpSource GLSL 450 OpName %main "main" OpName %foo_ "foo(" %void = OpTypeVoid %4 = OpTypeFunction %void %bool = OpTypeBool %true = OpConstantTrue %bool %foo_ = OpFunction %void None %4 %7 = OpLabel OpSelectionMerge %8 None OpBranchConditional %true %9 %8 %8 = OpLabel OpReturn %9 = OpLabel OpBranch %8 OpFunctionEnd %main = OpFunction %void None %4 %10 = OpLabel %11 = OpFunctionCall %void %foo_ OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(before, false); } TEST_F(MergeReturnPassTest, PhiWithTooManyEntries) { // Check that the OpPhi node has the correct number of entries. This is // checked by doing validation with the match. const std::string before = R"( ; CHECK: OpLoopMerge [[merge:%\w+]] ; CHECK: [[merge]] = OpLabel ; CHECK-NEXT: {{%\w+}} = OpPhi %int {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft %void = OpTypeVoid %4 = OpTypeFunction %void %int = OpTypeInt 32 1 %6 = OpTypeFunction %int %bool = OpTypeBool %int_1 = OpConstant %int 1 %false = OpConstantFalse %bool %2 = OpFunction %void None %4 %10 = OpLabel %11 = OpFunctionCall %int %12 OpReturn OpFunctionEnd %12 = OpFunction %int None %6 %13 = OpLabel OpBranch %14 %14 = OpLabel %15 = OpPhi %int %int_1 %13 %16 %17 OpLoopMerge %18 %17 None OpBranch %19 %19 = OpLabel %20 = OpUndef %bool OpBranch %21 %21 = OpLabel OpLoopMerge %22 %23 None OpBranch %24 %24 = OpLabel OpSelectionMerge %25 None OpBranchConditional %20 %22 %25 %25 = OpLabel OpReturnValue %int_1 %23 = OpLabel OpBranch %21 %22 = OpLabel OpSelectionMerge %26 None OpBranchConditional %20 %27 %26 %27 = OpLabel OpBranch %28 %28 = OpLabel OpLoopMerge %29 %30 None OpBranch %31 %31 = OpLabel OpReturnValue %int_1 %30 = OpLabel OpBranch %28 %29 = OpLabel OpUnreachable %26 = OpLabel OpBranch %17 %17 = OpLabel %16 = OpPhi %int %15 %26 OpBranchConditional %false %14 %18 %18 = OpLabel OpReturnValue %16 OpFunctionEnd )"; SinglePassRunAndMatch(before, true); } TEST_F(MergeReturnPassTest, PointerUsedAfterLoop) { // Make sure that a Phi instruction is not generated for an id whose type is a // pointer. It needs to be regenerated. const std::string before = R"( ; CHECK: OpFunction %void ; CHECK: OpFunction %void ; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_v2uint ; CHECK: OpLoopMerge [[merge_bb:%\w+]] ; CHECK: [[merge_bb]] = OpLabel ; CHECK-NEXT: [[ac:%\w+]] = OpAccessChain %_ptr_Function_uint [[param]] %uint_1 ; CHECK: OpStore [[ac]] %uint_1 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 %void = OpTypeVoid %4 = OpTypeFunction %void %uint = OpTypeInt 32 0 %v2uint = OpTypeVector %uint 2 %_ptr_Function_v2uint = OpTypePointer Function %v2uint %8 = OpTypeFunction %void %_ptr_Function_v2uint %uint_1 = OpConstant %uint 1 %bool = OpTypeBool %_ptr_Function_uint = OpTypePointer Function %uint %false = OpConstantFalse %bool %2 = OpFunction %void None %4 %13 = OpLabel %14 = OpVariable %_ptr_Function_v2uint Function %15 = OpFunctionCall %void %16 %14 OpReturn OpFunctionEnd %16 = OpFunction %void None %8 %17 = OpFunctionParameter %_ptr_Function_v2uint %18 = OpLabel OpBranch %19 %19 = OpLabel OpLoopMerge %20 %21 None OpBranch %22 %22 = OpLabel OpSelectionMerge %23 None OpBranchConditional %false %24 %23 %24 = OpLabel OpReturn %23 = OpLabel OpBranch %21 %21 = OpLabel %25 = OpAccessChain %_ptr_Function_uint %17 %uint_1 OpBranchConditional %false %19 %20 %20 = OpLabel OpStore %25 %uint_1 OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(before, true); } TEST_F(MergeReturnPassTest, VariablePointerFunctionScope) { // Make sure that a Phi instruction is not generated for an id whose type is a // function scope pointer, even if the VariablePointers capability is // available. It needs to be regenerated. const std::string before = R"( ; CHECK: OpFunction %void ; CHECK: OpFunction %void ; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_v2uint ; CHECK: OpLoopMerge [[merge_bb:%\w+]] ; CHECK: [[merge_bb]] = OpLabel ; CHECK-NEXT: [[ac:%\w+]] = OpAccessChain %_ptr_Function_uint [[param]] %uint_1 ; CHECK: OpStore [[ac]] %uint_1 OpCapability Shader OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 %void = OpTypeVoid %4 = OpTypeFunction %void %uint = OpTypeInt 32 0 %v2uint = OpTypeVector %uint 2 %_ptr_Function_v2uint = OpTypePointer Function %v2uint %8 = OpTypeFunction %void %_ptr_Function_v2uint %uint_1 = OpConstant %uint 1 %bool = OpTypeBool %_ptr_Function_uint = OpTypePointer Function %uint %false = OpConstantFalse %bool %2 = OpFunction %void None %4 %13 = OpLabel %14 = OpVariable %_ptr_Function_v2uint Function %15 = OpFunctionCall %void %16 %14 OpReturn OpFunctionEnd %16 = OpFunction %void None %8 %17 = OpFunctionParameter %_ptr_Function_v2uint %18 = OpLabel OpBranch %19 %19 = OpLabel OpLoopMerge %20 %21 None OpBranch %22 %22 = OpLabel OpSelectionMerge %23 None OpBranchConditional %false %24 %23 %24 = OpLabel OpReturn %23 = OpLabel OpBranch %21 %21 = OpLabel %25 = OpAccessChain %_ptr_Function_uint %17 %uint_1 OpBranchConditional %false %19 %20 %20 = OpLabel OpStore %25 %uint_1 OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(before, true); } TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) { // Make sure that a Phi instruction is not generated for an id whose type is a // pointer. It needs to be regenerated. const std::string before = R"( ; CHECK: OpFunction %void ; CHECK: OpFunction %void ; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_ ; CHECK: OpLoopMerge [[merge_bb:%\w+]] ; CHECK: [[merge_bb]] = OpLabel ; CHECK-NEXT: [[ac1:%\w+]] = OpAccessChain %_ptr_Function_v2uint [[param]] %uint_1 ; CHECK-NEXT: [[ac2:%\w+]] = OpAccessChain %_ptr_Function_uint [[ac1]] %uint_1 ; CHECK: OpStore [[ac2]] %uint_1 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 %void = OpTypeVoid %4 = OpTypeFunction %void %uint = OpTypeInt 32 0 %uint_1 = OpConstant %uint 1 %uint_2 = OpConstant %uint 2 %v2uint = OpTypeVector %uint 2 %_arr_v2uint_uint_2 = OpTypeArray %v2uint %uint_2 %_ptr_Function_v2uint = OpTypePointer Function %v2uint %_ptr_Function__arr_v2uint_uint_2 = OpTypePointer Function %_arr_v2uint_uint_2 %_ptr_Function_uint = OpTypePointer Function %uint %13 = OpTypeFunction %void %_ptr_Function__arr_v2uint_uint_2 %bool = OpTypeBool %false = OpConstantFalse %bool %2 = OpFunction %void None %4 %16 = OpLabel %17 = OpVariable %_ptr_Function__arr_v2uint_uint_2 Function %18 = OpFunctionCall %void %19 %17 OpReturn OpFunctionEnd %19 = OpFunction %void None %13 %20 = OpFunctionParameter %_ptr_Function__arr_v2uint_uint_2 %21 = OpLabel OpBranch %22 %22 = OpLabel OpLoopMerge %23 %24 None OpBranch %25 %25 = OpLabel OpSelectionMerge %26 None OpBranchConditional %false %27 %26 %27 = OpLabel OpReturn %26 = OpLabel OpBranch %24 %24 = OpLabel %28 = OpAccessChain %_ptr_Function_v2uint %20 %uint_1 %29 = OpAccessChain %_ptr_Function_uint %28 %uint_1 OpBranchConditional %false %22 %23 %23 = OpLabel OpStore %29 %uint_1 OpReturn OpFunctionEnd )"; SinglePassRunAndMatch(before, true); } } // namespace } // namespace opt } // namespace spvtools