---
generated: true
model: claude-opus-4-7
generated_at: 2026-05-22T00:00:00+00:00
source_commit: 6e923e2c0fe3cae4e7cf40e25a96569df5b9f009
watched_paths_digest: bd75ad021965ba68fbd7d335d6359ad1cf49b78a95a9be48be867759452b78f1
source_doc: docs/generated/design/target-pipelines/spirv.md
source_doc_digest: b1daf02c9f85dda22a3cac37e70daa316be6cde3c511da1abf8a80d483e1dba1
warning: "Auto-generated. May drift from source. Do not edit by hand."
---

# Tests for target-pipelines/spirv

## Intent
Tests verify the SPIR-V direct-emit target pipeline described in
[`docs/generated/design/target-pipelines/spirv.md`](../../../design/target-pipelines/spirv.md):
the ordered IR-pass + emit sequence executed when
`CodeGenTarget::SPIRV` / `CodeGenTarget::SPIRVAssembly` is the
target and `shouldEmitSPIRVDirectly() == true`. The bundle
exercises:

- the SPIR-V text prelude (`OpCapability Shader`,
  `OpExtension SPV_KHR_storage_buffer_storage_class`,
  `OpMemoryModel Logical GLSL450`, `OpSource Slang 1`);
- the entry-point shape (`OpEntryPoint GLCompute %func "main"`,
  `OpExecutionMode %func LocalSize X Y Z` from
  `[numthreads(X,Y,Z)]`);
- the entry-point varying lowering done by
  `legalizeEntryPointsForGLSL` (`gl_GlobalInvocationID` /
  `gl_LocalInvocationIndex` with `BuiltIn` decorations);
- the storage-class assignments (`StorageBuffer` for
  `StructuredBuffer`, `Uniform` for `cbuffer` /
  `ConstantBuffer<T>`, `Workgroup` for `groupshared`,
  `UniformConstant` for textures/samplers, `Input` for
  varying inputs);
- the resource decorations (`Block`, `Offset 0`, `Binding`,
  `DescriptorSet 0`, `NonWritable`, `ArrayStride`);
- Phase C / D IR-pass effects:
  `legalizeByteAddressBufferOps` with
  `translateToStructuredBufferOps=true`;
  `legalizeLogicalAndOr` (`OpLogicalAnd` on `v3bool`);
  `legalizeImageSubscript` (`OpImageWrite` / `OpImageRead`);
  `validateAtomicOperations` (`OpAtomicIAdd`);
  `moveGlobalVarInitializationToEntryPoints`;
  `performForceInlining` + `performIntrinsicFunctionInlining`
  (the SPIR-V-only second inlining pass);
  `eliminatePhis` with `useRegisterAllocation = true`;
  Sampler2D as `OpTypeSampledImage` (combined types are
  supported natively in SPIR-V, so
  `lowerCombinedTextureSamplers` is **skipped**, unlike HLSL);
- the SPIR-V word-emission shape (`OpFunction` / `OpReturn` /
  `OpFunctionEnd`, `OpAccessChain` for buffer indexing,
  `OpBitcast` for `uint`→`int` casts, `OpStore` / `OpLoad`
  for register-allocated phi locals);
- debug-info opt-in (`DebugLine` ExtInst instructions emit
  only with `-g`).

Coverage strategy: one test per concrete claim that can be
observed in `slangc -target spirv-asm` text. Default directive
is
`//TEST:SIMPLE(filecheck=CHECK):-target spirv-asm -entry main -stage compute`.
The bundle is SPIR-V-only by design; cross-target probes are
not part of this bundle. spirv-link / spirv-val / spirv-opt
are downstream tools that don't change the emitted assembly
text and are out of scope.


## Functional coverage
| Claim | Intent | Anchor | Tests |
| --- | --- | --- | --- |
| A ternary "?:" expression emits an OpSelectionMerge structured-control-flow header with two OpStore branches into a Function-storage variable — eliminatePhis(useRegisterAllocation=true) rewrites the merge phi as a per-branch store. | expansion | [#eliminatephis-with-spir-v-specific-options](../../../design/target-pipelines/spirv.md#eliminatephis-with-spir-v-specific-options) | [`select-ternary-selection-merge.slang`](select-ternary-selection-merge.slang) |
| Deeply nested if/else branches (6 distinct assigned values) all funnel into a single Function-storage OpVariable via OpStore; no OpPhi survives because eliminatePhis runs with useRegisterAllocation=true. | stress | [#eliminatephis-with-spir-v-specific-options](../../../design/target-pipelines/spirv.md#eliminatephis-with-spir-v-specific-options) | [`eliminate-phis-deeply-nested-branches.slang`](eliminate-phis-deeply-nested-branches.slang) |
| eliminatePhis on SPIR-V uses useRegisterAllocation=true so an if/else-merged value becomes a Function-storage OpVariable populated by OpStore in each branch (no surviving OpPhi). | functional | [#eliminatephis-with-spir-v-specific-options](../../../design/target-pipelines/spirv.md#eliminatephis-with-spir-v-specific-options) | [`eliminate-phis-register-allocation.slang`](eliminate-phis-register-allocation.slang) |
| SV_DispatchThreadID lowers to gl_GlobalInvocationID with BuiltIn GlobalInvocationId decoration in SPIR-V. | functional | [#legalizeentrypointsforglsl-despite-the-name](../../../design/target-pipelines/spirv.md#legalizeentrypointsforglsl-despite-the-name) | [`sv-dispatch-thread-id-builtin.slang`](sv-dispatch-thread-id-builtin.slang) |
| SV_GroupID lowers to gl_WorkGroupID with BuiltIn WorkgroupId in SPIR-V. | expansion | [#legalizeentrypointsforglsl-despite-the-name](../../../design/target-pipelines/spirv.md#legalizeentrypointsforglsl-despite-the-name) | [`sv-group-id-builtin.slang`](sv-group-id-builtin.slang) |
| SV_GroupIndex lowers to gl_LocalInvocationIndex with BuiltIn LocalInvocationIndex decoration in SPIR-V. | functional | [#legalizeentrypointsforglsl-despite-the-name](../../../design/target-pipelines/spirv.md#legalizeentrypointsforglsl-despite-the-name) | [`sv-group-index-builtin.slang`](sv-group-index-builtin.slang) |
| SV_GroupThreadID (per-thread local id) lowers to gl_LocalInvocationID with BuiltIn LocalInvocationId, distinct from SV_GroupIndex (a flat uint scalar). | expansion | [#legalizeentrypointsforglsl-despite-the-name](../../../design/target-pipelines/spirv.md#legalizeentrypointsforglsl-despite-the-name) | [`sv-group-thread-id-builtin.slang`](sv-group-thread-id-builtin.slang) |
| SV_InstanceID lowers to gl_InstanceIndex with BuiltIn InstanceIndex (Vulkan-style index). | expansion | [#legalizeentrypointsforglsl-despite-the-name](../../../design/target-pipelines/spirv.md#legalizeentrypointsforglsl-despite-the-name) | [`sv-instance-id-builtin.slang`](sv-instance-id-builtin.slang) |
| SV_VertexID lowers to gl_VertexIndex with BuiltIn VertexIndex (Vulkan-style index, not D3D-style ID, hence the rename). | expansion | [#legalizeentrypointsforglsl-despite-the-name](../../../design/target-pipelines/spirv.md#legalizeentrypointsforglsl-despite-the-name) | [`sv-vertex-id-builtin.slang`](sv-vertex-id-builtin.slang) |
| Without -g, no DebugLine OpExtInst calls appear; the only source marker is OpSource Slang 1. | functional | [#option-set-toggles](../../../design/target-pipelines/spirv.md#option-set-toggles) | [`debug-info-opt-in-default-off.slang`](debug-info-opt-in-default-off.slang) |
| invertYOfPositionOutput fires under -fvk-invert-y for a vertex entry point — the SV_Position output's Y component is negated before being written to gl_Position, observable as an OpFNegate / OpFSub on the y lane in the emitted SPIR-V. | expansion | [#option-set-toggles](../../../design/target-pipelines/spirv.md#option-set-toggles) | [`vk-invert-y-position-output.slang`](vk-invert-y-position-output.slang) |
| Phase B's checkForMissingReturns (table row 24, gated on reqSet.missingReturn) fires for a non-void function that does not return on every path — the error fires before SPIR-V emit. | negative | [#phase-b-specialization-and-type-legalization](../../../design/target-pipelines/spirv.md#phase-b-specialization-and-type-legalization) | [`negative-missing-return-non-void.slang`](negative-missing-return-non-void.slang) |
| Phase B's checkForOutOfBoundAccess fires before SPIR-V emit when a static array is indexed past its declared bound; the documented diagnostic is reported on the IR pipeline (not the SPIR-V backend). | negative | [#phase-b-specialization-and-type-legalization](../../../design/target-pipelines/spirv.md#phase-b-specialization-and-type-legalization) | [`negative-out-of-bound-static-array-index.slang`](negative-out-of-bound-static-array-index.slang) |
| Phase B's checkForRecursiveFunctions (table row 22) rejects a self-recursive call with E55201 before Phase C/D legalization can run. | negative | [#phase-b-specialization-and-type-legalization](../../../design/target-pipelines/spirv.md#phase-b-specialization-and-type-legalization) | [`negative-recursive-function.slang`](negative-recursive-function.slang) |
| A ConstantBuffer<float4> (scalar payload of one vector) lowers to a Uniform-storage OpVariable; the address-space mapping does not change with payload shape. | boundary | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`constant-buffer-scalar-uniform.slang`](constant-buffer-scalar-uniform.slang) |
| A DXR closesthit entry point with an inout Payload exercises replaceLocationIntrinsicsWithRaytracingObject — the payload parameter lowers to an IncomingRayPayloadKHR global variable and the SPIR-V emit declares OpCapability RayTracingKHR. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`dxr-closesthit-raytracing-object.slang`](dxr-closesthit-raytracing-object.slang) |
| A RWTexture3D<float4> with no resolved Image Format (Unknown) requires both StorageImageReadWithoutFormat and StorageImageWriteWithoutFormat capabilities. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rwtexture3d-storageimage-capability.slang`](rwtexture3d-storageimage-capability.slang) |
| A cbuffer { ... } block lowers via legalizeConstantBufferLoadForGLSL to a *_std140 Block-decorated struct in Uniform storage class. | functional | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`cbuffer-uniform-std140.slang`](cbuffer-uniform-std140.slang) |
| A mesh entry point with `out vertices` and `out indices` parameters exercises legalizeMeshOutputTypes — the SPIR-V emit declares the MeshShadingEXT capability and an OpEntryPoint MeshEXT with a Position-decorated per-vertex output. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`mesh-shader-output-types.slang`](mesh-shader-output-types.slang) |
| A separate Texture2D and SamplerState pair both lower to UniformConstant-storage variables (the SPIR-V mapping for opaque texture and sampler handles). | boundary | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`uniform-constant-texture-sampler-pair.slang`](uniform-constant-texture-sampler-pair.slang) |
| A vector && (legalizeLogicalAndOr's Khronos arm) lowers to OpLogicalAnd on bool3 operands. | functional | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`vector-logical-and-or.slang`](vector-logical-and-or.slang) |
| An RWTexture2D subscript at the (0,0) origin still lowers to OpImageWrite / OpImageRead - the legalization is shape-driven, not value-driven. | boundary | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`image-subscript-at-origin-zero-zero.slang`](image-subscript-at-origin-zero-zero.slang) |
| Buffer<float4> (texel buffer) lowers to OpTypeImage with dim Buffer; the read dispatches via OpImageFetch and requires the SampledBuffer capability. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`buffer-image-type.slang`](buffer-image-type.slang) |
| ByteAddressBuffer.Load<T>(offset) lowers via legalizeByteAddressBufferOps (SPIR-V uses translateToStructuredBufferOps=true) into an OpAccessChain through the underlying runtime array. | functional | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`byte-address-buffer-access-chain.slang`](byte-address-buffer-access-chain.slang) |
| ConstantBuffer<T> lowers identically to cbuffer { T fields } - a *_std140 Block struct in Uniform storage class. | functional | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`constant-buffer-uniform-std140.slang`](constant-buffer-uniform-std140.slang) |
| InterlockedAdd on a groupshared int lowers to OpAtomicIAdd against a Workgroup-storage pointer; validateAtomicOperations accepts atomics on Workgroup storage in addition to StorageBuffer. | boundary | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`atomic-on-workgroup-storage.slang`](atomic-on-workgroup-storage.slang) |
| InterlockedAdd on a uint UAV lowers to OpAtomicIAdd; validateAtomicOperations inside legalizeIRForSPIRV accepts the operation. | functional | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`atomic-operation-opatomic.slang`](atomic-operation-opatomic.slang) |
| InterlockedCompareExchange lowers to OpAtomicCompareExchange; validateAtomicOperations accepts the CAS form alongside the documented OpAtomicIAdd. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`atomic-compare-exchange.slang`](atomic-compare-exchange.slang) |
| InterlockedMin and InterlockedOr on uint lower to OpAtomicUMin and OpAtomicOr respectively — additional members of the validateAtomicOperations-accepted atomic family. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`atomic-min-or.slang`](atomic-min-or.slang) |
| RWBuffer<float4> lowers to OpTypeImage with dim Buffer and the storage-image sampled bit (2); it requires the ImageBuffer capability and dispatches OpImageWrite / OpImageRead. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rwbuffer-image-type.slang`](rwbuffer-image-type.slang) |
| RWBuffer<float4> triggers the ImageBuffer SPIR-V capability (the storage-side capability that mirrors SampledBuffer for read-only Buffer<T>). | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rwbuffer-imagebuffer-capability.slang`](rwbuffer-imagebuffer-capability.slang) |
| RWByteAddressBuffer.Store<uint>(off, v) lowers to an OpAccessChain through the underlying runtime array followed by OpStore — the write counterpart to the byte-address Load case. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rw-byte-address-buffer-store.slang`](rw-byte-address-buffer-store.slang) |
| RWTexture1D lowers to an OpTypeImage with dim 1D and storage-image sampled-bit 2; legalizeImageSubscript rewrites subscript writes/reads to OpImageWrite / OpImageRead, and the Image1D capability is required. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rwtexture1d-imagewrite.slang`](rwtexture1d-imagewrite.slang) |
| RWTexture1D requires the Image1D SPIR-V capability (distinct from the Sampled1D capability used for read-only Texture1D). | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rwtexture1d-image1d-capability.slang`](rwtexture1d-image1d-capability.slang) |
| RWTexture2D subscript writes lower via legalizeImageSubscript to OpImageWrite and subscript reads to OpImageRead. | functional | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rw-texture-image-subscript.slang`](rw-texture-image-subscript.slang) |
| RWTexture2D.GetDimensions(w, h) dispatches via OpImageQuerySize (no Lod operand) because storage images have no mip chain; the ImageQuery capability is still required. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rwtexture2d-getdimensions-querysize.slang`](rwtexture2d-getdimensions-querysize.slang) |
| RWTexture2D<uint4> emits OpTypeImage with sampled type %uint and image format Rgba32ui (resolveTextureFormat sets the format from the Slang element type). | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rwtexture2d-uint4-format.slang`](rwtexture2d-uint4-format.slang) |
| RWTexture2DArray lowers to OpTypeImage with dim 2D, Arrayed=1, and storage-image sampled-bit 2; writes/reads go through OpImageWrite / OpImageRead. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rwtexture2darray-imagewrite.slang`](rwtexture2darray-imagewrite.slang) |
| RWTexture3D lowers to an OpTypeImage with dim 3D and storage-image sampled-bit 2; legalizeImageSubscript rewrites writes/reads to OpImageWrite / OpImageRead. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`rwtexture3d-imagewrite.slang`](rwtexture3d-imagewrite.slang) |
| Read-only Buffer<float4> triggers the SampledBuffer SPIR-V capability, distinct from RWBuffer which triggers ImageBuffer. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`buffer-sampledbuffer-capability.slang`](buffer-sampledbuffer-capability.slang) |
| Sampler2D lowers to OpTypeSampledImage on SPIR-V because SPIR-V supports combined texture+sampler types natively; lowerCombinedTextureSamplers is skipped. | functional | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`sampler2d-sampled-image.slang`](sampler2d-sampled-image.slang) |
| Sampler2DArray (combined image+sampler) lowers to OpTypeSampledImage backed by an OpTypeImage with dim=2D and Arrayed=1 — combining the Sampler2D combined-sampler claim with the array-image dim claim. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`sampler2darray-combined.slang`](sampler2darray-combined.slang) |
| Texture1D lowers to OpTypeImage with the 1D dimension and triggers the Sampled1D capability. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture1d-image-type.slang`](texture1d-image-type.slang) |
| Texture1D.GetDimensions returns a scalar uint via OpImageQuerySizeLod (no vector result type) — the lower edge of the size-query result-width ladder. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture1d-getdimensions-scalar.slang`](texture1d-getdimensions-scalar.slang) |
| Texture1D.SampleLevel dispatches via OpImageSampleExplicitLod with the Lod image operand — paired with the 1D OpTypeImage. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture1d-samplelevel-explicit-lod.slang`](texture1d-samplelevel-explicit-lod.slang) |
| Texture1DArray lowers to OpTypeImage with dim 1D and the Arrayed bit set; like Texture1D it triggers the Sampled1D capability. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture1darray-image-type.slang`](texture1darray-image-type.slang) |
| Texture2D paired with SamplerComparisonState dispatches SampleCmpLevelZero via OpImageSampleDrefExplicitLod — the depth-reference comparison form of the sample family. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture-samplecmp-dref.slang`](texture-samplecmp-dref.slang) |
| Texture2D.GetDimensions(mip, w, h, levels) dispatches via OpImageQuerySizeLod (returning v2uint) and OpImageQueryLevels; the ImageQuery capability is required. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture2d-getdimensions-querysize-lod.slang`](texture2d-getdimensions-querysize-lod.slang) |
| Texture2D.Load (no sampler, no filtering) dispatches via OpImageFetch with the Lod image operand carrying the mip level. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture2d-load-imagefetch.slang`](texture2d-load-imagefetch.slang) |
| Texture2D.Sample (no explicit Lod) in a fragment shader dispatches via OpImageSampleImplicitLod — the third sibling of the SampleLevel/SampleGrad family. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture2d-sample-implicit-lod.slang`](texture2d-sample-implicit-lod.slang) |
| Texture2D<uint4> emits OpTypeImage with sampled type %uint instead of %float — the resolveTextureFormat / element-type axis of the image-type table. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture2d-int-sampled-type.slang`](texture2d-int-sampled-type.slang) |
| Texture2DArray emits OpTypeImage with dim 2D and the Arrayed=1 flag — a fourth axis of the SPIR-V image-type table. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture-2d-array-sampled-image.slang`](texture-2d-array-sampled-image.slang) |
| Texture2DArray.SampleGrad dispatches via OpImageSampleExplicitLod with Grad on a 2D Arrayed=1 OpTypeImage; gradients are float2 because the layer axis is integer. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture2darray-samplegrad.slang`](texture2darray-samplegrad.slang) |
| Texture2DMS lowers to OpTypeImage with dim 2D and the MS bit set (sixth operand position). | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture2dms-image-type.slang`](texture2dms-image-type.slang) |
| Texture2DMS.Load with a sample index dispatches via OpImageFetch with the Sample image operand instead of Lod (multi-sample images do not have mip levels). | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture2dms-load-fetch.slang`](texture2dms-load-fetch.slang) |
| Texture2DMSArray lowers to OpTypeImage with dim 2D and both Arrayed=1 and MS=1 bits set simultaneously. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture2dmsarray-image-type.slang`](texture2dmsarray-image-type.slang) |
| Texture3D lowers to OpTypeImage with the 3D dimension and OpTypeSampledImage when paired with a sampler — distinct from the Texture2D case the bundle already pins. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture3d-sampled-image.slang`](texture3d-sampled-image.slang) |
| Texture3D.GetDimensions(mip, w, h, d, levels) returns a v3uint from OpImageQuerySizeLod — the result-type width tracks the image dimensionality. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture3d-getdimensions-3d.slang`](texture3d-getdimensions-3d.slang) |
| Texture3D.SampleGrad dispatches via OpImageSampleExplicitLod with the Grad image operand and 3-component dPdx / dPdy vectors. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture3d-samplegrad.slang`](texture3d-samplegrad.slang) |
| Texture3D.SampleLevel dispatches via OpImageSampleExplicitLod with the Lod operand on a 3D OpTypeImage. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture3d-samplelevel.slang`](texture3d-samplelevel.slang) |
| TextureCube lowers to OpTypeImage with the Cube dimension — a third axis of the image-dim boundary table. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture-cube-sampled-image.slang`](texture-cube-sampled-image.slang) |
| TextureCube.GetDimensions(mip, w, h, levels) returns a v2uint via OpImageQuerySizeLod — a cube face is square (width == height), so the result width matches a 2D image even though dim is Cube. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texturecube-getdimensions-v2.slang`](texturecube-getdimensions-v2.slang) |
| TextureCube.SampleGrad dispatches via OpImageSampleExplicitLod with the Grad image operand on a Cube OpTypeImage. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texturecube-samplegrad.slang`](texturecube-samplegrad.slang) |
| TextureCube.SampleLevel dispatches via OpImageSampleExplicitLod with Lod on a Cube OpTypeImage, using a 3-component direction vector. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texturecube-samplelevel.slang`](texturecube-samplelevel.slang) |
| TextureCubeArray lowers to OpTypeImage with dim Cube and the Arrayed bit set (1), sampled via a 4-component vector (xyz + layer). | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texturecubearray-image-type.slang`](texturecubearray-image-type.slang) |
| Using OpImageQuerySize* triggers the ImageQuery SPIR-V capability, emitted only when a size-query is actually present. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`texture-imagequery-capability.slang`](texture-imagequery-capability.slang) |
| Vector \|\| (the OR companion to && in legalizeLogicalAndOr's Khronos arm) lowers to OpLogicalOr on bool3. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`vector-logical-or.slang`](vector-logical-or.slang) |
| collectCooperativeMetadata fires when the program uses a cooperative-vector type — the SPIR-V emit declares OpCapability CooperativeVectorNV and routes coopVecLoad/coopVecMatMul through cooperative-vector opcodes. | expansion | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`cooperative-vector-metadata-capability.slang`](cooperative-vector-metadata-capability.slang) |
| performIntrinsicFunctionInlining (SPIR-V-only, gated on emitSpirvDirectly) and performForceInlining fold a small helper into the caller - no separate OpFunction survives. | functional | [#phase-c-spir-v-legalization-lowering-phi-elimination](../../../design/target-pipelines/spirv.md#phase-c-spir-v-legalization-lowering-phi-elimination) | [`intrinsic-function-inlining.slang`](intrinsic-function-inlining.slang) |
| A -0.0 float literal is preserved as a distinct OpConstant %float -0 in SPIR-V emit (not folded to +0). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`float-negative-zero-constant.slang`](float-negative-zero-constant.slang) |
| A 16-bit integer element in a StructuredBuffer triggers the UniformAndStorageBuffer16BitAccess and Int16 capability declarations alongside Shader. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`uint16-storage-buffer-16bit-capability.slang`](uint16-storage-buffer-16bit-capability.slang) |
| A 2x2 float matrix emits OpTypeMatrix %v2float 2 — the smallest documented matrix shape on the matrix-dim boundary axis (lower edge companion to 4x4). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`matrix-2x2-emit.slang`](matrix-2x2-emit.slang) |
| A 64-bit signed integer element triggers OpCapability Int64 and emits OpTypeInt 64 1. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`int64-storage-buffer-int64-capability.slang`](int64-storage-buffer-int64-capability.slang) |
| A RWStructuredBuffer element access outBuf[idx] lowers to OpAccessChain that indexes through the runtime array inside the wrapping struct. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`buffer-index-access-chain.slang`](buffer-index-access-chain.slang) |
| A RWStructuredBuffer<T> emits an OpTypeRuntimeArray %T declaration wrapped inside the Block-decorated struct — the explicit SPIR-V opcode for "unbounded array". | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`runtime-array-type-decl.slang`](runtime-array-type-decl.slang) |
| A StructuredBuffer of a 3-float4 struct emits ArrayStride 48 on the runtime array (16 bytes per float4 * 3 fields). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`array-stride-struct-element-large.slang`](array-stride-struct-element-large.slang) |
| A StructuredBuffer triggers the SPV_KHR_storage_buffer_storage_class OpExtension declaration. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`storage-buffer-extension.slang`](storage-buffer-extension.slang) |
| A [SpecializationConstant] global emits OpSpecConstant with a SpecId decoration — distinct from OpConstant for plain compile-time constants. | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`spec-constant-spec-id.slang`](spec-constant-spec-id.slang) |
| A [unroll] attribute on a for-loop changes the OpLoopMerge attribute to "Unroll" — companion to the unattributed loop test that emits "None". | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`loop-unroll-spirv-attribute.slang`](loop-unroll-spirv-attribute.slang) |
| A compute entry point emits OpEntryPoint GLCompute %func "main" with the GLCompute execution model. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`entry-point-glcompute.slang`](entry-point-glcompute.slang) |
| A compute entry point's function body is wrapped in OpFunction %void None %funcType ... OpReturn OpFunctionEnd. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`entry-point-function-shape.slang`](entry-point-function-shape.slang) |
| A double element in a StructuredBuffer triggers OpCapability Float64 and emits OpTypeFloat 64. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`double-storage-buffer-float64-capability.slang`](double-storage-buffer-float64-capability.slang) |
| A for loop with a data-dependent bound survives as SPIR-V structured control flow (OpLoopMerge / OpBranchConditional). | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`for-loop-structured-control-flow.slang`](for-loop-structured-control-flow.slang) |
| A fragment entry point emits OpEntryPoint Fragment and an OpExecutionMode %main OriginUpperLeft (the SPIR-V default coordinate origin Slang requests). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`entry-point-fragment-origin-upper-left.slang`](entry-point-fragment-origin-upper-left.slang) |
| A fragment entry point that uses a RasterizerOrderedTexture2D inside beginInvocationInterlock/endInvocationInterlock triggers insertFragmentShaderInterlock — the SPIR-V emit declares the FragmentShaderPixelInterlockEXT capability and adds OpExecutionMode %main PixelInterlockOrderedEXT. | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`fragment-shader-interlock.slang`](fragment-shader-interlock.slang) |
| A full-width float4 swizzle (.wxyz) on a loaded buffer value emits an OpVectorShuffle reorder with indices 3 0 1 2. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`vector-swizzle-wxyz-shuffle.slang`](vector-swizzle-wxyz-shuffle.slang) |
| A groupshared module-scope array lowers to a Workgroup storage class OpVariable in SPIR-V. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`groupshared-workgroup-storage-class.slang`](groupshared-workgroup-storage-class.slang) |
| A half element in a StructuredBuffer triggers OpCapability Float16 and the UniformAndStorageBuffer16BitAccess capability. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`half-storage-buffer-float16-capability.slang`](half-storage-buffer-float16-capability.slang) |
| A read-only StructuredBuffer carries the NonWritable SPIR-V decoration in addition to Block / Binding. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`read-only-buffer-nonwritable.slang`](read-only-buffer-nonwritable.slang) |
| A renamed entry-point function keeps its symbol (%myKernel) but the OpEntryPoint string operand is the canonical "main" name the driver looks up. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`entry-point-name-symbol-vs-string.slang`](entry-point-name-symbol-vs-string.slang) |
| A runtime float / 0.0 emits as OpFDiv %float against an OpConstant %float 0 (no compile-time fold of the divisor); IEEE behavior is left to the runtime. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`float-divide-by-zero-runtime.slang`](float-divide-by-zero-runtime.slang) |
| A runtime inf+inf (computed as (a/0)+(a/0) with data-dependent a) emits OpFAdd %float; the operands are derived from a runtime OpFDiv against +0, not folded at compile time. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`float-inf-plus-inf-runtime.slang`](float-inf-plus-inf-runtime.slang) |
| A switch with 10 cases plus default lowers to a single OpSwitch instruction with all 10 case values and a single default label. | stress | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`switch-many-cases-ten-plus-default.slang`](switch-many-cases-ten-plus-default.slang) |
| A switch with exactly one case plus default still emits OpSwitch (lower edge of case count) with one labeled target and one default target. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`switch-one-case-plus-default.slang`](switch-one-case-plus-default.slang) |
| A uint to int integer reinterpret cast emits an OpBitcast in the SPIR-V module. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`bitcast-uint-to-int.slang`](bitcast-uint-to-int.slang) |
| A uint+uint addition near the uint MAX boundary emits an OpIAdd on %uint and a uint MAX OpConstant; the wrap is left to the runtime (IEEE/two's-complement modular arithmetic on 32-bit unsigned). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`add-uint32-max-overflow.slang`](add-uint32-max-overflow.slang) |
| A vertex entry point that returns SV_Position emits OpEntryPoint Vertex and lowers the position output to gl_Position with the BuiltIn Position decoration. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`entry-point-vertex-position.slang`](entry-point-vertex-position.slang) |
| A write through a nested struct member chains additional OpAccessChain steps and emits an Offset 8 on the outer struct member (the documented Offset decoration per member). | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`nested-struct-access-chain.slang`](nested-struct-access-chain.slang) |
| An asymmetric [numthreads(8,4,2)] propagates exactly to OpExecutionMode %main LocalSize 8 4 2; the three integers preserve their per-axis ordering. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`numthreads-localsize-asymmetric-xyz.slang`](numthreads-localsize-asymmetric-xyz.slang) |
| An explicit [[vk::binding(7, 1)]] decoration places the resource on Binding 7 / DescriptorSet 1 instead of the default Binding 0 / DescriptorSet 0. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`binding-explicit-vk-binding.slang`](binding-explicit-vk-binding.slang) |
| An int element StructuredBuffer carries ArrayStride 4 (lower-edge integer-typed element stride). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`array-stride-int8-element.slang`](array-stride-int8-element.slang) |
| An int4 + int4 add emits a single OpIAdd %v4int — SPIR-V's per-element arithmetic operates on the vector type without unpacking. | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`vector-int-add.slang`](vector-int-add.slang) |
| An undefined identifier in a SPIR-V-target compile is rejected with the documented undefined-identifier diagnostic before the SPIR-V emit pipeline (Phase D) is reached. | negative | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`negative-undefined-identifier.slang`](negative-undefined-identifier.slang) |
| Converting float to int emits OpConvertFToS — the reverse direction of int->float, also distinct from OpBitcast. | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`float-to-int-convert.slang`](float-to-int-convert.slang) |
| Converting int to float in user code emits OpConvertSToF — companion to the documented OpBitcast (which only handles same-width type-pun reinterprets). | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`int-to-float-convert.slang`](int-to-float-convert.slang) |
| Eight distinct RWStructuredBuffer globals receive sequential Binding 0..Binding 7 decorations with DescriptorSet 0 — stresses the sequential-binding allocation past the small-count case. | stress | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`many-uniform-bindings-stress.slang`](many-uniform-bindings-stress.slang) |
| Every SPIR-V emit declares its source language with OpSource Slang 1. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`opsource-slang.slang`](opsource-slang.slang) |
| OpCapability Shader is the first capability declaration in the SPIR-V emit. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`capability-shader.slang`](capability-shader.slang) |
| RWStructuredBuffer<T> lowers to a Block-decorated struct in StorageBuffer storage class with Binding 0 / DescriptorSet 0 decorations. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`rw-structured-buffer-storage-buffer.slang`](rw-structured-buffer-storage-buffer.slang) |
| Reading and writing a groupshared array element produces OpAccessChain against a _ptr_Workgroup pointer, exercising the Workgroup storage class on both the load and store sides. | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`groupshared-workgroup-store-load.slang`](groupshared-workgroup-store-load.slang) |
| SPIR-V direct emit declares the Logical addressing model and the GLSL450 memory model. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`memory-model-logical-glsl450.slang`](memory-model-logical-glsl450.slang) |
| SPIR-V emit begins with the canonical ; SPIR-V text prelude and a ; Version: 1.X line. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`prelude-spirv-header.slang`](prelude-spirv-header.slang) |
| The first declared resource defaults to Binding 0 on DescriptorSet 0 (lower edge of both indices). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`binding-zero-descriptor-set-zero.slang`](binding-zero-descriptor-set-zero.slang) |
| The first struct member always receives OpMemberDecorate ... 0 Offset 0 (lower edge of the offset space). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`struct-offset-zero-first-member.slang`](struct-offset-zero-first-member.slang) |
| The int32 MIN literal (-2147483648, expressed as -2147483647-1) emits as OpConstant %int -2147483648. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`int32-min-literal.slang`](int32-min-literal.slang) |
| The minimum-shape numthreads(1,1,1) emits OpExecutionMode %main LocalSize 1 1 1, exercising the lower edge of all three group-size dimensions. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`numthreads-localsize-one-one-one.slang`](numthreads-localsize-one-one-one.slang) |
| The runtime array inside a StructuredBuffer block carries an ArrayStride decoration matching the element size in bytes. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`runtime-array-stride.slang`](runtime-array-stride.slang) |
| The third member of a struct of three float4 fields receives OpMemberDecorate ... 2 Offset 32 (16-byte stride accumulates to a larger byte offset). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`struct-offset-large-third-member.slang`](struct-offset-large-third-member.slang) |
| The uint32 MAX literal (0xFFFFFFFFu) emits as OpConstant %uint 4294967295 (decimal in spirv-asm). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`uint32-max-literal.slang`](uint32-max-literal.slang) |
| Three nested data-dependent for loops emit exactly three OpLoopMerge instructions; structured control flow preserves the loop nesting. | stress | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`nested-loops-three-deep.slang`](nested-loops-three-deep.slang) |
| Two resources receive sequential Binding 0 and Binding 1 decorations on the default DescriptorSet. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`multiple-resources-sequential-bindings.slang`](multiple-resources-sequential-bindings.slang) |
| Using a transcendental builtin like sin emits OpExtInstImport "GLSL.std.450" and an OpExtInst dispatch — Slang routes math intrinsics through the GLSL std-450 extended instruction set. | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`extinst-import-glsl-std450.slang`](extinst-import-glsl-std450.slang) |
| Using int8_t in a storage-buffer element type emits OpCapability Int8 (the lowest-width signed integer in the per-type-width capability ladder). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`int8-storage-buffer-capability.slang`](int8-storage-buffer-capability.slang) |
| [numthreads(1024,1,1)] lowers to OpExecutionMode %main LocalSize 1024 1 1 (upper-edge X dimension typical of compute shaders). | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`numthreads-localsize-1024-x.slang`](numthreads-localsize-1024-x.slang) |
| [numthreads(8,4,2)] lowers to OpExecutionMode %main LocalSize 8 4 2 in SPIR-V. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`execution-mode-local-size.slang`](execution-mode-local-size.slang) |
| float4 lowers to OpTypeVector %float 4 and int / uint to OpTypeInt 32 (1\|0) in SPIR-V emit. | functional | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`primitive-type-emission.slang`](primitive-type-emission.slang) |
| float4x4 lowers to OpTypeMatrix %v4float 4 and matrix-vector multiplication emits an OpVectorTimesMatrix / OpMatrixTimesVector depending on HLSL row/column-major convention. | boundary | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`matrix-4x4-emit.slang`](matrix-4x4-emit.slang) |
| mul(matA, matB) for 4x4 matrices emits OpMatrixTimesMatrix — the matrix-product half of the SPIR-V matrix-arithmetic opcode family beyond primitive emission. | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`matrix-times-matrix.slang`](matrix-times-matrix.slang) |
| mul(matrix, vector) — companion to the matrix-times-matrix case — emits OpVectorTimesMatrix because Slang's HLSL-derived mul follows the column-major convention. | expansion | [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | [`vector-times-matrix.slang`](vector-times-matrix.slang) |
| Compiling with -profile spirv_1_6 takes the shouldEmitDiscardAsDemote=true branch — discard lowers to OpDemoteToHelperInvocation (not a terminator), and the OpKill fix-up pass is skipped. | boundary | [#spir-v-specific-runtime-predicates](../../../design/target-pipelines/spirv.md#spir-v-specific-runtime-predicates) | [`discard-spirv-16-demote.slang`](discard-spirv-16-demote.slang) |
| Under SPIR-V 1.5 (the Slang default) discard lowers to OpKill — the documented "shouldEmitDiscardAsDemote returns false" branch where removeUnreachableCodeAfterDiscardForOpKill is active. | boundary | [#spir-v-specific-runtime-predicates](../../../design/target-pipelines/spirv.md#spir-v-specific-runtime-predicates) | [`discard-fragment-opkill.slang`](discard-fragment-opkill.slang) |


## Untested claims
| Claim | Reason | Anchor | Why untested |
| --- | --- | --- | --- |
| **`autodiff` / `higherOrderFunc` passes** (`checkAutodiffPatterns`, `specializeHigherOrderParameters`, `finalizeAutoDiffPass`). | (unclassified) | [#autodiff](../../../design/target-pipelines/spirv.md#autodiff) | Reason and explanation to be refined by the next regeneration. |
| **Forward-declared-pointer fixup loop** (Phase D step 12). | (unclassified) | [#checkforrecursivetypes](../../../design/target-pipelines/spirv.md#checkforrecursivetypes) | Only fires on recursive struct-pointer graphs. `checkForRecursiveTypes` errors out before Phase D for user-declared recursive structs. |
| **`coverageTracing`-gated passes** (`instrumentCoverage`, `finalizeCoverageInstrumentationMetadata`). | (unclassified) | [#coveragetracing](../../../design/target-pipelines/spirv.md#coveragetracing) | Reason and explanation to be refined by the next regeneration. |
| **`dynamicResourceHeap`** — SM 6.6 dynamic resource heap. | (unclassified) | [#dynamicresourceheap](../../../design/target-pipelines/spirv.md#dynamicresourceheap) | Reason and explanation to be refined by the next regeneration. |
| **`introduceExplicitGlobalContext`** — gated on `EnableExperimentalPasses`. | (unclassified) | [#introduceexplicitglobalcontext](../../../design/target-pipelines/spirv.md#introduceexplicitglobalcontext) | Reason and explanation to be refined by the next regeneration. |
| **`legalizeUniformBufferLoad`** — anchored to an IR-level canonicalization without a documented text-emit marker. | (unclassified) | [#legalizeuniformbufferload](../../../design/target-pipelines/spirv.md#legalizeuniformbufferload) | Reason and explanation to be refined by the next regeneration. |
| **`removeUnreachableCodeAfterDiscardForOpKill`** — requires `discard` and SPIR-V `< 1.6`; Slang defaults to `>= 1.5` and gates the pass on `!shouldEmitDiscardAsDemote()`. | (unclassified) | [#removeunreachablecodeafterdiscardforopkill](../../../design/target-pipelines/spirv.md#removeunreachablecodeafterdiscardforopkill) | Reason and explanation to be refined by the next regeneration. |
| **`simplifyIRForSpirvLegalization` outer/inner loop bounds** (`kMaxIterations = 8`, `kMaxFuncIterations = 16`). The loops terminate by fixed-point in practice; no documented user-source pattern hits the bound. | (unclassified) | [#simplifyirforspirvlegalization](../../../design/target-pipelines/spirv.md#simplifyirforspirvlegalization) | Reason and explanation to be refined by the next regeneration. |
| **`unexportNonEmbeddableIR`** — gated on `EmbedDownstreamIR`. | (unclassified) | [#unexportnonembeddableir](../../../design/target-pipelines/spirv.md#unexportnonembeddableir) | Reason and explanation to be refined by the next regeneration. |
| **Cross-target probes.** The bundle is single-target by design — SPIR-V observations only. "X fires on SPIR-V but Y doesn't" claims are documented in the `## Conditional gates` section of the doc but not exercised as multi-target CHECK tests in this bundle. | (unclassified) | [#x-fires-on-spir-v-but-y-doesnt](../../../design/target-pipelines/spirv.md#x-fires-on-spir-v-but-y-doesnt) | Reason and explanation to be refined by the next regeneration. |
| **Pass-ordering claims** (Phase A passes 1-19, Phase B passes 1-65, Phase C passes 1-41, Phase D steps 1-20). Pass _existence_ is observable via emit; pass _ordering_ would require `-dump-ir` cross-pass comparison without doc-anchored ordering markers. Covered by `pipeline/05-ir-passes`. | implementation-detail | (unspecified) | Internal compiler choice (pass ordering, hoistability decisions, deduplication) with no test-directive that reveals it. |

| **spirv-link**. | gpu-spirv-tools | [#downstream-spirv-link-spirv-val-spirv-opt-chain](../../../design/target-pipelines/spirv.md#downstream-spirv-link-spirv-val-spirv-opt-chain) | Only invoked when `spirvFiles.getCount() > 1`, i.e. when there is an `IREmbeddedDownstreamIR` of `CodeGenTarget::SPIRV` to merge with the freshly emitted module. A single `//TEST:SIMPLE` directive cannot set up `-embed-downstream-ir` plus a pre-built `.slang-module` artifact; the multi-file `COMPILE` + `SIMPLE` chain is not in the `//TEST:SIMPLE`-only validation contract this bundle uses. |
| **spirv-opt**. Invoked via the generic downstream path; options-dependent whether it changes anything. The inline `optimizeSPIRV` call site is `#if 0`'d out (doc-only). | gpu-spirv-tools | [#downstream-spirv-link-spirv-val-spirv-opt-chain](../../../design/target-pipelines/spirv.md#downstream-spirv-link-spirv-val-spirv-opt-chain) | The downstream-compile invocation does not change the emitted spirv-asm text deterministically; FileCheck cannot pin an observable artifact. CI can observe whether spirv-opt fails on the emitted SPIR-V (a process-level signal), but that is outside the `-target spirv-asm` text the bundle checks. |


## Doc gaps observed
| Anchor | Kind | Gap | Suggested addition |
| --- | --- | --- | --- |
| [#downstream-spirv-link-spirv-val-spirv-opt-chain](../../../design/target-pipelines/spirv.md#downstream-spirv-link-spirv-val-spirv-opt-chain) | drift-from-source | The doc names `-validate-spirv` as the CLI flag that enables `spirv-val`. No such option exists in `slang-options.cpp`; the only controls are the `SLANG_RUN_SPIRV_VALIDATION` env var and the opt-out flag `-skip-spirv-validation`. slangc rejects `-validate-spirv` with E00017. | Replace the `-validate-spirv` references with the actual surface (`SLANG_RUN_SPIRV_VALIDATION` env var + `-skip-spirv-validation` opt-out), or implement the flag and add a real CLI test. |
| [#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools](../../../design/target-pipelines/spirv.md#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools) | undocumented-behavior | The doc enumerates the Phase D word-emission steps (`emitDebug`, `emitParams`, `emitEPs`, `emitFront`, `emitPhys`) but does not name the canonical SPIR-V text prelude markers (`; SPIR-V`, `; Version: 1.X`, `OpCapability Shader`, `OpMemoryModel Logical GLSL450`, `SPV_KHR_storage_buffer_storage_class`, `OpSource Slang 1`). | A one-line statement of these prelude facts would let a test anchor them precisely; the bundle anchors them to the general `#phase-d-ir-to-spir-v-emit-simplification-loop-downstream-tools` section. |
| [#storagebuffer](../../../design/target-pipelines/spirv.md#storagebuffer) | undocumented-behavior | The doc does not name the storage-class assignments (`StorageBuffer` for `StructuredBuffer`, `Uniform` for `cbuffer`, `Workgroup` for `groupshared`, `UniformConstant` for textures/samplers, `Input` for varying inputs). These are core to the SPIR-V emit but are inferred via cross-referencing `legalizeEntryPointsForGLSL` and the per-target storage-class table. A doc table enumerating storage classes per Slang resource type would be useful. |  |
| [#legalizeconstantbufferloadforglsl](../../../design/target-pipelines/spirv.md#legalizeconstantbufferloadforglsl) | undocumented-behavior | The doc names `legalizeConstantBufferLoadForGLSL` and `lowerBufferElementTypeToStorageType` but does not state that the emitted SPIR-V uses the `*_std140`-suffixed wrapper-struct convention (visible in `OpName %SLANG_ParameterGroup_<name>_std140 ...`). | A one-line statement of this would let a test pin the exact spelling rather than a regex; the bundle uses regex wildcards. |
| [#source](../../../design/target-pipelines/spirv.md#source) | undocumented-behavior | The doc's `## Source` table cites line numbers (`linkAndOptimizeIR` at line ~893, `emitSPIRVForEntryPointsDirectly` at line ~3122, etc.). These are navigation aids and not user-observable; no test. |  |
| [#simplifyirforspirvlegalization](../../../design/target-pipelines/spirv.md#simplifyirforspirvlegalization) | undocumented-behavior | The doc states `simplifyIRForSpirvLegalization` settles in 2-3 outer iterations in practice but has no documented observable consequence; no test. |  |
| [#addusertypehintdecorations](../../../design/target-pipelines/spirv.md#addusertypehintdecorations) | undocumented-behavior | The doc lists `addUserTypeHintDecorations` as gated on `VulkanEmitReflection`. The doc does not name an emit marker; this is a reflection-only feature, not a SPIR-V text observable. No test. |  |
| [#forward-declared-pointer-fixup](../../../design/target-pipelines/spirv.md#forward-declared-pointer-fixup) | undocumented-behavior | The doc's `## Forward-declared pointer fixup` describes the loop but does not give a user-source pattern that triggers it. Slang's `checkForRecursiveTypes` rejects recursive structs, so the loop is unreachable from compute-stage user code. No test. |  |
| [#main](../../../design/target-pipelines/spirv.md#main) | undocumented-behavior | The doc says `OpEntryPoint` operands include all variables the entry point references but does not say the entry-point string operand is always `"main"` regardless of the Slang entry-point function name. We pin this via experiment in the test `entry-point-name-symbol-vs-string.slang`; a doc statement would be cleaner. |  |
| [#validateatomicoperations](../../../design/target-pipelines/spirv.md#validateatomicoperations) | undocumented-behavior | The doc says `validateAtomicOperations` is called inside `legalizeIRForSPIRV` but does not state which SPIR-V opcode `InterlockedAdd` lowers to. We pin `OpAtomicIAdd` from emit-experiment. |  |
| [#legalizelogicalandor](../../../design/target-pipelines/spirv.md#legalizelogicalandor) | undocumented-behavior | The doc says `legalizeLogicalAndOr` runs for SPIR-V (Khronos arm) but does not state the SPIR-V opcode the vector `&&` produces. We pin `OpLogicalAnd` from emit-experiment. |  |
| [#legalizeentrypointsforglsl](../../../design/target-pipelines/spirv.md#legalizeentrypointsforglsl) | undocumented-behavior | The doc names `legalizeEntryPointsForGLSL` but does not list the HLSL-SV-to-SPIR-V builtin mapping table (`SV_DispatchThreadID -> gl_GlobalInvocationID`, `SV_GroupThreadID -> gl_LocalInvocationID`, `SV_GroupID -> gl_WorkGroupID`, `SV_VertexID -> gl_VertexIndex`, `SV_InstanceID -> gl_InstanceIndex`, etc.). The new builtin tests pin these mappings from emit-experiment; a doc table would make them anchorable to a specific row. |  |
| [#validateatomicoperations](../../../design/target-pipelines/spirv.md#validateatomicoperations) | undocumented-behavior | The doc names `validateAtomicOperations` but lists only `IAdd` as the example. We pin `OpAtomicCompareExchange`, `OpAtomicUMin`, and `OpAtomicOr` from emit-experiment as additional accepted opcodes. |  |
| [#legalizeimagesubscript](../../../design/target-pipelines/spirv.md#legalizeimagesubscript) | undocumented-behavior | The doc names `legalizeImageSubscript` and `OpTypeSampledImage` but does not enumerate the SPIR-V image-dim values for the Slang texture-type family (`Texture2D`/`Texture3D`/`TextureCube`/ `Texture2DArray`). The dim and Arrayed bit are user-observable in spirv-asm and would benefit from explicit doc rows. |  |
| [#no-indication](../../../design/target-pipelines/spirv.md#no-indication) | undocumented-behavior | The doc does not state the full 7-operand `OpTypeImage` encoding emitted by Slang (`%sampled-type Dim Depth Arrayed MS Sampled Format`). In particular: the Depth operand is `2` ("no indication" / Unknown) rather than the SPIR-V-spec default of `0`, and the Format operand is `Unknown` for `Texture*<floatN>` but `Rgba32ui` for `RWTexture*<uintN>` (set by `resolveTextureFormat`). The new image-type tests pin these bit positions from emit-experiment. |  |
| [#legalizeimagesubscript](../../../design/target-pipelines/spirv.md#legalizeimagesubscript) | undocumented-behavior | The doc enumerates `legalizeImageSubscript` for storage-image writes/reads but does not name the SPIR-V capability ladder for the texture-type family: `Sampled1D` / `Image1D` (1D dim split by sampled-vs-storage), `SampledBuffer` / `ImageBuffer` (Buffer dim split), and `StorageImageReadWithoutFormat` / `StorageImageWriteWithoutFormat` (RW texture with unresolved format). The new capability tests pin these. |  |
| [#opimagesampleimplicitlod](../../../design/target-pipelines/spirv.md#opimagesampleimplicitlod) | undocumented-behavior | The doc does not separate the sampled-image dispatch family (`OpImageSampleImplicitLod` / `OpImageSampleExplicitLod` with `Lod` / `Grad` operands / `OpImageSampleDrefExplicitLod` for the comparison form) from the texel-fetch family (`OpImageFetch` with `Lod` for sampled images, `Sample` for MS images, `OpImageRead` for storage images). All these opcodes are user-observable in spirv-asm; the new SampleLevel / SampleGrad / SampleImplicitLod / Load / SampleCmpLevelZero tests pin them from emit-experiment. |  |
| [#opimagequerysizelod](../../../design/target-pipelines/spirv.md#opimagequerysizelod) | undocumented-behavior | The doc does not name the SPIR-V size-query opcode pair `OpImageQuerySizeLod` (sampled images) vs `OpImageQuerySize` (storage images, no Lod) and `OpImageQueryLevels` for the mip count, nor that the `ImageQuery` capability is emitted only when a size/level query is present. The new GetDimensions tests pin this distinction. |  |
| [#isspirv16orlater](../../../design/target-pipelines/spirv.md#isspirv16orlater) | undocumented-behavior | The doc references SPIR-V version selection (`isSpirv16OrLater`, `shouldEmitDiscardAsDemote`) but does not name the `-profile spirv_1_6` command-line knob that flips the predicate. The two discard tests pin behavior on both sides; a doc note linking the CLI flag to the predicate would make this anchorable. |  |
| [#int8](../../../design/target-pipelines/spirv.md#int8) | undocumented-behavior | The doc mentions Khronos-target capability emission per type width but does not enumerate the integer-width capability ladder (`Int8` / `Int16` / `Int64`) or where the corresponding `UniformAndStorageBuffer*BitAccess` extensions trigger. |  |
| [#glslstd450](../../../design/target-pipelines/spirv.md#glslstd450) | undocumented-behavior | The doc mentions `GLSL.std.450` only obliquely (the via-GLSL legacy path). The SPIR-V direct-emit path also relies on `OpExtInstImport "GLSL.std.450"` for transcendentals; this belongs in the doc's "common to all SPIR-V emit" list. |  |
| [#spec-constants-emit-as-opspecconstant](../../../design/target-pipelines/spirv.md#spec-constants-emit-as-opspecconstant) | undocumented-behavior | The doc does not name `OpSpecConstant` or the `[SpecializationConstant]` -> `SpecId` mapping. Specialization constants are core to Vulkan but the doc treats them as out of scope; a one-line "spec-constants emit as OpSpecConstant" note would let the bundle anchor that claim. |  |
| [#oploopmerge](../../../design/target-pipelines/spirv.md#oploopmerge) | undocumented-behavior | The doc names `OpLoopMerge` for structured control flow but does not state that `[unroll]` adds the `Unroll` annotation (and `[loop]` adds `DontUnroll`). Pinned by emit-experiment. |  |
