Skip to content

Skip loading slang-glslang when no downstream SPIR-V work is needed#11797

Draft
nv-slang-bot[bot] wants to merge 1 commit into
masterfrom
fix/issue-11662-skip-glslang-load
Draft

Skip loading slang-glslang when no downstream SPIR-V work is needed#11797
nv-slang-bot[bot] wants to merge 1 commit into
masterfrom
fix/issue-11662-skip-glslang-load

Conversation

@nv-slang-bot

@nv-slang-bot nv-slang-bot Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Motivation

A single-module -O0 -target spirv compile needs no downstream (slang-glslang / SPIRV-Tools) work: there is nothing to optimize, no second module to link, validation is off, and no separate debug info is requested. Yet createArtifactFromIR in source/slang/slang-emit.cpp unconditionally loaded the slang-glslang downstream compiler before deciding whether any of that work was actually needed:

artifact->addRepresentationUnknown(ListBlob::moveCreate(spirv));

IDownstreamCompiler* compiler = codeGenContext->getSession()->getOrLoadDownstreamCompiler(
    PassThroughMode::SpirvOpt,
    codeGenContext->getSink());          // <-- always attempted
if (compiler) { ... }

Because the diagnostic sink passed here is non-null, a build that does not ship the slang-glslang module fails with a fatal E00100 (failed-to-load-downstream-compiler) — even though, for a default -O0 -target spirv compile, the natively emitted SPIR-V is already the final output and the module would never have done anything. This is the failure reported in #11662.

Proposed solution

Decide whether any downstream work is actually required before loading the downstream compiler, and only load it when it is. Slang emits and legalizes SPIR-V natively, so the module added to the artifact immediately above the load is already a complete, valid SPIR-V module. The slang-glslang compiler is only needed to:

  • run the SPIRV-Tools optimizer (optimization level above None),
  • link multiple embedded SPIR-V modules (separate / precompiled downstream modules present),
  • validate the result (SPIR-V validation enabled), or
  • emit separate debug info (produced as a side effect of the downstream compile step).

When none of these apply, the natively emitted SPIR-V is returned as-is and slang-glslang is never loaded — so a single-module -O0 -target spirv compile has no dependency on the module.

This is the runtime-gating approach. A companion PR implements the alternative config-time approach (a CMake option that compiles the registration out); the two are intended to be compared. This PR is the one that does not add a new build option.

Change summary

File Change
source/slang/slang-emit.cpp In createArtifactFromIR: hoist the isPrecompilation flag and the link-candidate enumeration (which walks IR only) above the downstream-compiler load; compute needsLink / needsOptimization / needsValidation / needsSeparateDebugInfo and load slang-glslang only when one of them holds; guard the link block on needsLink and reuse the pre-collected module list.
tests/bugs/gh-11662.slang Regression test compiling a compute entry point with -O0 -target spirv and FileCheck-ing a complete module (OpEntryPoint GLCompute, OpExecutionMode … LocalSize, OpStore).

Concepts and vocabulary

  • Downstream compiler / PassThroughMode::SpirvOpt — the slang-glslang module, loaded via getOrLoadDownstreamCompiler and dlopen. It wraps SPIRV-Tools (optimize/validate/link/disassemble) plus glslang. It is loaded at runtime, not linked.
  • Native SPIR-V emissionemitSPIRVFromIR produces the SPIR-V directly from Slang IR, including all legalization. The downstream optimizer is a perf/size pass, not part of producing a valid module.
  • E00100failed-to-load-downstream-compiler; raised (as an error, because the sink is non-null) when getOrLoadDownstreamCompiler cannot find the module.

Process report

The load is the only thing that touched slang-glslang on this path, and it was unconditional. In createArtifactFromIR the optimizer compile (compiler->compile) ran unconditionally inside if (compiler), the link was already gated to spirvFiles.getCount() > 1, validation was gated to shouldRunSPIRVValidation, and disassembly is only reached on the spirv-asm / IR-dump paths (not this binary-SPIR-V output path). So for a single-module -O0 compile the only reference to the module was the unconditional getOrLoadDownstreamCompiler call — which is what raised E00100. The fix moves the need-determination ahead of that call.

Why this is behavior-preserving except for the intended case. Previously the compiler was always loaded and compile() always ran, so even a single-module -O0 compile went through a SPIRV-Tools optimize-at-None round-trip. The optimizer at OptimizationLevel::None is an identity copy (glslang_optimizeSPIRV returns immediately and the wrapper copies the input SPIR-V to the output artifact), so skipping it in the no-work case only skips that identity round-trip; the resulting module is the same natively emitted, already-legal SPIR-V. For every case where the compiler is still loaded (optimization above None, link, validation, separate debug info), compile() still runs exactly as before, so output is unchanged. The one changed output path is single-module -O0 with validation off and no separate debug info, which now returns the native SPIR-V without the identity round-trip — the intended fix.

Why needsSeparateDebugInfo is part of the gate. The separate-debug artifact is created after a successful downstream compile() (via stripDbgSpirvFromArtifact). If separate debug info were requested but we did not load the compiler, that output would be silently skipped — so shouldEmitSeparateDebugInfo() is included in needsDownstreamCompiler.

Why hoisting the enumeration is safe. The link-candidate enumeration (program->enumerateIRModules) only walks IR and records embedded SPIR-V blobs; it needs no downstream compiler and mutates nothing, so it is safe to run before the load and reuse afterward. It adds negligible work to the no-downstream path.

Input-shape check. No malformed input is being masked. The natively emitted SPIR-V is a correct, complete module by construction (legalization is native); the change only avoids an unnecessary downstream load. This is a consumer-side decision about when an optional downstream tool is required, not a workaround for a bad upstream representation.

Verification. Built slangc + slang-test (debug); the new test passes; existing -O0 -target spirv tests (forceinline-basic-block, entry-point-array-return) pass; the full tests/spirv/ suite passes (470/470, 0 regressions). The test guards that the -O0 -target spirv path still emits a complete module after the gating change; it cannot exercise the "module physically absent" scenario because the standard test build always bundles slang-glslang (and the binary SPIR-V output is disassembled for FileCheck, which itself uses slang-glslang).

Fixes #11662.

The SPIR-V backend's createArtifactFromIR unconditionally loaded the
slang-glslang downstream compiler (PassThroughMode::SpirvOpt) before
deciding whether any downstream work was required. Because the diagnostic
sink is non-null, a build without slang-glslang then failed with a fatal
E00100 (failed-to-load-downstream-compiler) even for a single-module
-O0 -target spirv compile, which needs no optimization, link, validation,
or separate debug info.

Slang emits and legalizes SPIR-V natively, so the natively emitted module
is already complete and valid. Determine up front whether downstream work
is actually needed -- optimization above None, a multi-module link, SPIR-V
validation, or separate debug info -- and only load slang-glslang in that
case. The link-candidate enumeration walks IR only, so it is hoisted ahead
of the load and reused. When nothing downstream is required the natively
emitted SPIR-V is returned as-is, so a default -O0 -target spirv compile no
longer depends on slang-glslang.

Add tests/bugs/gh-11662.slang covering the -O0 -target spirv path.

Fixes #11662.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr: non-breaking PRs without breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

slang wants to load libslang-glslang-<version>.so even when it does not build it

0 participants