spirv-reader: disallow OpSwitch without preceding OpSelectionMerge
This was updated/clarified by the SPIR WG.
Bug: tint:3
Change-Id: Ie4c503f0e5f80ffeabada9c526375588e81a5ceb
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/45740
Auto-Submit: David Neto <dneto@google.com>
Commit-Queue: Alan Baker <alanbaker@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Baker <alanbaker@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 0b03991..09eaa5b 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -2621,8 +2621,14 @@
return true;
}
case SpvOpSwitch:
- // TODO(dneto)
- break;
+ // An OpSelectionMerge must precede an OpSwitch. That is clarified
+ // in the resolution to Khronos-internal SPIR-V issue 115.
+ // A new enough version of the SPIR-V validator checks this case.
+ // But issue an error in this case, as a defensive measure.
+ return Fail() << "invalid structured control flow: found an OpSwitch "
+ "that is not preceded by an "
+ "OpSelectionMerge: "
+ << terminator.PrettyPrint();
default:
break;
}
diff --git a/src/reader/spirv/function_cfg_test.cc b/src/reader/spirv/function_cfg_test.cc
index d5a5756..62ef821 100644
--- a/src/reader/spirv/function_cfg_test.cc
+++ b/src/reader/spirv/function_cfg_test.cc
@@ -13542,9 +13542,31 @@
"a structured header (it has no merge instruction)"));
}
+TEST_F(SpvParserTest, Switch_NotAsSelectionHeader_Simple) {
+ auto assembly = CommonTypes() + R"(
+ %100 = OpFunction %void None %voidfn
+
+ %10 = OpLabel
+ OpSwitch %uint_0 %99
+
+ %99 = OpLabel
+ OpReturn
+
+ OpFunctionEnd
+)";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+ FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+ EXPECT_FALSE(fe.EmitBody());
+ EXPECT_THAT(
+ p->error(),
+ HasSubstr("invalid structured control flow: found an OpSwitch that "
+ "is not preceded by an OpSelectionMerge:"));
+}
+
TEST_F(SpvParserTest,
- DISABLED_Switch_NotAsSelectionHeader_NonDefaultBranchesAreContinue) {
- // Adapted from SPIRV-Tools test MissingMergeOneUnseenTargetSwitchGood
+ Switch_NotAsSelectionHeader_NonDefaultBranchesAreContinue) {
+ // Adapted from SPIRV-Tools test MissingMergeOneUnseenTargetSwitchBad
auto p = parser(test::Assemble(CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
%entry = OpLabel
@@ -13571,15 +13593,15 @@
)"));
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
- EXPECT_TRUE(fe.EmitBody()) << p->error();
- auto got = ToString(p->builder(), fe.ast_body());
- auto* expect = "unhandled case";
- ASSERT_EQ(expect, got);
+ EXPECT_FALSE(fe.EmitBody());
+ EXPECT_THAT(
+ p->error(),
+ HasSubstr("invalid structured control flow: found an OpSwitch that "
+ "is not preceded by an OpSelectionMerge:"));
}
-TEST_F(SpvParserTest,
- DISABLED_Switch_NotAsSelectionHeader_DefaultBranchIsContinue) {
- // Adapted from SPIRV-Tools test MissingMergeOneUnseenTargetSwitchGood
+TEST_F(SpvParserTest, Switch_NotAsSelectionHeader_DefaultBranchIsContinue) {
+ // Adapted from SPIRV-Tools test MissingMergeOneUnseenTargetSwitchBad
auto p = parser(test::Assemble(CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
%entry = OpLabel
@@ -13606,10 +13628,11 @@
)"));
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
- EXPECT_TRUE(fe.EmitBody()) << p->error();
- auto got = ToString(p->builder(), fe.ast_body());
- auto* expect = "unhandled case";
- ASSERT_EQ(expect, got);
+ EXPECT_FALSE(fe.EmitBody());
+ EXPECT_THAT(
+ p->error(),
+ HasSubstr("invalid structured control flow: found an OpSwitch that "
+ "is not preceded by an OpSelectionMerge:"));
}
TEST_F(SpvParserTest, SiblingLoopConstruct_Null) {