[spirv-reader] Test loop block order + nesting

Bug: tint:3
Change-Id: I97915d8e30c1676e1c1340217c5eb732c44f8ef0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/20067
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/reader/spirv/function_cfg_test.cc b/src/reader/spirv/function_cfg_test.cc
index e5b45ee..4d12dc9 100644
--- a/src/reader/spirv/function_cfg_test.cc
+++ b/src/reader/spirv/function_cfg_test.cc
@@ -30,12 +30,16 @@
 
 std::string CommonTypes() {
   return R"(
+    OpCapability Shader
+    OpMemoryModel Logical Simple
+
     %void = OpTypeVoid
     %voidfn = OpTypeFunction %void
 
     %bool = OpTypeBool
     %cond = OpUndef %bool
     %cond2 = OpUndef %bool
+    %cond3 = OpUndef %bool
 
     %uint = OpTypeInt 32 0
     %selector = OpUndef %uint
@@ -907,6 +911,80 @@
   EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 40, 50, 99)) << assembly;
 }
 
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_If) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpSelectionMerge %49 None
+     OpBranchConditional %cond2 %40 %45 ; nested if
+
+     %40 = OpLabel
+     OpBranch %49
+
+     %45 = OpLabel
+     OpBranch %49
+
+     %49 = OpLabel
+     OpBranch %50
+
+     %50 = OpLabel
+     OpBranch %20
+
+     %99 = OpLabel
+     OpReturn
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 40, 45, 49, 50, 99))
+      << assembly;
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_If_Break) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpSelectionMerge %49 None
+     OpBranchConditional %cond2 %40 %49 ; nested if
+
+     %40 = OpLabel
+     OpBranch %99   ; break from nested if
+
+     %49 = OpLabel
+     OpBranch %50
+
+     %50 = OpLabel
+     OpBranch %20
+
+     %99 = OpLabel
+     OpReturn
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 40, 49, 50, 99)) << assembly;
+}
+
 TEST_F(SpvParserTest, ComputeBlockOrder_Loop_BodyHasContinueIf) {
   auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -969,6 +1047,160 @@
   EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 40, 50, 99)) << assembly;
 }
 
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_If_Continue) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpSelectionMerge %49 None
+     OpBranchConditional %cond2 %40 %49 ; nested if
+
+     %40 = OpLabel
+     OpBranch %50   ; continue from nested if
+
+     %49 = OpLabel
+     OpBranch %50
+
+     %50 = OpLabel
+     OpBranch %20
+
+     %99 = OpLabel
+     OpReturn
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 40, 49, 50, 99)) << assembly;
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_Switch) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpSelectionMerge %49 None
+     OpSwitch %selector %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
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
+      << assembly;
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_Switch_CaseBreaks) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpSelectionMerge %49 None
+     OpSwitch %selector %49 40 %40 45 %45
+
+     %40 = OpLabel
+     ; This case breaks out of the loop. This is not possible in C
+     ; because "break" will escape the switch only.
+     OpBranch %99
+
+     %45 = OpLabel
+     OpBranch %49
+
+     %49 = OpLabel
+     OpBranch %50
+
+     %50 = OpLabel
+     OpBranch %20
+
+     %99 = OpLabel
+     OpReturn
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
+      << assembly;
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Body_Switch_CaseContinues) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpSelectionMerge %49 None
+     OpSwitch %selector %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
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
+      << assembly;
+}
+
 TEST_F(SpvParserTest, ComputeBlockOrder_Loop_BodyHasSwitchContinueBreak) {
   auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
@@ -997,7 +1229,7 @@
   FunctionEmitter fe(p, *spirv_function(100));
   fe.ComputeBlockOrderAndPositions();
 
-  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 99));
 }
 
 TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_Sequence) {
@@ -1028,7 +1260,7 @@
   FunctionEmitter fe(p, *spirv_function(100));
   fe.ComputeBlockOrderAndPositions();
 
-  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 60, 99)) << assembly;
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 60, 99));
 }
 
 TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_ContainsIf) {
@@ -1062,11 +1294,11 @@
      OpReturn
   )";
   auto* p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly << p->error();
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   FunctionEmitter fe(p, *spirv_function(100));
   fe.ComputeBlockOrderAndPositions();
 
-  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 60, 70, 89, 99)) << assembly;
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 60, 70, 89, 99));
 }
 
 TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_HasBreakIf) {
@@ -1094,7 +1326,7 @@
   FunctionEmitter fe(p, *spirv_function(100));
   fe.ComputeBlockOrderAndPositions();
 
-  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 99));
 }
 
 TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_HasBreakUnless) {
@@ -1122,7 +1354,7 @@
   FunctionEmitter fe(p, *spirv_function(100));
   fe.ComputeBlockOrderAndPositions();
 
-  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 99));
 }
 
 TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Continue_SwitchBreak) {
@@ -1150,7 +1382,258 @@
   FunctionEmitter fe(p, *spirv_function(100));
   fe.ComputeBlockOrderAndPositions();
 
-  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 50, 99));
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpLoopMerge %49 %40 None
+     OpBranchConditional %cond2 %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
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_InnerBreak) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpLoopMerge %49 %40 None
+     OpBranchConditional %cond2 %35 %49
+
+     %35 = OpLabel
+     OpBranchConditional %cond3 %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
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_InnerContinue) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpLoopMerge %49 %40 None
+     OpBranchConditional %cond2 %35 %49
+
+     %35 = OpLabel
+     OpBranchConditional %cond3 %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
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_InnerContinueBreaks) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpLoopMerge %49 %40 None
+     OpBranchConditional %cond2 %35 %49
+
+     %35 = OpLabel
+     OpBranch %37
+
+     %37 = OpLabel
+     OpBranch %40
+
+     %40 = OpLabel ; inner loop's continue
+     OpBranchConditional %cond3 %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
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_InnerContinueContinues) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpLoopMerge %49 %40 None
+     OpBranchConditional %cond2 %35 %49
+
+     %35 = OpLabel
+     OpBranch %37
+
+     %37 = OpLabel
+     OpBranch %40
+
+     %40 = OpLabel ; inner loop's continue
+     OpBranchConditional %cond3 %30 %50 ; backedge and continue to outer
+
+     %49 = OpLabel ; inner loop's merge
+     OpBranch %50
+
+     %50 = OpLabel ; outer loop's continue
+     OpBranch %20 ; outer loop's backege
+
+     %99 = OpLabel
+     OpReturn
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
+}
+
+TEST_F(SpvParserTest, ComputeBlockOrder_Loop_Loop_SwitchBackedgeBreakContinue) {
+  auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+
+     %10 = OpLabel
+     OpBranch %20
+
+     %20 = OpLabel
+     OpLoopMerge %99 %50 None
+     OpBranchConditional %cond %30 %99
+
+     %30 = OpLabel
+     OpLoopMerge %49 %40 None
+     OpBranchConditional %cond2 %35 %49
+
+     %35 = OpLabel
+     OpBranch %37
+
+     %37 = OpLabel
+     OpBranch %40
+
+     %40 = OpLabel ; inner loop's continue
+     ; This switch does triple duty:
+     ; default -> backedge
+     ; 49 -> loop break
+     ; 49 -> inner loop break
+     ; 50 -> outer loop continue
+     OpSwitch %selector %30 49 %49 50 %50
+
+     %49 = OpLabel ; inner loop's merge
+     OpBranch %50
+
+     %50 = OpLabel ; outer loop's continue
+     OpBranch %20 ; outer loop's backege
+
+     %99 = OpLabel
+     OpReturn
+  )";
+  auto* p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  FunctionEmitter fe(p, *spirv_function(100));
+  fe.ComputeBlockOrderAndPositions();
+
+  EXPECT_THAT(fe.rspo(), ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
 }
 
 }  // namespace