From beac9c7fc64b69bb0c38c5d301b48b584f283fc2 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Mon, 8 Sep 2025 13:16:44 +0200 Subject: [PATCH 01/20] abi layout difftest: add difftest for layout size and alignment --- tests/difftests/tests/Cargo.lock | 17 +++++++++ tests/difftests/tests/Cargo.toml | 2 + .../lang/abi/vector_layout/cpu/Cargo.toml | 15 ++++++++ .../abi/vector_layout/cpu/src/cpu_driver.rs | 9 +++++ .../lang/abi/vector_layout/cpu/src/layout.rs | 37 +++++++++++++++++++ .../lang/abi/vector_layout/cpu/src/lib.rs | 8 ++++ .../lang/abi/vector_layout/cpu/src/main.rs | 3 ++ .../lang/abi/vector_layout/cpu/src/shader.rs | 7 ++++ .../vector_layout/cpu/src/shader_driver.rs | 13 +++++++ .../abi/vector_layout/rust-gpu/Cargo.toml | 16 ++++++++ .../abi/vector_layout/rust-gpu/src/lib.rs | 3 ++ .../abi/vector_layout/rust-gpu/src/main.rs | 3 ++ 12 files changed, 133 insertions(+) create mode 100644 tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml create mode 100644 tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout/rust-gpu/Cargo.toml create mode 100644 tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/lib.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs diff --git a/tests/difftests/tests/Cargo.lock b/tests/difftests/tests/Cargo.lock index 74d87cce66..56cbc7a14e 100644 --- a/tests/difftests/tests/Cargo.lock +++ b/tests/difftests/tests/Cargo.lock @@ -2,6 +2,23 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "abi-vector-layout-cpu" +version = "0.0.0" +dependencies = [ + "bytemuck", + "difftest", + "spirv-std", +] + +[[package]] +name = "abi-vector-layout-rust-gpu" +version = "0.0.0" +dependencies = [ + "abi-vector-layout-cpu", + "difftest", +] + [[package]] name = "allocator-api2" version = "0.2.21" diff --git a/tests/difftests/tests/Cargo.toml b/tests/difftests/tests/Cargo.toml index 7c74495325..fa3078d664 100644 --- a/tests/difftests/tests/Cargo.toml +++ b/tests/difftests/tests/Cargo.toml @@ -16,6 +16,8 @@ members = [ "arch/push_constants/push_constants-wgsl", "storage_class/array_access/array_access-rust", "storage_class/array_access/array_access-wgsl", + "lang/abi/vector_layout/cpu", + "lang/abi/vector_layout/rust-gpu", "lang/control_flow/control_flow-rust", "lang/control_flow/control_flow-wgsl", "lang/control_flow_complex/control_flow_complex-rust", diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml new file mode 100644 index 0000000000..db41132735 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "abi-vector-layout-cpu" +edition.workspace = true + +[lints] +workspace = true + +# GPU deps +[dependencies] +spirv-std.workspace = true +bytemuck.workspace = true + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs new file mode 100644 index 0000000000..9a6f85c3d9 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs @@ -0,0 +1,9 @@ +use crate::layout::eval_layouts; +use difftest::config::Config; + +pub fn run() { + let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); + config + .write_result(bytemuck::bytes_of(&eval_layouts())) + .unwrap() +} diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs new file mode 100644 index 0000000000..ff8d6a067d --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs @@ -0,0 +1,37 @@ +use bytemuck::{Pod, Zeroable}; +use spirv_std::glam::*; + +#[repr(C)] +#[derive(Copy, Clone, Zeroable, Pod)] +pub struct SizeAndAlign { + pub size: u32, + pub align: u32, +} + +impl SizeAndAlign { + pub fn from() -> Self { + Self { + size: size_of::() as u32, + align: align_of::() as u32, + } + } + + pub fn flatten(&self) -> [u32; 2] { + [self.size, self.align] + } +} + +pub type EvalResult = [SizeAndAlign; 8]; + +pub fn eval_layouts() -> EvalResult { + [ + SizeAndAlign::from::(), + SizeAndAlign::from::(), + SizeAndAlign::from::(), + SizeAndAlign::from::(), + SizeAndAlign::from::(), + SizeAndAlign::from::(), + SizeAndAlign::from::(), + SizeAndAlign::from::(), + ] +} diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs new file mode 100644 index 0000000000..4838d65e2f --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs @@ -0,0 +1,8 @@ +#![cfg_attr(target_arch = "spirv", no_std)] + +#[cfg(not(target_arch = "spirv"))] +pub mod cpu_driver; +pub mod layout; +pub mod shader; +#[cfg(not(target_arch = "spirv"))] +pub mod shader_driver; diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs new file mode 100644 index 0000000000..cecb91f595 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + abi_vector_layout_cpu::cpu_driver::run(); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs new file mode 100644 index 0000000000..f2ced20993 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs @@ -0,0 +1,7 @@ +use crate::layout::{EvalResult, eval_layouts}; +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main_cs(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] output: &mut EvalResult) { + *output = eval_layouts(); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs new file mode 100644 index 0000000000..ee99bba190 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs @@ -0,0 +1,13 @@ +use crate::layout::EvalResult; +use difftest::config::Config; +use difftest::scaffold::compute::{BufferConfig, RustComputeShader, WgpuComputeTestMultiBuffer}; + +pub fn run() { + let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); + let test = WgpuComputeTestMultiBuffer::new( + RustComputeShader::default(), + [1, 1, 1], + Vec::from(&[BufferConfig::writeback(size_of::())]), + ); + test.run_test(&config).unwrap(); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/Cargo.toml new file mode 100644 index 0000000000..e178ce6d25 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "abi-vector-layout-rust-gpu" +edition.workspace = true + +[lib] +crate-type = ["lib", "dylib"] + +[lints] +workspace = true + +[dependencies] +abi-vector-layout-cpu = { path = "../cpu" } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/lib.rs b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/lib.rs new file mode 100644 index 0000000000..219229aa2e --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/lib.rs @@ -0,0 +1,3 @@ +#![cfg_attr(target_arch = "spirv", no_std)] + +pub use abi_vector_layout_cpu::shader::main_cs; diff --git a/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs new file mode 100644 index 0000000000..98fa9c5549 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + abi_vector_layout_cpu::shader_driver::run(); +} From 1e1922fb94d0a67703024eeccf696c5566611c0d Mon Sep 17 00:00:00 2001 From: firestar99 Date: Mon, 8 Sep 2025 16:57:42 +0200 Subject: [PATCH 02/20] abi layout difftest: add member offset checking --- .../abi/vector_layout/cpu/src/cpu_driver.rs | 10 +- .../lang/abi/vector_layout/cpu/src/layout.rs | 102 +++++++++++++----- .../lang/abi/vector_layout/cpu/src/shader.rs | 10 +- .../vector_layout/cpu/src/shader_driver.rs | 6 +- 4 files changed, 90 insertions(+), 38 deletions(-) diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs index 9a6f85c3d9..bbfa3b6289 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs @@ -1,9 +1,11 @@ -use crate::layout::eval_layouts; +use crate::layout::{LAYOUT_COUNT, LAYOUT_LEN, eval_layouts}; use difftest::config::Config; pub fn run() { let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); - config - .write_result(bytemuck::bytes_of(&eval_layouts())) - .unwrap() + let mut out = vec![0; LAYOUT_LEN]; + for gid in 0..LAYOUT_COUNT { + eval_layouts(gid as u32, &mut out); + } + config.write_result(&out).unwrap() } diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs index ff8d6a067d..8751a076d2 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs @@ -1,37 +1,83 @@ -use bytemuck::{Pod, Zeroable}; +use core::mem::offset_of; +use experiments::*; use spirv_std::glam::*; -#[repr(C)] -#[derive(Copy, Clone, Zeroable, Pod)] -pub struct SizeAndAlign { - pub size: u32, - pub align: u32, -} +pub struct BumpAlloc(usize); -impl SizeAndAlign { - pub fn from() -> Self { - Self { - size: size_of::() as u32, - align: align_of::() as u32, - } +impl BumpAlloc { + pub fn inc(&mut self) -> usize { + let old = self.0; + self.0 += 1; + old } +} + +pub trait WriteLayout { + fn write_layout(offset: &mut BumpAlloc, out: &mut [u32]); +} + +macro_rules! write_layout { + ($out:ident, $offset:ident, $name:ident($($member:ident),*)) => { + { + // offset 0: size + $out[$offset.inc()] = size_of::<$name>() as u32; + // offset 4: alignment + $out[$offset.inc()] = align_of::<$name>() as u32; + // offset 8 onwards: members + $($out[$offset.inc()] = offset_of!($name, $member) as u32;)* + } + }; +} - pub fn flatten(&self) -> [u32; 2] { - [self.size, self.align] +/// gid is checked between `0..LAYOUT_COUNT` +pub const LAYOUT_COUNT: usize = 0x40; +/// at byte offset 0x100 * N starts layout N +pub const LAYOUT_MAX_SIZE: usize = 0x100 / 0x4; +pub const LAYOUT_LEN: usize = LAYOUT_COUNT * LAYOUT_MAX_SIZE; + +pub fn eval_layouts(gid: u32, out: &mut [u32]) { + let mut offset = BumpAlloc(gid as usize * LAYOUT_MAX_SIZE); + match gid { + // vec + 0x0 => write_layout!(out, offset, u32()), + 0x1 => write_layout!(out, offset, i32()), + 0x2 => write_layout!(out, offset, f32()), + 0x4 => write_layout!(out, offset, UVec2(x, y)), + 0x5 => write_layout!(out, offset, IVec2(x, y)), + 0x6 => write_layout!(out, offset, Vec2(x, y)), + 0x8 => write_layout!(out, offset, UVec3(x, y, z)), + 0x9 => write_layout!(out, offset, IVec3(x, y, z)), + 0xA => write_layout!(out, offset, Vec3(x, y, z)), + 0xB => write_layout!(out, offset, Vec3A()), // private members + 0xC => write_layout!(out, offset, UVec4(x, y, z, w)), + 0xD => write_layout!(out, offset, IVec4(x, y, z, w)), + 0xE => write_layout!(out, offset, Vec4()), // private members + + // experiments structs + 0x10 => write_layout!(out, offset, Struct0x10(a, b)), + 0x11 => write_layout!(out, offset, Struct0x11(a, b)), + _ => {} } } -pub type EvalResult = [SizeAndAlign; 8]; - -pub fn eval_layouts() -> EvalResult { - [ - SizeAndAlign::from::(), - SizeAndAlign::from::(), - SizeAndAlign::from::(), - SizeAndAlign::from::(), - SizeAndAlign::from::(), - SizeAndAlign::from::(), - SizeAndAlign::from::(), - SizeAndAlign::from::(), - ] +mod experiments { + use spirv_std::glam::*; + + #[repr(C)] + pub struct Struct0x10 { + pub a: f32, + /// if UVec2 has an alignment of + /// * 4 (CPU): 12 size, 4 alignment, 4 b offset + /// * 8 (GPU): 16 size, 8 alignment, 8 b offset + pub b: UVec2, + } + + #[repr(C)] + pub struct Struct0x11 { + pub a: Vec3, + /// if Vec3 has an alignment of + /// * 4 (CPU): 16 size, 4 alignment, 12 b offset + /// * 16 (GPU): 32 size, 16 alignment, 16 b offset + pub b: f32, + } } diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs index f2ced20993..4ce4ea67ca 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs @@ -1,7 +1,11 @@ -use crate::layout::{EvalResult, eval_layouts}; +use crate::layout::eval_layouts; +use spirv_std::glam::UVec3; use spirv_std::spirv; #[spirv(compute(threads(1)))] -pub fn main_cs(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] output: &mut EvalResult) { - *output = eval_layouts(); +pub fn main_cs( + #[spirv(workgroup_id)] gid: UVec3, + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] output: &mut [u32], +) { + eval_layouts(gid.x, output); } diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs index ee99bba190..3e3639a3ff 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs @@ -1,4 +1,4 @@ -use crate::layout::EvalResult; +use crate::layout::{LAYOUT_COUNT, LAYOUT_LEN}; use difftest::config::Config; use difftest::scaffold::compute::{BufferConfig, RustComputeShader, WgpuComputeTestMultiBuffer}; @@ -6,8 +6,8 @@ pub fn run() { let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); let test = WgpuComputeTestMultiBuffer::new( RustComputeShader::default(), - [1, 1, 1], - Vec::from(&[BufferConfig::writeback(size_of::())]), + [LAYOUT_COUNT as u32, 1, 1], + Vec::from(&[BufferConfig::writeback(LAYOUT_LEN * size_of::())]), ); test.run_test(&config).unwrap(); } From 236ee2c7ce7d1555ccf27defc35e055ab063ffb1 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 16 Sep 2025 16:56:45 +0200 Subject: [PATCH 03/20] update glam: switch from `#[repr(SIMD)]` to `#[rust_gpu::vector::v1]` --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- tests/difftests/tests/Cargo.lock | 4 ++-- tests/difftests/tests/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad4d30a45d..e34454bb23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1259,9 +1259,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.5" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d1aab06663bdce00d6ca5e5ed586ec8d18033a771906c993a1e3755b368d85" +checksum = "e12d847aeb25f41be4c0ec9587d624e9cd631bc007a8fd7ce3f5851e064c6460" dependencies = [ "bytemuck", "libm", diff --git a/Cargo.toml b/Cargo.toml index b461aed226..7e00acb1ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ difftest = { path = "tests/difftests/lib" } tracing = "0.1" tracing-subscriber = { version = "0.3.20", features = ["env-filter", "json"] } num-traits = { version = "0.2.15", default-features = false } -glam = { version = ">=0.22, <=0.30.7", default-features = false } +glam = { version = ">=0.30.8", default-features = false } libm = { version = "0.2.5", default-features = false } bytemuck = { version = "1.23", features = ["derive"] } diff --git a/tests/difftests/tests/Cargo.lock b/tests/difftests/tests/Cargo.lock index 56cbc7a14e..626060e31b 100644 --- a/tests/difftests/tests/Cargo.lock +++ b/tests/difftests/tests/Cargo.lock @@ -610,9 +610,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.5" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d1aab06663bdce00d6ca5e5ed586ec8d18033a771906c993a1e3755b368d85" +checksum = "e12d847aeb25f41be4c0ec9587d624e9cd631bc007a8fd7ce3f5851e064c6460" dependencies = [ "libm", ] diff --git a/tests/difftests/tests/Cargo.toml b/tests/difftests/tests/Cargo.toml index fa3078d664..37b29cc4b8 100644 --- a/tests/difftests/tests/Cargo.toml +++ b/tests/difftests/tests/Cargo.toml @@ -59,7 +59,7 @@ spirv-std = { path = "../../../crates/spirv-std", version = "=0.9.0" } difftest = { path = "../../../tests/difftests/lib" } # External dependencies that need to be mentioned more than once. num-traits = { version = "0.2.15", default-features = false } -glam = { version = ">=0.22, <=0.30", default-features = false } +glam = { version = ">=0.30.8", default-features = false } bytemuck = { version = "1.14", features = ["derive"] } # Enable incremental by default in release mode. From fbc77ce8e7e0fe2695aa9d3bc6bd8707e0e400af Mon Sep 17 00:00:00 2001 From: firestar99 Date: Mon, 15 Sep 2025 17:57:27 +0200 Subject: [PATCH 04/20] abi layout: give Vector a dynamic size and alignment --- crates/rustc_codegen_spirv/src/abi.rs | 105 +++++++++++++----- crates/rustc_codegen_spirv/src/attr.rs | 17 +++ .../src/builder/builder_methods.rs | 22 ++-- .../src/builder/byte_addressable_buffer.rs | 4 +- .../src/builder/spirv_asm.rs | 1 + .../src/codegen_cx/constant.rs | 10 +- crates/rustc_codegen_spirv/src/spirv_type.rs | 51 ++++++--- crates/rustc_codegen_spirv/src/symbols.rs | 8 ++ 8 files changed, 159 insertions(+), 59 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index f80bf6092c..f74fa222bb 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -648,6 +648,8 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> { SpirvType::Vector { element: elem_spirv, count: count as u32, + size: self.size, + align: self.align.abi, } .def(span, cx) } @@ -1229,43 +1231,92 @@ fn trans_intrinsic_type<'tcx>( } } IntrinsicType::Matrix => { - let span = def_id_for_spirv_type_adt(ty) - .map(|did| cx.tcx.def_span(did)) - .expect("#[spirv(matrix)] must be added to a type which has DefId"); - - let field_types = (0..ty.fields.count()) - .map(|i| ty.field(cx, i).spirv_type(span, cx)) - .collect::>(); - if field_types.len() < 2 { - return Err(cx - .tcx - .dcx() - .span_err(span, "#[spirv(matrix)] type must have at least two fields")); - } - let elem_type = field_types[0]; - if !field_types.iter().all(|&ty| ty == elem_type) { - return Err(cx.tcx.dcx().span_err( - span, - "#[spirv(matrix)] type fields must all be the same type", - )); - } - match cx.lookup_type(elem_type) { + let (element, count) = + trans_glam_like_struct(cx, span, ty, args, "`#[spirv(matrix)]`")?; + match cx.lookup_type(element) { SpirvType::Vector { .. } => (), ty => { return Err(cx .tcx .dcx() - .struct_span_err(span, "#[spirv(matrix)] type fields must all be vectors") - .with_note(format!("field type is {}", ty.debug(elem_type, cx))) + .struct_span_err(span, "`#[spirv(matrix)]` type fields must all be vectors") + .with_note(format!("field type is {}", ty.debug(element, cx))) .emit()); } } - - Ok(SpirvType::Matrix { - element: elem_type, - count: field_types.len() as u32, + Ok(SpirvType::Matrix { element, count }.def(span, cx)) + } + IntrinsicType::Vector => { + let (element, count) = + trans_glam_like_struct(cx, span, ty, args, "`#[spirv(vector)]`")?; + match cx.lookup_type(element) { + SpirvType::Float { .. } | SpirvType::Integer { .. } => (), + ty => { + return Err(cx + .tcx + .dcx() + .struct_span_err( + span, + "`#[spirv(vector)]` type fields must all be floats or integers", + ) + .with_note(format!("field type is {}", ty.debug(element, cx))) + .emit()); + } + } + Ok(SpirvType::Vector { + element, + count, + size: ty.size, + align: ty.align.abi, } .def(span, cx)) } } } + +/// A struct with multiple fields of the same kind +/// Used for `#[spirv(vector)]` and `#[spirv(matrix)]` +fn trans_glam_like_struct<'tcx>( + cx: &CodegenCx<'tcx>, + span: Span, + ty: TyAndLayout<'tcx>, + args: GenericArgsRef<'tcx>, + err_attr_name: &str, +) -> Result<(Word, u32), ErrorGuaranteed> { + let tcx = cx.tcx; + if let Some(adt) = ty.ty.ty_adt_def() + && adt.is_struct() + { + let (count, element) = adt + .non_enum_variant() + .fields + .iter() + .map(|f| f.ty(tcx, args)) + .dedup_with_count() + .exactly_one() + .map_err(|_e| { + tcx.dcx().span_err( + span, + format!("{err_attr_name} member types must all be the same"), + ) + })?; + + let element = cx.layout_of(element); + let element_word = element.spirv_type(span, cx); + let count = u32::try_from(count) + .ok() + .filter(|count| *count >= 2) + .ok_or_else(|| { + tcx.dcx().span_err( + span, + format!("{err_attr_name} must have at least 2 members"), + ) + })?; + + Ok((element_word, count)) + } else { + Err(tcx + .dcx() + .span_err(span, "#[spirv(vector)] type must be a struct")) + } +} diff --git a/crates/rustc_codegen_spirv/src/attr.rs b/crates/rustc_codegen_spirv/src/attr.rs index cc7f7b52fd..9be755e687 100644 --- a/crates/rustc_codegen_spirv/src/attr.rs +++ b/crates/rustc_codegen_spirv/src/attr.rs @@ -68,6 +68,7 @@ pub enum IntrinsicType { RuntimeArray, TypedBuffer, Matrix, + Vector, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -548,6 +549,22 @@ fn parse_attrs_for_checking<'a>( )) } } + Some(command) if command.name == sym.vector => { + // #[rust_gpu::vector ...] + match s.get(2) { + // #[rust_gpu::vector::v1] + Some(version) if version.name == sym.v1 => { + Ok(SmallVec::from_iter([ + Ok((attr.span(), SpirvAttribute::IntrinsicType(IntrinsicType::Vector))) + ])) + }, + _ => Err(( + attr.span(), + "unknown `rust_gpu::vector` version, expected `rust_gpu::vector::v1`" + .to_string(), + )), + } + } _ => { // #[rust_gpu::...] but not a know version let spirv = sym.spirv_attr_with_version.as_str(); diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index b5a727be7c..b3d9fff030 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -317,10 +317,12 @@ fn memset_dynamic_scalar( byte_width: usize, is_float: bool, ) -> Word { - let composite_type = SpirvType::Vector { - element: SpirvType::Integer(8, false).def(builder.span(), builder), - count: byte_width as u32, - } + let composite_type = SpirvType::simd_vector( + builder, + builder.span(), + SpirvType::Integer(8, false), + byte_width as u32, + ) .def(builder.span(), builder); let composite = builder .emit() @@ -417,7 +419,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => self.fatal(format!("memset on float width {width} not implemented yet")), }, SpirvType::Adt { .. } => self.fatal("memset on structs not implemented yet"), - SpirvType::Vector { element, count } | SpirvType::Matrix { element, count } => { + SpirvType::Vector { element, count, .. } | SpirvType::Matrix { element, count } => { let elem_pat = self.memset_const_pattern(&self.lookup_type(element), fill_byte); self.constant_composite( ty.def(self.span(), self), @@ -478,7 +480,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) .unwrap() } - SpirvType::Vector { element, count } | SpirvType::Matrix { element, count } => { + SpirvType::Vector { element, count, .. } | SpirvType::Matrix { element, count } => { let elem_pat = self.memset_dynamic_pattern(&self.lookup_type(element), fill_var); self.emit() .composite_construct( @@ -2976,11 +2978,9 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { } fn vector_splat(&mut self, num_elts: usize, elt: Self::Value) -> Self::Value { - let result_type = SpirvType::Vector { - element: elt.ty, - count: num_elts as u32, - } - .def(self.span(), self); + let result_type = + SpirvType::simd_vector(self, self.span(), self.lookup_type(elt.ty), num_elts as u32) + .def(self.span(), self); if self.builder.lookup_const(elt).is_some() { self.constant_composite(result_type, iter::repeat_n(elt.def(self), num_elts)) } else { diff --git a/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs b/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs index 0aaee7f8d7..00ffaf661b 100644 --- a/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs +++ b/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs @@ -114,7 +114,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let val = self.load_u32(array, dynamic_word_index, constant_word_offset); self.bitcast(val, result_type) } - SpirvType::Vector { element, count } | SpirvType::Matrix { element, count } => self + SpirvType::Vector { element, count, .. } | SpirvType::Matrix { element, count } => self .load_vec_mat_arr( original_type, result_type, @@ -314,7 +314,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let value_u32 = self.bitcast(value, u32_ty); self.store_u32(array, dynamic_word_index, constant_word_offset, value_u32) } - SpirvType::Vector { element, count } | SpirvType::Matrix { element, count } => self + SpirvType::Vector { element, count, .. } | SpirvType::Matrix { element, count } => self .store_vec_mat_arr( original_type, value, diff --git a/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs b/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs index 8441d74c08..894c5a8d02 100644 --- a/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs +++ b/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs @@ -728,6 +728,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> { SpirvType::Vector { element: ty, count: 4, + .. }, ) | ( diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs b/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs index eb8783049c..acd9b42172 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs @@ -200,10 +200,12 @@ impl ConstCodegenMethods for CodegenCx<'_> { self.constant_composite(struct_ty, elts.iter().map(|f| f.def_cx(self))) } fn const_vector(&self, elts: &[Self::Value]) -> Self::Value { - let vector_ty = SpirvType::Vector { - element: elts[0].ty, - count: elts.len() as u32, - } + let vector_ty = SpirvType::simd_vector( + self, + DUMMY_SP, + self.lookup_type(elts[0].ty), + elts.len() as u32, + ) .def(DUMMY_SP, self); self.constant_composite(vector_ty, elts.iter().map(|elt| elt.def_cx(self))) } diff --git a/crates/rustc_codegen_spirv/src/spirv_type.rs b/crates/rustc_codegen_spirv/src/spirv_type.rs index 074e5708d1..c2d85dcbac 100644 --- a/crates/rustc_codegen_spirv/src/spirv_type.rs +++ b/crates/rustc_codegen_spirv/src/spirv_type.rs @@ -45,6 +45,8 @@ pub enum SpirvType<'tcx> { element: Word, /// Note: vector count is literal. count: u32, + size: Size, + align: Align, }, Matrix { element: Word, @@ -131,7 +133,9 @@ impl SpirvType<'_> { } result } - Self::Vector { element, count } => cx.emit_global().type_vector_id(id, element, count), + Self::Vector { element, count, .. } => { + cx.emit_global().type_vector_id(id, element, count) + } Self::Matrix { element, count } => cx.emit_global().type_matrix_id(id, element, count), Self::Array { element, count } => { let result = cx @@ -280,9 +284,7 @@ impl SpirvType<'_> { Self::Bool => Size::from_bytes(1), Self::Integer(width, _) | Self::Float(width) => Size::from_bits(width), Self::Adt { size, .. } => size?, - Self::Vector { element, count } => { - cx.lookup_type(element).sizeof(cx)? * count.next_power_of_two() as u64 - } + Self::Vector { size, .. } => size, Self::Matrix { element, count } => cx.lookup_type(element).sizeof(cx)? * count as u64, Self::Array { element, count } => { cx.lookup_type(element).sizeof(cx)? @@ -310,14 +312,7 @@ impl SpirvType<'_> { Self::Bool => Align::from_bytes(1).unwrap(), Self::Integer(width, _) | Self::Float(width) => Align::from_bits(width as u64).unwrap(), - Self::Adt { align, .. } => align, - // Vectors have size==align - Self::Vector { .. } => Align::from_bytes( - self.sizeof(cx) - .expect("alignof: Vectors must be sized") - .bytes(), - ) - .expect("alignof: Vectors must have power-of-2 size"), + Self::Adt { align, .. } | Self::Vector { align, .. } => align, Self::Array { element, .. } | Self::RuntimeArray { element } | Self::Matrix { element, .. } => cx.lookup_type(element).alignof(cx), @@ -382,7 +377,17 @@ impl SpirvType<'_> { SpirvType::Bool => SpirvType::Bool, SpirvType::Integer(width, signedness) => SpirvType::Integer(width, signedness), SpirvType::Float(width) => SpirvType::Float(width), - SpirvType::Vector { element, count } => SpirvType::Vector { element, count }, + SpirvType::Vector { + element, + count, + size, + align, + } => SpirvType::Vector { + element, + count, + size, + align, + }, SpirvType::Matrix { element, count } => SpirvType::Matrix { element, count }, SpirvType::Array { element, count } => SpirvType::Array { element, count }, SpirvType::RuntimeArray { element } => SpirvType::RuntimeArray { element }, @@ -435,6 +440,15 @@ impl SpirvType<'_> { }, } } + + pub fn simd_vector(cx: &CodegenCx<'_>, span: Span, element: SpirvType<'_>, count: u32) -> Self { + Self::Vector { + element: element.def(span, cx), + count, + size: element.sizeof(cx).unwrap() * count as u64, + align: element.alignof(cx), + } + } } impl<'a> SpirvType<'a> { @@ -501,11 +515,18 @@ impl fmt::Debug for SpirvTypePrinter<'_, '_> { .field("field_names", &field_names) .finish() } - SpirvType::Vector { element, count } => f + SpirvType::Vector { + element, + count, + size, + align, + } => f .debug_struct("Vector") .field("id", &self.id) .field("element", &self.cx.debug_type(element)) .field("count", &count) + .field("size", &size) + .field("align", &align) .finish(), SpirvType::Matrix { element, count } => f .debug_struct("Matrix") @@ -668,7 +689,7 @@ impl SpirvTypePrinter<'_, '_> { } f.write_str(" }") } - SpirvType::Vector { element, count } | SpirvType::Matrix { element, count } => { + SpirvType::Vector { element, count, .. } | SpirvType::Matrix { element, count } => { ty(self.cx, stack, f, element)?; write!(f, "x{count}") } diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index 917a61a86a..e81f1bb7ad 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -15,6 +15,8 @@ pub struct Symbols { pub discriminant: Symbol, pub rust_gpu: Symbol, pub spirv_attr_with_version: Symbol, + pub vector: Symbol, + pub v1: Symbol, pub libm: Symbol, pub entry_point_name: Symbol, pub spv_khr_vulkan_memory_model: Symbol, @@ -371,6 +373,10 @@ impl Symbols { "matrix", SpirvAttribute::IntrinsicType(IntrinsicType::Matrix), ), + ( + "vector", + SpirvAttribute::IntrinsicType(IntrinsicType::Vector), + ), ("buffer_load_intrinsic", SpirvAttribute::BufferLoadIntrinsic), ( "buffer_store_intrinsic", @@ -406,6 +412,8 @@ impl Symbols { discriminant: Symbol::intern("discriminant"), rust_gpu: Symbol::intern("rust_gpu"), spirv_attr_with_version: Symbol::intern(&spirv_attr_with_version()), + vector: Symbol::intern("vector"), + v1: Symbol::intern("v1"), libm: Symbol::intern("libm"), entry_point_name: Symbol::intern("entry_point_name"), spv_khr_vulkan_memory_model: Symbol::intern("SPV_KHR_vulkan_memory_model"), From 594283432cc931abf31a034aa2085580b6d941e3 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 16 Sep 2025 18:58:11 +0200 Subject: [PATCH 05/20] abi layout: `glam::BVec` support --- crates/rustc_codegen_spirv/src/abi.rs | 4 +-- crates/spirv-std/src/vector.rs | 1 + tests/compiletests/ui/arch/all.rs | 27 +------------------ tests/compiletests/ui/arch/all.stderr | 12 --------- tests/compiletests/ui/arch/any.rs | 27 +------------------ tests/compiletests/ui/arch/any.stderr | 12 --------- .../ui/arch/debug_printf_type_checking.stderr | 8 +++--- 7 files changed, 9 insertions(+), 82 deletions(-) delete mode 100644 tests/compiletests/ui/arch/all.stderr delete mode 100644 tests/compiletests/ui/arch/any.stderr diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index f74fa222bb..2e876c4cd7 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -1250,14 +1250,14 @@ fn trans_intrinsic_type<'tcx>( let (element, count) = trans_glam_like_struct(cx, span, ty, args, "`#[spirv(vector)]`")?; match cx.lookup_type(element) { - SpirvType::Float { .. } | SpirvType::Integer { .. } => (), + SpirvType::Bool | SpirvType::Float { .. } | SpirvType::Integer { .. } => (), ty => { return Err(cx .tcx .dcx() .struct_span_err( span, - "`#[spirv(vector)]` type fields must all be floats or integers", + "`#[spirv(vector)]` type fields must all be floats, integers or bools", ) .with_note(format!("field type is {}", ty.debug(element, cx))) .emit()); diff --git a/crates/spirv-std/src/vector.rs b/crates/spirv-std/src/vector.rs index 0385db83ac..7c4943ce17 100644 --- a/crates/spirv-std/src/vector.rs +++ b/crates/spirv-std/src/vector.rs @@ -49,6 +49,7 @@ impl_vector! { f64: glam::DVec2 => 2, glam::DVec3 => 3, glam::DVec4 => 4; u32: glam::UVec2 => 2, glam::UVec3 => 3, glam::UVec4 => 4; i32: glam::IVec2 => 2, glam::IVec3 => 3, glam::IVec4 => 4; + bool: glam::BVec2 => 2, glam::BVec3 => 3, glam::BVec4 => 4; } /// Trait that implements slicing of a vector into a scalar or vector of lower dimensions, by diff --git a/tests/compiletests/ui/arch/all.rs b/tests/compiletests/ui/arch/all.rs index 088e07f0a5..1d99c101cb 100644 --- a/tests/compiletests/ui/arch/all.rs +++ b/tests/compiletests/ui/arch/all.rs @@ -1,36 +1,11 @@ // build-pass -#![feature(repr_simd)] - use core::num::NonZeroUsize; use spirv_std::spirv; use spirv_std::{scalar::Scalar, vector::Vector, vector::VectorOrScalar}; -/// HACK(shesp). Rust doesn't allow us to declare regular (tuple-)structs containing `bool` members -/// as `#[repl(simd)]`. But we need this for `spirv_std::arch::any()` and `spirv_std::arch::all()` -/// to work. -/// Fortunately, this requirement isn't checked on generic structs, so we have a way to work around -/// it (for now at least) -#[repr(simd)] -#[derive(Copy, Clone, Debug)] -struct Vec2(T, T); -unsafe impl VectorOrScalar for Vec2 { - type Scalar = T; - const DIM: NonZeroUsize = match NonZeroUsize::new(2) { - None => panic!(), - Some(n) => n, - }; -} -unsafe impl Vector for Vec2 {} - -impl Default for Vec2 { - fn default() -> Vec2 { - Vec2(T::default(), T::default()) - } -} - #[spirv(fragment)] pub fn main() { - let vector = Vec2(true, true); + let vector = glam::BVec2::new(true, true); assert!(spirv_std::arch::all(vector)); } diff --git a/tests/compiletests/ui/arch/all.stderr b/tests/compiletests/ui/arch/all.stderr deleted file mode 100644 index beed8baf22..0000000000 --- a/tests/compiletests/ui/arch/all.stderr +++ /dev/null @@ -1,12 +0,0 @@ -warning: [Rust-GPU] temporarily re-allowing old-style `#[repr(simd)]` (with fields) - --> $DIR/all.rs:16:1 - | -16 | struct Vec2(T, T); - | ^^^^^^^^^^^^^^ - | - = note: removed upstream by https://github.com/rust-lang/rust/pull/129403 - = note: in favor of the new `#[repr(simd)] struct TxN([T; N]);` style - = note: (taking effect since `nightly-2024-09-12` / `1.83.0` stable) - -warning: 1 warning emitted - diff --git a/tests/compiletests/ui/arch/any.rs b/tests/compiletests/ui/arch/any.rs index 9e8e1d2f3f..e113927411 100644 --- a/tests/compiletests/ui/arch/any.rs +++ b/tests/compiletests/ui/arch/any.rs @@ -1,36 +1,11 @@ // build-pass -#![feature(repr_simd)] - use core::num::NonZeroUsize; use spirv_std::spirv; use spirv_std::{scalar::Scalar, vector::Vector, vector::VectorOrScalar}; -/// HACK(shesp). Rust doesn't allow us to declare regular (tuple-)structs containing `bool` members -/// as `#[repl(simd)]`. But we need this for `spirv_std::arch::any()` and `spirv_std::arch::all()` -/// to work. -/// Fortunately, this requirement isn't checked on generic structs, so we have a way to work around -/// it (for now at least) -#[repr(simd)] -#[derive(Copy, Clone, Debug)] -struct Vec2(T, T); -unsafe impl VectorOrScalar for Vec2 { - type Scalar = T; - const DIM: NonZeroUsize = match NonZeroUsize::new(2) { - None => panic!(), - Some(n) => n, - }; -} -unsafe impl Vector for Vec2 {} - -impl Default for Vec2 { - fn default() -> Vec2 { - Vec2(T::default(), T::default()) - } -} - #[spirv(fragment)] pub fn main() { - let vector = Vec2(false, true); + let vector = glam::BVec2::new(false, true); assert!(spirv_std::arch::any(vector)); } diff --git a/tests/compiletests/ui/arch/any.stderr b/tests/compiletests/ui/arch/any.stderr deleted file mode 100644 index c01af8f3ba..0000000000 --- a/tests/compiletests/ui/arch/any.stderr +++ /dev/null @@ -1,12 +0,0 @@ -warning: [Rust-GPU] temporarily re-allowing old-style `#[repr(simd)]` (with fields) - --> $DIR/any.rs:16:1 - | -16 | struct Vec2(T, T); - | ^^^^^^^^^^^^^^ - | - = note: removed upstream by https://github.com/rust-lang/rust/pull/129403 - = note: in favor of the new `#[repr(simd)] struct TxN([T; N]);` style - = note: (taking effect since `nightly-2024-09-12` / `1.83.0` stable) - -warning: 1 warning emitted - diff --git a/tests/compiletests/ui/arch/debug_printf_type_checking.stderr b/tests/compiletests/ui/arch/debug_printf_type_checking.stderr index 5c1b487bde..6ec5956a55 100644 --- a/tests/compiletests/ui/arch/debug_printf_type_checking.stderr +++ b/tests/compiletests/ui/arch/debug_printf_type_checking.stderr @@ -121,15 +121,15 @@ error[E0277]: the trait bound `{float}: Vector` is not satisfied | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Vector` is not implemented for `{float}` | = help: the following other types implement trait `Vector`: + `BVec2` implements `Vector` + `BVec3` implements `Vector` + `BVec4` implements `Vector` `DVec2` implements `Vector` `DVec3` implements `Vector` `DVec4` implements `Vector` `IVec2` implements `Vector` `IVec3` implements `Vector` - `IVec4` implements `Vector` - `UVec2` implements `Vector` - `UVec3` implements `Vector` - and 5 others + and 8 others note: required by a bound in `debug_printf_assert_is_vector` --> $SPIRV_STD_SRC/lib.rs:141:8 | From fdbef58e7fe7732612fba5acdf6d3dec655efb17 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 16 Sep 2025 15:04:23 +0200 Subject: [PATCH 06/20] abi layout: make `insert_value()` support Array, Vector, Matrix --- crates/rustc_codegen_spirv/src/builder/builder_methods.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index b3d9fff030..e44fc1e25c 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -3017,6 +3017,9 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { fn insert_value(&mut self, agg_val: Self::Value, elt: Self::Value, idx: u64) -> Self::Value { let field_type = match self.lookup_type(agg_val.ty) { SpirvType::Adt { field_types, .. } => field_types[idx as usize], + SpirvType::Array { element, .. } + | SpirvType::Vector { element, .. } + | SpirvType::Matrix { element, .. } => element, other => self.fatal(format!("insert_value not implemented on type {other:?}")), }; From e9828e0cf033f78714a895b4ed1f94ce35ecea0c Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 16 Sep 2025 17:51:18 +0200 Subject: [PATCH 07/20] abi layout compiletest: fix invalid-matrix-type --- crates/rustc_codegen_spirv/src/abi.rs | 6 ++++++ .../ui/spirv-attr/invalid-matrix-type-empty.stderr | 2 +- tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr | 6 +++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 2e876c4cd7..aa80b22b97 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -982,6 +982,10 @@ fn def_id_for_spirv_type_adt(layout: TyAndLayout<'_>) -> Option { } } +fn span_for_spirv_type_adt(cx: &CodegenCx<'_>, layout: TyAndLayout<'_>) -> Option { + def_id_for_spirv_type_adt(layout).map(|did| cx.tcx.def_span(did)) +} + /// Minimal and cheaply comparable/hashable subset of the information contained /// in `TyLayout` that can be used to generate a name (assuming a nominal type). #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -1231,6 +1235,7 @@ fn trans_intrinsic_type<'tcx>( } } IntrinsicType::Matrix => { + let span = span_for_spirv_type_adt(cx, ty).unwrap(); let (element, count) = trans_glam_like_struct(cx, span, ty, args, "`#[spirv(matrix)]`")?; match cx.lookup_type(element) { @@ -1247,6 +1252,7 @@ fn trans_intrinsic_type<'tcx>( Ok(SpirvType::Matrix { element, count }.def(span, cx)) } IntrinsicType::Vector => { + let span = span_for_spirv_type_adt(cx, ty).unwrap(); let (element, count) = trans_glam_like_struct(cx, span, ty, args, "`#[spirv(vector)]`")?; match cx.lookup_type(element) { diff --git a/tests/compiletests/ui/spirv-attr/invalid-matrix-type-empty.stderr b/tests/compiletests/ui/spirv-attr/invalid-matrix-type-empty.stderr index d34ef0ac5e..45b1139cc0 100644 --- a/tests/compiletests/ui/spirv-attr/invalid-matrix-type-empty.stderr +++ b/tests/compiletests/ui/spirv-attr/invalid-matrix-type-empty.stderr @@ -1,4 +1,4 @@ -error: #[spirv(matrix)] type must have at least two fields +error: `#[spirv(matrix)]` member types must all be the same --> $DIR/invalid-matrix-type-empty.rs:7:1 | 7 | pub struct EmptyStruct {} diff --git a/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr b/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr index eda87f4c6e..f1dbb5a145 100644 --- a/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr +++ b/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr @@ -1,10 +1,10 @@ -error: #[spirv(matrix)] type must have at least two fields +error: `#[spirv(matrix)]` must have at least 2 members --> $DIR/invalid-matrix-type.rs:7:1 | 7 | pub struct _FewerFields { | ^^^^^^^^^^^^^^^^^^^^^^^ -error: #[spirv(matrix)] type fields must all be vectors +error: `#[spirv(matrix)]` type fields must all be vectors --> $DIR/invalid-matrix-type.rs:12:1 | 12 | pub struct _NotVectorField { @@ -12,7 +12,7 @@ error: #[spirv(matrix)] type fields must all be vectors | = note: field type is f32 -error: #[spirv(matrix)] type fields must all be the same type +error: `#[spirv(matrix)]` member types must all be the same --> $DIR/invalid-matrix-type.rs:19:1 | 19 | pub struct _DifferentType { From 977fe4934454b5b385bca3726a804c86a7c830b5 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Thu, 18 Sep 2025 16:54:17 +0200 Subject: [PATCH 08/20] abi layout: change Subgroup from transparent struct to typedef --- crates/spirv-std/src/arch/subgroup.rs | 5 +--- .../ui/arch/subgroup/subgroup_ballot.stderr | 4 +-- .../subgroup/subgroup_ballot_bit_count.stderr | 2 +- .../subgroup/subgroup_broadcast_first.stderr | 2 +- .../subgroup_cluster_size_0_fail.stderr | 28 +++++++++---------- ..._cluster_size_non_power_of_two_fail.stderr | 28 +++++++++---------- .../ui/arch/subgroup/subgroup_elect.stderr | 2 +- .../subgroup/subgroup_i_add_clustered.stderr | 2 +- .../subgroup_i_add_exclusive_scan.stderr | 2 +- .../subgroup_i_add_inclusive_scan.stderr | 2 +- .../subgroup/subgroup_i_add_reduce.stderr | 2 +- 11 files changed, 38 insertions(+), 41 deletions(-) diff --git a/crates/spirv-std/src/arch/subgroup.rs b/crates/spirv-std/src/arch/subgroup.rs index e9793db114..05798a6f1d 100644 --- a/crates/spirv-std/src/arch/subgroup.rs +++ b/crates/spirv-std/src/arch/subgroup.rs @@ -13,10 +13,7 @@ const SUBGROUP: u32 = Scope::Subgroup as u32; /// `SubgroupMask` is a [`glam::UVec4`] representing a bitmask of all invocations within a subgroup. /// Mostly used in group ballot operations. -#[repr(transparent)] -#[derive(Copy, Clone, Default, Eq, PartialEq)] -#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))] -pub struct SubgroupMask(pub glam::UVec4); +pub type SubgroupMask = glam::UVec4; /// Defines the class of group operation. #[non_exhaustive] diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_ballot.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_ballot.stderr index ffc7660bb9..a0994f5353 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_ballot.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_ballot.stderr @@ -1,9 +1,9 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 367 13 +OpLine %6 364 13 %7 = OpGroupNonUniformBallot %8 %9 %4 -OpLine %6 406 13 +OpLine %6 403 13 %10 = OpGroupNonUniformInverseBallot %2 %9 %7 OpNoLine OpReturnValue %10 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_ballot_bit_count.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_ballot_bit_count.stderr index 0ecffc39c4..52b6bcf944 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_ballot_bit_count.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_ballot_bit_count.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %5 %6 = OpLabel -OpLine %7 501 0 +OpLine %7 498 0 %8 = OpGroupNonUniformBallotBitCount %2 %9 Reduce %4 OpNoLine OpReturnValue %8 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_broadcast_first.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_broadcast_first.stderr index 9bb737c779..44fa624f0e 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_broadcast_first.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_broadcast_first.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 334 13 +OpLine %6 331 13 %7 = OpGroupNonUniformBroadcastFirst %2 %8 %4 OpNoLine OpReturnValue %7 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_0_fail.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_0_fail.stderr index 821239dc33..fe2f180d78 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_0_fail.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_0_fail.stderr @@ -1,27 +1,27 @@ error[E0080]: evaluation panicked: `ClusterSize` must be at least 1 - --> $SPIRV_STD_SRC/arch/subgroup.rs:829:1 + --> $SPIRV_STD_SRC/arch/subgroup.rs:826:1 | -829 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" -830 | | An integer add group operation of all `value` operands contributed by active invocations in the group. -831 | | -832 | | Result Type must be a scalar or vector of integer type. +826 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" +827 | | An integer add group operation of all `value` operands contributed by active invocations in the group. +828 | | +829 | | Result Type must be a scalar or vector of integer type. ... | -845 | | * `ClusterSize` must not be greater than the size of the group -846 | | "); +842 | | * `ClusterSize` must not be greater than the size of the group +843 | | "); | |__^ evaluation of `spirv_std::arch::subgroup_clustered_i_add::<0, u32, u32>::{constant#0}` failed here | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `macro_subgroup_op_clustered` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $SPIRV_STD_SRC/arch/subgroup.rs:829:1 + --> $SPIRV_STD_SRC/arch/subgroup.rs:826:1 | -829 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" -830 | | An integer add group operation of all `value` operands contributed by active invocations in the group. -831 | | -832 | | Result Type must be a scalar or vector of integer type. +826 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" +827 | | An integer add group operation of all `value` operands contributed by active invocations in the group. +828 | | +829 | | Result Type must be a scalar or vector of integer type. ... | -845 | | * `ClusterSize` must not be greater than the size of the group -846 | | "); +842 | | * `ClusterSize` must not be greater than the size of the group +843 | | "); | |__^ | = note: this note originates in the macro `macro_subgroup_op_clustered` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_non_power_of_two_fail.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_non_power_of_two_fail.stderr index 8ef6ee7cb1..40c37e2740 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_non_power_of_two_fail.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_non_power_of_two_fail.stderr @@ -1,27 +1,27 @@ error[E0080]: evaluation panicked: `ClusterSize` must be a power of 2 - --> $SPIRV_STD_SRC/arch/subgroup.rs:829:1 + --> $SPIRV_STD_SRC/arch/subgroup.rs:826:1 | -829 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" -830 | | An integer add group operation of all `value` operands contributed by active invocations in the group. -831 | | -832 | | Result Type must be a scalar or vector of integer type. +826 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" +827 | | An integer add group operation of all `value` operands contributed by active invocations in the group. +828 | | +829 | | Result Type must be a scalar or vector of integer type. ... | -845 | | * `ClusterSize` must not be greater than the size of the group -846 | | "); +842 | | * `ClusterSize` must not be greater than the size of the group +843 | | "); | |__^ evaluation of `spirv_std::arch::subgroup_clustered_i_add::<5, u32, u32>::{constant#0}` failed here | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `macro_subgroup_op_clustered` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $SPIRV_STD_SRC/arch/subgroup.rs:829:1 + --> $SPIRV_STD_SRC/arch/subgroup.rs:826:1 | -829 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" -830 | | An integer add group operation of all `value` operands contributed by active invocations in the group. -831 | | -832 | | Result Type must be a scalar or vector of integer type. +826 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" +827 | | An integer add group operation of all `value` operands contributed by active invocations in the group. +828 | | +829 | | Result Type must be a scalar or vector of integer type. ... | -845 | | * `ClusterSize` must not be greater than the size of the group -846 | | "); +842 | | * `ClusterSize` must not be greater than the size of the group +843 | | "); | |__^ | = note: this note originates in the macro `macro_subgroup_op_clustered` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_elect.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_elect.stderr index d18849b6f6..26a079b145 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_elect.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_elect.stderr @@ -1,6 +1,6 @@ %1 = OpFunction %2 None %3 %4 = OpLabel -OpLine %5 164 13 +OpLine %5 161 13 %6 = OpGroupNonUniformElect %2 %7 OpNoLine OpReturnValue %6 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_clustered.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_clustered.stderr index 4d06f8f632..4c022abaf0 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_clustered.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_clustered.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 829 0 +OpLine %6 826 0 %7 = OpGroupNonUniformIAdd %2 %8 ClusteredReduce %4 %9 OpNoLine OpReturnValue %7 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_exclusive_scan.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_exclusive_scan.stderr index 4890551e39..a5ec1af956 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_exclusive_scan.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_exclusive_scan.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 816 0 +OpLine %6 813 0 %7 = OpGroupNonUniformIAdd %2 %8 ExclusiveScan %4 OpNoLine OpReturnValue %7 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_inclusive_scan.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_inclusive_scan.stderr index a41a806a40..b6fcceb39f 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_inclusive_scan.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_inclusive_scan.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 816 0 +OpLine %6 813 0 %7 = OpGroupNonUniformIAdd %2 %8 InclusiveScan %4 OpNoLine OpReturnValue %7 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_reduce.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_reduce.stderr index 2e8d77e479..73fdd65b86 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_reduce.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_reduce.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 816 0 +OpLine %6 813 0 %7 = OpGroupNonUniformIAdd %2 %8 Reduce %4 OpNoLine OpReturnValue %7 From bf899ed13ae00fe48228f953dabea6abf5fb5330 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 16 Sep 2025 18:22:30 +0200 Subject: [PATCH 09/20] abi layout compiletest: bless complex_image_sample_inst --- .../ui/dis/complex_image_sample_inst.stderr | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/compiletests/ui/dis/complex_image_sample_inst.stderr b/tests/compiletests/ui/dis/complex_image_sample_inst.stderr index cde98c1dec..b99b63a736 100644 --- a/tests/compiletests/ui/dis/complex_image_sample_inst.stderr +++ b/tests/compiletests/ui/dis/complex_image_sample_inst.stderr @@ -3,12 +3,18 @@ %5 = OpFunctionParameter %6 %7 = OpFunctionParameter %6 %8 = OpLabel -OpLine %9 29 13 -%10 = OpAccessChain %11 %12 %13 -OpLine %9 30 13 -%14 = OpLoad %15 %10 -OpLine %9 34 13 -%16 = OpImageSampleProjExplicitLod %2 %14 %4 Grad %5 %7 +%9 = OpCompositeExtract %10 %5 0 +%11 = OpCompositeExtract %10 %5 1 +%12 = OpCompositeConstruct %6 %9 %11 +%13 = OpCompositeExtract %10 %7 0 +%14 = OpCompositeExtract %10 %7 1 +%15 = OpCompositeConstruct %6 %13 %14 +OpLine %16 29 13 +%17 = OpAccessChain %18 %19 %20 +OpLine %16 30 13 +%21 = OpLoad %22 %17 +OpLine %16 34 13 +%23 = OpImageSampleProjExplicitLod %2 %21 %4 Grad %12 %15 OpNoLine -OpReturnValue %16 +OpReturnValue %23 OpFunctionEnd From 807ea9308efda76e7852a20b01af1c8c85c25d93 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 16 Sep 2025 18:25:44 +0200 Subject: [PATCH 10/20] abi layout: remove `#[repr(SIMD)]` hack --- crates/rustc_codegen_spirv/src/abi.rs | 265 +------------------------- 1 file changed, 1 insertion(+), 264 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index aa80b22b97..7cf33c34e3 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -8,12 +8,10 @@ use itertools::Itertools; use rspirv::spirv::{Dim, ImageFormat, StorageClass, Word}; use rustc_abi::ExternAbi as Abi; use rustc_abi::{ - Align, BackendRepr, FieldIdx, FieldsShape, HasDataLayout as _, LayoutData, Primitive, - ReprFlags, ReprOptions, Scalar, Size, TagEncoding, VariantIdx, Variants, + Align, BackendRepr, FieldIdx, FieldsShape, Primitive, Scalar, Size, VariantIdx, Variants, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; -use rustc_hashes::Hash64; use rustc_index::Idx; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; @@ -107,267 +105,6 @@ pub(crate) fn provide(providers: &mut Providers) { Ok(readjust_fn_abi(tcx, result?)) }; - // FIXME(eddyb) remove this by deriving `Clone` for `LayoutData` upstream. - fn clone_layout( - layout: &LayoutData, - ) -> LayoutData { - let LayoutData { - ref fields, - ref variants, - backend_repr, - largest_niche, - uninhabited, - align, - size, - max_repr_align, - unadjusted_abi_align, - randomization_seed, - } = *layout; - LayoutData { - fields: match *fields { - FieldsShape::Primitive => FieldsShape::Primitive, - FieldsShape::Union(count) => FieldsShape::Union(count), - FieldsShape::Array { stride, count } => FieldsShape::Array { stride, count }, - FieldsShape::Arbitrary { - ref offsets, - ref memory_index, - } => FieldsShape::Arbitrary { - offsets: offsets.clone(), - memory_index: memory_index.clone(), - }, - }, - variants: match *variants { - Variants::Empty => Variants::Empty, - Variants::Single { index } => Variants::Single { index }, - Variants::Multiple { - tag, - ref tag_encoding, - tag_field, - ref variants, - } => Variants::Multiple { - tag, - tag_encoding: match *tag_encoding { - TagEncoding::Direct => TagEncoding::Direct, - TagEncoding::Niche { - untagged_variant, - ref niche_variants, - niche_start, - } => TagEncoding::Niche { - untagged_variant, - niche_variants: niche_variants.clone(), - niche_start, - }, - }, - tag_field, - variants: variants.clone(), - }, - }, - backend_repr, - largest_niche, - uninhabited, - align, - size, - max_repr_align, - unadjusted_abi_align, - randomization_seed, - } - } - - providers.layout_of = |tcx, key| { - // HACK(eddyb) to special-case any types at all, they must be normalized, - // but when normalization would be needed, `layout_of`'s default provider - // recurses (supposedly for caching reasons), i.e. its calls `layout_of` - // w/ the normalized type in input, which once again reaches this hook, - // without ever needing any explicit normalization here. - let ty = key.value; - - // HACK(eddyb) bypassing upstream `#[repr(simd)]` changes (see also - // the later comment above `check_well_formed`, for more details). - let reimplement_old_style_repr_simd = match ty.kind() { - ty::Adt(def, args) if def.repr().simd() && !def.repr().packed() && def.is_struct() => { - Some(def.non_enum_variant()).and_then(|v| { - let (count, e_ty) = v - .fields - .iter() - .map(|f| f.ty(tcx, args)) - .dedup_with_count() - .exactly_one() - .ok()?; - let e_len = u64::try_from(count).ok().filter(|&e_len| e_len > 1)?; - Some((def, e_ty, e_len)) - }) - } - _ => None, - }; - - // HACK(eddyb) tweaked copy of the old upstream logic for `#[repr(simd)]`: - // https://github.com/rust-lang/rust/blob/1.86.0/compiler/rustc_ty_utils/src/layout.rs#L464-L590 - if let Some((adt_def, e_ty, e_len)) = reimplement_old_style_repr_simd { - let cx = rustc_middle::ty::layout::LayoutCx::new( - tcx, - key.typing_env.with_post_analysis_normalized(tcx), - ); - let dl = cx.data_layout(); - - // Compute the ABI of the element type: - let e_ly = cx.layout_of(e_ty)?; - let BackendRepr::Scalar(e_repr) = e_ly.backend_repr else { - // This error isn't caught in typeck, e.g., if - // the element type of the vector is generic. - tcx.dcx().span_fatal( - tcx.def_span(adt_def.did()), - format!( - "SIMD type `{ty}` with a non-primitive-scalar \ - (integer/float/pointer) element type `{}`", - e_ly.ty - ), - ); - }; - - // Compute the size and alignment of the vector: - let size = e_ly.size.checked_mul(e_len, dl).unwrap(); - let align = dl.llvmlike_vector_align(size); - let size = size.align_to(align.abi); - - let layout = tcx.mk_layout(LayoutData { - variants: Variants::Single { - index: rustc_abi::FIRST_VARIANT, - }, - fields: FieldsShape::Array { - stride: e_ly.size, - count: e_len, - }, - backend_repr: BackendRepr::SimdVector { - element: e_repr, - count: e_len, - }, - largest_niche: e_ly.largest_niche, - uninhabited: false, - size, - align, - max_repr_align: None, - unadjusted_abi_align: align.abi, - randomization_seed: e_ly.randomization_seed.wrapping_add(Hash64::new(e_len)), - }); - - return Ok(TyAndLayout { ty, layout }); - } - - let TyAndLayout { ty, mut layout } = - (rustc_interface::DEFAULT_QUERY_PROVIDERS.layout_of)(tcx, key)?; - - #[allow(clippy::match_like_matches_macro)] - let hide_niche = match ty.kind() { - ty::Bool => { - // HACK(eddyb) we can't bypass e.g. `Option` being a byte, - // due to `core` PR https://github.com/rust-lang/rust/pull/138881 - // (which adds a new `transmute`, from `ControlFlow` to `u8`). - let libcore_needs_bool_niche = true; - - !libcore_needs_bool_niche - } - _ => false, - }; - - if hide_niche { - layout = tcx.mk_layout(LayoutData { - largest_niche: None, - ..clone_layout(layout.0.0) - }); - } - - Ok(TyAndLayout { ty, layout }) - }; - - // HACK(eddyb) work around https://github.com/rust-lang/rust/pull/129403 - // banning "struct-style" `#[repr(simd)]` (in favor of "array-newtype-style"), - // by simply bypassing "type definition WF checks" for affected types, which: - // - can only really be sound for types with trivial field types, that are - // either completely non-generic (covering most `#[repr(simd)]` `struct`s), - // or *at most* one generic type parameter with no bounds/where clause - // - relies on upstream `layout_of` not having had the non-array logic removed - // - // FIXME(eddyb) remove this once migrating beyond `#[repr(simd)]` becomes - // an option (may require Rust-GPU distinguishing between "SPIR-V interface" - // and "Rust-facing" types, which is even worse when the `OpTypeVector`s - // may be e.g. nested in `struct`s/arrays/etc. - at least buffers are easy). - // - // FIXME(eddyb) maybe using `#[spirv(vector)]` and `BackendRepr::Memory`, - // no claims at `rustc`-understood SIMD whatsoever, would be enough? - // (i.e. only SPIR-V caring about such a type vs a struct/array) - providers.check_well_formed = |tcx, def_id| { - let trivial_struct = match tcx.hir_node_by_def_id(def_id) { - rustc_hir::Node::Item(item) => match item.kind { - rustc_hir::ItemKind::Struct( - _, - &rustc_hir::Generics { - params: - &[] - | &[ - rustc_hir::GenericParam { - kind: - rustc_hir::GenericParamKind::Type { - default: None, - synthetic: false, - }, - .. - }, - ], - predicates: &[], - has_where_clause_predicates: false, - where_clause_span: _, - span: _, - }, - _, - ) => Some(tcx.adt_def(def_id)), - _ => None, - }, - _ => None, - }; - let valid_non_array_simd_struct = trivial_struct.is_some_and(|adt_def| { - let ReprOptions { - int: None, - align: None, - pack: None, - flags: ReprFlags::IS_SIMD, - field_shuffle_seed: _, - } = adt_def.repr() - else { - return false; - }; - if adt_def.destructor(tcx).is_some() { - return false; - } - - let field_types = adt_def - .non_enum_variant() - .fields - .iter() - .map(|f| tcx.type_of(f.did).instantiate_identity()); - field_types.dedup().exactly_one().is_ok_and(|elem_ty| { - matches!( - elem_ty.kind(), - ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Param(_) - ) - }) - }); - - if valid_non_array_simd_struct { - tcx.dcx() - .struct_span_warn( - tcx.def_span(def_id), - "[Rust-GPU] temporarily re-allowing old-style `#[repr(simd)]` (with fields)", - ) - .with_note("removed upstream by https://github.com/rust-lang/rust/pull/129403") - .with_note("in favor of the new `#[repr(simd)] struct TxN([T; N]);` style") - .with_note("(taking effect since `nightly-2024-09-12` / `1.83.0` stable)") - .emit(); - return Ok(()); - } - - (rustc_interface::DEFAULT_QUERY_PROVIDERS.check_well_formed)(tcx, def_id) - }; - // HACK(eddyb) work around https://github.com/rust-lang/rust/pull/132173 // (and further changes from https://github.com/rust-lang/rust/pull/132843) // starting to ban SIMD ABI misuse (or at least starting to warn about it). From 3a1b41e896f437c894d5f19509a59aa9676a1841 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Wed, 17 Sep 2025 18:05:41 +0200 Subject: [PATCH 11/20] abi layout difftest: add all remaining glam types --- .../lang/abi/vector_layout/cpu/src/layout.rs | 81 ++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs index 8751a076d2..d050e60d5d 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs @@ -30,7 +30,7 @@ macro_rules! write_layout { } /// gid is checked between `0..LAYOUT_COUNT` -pub const LAYOUT_COUNT: usize = 0x40; +pub const LAYOUT_COUNT: usize = 0x60; /// at byte offset 0x100 * N starts layout N pub const LAYOUT_MAX_SIZE: usize = 0x100 / 0x4; pub const LAYOUT_LEN: usize = LAYOUT_COUNT * LAYOUT_MAX_SIZE; @@ -52,10 +52,74 @@ pub fn eval_layouts(gid: u32, out: &mut [u32]) { 0xC => write_layout!(out, offset, UVec4(x, y, z, w)), 0xD => write_layout!(out, offset, IVec4(x, y, z, w)), 0xE => write_layout!(out, offset, Vec4()), // private members + 0xF => write_layout!(out, offset, Quat()), // private members // experiments structs 0x10 => write_layout!(out, offset, Struct0x10(a, b)), 0x11 => write_layout!(out, offset, Struct0x11(a, b)), + 0x12 => write_layout!(out, offset, Struct0x12(a, b, c, d, e)), + 0x13 => write_layout!(out, offset, Struct0x13(a)), + + // mat + 0x20 => write_layout!(out, offset, Mat2()), // private members + 0x21 => write_layout!(out, offset, Mat3(x_axis, y_axis, z_axis)), + 0x22 => write_layout!(out, offset, Mat3A(x_axis, y_axis, z_axis)), + 0x23 => write_layout!(out, offset, Mat4(x_axis, y_axis, z_axis, w_axis)), + // f64 + 0x24 => write_layout!(out, offset, f64()), + 0x25 => write_layout!(out, offset, DVec2(x, y)), + 0x26 => write_layout!(out, offset, DVec3(x, y, z)), + 0x27 => write_layout!(out, offset, DVec4(x, y, z, w)), + 0x28 => write_layout!(out, offset, DQuat(x, y, z, w)), + 0x29 => write_layout!(out, offset, DMat2()), // private members + 0x2a => write_layout!(out, offset, DMat3(x_axis, y_axis, z_axis)), + 0x2b => write_layout!(out, offset, DMat4(x_axis, y_axis, z_axis, w_axis)), + + // i8 + 0x30 => write_layout!(out, offset, i8()), + 0x31 => write_layout!(out, offset, I8Vec2(x, y)), + 0x32 => write_layout!(out, offset, I8Vec3(x, y, z)), + 0x33 => write_layout!(out, offset, I8Vec4(x, y, z, w)), + // i16 + 0x34 => write_layout!(out, offset, i16()), + 0x35 => write_layout!(out, offset, I16Vec2(x, y)), + 0x36 => write_layout!(out, offset, I16Vec3(x, y, z)), + 0x37 => write_layout!(out, offset, I16Vec4(x, y, z, w)), + // i64 + 0x38 => write_layout!(out, offset, i64()), + 0x39 => write_layout!(out, offset, I64Vec2(x, y)), + 0x3a => write_layout!(out, offset, I64Vec3(x, y, z)), + 0x3b => write_layout!(out, offset, I64Vec4(x, y, z, w)), + + // u8 + 0x40 => write_layout!(out, offset, u8()), + 0x41 => write_layout!(out, offset, U8Vec2(x, y)), + 0x42 => write_layout!(out, offset, U8Vec3(x, y, z)), + 0x43 => write_layout!(out, offset, U8Vec4(x, y, z, w)), + // u16 + 0x44 => write_layout!(out, offset, u16()), + 0x45 => write_layout!(out, offset, U16Vec2(x, y)), + 0x46 => write_layout!(out, offset, U16Vec3(x, y, z)), + 0x47 => write_layout!(out, offset, U16Vec4(x, y, z, w)), + // u64 + 0x48 => write_layout!(out, offset, u64()), + 0x49 => write_layout!(out, offset, U64Vec2(x, y)), + 0x4a => write_layout!(out, offset, U64Vec3(x, y, z)), + 0x4b => write_layout!(out, offset, U64Vec4(x, y, z, w)), + // Affine + 0x4c => write_layout!(out, offset, Affine2(matrix2, translation)), + 0x4d => write_layout!(out, offset, Affine3A(matrix3, translation)), + 0x4e => write_layout!(out, offset, DAffine2(matrix2, translation)), + 0x4f => write_layout!(out, offset, DAffine3(matrix3, translation)), + + // bool + 0x50 => write_layout!(out, offset, bool()), + 0x51 => write_layout!(out, offset, BVec2(x, y)), + 0x52 => write_layout!(out, offset, BVec3(x, y, z)), + 0x53 => write_layout!(out, offset, BVec3A()), // private members + 0x54 => write_layout!(out, offset, BVec4(x, y, z, w)), + 0x55 => write_layout!(out, offset, BVec4A()), + _ => {} } } @@ -80,4 +144,19 @@ mod experiments { /// * 16 (GPU): 32 size, 16 alignment, 16 b offset pub b: f32, } + + #[repr(C)] + pub struct Struct0x12 { + pub a: BVec3, + pub b: f32, + pub c: BVec2, + pub d: f32, + pub e: BVec4, + } + + #[repr(C)] + #[repr(align(16))] + pub struct Struct0x13 { + pub a: f32, + } } From d4c211385cb1b819dfad8af0771823ee17d5a5ac Mon Sep 17 00:00:00 2001 From: firestar99 Date: Thu, 18 Sep 2025 16:41:35 +0200 Subject: [PATCH 12/20] abi layout difftest: cuda and scalar-math feature forwarding --- tests/difftests/tests/Cargo.lock | 1 + .../lang/abi/vector_layout/cpu/Cargo.toml | 5 +++ .../abi/vector_layout/cpu/src/cpu_driver.rs | 4 ++- .../vector_layout/cpu/src/glam_features.rs | 35 +++++++++++++++++++ .../lang/abi/vector_layout/cpu/src/lib.rs | 1 + .../lang/abi/vector_layout/cpu/src/main.rs | 4 ++- .../vector_layout/cpu/src/shader_driver.rs | 4 ++- .../abi/vector_layout/rust-gpu/src/main.rs | 4 ++- 8 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 tests/difftests/tests/lang/abi/vector_layout/cpu/src/glam_features.rs diff --git a/tests/difftests/tests/Cargo.lock b/tests/difftests/tests/Cargo.lock index 626060e31b..582763fb67 100644 --- a/tests/difftests/tests/Cargo.lock +++ b/tests/difftests/tests/Cargo.lock @@ -8,6 +8,7 @@ version = "0.0.0" dependencies = [ "bytemuck", "difftest", + "glam", "spirv-std", ] diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml index db41132735..007ef92b04 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml @@ -5,9 +5,14 @@ edition.workspace = true [lints] workspace = true +[features] +cuda = ["glam/cuda"] +scalar-math = ["glam/scalar-math"] + # GPU deps [dependencies] spirv-std.workspace = true +glam.workspace = true bytemuck.workspace = true # CPU deps (for the test harness) diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs index bbfa3b6289..87ea655780 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs @@ -1,7 +1,9 @@ +use crate::glam_features::GlamFeatures; use crate::layout::{LAYOUT_COUNT, LAYOUT_LEN, eval_layouts}; use difftest::config::Config; -pub fn run() { +pub fn run(glam_feature: GlamFeatures) { + glam_feature.assert(); let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); let mut out = vec![0; LAYOUT_LEN]; for gid in 0..LAYOUT_COUNT { diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/glam_features.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/glam_features.rs new file mode 100644 index 0000000000..7259525b71 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/glam_features.rs @@ -0,0 +1,35 @@ +#[repr(u32)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum GlamFeatures { + Default, + Cuda, + ScalarMath, +} + +impl GlamFeatures { + pub fn is(&self) -> bool { + match self { + // cuda: 16 (!) + // cuda + scalar-math: 8 + // scalar-math: (native) 4 + GlamFeatures::Default => align_of::() == 16 && !GlamFeatures::Cuda.is(), + // default, scalar-math: (native) 4 + GlamFeatures::Cuda => align_of::() == 8, + // default, cuda: 16 + GlamFeatures::ScalarMath => align_of::() == 4, + } + } + + pub fn assert(&self) { + for feature in [Self::Default, Self::Cuda, Self::ScalarMath] { + if *self == feature { + assert!(feature.is(), "Expected feature {feature:?} to be enabled"); + } else { + assert!( + !feature.is(), + "Feature {feature:?} mistakenly enabled! Too aggressive feature unification?" + ); + } + } + } +} diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs index 4838d65e2f..08e80c346f 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs @@ -2,6 +2,7 @@ #[cfg(not(target_arch = "spirv"))] pub mod cpu_driver; +pub mod glam_features; pub mod layout; pub mod shader; #[cfg(not(target_arch = "spirv"))] diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs index cecb91f595..bcd5f7c800 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs @@ -1,3 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + fn main() { - abi_vector_layout_cpu::cpu_driver::run(); + abi_vector_layout_cpu::cpu_driver::run(GlamFeatures::Default); } diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs index 3e3639a3ff..4502da3197 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs @@ -1,8 +1,10 @@ +use crate::glam_features::GlamFeatures; use crate::layout::{LAYOUT_COUNT, LAYOUT_LEN}; use difftest::config::Config; use difftest::scaffold::compute::{BufferConfig, RustComputeShader, WgpuComputeTestMultiBuffer}; -pub fn run() { +pub fn run(glam_feature: GlamFeatures) { + glam_feature.assert(); let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); let test = WgpuComputeTestMultiBuffer::new( RustComputeShader::default(), diff --git a/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs index 98fa9c5549..5ab8e426b5 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs @@ -1,3 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + fn main() { - abi_vector_layout_cpu::shader_driver::run(); + abi_vector_layout_cpu::shader_driver::run(GlamFeatures::Default); } From c41459170bf57f8045dfed892f9302687c0c0220 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Thu, 18 Sep 2025 16:44:17 +0200 Subject: [PATCH 13/20] abi layout difftest: cuda and scalar-math feature testing --- tests/difftests/tests/Cargo.lock | 32 +++++++++++++++++++ tests/difftests/tests/Cargo.toml | 4 +++ .../abi/vector_layout_cuda/cpu/Cargo.toml | 14 ++++++++ .../abi/vector_layout_cuda/cpu/src/main.rs | 5 +++ .../vector_layout_cuda/rust-gpu/Cargo.toml | 16 ++++++++++ .../vector_layout_cuda/rust-gpu/src/lib.rs | 3 ++ .../vector_layout_cuda/rust-gpu/src/main.rs | 5 +++ .../vector_layout_scalar_math/cpu/Cargo.toml | 14 ++++++++ .../vector_layout_scalar_math/cpu/src/main.rs | 5 +++ .../rust-gpu/Cargo.toml | 16 ++++++++++ .../rust-gpu/src/lib.rs | 3 ++ .../rust-gpu/src/main.rs | 5 +++ 12 files changed, 122 insertions(+) create mode 100644 tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/Cargo.toml create mode 100644 tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/src/main.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/Cargo.toml create mode 100644 tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/lib.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/main.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/Cargo.toml create mode 100644 tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/src/main.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/Cargo.toml create mode 100644 tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/lib.rs create mode 100644 tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/main.rs diff --git a/tests/difftests/tests/Cargo.lock b/tests/difftests/tests/Cargo.lock index 582763fb67..982445e579 100644 --- a/tests/difftests/tests/Cargo.lock +++ b/tests/difftests/tests/Cargo.lock @@ -12,6 +12,22 @@ dependencies = [ "spirv-std", ] +[[package]] +name = "abi-vector-layout-cuda-cpu" +version = "0.0.0" +dependencies = [ + "abi-vector-layout-cpu", + "difftest", +] + +[[package]] +name = "abi-vector-layout-cuda-rust-gpu" +version = "0.0.0" +dependencies = [ + "abi-vector-layout-cpu", + "difftest", +] + [[package]] name = "abi-vector-layout-rust-gpu" version = "0.0.0" @@ -20,6 +36,22 @@ dependencies = [ "difftest", ] +[[package]] +name = "abi-vector-layout-scalar-math-cpu" +version = "0.0.0" +dependencies = [ + "abi-vector-layout-cpu", + "difftest", +] + +[[package]] +name = "abi-vector-layout-scalar-math-rust-gpu" +version = "0.0.0" +dependencies = [ + "abi-vector-layout-cpu", + "difftest", +] + [[package]] name = "allocator-api2" version = "0.2.21" diff --git a/tests/difftests/tests/Cargo.toml b/tests/difftests/tests/Cargo.toml index 37b29cc4b8..6441b7925d 100644 --- a/tests/difftests/tests/Cargo.toml +++ b/tests/difftests/tests/Cargo.toml @@ -18,6 +18,10 @@ members = [ "storage_class/array_access/array_access-wgsl", "lang/abi/vector_layout/cpu", "lang/abi/vector_layout/rust-gpu", + "lang/abi/vector_layout_cuda/cpu", + "lang/abi/vector_layout_cuda/rust-gpu", + "lang/abi/vector_layout_scalar_math/cpu", + "lang/abi/vector_layout_scalar_math/rust-gpu", "lang/control_flow/control_flow-rust", "lang/control_flow/control_flow-wgsl", "lang/control_flow_complex/control_flow_complex-rust", diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/Cargo.toml new file mode 100644 index 0000000000..4a3223d9db --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "abi-vector-layout-cuda-cpu" +edition.workspace = true + +[lints] +workspace = true + +# GPU deps +[dependencies] +abi-vector-layout-cpu = { path = "../../vector_layout/cpu", features = ["cuda"] } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/src/main.rs new file mode 100644 index 0000000000..84861065a3 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/src/main.rs @@ -0,0 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + +fn main() { + abi_vector_layout_cpu::cpu_driver::run(GlamFeatures::Cuda); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/Cargo.toml new file mode 100644 index 0000000000..2ae5c7a13f --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "abi-vector-layout-cuda-rust-gpu" +edition.workspace = true + +[lib] +crate-type = ["lib", "dylib"] + +[lints] +workspace = true + +[dependencies] +abi-vector-layout-cpu = { path = "../../vector_layout/cpu", features = ["cuda"] } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/lib.rs b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/lib.rs new file mode 100644 index 0000000000..219229aa2e --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/lib.rs @@ -0,0 +1,3 @@ +#![cfg_attr(target_arch = "spirv", no_std)] + +pub use abi_vector_layout_cpu::shader::main_cs; diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/main.rs new file mode 100644 index 0000000000..647d8c3385 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/main.rs @@ -0,0 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + +fn main() { + abi_vector_layout_cpu::shader_driver::run(GlamFeatures::Cuda); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/Cargo.toml new file mode 100644 index 0000000000..fa834aa419 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "abi-vector-layout-scalar-math-cpu" +edition.workspace = true + +[lints] +workspace = true + +# GPU deps +[dependencies] +abi-vector-layout-cpu = { path = "../../vector_layout/cpu", features = ["scalar-math"] } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/src/main.rs new file mode 100644 index 0000000000..044b1b025f --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/src/main.rs @@ -0,0 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + +fn main() { + abi_vector_layout_cpu::cpu_driver::run(GlamFeatures::ScalarMath); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/Cargo.toml new file mode 100644 index 0000000000..225553fd24 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "abi-vector-layout-scalar-math-rust-gpu" +edition.workspace = true + +[lib] +crate-type = ["lib", "dylib"] + +[lints] +workspace = true + +[dependencies] +abi-vector-layout-cpu = { path = "../../vector_layout/cpu", features = ["scalar-math"] } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/lib.rs b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/lib.rs new file mode 100644 index 0000000000..219229aa2e --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/lib.rs @@ -0,0 +1,3 @@ +#![cfg_attr(target_arch = "spirv", no_std)] + +pub use abi_vector_layout_cpu::shader::main_cs; diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/main.rs new file mode 100644 index 0000000000..c0265cc7e3 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/main.rs @@ -0,0 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + +fn main() { + abi_vector_layout_cpu::shader_driver::run(GlamFeatures::ScalarMath); +} From dbfef21a9afaf6fc0bac5907009935579ce51d7b Mon Sep 17 00:00:00 2001 From: firestar99 Date: Wed, 24 Sep 2025 10:21:33 +0200 Subject: [PATCH 14/20] abi layout: minor code cleanups --- crates/rustc_codegen_spirv/src/abi.rs | 4 ++-- .../tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs | 4 ++-- .../tests/lang/abi/vector_layout/cpu/src/layout.rs | 9 ++++++--- .../lang/abi/vector_layout/cpu/src/shader_driver.rs | 5 +++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 7cf33c34e3..bd377a8db1 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -1017,8 +1017,8 @@ fn trans_intrinsic_type<'tcx>( } } -/// A struct with multiple fields of the same kind -/// Used for `#[spirv(vector)]` and `#[spirv(matrix)]` +/// A struct with multiple fields of the same kind. +/// Used for `#[spirv(vector)]` and `#[spirv(matrix)]`. fn trans_glam_like_struct<'tcx>( cx: &CodegenCx<'tcx>, span: Span, diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs index 87ea655780..6ed4a0796d 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs @@ -1,12 +1,12 @@ use crate::glam_features::GlamFeatures; -use crate::layout::{LAYOUT_COUNT, LAYOUT_LEN, eval_layouts}; +use crate::layout::{LAYOUT_LEN, LAYOUT_RANGE, eval_layouts}; use difftest::config::Config; pub fn run(glam_feature: GlamFeatures) { glam_feature.assert(); let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); let mut out = vec![0; LAYOUT_LEN]; - for gid in 0..LAYOUT_COUNT { + for gid in LAYOUT_RANGE { eval_layouts(gid as u32, &mut out); } config.write_result(&out).unwrap() diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs index d050e60d5d..c9b660f63b 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs @@ -1,4 +1,5 @@ use core::mem::offset_of; +use core::ops::Range; use experiments::*; use spirv_std::glam::*; @@ -29,11 +30,12 @@ macro_rules! write_layout { }; } -/// gid is checked between `0..LAYOUT_COUNT` -pub const LAYOUT_COUNT: usize = 0x60; +/// gid is checked within this range. Note this is a range, so it **must be +1 from the max entry you use**. +/// (Must always start at 0, `InclusiveRange` doesn't work due to constness) +pub const LAYOUT_RANGE: Range = 0..0x56; /// at byte offset 0x100 * N starts layout N pub const LAYOUT_MAX_SIZE: usize = 0x100 / 0x4; -pub const LAYOUT_LEN: usize = LAYOUT_COUNT * LAYOUT_MAX_SIZE; +pub const LAYOUT_LEN: usize = LAYOUT_RANGE.end * LAYOUT_MAX_SIZE; pub fn eval_layouts(gid: u32, out: &mut [u32]) { let mut offset = BumpAlloc(gid as usize * LAYOUT_MAX_SIZE); @@ -120,6 +122,7 @@ pub fn eval_layouts(gid: u32, out: &mut [u32]) { 0x54 => write_layout!(out, offset, BVec4(x, y, z, w)), 0x55 => write_layout!(out, offset, BVec4A()), + // IMPORTANT: when adding new rows, remember to adjust `LAYOUT_COUNT` to be the maximum + 1! _ => {} } } diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs index 4502da3197..e6d10ffa3d 100644 --- a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs @@ -1,14 +1,15 @@ use crate::glam_features::GlamFeatures; -use crate::layout::{LAYOUT_COUNT, LAYOUT_LEN}; +use crate::layout::{LAYOUT_LEN, LAYOUT_RANGE}; use difftest::config::Config; use difftest::scaffold::compute::{BufferConfig, RustComputeShader, WgpuComputeTestMultiBuffer}; pub fn run(glam_feature: GlamFeatures) { glam_feature.assert(); let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); + assert_eq!(0, LAYOUT_RANGE.start); let test = WgpuComputeTestMultiBuffer::new( RustComputeShader::default(), - [LAYOUT_COUNT as u32, 1, 1], + [LAYOUT_RANGE.end as u32, 1, 1], Vec::from(&[BufferConfig::writeback(LAYOUT_LEN * size_of::())]), ); test.run_test(&config).unwrap(); From fbb36aa006c69acc62e0ce2b80624e4d2d26659b Mon Sep 17 00:00:00 2001 From: firestar99 Date: Wed, 24 Sep 2025 13:31:54 +0200 Subject: [PATCH 15/20] abi layout: improve documentation on `Scalar` and `Vector` --- crates/spirv-std/src/scalar.rs | 10 ++++++-- crates/spirv-std/src/vector.rs | 42 ++++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/crates/spirv-std/src/scalar.rs b/crates/spirv-std/src/scalar.rs index bff86b6964..520348be3a 100644 --- a/crates/spirv-std/src/scalar.rs +++ b/crates/spirv-std/src/scalar.rs @@ -5,9 +5,15 @@ use core::num::NonZeroUsize; /// Abstract trait representing a SPIR-V scalar type. /// +/// Implemented on types that map to spirv "scalar" types, which includes: +/// * Floating-point type: f32, f64 +/// * Integer type: u8, u16, u32, u64, i8, i16, i32, i64 +/// * Boolean type: bool +/// +/// See the SPIRV spec on [Types](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_types). +/// /// # Safety -/// Implementing this trait on non-scalar types breaks assumptions of other unsafe code, and should -/// not be done. +/// Must only be implemented on spirv "scalar" types, as mentioned above. pub unsafe trait Scalar: VectorOrScalar + crate::sealed::Sealed {} macro_rules! impl_scalar { diff --git a/crates/spirv-std/src/vector.rs b/crates/spirv-std/src/vector.rs index 7c4943ce17..f3ba4a42bb 100644 --- a/crates/spirv-std/src/vector.rs +++ b/crates/spirv-std/src/vector.rs @@ -7,8 +7,7 @@ use glam::{Vec3Swizzles, Vec4Swizzles}; /// Abstract trait representing either a vector or a scalar type. /// /// # Safety -/// Implementing this trait on non-scalar or non-vector types may break assumptions about other -/// unsafe code, and should not be done. +/// Your type must also implement [`Vector`] or [`Scalar`], see their safety sections as well. pub unsafe trait VectorOrScalar: Copy + Default + Send + Sync + 'static { /// Either the scalar component type of the vector or the scalar itself. type Scalar: Scalar; @@ -27,9 +26,44 @@ pub(crate) const fn create_dim(n: usize) -> NonZeroUsize { /// Abstract trait representing a SPIR-V vector type. /// +/// To implement this trait, your struct must be marked with: +/// ```no_run +/// #[cfg_attr(target_arch = "spirv", rust_gpu::vector::v1)] +/// # struct Bla(f32, f32); +/// ``` +/// +/// This places these additional constraints on your type, checked by the spirv codegen: +/// * must be a struct +/// * members must be a spirv [`Scalar`] type, which includes: +/// * Floating-point type: f32, f64 +/// * Integer type: u8, u16, u32, u64, i8, i16, i32, i64 +/// * Boolean type: bool +/// * all members must be of the same primitive type +/// * must have 2, 3 or 4 vector components / members +/// * type must derive Copy, Clone, Default +/// +/// The spirv codegen backend will then emit your type as an `OpTypeVector` instead of an `OpTypeStruct`. The layout of +/// your type is unaffected, the size, alignment and member offsets will follow standard rustc layout rules. This hint +/// does nothing on other target platforms. +/// +/// See the SPIRV spec on [Types](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_types) and the +/// "Data rules" in the [Universal Validation Rules](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_universal_validation_rules). +/// +/// # Example +/// ```no_run +/// #[derive(Copy, Clone, Default)] +/// #[cfg_attr(target_arch = "spirv", rust_gpu::vector::v1)] +/// struct MyColor { +/// r: f32, +/// b: f32, +/// g: f32, +/// } +/// ``` +/// +/// /// # Safety -/// Implementing this trait on non-simd-vector types breaks assumptions of other unsafe code, and -/// should not be done. +/// Must only be implemented on types that the spirv codegen emits as valid `OpTypeVector`. This includes all structs +/// marked with `#[rust_gpu::vector::v1]`, like [`glam`]'s non-SIMD "scalar" vector types. pub unsafe trait Vector: VectorOrScalar {} macro_rules! impl_vector { From c58aaec5884d37670f46bf7af7e7ae012956347d Mon Sep 17 00:00:00 2001 From: firestar99 Date: Wed, 24 Sep 2025 13:39:52 +0200 Subject: [PATCH 16/20] abi layout: limit vectors to at most 4 components, as spec states --- crates/rustc_codegen_spirv/src/abi.rs | 27 ++++++++++--------- .../ui/spirv-attr/invalid-matrix-type.stderr | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index bd377a8db1..16c3f0775b 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -973,15 +973,18 @@ fn trans_intrinsic_type<'tcx>( } IntrinsicType::Matrix => { let span = span_for_spirv_type_adt(cx, ty).unwrap(); - let (element, count) = - trans_glam_like_struct(cx, span, ty, args, "`#[spirv(matrix)]`")?; + let err_attr_name = "`#[spirv(matrix)]`"; + let (element, count) = trans_glam_like_struct(cx, span, ty, args, err_attr_name)?; match cx.lookup_type(element) { SpirvType::Vector { .. } => (), ty => { return Err(cx .tcx .dcx() - .struct_span_err(span, "`#[spirv(matrix)]` type fields must all be vectors") + .struct_span_err( + span, + format!("{err_attr_name} type fields must all be vectors"), + ) .with_note(format!("field type is {}", ty.debug(element, cx))) .emit()); } @@ -990,8 +993,8 @@ fn trans_intrinsic_type<'tcx>( } IntrinsicType::Vector => { let span = span_for_spirv_type_adt(cx, ty).unwrap(); - let (element, count) = - trans_glam_like_struct(cx, span, ty, args, "`#[spirv(vector)]`")?; + let err_attr_name = "`#[spirv(vector)]`"; + let (element, count) = trans_glam_like_struct(cx, span, ty, args, err_attr_name)?; match cx.lookup_type(element) { SpirvType::Bool | SpirvType::Float { .. } | SpirvType::Integer { .. } => (), ty => { @@ -1000,7 +1003,9 @@ fn trans_intrinsic_type<'tcx>( .dcx() .struct_span_err( span, - "`#[spirv(vector)]` type fields must all be floats, integers or bools", + format!( + "{err_attr_name} type fields must all be floats, integers or bools" + ), ) .with_note(format!("field type is {}", ty.debug(element, cx))) .emit()); @@ -1048,18 +1053,16 @@ fn trans_glam_like_struct<'tcx>( let element_word = element.spirv_type(span, cx); let count = u32::try_from(count) .ok() - .filter(|count| *count >= 2) + .filter(|count| 2 <= *count && *count <= 4) .ok_or_else(|| { - tcx.dcx().span_err( - span, - format!("{err_attr_name} must have at least 2 members"), - ) + tcx.dcx() + .span_err(span, format!("{err_attr_name} must have 2, 3 or 4 members")) })?; Ok((element_word, count)) } else { Err(tcx .dcx() - .span_err(span, "#[spirv(vector)] type must be a struct")) + .span_err(span, format!("{err_attr_name} type must be a struct"))) } } diff --git a/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr b/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr index f1dbb5a145..a14a76b6d6 100644 --- a/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr +++ b/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr @@ -1,4 +1,4 @@ -error: `#[spirv(matrix)]` must have at least 2 members +error: `#[spirv(matrix)]` must have 2, 3 or 4 members --> $DIR/invalid-matrix-type.rs:7:1 | 7 | pub struct _FewerFields { From 582df867dab8e5c0aae6019861b8b8ecbc5fae52 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Wed, 24 Sep 2025 13:49:49 +0200 Subject: [PATCH 17/20] abi layout: add some compiletests --- .../ui/glam/invalid_vector_type.rs | 47 +++++++++++++++ .../ui/glam/invalid_vector_type.stderr | 28 +++++++++ .../ui/glam/offsets_vec3_vec3a.rs | 57 +++++++++++++++++++ .../ui/glam/offsets_vec3_vec3a.stderr | 55 ++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 tests/compiletests/ui/glam/invalid_vector_type.rs create mode 100644 tests/compiletests/ui/glam/invalid_vector_type.stderr create mode 100644 tests/compiletests/ui/glam/offsets_vec3_vec3a.rs create mode 100644 tests/compiletests/ui/glam/offsets_vec3_vec3a.stderr diff --git a/tests/compiletests/ui/glam/invalid_vector_type.rs b/tests/compiletests/ui/glam/invalid_vector_type.rs new file mode 100644 index 0000000000..7c8ed9be5a --- /dev/null +++ b/tests/compiletests/ui/glam/invalid_vector_type.rs @@ -0,0 +1,47 @@ +// build-fail + +use core::num::NonZeroU32; +use spirv_std::glam::Vec2; +use spirv_std::spirv; + +#[rust_gpu::vector::v1] +pub struct FewerFields { + _v: f32, +} + +#[rust_gpu::vector::v1] +pub struct TooManyFields { + _x: f32, + _y: f32, + _z: f32, + _w: f32, + _v: f32, +} + +#[rust_gpu::vector::v1] +pub struct NotVectorField { + _x: Vec2, + _y: Vec2, +} + +#[rust_gpu::vector::v1] +pub struct NotVectorField2 { + _x: NonZeroU32, + _y: NonZeroU32, +} + +#[rust_gpu::vector::v1] +pub struct DifferentTypes { + _x: f32, + _y: u32, +} + +#[spirv(fragment)] +pub fn entry( + _: FewerFields, + _: TooManyFields, + _: NotVectorField, + #[spirv(flat)] _: NotVectorField2, + #[spirv(flat)] _: DifferentTypes, +) { +} diff --git a/tests/compiletests/ui/glam/invalid_vector_type.stderr b/tests/compiletests/ui/glam/invalid_vector_type.stderr new file mode 100644 index 0000000000..b0826e800c --- /dev/null +++ b/tests/compiletests/ui/glam/invalid_vector_type.stderr @@ -0,0 +1,28 @@ +error: `#[spirv(vector)]` must have 2, 3 or 4 members + --> $DIR/invalid_vector_type.rs:8:1 + | +8 | pub struct FewerFields { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[spirv(vector)]` must have 2, 3 or 4 members + --> $DIR/invalid_vector_type.rs:13:1 + | +13 | pub struct TooManyFields { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[spirv(vector)]` type fields must all be floats, integers or bools + --> $DIR/invalid_vector_type.rs:22:1 + | +22 | pub struct NotVectorField { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: field type is f32x2 + +error: `#[spirv(vector)]` member types must all be the same + --> $DIR/invalid_vector_type.rs:34:1 + | +34 | pub struct DifferentTypes { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/compiletests/ui/glam/offsets_vec3_vec3a.rs b/tests/compiletests/ui/glam/offsets_vec3_vec3a.rs new file mode 100644 index 0000000000..6d1e56cf84 --- /dev/null +++ b/tests/compiletests/ui/glam/offsets_vec3_vec3a.rs @@ -0,0 +1,57 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble +// normalize-stderr-test "OpSource .*\n" -> "" +// normalize-stderr-test "OpLine .*\n" -> "" +// normalize-stderr-test "%\d+ = OpString .*\n" -> "" +// normalize-stderr-test "; .*\n" -> "" +// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> "" +// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple" +// ignore-vulkan1.0 +// ignore-vulkan1.1 + +use spirv_std::arch::subgroup_shuffle_up; +use spirv_std::spirv; + +#[repr(C)] +#[derive(Copy, Clone, Default)] +#[rust_gpu::vector::v1] +pub struct Vec3 { + x: f32, + y: f32, + z: f32, +} + +#[repr(C, align(16))] +#[derive(Copy, Clone, Default)] +#[rust_gpu::vector::v1] +pub struct Vec3A { + x: f32, + y: f32, + z: f32, +} + +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Data { + t: T, + // this should generate two distinct structs where this member has different offsets + value: f32, +} + +impl Vec3 { + pub fn to_vec3a(&self) -> Vec3A { + Vec3A { + x: self.x, + y: self.y, + z: self.z, + } + } +} + +#[spirv(fragment)] +pub fn main(input: Data, output: &mut Data) { + *output = Data { + t: input.t.to_vec3a(), + value: input.value, + }; +} diff --git a/tests/compiletests/ui/glam/offsets_vec3_vec3a.stderr b/tests/compiletests/ui/glam/offsets_vec3_vec3a.stderr new file mode 100644 index 0000000000..f0b05652ae --- /dev/null +++ b/tests/compiletests/ui/glam/offsets_vec3_vec3a.stderr @@ -0,0 +1,55 @@ +OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint Fragment %1 "main" %2 %3 +OpExecutionMode %1 OriginUpperLeft +OpName %5 "Data" +OpMemberName %5 0 "t" +OpMemberName %5 1 "value" +OpName %6 "Data" +OpMemberName %6 0 "t" +OpMemberName %6 1 "value" +OpName %2 "input" +OpName %7 "Data" +OpMemberName %7 0 "t" +OpMemberName %7 1 "value" +OpName %8 "Data" +OpMemberName %8 0 "t" +OpMemberName %8 1 "value" +OpName %3 "output" +OpDecorate %2 Location 0 +OpMemberDecorate %7 0 Offset 0 +OpMemberDecorate %7 1 Offset 12 +OpMemberDecorate %8 0 Offset 0 +OpMemberDecorate %8 1 Offset 16 +OpDecorate %3 Location 0 +%9 = OpTypeFloat 32 +%10 = OpTypeVector %9 3 +%5 = OpTypeStruct %10 %9 +%11 = OpTypePointer Input %5 +%6 = OpTypeStruct %10 %9 +%12 = OpTypePointer Output %6 +%13 = OpTypeVoid +%14 = OpTypeFunction %13 +%2 = OpVariable %11 Input +%7 = OpTypeStruct %10 %9 +%8 = OpTypeStruct %10 %9 +%3 = OpVariable %12 Output +%1 = OpFunction %13 None %14 +%15 = OpLabel +%16 = OpLoad %5 %2 +%17 = OpCompositeExtract %10 %16 0 +%18 = OpCompositeExtract %9 %16 1 +%19 = OpCompositeConstruct %7 %17 %18 +%20 = OpCompositeExtract %9 %19 0 0 +%21 = OpCompositeExtract %9 %19 0 1 +%22 = OpCompositeExtract %9 %19 0 2 +%23 = OpCompositeConstruct %10 %20 %21 %22 +%24 = OpCompositeExtract %9 %19 1 +%25 = OpCompositeConstruct %8 %23 %24 +%26 = OpCompositeExtract %10 %25 0 +%27 = OpCompositeExtract %9 %25 1 +%28 = OpCompositeConstruct %6 %26 %27 +OpStore %3 %28 +OpNoLine +OpReturn +OpFunctionEnd From 8c715a2ce4788609d6178e684a2e27e579c4dc7c Mon Sep 17 00:00:00 2001 From: firestar99 Date: Wed, 24 Sep 2025 16:02:47 +0200 Subject: [PATCH 18/20] abi layout: assert member offsets of vectors are as expected --- crates/rustc_codegen_spirv/src/abi.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 16c3f0775b..0c5cf69f4d 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -1059,6 +1059,27 @@ fn trans_glam_like_struct<'tcx>( .span_err(span, format!("{err_attr_name} must have 2, 3 or 4 members")) })?; + for i in 0..ty.fields.count() { + let expected = element.size.checked_mul(i as u64, cx).unwrap(); + let actual = ty.fields.offset(i); + if actual != expected { + let name: &str = adt + .non_enum_variant() + .fields + .get(FieldIdx::from(i)) + .unwrap() + .name + .as_str(); + tcx.dcx().span_fatal( + span, + format!( + "Unexpected layout for {err_attr_name} annotated struct: \ + Expected member `{name}` at offset {expected:?}, but was at {actual:?}" + ), + ) + } + } + Ok((element_word, count)) } else { Err(tcx From ce5d860ec30817e37886db3fd2506c868b5c0e78 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Wed, 24 Sep 2025 13:28:14 +0200 Subject: [PATCH 19/20] compiletest: update readme normalize examples --- tests/compiletests/README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/compiletests/README.md b/tests/compiletests/README.md index 1a48bcb438..374bb0130b 100644 --- a/tests/compiletests/README.md +++ b/tests/compiletests/README.md @@ -71,10 +71,15 @@ contents of `path/to/test.rs.stderr`. * `// normalize-stderr-test "targetSpecificMsg" -> ""` Replaces any substrings in stderr with another to normalise output between different machines and targets. By default, you should have to not specify any and only add them as needed. List of common substitutions: - * `// normalize-stderr-test "OpLine .*\n" -> ""` remove all line numbers from debug info - * `// normalize-stderr-test "OpSource .*\n" -> ""` remove all source code from debug info - * `// normalize-stderr-test "\S*/crates/spirv-std/src/" -> "$$SPIRV_STD_SRC/"` normalize path to the `spirv-std` crate - * `// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""` remove the vulkan memory model *capability*, only used by vulkan targets - * `// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""` remove the vulkan memory model *extension*, only used by vulkan targets - * `// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"` replace the `Vulkan` memory model with `Simple` - * `// normalize-stderr-test "\S*/lib/rustlib/" -> "$$SYSROOT/lib/rustlib/"` normalize path to crates delivered with rustc, such as `core` + * remove debug info: + * `// normalize-stderr-test "OpLine .*\n" -> ""` remove all line numbers + * `// normalize-stderr-test "OpSource .*\n" -> ""` remove all source code + * `// normalize-stderr-test "%\d+ = OpString .*\n" -> ""` remove all src file paths + * when disassembling globals and testing on both `vulkan` and `spv` targets: + * `// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""` remove the vulkan memory model *capability*, only used by `vulkan` targets + * `// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""` remove the vulkan memory model *extension*, only used by `vulkan` targets + * `// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"` replace the `Vulkan` memory model with `Simple`, which `spv` targets use + * `// normalize-stderr-test "; .*\n" -> ""` remove comments on spirv version by rspirv + * remove rustc error src paths: + * `// normalize-stderr-test "\S*/lib/rustlib/" -> "$$SYSROOT/lib/rustlib/"` normalize path to crates delivered with rustc, such as `core` + * `// normalize-stderr-test "\S*/crates/spirv-std/src/" -> "$$SPIRV_STD_SRC/"` normalize path to the `spirv-std` crate From f81832c5b41c90ed15ea8ba01633451e69495fc6 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Mon, 13 Oct 2025 17:04:06 +0200 Subject: [PATCH 20/20] abi layout: remove pqp_cg_ssa patch for `#[repr(SIMD)]` --- crates/rustc_codegen_spirv/build.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/crates/rustc_codegen_spirv/build.rs b/crates/rustc_codegen_spirv/build.rs index 624be5ecf3..3cf04312e1 100644 --- a/crates/rustc_codegen_spirv/build.rs +++ b/crates/rustc_codegen_spirv/build.rs @@ -234,17 +234,6 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {", ); } else if relative_path == Path::new("src/mir/operand.rs") { src = src.replace("alloca(field.size,", "typed_alloca(llfield_ty,"); - - // HACK(eddyb) non-array `#[repr(simd)]` workarounds (see `src/abi.rs`). - src = src.replace("if constant_ty.is_simd() {", "if false {"); - src = src.replace( - "match (self.val, self.layout.backend_repr) {", - "match (self.val, self.layout.backend_repr) { - // `#[repr(simd)]` types are also immediate. - (OperandValue::Immediate(llval), BackendRepr::SimdVector { element, .. }) => { - (Some(element), bx.extract_element(llval, bx.cx().const_usize(i as u64))) - }", - ); } fs::write(out_path, src)?;