microsoft / microsoft/DirectXShaderCompiler

dxc moves wave intrisics out of a waterfall loop

Open
#6,109 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug correctness
Dominant language
C++
Stars
3.7k
Forks
900
Avg merge
2d 11h
Merged PRs (30d)
44

Description

Description
The output of a wave intrinsic depends on the current state of which threads are active. When a wave intrinsic occurs in a loop it needs to execute in the loop body since the state of active threads changes over time (threads can exit the loop at different times). The shader in this bug shows a case where dxc is incorrectly moving wave intrinsics and their dependent operations out of a loop.

In this shader the InterlockedOr operation is only done once outside the loop, but it should be done inside the loop for each unique value of arrayIndex.

The bug only repros with lifetime markers enabled. If I compile with them off (either by default in the shader model or explicitly with a flag) the code looks correct. My guess is maybe jump-threading is interfering with the dx.break calls working as expected. Note that we also get correct code with partial lifetime markers -opt-enable partial-lifetime-markers .

Steps to Reproduce

// dxc /Tps_6_6 bug.hlsl
RWBuffer<uint> rwOutput : register(u0);

struct PixelInput {
        uint m_InstanceID       : PARAM0;
};

[earlydepthstencil]
void main(PixelInput input)
{
        uint arrayIndex = input.m_InstanceID >> 5;
        uint bitIndex = (input.m_InstanceID & 31);

        bool isThisLaneNotDone = true;

        do {
                if (isThisLaneNotDone) {
                        uint firstLaneArrayIndex = WaveReadLaneFirst(arrayIndex);

                        if (firstLaneArrayIndex == arrayIndex) {
                                uint mask = WaveActiveBitOr(1u << bitIndex);

                                if (WaveIsFirstLane()) {
                                        InterlockedOr(rwOutput[firstLaneArrayIndex], mask);
                                }

                                isThisLaneNotDone = false;
                        }
                }
        } while (isThisLaneNotDone);

}
Lifetime markers on

dxc /Tps_6_6 bug.hlsl
dxc /Tps_6_5 -enable-lifetime-markers bug.hlsl
image

Lifetime markers off

dxc /Tps_6_5 bug.hlsl
dxc /Tps_6_6 -disable-lifetime-markers bug.hlsl
image

Actual Behavior
The CFG with lifetime markers on shows the atomic operation is moved out of the loop.

Environment

  • DXC version dxcompiler.dll: 1.8 - 1.7.0.4383 (ad3958c2d); dxil.dll: 1.6(101.6.2104.33)
  • Host Operating System Windows

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by compiling the reproduced bug.hlsl shader with lifetime markers enabled and disabled using the dxc commands in the issue, then compare the generated CFG. Trace the optimization responsible for moving the InterlockedOr out of the loop; done means the operation remains in the loop for each unique arrayIndex and the corrected behavior is verified in the generated CFG.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.