Skip to content

[SPIR-V] Nonsemantic debug info mode should emit a DebugLine & DebugScope for the wrapper main function #7341

@sajjadmirzanv

Description

@sajjadmirzanv

This is related, but not exactly the same as, an issue which I filed against spirv-opt: KhronosGroup/SPIRV-Tools#6085
This specific problem would require special case logic to be handled in spirv-opt, so I think it is better to solve it in DXC.

tldr: DXC should generate debug info for the wrapper %main function as if it were on the function signature (or opening brace) of the shader's entry point

Description
If a shader is compiled with -Zi but without the -fspv-debug=vulkan-with-source option, the wrapper main function has a preceding OpLine instruction:

               OpLine %6 12 1
       %main = OpFunction %void None %18
         %19 = OpLabel
%param_var_psin = OpVariable %_ptr_Function_PSInput Function
         %23 = OpLoad %v4float %gl_FragCoord
         %24 = OpLoad %v4float %in_var_COLOR
         %25 = OpCompositeConstruct %PSInput %23 %24
               OpStore %param_var_psin %25
         %26 = OpFunctionCall %v4float %src_main %param_var_psin
               OpStore %out_var_SV_Target %26
               OpLine %6 18 1
               OpReturn
               OpFunctionEnd

This tells a debugger that setup code happens on line 12, which in this shader is the line of main's function signature. That improves the cursor behavior when stepping line by line: the user sees that the program starts at the top of main, runs through the body, and then goes to the closing brace.

Whereas when -fspv-debug=vulkan-with-source is on, the debug info doesn't have a DebugLine instruction for line 12:

       %main = OpFunction %void None %90
         %91 = OpLabel
%param_var_psin = OpVariable %_ptr_Function_PSInput Function
         %95 = OpExtInst %void %1 DebugFunctionDefinition %84 %main
         %96 = OpLoad %v4float %gl_FragCoord
         %97 = OpLoad %v4float %in_var_COLOR
         %98 = OpCompositeConstruct %PSInput %96 %97
               OpStore %param_var_psin %98
         %99 = OpFunctionCall %v4float %src_main %param_var_psin
               OpStore %out_var_SV_Target %99
        %101 = OpExtInst %void %1 DebugLine %28 %uint_18 %uint_18 %uint_1 %uint_1
               OpReturn
               OpFunctionEnd

Apart from just consistency with old-style debug info, this becomes worse when legalization is on, as the %main function also has no DebugScope instruction. Therefore when spirv-opt does inlining, it is unable to tell that a DebugScope instruction from inside the %src_main should be translated into a DebugScope+DebugInlinedAt inside of %main.

To demonstrate this, I took the output of compiling with -fcgl to get pre-legalization SPIR-V, disassembled it, and hand edited in the relevant-instructions:

         %84 = OpExtInst %void %1 DebugFunction %83 %82 %28 %uint_12 %uint_1 %29 %43 %uint_3 %uint_13
        %901 = OpExtInst %void %1 DebugLexicalBlock %28 %uint_12 %uint_1 %84 ;; fake lexical block for the wrapper
         %89 = OpExtInst %void %1 DebugGlobalVariable %88 %38 %28 %uint_1 %uint_16 %29 %88 %depthTex %uint_8
         %87 = OpExtInst %void %1 DebugEntryPoint %84 %29 %85 %86
       %main = OpFunction %void None %90
         %91 = OpLabel
%param_var_psin = OpVariable %_ptr_Function_PSInput Function
         %95 = OpExtInst %void %1 DebugFunctionDefinition %84 %main
        %902 = OpExtInst %void %1 DebugScope %901 ;; scope to the wrapper
        %903 = OpExtInst %void %1 DebugLine %28 %uint_12 %uint_12 %uint_1 %uint_1 ;; main was declared on line 12, could also use 13 for the opening brace
         %96 = OpLoad %v4float %gl_FragCoord
         %97 = OpLoad %v4float %in_var_COLOR
         %98 = OpCompositeConstruct %PSInput %96 %97
               OpStore %param_var_psin %98
         %99 = OpFunctionCall %v4float %src_main %param_var_psin
               OpStore %out_var_SV_Target %99
        %101 = OpExtInst %void %1 DebugLine %28 %uint_18 %uint_18 %uint_1 %uint_1
               OpReturn

I then re-assembled that and ran it through spirv-opt --legalize-hlsl:

       %main = OpFunction %void None %90
         %91 = OpLabel
         %95 = OpExtInst %void %1 DebugFunctionDefinition %84 %main
       %1058 = OpExtInst %void %1 DebugScope %901
        %903 = OpExtInst %void %1 DebugLine %28 %uint_12 %uint_12 %uint_1 %uint_1
         %96 = OpLoad %v4float %gl_FragCoord
         %97 = OpLoad %v4float %in_var_COLOR
       %1056 = OpExtInst %void %1 DebugValue %80 %96 %78 %int_0
       %1053 = OpExtInst %void %1 DebugValue %80 %97 %78 %int_1
       %1059 = OpExtInst %void %1 DebugScope %68 %946 ;; <- uses inlined operand
        %973 = OpExtInst %void %1 DebugLine %28 %uint_14 %uint_14 %uint_17 %uint_31
        %957 = OpVectorShuffle %v2float %96 %96 0 1

And it worked, the load instructions are correctly set to line 12, while the first real instruction from the original main function has a scope that correctly uses a DebugInlinedAt: %946 = OpExtInst %void %1 DebugInlinedAt %uint_12 %901

Steps to Reproduce
wrapper_main_scope.zip
The HLSL shader and SPIR-V files are attached.
dxc.exe -spirv -Zi -O0 -fspv-target-env=vulkan1.2 -fvk-use-gl-layout -fspv-debug=vulkan-with-source debug_legalization.ps.hlsl will demonstrate the problem.

Environment

  • DXC version dxcompiler.dll: 1.8 - 1.8.2502.8 (b471183); dxil.dll: 1.8(1.8.2502.8)
  • Host Operating System Windows 11

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug, regression, crashspirvWork related to SPIR-V

    Type

    No type

    Projects

    Status

    Done

    Status

    Triaged

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions