Copyright © 2015-2018 LunarG, Inc.

# SPIR-V Toolchain

The Vulkan graphics API requires that all shaders be presented in SPIR-V format. SPIR-V is a binary intermediate representation for graphical shaders and compute kernels. The advantages and a high-level description of the representation are given in the SPIR-V White Paper. A more detailed description of the SPIR-V supported by this SDK can be found in The SPIR-V Specification v1.2.

The LunarG Vulkan SDK includes tools for SPIR-V shader generation, inspection, editing, validation, optimization, constant manipulation and compression improvement.

Chief member of the SPIR-V toolchain is glslangValidator, which can be used to create SPIR-V shaders from equivalent GLSL or HLSL shaders.

The SDK also includes `spirv-opt` which can be used (along with `spirv-remap`, described below) to reduce the size of SPIR-V code by more than 60% through classic optimization tranformations. `spirv-opt` can also be used to set and optimize a SPIR-V file's specialization constants.

The SPIR-V disassembler `spirv-dis` converts SPIR-V binary into a special text format for reading and editing. The SPIR-V assembler `spirv-as` converts such text back into SPIR-V binary format. The SPIR-V validator `spirv-val` can be used to assure that the resulting SPIR-V binary code is valid.

The SDK also includes `spirv-remap`, a tool to improve the compressibility of a collection of SPIR-V modules. This can result in up to a 50% reduction in the size of a compressed collection relative to the compressed originals. `spirv-remap` also has options (`--dce all`) to remove dead functions and types exposed by `spirv-opt` size reduction passes. It also can strip debug instructions from SPIR-V (`--strip all`) if not needed.

Finally, the SDK also provides a tool for viewing a SPIR-V file's control flow with `spirv-cfg`, a tool for generating high-level language representation and reflection data for a SPIR-V file with `spirv-cross`, and a standard compilation command line with `shaderc`.

## glslangValidator (SPIR-V Generator)

`glslangValidator` is the tool used to compile GLSL and HLSL shaders into SPIR-V, Vulkan's shader format. Additional information on compiling HLSL can be found in the HLSL FAQ.

Executed without any options, `glslangValidator` returns the message:

    Usage: glslangValidator [option]... [file]...

    Where: each 'file' ends in .<stage>, where <stage> is one of
        .conf   to provide an optional config file that replaces the default configuration
                (see -c option below for generating a template)
        .vert   for a vertex shader
        .tesc   for a tessellation control shader
        .tese   for a tessellation evaluation shader
        .geom   for a geometry shader
        .frag   for a fragment shader
        .comp   for a compute shader

    Compilation warnings and errors will be printed to stdout.

    To get other information, use one of the following options:
    Each option must be specified separately.
      -V          create SPIR-V binary, under Vulkan semantics; turns on -l;
                  default file name is <stage>.spv (-o overrides this)
      -G          create SPIR-V binary, under OpenGL semantics; turns on -l;
                  default file name is <stage>.spv (-o overrides this)
      -H          print human readable form of SPIR-V; turns on -V
      -E          print pre-processed GLSL; cannot be used with -l;
                  errors will appear on stderr.
      -S <stage>  uses explicit stage specified, rather then the file extension.
                  valid choices are vert, tesc, tese, geom, frag, or comp
      -c          configuration dump;
                  creates the default configuration file (redirect to a .conf file)
      -C          cascading errors; risks crashes from accumulation of error recoveries
      -d          default to desktop (#version 110) when there is no shader #version
                  (default is ES version 100)
      -D          input is HLSL
      -e          specify entry-point name
      -h          print this usage message
      -i          intermediate tree (glslang AST) is printed out
      -l          link all input files together to form a single module
      -m          memory leak mode
      -o  <file>  save binary to <file>, requires a binary option (e.g., -V)
      -q          dump reflection query database
      -r          relaxed semantic error-checking mode
      -s          silent mode
      -t          multi-threaded mode
      -v          print version strings
      -w          suppress warnings (except as required by #extension : warn)
      -x          save 32-bit hexadecimal numbers as text, requires a binary option (e.g., -V)

      --shift-sampler-binding [stage] num     set base binding number for samplers
      --ssb [stage] num                       synonym for --shift-sampler-binding

      --shift-texture-binding [stage] num     set base binding number for textures
      --stb [stage] num                       synonym for --shift-texture-binding

      --shift-UBO-binding [stage] num         set base binding number for UBOs
      --sub [stage] num                       synonym for --shift-UBO-binding

      --auto-map-bindings                     automatically bind uniform variables without
                                              explicit bindings.
      --amb                                   synonym for --auto-map-bindings

      --flatten-uniform-arrays                flatten uniform texture & sampler arrays to scalars
      --fua                                   synonym for --flatten-uniform-arrays

## SPIR-V Optimizer

A programmer may wish to reduce the size of a SPIR-V file, especially as it is initially generated by the glslangValidator frontend. The SPIR-V optimizer `spirv-opt` can be used to apply classic optimization transformations to SPIR-V to reduce its size without changing its functionality. The following "recipe" of `spirv-opt` options can be effective at reducing SPIR-V size:

    --inline-entry-points-exhaustive
    --convert-local-access-chains
    --eliminate-local-single-block
    --eliminate-local-single-store
    --eliminate-insert-extract
    --eliminate-dead-code-aggressive
    --eliminate-dead-branches
    --merge-blocks
    --eliminate-local-single-block
    --eliminate-local-single-store
    --eliminate-local-multi-store
    --eliminate-insert-extract
    --eliminate-dead-code-aggressive
    --eliminate-common-uniform

The transformations are applied in the order specified on the command line. Note: some options are applied more than once. More detailed information can be found in the file `SpirvOptSizeReduction.pdf` in the SDK documentation folder.

spirv-opt can also be used to manipulate the SPIR-V file's specialization constants.

spirv-opt -h returns the message:

    spirv-opt - Optimize a SPIR-V binary file.

    USAGE: spirv-opt [options] [<input>] -o <output>

    The SPIR-V binary is read from <input>. If no file is specified,
    or if <input> is "-", then the binary is read from standard input.
    if <output> is "-", then the optimized output is written to
    standard output.

    NOTE: The optimizer is a work in progress.

    Options:
      --strip-debug
                   Remove all debug instructions.
      --freeze-spec-const
                   Freeze the values of specialization constants to their default
                   values.
      --eliminate-dead-const
                   Eliminate dead constants.
      --fold-spec-const-op-composite
                   Fold the spec constants defined by OpSpecConstantOp or
                   OpSpecConstantComposite instructions to front-end constants
                   when possible.
      --set-spec-const-default-value "<spec id>:<default value> ..."
                   Set the default values of the specialization constants with
                   <spec id>:<default value> pairs specified in a double-quoted
                   string. <spec id>:<default value> pairs must be separated by
                   blank spaces, and in each pair, spec id and default value must
                   be separated with colon ':' without any blank spaces in between.
                   e.g.: --set-spec-const-default-value "1:100 2:400"
      --unify-const
                   Remove the duplicated constants.
      --flatten-decorations
                   Replace decoration groups with repeated OpDecorate and
                   OpMemberDecorate instructions.
      --compact-ids
                   Remap result ids to a compact range starting from %1 and without
                   any gaps.
      --inline-entry-points-exhaustive
                   Exhaustively inline all function calls in entry point functions.
                   Currently does not inline calls to functions with early return
                   in a loop.
      --convert-local-access-chains
                   Convert constant index access chain loads/stores into
                   equivalent load/stores with inserts and extracts. Performed
                   on function scope variables referenced only with load, store,
                   and constant index access chains.
      --eliminate-common-uniform
                   Perform load/load elimination for duplicate uniform values.
                   Converts any constant index access chain uniform loads into
                   its equivalent load and extract. Some loads will be moved
                   to facilitate sharing. Performed only on entry point
                   functions.
      --eliminate-local-single-block
                   Perform single-block store/load and load/load elimination.
                   Performed only on function scope variables in entry point
                   functions.
      --eliminate-local-single-store
                   Replace stores and loads of function scope variables that are
                   only stored once. Performed on variables referenceed only with
                   loads and stores. Performed only on entry point functions.
      --eliminate-local-multi-store
                   Replace stores and loads of function scope variables that are
                   stored multiple times. Performed on variables referenceed only
                   with loads and stores. Performed only on entry point functions.
      --eliminate-insert-extract
                   Replace extract from a sequence of inserts with the
                   corresponding value. Performed only on entry point functions.
      --eliminate-dead-code-aggressive
                   Delete instructions which do not contribute to a function's
                   output. Performed only on entry point functions.
      --eliminate-dead-branches
                   Convert conditional branches with constant condition to the
                   indicated unconditional brranch. Delete all resulting dead
                   code. Performed only on entry point functions.
      --merge-blocks
                   Join two blocks into a single block if the second has the
                   first as its only predecessor. Performed only on entry point
                   functions.
      -h, --help
                   Print this help.
      --version
                   Display optimizer version information.

## SPIR-V Disassembler, Assembler and Validator

A programmer may wish to view or change the contents of a shader while debugging a Vulkan application. Since Vulkan accepts shaders only in SPIR-V, a binary format, the SDK provides `spirv-dis`, a standalone program which prints a SPIR-V shader in human-readable and parsable form, and `spirv-as`, which can assemble the possibly edited output of `spirv-dis`. Finally, `spirv-val` can be used to verify that the resulting SPIR-V code is valid.

`spirv-dis -h` returns the message:

    ./spirv-dis - Disassemble a SPIR-V binary module

    Usage: ./spirv-dis [options] [<filename>]

    The SPIR-V binary is read from <filename>. If no file is specified,
    or if the filename is "-", then the binary is read from standard input.

    Options:

      -h, --help      Print this help.
      --version       Display disassembler version information.

      -o <filename>   Set the output filename.
                      Output goes to standard output if this option is
                      not specified, or if the filename is "-".

      --no-color      Don't print in color.
                      The default when output goes to a file.

      --no-indent     Don't indent instructions.

      --offsets       Show byte offsets for each instruction.

`spirv-as -h` returns the message:

    ./spirv-as - Create a SPIR-V binary module from SPIR-V assembly text

    Usage: ./spirv-as [options] <filename>

    The SPIR-V assembly text is read from <filename>.  The SPIR-V binary
    module is written to file "out.spv", unless the -o option is used.

    Options:

      -h              Print this help.

      -o <filename>   Set the output filename.
      --version       Display assembler version information.

`spirv-val -h` returns the message:

    spirv-val - Validate a SPIR-V binary file.

    USAGE: spirv-val [options] [<filename>]

    The SPIR-V binary is read from <filename>. If no file is specified,
    or if the filename is "-", then the binary is read from standard input.

    NOTE: The validator is a work in progress.

    Options:
      -h, --help                       Print this help.
      --max-struct-members             <maximum number of structure members allowed>
      --max-struct-depth               <maximum allowed nesting depth of structures>
      --max-local-variables            <maximum number of local variables allowed>
      --max-global-variables           <maximum number of global variables allowed>
      --max-switch-branches            <maximum number of branches allowed in switch statements>
      --max-function-args              <maximum number arguments allowed per function>
      --max-control-flow-nesting-depth <maximum Control Flow nesting depth allowed>
      --max-access-chain-indexes       <maximum number of indexes allowed to use for Access Chain instructions>
      --version                        Display validator version information.
      --target-env                     {vulkan1.0|spv1.0|spv1.1|spv1.2}
                                       Use Vulkan1.0/SPIR-V1.0/SPIR-V1.1/SPIR-V1.2 validation rules.

## SPIR-V Remapper

The SPIR-V remapping tool, `spirv-remap`, enhances compression of SPIR-V binary files via entropy reduction, including optional stripping of debug information and dead functions. `spirv-remap` transforms SPIR-V to SPIR-V, remapping IDs to reduce the size of a compressed image containing multiple SPIR-V modules. This works with compression procedures that can find dictionary entries across multiple files.

The SPIR-V Remapper supports two modes of use: command line and a C++11 API.

Remapping is accomplished via ID canonicalization. In this scheme, IDs become larger and are no longer tightly packed near zero, but should compress better when multiple modules are compressed together. The command line tool operates serially on multiple modules. The API accepts only a single module at a time.

Note: The `spirv-remap` tool is in early stages of development and should be considered alpha quality.

### Command Line

`spirv-remap` supports multiple levels of verbosity, specified with `-v`, `-vv`, `-vvv`, etc or by providing an integer parameter to the `--verbose` option. Note that higher levels of verbosity provide a considerable quantity of `stdout` messages. With no verbosity, `spirv-remap` is silent, returning 0 on success and a positive integer error status on error.

The SPIR-V remapper provides basic ID remapping, ID remapping with debug symbol and line number stripping, and ID remapping with those features plus dead function and type elimination.

Executed without any options, `spirv-remap` returns a message similar to:

    Usage:
      spirv-remap [-v[v[...]] | --verbose [int]] [--map (all|types|names|funcs)] [--dce (all|types|funcs)] [--opt (all|loadstore)] [--strip-all | --strip all | -s] [--do-everything] --input | -i file1 [file2...] --output|-o DESTDIR
      spirv-remap [--version | -V]
      spirv-remap [--help | -?]

Options supported by `spirv-remap` are identified in the table below. Options can appear anywhere on the command line.

Option | Description
--- | ---
`--dce` | dead code elimination mode; eliminates dead functions and types
`-i, --input` | `*.spv` shader input files
`--map` | remap types, names, functions, or all
`-o, --output` | write remapped shader files to this directory
`-s, --strip-all` | strip debug symbol information and line numbers

Examples:

1. Basic ID remapping example:

       spirv-remap --map all --input *.spv --output /tmp/out_dir

Performs ID remapping on all shaders in the current directory, writing new files with the same basenames to `/tmp/out_dir`.

2. ID remapping with debug symbol and line number stripping:

       spirv-remap --map all --strip-all --input *.spv --output /tmp/out_dir

3. ID remapping with debug symbol and line number stripping plus dead function and type elimination:

       spirv-remap --map all --dce all --strip-all --input *.spv --output /tmp/out_dir

### API

API access to the SPIR-V Remapper may be provided in future SDKs.

## SPIR-V Control Flow Visualization

`spirv-cfg` shows the SPIR-V control flow in GraphiViz "dot" form. It is experimental. `spirv-cfg --help` prints the following:

    ./spirv-cfg - Show the control flow graph in GraphiViz "dot" form. EXPERIMENTAL

    Usage: ./spirv-cfg [options] [<filename>]

    The SPIR-V binary is read from <filename>. If no file is specified,
    or if the filename is "-", then the binary is read from standard input.

    Options:

      -h, --help      Print this help.
      --version       Display version information.

      -o <filename>   Set the output filename.
                      Output goes to standard output if this option is
                      not specified, or if the filename is "-".

## SPIR-V Cross-Compilation and Reflection

`spirv-cross` translates SPIR-V files into readable shader languages including GLSL, MSL (experimental) and C++ (experimental). It can also generate reflection information for a SPIR-V shader. `spirv-cross --help` shows the latest command line options. Additional information for using this tool can be found at the spirv-cross github page.

## Shaderc - SPIR-V Compilation Wrapper

Shaderc wraps around core functionality in glslang and SPIRV-Tools. Shaderc aims to to provide: a command line compiler with GCC and Clang-like usage for better integration with build systems, an API where functionality can be added without breaking existing clients, an API supporting standard concurrency patterns across multiple operating systems, and increased functionality such as file #include support.

Shaderc consists of two components: a command line compiler and a static library. The command line compiler (glslc) is provided by the SDK and is put in the user's path so that it can be used from the command prompt. `glslc --help` shows the latest command line options.

The static library is provided only as a release build. As such, to link a debuggable program to the shaderc included in the SDK, it is necessary to build the library from the source provided in the SDK. To build a 64-bit debuggable library from the SDK, use the following commands (you will need msbuild and cmake to be available from the command prompt):

    cd %VULKAN_SDK%\shaderc
    cmake -H. -Bbuild -G "Visual Studio 14 Win64"
    cmake --build ./build --config Debug

Note that you may have to adjust the Visual Studio version, depending on which version you are using.

Additional information for using shaderc can be found at the shaderc github page.