<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>