spirv-reader: CFG tests: make valid SPIR-V
Bug: tint:765
Change-Id: I84ea018478feafc4a5c03052832acae8cf3f15b9
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49940
Commit-Queue: David Neto <dneto@google.com>
Reviewed-by: Alan Baker <alanbaker@google.com>
diff --git a/src/reader/spirv/function_cfg_test.cc b/src/reader/spirv/function_cfg_test.cc
index 7217d3b..8889b2a 100644
--- a/src/reader/spirv/function_cfg_test.cc
+++ b/src/reader/spirv/function_cfg_test.cc
@@ -46,6 +46,8 @@
return R"(
OpCapability Shader
OpMemoryModel Logical Simple
+ OpEntryPoint Fragment %100 "main"
+ OpExecutionMode %100 OriginUpperLeft
OpName %var "var"
@@ -1078,13 +1080,17 @@
%100 = OpFunction %void None %voidfn
%10 = OpLabel
- OpBranch %20
+ OpSelectionMerge %99 None
+ OpBranchConditional %cond %20 %30
%30 = OpLabel
OpReturn
%20 = OpLabel
- OpBranch %30 ; backtrack
+ OpBranch %30 ; backtrack, but does dominate %30
+
+ %99 = OpLabel
+ OpReturn
OpFunctionEnd
)"));
@@ -1093,7 +1099,7 @@
fe.RegisterBasicBlocks();
fe.ComputeBlockOrderAndPositions();
- EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30));
+ EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 99));
const auto* bi10 = fe.GetBlockInfo(10);
ASSERT_NE(bi10, nullptr);
@@ -1104,6 +1110,9 @@
const auto* bi30 = fe.GetBlockInfo(30);
ASSERT_NE(bi30, nullptr);
EXPECT_EQ(bi30->pos, 2u);
+ const auto* bi99 = fe.GetBlockInfo(99);
+ ASSERT_NE(bi99, nullptr);
+ EXPECT_EQ(bi99->pos, 3u);
}
TEST_F(SpvParserCFGTest, ComputeBlockOrder_DupConditionalBranch) {
@@ -1114,12 +1123,12 @@
OpSelectionMerge %99 None
OpBranchConditional %cond %20 %20
- %99 = OpLabel
- OpReturn
-
%20 = OpLabel
OpBranch %99
+ %99 = OpLabel
+ OpReturn
+
OpFunctionEnd
)"));
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
@@ -1138,15 +1147,15 @@
OpSelectionMerge %99 None
OpBranchConditional %cond %20 %30
- %99 = OpLabel
- OpReturn
-
%30 = OpLabel
OpReturn
%20 = OpLabel
OpBranch %99
+ %99 = OpLabel ; dominated by %20, so follow %20
+ OpReturn
+
OpFunctionEnd
)"));
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
@@ -1299,7 +1308,9 @@
%10 = OpLabel
OpSelectionMerge %99 None
- OpSwitch %selector %99 20 %20 30 %30 40 %40 50 %50
+ ; SPIR-V validation requires a fallthrough destination to immediately
+ ; follow the source. So %20 -> %40, %30 -> %50
+ OpSwitch %selector %99 20 %20 40 %40 30 %30 50 %50
%50 = OpLabel
OpBranch %99
@@ -1373,9 +1384,6 @@
OpSelectionMerge %99 None
OpSwitch %selector %80 20 %20 30 %30
- %99 = OpLabel
- OpReturn
-
%20 = OpLabel
OpBranch %80 ; fallthrough to default
@@ -1385,6 +1393,9 @@
%30 = OpLabel
OpBranch %99
+ %99 = OpLabel ; dominated by %30, so follow %30
+ OpReturn
+
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
@@ -1432,6 +1443,9 @@
EXPECT_THAT(fe.block_order(), ElementsAre(10, 50, 40, 20, 30, 99))
<< assembly;
+
+ // We're deliberately testing a case that SPIR-V doesn't allow.
+ p->DeliberatelyInvalidSpirv();
}
TEST_F(SpvParserCFGTest,
@@ -1441,7 +1455,9 @@
%10 = OpLabel
OpSelectionMerge %99 None
- OpSwitch %selector %99 20 %20 30 %30 40 %40 50 %50
+ ; SPIR-V validation requires a fallthrough destination to immediately
+ ; follow the source. So %20 -> %40
+ OpSwitch %selector %99 20 %20 40 %40 30 %30 50 %50
%99 = OpLabel
OpReturn
@@ -1641,22 +1657,22 @@
OpSelectionMerge %49 None
OpBranchConditional %cond %99 %40 ; break-if
- %49 = OpLabel
- OpBranch %99
-
%40 = OpLabel
OpBranch %49
+ %49 = OpLabel
+ OpBranch %99
+
%50 = OpLabel
OpSelectionMerge %79 None
OpBranchConditional %cond %60 %99 ; break-unless
- %79 = OpLabel
- OpBranch %99
-
%60 = OpLabel
OpBranch %79
+ %79 = OpLabel ; dominated by 60, so must follow 60
+ OpBranch %99
+
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
@@ -2184,6 +2200,11 @@
EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
<< assembly;
+
+ // Fails SPIR-V validation:
+ // Branch from block 40 to block 99 is an invalid exit from construct starting
+ // at block 30; branch bypasses merge block 49
+ p->DeliberatelyInvalidSpirv();
}
TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Body_Switch_CaseContinues) {
@@ -2240,6 +2261,7 @@
OpBranchConditional %cond %30 %99
%30 = OpLabel
+ ; OpSwitch must be preceded by a selection merge
OpSwitch %selector %99 50 %50 ; default is break, 50 is continue
%40 = OpLabel
@@ -2254,12 +2276,10 @@
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
- ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
- auto fe = p->function_emitter(100);
- fe.RegisterBasicBlocks();
- fe.ComputeBlockOrderAndPositions();
-
- EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99));
+ EXPECT_FALSE(p->Parse());
+ EXPECT_FALSE(p->success());
+ EXPECT_THAT(p->error(),
+ HasSubstr("OpSwitch must be preceeded by an OpSelectionMerge"));
}
TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Continue_Sequence) {
@@ -2414,7 +2434,9 @@
OpBranch %50
%50 = OpLabel
- OpSwitch %selector %20 99 %99 ; yes, this is obtuse but valid
+ ; Updated SPIR-V rule:
+ ; OpSwitch must be preceded by a selection.
+ OpSwitch %selector %20 99 %99
%99 = OpLabel
OpReturn
@@ -2422,12 +2444,10 @@
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
- ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
- auto fe = p->function_emitter(100);
- fe.RegisterBasicBlocks();
- fe.ComputeBlockOrderAndPositions();
-
- EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99));
+ EXPECT_FALSE(p->Parse());
+ EXPECT_FALSE(p->success());
+ EXPECT_THAT(p->error(),
+ HasSubstr("OpSwitch must be preceeded by an OpSelectionMerge"));
}
TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Loop) {
@@ -2653,6 +2673,11 @@
EXPECT_THAT(fe.block_order(),
ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
+
+ p->DeliberatelyInvalidSpirv();
+ // SPIR-V validation fails:
+ // block <ID> 40[%40] exits the continue headed by <ID> 40[%40], but not
+ // via a structured exit"
}
TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Loop_SwitchBackedgeBreakContinue) {
@@ -2703,6 +2728,11 @@
EXPECT_THAT(fe.block_order(),
ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
+
+ p->DeliberatelyInvalidSpirv();
+ // SPIR-V validation fails:
+ // block <ID> 40[%40] exits the continue headed by <ID> 40[%40], but not
+ // via a structured exit"
}
TEST_F(SpvParserCFGTest, VerifyHeaderContinueMergeOrder_Selection_Good) {
@@ -3178,7 +3208,7 @@
%20 = OpLabel
OpLoopMerge %99 %20 None
- OpBranchConditional %cond %30 %99
+ OpBranch %30
%30 = OpLabel
OpBranch %40
@@ -3187,7 +3217,7 @@
OpBranch %50
%50 = OpLabel
- OpBranch %20
+ OpBranchConditional %cond %20 %99
%99 = OpLabel
OpReturn
@@ -3487,7 +3517,7 @@
%20 = OpLabel
OpLoopMerge %89 %50 None
- OpBranchConditional %cond %30 %99
+ OpBranchConditional %cond %30 %89
%30 = OpLabel ; single block loop
OpLoopMerge %40 %30 None
@@ -3650,7 +3680,7 @@
%20 = OpLabel
OpLoopMerge %89 %20 None
- OpBranchConditional %cond %20 %99
+ OpBranchConditional %cond %20 %89
%89 = OpLabel
OpBranch %99
@@ -3702,7 +3732,7 @@
OpBranch %20
%89 = OpLabel ; merge for the loop
- OpBranch %20
+ OpBranch %99
%99 = OpLabel ; merge for the if
OpReturn
@@ -4050,6 +4080,9 @@
fe.RegisterMerges();
fe.LabelControlFlowConstructs();
EXPECT_TRUE(fe.FindSwitchCaseHeaders());
+
+ // TODO(crbug.com/tint/774) Re-enable after codegen bug fixed.
+ p->DeliberatelyInvalidSpirv();
}
TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_CaseCantEscapeSwitch) {
@@ -7493,6 +7526,10 @@
EXPECT_EQ(bi20->succ_edge[99], EdgeKind::kIfBreak);
EXPECT_THAT(p->error(), Eq(""));
+
+ // TODO(crbug.com/tint/775): The SPIR-V reader errors out on this case.
+ // Remove this when it's fixed.
+ p->DeliberatelyInvalidSpirv();
}
TEST_F(
@@ -10460,7 +10497,7 @@
TEST_F(SpvParserCFGTest, EmitBody_ReturnValue_Loop) {
auto p = parser(test::Assemble(CommonTypes() + R"(
- %200 = OpFunction %void None %voidfn
+ %200 = OpFunction %uint None %uintfn
%210 = OpLabel
OpBranch %220
@@ -11002,8 +11039,9 @@
"(violates post-dominance rule)"));
}
-TEST_F(SpvParserCFGTest,
- EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd) {
+TEST_F(
+ SpvParserCFGTest,
+ EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Unconditional) {
auto p = parser(test::Assemble(CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@@ -11017,6 +11055,52 @@
%80 = OpLabel ; continue target
OpStore %var %uint_1
OpBranch %99 ; should be a backedge
+ ; This is invalid as there must be a backedge to the loop header.
+ ; The SPIR-V allows this and understands how to emit it, even if it's not
+ ; permitted by the SPIR-V validator.
+
+ %99 = OpLabel
+ OpReturn
+
+ OpFunctionEnd
+ )"));
+
+ p->DeliberatelyInvalidSpirv();
+
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto got = ToString(p->builder(), fe.ast_body());
+ auto* expect = R"(Loop{
+ continuing {
+ Assignment{
+ Identifier[not set]{var_1}
+ ScalarConstructor[not set]{1u}
+ }
+ Break{}
+ }
+}
+Return{}
+)";
+ ASSERT_EQ(expect, got);
+}
+
+TEST_F(
+ SpvParserCFGTest,
+ EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional) {
+ auto p = parser(test::Assemble(CommonTypes() + R"(
+ %100 = OpFunction %void None %voidfn
+
+ %10 = OpLabel
+ OpBranch %20
+
+ %20 = OpLabel
+ OpLoopMerge %99 %80 None
+ OpBranch %80
+
+ %80 = OpLabel ; continue target
+ OpStore %var %uint_1
+ OpBranchConditional %cond %20 %99 ; backedge, and exit
%99 = OpLabel
OpReturn
@@ -11033,7 +11117,18 @@
Identifier[not set]{var_1}
ScalarConstructor[not set]{1u}
}
- Break{}
+ If{
+ (
+ ScalarConstructor[not set]{false}
+ )
+ {
+ }
+ }
+ Else{
+ {
+ Break{}
+ }
+ }
}
}
Return{}
@@ -14198,10 +14293,10 @@
%20 = OpLabel
OpLoopMerge %99 %20 None ; continue target is also loop header
- OpBranchConditional %cond %30 %99
+ OpBranch %30
%30 = OpLabel
- OpBranch %20
+ OpBranchConditional %cond %20 %99
%99 = OpLabel
OpReturn