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 74d87cce664..56cbc7a14eb 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 7c744953257..fa3078d664c 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 00000000000..db41132735b --- /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 00000000000..9a6f85c3d91 --- /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 00000000000..ff8d6a067d9 --- /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 00000000000..4838d65e2f0 --- /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 00000000000..cecb91f595d --- /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 00000000000..f2ced209936 --- /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 00000000000..ee99bba1909 --- /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 00000000000..e178ce6d251 --- /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 00000000000..219229aa2ec --- /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 00000000000..98fa9c55496 --- /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 9a6f85c3d91..bbfa3b6289f 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 ff8d6a067d9..8751a076d2f 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 f2ced209936..4ce4ea67ca7 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 ee99bba1909..3e3639a3ff4 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 ad4d30a45d6..e34454bb239 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 b461aed2264..7e00acb1ed6 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 56cbc7a14eb..626060e31b4 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 fa3078d664c..37b29cc4b83 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 f80bf6092cb..f74fa222bbb 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 cc7f7b52fd3..9be755e6875 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 b5a727be7c8..b3d9fff0302 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 0aaee7f8d7e..00ffaf661bb 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 8441d74c08a..894c5a8d028 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 eb8783049c4..acd9b42172c 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 074e5708d1d..c2d85dcbac3 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 917a61a86a2..e81f1bb7ad1 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 f74fa222bbb..2e876c4cd75 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 0385db83ac7..7c4943ce179 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 088e07f0a51..1d99c101cbb 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 beed8baf222..00000000000 --- 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 9e8e1d2f3f1..e113927411c 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 c01af8f3bad..00000000000 --- 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 5c1b487bded..6ec5956a55a 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 b3d9fff0302..e44fc1e25c9 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 2e876c4cd75..aa80b22b97c 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 d34ef0ac5e1..45b1139cc0f 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 eda87f4c6e0..f1dbb5a1453 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 e9793db1146..05798a6f1d0 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 ffc7660bb95..a0994f53532 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 0ecffc39c4d..52b6bcf9449 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 9bb737c7792..44fa624f0e3 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 821239dc33c..fe2f180d78e 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 8ef6ee7cb1d..40c37e27407 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 d18849b6f6c..26a079b1456 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 4d06f8f6324..4c022abaf0c 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 4890551e39f..a5ec1af9562 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 a41a806a40c..b6fcceb39f9 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 2e8d77e4792..73fdd65b868 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 cde98c1dec4..b99b63a736b 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 aa80b22b97c..7cf33c34e34 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 8751a076d2f..d050e60d5d3 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 626060e31b4..582763fb67e 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 db41132735b..007ef92b042 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 bbfa3b6289f..87ea655780f 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 00000000000..7259525b713 --- /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 4838d65e2f0..08e80c346f8 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 cecb91f595d..bcd5f7c8009 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 3e3639a3ff4..4502da31973 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 98fa9c55496..5ab8e426b51 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 582763fb67e..982445e579f 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 37b29cc4b83..6441b7925da 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 00000000000..4a3223d9db0 --- /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 00000000000..84861065a35 --- /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 00000000000..2ae5c7a13f8 --- /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 00000000000..219229aa2ec --- /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 00000000000..647d8c33857 --- /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 00000000000..fa834aa4194 --- /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 00000000000..044b1b025f5 --- /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 00000000000..225553fd249 --- /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 00000000000..219229aa2ec --- /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 00000000000..c0265cc7e38 --- /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 7cf33c34e34..bd377a8db1f 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 87ea655780f..6ed4a0796dd 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 d050e60d5d3..c9b660f63b3 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 4502da31973..e6d10ffa3db 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 bff86b69649..520348be3a6 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 7c4943ce179..f3ba4a42bbc 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 bd377a8db1f..16c3f0775bf 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 f1dbb5a1453..a14a76b6d65 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 00000000000..7c8ed9be5a5 --- /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 00000000000..b0826e800c3 --- /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 00000000000..6d1e56cf843 --- /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 00000000000..f0b05652ae7 --- /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 16c3f0775bf..0c5cf69f4d8 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 1a48bcb4381..374bb0130ba 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 624be5ecf36..3cf04312e1f 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)?;