Skip to content

add support for unions#569

Open
39ali wants to merge 2 commits into
Rust-GPU:mainfrom
39ali:unions
Open

add support for unions#569
39ali wants to merge 2 commits into
Rust-GPU:mainfrom
39ali:unions

Conversation

@39ali

@39ali 39ali commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Rust unions require reinterpreting the bits of one type as another — e.g. reading [f32; 5] as struct { a: i32, b: [f32; 3], c: u32 }. In SPIR-V this would naively lower to an OpBitcast between two composite types, which is illegal by the spec: OpBitcast only accepts scalar, vector, or pointer operands, never structs or arrays.

previously, any union field access involving layout-compatible but differently-typed SPIR-V composites would either produce invalid SPIR-V or fail to compile.

solution:

implement two new codegen paths in builder_methods.rs that handle type-mismatched copies without relying on composite OpBitcast:

memcpy_const_size — used by the memcpy / OpCopyMemory path (pointer-to-pointer copies).

try_store_composite_scalar_leaves — used by the store path (value-to-pointer writes).

Both work by:

decomposing both the source and destination types recursively into scalar leaves (collect_scalar_leaves), collecting the OpAccessChain index path, SPIR-V type, and bit width for each leaf.

grouping leaves by equal total bit widths (group_leaves_by_bits), this handles both 1:1 (same scalar count) and N:M (different counts, same total bits) cases.

emitting element-wise copies per group:

1→1: OpLoad + optional OpBitcast + OpStore
N→1 (pack): load N scalars, OpCompositeConstruct into an intermediate uN×k vector, OpBitcast to the wider dst type, OpStore
1→N (unpack): OpLoad, OpBitcast to an intermediate uN×k vector, OpCompositeExtract each component, OpStore each

solves #241

@Firestar99 Firestar99 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like the simplicity of the implementation, this was pretty easy to review, but it does ring "AI impl" bells.

/// support union field access, e.g. `[f32; 5]` -> `struct { a: f64, b: [f32; 3] }`.
/// handles both 1:1 (same scalar count) and N:M (different counts but same total bits)
/// returns `false` if the types are not compatible.
fn memcpy_const_size(&mut self, dst: SpirvValue, src: SpirvValue) -> bool {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What's the difference between memcpy_const_size() and try_store_composite_scalar_leaves()? Both functions seem to be almost the exact same, apart from memcpy's arg being src: SpirvValue as a pointer and the other one val: SpirvValue as a value.

}
}

_ => return false,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see an implementation for 1 => 1, 1 => N, N => 1 but no N => M?

This compiletest fails:

// build-pass
// compile-flags: -C target-feature=+Int64

use spirv_std::spirv;

union MyUnion {
    a: (u32, u64),
    b: (u64, u32),
}

#[spirv(fragment)]
pub fn union_mixed_types(
    out: &mut u64,
) {
    let bla = MyUnion { a: (42, 123) };
    *out = unsafe { bla.b }.0;
}

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants