vulkancontrol-flowcontrol-flow-graphspir-v

Clarification on what exactly constitutes as a continue target in Vulkan SPIR-V


I'm trying to understand the SPIR-V spec, understanding for loops for example. In the beginning of the 1.6 revision 2 spec, the following example (taking only part of it for consisveness)

for (int i = 0; i < 4; ++i)
    color *= multiplier;

turns into:

%49 = OpLabel
      OpLoopMerge %51 %52 None ; structured loop
      OpBranch %53
%53 = OpLabel
%54 = OpLoad %16 %48
%56 = OpSLessThan %25 %54 %55 ; i < 4 ?
      OpBranchConditional %56 %50 %51 ; body or break
%50 = OpLabel ; body
%58 = OpLoad %7 %57
%59 = OpLoad %7 %31
%60 = OpFMul %7 %59 %58
      OpStore %31 %60
      OpBranch %52
%52 = OpLabel ; continue target
%61 = OpLoad %16 %48
%62 = OpIAdd %16 %61 %21 ; ++i
      OpStore %48 %62
      OpBranch %49 ; loop back
%51 = OpLabel ; loop merge point
      OpReturn
      OpFunctionEnd

In the spec, it specifies a block starts with a label, and ends with a any branch termination instruction. This turns the above into the following:

;Block 1
%49 = OpLabel
      OpLoopMerge %51 %52 None ; structured loop
      OpBranch %53

;Block 2
%53 = OpLabel
%54 = OpLoad %16 %48
%56 = OpSLessThan %25 %54 %55 ; i < 4 ?
      OpBranchConditional %56 %50 %51 ; body or break

;Block 3
%50 = OpLabel ; body
%58 = OpLoad %7 %57
%59 = OpLoad %7 %31
%60 = OpFMul %7 %59 %58
      OpStore %31 %60
      OpBranch %52

;Block 4
%52 = OpLabel ; continue target
%61 = OpLoad %16 %48
%62 = OpIAdd %16 %61 %21 ; ++i
      OpStore %48 %62
      OpBranch %49 ; loop back

;Block 5
%51 = OpLabel ; loop merge point
      OpReturn

      OpFunctionEnd

Then the spec states:

Header Block: A block containing a merge instruction.

Loop Header: A header block whose merge instruction is an OpLoopMerge

So the loop header here is block 1, where OpLoopMerge is located.

OpLoopMerge claims three parameters, two which matter here:

Merge Block is the label of the merge block for this structured loop.

Continue Target is the label of a block targeted for processing a loop "continue".

Merge block is

Merge Block: A block declared by the Merge Block operand of a merge instruction.

Which my understanding is when the "structured loop/selection" ends, in this case it's right at the end of the function, block 5. This then makes sense to me.

The Continue target is what isn't clear to me. The continue target here is block 4, according to the op loop merge instruction. Why is the continue target not block 3? or block 2? What makes block 4 the continue target here? Is it because it's the block where the final decision is made to exit or continue the loop? is that what is meant by "processing a loop "continue"?


Solution

  • The "continue target" exists to serve the needs of the continue control flow instruction in most languages. In higher-level languages, in a loop, you can issue a continue statement. This jumps to the next iteration in the loop.

    The "continue target" is the part of the loop code that starts the next iteration. You either branch here directly from a continue statement, or you branch here from the end of the body of the loop.

    If the source contained a continue statement, that would be part of the loop's body, which in your example is Block 3. So there'd be no reason for that to be the continue target; that would cause an infinite loop, since the actual terminal condition is never checked, nor is the iteration expression evaluated.

    And Block 2 also makes no sense. When you continue a loop, you must execute the iteration expression. Repeating the terminal condition wouldn't help, because it obviously already passed. That's how it got into the loop body to begin with. Until the iteration expression is evaluated, the terminal condition will continue to pass. And again, an infinite loop would result.