// Copyright 2025 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "src/tint/lang/spirv/reader/parser/helper_test.h"

namespace tint::spirv::reader {
namespace {

TEST_F(SpirvParserTest, Branch_Forward) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
          %1 = OpCopyObject %i32 %one
               OpBranch %bb_2
       %bb_2 = OpLabel
          %2 = OpCopyObject %i32 %two
               OpBranch %bb_3
       %bb_3 = OpLabel
          %3 = OpCopyObject %i32 %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = let 1i
    %3:i32 = let 2i
    %4:i32 = let 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %10 = OpLabel
          %1 = OpCopyObject %i32 %one
               OpSelectionMerge %bb_merge None
               OpBranchConditional %true %bb_true %bb_false
    %bb_true = OpLabel
          %2 = OpCopyObject %i32 %two
               OpReturn
   %bb_false = OpLabel
          %3 = OpCopyObject %i32 %three
               OpBranch %bb_merge
   %bb_merge = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = let 1i
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %3:i32 = let 2i
        ret
      }
      $B3: {  # false
        %4:i32 = let 3i
        exit_if  # if_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Empty) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpBranchConditional %true %99 %99
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:bool = or true, true
    if %2 [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        exit_if  # if_1
      }
      $B3: {  # false
        unreachable
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_TrueToMerge) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
          %1 = OpCopyObject %i32 %one
               OpSelectionMerge %bb_merge None
               OpBranchConditional %true %bb_merge %bb_false
   %bb_false = OpLabel
          %3 = OpCopyObject %i32 %three
               OpBranch %bb_merge
   %bb_merge = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = let 1i
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        exit_if  # if_1
      }
      $B3: {  # false
        %3:i32 = let 3i
        exit_if  # if_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_FalseToMerge) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
          %1 = OpCopyObject %i32 %one
               OpSelectionMerge %bb_merge None
               OpBranchConditional %true %bb_true %bb_merge
    %bb_true = OpLabel
          %2 = OpCopyObject %i32 %two
               OpReturn
   %bb_merge = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = let 1i
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %3:i32 = let 2i
        ret
      }
      $B3: {  # false
        exit_if  # if_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_TrueMatchesFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
          %1 = OpCopyObject %i32 %one
               OpBranchConditional %true %bb_true %bb_true
    %bb_true = OpLabel
          %2 = OpCopyObject %i32 %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = let 1i
    %3:bool = or true, true
    if %3 [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %4:i32 = let 2i
        ret
      }
      $B3: {  # false
        unreachable
      }
    }
    unreachable
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Nested_FalseExit) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
          %1 = OpCopyObject %i32 %one
               OpSelectionMerge %bb_merge None
               OpBranchConditional %true %bb_true %bb_merge
    %bb_true = OpLabel
               OpBranchConditional %false %99 %bb_merge
         %99 = OpLabel
          %2 = OpCopyObject %i32 %two
               OpBranch %bb_merge
   %bb_merge = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = let 1i
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        if false [t: $B4, f: $B5] {  # if_2
          $B4: {  # true
            %3:i32 = let 2i
            exit_if  # if_2
          }
          $B5: {  # false
            exit_if  # if_2
          }
        }
        exit_if  # if_1
      }
      $B3: {  # false
        exit_if  # if_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Nested_TrueExit) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
          %1 = OpCopyObject %i32 %one
               OpSelectionMerge %bb_merge None
               OpBranchConditional %true %bb_true %bb_merge
    %bb_true = OpLabel
               OpBranchConditional %false %bb_merge %99
         %99 = OpLabel
          %2 = OpCopyObject %i32 %two
               OpBranch %bb_merge
   %bb_merge = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = let 1i
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        if false [t: $B4, f: $B5] {  # if_2
          $B4: {  # true
            exit_if  # if_2
          }
          $B5: {  # false
            %3:i32 = let 2i
            exit_if  # if_2
          }
        }
        exit_if  # if_1
      }
      $B3: {  # false
        exit_if  # if_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_TrueBranchesToFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %20 = OpLabel
               OpSelectionMerge %99 None
               OpBranchConditional %false %40 %30
         %30 = OpLabel
         %50 = OpIAdd %i32 %one %one
               OpReturn
         %40 = OpLabel
         %51 = OpIAdd %i32 %two %two
               OpBranch %30 ; backtrack, but does dominate %30
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    if false [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %2:i32 = spirv.add<i32> 2i, 2i
        exit_if  # if_1
      }
      $B3: {  # false
        exit_if  # if_1
      }
    }
    if true [t: $B4, f: $B5] {  # if_2
      $B4: {  # true
        %3:i32 = spirv.add<i32> 1i, 1i
        ret
      }
      $B5: {  # false
        unreachable
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Hoisting) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
          %1 = OpCopyObject %i32 %one
               OpSelectionMerge %bb_merge None
               OpBranchConditional %true %bb_true %bb_false
    %bb_true = OpLabel
          %2 = OpCopyObject %i32 %two
               OpBranch %bb_merge
   %bb_false = OpLabel
               OpReturn
   %bb_merge = OpLabel
          %3 = OpIAdd %i32 %2 %2
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = let 1i
    %3:i32 = if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %4:i32 = let 2i
        exit_if %4  # if_1
      }
      $B3: {  # false
        ret
      }
    }
    %5:i32 = spirv.add<i32> %3, %3
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_HoistingMultiExit) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
               OpBranch %10
         %10 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %20
         %20 = OpLabel
        %foo = OpIAdd %i32 %one %two
               OpSelectionMerge %50 None
               OpBranchConditional %true %30 %40
         %30 = OpLabel
               OpBranch %99
         %40 = OpLabel
               OpBranch %99
         %50 = OpLabel
               OpUnreachable
         %80 = OpLabel
               OpBranch %10
         %99 = OpLabel
       %foo2 = OpCopyObject %i32 %foo
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 1i, 2i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop %3  # loop_1
          }
          $B5: {  # false
            exit_loop %3  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %4:i32 = let %2
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_HoistingMultiExit_FromMiddle) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
               OpBranch %10
         %10 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %20
         %20 = OpLabel
               OpSelectionMerge %50 None
               OpBranchConditional %true %30 %30
         %30 = OpLabel
        %foo = OpIAdd %i32 %one %two
               OpSelectionMerge %60 None
               OpBranchConditional %true %65 %70
         %65 = OpLabel
               OpBranch %99
         %70 = OpLabel
               OpBranch %99
         %60 = OpLabel
               OpUnreachable
         %50 = OpLabel
               OpUnreachable
         %80 = OpLabel
               OpBranch %10
         %99 = OpLabel
       %foo2 = OpCopyObject %i32 %foo
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:bool = or true, true
        if %3 [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %4:i32 = spirv.add<i32> 1i, 2i
            if true [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                exit_loop %4  # loop_1
              }
              $B7: {  # false
                exit_loop %4  # loop_1
              }
            }
            unreachable
          }
          $B5: {  # false
            unreachable
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %5:i32 = let %2
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_HoistingIntoNested) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %two = OpConstant %i32 2
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
        %502 = OpLabel
               OpSelectionMerge %50 None
               OpBranchConditional %true %20 %30
         %20 = OpLabel
        %200 = OpCopyObject %i32 %two
               OpBranch %50
         %30 = OpLabel
               OpReturn
         %50 = OpLabel
               OpSelectionMerge %60 None
               OpBranchConditional %true %70 %80
         %70 = OpLabel
        %201 = OpIAdd %i32 %200 %200
               OpBranch %60
         %80 = OpLabel
               OpReturn
         %60 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %3:i32 = let 2i
        exit_if %3  # if_1
      }
      $B3: {  # false
        ret
      }
    }
    if true [t: $B4, f: $B5] {  # if_2
      $B4: {  # true
        %4:i32 = spirv.add<i32> %2, %2
        exit_if  # if_2
      }
      $B5: {  # false
        ret
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_HoistingIntoParentNested) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %two = OpConstant %i32 2
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
          %1 = OpLabel
               OpSelectionMerge %90 None
               OpBranchConditional %true %10 %15
         %15 = OpLabel
               OpReturn
         %10 = OpLabel
               OpSelectionMerge %50 None
               OpBranchConditional %true %20 %30
         %20 = OpLabel
        %200 = OpCopyObject %i32 %two
               OpBranch %50
         %30 = OpLabel
               OpReturn
         %50 = OpLabel
               OpSelectionMerge %60 None
               OpBranchConditional %true %70 %80
         %70 = OpLabel
        %201 = OpIAdd %i32 %200 %200
               OpBranch %60
         %80 = OpLabel
               OpReturn
         %60 = OpLabel
               OpBranch %90
         %90 = OpLabel
        %202 = OpIAdd %i32 %200 %200
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %3:i32 = if true [t: $B4, f: $B5] {  # if_2
          $B4: {  # true
            %4:i32 = let 2i
            exit_if %4  # if_2
          }
          $B5: {  # false
            ret
          }
        }
        if true [t: $B6, f: $B7] {  # if_3
          $B6: {  # true
            %5:i32 = spirv.add<i32> %3, %3
            exit_if  # if_3
          }
          $B7: {  # false
            ret
          }
        }
        exit_if %3  # if_1
      }
      $B3: {  # false
        ret
      }
    }
    %6:i32 = spirv.add<i32> %2, %2
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_DuplicateTrue_UsedValue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
          %1 = OpCopyObject %i32 %one
               OpBranchConditional %true %bb_true %bb_true
    %bb_true = OpLabel
          %2 = OpCopyObject %i32 %two
         %22 = OpCopyObject %i32 %2
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = let 1i
    %3:bool = or true, true
    if %3 [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %4:i32 = let 2i
        %5:i32 = let %4
        ret
      }
      $B3: {  # false
        unreachable
      }
    }
    unreachable
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_DuplicateTrue_Premerge) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %12 = OpLabel
               OpSelectionMerge %13 None
               OpBranchConditional %true %14 %15
         %14 = OpLabel
               OpBranch %16
         %15 = OpLabel
               OpBranch %16
         %16 = OpLabel
          %1 = OpCopyObject %i32 %one
          %2 = OpCopyObject %i32 %1
               OpBranch %13
         %13 = OpLabel
          %3 = OpCopyObject %i32 %1
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        exit_if  # if_1
      }
      $B3: {  # false
        exit_if  # if_1
      }
    }
    %2:i32 = if true [t: $B4, f: $B5] {  # if_2
      $B4: {  # true
        %3:i32 = let 1i
        %4:i32 = let %3
        exit_if %3  # if_2
      }
      $B5: {  # false
        unreachable
      }
    }
    %5:i32 = let %2
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %98 20 %20
         %20 = OpLabel
         %21 = OpIAdd %i32 %one %two
               OpBranch %99
         %98 = OpLabel
         %22 = OpIAdd %i32 %two %two
               OpBranch %99
         %99 = OpLabel
         %23 = OpIAdd %i32 %three %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 2i [c: (default, $B2), c: (20i, $B3)] {  # switch_1
      $B2: {  # case
        %2:i32 = spirv.add<i32> 2i, 2i
        exit_switch  # switch_1
      }
      $B3: {  # case
        %3:i32 = spirv.add<i32> 1i, 2i
        exit_switch  # switch_1
      }
    }
    %4:i32 = spirv.add<i32> 3i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, SwitchManyCases) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %four = OpConstant %i32 4
       %five = OpConstant %i32 5
        %six = OpConstant %i32 6
      %seven = OpConstant %i32 7
      %eight = OpConstant %i32 8
       %nine = OpConstant %i32 9
        %ten = OpConstant %i32 10
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %98 20 %20 30 %30 40 %40 50 %50 60 %60 70 %70 80 %80 90 %90 100 %100
         %20 = OpLabel
         %21 = OpIAdd %i32 %one %two
               OpBranch %99
         %30 = OpLabel
         %31 = OpIAdd %i32 %one %three
               OpBranch %99
         %40 = OpLabel
         %41 = OpIAdd %i32 %one %four
               OpBranch %99
         %50 = OpLabel
         %51 = OpIAdd %i32 %one %five
               OpBranch %99
         %60 = OpLabel
         %61 = OpIAdd %i32 %one %six
               OpBranch %99
         %70 = OpLabel
         %71 = OpIAdd %i32 %one %seven
               OpBranch %99
         %80 = OpLabel
         %81 = OpIAdd %i32 %one %eight
               OpBranch %99
         %90 = OpLabel
         %91 = OpIAdd %i32 %one %nine
               OpBranch %99
        %100 = OpLabel
        %101 = OpIAdd %i32 %one %ten
               OpBranch %99
         %98 = OpLabel
         %22 = OpIAdd %i32 %two %two
               OpBranch %99
         %99 = OpLabel
         %23 = OpIAdd %i32 %ten %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 2i [c: (default, $B2), c: (20i, $B3), c: (30i, $B4), c: (40i, $B5), c: (50i, $B6), c: (60i, $B7), c: (70i, $B8), c: (80i, $B9), c: (90i, $B10), c: (100i, $B11)] {  # switch_1
      $B2: {  # case
        %2:i32 = spirv.add<i32> 2i, 2i
        exit_switch  # switch_1
      }
      $B3: {  # case
        %3:i32 = spirv.add<i32> 1i, 2i
        exit_switch  # switch_1
      }
      $B4: {  # case
        %4:i32 = spirv.add<i32> 1i, 3i
        exit_switch  # switch_1
      }
      $B5: {  # case
        %5:i32 = spirv.add<i32> 1i, 4i
        exit_switch  # switch_1
      }
      $B6: {  # case
        %6:i32 = spirv.add<i32> 1i, 5i
        exit_switch  # switch_1
      }
      $B7: {  # case
        %7:i32 = spirv.add<i32> 1i, 6i
        exit_switch  # switch_1
      }
      $B8: {  # case
        %8:i32 = spirv.add<i32> 1i, 7i
        exit_switch  # switch_1
      }
      $B9: {  # case
        %9:i32 = spirv.add<i32> 1i, 8i
        exit_switch  # switch_1
      }
      $B10: {  # case
        %10:i32 = spirv.add<i32> 1i, 9i
        exit_switch  # switch_1
      }
      $B11: {  # case
        %11:i32 = spirv.add<i32> 1i, 10i
        exit_switch  # switch_1
      }
    }
    %12:i32 = spirv.add<i32> 10i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_DefaultIsMerge) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %two = OpConstant %i32 2
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %99 20 %20
         %20 = OpLabel
         %21 = OpIAdd %i32 %two %two
               OpBranch %99
         %99 = OpLabel
         %98 = OpIAdd %i32 %two %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 2i [c: (default, $B2), c: (20i, $B3)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
      $B3: {  # case
        %2:i32 = spirv.add<i32> 2i, 2i
        exit_switch  # switch_1
      }
    }
    %3:i32 = spirv.add<i32> 2i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_DefaultIsCase) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %two = OpConstant %i32 2
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %20 20 %20
         %20 = OpLabel
         %21 = OpIAdd %i32 %two %two
               OpBranch %99
         %99 = OpLabel
         %98 = OpIAdd %i32 %two %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 2i [c: (20i default, $B2)] {  # switch_1
      $B2: {  # case
        %2:i32 = spirv.add<i32> 2i, 2i
        exit_switch  # switch_1
      }
    }
    %3:i32 = spirv.add<i32> 2i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_MuliselectorCase) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %two = OpConstant %i32 2
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %50 20 %20 30 %20
         %20 = OpLabel
         %21 = OpIAdd %i32 %two %two
               OpBranch %99
         %50 = OpLabel
               OpReturn
         %99 = OpLabel
         %98 = OpIAdd %i32 %two %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 2i [c: (default, $B2), c: (20i 30i, $B3)] {  # switch_1
      $B2: {  # case
        ret
      }
      $B3: {  # case
        %2:i32 = spirv.add<i32> 2i, 2i
        exit_switch  # switch_1
      }
    }
    %3:i32 = spirv.add<i32> 2i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_OnlyDefault) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %20
         %20 = OpLabel
         %51 = OpIAdd %i32 %one %two
               OpBranch %99
         %99 = OpLabel
         %52 = OpIAdd %i32 %two %two
               OpReturn
               OpFunctionEnd
  )",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 2i [c: (default, $B2)] {  # switch_1
      $B2: {  # case
        %2:i32 = spirv.add<i32> 1i, 2i
        exit_switch  # switch_1
      }
    }
    %3:i32 = spirv.add<i32> 2i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_IfBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
       %bool = OpTypeBool
        %one = OpConstant %u32 1
        %two = OpConstant %u32 2
      %three = OpConstant %u32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %50 20 %20 50 %50
         %99 = OpLabel
               OpReturn
         %20 = OpLabel
               OpSelectionMerge %49 None
               OpBranchConditional %true %99 %40 ; break-if
         %40 = OpLabel
               OpBranch %49
         %49 = OpLabel
        %101 = OpIAdd %u32 %two %three
               OpBranch %99
         %50 = OpLabel
               OpSelectionMerge %79 None
               OpBranchConditional %true %60 %99 ; break-unless
         %60 = OpLabel
               OpBranch %79
         %79 = OpLabel ; dominated by 60, so must follow 60
        %100 = OpIAdd %u32 %one %two
               OpBranch %99
               OpFunctionEnd
  )",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 2u [c: (50u default, $B2), c: (20u, $B3)] {  # switch_1
      $B2: {  # case
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_if  # if_1
          }
          $B5: {  # false
            exit_switch  # switch_1
          }
        }
        %2:u32 = spirv.add<u32> 1u, 2u
        exit_switch  # switch_1
      }
      $B3: {  # case
        if true [t: $B6, f: $B7] {  # if_2
          $B6: {  # true
            exit_switch  # switch_1
          }
          $B7: {  # false
            exit_if  # if_2
          }
        }
        %3:u32 = spirv.add<u32> 2u, 3u
        exit_switch  # switch_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_Nest_If_In_Case) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
       %bool = OpTypeBool
        %one = OpConstant %u32 1
        %two = OpConstant %u32 2
      %three = OpConstant %u32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %50 20 %20 50 %50
         %99 = OpLabel
               OpReturn
         %20 = OpLabel
               OpSelectionMerge %49 None
               OpBranchConditional %true %30 %40
         %49 = OpLabel
               OpBranch %99
         %30 = OpLabel
               OpBranch %49
         %40 = OpLabel
               OpBranch %49
         %50 = OpLabel
               OpSelectionMerge %79 None
               OpBranchConditional %false %60 %70
         %79 = OpLabel
               OpBranch %99
         %60 = OpLabel
               OpBranch %79
         %70 = OpLabel
               OpBranch %79
               OpFunctionEnd
  )",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 2u [c: (50u default, $B2), c: (20u, $B3)] {  # switch_1
      $B2: {  # case
        if false [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_if  # if_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        exit_switch  # switch_1
      }
      $B3: {  # case
        if true [t: $B6, f: $B7] {  # if_2
          $B6: {  # true
            exit_if  # if_2
          }
          $B7: {  # false
            exit_if  # if_2
          }
        }
        exit_switch  # switch_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_HoistFromDefault) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
       %bool = OpTypeBool
        %one = OpConstant %u32 1
        %two = OpConstant %u32 2
      %three = OpConstant %u32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %20
         %20 = OpLabel
        %100 = OpCopyObject %u32 %one
               OpBranch %99
         %99 = OpLabel
        %102 = OpIAdd %u32 %100 %100
               OpReturn
               OpFunctionEnd
  )",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:u32 = switch 2u [c: (default, $B2)] {  # switch_1
      $B2: {  # case
        %3:u32 = let 1u
        exit_switch %3  # switch_1
      }
    }
    %4:u32 = spirv.add<u32> %2, %2
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_HoistFromCase) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
       %bool = OpTypeBool
        %one = OpConstant %u32 1
        %two = OpConstant %u32 2
      %three = OpConstant %u32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %50 20 %20
         %20 = OpLabel
        %100 = OpCopyObject %u32 %one
               OpBranch %99
         %50 = OpLabel
               OpReturn
         %99 = OpLabel
        %102 = OpIAdd %u32 %100 %100
               OpReturn
               OpFunctionEnd
  )",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:u32 = switch 2u [c: (default, $B2), c: (20u, $B3)] {  # switch_1
      $B2: {  # case
        ret
      }
      $B3: {  # case
        %3:u32 = let 1u
        exit_switch %3  # switch_1
      }
    }
    %4:u32 = spirv.add<u32> %2, %2
    ret
  }
}
)");
}

TEST_F(SpirvParserDeathTest, Switch_Fallthrough) {
    auto src = R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %two %80 20 %20 30 %30
         %20 = OpLabel
         %50 = OpIAdd %i32 %one %two
               OpBranch %30 ; fall through
         %30 = OpLabel
         %51 = OpIAdd %i32 %two %two
               OpBranch %99
         %80 = OpLabel
         %52 = OpIAdd %i32 %three %two
               OpBranch %99
         %99 = OpLabel
         %53 = OpIAdd %i32 %one %three
               OpReturn
               OpFunctionEnd
)";
    EXPECT_DEATH_IF_SUPPORTED({ auto _ = Run(src); }, "internal compiler error");
}

TEST_F(SpirvParserTest, Switch_IfBreakInCase) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %one %50 20 %20 50 %50
         %99 = OpLabel
               OpReturn
         %20 = OpLabel
               OpSelectionMerge %49 None
               OpBranchConditional %true %99 %40 ; break-if
         %40 = OpLabel
               OpBranch %49
         %49 = OpLabel
               OpBranch %99
         %50 = OpLabel
               OpSelectionMerge %79 None
               OpBranchConditional %true %60 %99 ; break-unless
         %60 = OpLabel
               OpBranch %79
         %79 = OpLabel ; dominated by 60, so must follow 60
               OpBranch %99
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 1i [c: (50i default, $B2), c: (20i, $B3)] {  # switch_1
      $B2: {  # case
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_if  # if_1
          }
          $B5: {  # false
            exit_switch  # switch_1
          }
        }
        exit_switch  # switch_1
      }
      $B3: {  # case
        if true [t: $B6, f: $B7] {  # if_2
          $B6: {  # true
            exit_switch  # switch_1
          }
          $B7: {  # false
            exit_if  # if_2
          }
        }
        exit_switch  # switch_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_CaseCanBeMerge) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %one %99 20 %99
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 1i [c: (20i default, $B2)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
          %1 = OpIAdd %i32 %one %two
               OpLoopMerge %99 %20 None
               OpBranchConditional %true %20 %99
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 2i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Infinite_Branch) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %20 None
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        continue  # -> $B3
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Infinite_BranchConditional) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %20 None
               OpBranchConditional %true %20 %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:bool = or true, true
        if %2 [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            unreachable
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BranchConditional) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %40 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranch %40
         %40 = OpLabel
               OpBranch %20 ; back edge
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Branch) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %40 None
               OpBranch %30
         %30 = OpLabel
               OpBranchConditional %true %40 %99
         %40 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BranchMergeOrContinue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %30 = OpIAdd %i32 %one %two
               OpBranch %20
         %20 = OpLabel
         %31 = OpIAdd %i32 %two %three
               OpLoopMerge %99 %20 None
               OpBranchConditional %true %99 %20
         %99 = OpLabel
         %32 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 2i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 3i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            continue  # -> $B3
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %4:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_HeaderHasBreakIf) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99 ; like While
         %30 = OpLabel ; trivial body
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_HeaderHasBreakUnless) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %99 %30 ; has break-unless
         %30 = OpLabel ; trivial body
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            continue  # -> $B3
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyHasBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranch %99 ; break
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyHasBreakIf) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranchConditional %false %99 %40 ; break-if
         %40 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            if false [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                exit_loop  # loop_1
              }
              $B7: {  # false
                continue  # -> $B3
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyHasBreakUnless) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranchConditional %false %40 %99 ; break-unless
         %40 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            if false [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                exit_loop  # loop_1
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyIf) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpSelectionMerge %49 None
               OpBranchConditional %false %40 %45 ; nested if
         %40 = OpLabel
               OpBranch %49
         %45 = OpLabel
               OpBranch %49
         %49 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            if false [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                exit_if  # if_2
              }
              $B7: {  # false
                exit_if  # if_2
              }
            }
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyIfBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpSelectionMerge %49 None
               OpBranchConditional %false %40 %49 ; nested if
         %40 = OpLabel
               OpBranch %99   ; break from nested if
         %49 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            if false [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                exit_loop  # loop_1
              }
              $B7: {  # false
                exit_if  # if_2
              }
            }
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyHasContinueIf) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranchConditional %false %50 %40 ; continue-if
         %40 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            if false [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                continue  # -> $B3
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyHasContinueUnless) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranchConditional %false %40 %50 ; continue-unless
         %40 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            if false [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                continue  # -> $B3
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Body_If_Continue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpSelectionMerge %49 None
               OpBranchConditional %false %40 %49 ; nested if
         %40 = OpLabel
               OpBranch %50   ; continue from nested if
         %49 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            if false [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                exit_if  # if_2
              }
            }
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_ContinueUseBodyValue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
         %40 = OpIAdd %i32 %one %two
               OpBranch %50
         %50 = OpLabel
         %41 = OpIAdd %i32 %40 %40
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %2:i32 = spirv.add<i32> 1i, 2i
            continue %2  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3 (%3:i32): {  # continuing
        %4:i32 = spirv.add<i32> %3, %3
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Body_Switch) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpSelectionMerge %49 None
               OpSwitch %one %49 40 %40 45 %45 ; fully nested switch
         %40 = OpLabel
               OpBranch %49
         %45 = OpLabel
               OpBranch %49
         %49 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            switch 1i [c: (default, $B6), c: (40i, $B7), c: (45i, $B8)] {  # switch_1
              $B6: {  # case
                exit_switch  # switch_1
              }
              $B7: {  # case
                exit_switch  # switch_1
              }
              $B8: {  # case
                exit_switch  # switch_1
              }
            }
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Body_Switch_CaseContinues) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpSelectionMerge %49 None
               OpSwitch %one %49 40 %40 45 %45
         %40 = OpLabel
               OpBranch %50   ; continue bypasses switch merge
         %45 = OpLabel
               OpBranch %49
         %49 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            switch 1i [c: (default, $B6), c: (40i, $B7), c: (45i, $B8)] {  # switch_1
              $B6: {  # case
                exit_switch  # switch_1
              }
              $B7: {  # case
                continue  # -> $B3
              }
              $B8: {  # case
                exit_switch  # switch_1
              }
            }
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Continue_Sequence) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %60
         %60 = OpLabel
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Continue_ContainsIf) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpSelectionMerge %89 None
               OpBranchConditional %true %60 %70
         %89 = OpLabel
               OpBranch %20 ; backedge
         %60 = OpLabel
               OpBranch %89
         %70 = OpLabel
               OpBranch %89
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        if true [t: $B6, f: $B7] {  # if_2
          $B6: {  # true
            exit_if  # if_2
          }
          $B7: {  # false
            exit_if  # if_2
          }
        }
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Continue_HasBreakIf) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranchConditional %false %99 %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        break_if false  # -> [t: exit_loop loop_1, f: $B2]
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Continue_HasBreakUnless) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranchConditional %false %20 %99
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %2:bool = not false
        break_if %2  # -> [t: exit_loop loop_1, f: $B2]
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Loop) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpLoopMerge %49 %40 None
               OpBranchConditional %false %35 %49
         %35 = OpLabel
               OpBranch %37
         %37 = OpLabel
               OpBranch %40
         %40 = OpLabel ; inner loop's continue
               OpBranch %30 ; backedge
         %49 = OpLabel ; inner loop's merge
               OpBranch %50
         %50 = OpLabel ; outer loop's continue
               OpBranch %20 ; outer loop's backege
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            loop [b: $B6, c: $B7] {  # loop_2
              $B6: {  # body
                if false [t: $B8, f: $B9] {  # if_2
                  $B8: {  # true
                    continue  # -> $B7
                  }
                  $B9: {  # false
                    exit_loop  # loop_2
                  }
                }
                unreachable
              }
              $B7: {  # continuing
                next_iteration  # -> $B6
              }
            }
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Loop_InnerBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpLoopMerge %49 %40 None
               OpBranchConditional %false %35 %49
         %35 = OpLabel
               OpBranchConditional %true %49 %37 ; break to inner merge
         %37 = OpLabel
               OpBranch %40
         %40 = OpLabel ; inner loop's continue
               OpBranch %30 ; backedge
         %49 = OpLabel ; inner loop's merge
               OpBranch %50
         %50 = OpLabel ; outer loop's continue
               OpBranch %20 ; outer loop's backege
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            loop [b: $B6, c: $B7] {  # loop_2
              $B6: {  # body
                if false [t: $B8, f: $B9] {  # if_2
                  $B8: {  # true
                    if true [t: $B10, f: $B11] {  # if_3
                      $B10: {  # true
                        exit_loop  # loop_2
                      }
                      $B11: {  # false
                        continue  # -> $B7
                      }
                    }
                    exit_if  # if_2
                  }
                  $B9: {  # false
                    exit_loop  # loop_2
                  }
                }
                unreachable
              }
              $B7: {  # continuing
                next_iteration  # -> $B6
              }
            }
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Loop_InnerContinue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpLoopMerge %49 %40 None
               OpBranchConditional %false %35 %49
         %35 = OpLabel
               OpBranchConditional %true %37 %49 ; continue to inner continue target
         %37 = OpLabel
               OpBranch %40
         %40 = OpLabel ; inner loop's continue
               OpBranch %30 ; backedge
         %49 = OpLabel ; inner loop's merge
               OpBranch %50
         %50 = OpLabel ; outer loop's continue
               OpBranch %20 ; outer loop's backege
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            loop [b: $B6, c: $B7] {  # loop_2
              $B6: {  # body
                if false [t: $B8, f: $B9] {  # if_2
                  $B8: {  # true
                    if true [t: $B10, f: $B11] {  # if_3
                      $B10: {  # true
                        continue  # -> $B7
                      }
                      $B11: {  # false
                        exit_loop  # loop_2
                      }
                    }
                    exit_if  # if_2
                  }
                  $B9: {  # false
                    exit_loop  # loop_2
                  }
                }
                unreachable
              }
              $B7: {  # continuing
                next_iteration  # -> $B6
              }
            }
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Loop_InnerContinueBreaks) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpLoopMerge %49 %40 None
               OpBranchConditional %false %35 %49
         %35 = OpLabel
               OpBranch %37
         %37 = OpLabel
               OpBranch %40
         %40 = OpLabel ; inner loop's continue
               OpBranchConditional %true %30 %49 ; backedge and inner break
         %49 = OpLabel ; inner loop's merge
               OpBranch %50
         %50 = OpLabel ; outer loop's continue
               OpBranch %20 ; outer loop's backege
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            loop [b: $B6, c: $B7] {  # loop_2
              $B6: {  # body
                if false [t: $B8, f: $B9] {  # if_2
                  $B8: {  # true
                    continue  # -> $B7
                  }
                  $B9: {  # false
                    exit_loop  # loop_2
                  }
                }
                unreachable
              }
              $B7: {  # continuing
                %2:bool = not true
                break_if %2  # -> [t: exit_loop loop_2, f: $B6]
              }
            }
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_MergeBlockIsLoop) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %50 None
               OpBranchConditional %true %20 %50
         %20 = OpLabel
               OpBranch %50
         ; %50 is the merge block for the selection starting at 10,
         ; and its own continue target.
         %50 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %50 %99
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        exit_if  # if_1
      }
      $B3: {  # false
        exit_if  # if_1
      }
    }
    loop [b: $B4, c: $B5] {  # loop_1
      $B4: {  # body
        if true [t: $B6, f: $B7] {  # if_2
          $B6: {  # true
            continue  # -> $B5
          }
          $B7: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B5: {  # continuing
        next_iteration  # -> $B4
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, MergeIsAlsoMultiBlockLoopHeader) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %50 None
               OpBranchConditional %true %20 %50
         %20 = OpLabel
               OpBranch %50
         ; %50 is the merge block for the selection starting at 10,
         ; and a loop block header but not its own continue target.
         %50 = OpLabel
               OpLoopMerge %99 %60 None
               OpBranchConditional %true %60 %99
         %60 = OpLabel
               OpBranch %50
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        exit_if  # if_1
      }
      $B3: {  # false
        exit_if  # if_1
      }
    }
    loop [b: $B4, c: $B5] {  # loop_1
      $B4: {  # body
        if true [t: $B6, f: $B7] {  # if_2
          $B6: {  # true
            continue  # -> $B5
          }
          $B7: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B5: {  # continuing
        next_iteration  # -> $B4
      }
    }
    ret
  }
}
)");
}

// Exercises the hard case where we a single OpBranchConditional has both
// IfBreak and Forward edges, within the true-branch clause.
TEST_F(SpirvParserTest, IfBreak_FromThen_ForwardWithinThen) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %two
               OpSelectionMerge %99 None
               OpBranchConditional %true %20 %50
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %three
               OpBranchConditional %false %99 %30 ; kIfBreak with kForward
         %30 = OpLabel ; still in then clause
         %36 = OpIAdd %i32 %one %three
               OpBranch %99
         %50 = OpLabel ; else clause
         %37 = OpIAdd %i32 %three %one
               OpBranch %99
         %99 = OpLabel
         %38 = OpIAdd %i32 %three %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 2i
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %3:i32 = spirv.add<i32> 2i, 3i
        if false [t: $B4, f: $B5] {  # if_2
          $B4: {  # true
            exit_if  # if_2
          }
          $B5: {  # false
            %4:i32 = spirv.add<i32> 1i, 3i
            exit_if  # if_2
          }
        }
        exit_if  # if_1
      }
      $B3: {  # false
        %5:i32 = spirv.add<i32> 3i, 1i
        exit_if  # if_1
      }
    }
    %6:i32 = spirv.add<i32> 3i, 2i
    ret
  }
}
)");
}

// Exercises the hard case where we a single OpBranchConditional has both
// IfBreak and Forward edges, within the false-branch clause.
TEST_F(SpirvParserTest, IfBreak_FromElse_ForwardWithinElse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %35 = OpIAdd %i32 %one %one
               OpSelectionMerge %99 None
               OpBranchConditional %true %20 %50
         %20 = OpLabel
         %36 = OpIAdd %i32 %two %two
               OpBranch %99
         %50 = OpLabel ; else clause
         %37 = OpIAdd %i32 %three %three
               OpBranchConditional %false %99 %80 ; kIfBreak with kForward
         %80 = OpLabel ; still in then clause
         %38 = OpIAdd %i32 %one %two
               OpBranch %99
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %3:i32 = spirv.add<i32> 2i, 2i
        exit_if  # if_1
      }
      $B3: {  # false
        %4:i32 = spirv.add<i32> 3i, 3i
        if false [t: $B4, f: $B5] {  # if_2
          $B4: {  # true
            exit_if  # if_2
          }
          $B5: {  # false
            %5:i32 = spirv.add<i32> 1i, 2i
            exit_if  # if_2
          }
        }
        exit_if  # if_1
      }
    }
    %6:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");
}

// This is a combination of the previous two, but also adding a premerge.
// We have IfBreak and Forward edges from the same OpBranchConditional, and
// this occurs in the true-branch clause, the false-branch clause, and within
// the premerge clause.  Flow guards have to be sprinkled in lots of places.
TEST_F(SpirvParserTest, IfBreak_FromThenAndElseWithForward_Premerge) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %35 = OpIAdd %i32 %one %one
               OpSelectionMerge %99 None
               OpBranchConditional %true %20 %50
         %20 = OpLabel ; then
         %36 = OpIAdd %i32 %two %two
               OpBranchConditional %false %21 %99 ; kForward and kIfBreak
         %21 = OpLabel ; still in then clause
         %37 = OpIAdd %i32 %three %three
               OpBranch %80 ; to premerge
         %50 = OpLabel ; else clause
         %38 = OpIAdd %i32 %one %two
               OpBranchConditional %false %99 %51 ; kIfBreak with kForward
         %51 = OpLabel ; still in else clause
         %39 = OpIAdd %i32 %two %three
               OpBranch %80 ; to premerge
         %80 = OpLabel ; premerge
         %41 = OpIAdd %i32 %three %two
               OpBranchConditional %true %81 %99
         %81 = OpLabel ; premerge
         %42 = OpIAdd %i32 %three %one
               OpBranch %99
         %99 = OpLabel
         %43 = OpIAdd %i32 %two %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    %execute_premerge:ptr<function, bool, read_write> = var true
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %4:i32 = spirv.add<i32> 2i, 2i
        if false [t: $B4, f: $B5] {  # if_2
          $B4: {  # true
            %5:i32 = spirv.add<i32> 3i, 3i
            exit_if  # if_2
          }
          $B5: {  # false
            store %execute_premerge, false
            exit_if  # if_2
          }
        }
        exit_if  # if_1
      }
      $B3: {  # false
        %6:i32 = spirv.add<i32> 1i, 2i
        if false [t: $B6, f: $B7] {  # if_3
          $B6: {  # true
            store %execute_premerge, false
            exit_if  # if_3
          }
          $B7: {  # false
            %7:i32 = spirv.add<i32> 2i, 3i
            exit_if  # if_3
          }
        }
        exit_if  # if_1
      }
    }
    %8:bool = load %execute_premerge
    if %8 [t: $B8, f: $B9] {  # if_4
      $B8: {  # true
        %9:i32 = spirv.add<i32> 3i, 2i
        if true [t: $B10, f: $B11] {  # if_5
          $B10: {  # true
            %10:i32 = spirv.add<i32> 3i, 1i
            exit_if  # if_5
          }
          $B11: {  # false
            exit_if  # if_5
          }
        }
        exit_if  # if_4
      }
      $B9: {  # false
        unreachable
      }
    }
    %11:i32 = spirv.add<i32> 2i, 1i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_SingleBlock_BothBackedge) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %35 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %36 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %20 None
               OpBranchConditional %true %20 %20
         %99 = OpLabel
         %37 = OpIAdd %i32 %three %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:bool = or true, true
        if %4 [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            unreachable
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %5:i32 = spirv.add<i32> 3i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_SingleBlock_UnconditionalBackege) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %35 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %36 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %20 None
               OpBranch %20
         %99 = OpLabel
         %37 = OpIAdd %i32 %three %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        continue  # -> $B3
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %4:i32 = spirv.add<i32> 3i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Unconditional_Body_SingleBlockContinue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %35 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %36 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %50 None
               OpBranch %30
         %30 = OpLabel
         %37 = OpIAdd %i32 %three %three
               OpBranch %50
         %50 = OpLabel
         %38 = OpIAdd %i32 %one %two
               OpBranch %20
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %5:i32 = spirv.add<i32> 1i, 2i
        next_iteration  # -> $B2
      }
    }
    %6:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Unconditional_Body_MultiBlockContinue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %50 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranch %50
         %50 = OpLabel
         %37 = OpIAdd %i32 %one %three
               OpBranch %60
         %60 = OpLabel
         %38 = OpIAdd %i32 %one %two
               OpBranch %20
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %5:i32 = spirv.add<i32> 1i, 3i
        %6:i32 = spirv.add<i32> 1i, 2i
        next_iteration  # -> $B2
      }
    }
    %7:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_Unconditional_Body_ContinueNestIf) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %50 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranch %50
         %50 = OpLabel ; continue target; also if-header
         %37 = OpIAdd %i32 %one %two
               OpSelectionMerge %80 None
               OpBranchConditional %false %60 %80
         %60 = OpLabel
         %38 = OpIAdd %i32 %two %three
               OpBranch %80
         %80 = OpLabel
         %39 = OpIAdd %i32 %one %three
               OpBranch %20
         %99 = OpLabel
         %41 = OpIAdd %i32 %three %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %5:i32 = spirv.add<i32> 1i, 2i
        if false [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %6:i32 = spirv.add<i32> 2i, 3i
            exit_if  # if_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %7:i32 = spirv.add<i32> 1i, 3i
        next_iteration  # -> $B2
      }
    }
    %8:i32 = spirv.add<i32> 3i, 2i
    ret
  }
}
)");
}

// Test case where both branches exit. e.g both go to merge.
TEST_F(SpirvParserTest, Loop_Never) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpLoopMerge %99 %80 None
               OpBranchConditional %true %99 %99
         %80 = OpLabel ; continue target
         %35 = OpIAdd %i32 %two %two
               OpBranch %20
         %99 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        %3:bool = or true, true
        if %3 [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            unreachable
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %4:i32 = spirv.add<i32> 3i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_TrueToBody_FalseBreaks) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpLoopMerge %99 %80 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %36 = OpIAdd %i32 %three %three
               OpBranch %20
         %99 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %3:i32 = spirv.add<i32> 2i, 2i
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %4:i32 = spirv.add<i32> 3i, 3i
        next_iteration  # -> $B2
      }
    }
    %5:i32 = spirv.add<i32> 1i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_FalseToBody_TrueBreaks) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpLoopMerge %99 %80 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %36 = OpIAdd %i32 %three %three
               OpBranch %20
         %99 = OpLabel
         %37 = OpIAdd %i32 %one %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %3:i32 = spirv.add<i32> 2i, 2i
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %4:i32 = spirv.add<i32> 3i, 3i
        next_iteration  # -> $B2
      }
    }
    %5:i32 = spirv.add<i32> 1i, 3i
    ret
  }
}
)");
}

// By construction, it has to come from nested code.
TEST_F(SpirvParserTest, Loop_NestedIfContinue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
               OpSelectionMerge %50 None
               OpBranchConditional %true %40 %50
         %40 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %80 ; continue edge
         %50 = OpLabel ; inner selection merge
         %35 = OpIAdd %i32 %two %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %36 = OpIAdd %i32 %three %three
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %2:i32 = spirv.add<i32> 1i, 1i
            continue  # -> $B3
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %3:i32 = spirv.add<i32> 2i, 2i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %4:i32 = spirv.add<i32> 3i, 3i
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyAlwaysBreaks) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %99 ; break is here
         %80 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranch %20 ; backedge
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        exit_loop  # loop_1
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

// The else-branch has a continue but it's skipped because it's from a
// block that immediately precedes the continue construct.
TEST_F(SpirvParserTest, Loop_BodyConditionallyBreaks_FromTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranchConditional %true %99 %80
         %80 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranch %20 ; backedge
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            continue  # -> $B3
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %3:i32 = spirv.add<i32> 2i, 2i
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

// The else-branch has a continue but it's skipped because it's from a
// block that immediately precedes the continue construct.
TEST_F(SpirvParserTest, Loop_BodyConditionallyBreaks_FromFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranchConditional %true %80 %99
         %80 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranch %20 ; backedge
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %3:i32 = spirv.add<i32> 2i, 2i
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyConditionallyBreaks_FromTrue_Early) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranchConditional %true %99 %70
         %70 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranch %80
         %80 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranch %20 ; backedge
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            %3:i32 = spirv.add<i32> 2i, 2i
            continue  # -> $B3
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %4:i32 = spirv.add<i32> 3i, 3i
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Loop_BodyConditionallyBreaks_FromFalse_Early) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranchConditional %true %70 %99
         %70 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranch %80
         %80 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranch %20 ; backedge
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %3:i32 = spirv.add<i32> 2i, 2i
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %4:i32 = spirv.add<i32> 3i, 3i
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_Case_SintValue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpSelectionMerge %99 None
               ; SPIR-V assembler doesn't support negative literals in switch
               OpSwitch %one %99 20 %20 2000000000 %30 !4000000000 %40
         %20 = OpLabel
         %35 = OpIAdd %i32 %one %one
               OpBranch %99
         %30 = OpLabel
         %36 = OpIAdd %i32 %two %two
               OpBranch %99
         %40 = OpLabel
         %37 = OpIAdd %i32 %three %three
               OpBranch %99
         %99 = OpLabel
         %38 = OpIAdd %i32 %one %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    switch 1i [c: (default, $B2), c: (20i, $B3), c: (2000000000i, $B4), c: (-294967296i, $B5)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
      $B3: {  # case
        %3:i32 = spirv.add<i32> 1i, 1i
        exit_switch  # switch_1
      }
      $B4: {  # case
        %4:i32 = spirv.add<i32> 2i, 2i
        exit_switch  # switch_1
      }
      $B5: {  # case
        %5:i32 = spirv.add<i32> 3i, 3i
        exit_switch  # switch_1
      }
    }
    %6:i32 = spirv.add<i32> 1i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Switch_Case_UintValue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
       %bool = OpTypeBool
        %one = OpConstant %u32 1
        %two = OpConstant %u32 2
      %three = OpConstant %u32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %u32 %one %one
               OpSelectionMerge %99 None
               OpSwitch %one %99 20 %20 2000000000 %30 50 %40
         %20 = OpLabel
         %35 = OpIAdd %u32 %two %two
               OpBranch %99
         %30 = OpLabel
         %36 = OpIAdd %u32 %three %three
               OpBranch %99
         %40 = OpLabel
         %37 = OpIAdd %u32 %one %two
               OpBranch %99
         %99 = OpLabel
         %38 = OpIAdd %u32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:u32 = spirv.add<u32> 1u, 1u
    switch 1u [c: (default, $B2), c: (20u, $B3), c: (2000000000u, $B4), c: (50u, $B5)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
      $B3: {  # case
        %3:u32 = spirv.add<u32> 2u, 2u
        exit_switch  # switch_1
      }
      $B4: {  # case
        %4:u32 = spirv.add<u32> 3u, 3u
        exit_switch  # switch_1
      }
      $B5: {  # case
        %5:u32 = spirv.add<u32> 1u, 2u
        exit_switch  # switch_1
      }
    }
    %6:u32 = spirv.add<u32> 2u, 3u
    ret
  }
}
)");
}

// When the break is not last in its case, we must emit a 'break'
TEST_F(SpirvParserTest, Branch_SwitchBreak_NotLastInCase) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpSelectionMerge %99 None
               OpSwitch %one %99 20 %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpSelectionMerge %50 None
               OpBranchConditional %true %40 %50
         %40 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranch %99 ; branch to merge. Not last in case
         %50 = OpLabel ; inner merge
         %37 = OpIAdd %i32 %one %two
               OpBranch %99
         %99 = OpLabel
         %38 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    switch 1i [c: (default, $B2), c: (20i, $B3)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
      $B3: {  # case
        %3:i32 = spirv.add<i32> 2i, 2i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %4:i32 = spirv.add<i32> 3i, 3i
            exit_switch  # switch_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %5:i32 = spirv.add<i32> 1i, 2i
        exit_switch  # switch_1
      }
    }
    %6:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");

    //     auto* expect = R"(var_1 = 1u;
    // switch(42u) {
    //   case 20u: {
    //     var_1 = 20u;
    //     if (false) {
    //       var_1 = 40u;
    //       break;
    //     }
    //     var_1 = 50u;
    //   }
    //   default: {
    //   }
    // }
    // var_1 = 7u;
    // return;
}

TEST_F(SpirvParserTest, Branch_LoopBreak_MultiBlockLoop_FromBody) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %99 ; break is here
         %80 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranch %20 ; backedge
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        exit_loop  # loop_1
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest,
       Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional_BreakIf) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %80
         %80 = OpLabel ; continue target
         %34 = OpIAdd %i32 %one %one
               OpBranchConditional %true %99 %20  ; exit, and backedge
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        continue  # -> $B3
      }
      $B3: {  # continuing
        %2:i32 = spirv.add<i32> 1i, 1i
        break_if true  # -> [t: exit_loop loop_1, f: $B2]
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %80
         %80 = OpLabel ; continue target
         %34 = OpIAdd %i32 %one %one
               OpBranchConditional %true %20 %99  ; backedge, and exit
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        continue  # -> $B3
      }
      $B3: {  # continuing
        %2:i32 = spirv.add<i32> 1i, 1i
        %3:bool = not true
        break_if %3  # -> [t: exit_loop loop_1, f: $B2]
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Branch_LoopBreak_FromContinueConstructTail) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %50 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
               OpBranch %50
         %50 = OpLabel
               OpBranch %60
         %60 = OpLabel
               OpBranchConditional %true %20 %99
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %2:bool = not true
        break_if %2  # -> [t: exit_loop loop_1, f: $B2]
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Branch_LoopContinue_LastInLoopConstruct) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %80 ; continue edge from last block before continue target
         %80 = OpLabel ; continue target
         %35 = OpIAdd %i32 %two %two
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %2:i32 = spirv.add<i32> 1i, 1i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %3:i32 = spirv.add<i32> 2i, 2i
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

// By construction, it has to come from nested code.
TEST_F(SpirvParserTest, Branch_LoopContinue_BeforeLast) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpBranch %20
         %20 = OpLabel
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
               OpSelectionMerge %50 None
               OpBranchConditional %true %40 %50
         %40 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %80 ; continue edge
         %50 = OpLabel ; inner selection merge
         %35 = OpIAdd %i32 %two %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %36 = OpIAdd %i32 %three %three
               OpBranch %20
         %99 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %2:i32 = spirv.add<i32> 1i, 1i
            continue  # -> $B3
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %3:i32 = spirv.add<i32> 2i, 2i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %4:i32 = spirv.add<i32> 3i, 3i
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Branch_LoopContinue_FromSwitch) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpSelectionMerge %79 None
               OpSwitch %one %79 40 %40
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranch %80 ; continue edge
         %79 = OpLabel ; switch merge
         %38 = OpIAdd %i32 %three %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %39 = OpIAdd %i32 %two %three
               OpBranch %20
         %99 = OpLabel
         %41 = OpIAdd %i32 %three %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        switch 1i [c: (default, $B4), c: (40i, $B5)] {  # switch_1
          $B4: {  # case
            exit_switch  # switch_1
          }
          $B5: {  # case
            %5:i32 = spirv.add<i32> 1i, 2i
            continue  # -> $B3
          }
        }
        %6:i32 = spirv.add<i32> 3i, 2i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %7:i32 = spirv.add<i32> 2i, 3i
        next_iteration  # -> $B2
      }
    }
    %8:i32 = spirv.add<i32> 3i, 1i
    ret
  }
}
)");
}

// When unconditional, the if-break must be last in the then clause.
TEST_F(SpirvParserTest, Branch_IfBreak_FromThen) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpBranchConditional %true %30 %99
         %30 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %99
         %99 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %2:i32 = spirv.add<i32> 1i, 1i
        exit_if  # if_1
      }
      $B3: {  # false
        exit_if  # if_1
      }
    }
    %3:i32 = spirv.add<i32> 2i, 2i
    ret
  }
}
)");
}

// When unconditional, the if-break must be last in the else clause.
TEST_F(SpirvParserTest, Branch_IfBreak_FromElse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpBranchConditional %true %99 %30
         %30 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %99
         %99 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    if true [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        exit_if  # if_1
      }
      $B3: {  # false
        %2:i32 = spirv.add<i32> 1i, 1i
        exit_if  # if_1
      }
    }
    %3:i32 = spirv.add<i32> 2i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Back_SingleBlock_LoopBreak_OnTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %20 None
               OpBranchConditional %true %99 %20
         %99 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            continue  # -> $B3
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %4:i32 = spirv.add<i32> 3i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Back_SingleBlock_LoopBreak_OnFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %20 None
               OpBranchConditional %true %20 %99
         %99 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %4:i32 = spirv.add<i32> 3i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Back_MultiBlock_LoopBreak_OnTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %80
         %80 = OpLabel
               OpBranchConditional %true %99 %20
         %99 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        continue  # -> $B3
      }
      $B3: {  # continuing
        break_if true  # -> [t: exit_loop loop_1, f: $B2]
      }
    }
    %4:i32 = spirv.add<i32> 3i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Back_MultiBlock_LoopBreak_OnFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %80
         %80 = OpLabel
               OpBranchConditional %true %20 %99
         %99 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %4:bool = not true
        break_if %4  # -> [t: exit_loop loop_1, f: $B2]
      }
    }
    %5:i32 = spirv.add<i32> 3i, 3i
    ret
  }
}
)");
}

// When the break is not last in its case, we must emit a 'break'
TEST_F(SpirvParserTest, BranchConditional_SwitchBreak_SwitchBreak_NotLastInCase) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpSelectionMerge %99 None
               OpSwitch %one %99 20 %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpSelectionMerge %50 None
               OpBranchConditional %true %40 %50
         %40 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranchConditional %false %99 %99 ; branch to merge. Not last in case
         %50 = OpLabel ; inner merge
         %37 = OpIAdd %i32 %one %two
               OpBranch %99
         %99 = OpLabel
         %38 = OpIAdd %i32 %one %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    switch 1i [c: (default, $B2), c: (20i, $B3)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
      $B3: {  # case
        %3:i32 = spirv.add<i32> 2i, 2i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %4:i32 = spirv.add<i32> 3i, 3i
            %5:bool = or false, true
            if %5 [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                exit_switch  # switch_1
              }
              $B7: {  # false
                unreachable
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %6:i32 = spirv.add<i32> 1i, 2i
        exit_switch  # switch_1
      }
    }
    %7:i32 = spirv.add<i32> 1i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_SwitchBreak_Continue_OnTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpSelectionMerge %79 None
               OpSwitch %one %79 40 %40
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranchConditional %true %80 %79 ; break; continue on true
         %79 = OpLabel
         %38 = OpIAdd %i32 %one %three
               OpBranch %80
         %80 = OpLabel ; continue target
         %39 = OpIAdd %i32 %two %three
               OpBranch %20
         %99 = OpLabel ; loop merge
         %41 = OpIAdd %i32 %three %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        switch 1i [c: (default, $B4), c: (40i, $B5)] {  # switch_1
          $B4: {  # case
            exit_switch  # switch_1
          }
          $B5: {  # case
            %5:i32 = spirv.add<i32> 1i, 2i
            if true [t: $B6, f: $B7] {  # if_1
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                exit_switch  # switch_1
              }
            }
            exit_switch  # switch_1
          }
        }
        %6:i32 = spirv.add<i32> 1i, 3i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %7:i32 = spirv.add<i32> 2i, 3i
        next_iteration  # -> $B2
      }
    }
    %8:i32 = spirv.add<i32> 3i, 1i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_SwitchBreak_Continue_OnFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpSelectionMerge %79 None
               OpSwitch %one %79 40 %40
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranchConditional %true %79 %80 ; break; continue on false
         %79 = OpLabel
         %38 = OpIAdd %i32 %one %three
               OpBranch %80
         %80 = OpLabel ; continue target
         %39 = OpIAdd %i32 %two %three
               OpBranch %20
         %99 = OpLabel ; loop merge
         %41 = OpIAdd %i32 %three %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        switch 1i [c: (default, $B4), c: (40i, $B5)] {  # switch_1
          $B4: {  # case
            exit_switch  # switch_1
          }
          $B5: {  # case
            %5:i32 = spirv.add<i32> 1i, 2i
            if true [t: $B6, f: $B7] {  # if_1
              $B6: {  # true
                exit_switch  # switch_1
              }
              $B7: {  # false
                continue  # -> $B3
              }
            }
            exit_switch  # switch_1
          }
        }
        %6:i32 = spirv.add<i32> 1i, 3i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %7:i32 = spirv.add<i32> 2i, 3i
        next_iteration  # -> $B2
      }
    }
    %8:i32 = spirv.add<i32> 3i, 1i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_SwitchBreak_Forward_OnTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpSelectionMerge %99 None
               OpSwitch %one %99 20 %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranchConditional %true %30 %99 ; break; forward on true
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranch %99
         %99 = OpLabel ; switch merge
         %37 = OpIAdd %i32 %one %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    switch 1i [c: (default, $B2), c: (20i, $B3)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
      $B3: {  # case
        %3:i32 = spirv.add<i32> 2i, 2i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %4:i32 = spirv.add<i32> 3i, 3i
            exit_switch  # switch_1
          }
          $B5: {  # false
            exit_switch  # switch_1
          }
        }
        exit_switch  # switch_1
      }
    }
    %5:i32 = spirv.add<i32> 1i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_SwitchBreak_Forward_OnFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpSelectionMerge %99 None
               OpSwitch %one %99 20 %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpBranchConditional %true %99 %30 ; break; forward on false
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranch %99
         %99 = OpLabel ; switch merge
         %37 = OpIAdd %i32 %one %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    switch 1i [c: (default, $B2), c: (20i, $B3)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
      $B3: {  # case
        %3:i32 = spirv.add<i32> 2i, 2i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_switch  # switch_1
          }
          $B5: {  # false
            %4:i32 = spirv.add<i32> 3i, 3i
            exit_switch  # switch_1
          }
        }
        exit_switch  # switch_1
      }
    }
    %5:i32 = spirv.add<i32> 1i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_LoopBreak_SingleBlock_LoopBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranchConditional %true %99 %99
         %80 = OpLabel ; continue target
         %36 = OpIAdd %i32 %three %three
               OpBranch %20
         %99 = OpLabel
         %37 = OpIAdd %i32 %one %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:bool = or true, true
        if %4 [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            unreachable
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %5:i32 = spirv.add<i32> 1i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_LoopBreak_MultiBlock_LoopBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranchConditional %true %99 %99
         %80 = OpLabel ; continue target
         %37 = OpIAdd %i32 %one %two
               OpBranch %20
         %99 = OpLabel
         %38 = OpIAdd %i32 %one %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        %5:bool = or true, true
        if %5 [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            unreachable
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %6:i32 = spirv.add<i32> 1i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_LoopBreak_Continue_OnTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %25
         ; Need this extra selection to make another block between
         ; %30 and the continue target, so we actually induce a Continue
         ; statement to exist.
         %25 = OpLabel
               OpSelectionMerge %40 None
               OpBranchConditional %false %30 %40
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               ; break; continue on true
               OpBranchConditional %false %80 %99
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %38 = OpIAdd %i32 %one %three
               OpBranch %20
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        if false [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %4:i32 = spirv.add<i32> 3i, 3i
            if false [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                exit_loop  # loop_1
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %5:i32 = spirv.add<i32> 1i, 2i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %6:i32 = spirv.add<i32> 1i, 3i
        next_iteration  # -> $B2
      }
    }
    %7:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_LoopBreak_Continue_OnFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %25
         ; Need this extra selection to make another block between
         ; %30 and the continue target, so we actually induce a Continue
         ; statement to exist.
         %25 = OpLabel
               OpSelectionMerge %40 None
               OpBranchConditional %false %30 %40
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               ; break; continue on false
               OpBranchConditional %true %99 %80
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %38 = OpIAdd %i32 %one %three
               OpBranch %20
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        if false [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %4:i32 = spirv.add<i32> 3i, 3i
            if true [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                exit_loop  # loop_1
              }
              $B7: {  # false
                continue  # -> $B3
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %5:i32 = spirv.add<i32> 1i, 2i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %6:i32 = spirv.add<i32> 1i, 3i
        next_iteration  # -> $B2
      }
    }
    %7:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_LoopBreak_Forward_OnTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               ; break; forward on true
               OpBranchConditional %true %40 %99
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %38 = OpIAdd %i32 %one %three
               OpBranch %20
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %5:i32 = spirv.add<i32> 1i, 2i
            continue  # -> $B3
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %6:i32 = spirv.add<i32> 1i, 3i
        next_iteration  # -> $B2
      }
    }
    %7:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_LoopBreak_Forward_OnFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               ; break; forward on false
               OpBranchConditional %true %99 %40
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %38 = OpIAdd %i32 %one %three
               OpBranch %20
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            %5:i32 = spirv.add<i32> 1i, 2i
            continue  # -> $B3
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %6:i32 = spirv.add<i32> 1i, 3i
        next_iteration  # -> $B2
      }
    }
    %7:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Continue_Continue_FromHeader) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranchConditional %true %80 %80 ; to continue
         %80 = OpLabel ; continue target
         %36 = OpIAdd %i32 %three %three
               OpBranch %20
         %99 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:bool = or true, true
        if %4 [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            unreachable
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %5:i32 = spirv.add<i32> 3i, 3i
        next_iteration  # -> $B2
      }
    }
    %6:i32 = spirv.add<i32> 1i, 2i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Continue_Continue_AfterHeader_Unconditional) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpBranchConditional %true %80 %80 ; to continue
         %80 = OpLabel ; continue target
         %37 = OpIAdd %i32 %one %two
               OpBranch %20
         %99 = OpLabel
         %38 = OpIAdd %i32 %one %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        %5:bool = or true, true
        if %5 [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            unreachable
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %6:i32 = spirv.add<i32> 1i, 2i
        next_iteration  # -> $B2
      }
    }
    %7:i32 = spirv.add<i32> 1i, 3i
    ret
  }
}
)");
}

// Create an intervening block so we actually require a "continue" statement
TEST_F(SpirvParserTest, BranchConditional_Continue_Continue_AfterHeader_Conditional) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpSelectionMerge %50 None
               OpBranchConditional %false %40 %50
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranchConditional %true %80 %80 ; to continue
         %50 = OpLabel ; merge for selection
         %38 = OpIAdd %i32 %one %three
               OpBranch %80
         %80 = OpLabel ; continue target
         %39 = OpIAdd %i32 %two %three
               OpBranch %20
         %99 = OpLabel
         %41 = OpIAdd %i32 %three %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        if false [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %5:i32 = spirv.add<i32> 1i, 2i
            %6:bool = or true, true
            if %6 [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                unreachable
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %7:i32 = spirv.add<i32> 1i, 3i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %8:i32 = spirv.add<i32> 2i, 3i
        next_iteration  # -> $B2
      }
    }
    %9:i32 = spirv.add<i32> 3i, 1i
    ret
  }
}
)");
}

// Like the previous tests, but with an empty continuing clause.
TEST_F(SpirvParserTest,
       BranchConditional_Continue_Continue_AfterHeader_Conditional_EmptyContinuing) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpSelectionMerge %50 None
               OpBranchConditional %false %40 %50
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranchConditional %false %80 %80 ; to continue
         %50 = OpLabel ; merge for selection
         %38 = OpIAdd %i32 %one %three
               OpBranch %80
         %80 = OpLabel ; continue target
               ; no statements here.
               OpBranch %20
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        if false [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %5:i32 = spirv.add<i32> 1i, 2i
            %6:bool = or false, true
            if %6 [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                unreachable
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %7:i32 = spirv.add<i32> 1i, 3i
        continue  # -> $B3
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    %8:i32 = spirv.add<i32> 2i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_LoopContinue_FromSwitch) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpSelectionMerge %79 None
               OpSwitch %one %79 40 %40
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranchConditional %false %80 %80; dup continue edge
         %79 = OpLabel ; switch merge
         %38 = OpIAdd %i32 %one %three
               OpBranch %80
         %80 = OpLabel ; continue target
         %39 = OpIAdd %i32 %two %three
               OpBranch %20
         %99 = OpLabel
         %41 = OpIAdd %i32 %three %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        switch 1i [c: (default, $B4), c: (40i, $B5)] {  # switch_1
          $B4: {  # case
            exit_switch  # switch_1
          }
          $B5: {  # case
            %5:i32 = spirv.add<i32> 1i, 2i
            %6:bool = or false, true
            if %6 [t: $B6, f: $B7] {  # if_1
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                unreachable
              }
            }
            exit_switch  # switch_1
          }
        }
        %7:i32 = spirv.add<i32> 1i, 3i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %8:i32 = spirv.add<i32> 2i, 3i
        next_iteration  # -> $B2
      }
    }
    %9:i32 = spirv.add<i32> 3i, 1i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Continue_IfBreak_OnTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpSelectionMerge %50 None
               OpBranchConditional %false %40 %50
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               ; true to if's merge;  false to continue
               OpBranchConditional %true %50 %80
         %50 = OpLabel ; merge for selection
         %38 = OpIAdd %i32 %one %three
               OpBranch %80
         %80 = OpLabel ; continue target
         %39 = OpIAdd %i32 %two %one
               OpBranch %20
         %99 = OpLabel
         %41 = OpIAdd %i32 %three %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        if false [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %5:i32 = spirv.add<i32> 1i, 2i
            if true [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                exit_if  # if_2
              }
              $B7: {  # false
                continue  # -> $B3
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %6:i32 = spirv.add<i32> 1i, 3i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %7:i32 = spirv.add<i32> 2i, 1i
        next_iteration  # -> $B2
      }
    }
    %8:i32 = spirv.add<i32> 3i, 1i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Continue_IfBreak_OnFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpSelectionMerge %50 None
               OpBranchConditional %false %40 %50
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               ; false to if's merge;  true to continue
               OpBranchConditional %true %80 %50
         %50 = OpLabel ; merge for selection
         %38 = OpIAdd %i32 %one %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %39 = OpIAdd %i32 %two %three
               OpBranch %20
         %99 = OpLabel
         %41 = OpIAdd %i32 %three %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        if false [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %5:i32 = spirv.add<i32> 1i, 2i
            if true [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                continue  # -> $B3
              }
              $B7: {  # false
                exit_if  # if_2
              }
            }
            exit_if  # if_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        %6:i32 = spirv.add<i32> 1i, 2i
        continue  # -> $B3
      }
      $B3: {  # continuing
        %7:i32 = spirv.add<i32> 2i, 3i
        next_iteration  # -> $B2
      }
    }
    %8:i32 = spirv.add<i32> 3i, 1i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Continue_Forward_OnTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               ; continue; forward on true
               OpBranchConditional %true %40 %80
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %38 = OpIAdd %i32 %one %three
               OpBranch %20
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            %5:i32 = spirv.add<i32> 1i, 2i
            continue  # -> $B3
          }
          $B5: {  # false
            continue  # -> $B3
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %6:i32 = spirv.add<i32> 1i, 3i
        next_iteration  # -> $B2
      }
    }
    %7:i32 = spirv.add<i32> 2i, 1i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Continue_Forward_OnFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranch %20
         %20 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpLoopMerge %99 %80 None
               OpBranch %30
         %30 = OpLabel
         %36 = OpIAdd %i32 %three %three
               ; continue; forward on true
               OpBranchConditional %true %80 %40
         %40 = OpLabel
         %37 = OpIAdd %i32 %one %two
               OpBranch %80
         %80 = OpLabel ; continue target
         %38 = OpIAdd %i32 %one %three
               OpBranch %20
         %99 = OpLabel
         %39 = OpIAdd %i32 %two %one
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        %3:i32 = spirv.add<i32> 2i, 2i
        %4:i32 = spirv.add<i32> 3i, 3i
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            %5:i32 = spirv.add<i32> 1i, 2i
            continue  # -> $B3
          }
        }
        unreachable
      }
      $B3: {  # continuing
        %6:i32 = spirv.add<i32> 1i, 3i
        next_iteration  # -> $B2
      }
    }
    %7:i32 = spirv.add<i32> 2i, 1i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_IfBreak_IfBreak_Same) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpSelectionMerge %99 None
               OpBranchConditional %true %99 %99
         %20 = OpLabel ; dead
         %35 = OpIAdd %i32 %two %two
               OpBranch %99
         %99 = OpLabel
         %36 = OpIAdd %i32 %three %three
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    %3:bool = or true, true
    if %3 [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        exit_if  # if_1
      }
      $B3: {  # false
        unreachable
      }
    }
    %4:i32 = spirv.add<i32> 3i, 3i
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, BranchConditional_Forward_Forward_Same) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
         %34 = OpIAdd %i32 %one %one
               OpBranchConditional %true %99 %99; forward
         %99 = OpLabel
         %35 = OpIAdd %i32 %two %two
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:i32 = spirv.add<i32> 1i, 1i
    %3:bool = or true, true
    if %3 [t: $B2, f: $B3] {  # if_1
      $B2: {  # true
        %4:i32 = spirv.add<i32> 2i, 2i
        ret
      }
      $B3: {  # false
        unreachable
      }
    }
    unreachable
  }
}
)");
}

// crbug.com/tint/243
TEST_F(SpirvParserTest, IfSelection_TrueBranch_LoopBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
          %5 = OpLabel
               OpBranch %10
         %10 = OpLabel
               OpLoopMerge %99 %90 None
               OpBranch %20
         %20 = OpLabel
               OpSelectionMerge %40 None
               OpBranchConditional %true %99 %30 ; true branch breaking is ok
         %30 = OpLabel
               OpBranch %40
         %40 = OpLabel ; selection merge
               OpBranch %90
         %90 = OpLabel ; continue target
               OpBranch %10 ; backedge
         %99 = OpLabel ; loop merge
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        continue  # -> $B3
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

// crbug.com/tint/243
TEST_F(SpirvParserTest, TrueBranch_LoopContinue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
          %5 = OpLabel
               OpBranch %10
         %10 = OpLabel
               OpLoopMerge %99 %90 None
               OpBranch %20
         %20 = OpLabel
               OpSelectionMerge %40 None
               OpBranchConditional %true %90 %30 ; true branch continue is ok
         %30 = OpLabel
               OpBranch %40
         %40 = OpLabel ; selection merge
               OpBranch %90
         %90 = OpLabel ; continue target
               OpBranch %10 ; backedge
         %99 = OpLabel ; loop merge
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            continue  # -> $B3
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        continue  # -> $B3
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

// crbug.com/tint/243
TEST_F(SpirvParserTest, TrueBranch_SwitchBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %one %99 20 %20
         %20 = OpLabel
               OpSelectionMerge %40 None
               OpBranchConditional %true %99 %30 ; true branch switch break is ok
         %30 = OpLabel
               OpBranch %40
         %40 = OpLabel ; if-selection merge
               OpBranch %99
         %99 = OpLabel ; switch merge
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 1i [c: (default, $B2), c: (20i, $B3)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
      $B3: {  # case
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_switch  # switch_1
          }
          $B5: {  # false
            exit_if  # if_1
          }
        }
        exit_switch  # switch_1
      }
    }
    ret
  }
}
)");
}

// crbug.com/tint/243
TEST_F(SpirvParserTest, FalseBranch_LoopBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
          %5 = OpLabel
               OpBranch %10
         %10 = OpLabel
               OpLoopMerge %99 %90 None
               OpBranch %20
         %20 = OpLabel
               OpSelectionMerge %40 None
               OpBranchConditional %true %30 %99 ; false branch breaking is ok
         %30 = OpLabel
               OpBranch %40
         %40 = OpLabel ; selection merge
               OpBranch %90
         %90 = OpLabel ; continue target
               OpBranch %10 ; backedge
         %99 = OpLabel ; loop merge
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_if  # if_1
          }
          $B5: {  # false
            exit_loop  # loop_1
          }
        }
        continue  # -> $B3
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

// crbug.com/tint/243
TEST_F(SpirvParserTest, FalseBranch_LoopContinue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
          %5 = OpLabel
               OpBranch %10
         %10 = OpLabel
               OpLoopMerge %99 %90 None
               OpBranch %20
         %20 = OpLabel
               OpSelectionMerge %40 None
               OpBranchConditional %true %30 %90 ; false branch continue is ok
         %30 = OpLabel
               OpBranch %40
         %40 = OpLabel ; selection merge
               OpBranch %90
         %90 = OpLabel ; continue target
               OpBranch %10 ; backedge
         %99 = OpLabel ; loop merge
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_if  # if_1
          }
          $B5: {  # false
            continue  # -> $B3
          }
        }
        continue  # -> $B3
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

// crbug.com/tint/243
TEST_F(SpirvParserTest, FalseBranch_SwitchBreak) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
       %bool = OpTypeBool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpConstant %i32 3
       %true = OpConstantTrue %bool
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
         %10 = OpLabel
               OpSelectionMerge %99 None
               OpSwitch %one %99 20 %20
         %20 = OpLabel
               OpSelectionMerge %40 None
               OpBranchConditional %true %30 %99 ; false branch switch break is ok
         %30 = OpLabel
               OpBranch %40
         %40 = OpLabel ; if-selection merge
               OpBranch %99
         %99 = OpLabel ; switch merge
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 1i [c: (default, $B2), c: (20i, $B3)] {  # switch_1
      $B2: {  # case
        exit_switch  # switch_1
      }
      $B3: {  # case
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_if  # if_1
          }
          $B5: {  # false
            exit_switch  # switch_1
          }
        }
        exit_switch  # switch_1
      }
    }
    ret
  }
}
)");
}
TEST_F(SpirvParserTest, ConvertHoisted) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical Simple
               OpEntryPoint Fragment %100 "main"
               OpExecutionMode %100 OriginUpperLeft
       %void = OpTypeVoid
          %2 = OpTypeFunction %void
       %bool = OpTypeBool
       %uint = OpTypeInt 32 0
      %float = OpTypeFloat 32
       %true = OpConstantTrue %bool
   %float_50 = OpConstant %float 50
        %100 = OpFunction %void None %2
         %10 = OpLabel
               OpBranch %30
         %30 = OpLabel
               OpLoopMerge %90 %80 None
               OpBranchConditional %true %90 %40
         %40 = OpLabel
               OpSelectionMerge %50 None
               OpBranchConditional %true %45 %50
         %45 = OpLabel
        %600 = OpCopyObject %float %float_50
               OpBranch %50
         %50 = OpLabel
               OpBranch %90
         %80 = OpLabel
         %82 = OpConvertFToU %uint %600
               OpBranch %30
         %90 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @fragment func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if true [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            if true [t: $B6, f: $B7] {  # if_2
              $B6: {  # true
                %2:f32 = let 50.0f
                exit_if  # if_2
              }
              $B7: {  # false
                exit_if  # if_2
              }
            }
            exit_loop  # loop_1
          }
        }
        unreachable
      }
      $B3: {  # continuing
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

// https://crbug.com/440964231
TEST_F(SpirvParserTest, SwitchWithSwitchInDefault) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %main "main"
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
       %main = OpFunction %void None %3
          %4 = OpLabel
               OpSelectionMerge %24 None
               OpSwitch %int_0 %22 0 %23
         %23 = OpLabel
               OpBranch %24
         %22 = OpLabel
               OpSelectionMerge %35 None
               OpSwitch %int_0 %33
         %33 = OpLabel
               OpBranch %35
         %35 = OpLabel
               OpBranch %24
         %24 = OpLabel
               OpBranch %16
         %16 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    switch 0i [c: (default, $B2), c: (0i, $B3)] {  # switch_1
      $B2: {  # case
        switch 0i [c: (default, $B4)] {  # switch_2
          $B4: {  # case
            exit_switch  # switch_2
          }
        }
        exit_switch  # switch_1
      }
      $B3: {  # case
        exit_switch  # switch_1
      }
    }
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, LoopInContinuing) {
    EXPECT_IR(R"(
                 OpCapability Shader
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %fn "main"
                 OpExecutionMode %fn OriginUpperLeft
         %void = OpTypeVoid
         %bool = OpTypeBool
        %false = OpConstantFalse %bool
      %void_fn = OpTypeFunction %void

           %fn = OpFunction %void None %void_fn
        %entry = OpLabel
                 OpBranch %loop1
        %loop1 = OpLabel

                 OpLoopMerge %loop1_merge %loop2 None
                 OpBranch %loop1_body
   %loop1_body = OpLabel
                 OpBranchConditional %false %loop1_merge %loop2
        %loop2 = OpLabel

                 OpLoopMerge %loop2_merge %loop2 None
                 OpBranchConditional %false %loop2_merge %loop2
  %loop2_merge = OpLabel
                 OpBranch %loop1

  %loop1_merge = OpLabel
                 OpReturn
                 OpFunctionEnd
)",
              R"(
%main = @fragment func():void {
  $B1: {
    loop [b: $B2, c: $B3] {  # loop_1
      $B2: {  # body
        if false [t: $B4, f: $B5] {  # if_1
          $B4: {  # true
            exit_loop  # loop_1
          }
          $B5: {  # false
            continue  # -> $B3
          }
        }
        unreachable
      }
      $B3: {  # continuing
        loop [b: $B6, c: $B7] {  # loop_2
          $B6: {  # body
            if false [t: $B8, f: $B9] {  # if_2
              $B8: {  # true
                exit_loop  # loop_2
              }
              $B9: {  # false
                continue  # -> $B7
              }
            }
            unreachable
          }
          $B7: {  # continuing
            next_iteration  # -> $B6
          }
        }
        next_iteration  # -> $B2
      }
    }
    ret
  }
}
)");
}

}  // namespace
}  // namespace tint::spirv::reader
