microsoft/compiler: sink load_invocation_id in TCS split even for single-use

dxil_nir_split_tess_ctrl sinks load_invocation_id into per-use local
loads before wrapping the pre-barrier region in a loop. That sinking
was skipped when the load had only one use, on the assumption that
duplication wasn't needed.

The skip produces wrong code when the single use is on the opposite
side of the barrier from the load — e.g. HLSL-style TCS with a post-
barrier `if (InvocationId == 0)` patch-constant gate. The load gets
wrapped into the loop but the use stays outside, and after SSA repair
the use sees the counter's post-loop value (== tcs_vertices_out),
making the gate fold to false. opt_dead_cf then strips the patch-
constant stores inside the gate body, producing an empty
PatchConstantFunc and zero tess factors at runtime.

Dropping the list_is_singular skip moves the single-use load right
before its use, letting the existing pre/post-barrier handling wrap
each region correctly. Multi-use loads still get one local load per
use as before.

Assisted-by: Claude Opus 4.7 <noreply@anthropic.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41028>
This commit is contained in:
Virgile Bello 2026-04-17 10:48:48 +09:00 committed by Marge Bot
parent b7f9974f3e
commit 1262600f0f

View file

@ -214,9 +214,11 @@ dxil_nir_split_tess_ctrl(nir_shader *nir, nir_function **patch_const_func)
* will run sequentially. Then a loop is inserted so load_invocation_id will load the
* loop counter. This loop continues until a barrier is reached, when the loop
* is closed and the process begins again.
*
* First, sink load_invocation_id so that it's present on both sides of barriers.
* Each use gets a unique load of the invocation ID.
*
* First, sink load_invocation_id so that each use has its own local load.
* This ensures the load sits in the same pre/post-barrier region as its
* use, which the loop-wrapping pass below relies on even for single-use
* loads whose use sits across a barrier.
*/
nir_builder b = nir_builder_create(patch_const_func_impl);
nir_foreach_block(block, patch_const_func_impl) {
@ -225,8 +227,7 @@ dxil_nir_split_tess_ctrl(nir_shader *nir, nir_function **patch_const_func)
continue;
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
if (intr->intrinsic != nir_intrinsic_load_invocation_id ||
list_is_empty(&intr->def.uses) ||
list_is_singular(&intr->def.uses))
list_is_empty(&intr->def.uses))
continue;
nir_foreach_use_including_if_safe(src, &intr->def) {
b.cursor = nir_before_src(src);