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