Repository: KhronosGroup/SPIRV-Tools (language: python)

Issue:
Unexpected exception about a block branching to a loop continue target
## Description

The Vulkan validation layer throws an exception when running a fragment shader program:

```txt
Block '34[%34]' branches to the loop continue target '31[%31]', but is not contained in the associated loop construct '20[%20]'
  OpBranch %31
```

The fragment shader is well-formed and does not contain undefined behavior. The shader can be compiled without any warning or errors by the `glslangValidator` tool. However, when it is executed with Vulkan validation layer enabled, the exception above is thrown. Also, the `spirv-val` tool reports the same error message when validating the optimized `spirv` binary.


## Steps to reproduce

I prepare a minimal Vulkan application that loads the fragment shader to draw a 256x256 white square. The application source code, fragment shader that triggers the error, and the disassembled spirv results are given [here](https://drive.proton.me/urls/0YWDBCNKJ0#tlReYm3fVhkI). The package also contains a detailed README file describing system information and steps to run the code. It should take no more than a minute to reproduce the issue.



Discussion / hints:
FYI: In this case, the validation layer is simply calling spirv-val to validate the spir-v. All that is needed is the glsl, spir-v before optimization, and the command used to optimize the code. Then you can run spirv-val from the command line.

In any case, thanks for providing thorough instructions on how to reproduce.

Fix patch:
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index d401a2e7b7..77e4f4f2fd 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -839,6 +839,9 @@ spv_result_t StructuredControlFlowChecks(
       const auto* continue_target = next_inst.block();
       if (header->id() != continue_id) {
         for (auto pred : *continue_target->predecessors()) {
+          if (!pred->structurally_reachable()) {
+            continue;
+          }
           // Ignore back-edges from within the continue construct.
           bool is_back_edge = false;
           for (auto back_edge : back_edges) {


Test patch:
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index 233aee645d..d3b5e90209 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -5118,6 +5118,43 @@ OpFunctionEnd
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateCFG, StructurallyUnreachableContinuePredecessor) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource ESSL 310
+               OpName %main "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+     %int_n7 = OpConstant %int -7
+       %bool = OpTypeBool
+       %main = OpFunction %void None %3
+          %8 = OpLabel
+               OpBranch %9
+          %9 = OpLabel
+         %10 = OpPhi %int %int_1 %8 %int_n7 %15
+         %12 = OpSGreaterThan %bool %10 %int_n7
+               OpLoopMerge %13 %15 None
+               OpBranchConditional %12 %14 %13
+         %14 = OpLabel
+               OpBranch %15
+         %15 = OpLabel
+               OpBranch %9
+         %17 = OpLabel
+               OpBranch %15
+         %13 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools