spirv-val: Fix validation of OpPhi instructions (#3919)
Fixes #3918.
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index 8eb3a96..8babd35 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -89,6 +89,8 @@
<< block->predecessors()->size() << ").";
}
+ std::unordered_set<uint32_t> observed_predecessors;
+
for (size_t i = 3; i < inst->words().size(); ++i) {
auto inc_id = inst->word(i);
if (i % 2 == 1) {
@@ -115,6 +117,17 @@
<< " is not a predecessor of <id> " << _.getIdName(block->id())
<< ".";
}
+
+ // We must not have already seen this predecessor as one of the phi's
+ // operands.
+ if (observed_predecessors.count(inc_id) != 0) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "OpPhi references incoming basic block <id> "
+ << _.getIdName(inc_id) << " multiple times.";
+ }
+
+ // Note the fact that we have now observed this predecessor.
+ observed_predecessors.insert(inc_id);
}
}
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index 247a91f..b4d1c28 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -4565,6 +4565,39 @@
ASSERT_FALSE(b11->reachable());
}
+TEST_F(ValidateCFG, PhiInstructionWithDuplicateIncomingEdges) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %10 None
+ OpBranchConditional %7 %8 %9
+ %8 = OpLabel
+ OpBranch %10
+ %9 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ %11 = OpPhi %6 %7 %8 %7 %8
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpPhi references incoming basic block <id> "));
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("multiple times."));
+}
+
} // namespace
} // namespace val
} // namespace spvtools