Skip to content

Conversation

@gtong-nv
Copy link
Contributor

@gtong-nv gtong-nv commented Jan 20, 2026

Problem

When using Conditional<float, true> with the SV_Depth semantic, the SPIR-V emitter was generating invalid SPIR-V code. The issue stemmed from Conditional<T, hasValue> being implemented using T[hasValue] (arrays), which caused:

  1. Invalid SPIR-V for FragDepth: Vulkan requires gl_FragDepth to be a scalar float, but the array-based implementation resulted in an array type pointer
  2. Type layout crashes: Non-constant array sizes in conditionals caused crashes during varying parameter processing when getIntVal() expected a compile-time constant

Related to #9578

Solution

1. Change Conditional Implementation to Use Vectors

Before:

struct Conditional<T, bool hasValue>
{
    internal T storage[hasValue];
    // ...
}

After:

struct Conditional<T, bool hasValue>
{
    internal vector<T, hasValue ? 1 : 0> storage;
    // ...
}

This change:

  • Uses vector<T, 1> when hasValue = true (which gets legalized to scalar T)
  • Uses vector<T, 0> when hasValue = false (which gets legalized to void)

2. Implement 0-Length Vector Legalization

Added comprehensive support for 0-length vectors in slang-ir-legalize-vector-types.cpp:

  • Detection: Added is0Vector() and related helper functions
  • Value operations: Operations that create or access 0-vectors are replaced with appropriate values:
    • IRMakeVector / IRMakeVectorFromScalarvoid value
    • IRGetElement / IRSwizzlepoison (undefined behavior)
    • IRGetElementPtrpoison
    • IRSwizzledStore → removed (no-op)
  • Type replacement: 0-vector types are replaced with IRVoidType

3. Fix Type Layout for Non-Constant Vector Sizes

Added safety checks in slang-type-layout.cpp to handle vectors with non-constant element counts:

auto elementCountVal = context.tryResolveLinkTimeVal(vecType->getElementCount());
if (!as<ConstantIntVal>(elementCountVal))
{
    // Fall back to default layout, defer to IR legalization
    RefPtr<TypeLayout> typeLayout = new TypeLayout();
    typeLayout->type = type;
    typeLayout->rules = rules;
    return TypeLayoutResult(typeLayout, element.info);
}

This prevents crashes in two locations:

  • General type layout creation (line ~5346)
  • Varying parameter layout creation (line ~6052)

4. Move Vector Legalization Pass Earlier

Moved legalizeVectorTypes to run immediately after legalizeEmptyArray and before resource/existential type legalization. This ensures:

  • 0-vectors are eliminated before they can cause issues in downstream passes
  • Consistent with the empty array handling approach

5. Update Validation

Updated slang-ir-validate.cpp to allow 0-length vectors as they are now used internally for conditional varying values and will be legalized away.

Testing

  • ✅ Added tests/bugs/conditional-sv-depth.slang - Verifies correct SPIR-V generation for Conditional<float, true> with SV_Depth
  • ✅ Updated tests/diagnostics/invalid-vector-element-count.slang - Removed error check for 0-length vectors as they're now valid internally
  • ✅ Existing tests pass with the new implementation

Impact

  • Breaking Changes: None for user code - this is an internal implementation detail
  • Performance: Minimal - 1-vectors and 0-vectors are legalized to more efficient representations
  • Targets: All targets benefit from proper legalization, especially SPIR-V/Vulkan

Key Files Changed

  • source/slang/core.meta.slang - Conditional implementation change
  • source/slang/slang-ir-legalize-vector-types.cpp - 0-vector legalization
  • source/slang/slang-type-layout.cpp - Non-constant vector size handling
  • source/slang/slang-emit.cpp - Pass ordering
  • source/slang/slang-ir-validate.cpp - Validation update

@gtong-nv gtong-nv requested a review from a team as a code owner January 20, 2026 04:38
@gtong-nv gtong-nv requested review from Copilot and removed request for a team January 20, 2026 04:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a SPIR-V validation error that occurs when using Conditional<float, true> with the SV_Depth semantic. The issue arises because Conditional<T, true> internally expands to a single-element array T[1], but Vulkan's SPIR-V specification requires FragDepth to be a scalar float, not an array.

Changes:

  • Added a new legalization pass legalizeDepthOutputTypes() that transforms depth-related outputs from pointer-to-single-element-array to pointer-to-scalar
  • Added helper function isDepthSystemValue() to identify depth-related system value semantics
  • Added test case to verify the fix generates valid SPIR-V with scalar FragDepth

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
tests/bugs/conditional-sv-depth.slang Regression test that verifies Conditional<float, true> with SV_Depth generates valid SPIR-V with scalar FragDepth variable
source/slang/slang-ir-spirv-legalize.cpp Implements legalization pass to unwrap single-element arrays from depth outputs, fixing store/load instructions to maintain correct types

@gtong-nv
Copy link
Contributor Author

/format

@slangbot
Copy link
Contributor

🌈 Formatted, please merge the changes from this PR

gtong-nv pushed a commit that referenced this pull request Jan 20, 2026
Automated code formatting for
#9655

Co-authored-by: slangbot <[email protected]>

// Legalize depth output types before other processing.
// This unwraps single-element arrays from SV_Depth system values.
legalizeDepthOutputTypes();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering the legalization may need to happen in another place.

legalizeSystemValueParameters(entryPoint);

@gtong-nv gtong-nv force-pushed the fix-conditional-fragDepth branch from 2a6e27d to b28de5d Compare January 28, 2026 00:07
@gtong-nv gtong-nv changed the title Legalize Conditional for SV_Depth to fix invalid spirv Change Conditional<> to Use Vector internally Jan 28, 2026
@gtong-nv gtong-nv added the pr: non-breaking PRs without breaking changes label Jan 28, 2026
@csyonghe csyonghe added this pull request to the merge queue Jan 28, 2026
github-merge-queue bot pushed a commit that referenced this pull request Jan 28, 2026
## Problem

When using `Conditional<float, true>` with the `SV_Depth` semantic, the
SPIR-V emitter was generating invalid SPIR-V code. The issue stemmed
from `Conditional<T, hasValue>` being implemented using `T[hasValue]`
(arrays), which caused:

1. **Invalid SPIR-V for FragDepth**: Vulkan requires `gl_FragDepth` to
be a scalar `float`, but the array-based implementation resulted in an
array type pointer
2. **Type layout crashes**: Non-constant array sizes in conditionals
caused crashes during varying parameter processing when `getIntVal()`
expected a compile-time constant

Related to #9578

## Solution

### 1. Change Conditional Implementation to Use Vectors

**Before:**
```slang
struct Conditional<T, bool hasValue>
{
    internal T storage[hasValue];
    // ...
}
```

**After:**
```slang
struct Conditional<T, bool hasValue>
{
    internal vector<T, hasValue ? 1 : 0> storage;
    // ...
}
```

This change:
- Uses `vector<T, 1>` when `hasValue = true` (which gets legalized to
scalar `T`)
- Uses `vector<T, 0>` when `hasValue = false` (which gets legalized to
void)

### 2. Implement 0-Length Vector Legalization

Added comprehensive support for 0-length vectors in
`slang-ir-legalize-vector-types.cpp`:

- **Detection**: Added `is0Vector()` and related helper functions
- **Value operations**: Operations that create or access 0-vectors are
replaced with appropriate values:
  - `IRMakeVector` / `IRMakeVectorFromScalar` → `void` value
  - `IRGetElement` / `IRSwizzle` → `poison` (undefined behavior)
  - `IRGetElementPtr` → `poison` 
  - `IRSwizzledStore` → removed (no-op)
- **Type replacement**: 0-vector types are replaced with `IRVoidType`

### 3. Fix Type Layout for Non-Constant Vector Sizes

Added safety checks in `slang-type-layout.cpp` to handle vectors with
non-constant element counts:

```cpp
auto elementCountVal = context.tryResolveLinkTimeVal(vecType->getElementCount());
if (!as<ConstantIntVal>(elementCountVal))
{
    // Fall back to default layout, defer to IR legalization
    RefPtr<TypeLayout> typeLayout = new TypeLayout();
    typeLayout->type = type;
    typeLayout->rules = rules;
    return TypeLayoutResult(typeLayout, element.info);
}
```

This prevents crashes in two locations:
- General type layout creation (line ~5346)
- Varying parameter layout creation (line ~6052)

### 4. Move Vector Legalization Pass Earlier

Moved `legalizeVectorTypes` to run immediately after
`legalizeEmptyArray` and before resource/existential type legalization.
This ensures:
- 0-vectors are eliminated before they can cause issues in downstream
passes
- Consistent with the empty array handling approach

### 5. Update Validation

Updated `slang-ir-validate.cpp` to allow 0-length vectors as they are
now used internally for conditional varying values and will be legalized
away.

## Testing

- ✅ Added `tests/bugs/conditional-sv-depth.slang` - Verifies correct
SPIR-V generation for `Conditional<float, true>` with `SV_Depth`
- ✅ Updated `tests/diagnostics/invalid-vector-element-count.slang` -
Removed error check for 0-length vectors as they're now valid internally
- ✅ Existing tests pass with the new implementation

## Impact

- **Breaking Changes**: None for user code - this is an internal
implementation detail
- **Performance**: Minimal - 1-vectors and 0-vectors are legalized to
more efficient representations
- **Targets**: All targets benefit from proper legalization, especially
SPIR-V/Vulkan

## Key Files Changed

- `source/slang/core.meta.slang` - Conditional implementation change
- `source/slang/slang-ir-legalize-vector-types.cpp` - 0-vector
legalization
- `source/slang/slang-type-layout.cpp` - Non-constant vector size
handling
- `source/slang/slang-emit.cpp` - Pass ordering
- `source/slang/slang-ir-validate.cpp` - Validation update

---------

Co-authored-by: slangbot <[email protected]>
Co-authored-by: slangbot <[email protected]>
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 28, 2026
@gtong-nv gtong-nv added this pull request to the merge queue Jan 28, 2026
Merged via the queue into master with commit f0719cb Jan 28, 2026
38 of 39 checks passed
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.

5 participants