Inspect DXIL Assembly with DirectX Shader Compiler

Inspecting the generated DirectX Intermediate Language (DXIL) assembly is essential for debugging, performance profiling, and verifying HLSL compiler optimizations. The DirectX Shader Compiler (dxc.exe) provides built-in command-line arguments and programmatic C++ APIs that allow developers to disassemble compiled shader blobs back into human-readable, LLVM-based DXIL representation. This guide explains how to generate, inspect, and extract DXIL assembly using both the command-line utility and runtime APIs.

Inspecting DXIL via the DXC Command Line

The most straightforward method to inspect DXIL is using the command-line interface provided by dxc.exe. You can either disassemble an existing compiled shader binary or output the assembly directly during compilation.

Disassembling During Compilation

To compile an HLSL source file and output the human-readable DXIL assembly to a text file simultaneously, use the -Fc flag:

dxc.exe -T ps_6_0 -E main -Fc shader_assembly.txt shader.hlsl
  • -T ps_6_0: Specifies the shader profile target (Pixel Shader 6.0).
  • -E main: Defines the entry point function.
  • -Fc shader_assembly.txt: Outputs the human-readable assembly to the specified file.

If you omit the file path and want to print the disassembly directly to the terminal stdout without generating a binary, omit the output flag and direct the compiler to emit assembly:

dxc.exe -T cs_6_5 -E CSMain shader.hlsl -dumpbin

Disassembling an Existing Binary

If you already have a compiled .bin, .cso, or .dxil binary file, you can inspect its DXIL representation using the -dumpbin argument:

dxc.exe -dumpbin compiled_shader.cso

To pipe this output directly into a file for review in a text editor:

dxc.exe -dumpbin compiled_shader.cso > disassembly.txt

Inspecting DXIL Programmatically Using the DXC API

For custom build tools or engine-level inspection, DXC provides the IDxcCompiler3 interface to disassemble DXIL blobs in C++.

Disassembly Workflow

  1. Initialize the DXC support instance using DxcCreateInstance.
  2. Obtain instances of IDxcUtils and IDxcCompiler3.
  3. Wrap your compiled bytecode inside a DxcBuffer structure.
  4. Call IDxcCompiler3::Disassemble.
  5. Extract the disassembly string from the resulting IDxcResult.

Example C++ Implementation

#include <windows.h>
#include <dxcapi.h>
#include <iostream>

void DisassembleDXIL(const void* bytecodeData, size_t bytecodeSize) {
    IDxcUtils* pUtils = nullptr;
    IDxcCompiler3* pCompiler = nullptr;

    DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&pUtils));
    DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&pCompiler));

    DxcBuffer sourceBuffer = {};
    sourceBuffer.Ptr = bytecodeData;
    sourceBuffer.Size = bytecodeSize;
    sourceBuffer.Encoding = DXC_CP_ACP;

    IDxcResult* pResult = nullptr;
    HRESULT hr = pCompiler->Disassemble(&sourceBuffer, IID_PPV_ARGS(&pResult));

    if (SUCCEEDED(hr)) {
        IDxcBlobUtf8* pDisassembly = nullptr;
        pResult->GetOutput(DXC_OUT_DISASSEMBLY, IID_PPV_ARGS(&pDisassembly), nullptr);

        if (pDisassembly && pDisassembly->GetStringLength() > 0) {
            std::cout << pDisassembly->GetStringPointer() << std::endl;
        }

        if (pDisassembly) pDisassembly->Release();
        pResult->Release();
    }

    if (pCompiler) pCompiler->Release();
    if (pUtils) pUtils->Release();
}

Reading the Generated DXIL

DXIL is an encapsulation of LLVM 3.7 IR (Intermediate Representation) augmented with DirectX-specific intrinsic operations and metadata. Key elements to look for when inspecting the assembly include:

  • Resource Declarations (!dx.resources): Found near the bottom in LLVM metadata blocks, detailing Constant Buffers, SRVs, UAVs, and Samplers, including register and space bindings.
  • Entry Points (!dx.entryPoints): Lists signature parameters, shader model targets, and root signature attachments.
  • Intrinsics (dx.op.*): Shader-specific operations are represented as internal function calls (e.g., call %dx.types.ResRet.f32 @dx.op.sample...), showing resource access, math operations, and wave intrinsics.