microsoft / microsoft/DirectXShaderCompiler

Some specially crafted shaders exhibit unexpected branching behavior

Open
#7,509 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Description
I'm observing some odd behavior in some shaders resulting in incorrect branching. The issues occurs in debug with -Od. The behavior seems triggered by certain sequences of operations involving texture loads and subsequent dependent branches. With -Od, adding code that shouldn't have any changes or side-effects in execution can affect the result of the branches.

Steps to Reproduce

Compile the following pixel shader with -T ps_6_6 -E PSMain -Od (Compiler Explorer: https://godbolt.org/z/nKx5qj33s). I was able reproduce this using the DirectX-Graphics-Samples hello triangle example.

#define TOGGLE_BUG 1
struct PSInput
{
  float4 position :SV_POSITION;
  float2 uv :TEXCOORD;
};

Texture2D<uint4> g_texture : register(t0);

float3 bugtest()
{
  float should_be_zerof = 0.0f;
  float4 should_be_onef4 = 1;

  uint test_flags = (1 << 0);
  bool test_false = (test_flags & (1 << 3)) ? 1 : 0;
  bool test_true = (test_flags & (1 << 0)) ? 1 : 0;
  float close_to_zerof = 1e-20;

  uint one_or_zero = (g_texture[uint2(0, 0)].w & 0x1);
  uint uint_zero = uint(one_or_zero * close_to_zerof);
  float zerof = 0.0f + close_to_zerof * (uint_zero != 0 ? 1.0f : 0.0f);

  float onef = 1.0;
  float4 zerof4 = 0.0f;

#if TOGGLE_BUG
  if (test_false)
  {
    zerof4.x = 0.0f;
  }
  else
  {
    zerof4.x = 0.0f;
  }
#endif

  onef = saturate(1.0f + close_to_zerof * zerof);
  should_be_onef4 = saturate(1.0f + close_to_zerof * zerof4);
  if (test_true)
  {
    should_be_zerof = saturate(1.0f - onef);
  }

  float3 red = saturate(should_be_onef4.xyz * close_to_zerof + float3(1.0, 0.0f, 0));
  float3 green = saturate(should_be_zerof * close_to_zerof + float3(0.0f, 1.0f, 0));

  if (should_be_zerof < 1.0f) 
  {
    // this should always be true
    return green;
  }
  else
  {
    return red; // wat?
  }
}

float4 PSMain(PSInput input) : SV_TARGET
{
  float3 out_color = bugtest();
  return float4(out_color, 1.0);
}

Actual Behavior
This code should always evaluate to producing a green pixel but ends up taking a wrong branch and produces red pixels. Changing the TOGGLE_BUG to 0 removes some extra code that shouldn't have side effects but changes the behavior with -Od and this results in the correct branch being taken at the end of the shader.

Environment

  • DXC version 1.8.2502
  • Host Operating System Windows 11 23H2

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

Compile the supplied PSMain shader with DXC 1.8.2502 using -T ps_6_6 -E PSMain -Od, then compare behavior with TOGGLE_BUG set to 1 and 0. Trace the compiler's handling of the texture load, dependent branches, and optimization-disabled path; done means both variants produce the expected green pixel.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.