<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>I'd like to update the ML on control-flow progress and
      demonstrate some problems.<br>
    </p>
    <p>The WIP repo is here:
      <a class="moz-txt-link-freetext" href="https://github.com/HansKristian-Work/DXIL2SPIRV">https://github.com/HansKristian-Work/DXIL2SPIRV</a>. I'm developing it
      as a standalone module for time being.</p>
    <p>Control flow in DXIL is a complicated beast as it's a soup of
      gotos, as it is LLVM. The only saving grace is that it must be
      reducible, i.e. no branching straight into a loop, or arbitrary
      backward gotos.</p>
    <p>The main problem with emitting SPIR-V is:</p>
    <p>- For every conditional branch we need a selection merge
      construct with a unique merge block which header dominates.<br>
      - For every loop header, we need a loop merge with designated
      continue block and unique merge block which header dominates.<br>
      - Cannot break out of more than one loop construct at a time
      (guess what DXIL does!).<br>
    </p>
    <p>The main complication currently is that we need ladder breaking.
      Here's a concrete example:</p>
    <p><font size="-1">cbuffer Buff : register(b10, space1)<br>
        {<br>
            int count1;<br>
            int count2;<br>
            int data[1024];<br>
        };<br>
        <br>
        float get_r()<br>
        {<br>
            float r = 0.0;<br>
            [loop]<br>
            for (int i = 0; i < count1; i++)<br>
            {<br>
                [loop]<br>
                for (int j = 0; j < count2; j++)<br>
                {<br>
                    if (data[i ^ j] == 40)<br>
                        return r;                // <-- goto end;
        when inlined<br>
                    r += float(data[i ^ j]);<br>
                }<br>
            }<br>
            return r;<br>
        }<br>
        <br>
        float4 main(float4 pos : POSITION, float4 pos2 : COLOR) :
        SV_Position<br>
        {<br>
            float r = get_r();<br>
            return r.xxxx;<br>
        }</font></p>
    <p>This gets compiled into:</p>
    <p>...<br>
    </p>
    <p><font size="-1">define void @main() {<br>
          %Buff_cbuffer = call %dx.types.Handle @dx.op.createHandle(i32
        57, i8 2, i32 0, i32 10, i1 false)  ;
        CreateHandle(resourceClass,rangeId,index,nonUniformIndex)<br>
          %1 = call %dx.types.CBufRet.i32
        @dx.op.cbufferLoadLegacy.i32(i32 59, %dx.types.Handle
        %Buff_cbuffer, i32 0)  ; CBufferLoadLegacy(handle,regIndex)<br>
          %2 = extractvalue %dx.types.CBufRet.i32 %1, 0<br>
          %3 = icmp sgt i32 %2, 0<br>
          br i1 %3, label %.lr.ph2.preheader, label
        %<a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit">"\01?get_r@@YAMXZ.exit"</a><br>
        <br>
        .lr.ph2.preheader:                                ; preds = %0<br>
          br label %.lr.ph2<br>
        <br>
        .lr.ph2:                                          ; preds =
        %._crit_edge, %.lr.ph2.preheader<br>
          %i.i.0 = phi i32 [ %19, %._crit_edge ], [ 0,
        %.lr.ph2.preheader ]<br>
          %r.i.0 = phi float [ %r.i.2, %._crit_edge ], [ 0.000000e+00,
        %.lr.ph2.preheader ]<br>
          %4 = call %dx.types.CBufRet.i32
        @dx.op.cbufferLoadLegacy.i32(i32 59, %dx.types.Handle
        %Buff_cbuffer, i32 0)  ; CBufferLoadLegacy(handle,regIndex)<br>
          %5 = extractvalue %dx.types.CBufRet.i32 %4, 1<br>
          %6 = icmp sgt i32 %5, 0<br>
          br i1 %6, label %.lr.ph.preheader, label %._crit_edge<br>
        <br>
        .lr.ph.preheader:                                 ; preds =
        %.lr.ph2<br>
          br label %.lr.ph<br>
        <br>
        .lr.ph:                                           ; preds = %12,
        %.lr.ph.preheader<br>
          %j.i.0 = phi i32 [ %15, %12 ], [ 0, %.lr.ph.preheader ]<br>
          %r.i.1 = phi float [ %14, %12 ], [ %r.i.0, %.lr.ph.preheader ]<br>
          %7 = xor i32 %j.i.0, %i.i.0<br>
          %8 = add i32 %7, 1<br>
          %9 = call %dx.types.CBufRet.i32
        @dx.op.cbufferLoadLegacy.i32(i32 59, %dx.types.Handle
        %Buff_cbuffer, i32 %8)  ; CBufferLoadLegacy(handle,regIndex)<br>
          %10 = extractvalue %dx.types.CBufRet.i32 %9, 0<br>
          %11 = icmp eq i32 %10, 40<br>
          br i1 %11, label %<a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit.loopexit">"\01?get_r@@YAMXZ.exit.loopexit"</a>, label %12<br>
        <br>
        ; <label>:12                                      ; preds
        = %.lr.ph<br>
          %13 = sitofp i32 %10 to float<br>
          %14 = fadd fast float %13, %r.i.1<br>
          %15 = add nuw nsw i32 %j.i.0, 1<br>
          %16 = call %dx.types.CBufRet.i32
        @dx.op.cbufferLoadLegacy.i32(i32 59, %dx.types.Handle
        %Buff_cbuffer, i32 0)  ; CBufferLoadLegacy(handle,regIndex)<br>
          %17 = extractvalue %dx.types.CBufRet.i32 %16, 1<br>
          %18 = icmp slt i32 %15, %17<br>
          br i1 %18, label %.lr.ph, label %._crit_edge.loopexit,
        !llvm.loop !25<br>
        <br>
        ._crit_edge.loopexit:                             ; preds = %12<br>
          br label %._crit_edge<br>
        <br>
        ._crit_edge:                                      ; preds =
        %._crit_edge.loopexit, %.lr.ph2<br>
          %r.i.2 = phi float [ %r.i.0, %.lr.ph2 ], [ %14,
        %._crit_edge.loopexit ]<br>
          %19 = add nuw nsw i32 %i.i.0, 1<br>
          %20 = call %dx.types.CBufRet.i32
        @dx.op.cbufferLoadLegacy.i32(i32 59, %dx.types.Handle
        %Buff_cbuffer, i32 0)  ; CBufferLoadLegacy(handle,regIndex)<br>
          %21 = extractvalue %dx.types.CBufRet.i32 %20, 0<br>
          %22 = icmp slt i32 %19, %21<br>
          br i1 %22, label %.lr.ph2, label
        %<a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit.loopexit.11">"\01?get_r@@YAMXZ.exit.loopexit.11"</a>, !llvm.loop !27<br>
        <br>
        <a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit.loopexit">"\01?get_r@@YAMXZ.exit.loopexit"</a>:                 ; preds =
        %.lr.ph<br>
          br label %<a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit">"\01?get_r@@YAMXZ.exit"</a><br>
        <br>
        <a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit.loopexit.11">"\01?get_r@@YAMXZ.exit.loopexit.11"</a>:              ; preds =
        %._crit_edge<br>
          br label %<a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit">"\01?get_r@@YAMXZ.exit"</a><br>
        <br>
        <a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit">"\01?get_r@@YAMXZ.exit"</a>:                          ; preds =
        %<a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit.loopexit.11">"\01?get_r@@YAMXZ.exit.loopexit.11"</a>,
        %<a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit.loopexit">"\01?get_r@@YAMXZ.exit.loopexit"</a>, %0<br>
          %.0 = phi float [ 0.000000e+00, %0 ], [ %r.i.1,
        %<a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit.loopexit">"\01?get_r@@YAMXZ.exit.loopexit"</a> ], [ %r.i.2,
        %<a class="moz-txt-link-rfc2396E" href="mailto:\01?get_r@@YAMXZ.exit.loopexit.11">"\01?get_r@@YAMXZ.exit.loopexit.11"</a> ]<br>
          call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0,
        float %.0)  ; StoreOutput(outputSigId,rowIndex,colIndex,value)<br>
          call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 1,
        float %.0)  ; StoreOutput(outputSigId,rowIndex,colIndex,value)<br>
          call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 2,
        float %.0)  ; StoreOutput(outputSigId,rowIndex,colIndex,value)<br>
          call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 3,
        float %.0)  ; StoreOutput(outputSigId,rowIndex,colIndex,value)<br>
          ret void<br>
        }</font><br>
    </p>
    <p>...</p>
    <p>The "return r;" inside the inner loop turns into a full "exit".
      This is a very common pattern where inlined leaf functions return.
      For a normal HLSL -> SPIR-V path, spirv-opt carefully
      introduces ladders to convert a return into a chain of breaks. We
      have no such luxury in DXIL. Basically, the return becomes a
      forward goto.</p>
    <p>After a lot of implementation weirdness, I can turn this into
      SPIR-V which validates. Only control flow is emitted since I
      haven't looked at actual codegen. The resulting GLSL from
      SPIRV-Cross ends up looking something like:</p>
    <p><font size="-1">#version 450<br>
        <br>
        void main()<br>
        {<br>
            bool COND1;<br>
            if (COND1)<br>
            {<br>
                bool _lr_ph2_preheader;<br>
                bool _lr_ph2;<br>
                bool _lr_ph2_succ;<br>
                bool _lr_ph_preheader;<br>
                bool _lr_ph;<br>
                bool COND11;<br>
                bool _get_r_YAMXZ_exit_loopexit;<br>
                bool _crit_edge_loopexit_pred;<br>
                bool _crit_edge_loopexit;<br>
                bool _crit_edge;<br>
                do<br>
                {<br>
                    if (_lr_ph2_succ)<br>
                    {<br>
                        do<br>
                        {<br>
                            if (_lr_ph)<br>
                            {<br>
                                break; // <-- goto end;<br>
                            }<br>
                            else<br>
                            {<br>
                            }<br>
                        } while (!COND11);<br>
                        if (!_crit_edge_loopexit_pred) // <-- Ladder
        to handle goto<br>
                        {<br>
                            break;<br>
                        }<br>
                    }<br>
                } while (!_crit_edge);<br>
                bool _get_r_YAMXZ_exit_loopexit_11_pred;<br>
                if (_get_r_YAMXZ_exit_loopexit_11_pred) // <-- Ladder
        to handle goto<br>
                {<br>
                    bool _get_r_YAMXZ_exit_loopexit_11;<br>
                }<br>
                bool _get_r_YAMXZ_exit_loopexit_11_succ;<br>
            }<br>
        // end:<br>
            bool _get_r_YAMXZ_exit;<br>
        }</font><br>
    </p>
    <p>Some other complications left to consider will be to deal with
      Phi nodes properly, and switch blocks.</p>
    <p>Cheers,<br>
      Hans-Kristian<br>
    </p>
  </body>
</html>