diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 3c0c025845..4ad1777b86 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -4,22 +4,23 @@ use crate::attr::{AggregatedSpirvAttributes, IntrinsicType}; use crate::codegen_cx::CodegenCx; use crate::spirv_type::SpirvType; +use crate::symbols::Symbols; use itertools::Itertools; use rspirv::spirv::{Dim, ImageFormat, StorageClass, Word}; -use rustc_abi::ExternAbi as Abi; +use rustc_abi::{AbiAlign, 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, LayoutData, Primitive, ReprFlags, ReprOptions, + Scalar, Size, TagEncoding, 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}; +use rustc_middle::ty::layout::{FnAbiOf, LayoutError, LayoutOf, TyAndLayout}; use rustc_middle::ty::{ - self, Const, CoroutineArgs, CoroutineArgsExt as _, FloatTy, IntTy, PolyFnSig, Ty, TyCtxt, - TyKind, UintTy, + self, AdtDef, Const, CoroutineArgs, CoroutineArgsExt as _, FloatTy, GenericArgs, IntTy, + PolyFnSig, Ty, TyCtxt, TyKind, TypingEnv, UintTy, }; use rustc_middle::ty::{GenericArgsRef, ScalarInt}; use rustc_middle::{bug, span_bug}; @@ -164,84 +165,19 @@ pub(crate) fn provide(providers: &mut Providers) { } } - providers.layout_of = |tcx, key| { + providers.layout_of = layout_of; + + fn layout_of<'tcx>( + tcx: TyCtxt<'tcx>, + key: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, + ) -> Result, &'tcx LayoutError<'tcx>> { // 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 }); + if let Some(layout) = layout_of_spirv_attr_special(tcx, key)? { + return Ok(layout); } let TyAndLayout { ty, mut layout } = @@ -268,7 +204,136 @@ pub(crate) fn provide(providers: &mut Providers) { } Ok(TyAndLayout { ty, layout }) - }; + } + + fn layout_of_spirv_attr_special<'tcx>( + tcx: TyCtxt<'tcx>, + key: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, + ) -> Result>, &'tcx LayoutError<'tcx>> { + let ty::PseudoCanonicalInput { + typing_env, + value: ty, + } = key; + + match ty.kind() { + ty::Adt(def, args) => { + let def: &AdtDef<'tcx> = def; + let args: &'tcx GenericArgs<'tcx> = args; + let attrs = AggregatedSpirvAttributes::parse( + tcx, + &Symbols::get(), + tcx.get_all_attrs(def.did()), + ); + + // add spirv-attr special layouts here + if let Some(layout) = + layout_of_spirv_vector(tcx, typing_env, ty, def, args, &attrs)? + { + return Ok(Some(layout)); + } + } + _ => {} + } + Ok(None) + } + + fn layout_of_spirv_vector<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + ty: Ty<'tcx>, + def: &AdtDef<'tcx>, + args: &'tcx GenericArgs<'tcx>, + attrs: &AggregatedSpirvAttributes, + ) -> Result>, &'tcx LayoutError<'tcx>> { + let layout_err = |msg| { + &*tcx.arena.alloc(LayoutError::ReferencesError( + tcx.dcx().span_err(tcx.def_span(def.did()), msg), + )) + }; + + let has_spirv_vector_attr = attrs + .intrinsic_type + .as_ref() + .map_or(false, |attr| matches!(attr.value, IntrinsicType::Vector)); + let has_repr_simd = def.repr().simd() && !def.repr().packed(); + if !has_spirv_vector_attr && !has_repr_simd { + return Ok(None); + } + + let elements = def + .non_enum_variant() + .fields + .iter() + .map(|f| f.ty(tcx, args)) + .dedup_with_count() + .exactly_one() + .ok() + .and_then(|(count, e_ty)| { + u64::try_from(count) + .ok() + .filter(|&e_len| e_len >= 2) + .map(|e_len| (e_len, e_ty)) + }); + let (e_len, e_ty) = match elements { + None => { + return if has_repr_simd { + // core SIMD struct, not glam vector, don't do anything special + Ok(None) + } else { + Err(layout_err(format!( + "spirv vector type `{ty}` must have at least 2 elements of a single element" + ))) + }; + } + Some(len) => len, + }; + if !def.is_struct() { + return Err(layout_err(format!( + "spirv vector type `{ty}` must be a struct" + ))); + } + + let lcx = ty::layout::LayoutCx::new(tcx, typing_env.with_post_analysis_normalized(tcx)); + + // Compute the ABI of the element type: + let e_ly: TyAndLayout<'_> = lcx.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. + return Err(layout_err(format!( + "spirv vector type `{ty}` must have a non-primitive-scalar (integer/float/pointer) element type, got `{}`", + e_ly.ty + ))); + }; + + // Compute the size and alignment of the vector: + let size = e_ly.size.checked_mul(e_len, &lcx).unwrap(); + let align = def.repr().align.unwrap_or(e_ly.align.abi); + let size = size.align_to(align); + + 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: AbiAlign::new(align), + max_repr_align: None, + unadjusted_abi_align: align, + randomization_seed: e_ly.randomization_seed.wrapping_add(Hash64::new(e_len)), + }); + + Ok(Some(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"), @@ -318,7 +383,7 @@ pub(crate) fn provide(providers: &mut Providers) { let valid_non_array_simd_struct = trivial_struct.is_some_and(|adt_def| { let ReprOptions { int: None, - align: None, + align: _, pack: None, flags: ReprFlags::IS_SIMD, field_shuffle_seed: _, @@ -540,7 +605,8 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> { span = cx.tcx.def_span(adt.did()); } - let attrs = AggregatedSpirvAttributes::parse(cx, cx.tcx.get_attrs_unchecked(adt.did())); + let attrs = + AggregatedSpirvAttributes::parse(cx.tcx, &cx.sym, cx.tcx.get_all_attrs(adt.did())); if let Some(intrinsic_type_attr) = attrs.intrinsic_type.map(|attr| attr.value) && let Ok(spirv_type) = @@ -771,9 +837,9 @@ fn dig_scalar_pointee<'tcx>( match pointee { Some(old_pointee) if old_pointee != new_pointee => { cx.tcx.dcx().fatal(format!( - "dig_scalar_pointee: unsupported Pointer with different \ + "dig_scalar_pointee: unsupported Pointer with different \ pointee types ({old_pointee:?} vs {new_pointee:?}) at offset {offset:?} in {layout:#?}" - )); + )); } _ => pointee = Some(new_pointee), } @@ -1258,5 +1324,8 @@ fn trans_intrinsic_type<'tcx>( } .def(span, cx)) } + IntrinsicType::Vector => { + todo!() + } } } diff --git a/crates/rustc_codegen_spirv/src/attr.rs b/crates/rustc_codegen_spirv/src/attr.rs index b35dbb6c3c..33d725ad0a 100644 --- a/crates/rustc_codegen_spirv/src/attr.rs +++ b/crates/rustc_codegen_spirv/src/attr.rs @@ -2,8 +2,7 @@ //! //! The attribute-checking parts of this try to follow `rustc_passes::check_attr`. -use crate::codegen_cx::CodegenCx; -use crate::symbols::Symbols; +use crate::symbols::{Symbols, parse_attrs_for_checking}; use rspirv::spirv::{BuiltIn, ExecutionMode, ExecutionModel, StorageClass}; use rustc_hir as hir; use rustc_hir::def_id::LocalModDefId; @@ -66,6 +65,7 @@ pub enum IntrinsicType { RuntimeArray, TypedBuffer, Matrix, + Vector, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -147,16 +147,20 @@ impl AggregatedSpirvAttributes { /// /// Any errors for malformed/duplicate attributes will have been reported /// prior to codegen, by the `attr` check pass. - pub fn parse<'tcx>(cx: &CodegenCx<'tcx>, attrs: &'tcx [Attribute]) -> Self { + pub fn parse<'tcx>( + tcx: TyCtxt<'tcx>, + sym: &Symbols, + attrs: impl Iterator, + ) -> Self { let mut aggregated_attrs = Self::default(); // NOTE(eddyb) `span_delayed_bug` ensures that if attribute checking fails // to see an attribute error, it will cause an ICE instead. - for parse_attr_result in crate::symbols::parse_attrs_for_checking(&cx.sym, attrs) { + for parse_attr_result in parse_attrs_for_checking(sym, attrs) { let (span, parsed_attr) = match parse_attr_result { Ok(span_and_parsed_attr) => span_and_parsed_attr, Err((span, msg)) => { - cx.tcx.dcx().span_delayed_bug(span, msg); + tcx.dcx().span_delayed_bug(span, msg); continue; } }; @@ -166,8 +170,7 @@ impl AggregatedSpirvAttributes { prev_span: _, category, }) => { - cx.tcx - .dcx() + tcx.dcx() .span_delayed_bug(span, format!("multiple {category} attributes")); } } @@ -278,10 +281,8 @@ impl CheckSpirvAttrVisitor<'_> { fn check_spirv_attributes(&self, hir_id: HirId, target: Target) { let mut aggregated_attrs = AggregatedSpirvAttributes::default(); - let parse_attrs = |attrs| crate::symbols::parse_attrs_for_checking(&self.sym, attrs); - let attrs = self.tcx.hir_attrs(hir_id); - for parse_attr_result in parse_attrs(attrs) { + for parse_attr_result in parse_attrs_for_checking(&self.sym, attrs.into_iter()) { let (span, parsed_attr) = match parse_attr_result { Ok(span_and_parsed_attr) => span_and_parsed_attr, Err((span, msg)) => { @@ -326,9 +327,12 @@ impl CheckSpirvAttrVisitor<'_> { | SpirvAttribute::SpecConstant(_) => match target { Target::Param => { let parent_hir_id = self.tcx.parent_hir_id(hir_id); - let parent_is_entry_point = parse_attrs(self.tcx.hir_attrs(parent_hir_id)) - .filter_map(|r| r.ok()) - .any(|(_, attr)| matches!(attr, SpirvAttribute::Entry(_))); + let parent_is_entry_point = parse_attrs_for_checking( + &self.sym, + self.tcx.hir_attrs(parent_hir_id).iter(), + ) + .filter_map(|r| r.ok()) + .any(|(_, attr)| matches!(attr, SpirvAttribute::Entry(_))); if !parent_is_entry_point { self.tcx.dcx().span_err( span, diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs index 104a98e14a..b268dd91b9 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs @@ -133,7 +133,8 @@ impl<'tcx> CodegenCx<'tcx> { self.set_linkage(fn_id, symbol_name.to_owned(), linkage); } - let attrs = AggregatedSpirvAttributes::parse(self, self.tcx.get_attrs_unchecked(def_id)); + let attrs = + AggregatedSpirvAttributes::parse(self.tcx, &self.sym, self.tcx.get_all_attrs(def_id)); if let Some(entry) = attrs.entry.map(|attr| attr.value) { // HACK(eddyb) early insert to let `shader_entry_stub` call this // very function via `get_fn_addr`. diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs index 5de823d261..04a7ed8fb4 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs @@ -433,7 +433,11 @@ impl<'tcx> CodegenCx<'tcx> { call_args: &mut Vec, decoration_locations: &mut FxHashMap, ) { - let attrs = AggregatedSpirvAttributes::parse(self, self.tcx.hir_attrs(hir_param.hir_id)); + let attrs = AggregatedSpirvAttributes::parse( + self.tcx, + &self.sym, + self.tcx.hir_attrs(hir_param.hir_id).iter(), + ); let EntryParamDeducedFromRustRefOrValue { value_layout, diff --git a/crates/rustc_codegen_spirv/src/spirv_type.rs b/crates/rustc_codegen_spirv/src/spirv_type.rs index 074e5708d1..b488a75c3c 100644 --- a/crates/rustc_codegen_spirv/src/spirv_type.rs +++ b/crates/rustc_codegen_spirv/src/spirv_type.rs @@ -280,9 +280,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 { element, count } => cx.lookup_type(element).sizeof(cx)? * count as u64, Self::Matrix { element, count } => cx.lookup_type(element).sizeof(cx)? * count as u64, Self::Array { element, count } => { cx.lookup_type(element).sizeof(cx)? @@ -311,14 +309,8 @@ 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::Array { element, .. } + Self::Vector { element, .. } + | Self::Array { element, .. } | Self::RuntimeArray { element } | Self::Matrix { element, .. } => cx.lookup_type(element).alignof(cx), Self::Pointer { .. } => cx.tcx.data_layout.pointer_align.abi, diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index 4dfef5b4d3..c4e6863c6e 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -373,6 +373,10 @@ impl Symbols { "matrix", SpirvAttribute::IntrinsicType(IntrinsicType::Matrix), ), + ( + "vector", + SpirvAttribute::IntrinsicType(IntrinsicType::Vector), + ), ("buffer_load_intrinsic", SpirvAttribute::BufferLoadIntrinsic), ( "buffer_store_intrinsic", @@ -441,11 +445,11 @@ impl Symbols { type ParseAttrError = (Span, String); // FIXME(eddyb) maybe move this to `attr`? -pub(crate) fn parse_attrs_for_checking<'a>( - sym: &'a Symbols, - attrs: &'a [Attribute], -) -> impl Iterator> + 'a { - attrs.iter().flat_map(move |attr| { +pub(crate) fn parse_attrs_for_checking<'a, 'b>( + sym: &'b Symbols, + attrs: impl Iterator + 'b, +) -> impl Iterator> + 'b { + attrs.flat_map(move |attr| { let (whole_attr_error, args) = match attr { Attribute::Unparsed(item) => { // #[...] diff --git a/tests/difftests/tests/Cargo.lock b/tests/difftests/tests/Cargo.lock index c5ef3d9086..86389a07e6 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 = "adler2" version = "2.0.1" diff --git a/tests/difftests/tests/Cargo.toml b/tests/difftests/tests/Cargo.toml index d3e9fd6e3a..7f2d14cce2 100644 --- a/tests/difftests/tests/Cargo.toml +++ b/tests/difftests/tests/Cargo.toml @@ -17,6 +17,8 @@ members = [ "arch/push_constants/push_constants-wgsl", "storage_class/array_access/array_access-rust", "storage_class/array_access/array_access-wgsl", + "lang/abi/vector_layout/cpu", + "lang/abi/vector_layout/rust-gpu", "lang/control_flow/control_flow-rust", "lang/control_flow/control_flow-wgsl", "lang/control_flow_complex/control_flow_complex-rust", diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml new file mode 100644 index 0000000000..db41132735 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "abi-vector-layout-cpu" +edition.workspace = true + +[lints] +workspace = true + +# GPU deps +[dependencies] +spirv-std.workspace = true +bytemuck.workspace = true + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs new file mode 100644 index 0000000000..bbfa3b6289 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs @@ -0,0 +1,11 @@ +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(); + 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 new file mode 100644 index 0000000000..8751a076d2 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs @@ -0,0 +1,83 @@ +use core::mem::offset_of; +use experiments::*; +use spirv_std::glam::*; + +pub struct BumpAlloc(usize); + +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;)* + } + }; +} + +/// 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)), + _ => {} + } +} + +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/lib.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs new file mode 100644 index 0000000000..4838d65e2f --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs @@ -0,0 +1,8 @@ +#![cfg_attr(target_arch = "spirv", no_std)] + +#[cfg(not(target_arch = "spirv"))] +pub mod cpu_driver; +pub mod layout; +pub mod shader; +#[cfg(not(target_arch = "spirv"))] +pub mod shader_driver; diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs new file mode 100644 index 0000000000..cecb91f595 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + abi_vector_layout_cpu::cpu_driver::run(); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs new file mode 100644 index 0000000000..4ce4ea67ca --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader.rs @@ -0,0 +1,11 @@ +use crate::layout::eval_layouts; +use spirv_std::glam::UVec3; +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +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 new file mode 100644 index 0000000000..3e3639a3ff --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs @@ -0,0 +1,13 @@ +use crate::layout::{LAYOUT_COUNT, LAYOUT_LEN}; +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(), + [LAYOUT_COUNT as u32, 1, 1], + Vec::from(&[BufferConfig::writeback(LAYOUT_LEN * size_of::())]), + ); + test.run_test(&config).unwrap(); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/Cargo.toml new file mode 100644 index 0000000000..e178ce6d25 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "abi-vector-layout-rust-gpu" +edition.workspace = true + +[lib] +crate-type = ["lib", "dylib"] + +[lints] +workspace = true + +[dependencies] +abi-vector-layout-cpu = { path = "../cpu" } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/lib.rs b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/lib.rs new file mode 100644 index 0000000000..219229aa2e --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/lib.rs @@ -0,0 +1,3 @@ +#![cfg_attr(target_arch = "spirv", no_std)] + +pub use abi_vector_layout_cpu::shader::main_cs; diff --git a/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs new file mode 100644 index 0000000000..98fa9c5549 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/rust-gpu/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + abi_vector_layout_cpu::shader_driver::run(); +}