diff --git a/Cargo.lock b/Cargo.lock index fdc5a17b1c..734c913e01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -413,11 +413,10 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.36" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ - "find-msvc-tools", "jobserver", "libc", "shlex", @@ -1014,12 +1013,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "find-msvc-tools" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" - [[package]] name = "fixedbitset" version = "0.5.7" @@ -1259,9 +1252,8 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d1aab06663bdce00d6ca5e5ed586ec8d18033a771906c993a1e3755b368d85" +version = "0.30.7" +source = "git+https://github.com/bitshifter/glam-rs?rev=ae9627fca68d6b5b24e80711f8c40bb92d7bdc82#ae9627fca68d6b5b24e80711f8c40bb92d7bdc82" dependencies = [ "bytemuck", "libm", diff --git a/Cargo.toml b/Cargo.toml index 7cf0998410..fdc881fa0c 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", default-features = false } +glam = { version = ">=0.22, <=0.30", git = "https://github.com/bitshifter/glam-rs", rev = "ae9627fca68d6b5b24e80711f8c40bb92d7bdc82", default-features = false } libm = { version = "0.2.5", default-features = false } bytemuck = { version = "1.23", features = ["derive"] } diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 3c0c025845..077ae3d42f 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}; @@ -98,267 +96,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). @@ -639,6 +376,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) } @@ -655,7 +394,7 @@ pub fn scalar_pair_element_backend_type<'tcx>( ty: TyAndLayout<'tcx>, index: usize, ) -> Word { - let [a, b] = match ty.layout.backend_repr() { + let [a, b] = match ty.backend_repr { BackendRepr::ScalarPair(a, b) => [a, b], other => span_bug!( span, @@ -971,6 +710,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)] @@ -1220,43 +963,118 @@ 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 span = span_for_spirv_type_adt(cx, ty).unwrap(); + 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") - .with_note(format!("field type is {}", ty.debug(elem_type, cx))) + .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()); } } - - Ok(SpirvType::Matrix { - element: elem_type, - count: field_types.len() as u32, + Ok(SpirvType::Matrix { element, count }.def(span, cx)) + } + IntrinsicType::Vector => { + let span = span_for_spirv_type_adt(cx, ty).unwrap(); + 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 => { + return Err(cx + .tcx + .dcx() + .struct_span_err( + span, + format!( + "{err_attr_name} type fields must all be floats, integers or bools" + ), + ) + .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| 2 <= *count && *count <= 4) + .ok_or_else(|| { + tcx.dcx() + .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 + .dcx() + .span_err(span, format!("{err_attr_name} type must be a struct"))) + } +} diff --git a/crates/rustc_codegen_spirv/src/attr.rs b/crates/rustc_codegen_spirv/src/attr.rs index cc7f7b52fd..9be755e687 100644 --- a/crates/rustc_codegen_spirv/src/attr.rs +++ b/crates/rustc_codegen_spirv/src/attr.rs @@ -68,6 +68,7 @@ pub enum IntrinsicType { RuntimeArray, TypedBuffer, Matrix, + Vector, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -548,6 +549,22 @@ fn parse_attrs_for_checking<'a>( )) } } + Some(command) if command.name == sym.vector => { + // #[rust_gpu::vector ...] + match s.get(2) { + // #[rust_gpu::vector::v1] + Some(version) if version.name == sym.v1 => { + Ok(SmallVec::from_iter([ + Ok((attr.span(), SpirvAttribute::IntrinsicType(IntrinsicType::Vector))) + ])) + }, + _ => Err(( + attr.span(), + "unknown `rust_gpu::vector` version, expected `rust_gpu::vector::v1`" + .to_string(), + )), + } + } _ => { // #[rust_gpu::...] but not a know version let spirv = sym.spirv_attr_with_version.as_str(); diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index 30fc1ae6c9..a8578b485f 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( @@ -2966,11 +2968,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 { @@ -3007,6 +3007,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:?}")), }; 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 ab2a78cf65..89c488c082 100644 --- a/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs +++ b/crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs @@ -113,7 +113,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, @@ -312,7 +312,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let value_u32 = self.bitcast(value, u32_ty); self.store_u32(array, dynamic_word_index, constant_word_offset, value_u32) } - SpirvType::Vector { element, count } | SpirvType::Matrix { element, count } => self + SpirvType::Vector { element, count, .. } | SpirvType::Matrix { element, count } => self .store_vec_mat_arr( original_type, value, diff --git a/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs b/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs index 8441d74c08..894c5a8d02 100644 --- a/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs +++ b/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs @@ -728,6 +728,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> { SpirvType::Vector { element: ty, count: 4, + .. }, ) | ( diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs b/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs index eb8783049c..acd9b42172 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs @@ -200,10 +200,12 @@ impl ConstCodegenMethods for CodegenCx<'_> { self.constant_composite(struct_ty, elts.iter().map(|f| f.def_cx(self))) } fn const_vector(&self, elts: &[Self::Value]) -> Self::Value { - let vector_ty = SpirvType::Vector { - element: elts[0].ty, - count: elts.len() as u32, - } + let vector_ty = SpirvType::simd_vector( + self, + DUMMY_SP, + self.lookup_type(elts[0].ty), + elts.len() as u32, + ) .def(DUMMY_SP, self); self.constant_composite(vector_ty, elts.iter().map(|elt| elt.def_cx(self))) } diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs index 5de823d261..786cf50781 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs @@ -11,7 +11,10 @@ use rspirv::dr::Operand; use rspirv::spirv::{ Capability, Decoration, Dim, ExecutionModel, FunctionControl, StorageClass, Word, }; -use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods, MiscCodegenMethods as _}; +use rustc_codegen_ssa::traits::{ + BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, LayoutTypeCodegenMethods, + MiscCodegenMethods as _, +}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::MultiSpan; use rustc_hir as hir; @@ -86,22 +89,7 @@ impl<'tcx> CodegenCx<'tcx> { }; for (arg_abi, hir_param) in fn_abi.args.iter().zip(hir_params) { match arg_abi.mode { - PassMode::Direct(_) => {} - PassMode::Pair(..) => { - // FIXME(eddyb) implement `ScalarPair` `Input`s, or change - // the `FnAbi` readjustment to only use `PassMode::Pair` for - // pointers to `!Sized` types, but not other `ScalarPair`s. - if !matches!(arg_abi.layout.ty.kind(), ty::Ref(..)) { - self.tcx.dcx().span_err( - hir_param.ty_span, - format!( - "entry point parameter type not yet supported \ - (`{}` has `ScalarPair` ABI but is not a `&T`)", - arg_abi.layout.ty - ), - ); - } - } + PassMode::Direct(_) | PassMode::Pair(..) => {} // FIXME(eddyb) support these (by just ignoring them) - if there // is any validation concern, it should be done on the types. PassMode::Ignore => self.tcx.dcx().span_fatal( @@ -442,6 +430,33 @@ impl<'tcx> CodegenCx<'tcx> { } = self.entry_param_deduce_from_rust_ref_or_value(entry_arg_abi.layout, hir_param, &attrs); let value_spirv_type = value_layout.spirv_type(hir_param.ty_span, self); + // In compute shaders, user-provided data must come from buffers or push + // constants, i.e. by-reference parameters. + if execution_model == ExecutionModel::GLCompute + && matches!(entry_arg_abi.mode, PassMode::Direct(_) | PassMode::Pair(..)) + && !matches!(entry_arg_abi.layout.ty.kind(), ty::Ref(..)) + && attrs.builtin.is_none() + { + let param_name = if let hir::PatKind::Binding(_, _, ident, _) = &hir_param.pat.kind { + ident.name.to_string() + } else { + "parameter".to_string() + }; + self.tcx + .dcx() + .struct_span_err( + hir_param.ty_span, + format!("compute entry parameter `{param_name}` must be by-reference",), + ) + .with_help(format!( + "consider changing the type to `&{}`", + entry_arg_abi.layout.ty + )) + .emit(); + // Keep this a hard error to stop compilation after emitting help. + self.tcx.dcx().abort_if_errors(); + } + let (var_id, spec_const_id) = match storage_class { // Pre-allocate the module-scoped `OpVariable` *Result* ID. Ok(_) => ( @@ -491,14 +506,6 @@ impl<'tcx> CodegenCx<'tcx> { vs layout:\n{value_layout:#?}", entry_arg_abi.layout.ty ); - if is_pair && !is_unsized { - // If PassMode is Pair, then we need to fill in the second part of the pair with a - // value. We currently only do that with unsized types, so if a type is a pair for some - // other reason (e.g. a tuple), we bail. - self.tcx - .dcx() - .span_fatal(hir_param.ty_span, "pair type not supported yet") - } // FIXME(eddyb) should this talk about "typed buffers" instead of "interface blocks"? // FIXME(eddyb) should we talk about "descriptor indexing" or // actually use more reasonable terms like "resource arrays"? @@ -621,8 +628,8 @@ impl<'tcx> CodegenCx<'tcx> { } } - let value_len = if is_pair { - // We've already emitted an error, fill in a placeholder value + let value_len = if is_pair && is_unsized { + // For wide references (e.g., slices), the second component is a length. Some(bx.undef(self.type_isize())) } else { None @@ -645,21 +652,54 @@ impl<'tcx> CodegenCx<'tcx> { _ => unreachable!(), } } else { - assert_matches!(entry_arg_abi.mode, PassMode::Direct(_)); - - let value = match storage_class { - Ok(_) => { + match entry_arg_abi.mode { + PassMode::Direct(_) => { + let value = match storage_class { + Ok(_) => { + assert_eq!(storage_class, Ok(StorageClass::Input)); + bx.load( + entry_arg_abi.layout.spirv_type(hir_param.ty_span, bx), + value_ptr.unwrap(), + entry_arg_abi.layout.align.abi, + ) + } + Err(SpecConstant { .. }) => { + spec_const_id.unwrap().with_type(value_spirv_type) + } + }; + call_args.push(value); + assert_eq!(value_len, None); + } + PassMode::Pair(..) => { + // Load both elements of the scalar pair from the input variable. assert_eq!(storage_class, Ok(StorageClass::Input)); - bx.load( - entry_arg_abi.layout.spirv_type(hir_param.ty_span, bx), - value_ptr.unwrap(), - entry_arg_abi.layout.align.abi, - ) + let layout = entry_arg_abi.layout; + let (a, b) = match layout.backend_repr { + rustc_abi::BackendRepr::ScalarPair(a, b) => (a, b), + other => span_bug!( + hir_param.ty_span, + "ScalarPair expected for entry param, found {other:?}" + ), + }; + let b_offset = a + .primitive() + .size(self) + .align_to(b.primitive().align(self).abi); + + let elem0_ty = self.scalar_pair_element_backend_type(layout, 0, false); + let elem1_ty = self.scalar_pair_element_backend_type(layout, 1, false); + + let base_ptr = value_ptr.unwrap(); + let ptr1 = bx.inbounds_ptradd(base_ptr, self.const_usize(b_offset.bytes())); + + let v0 = bx.load(elem0_ty, base_ptr, layout.align.abi); + let v1 = bx.load(elem1_ty, ptr1, layout.align.restrict_for_offset(b_offset)); + call_args.push(v0); + call_args.push(v1); + assert_eq!(value_len, None); } - Err(SpecConstant { .. }) => spec_const_id.unwrap().with_type(value_spirv_type), - }; - call_args.push(value); - assert_eq!(value_len, None); + _ => unreachable!(), + } } // FIXME(eddyb) check whether the storage class is compatible with the diff --git a/crates/rustc_codegen_spirv/src/spirv_type.rs b/crates/rustc_codegen_spirv/src/spirv_type.rs index 074e5708d1..c2d85dcbac 100644 --- a/crates/rustc_codegen_spirv/src/spirv_type.rs +++ b/crates/rustc_codegen_spirv/src/spirv_type.rs @@ -45,6 +45,8 @@ pub enum SpirvType<'tcx> { element: Word, /// Note: vector count is literal. count: u32, + size: Size, + align: Align, }, Matrix { element: Word, @@ -131,7 +133,9 @@ impl SpirvType<'_> { } result } - Self::Vector { element, count } => cx.emit_global().type_vector_id(id, element, count), + Self::Vector { element, count, .. } => { + cx.emit_global().type_vector_id(id, element, count) + } Self::Matrix { element, count } => cx.emit_global().type_matrix_id(id, element, count), Self::Array { element, count } => { let result = cx @@ -280,9 +284,7 @@ impl SpirvType<'_> { Self::Bool => Size::from_bytes(1), Self::Integer(width, _) | Self::Float(width) => Size::from_bits(width), Self::Adt { size, .. } => size?, - Self::Vector { element, count } => { - cx.lookup_type(element).sizeof(cx)? * count.next_power_of_two() as u64 - } + Self::Vector { size, .. } => size, Self::Matrix { element, count } => cx.lookup_type(element).sizeof(cx)? * count as u64, Self::Array { element, count } => { cx.lookup_type(element).sizeof(cx)? @@ -310,14 +312,7 @@ impl SpirvType<'_> { Self::Bool => Align::from_bytes(1).unwrap(), Self::Integer(width, _) | Self::Float(width) => Align::from_bits(width as u64).unwrap(), - Self::Adt { align, .. } => align, - // Vectors have size==align - Self::Vector { .. } => Align::from_bytes( - self.sizeof(cx) - .expect("alignof: Vectors must be sized") - .bytes(), - ) - .expect("alignof: Vectors must have power-of-2 size"), + Self::Adt { align, .. } | Self::Vector { align, .. } => align, Self::Array { element, .. } | Self::RuntimeArray { element } | Self::Matrix { element, .. } => cx.lookup_type(element).alignof(cx), @@ -382,7 +377,17 @@ impl SpirvType<'_> { SpirvType::Bool => SpirvType::Bool, SpirvType::Integer(width, signedness) => SpirvType::Integer(width, signedness), SpirvType::Float(width) => SpirvType::Float(width), - SpirvType::Vector { element, count } => SpirvType::Vector { element, count }, + SpirvType::Vector { + element, + count, + size, + align, + } => SpirvType::Vector { + element, + count, + size, + align, + }, SpirvType::Matrix { element, count } => SpirvType::Matrix { element, count }, SpirvType::Array { element, count } => SpirvType::Array { element, count }, SpirvType::RuntimeArray { element } => SpirvType::RuntimeArray { element }, @@ -435,6 +440,15 @@ impl SpirvType<'_> { }, } } + + pub fn simd_vector(cx: &CodegenCx<'_>, span: Span, element: SpirvType<'_>, count: u32) -> Self { + Self::Vector { + element: element.def(span, cx), + count, + size: element.sizeof(cx).unwrap() * count as u64, + align: element.alignof(cx), + } + } } impl<'a> SpirvType<'a> { @@ -501,11 +515,18 @@ impl fmt::Debug for SpirvTypePrinter<'_, '_> { .field("field_names", &field_names) .finish() } - SpirvType::Vector { element, count } => f + SpirvType::Vector { + element, + count, + size, + align, + } => f .debug_struct("Vector") .field("id", &self.id) .field("element", &self.cx.debug_type(element)) .field("count", &count) + .field("size", &size) + .field("align", &align) .finish(), SpirvType::Matrix { element, count } => f .debug_struct("Matrix") @@ -668,7 +689,7 @@ impl SpirvTypePrinter<'_, '_> { } f.write_str(" }") } - SpirvType::Vector { element, count } | SpirvType::Matrix { element, count } => { + SpirvType::Vector { element, count, .. } | SpirvType::Matrix { element, count } => { ty(self.cx, stack, f, element)?; write!(f, "x{count}") } diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index 475e100f10..14d0c7ae77 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"), diff --git a/crates/spirv-std/src/arch/subgroup.rs b/crates/spirv-std/src/arch/subgroup.rs index e9793db114..05798a6f1d 100644 --- a/crates/spirv-std/src/arch/subgroup.rs +++ b/crates/spirv-std/src/arch/subgroup.rs @@ -13,10 +13,7 @@ const SUBGROUP: u32 = Scope::Subgroup as u32; /// `SubgroupMask` is a [`glam::UVec4`] representing a bitmask of all invocations within a subgroup. /// Mostly used in group ballot operations. -#[repr(transparent)] -#[derive(Copy, Clone, Default, Eq, PartialEq)] -#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))] -pub struct SubgroupMask(pub glam::UVec4); +pub type SubgroupMask = glam::UVec4; /// Defines the class of group operation. #[non_exhaustive] diff --git a/crates/spirv-std/src/scalar.rs b/crates/spirv-std/src/scalar.rs index bff86b6964..520348be3a 100644 --- a/crates/spirv-std/src/scalar.rs +++ b/crates/spirv-std/src/scalar.rs @@ -5,9 +5,15 @@ use core::num::NonZeroUsize; /// Abstract trait representing a SPIR-V scalar type. /// +/// Implemented on types that map to spirv "scalar" types, which includes: +/// * Floating-point type: f32, f64 +/// * Integer type: u8, u16, u32, u64, i8, i16, i32, i64 +/// * Boolean type: bool +/// +/// See the SPIRV spec on [Types](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_types). +/// /// # Safety -/// Implementing this trait on non-scalar types breaks assumptions of other unsafe code, and should -/// not be done. +/// Must only be implemented on spirv "scalar" types, as mentioned above. pub unsafe trait Scalar: VectorOrScalar + crate::sealed::Sealed {} macro_rules! impl_scalar { diff --git a/crates/spirv-std/src/vector.rs b/crates/spirv-std/src/vector.rs index 0385db83ac..f3ba4a42bb 100644 --- a/crates/spirv-std/src/vector.rs +++ b/crates/spirv-std/src/vector.rs @@ -7,8 +7,7 @@ use glam::{Vec3Swizzles, Vec4Swizzles}; /// Abstract trait representing either a vector or a scalar type. /// /// # Safety -/// Implementing this trait on non-scalar or non-vector types may break assumptions about other -/// unsafe code, and should not be done. +/// Your type must also implement [`Vector`] or [`Scalar`], see their safety sections as well. pub unsafe trait VectorOrScalar: Copy + Default + Send + Sync + 'static { /// Either the scalar component type of the vector or the scalar itself. type Scalar: Scalar; @@ -27,9 +26,44 @@ pub(crate) const fn create_dim(n: usize) -> NonZeroUsize { /// Abstract trait representing a SPIR-V vector type. /// +/// To implement this trait, your struct must be marked with: +/// ```no_run +/// #[cfg_attr(target_arch = "spirv", rust_gpu::vector::v1)] +/// # struct Bla(f32, f32); +/// ``` +/// +/// This places these additional constraints on your type, checked by the spirv codegen: +/// * must be a struct +/// * members must be a spirv [`Scalar`] type, which includes: +/// * Floating-point type: f32, f64 +/// * Integer type: u8, u16, u32, u64, i8, i16, i32, i64 +/// * Boolean type: bool +/// * all members must be of the same primitive type +/// * must have 2, 3 or 4 vector components / members +/// * type must derive Copy, Clone, Default +/// +/// The spirv codegen backend will then emit your type as an `OpTypeVector` instead of an `OpTypeStruct`. The layout of +/// your type is unaffected, the size, alignment and member offsets will follow standard rustc layout rules. This hint +/// does nothing on other target platforms. +/// +/// See the SPIRV spec on [Types](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_types) and the +/// "Data rules" in the [Universal Validation Rules](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_universal_validation_rules). +/// +/// # Example +/// ```no_run +/// #[derive(Copy, Clone, Default)] +/// #[cfg_attr(target_arch = "spirv", rust_gpu::vector::v1)] +/// struct MyColor { +/// r: f32, +/// b: f32, +/// g: f32, +/// } +/// ``` +/// +/// /// # Safety -/// Implementing this trait on non-simd-vector types breaks assumptions of other unsafe code, and -/// should not be done. +/// Must only be implemented on types that the spirv codegen emits as valid `OpTypeVector`. This includes all structs +/// marked with `#[rust_gpu::vector::v1]`, like [`glam`]'s non-SIMD "scalar" vector types. pub unsafe trait Vector: VectorOrScalar {} macro_rules! impl_vector { @@ -49,6 +83,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/README.md b/tests/compiletests/README.md index 599f3b2899..565e5ef9cc 100644 --- a/tests/compiletests/README.md +++ b/tests/compiletests/README.md @@ -71,10 +71,15 @@ contents of `path/to/test.rs.stdout`. * `// 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 diff --git a/tests/compiletests/ui/arch/all.rs b/tests/compiletests/ui/arch/all.rs index 088e07f0a5..1d99c101cb 100644 --- a/tests/compiletests/ui/arch/all.rs +++ b/tests/compiletests/ui/arch/all.rs @@ -1,36 +1,11 @@ // build-pass -#![feature(repr_simd)] - use core::num::NonZeroUsize; use spirv_std::spirv; use spirv_std::{scalar::Scalar, vector::Vector, vector::VectorOrScalar}; -/// HACK(shesp). Rust doesn't allow us to declare regular (tuple-)structs containing `bool` members -/// as `#[repl(simd)]`. But we need this for `spirv_std::arch::any()` and `spirv_std::arch::all()` -/// to work. -/// Fortunately, this requirement isn't checked on generic structs, so we have a way to work around -/// it (for now at least) -#[repr(simd)] -#[derive(Copy, Clone, Debug)] -struct Vec2(T, T); -unsafe impl VectorOrScalar for Vec2 { - type Scalar = T; - const DIM: NonZeroUsize = match NonZeroUsize::new(2) { - None => panic!(), - Some(n) => n, - }; -} -unsafe impl Vector for Vec2 {} - -impl Default for Vec2 { - fn default() -> Vec2 { - Vec2(T::default(), T::default()) - } -} - #[spirv(fragment)] pub fn main() { - let vector = Vec2(true, true); + let vector = glam::BVec2::new(true, true); assert!(spirv_std::arch::all(vector)); } diff --git a/tests/compiletests/ui/arch/all.stderr b/tests/compiletests/ui/arch/all.stderr deleted file mode 100644 index beed8baf22..0000000000 --- a/tests/compiletests/ui/arch/all.stderr +++ /dev/null @@ -1,12 +0,0 @@ -warning: [Rust-GPU] temporarily re-allowing old-style `#[repr(simd)]` (with fields) - --> $DIR/all.rs:16:1 - | -16 | struct Vec2(T, T); - | ^^^^^^^^^^^^^^ - | - = note: removed upstream by https://github.com/rust-lang/rust/pull/129403 - = note: in favor of the new `#[repr(simd)] struct TxN([T; N]);` style - = note: (taking effect since `nightly-2024-09-12` / `1.83.0` stable) - -warning: 1 warning emitted - diff --git a/tests/compiletests/ui/arch/any.rs b/tests/compiletests/ui/arch/any.rs index 9e8e1d2f3f..e113927411 100644 --- a/tests/compiletests/ui/arch/any.rs +++ b/tests/compiletests/ui/arch/any.rs @@ -1,36 +1,11 @@ // build-pass -#![feature(repr_simd)] - use core::num::NonZeroUsize; use spirv_std::spirv; use spirv_std::{scalar::Scalar, vector::Vector, vector::VectorOrScalar}; -/// HACK(shesp). Rust doesn't allow us to declare regular (tuple-)structs containing `bool` members -/// as `#[repl(simd)]`. But we need this for `spirv_std::arch::any()` and `spirv_std::arch::all()` -/// to work. -/// Fortunately, this requirement isn't checked on generic structs, so we have a way to work around -/// it (for now at least) -#[repr(simd)] -#[derive(Copy, Clone, Debug)] -struct Vec2(T, T); -unsafe impl VectorOrScalar for Vec2 { - type Scalar = T; - const DIM: NonZeroUsize = match NonZeroUsize::new(2) { - None => panic!(), - Some(n) => n, - }; -} -unsafe impl Vector for Vec2 {} - -impl Default for Vec2 { - fn default() -> Vec2 { - Vec2(T::default(), T::default()) - } -} - #[spirv(fragment)] pub fn main() { - let vector = Vec2(false, true); + let vector = glam::BVec2::new(false, true); assert!(spirv_std::arch::any(vector)); } diff --git a/tests/compiletests/ui/arch/any.stderr b/tests/compiletests/ui/arch/any.stderr deleted file mode 100644 index c01af8f3ba..0000000000 --- a/tests/compiletests/ui/arch/any.stderr +++ /dev/null @@ -1,12 +0,0 @@ -warning: [Rust-GPU] temporarily re-allowing old-style `#[repr(simd)]` (with fields) - --> $DIR/any.rs:16:1 - | -16 | struct Vec2(T, T); - | ^^^^^^^^^^^^^^ - | - = note: removed upstream by https://github.com/rust-lang/rust/pull/129403 - = note: in favor of the new `#[repr(simd)] struct TxN([T; N]);` style - = note: (taking effect since `nightly-2024-09-12` / `1.83.0` stable) - -warning: 1 warning emitted - diff --git a/tests/compiletests/ui/arch/debug_printf_type_checking.stderr b/tests/compiletests/ui/arch/debug_printf_type_checking.stderr index 5c1b487bde..6ec5956a55 100644 --- a/tests/compiletests/ui/arch/debug_printf_type_checking.stderr +++ b/tests/compiletests/ui/arch/debug_printf_type_checking.stderr @@ -121,15 +121,15 @@ error[E0277]: the trait bound `{float}: Vector` is not satisfied | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Vector` is not implemented for `{float}` | = help: the following other types implement trait `Vector`: + `BVec2` implements `Vector` + `BVec3` implements `Vector` + `BVec4` implements `Vector` `DVec2` implements `Vector` `DVec3` implements `Vector` `DVec4` implements `Vector` `IVec2` implements `Vector` `IVec3` implements `Vector` - `IVec4` implements `Vector` - `UVec2` implements `Vector` - `UVec3` implements `Vector` - and 5 others + and 8 others note: required by a bound in `debug_printf_assert_is_vector` --> $SPIRV_STD_SRC/lib.rs:141:8 | diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_ballot.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_ballot.stderr index ffc7660bb9..a0994f5353 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_ballot.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_ballot.stderr @@ -1,9 +1,9 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 367 13 +OpLine %6 364 13 %7 = OpGroupNonUniformBallot %8 %9 %4 -OpLine %6 406 13 +OpLine %6 403 13 %10 = OpGroupNonUniformInverseBallot %2 %9 %7 OpNoLine OpReturnValue %10 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_ballot_bit_count.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_ballot_bit_count.stderr index 0ecffc39c4..52b6bcf944 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_ballot_bit_count.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_ballot_bit_count.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %5 %6 = OpLabel -OpLine %7 501 0 +OpLine %7 498 0 %8 = OpGroupNonUniformBallotBitCount %2 %9 Reduce %4 OpNoLine OpReturnValue %8 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_broadcast_first.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_broadcast_first.stderr index 9bb737c779..44fa624f0e 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_broadcast_first.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_broadcast_first.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 334 13 +OpLine %6 331 13 %7 = OpGroupNonUniformBroadcastFirst %2 %8 %4 OpNoLine OpReturnValue %7 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_0_fail.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_0_fail.stderr index 821239dc33..fe2f180d78 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_0_fail.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_0_fail.stderr @@ -1,27 +1,27 @@ error[E0080]: evaluation panicked: `ClusterSize` must be at least 1 - --> $SPIRV_STD_SRC/arch/subgroup.rs:829:1 + --> $SPIRV_STD_SRC/arch/subgroup.rs:826:1 | -829 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" -830 | | An integer add group operation of all `value` operands contributed by active invocations in the group. -831 | | -832 | | Result Type must be a scalar or vector of integer type. +826 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" +827 | | An integer add group operation of all `value` operands contributed by active invocations in the group. +828 | | +829 | | Result Type must be a scalar or vector of integer type. ... | -845 | | * `ClusterSize` must not be greater than the size of the group -846 | | "); +842 | | * `ClusterSize` must not be greater than the size of the group +843 | | "); | |__^ evaluation of `spirv_std::arch::subgroup_clustered_i_add::<0, u32, u32>::{constant#0}` failed here | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `macro_subgroup_op_clustered` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $SPIRV_STD_SRC/arch/subgroup.rs:829:1 + --> $SPIRV_STD_SRC/arch/subgroup.rs:826:1 | -829 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" -830 | | An integer add group operation of all `value` operands contributed by active invocations in the group. -831 | | -832 | | Result Type must be a scalar or vector of integer type. +826 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" +827 | | An integer add group operation of all `value` operands contributed by active invocations in the group. +828 | | +829 | | Result Type must be a scalar or vector of integer type. ... | -845 | | * `ClusterSize` must not be greater than the size of the group -846 | | "); +842 | | * `ClusterSize` must not be greater than the size of the group +843 | | "); | |__^ | = note: this note originates in the macro `macro_subgroup_op_clustered` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_non_power_of_two_fail.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_non_power_of_two_fail.stderr index 8ef6ee7cb1..40c37e2740 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_non_power_of_two_fail.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_cluster_size_non_power_of_two_fail.stderr @@ -1,27 +1,27 @@ error[E0080]: evaluation panicked: `ClusterSize` must be a power of 2 - --> $SPIRV_STD_SRC/arch/subgroup.rs:829:1 + --> $SPIRV_STD_SRC/arch/subgroup.rs:826:1 | -829 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" -830 | | An integer add group operation of all `value` operands contributed by active invocations in the group. -831 | | -832 | | Result Type must be a scalar or vector of integer type. +826 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" +827 | | An integer add group operation of all `value` operands contributed by active invocations in the group. +828 | | +829 | | Result Type must be a scalar or vector of integer type. ... | -845 | | * `ClusterSize` must not be greater than the size of the group -846 | | "); +842 | | * `ClusterSize` must not be greater than the size of the group +843 | | "); | |__^ evaluation of `spirv_std::arch::subgroup_clustered_i_add::<5, u32, u32>::{constant#0}` failed here | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `macro_subgroup_op_clustered` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $SPIRV_STD_SRC/arch/subgroup.rs:829:1 + --> $SPIRV_STD_SRC/arch/subgroup.rs:826:1 | -829 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" -830 | | An integer add group operation of all `value` operands contributed by active invocations in the group. -831 | | -832 | | Result Type must be a scalar or vector of integer type. +826 | / macro_subgroup_op_clustered!(impl Integer, "OpGroupNonUniformIAdd", subgroup_clustered_i_add; r" +827 | | An integer add group operation of all `value` operands contributed by active invocations in the group. +828 | | +829 | | Result Type must be a scalar or vector of integer type. ... | -845 | | * `ClusterSize` must not be greater than the size of the group -846 | | "); +842 | | * `ClusterSize` must not be greater than the size of the group +843 | | "); | |__^ | = note: this note originates in the macro `macro_subgroup_op_clustered` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_elect.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_elect.stderr index d18849b6f6..26a079b145 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_elect.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_elect.stderr @@ -1,6 +1,6 @@ %1 = OpFunction %2 None %3 %4 = OpLabel -OpLine %5 164 13 +OpLine %5 161 13 %6 = OpGroupNonUniformElect %2 %7 OpNoLine OpReturnValue %6 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_clustered.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_clustered.stderr index 4d06f8f632..4c022abaf0 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_clustered.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_clustered.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 829 0 +OpLine %6 826 0 %7 = OpGroupNonUniformIAdd %2 %8 ClusteredReduce %4 %9 OpNoLine OpReturnValue %7 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_exclusive_scan.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_exclusive_scan.stderr index 4890551e39..a5ec1af956 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_exclusive_scan.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_exclusive_scan.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 816 0 +OpLine %6 813 0 %7 = OpGroupNonUniformIAdd %2 %8 ExclusiveScan %4 OpNoLine OpReturnValue %7 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_inclusive_scan.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_inclusive_scan.stderr index a41a806a40..b6fcceb39f 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_inclusive_scan.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_inclusive_scan.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 816 0 +OpLine %6 813 0 %7 = OpGroupNonUniformIAdd %2 %8 InclusiveScan %4 OpNoLine OpReturnValue %7 diff --git a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_reduce.stderr b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_reduce.stderr index 2e8d77e479..73fdd65b86 100644 --- a/tests/compiletests/ui/arch/subgroup/subgroup_i_add_reduce.stderr +++ b/tests/compiletests/ui/arch/subgroup/subgroup_i_add_reduce.stderr @@ -1,7 +1,7 @@ %1 = OpFunction %2 None %3 %4 = OpFunctionParameter %2 %5 = OpLabel -OpLine %6 816 0 +OpLine %6 813 0 %7 = OpGroupNonUniformIAdd %2 %8 Reduce %4 OpNoLine OpReturnValue %7 diff --git a/tests/compiletests/ui/dis/complex_image_sample_inst.stderr b/tests/compiletests/ui/dis/complex_image_sample_inst.stderr index cde98c1dec..414558e2c5 100644 --- a/tests/compiletests/ui/dis/complex_image_sample_inst.stderr +++ b/tests/compiletests/ui/dis/complex_image_sample_inst.stderr @@ -2,13 +2,17 @@ %4 = OpFunctionParameter %2 %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 +%8 = OpFunctionParameter %6 +%9 = OpFunctionParameter %6 +%10 = OpLabel +%11 = OpCompositeConstruct %12 %5 %7 +%13 = OpCompositeConstruct %12 %8 %9 +OpLine %14 29 13 +%15 = OpAccessChain %16 %17 %18 +OpLine %14 30 13 +%19 = OpLoad %20 %15 +OpLine %14 34 13 +%21 = OpImageSampleProjExplicitLod %2 %19 %4 Grad %11 %13 OpNoLine -OpReturnValue %16 +OpReturnValue %21 OpFunctionEnd diff --git a/tests/compiletests/ui/glam/invalid_vector_type.rs b/tests/compiletests/ui/glam/invalid_vector_type.rs new file mode 100644 index 0000000000..7c8ed9be5a --- /dev/null +++ b/tests/compiletests/ui/glam/invalid_vector_type.rs @@ -0,0 +1,47 @@ +// build-fail + +use core::num::NonZeroU32; +use spirv_std::glam::Vec2; +use spirv_std::spirv; + +#[rust_gpu::vector::v1] +pub struct FewerFields { + _v: f32, +} + +#[rust_gpu::vector::v1] +pub struct TooManyFields { + _x: f32, + _y: f32, + _z: f32, + _w: f32, + _v: f32, +} + +#[rust_gpu::vector::v1] +pub struct NotVectorField { + _x: Vec2, + _y: Vec2, +} + +#[rust_gpu::vector::v1] +pub struct NotVectorField2 { + _x: NonZeroU32, + _y: NonZeroU32, +} + +#[rust_gpu::vector::v1] +pub struct DifferentTypes { + _x: f32, + _y: u32, +} + +#[spirv(fragment)] +pub fn entry( + _: FewerFields, + _: TooManyFields, + _: NotVectorField, + #[spirv(flat)] _: NotVectorField2, + #[spirv(flat)] _: DifferentTypes, +) { +} diff --git a/tests/compiletests/ui/glam/invalid_vector_type.stderr b/tests/compiletests/ui/glam/invalid_vector_type.stderr new file mode 100644 index 0000000000..b0826e800c --- /dev/null +++ b/tests/compiletests/ui/glam/invalid_vector_type.stderr @@ -0,0 +1,28 @@ +error: `#[spirv(vector)]` must have 2, 3 or 4 members + --> $DIR/invalid_vector_type.rs:8:1 + | +8 | pub struct FewerFields { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[spirv(vector)]` must have 2, 3 or 4 members + --> $DIR/invalid_vector_type.rs:13:1 + | +13 | pub struct TooManyFields { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[spirv(vector)]` type fields must all be floats, integers or bools + --> $DIR/invalid_vector_type.rs:22:1 + | +22 | pub struct NotVectorField { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: field type is f32x2 + +error: `#[spirv(vector)]` member types must all be the same + --> $DIR/invalid_vector_type.rs:34:1 + | +34 | pub struct DifferentTypes { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/compiletests/ui/glam/offsets_vec3_vec3a.rs b/tests/compiletests/ui/glam/offsets_vec3_vec3a.rs new file mode 100644 index 0000000000..6d1e56cf84 --- /dev/null +++ b/tests/compiletests/ui/glam/offsets_vec3_vec3a.rs @@ -0,0 +1,57 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble +// normalize-stderr-test "OpSource .*\n" -> "" +// normalize-stderr-test "OpLine .*\n" -> "" +// normalize-stderr-test "%\d+ = OpString .*\n" -> "" +// normalize-stderr-test "; .*\n" -> "" +// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> "" +// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple" +// ignore-vulkan1.0 +// ignore-vulkan1.1 + +use spirv_std::arch::subgroup_shuffle_up; +use spirv_std::spirv; + +#[repr(C)] +#[derive(Copy, Clone, Default)] +#[rust_gpu::vector::v1] +pub struct Vec3 { + x: f32, + y: f32, + z: f32, +} + +#[repr(C, align(16))] +#[derive(Copy, Clone, Default)] +#[rust_gpu::vector::v1] +pub struct Vec3A { + x: f32, + y: f32, + z: f32, +} + +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Data { + t: T, + // this should generate two distinct structs where this member has different offsets + value: f32, +} + +impl Vec3 { + pub fn to_vec3a(&self) -> Vec3A { + Vec3A { + x: self.x, + y: self.y, + z: self.z, + } + } +} + +#[spirv(fragment)] +pub fn main(input: Data, output: &mut Data) { + *output = Data { + t: input.t.to_vec3a(), + value: input.value, + }; +} diff --git a/tests/compiletests/ui/glam/offsets_vec3_vec3a.stderr b/tests/compiletests/ui/glam/offsets_vec3_vec3a.stderr new file mode 100644 index 0000000000..e194cde0b1 --- /dev/null +++ b/tests/compiletests/ui/glam/offsets_vec3_vec3a.stderr @@ -0,0 +1,41 @@ +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 %3 "output" +OpMemberDecorate %5 0 Offset 0 +OpMemberDecorate %5 1 Offset 12 +OpMemberDecorate %6 0 Offset 0 +OpMemberDecorate %6 1 Offset 16 +OpDecorate %2 Location 0 +OpDecorate %3 Location 0 +%7 = OpTypeFloat 32 +%8 = OpTypeVector %7 3 +%5 = OpTypeStruct %8 %7 +%9 = OpTypePointer Input %5 +%6 = OpTypeStruct %8 %7 +%10 = OpTypePointer Output %6 +%11 = OpTypeVoid +%12 = OpTypeFunction %11 +%2 = OpVariable %9 Input +%3 = OpVariable %10 Output +%1 = OpFunction %11 None %12 +%13 = OpLabel +%14 = OpLoad %5 %2 +%15 = OpCompositeExtract %7 %14 0 0 +%16 = OpCompositeExtract %7 %14 0 1 +%17 = OpCompositeExtract %7 %14 0 2 +%18 = OpCompositeConstruct %8 %15 %16 %17 +%19 = OpCompositeExtract %7 %14 1 +%20 = OpCompositeConstruct %6 %18 %19 +OpStore %3 %20 +OpNoLine +OpReturn +OpFunctionEnd diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/compute_value_pair_fail.rs b/tests/compiletests/ui/lang/abi/scalar_pair/compute_value_pair_fail.rs new file mode 100644 index 0000000000..7c6d7eba57 --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/compute_value_pair_fail.rs @@ -0,0 +1,12 @@ +// compile-fail +#![no_std] + +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main( + w: (u32, u32), + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32], +) { + out[0] = w.0 + w.1; +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/compute_value_pair_fail.stderr b/tests/compiletests/ui/lang/abi/scalar_pair/compute_value_pair_fail.stderr new file mode 100644 index 0000000000..a6b234b15e --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/compute_value_pair_fail.stderr @@ -0,0 +1,10 @@ +error: compute entry parameter `w` must be by-reference + --> $DIR/compute_value_pair_fail.rs:8:8 + | +8 | w: (u32, u32), + | ^^^^^^^^^^ + | + = help: consider changing the type to `&(u32, u32)` + +error: aborting due to 1 previous error + diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/f32_u32.rs b/tests/compiletests/ui/lang/abi/scalar_pair/f32_u32.rs new file mode 100644 index 0000000000..35c9285324 --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/f32_u32.rs @@ -0,0 +1,12 @@ +// build-pass +#![no_std] + +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32], + #[spirv(uniform, descriptor_set = 0, binding = 1)] w: &(f32, u32), +) { + out[0] = w.0.to_bits() ^ w.1; +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/i32_i32.rs b/tests/compiletests/ui/lang/abi/scalar_pair/i32_i32.rs new file mode 100644 index 0000000000..f068febf71 --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/i32_i32.rs @@ -0,0 +1,15 @@ +// build-pass +// compile-flags: -C target-feature=+Int64 +#![no_std] + +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32], + #[spirv(uniform, descriptor_set = 0, binding = 1)] w: &(i32, i32), +) { + // Sum and reinterpret as u32 for output + let s = (w.0 as i64 + w.1 as i64) as i32; + out[0] = s as u32; +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/mixed.rs b/tests/compiletests/ui/lang/abi/scalar_pair/mixed.rs new file mode 100644 index 0000000000..685cdb9ffc --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/mixed.rs @@ -0,0 +1,14 @@ +// build-pass +#![no_std] + +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32], + #[spirv(uniform, descriptor_set = 0, binding = 1)] w: &(u32, f32), +) { + let a = w.0; + let b_bits = w.1.to_bits(); + out[0] = a ^ b_bits; +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/newtype.rs b/tests/compiletests/ui/lang/abi/scalar_pair/newtype.rs new file mode 100644 index 0000000000..4655d3acda --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/newtype.rs @@ -0,0 +1,17 @@ +// build-pass +#![no_std] + +use spirv_std::spirv; + +#[repr(transparent)] +pub struct Wrap((u32, u32)); + +#[spirv(compute(threads(1)))] +pub fn main( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32], + #[spirv(uniform, descriptor_set = 0, binding = 1)] w: &Wrap, +) { + let a = (w.0).0; + let b = (w.0).1; + out[0] = a + b; +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/newtype_fn.rs b/tests/compiletests/ui/lang/abi/scalar_pair/newtype_fn.rs new file mode 100644 index 0000000000..b5f5e63a0d --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/newtype_fn.rs @@ -0,0 +1,27 @@ +// build-pass +#![no_std] + +use spirv_std::spirv; + +#[repr(transparent)] +pub struct Inner((u32, u32)); + +#[repr(transparent)] +pub struct Outer( + core::mem::ManuallyDrop, + core::marker::PhantomData<()>, +); + +#[inline(never)] +fn sum_outer(o: Outer) -> u32 { + // SAFETY: repr(transparent) guarantees same layout as `Inner`. + let i: Inner = unsafe { core::mem::ManuallyDrop::into_inner((o.0)) }; + (i.0).0 + (i.0).1 +} + +#[spirv(compute(threads(1)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32]) { + let i = Inner((5, 7)); + let o = Outer(core::mem::ManuallyDrop::new(i), core::marker::PhantomData); + out[0] = sum_outer(o); +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/pair_input_fragment.rs b/tests/compiletests/ui/lang/abi/scalar_pair/pair_input_fragment.rs new file mode 100644 index 0000000000..29867b7f2d --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/pair_input_fragment.rs @@ -0,0 +1,12 @@ +// build-pass +#![no_std] + +use spirv_std::spirv; + +#[spirv(fragment)] +pub fn main( + #[spirv(flat)] pi: (u32, u32), + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] out: &mut [u32], +) { + out[0] = pi.0.wrapping_add(pi.1); +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/pair_input_vertex.rs b/tests/compiletests/ui/lang/abi/scalar_pair/pair_input_vertex.rs new file mode 100644 index 0000000000..daf3ecdb3e --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/pair_input_vertex.rs @@ -0,0 +1,12 @@ +// build-pass +#![no_std] + +use spirv_std::spirv; + +#[spirv(vertex)] +pub fn main( + p: (u32, u32), + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] out: &mut [u32], +) { + out[0] = p.0.wrapping_add(p.1); +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/tuple.rs b/tests/compiletests/ui/lang/abi/scalar_pair/tuple.rs new file mode 100644 index 0000000000..d989480b07 --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/tuple.rs @@ -0,0 +1,12 @@ +// build-pass +#![no_std] + +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32], + #[spirv(uniform, descriptor_set = 0, binding = 1)] w: &(u32, u32), +) { + out[0] = w.0 + w.1; +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/u32_u64.rs b/tests/compiletests/ui/lang/abi/scalar_pair/u32_u64.rs new file mode 100644 index 0000000000..f67ddc5bbf --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/u32_u64.rs @@ -0,0 +1,15 @@ +// build-pass +// compile-flags: -C target-feature=+Int64 +#![no_std] + +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32], + #[spirv(uniform, descriptor_set = 0, binding = 1)] w: &(u32, u64), +) { + let hi = (w.1 >> 32) as u32; + let lo = (w.1 & 0xFFFF_FFFF) as u32; + out[0] = w.0 ^ hi ^ lo; +} diff --git a/tests/compiletests/ui/lang/abi/scalar_pair/u64_u32.rs b/tests/compiletests/ui/lang/abi/scalar_pair/u64_u32.rs new file mode 100644 index 0000000000..69187d10bd --- /dev/null +++ b/tests/compiletests/ui/lang/abi/scalar_pair/u64_u32.rs @@ -0,0 +1,16 @@ +// build-pass +// compile-flags: -C target-feature=+Int64 +#![no_std] + +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32], + #[spirv(uniform, descriptor_set = 0, binding = 1)] w: &(u64, u32), +) { + // Fold 64-bit into 32-bit deterministically + let hi = (w.0 >> 32) as u32; + let lo = (w.0 & 0xFFFF_FFFF) as u32; + out[0] = hi ^ lo ^ w.1; +} diff --git a/tests/compiletests/ui/lang/core/intrinsics/black_box.rs b/tests/compiletests/ui/lang/core/intrinsics/black_box.rs index 4a6c85a76b..676f9177de 100644 --- a/tests/compiletests/ui/lang/core/intrinsics/black_box.rs +++ b/tests/compiletests/ui/lang/core/intrinsics/black_box.rs @@ -5,7 +5,6 @@ use core::hint::black_box; use spirv_std::spirv; -// Minimal kernel that writes the disassembly function result to a buffer #[spirv(compute(threads(1)))] pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32]) { let r = disassemble(); diff --git a/tests/compiletests/ui/lang/core/intrinsics/black_box.stderr b/tests/compiletests/ui/lang/core/intrinsics/black_box.stderr index 192a37e3a3..56700b0e32 100644 --- a/tests/compiletests/ui/lang/core/intrinsics/black_box.stderr +++ b/tests/compiletests/ui/lang/core/intrinsics/black_box.stderr @@ -2,15 +2,15 @@ warning: black_box intrinsic does not prevent optimization in Rust GPU %1 = OpFunction %2 DontInline %3 %4 = OpLabel -OpLine %5 32 17 +OpLine %5 31 17 %6 = OpIAdd %7 %8 %9 -OpLine %5 41 19 +OpLine %5 40 19 %10 = OpIAdd %7 %11 %12 -OpLine %5 47 8 +OpLine %5 46 8 %13 = OpBitcast %7 %14 OpLine %15 1092 17 %16 = OpBitcast %7 %17 -OpLine %5 46 4 +OpLine %5 45 4 %18 = OpCompositeConstruct %2 %13 %16 %19 %20 %21 %22 %6 %23 %10 %24 %24 %24 OpNoLine OpReturnValue %18 diff --git a/tests/compiletests/ui/spirv-attr/invalid-matrix-type-empty.stderr b/tests/compiletests/ui/spirv-attr/invalid-matrix-type-empty.stderr index d34ef0ac5e..45b1139cc0 100644 --- a/tests/compiletests/ui/spirv-attr/invalid-matrix-type-empty.stderr +++ b/tests/compiletests/ui/spirv-attr/invalid-matrix-type-empty.stderr @@ -1,4 +1,4 @@ -error: #[spirv(matrix)] type must have at least two fields +error: `#[spirv(matrix)]` member types must all be the same --> $DIR/invalid-matrix-type-empty.rs:7:1 | 7 | pub struct EmptyStruct {} diff --git a/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr b/tests/compiletests/ui/spirv-attr/invalid-matrix-type.stderr index eda87f4c6e..a14a76b6d6 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 2, 3 or 4 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 { diff --git a/tests/difftests/tests/Cargo.lock b/tests/difftests/tests/Cargo.lock index 74d87cce66..b172ec4aa4 100644 --- a/tests/difftests/tests/Cargo.lock +++ b/tests/difftests/tests/Cargo.lock @@ -2,6 +2,56 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "abi-vector-layout-cpu" +version = "0.0.0" +dependencies = [ + "bytemuck", + "difftest", + "glam", + "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" +dependencies = [ + "abi-vector-layout-cpu", + "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" @@ -593,9 +643,8 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d1aab06663bdce00d6ca5e5ed586ec8d18033a771906c993a1e3755b368d85" +version = "0.30.7" +source = "git+https://github.com/bitshifter/glam-rs?rev=ae9627fca68d6b5b24e80711f8c40bb92d7bdc82#ae9627fca68d6b5b24e80711f8c40bb92d7bdc82" dependencies = [ "libm", ] @@ -1332,6 +1381,23 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "scalar_pair-cpu" +version = "0.0.0" +dependencies = [ + "bytemuck", + "difftest", +] + +[[package]] +name = "scalar_pair-gpu" +version = "0.0.0" +dependencies = [ + "bytemuck", + "difftest", + "spirv-std", +] + [[package]] name = "scopeguard" version = "1.2.0" diff --git a/tests/difftests/tests/Cargo.toml b/tests/difftests/tests/Cargo.toml index 7c74495325..91a931a5e4 100644 --- a/tests/difftests/tests/Cargo.toml +++ b/tests/difftests/tests/Cargo.toml @@ -16,6 +16,12 @@ 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/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", @@ -40,6 +46,8 @@ members = [ "lang/core/ops/vector_swizzle/vector_swizzle-wgsl", "lang/core/intrinsics/black_box_noop/with-black-box", "lang/core/intrinsics/black_box_noop/without-black-box", + "lang/abi/scalar_pair/scalar_pair-cpu", + "lang/abi/scalar_pair/scalar_pair-gpu", ] [workspace.package] @@ -57,7 +65,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.22, <=0.30", git = "https://github.com/bitshifter/glam-rs", rev = "ae9627fca68d6b5b24e80711f8c40bb92d7bdc82", default-features = false } bytemuck = { version = "1.14", features = ["derive"] } # Enable incremental by default in release mode. diff --git a/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-cpu/Cargo.toml b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-cpu/Cargo.toml new file mode 100644 index 0000000000..d334ebc35c --- /dev/null +++ b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-cpu/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "scalar_pair-cpu" +edition.workspace = true + +[lints] +workspace = true + +[dependencies] +difftest.workspace = true +bytemuck.workspace = true diff --git a/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-cpu/src/main.rs b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-cpu/src/main.rs new file mode 100644 index 0000000000..dcd1aeb12e --- /dev/null +++ b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-cpu/src/main.rs @@ -0,0 +1,72 @@ +use difftest::config::{Config, TestMetadata}; +use std::{fs, io::Write}; + +fn main() { + let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); + + let a: (u32, u32) = (10, 20); + let b: (u32, f32) = (7, 3.5); + let c: (f32, u32) = (2.25, 9); + let d: (u32, u32) = (0xDEADBEEF, 0x12345678); + let e: (i32, i32) = (-5, 12); + + let sum_a = a.0 + a.1; + let mix_b = b.0 ^ b.1.to_bits(); + let mix_c = c.1 ^ c.0.to_bits(); + let d_and = d.0 & d.1; + let d_or = d.0 | d.1; + let d_xor = d.0 ^ d.1; + let d_sum = d.0.wrapping_add(d.1); + let e_sum_u32 = e.0.wrapping_add(e.1) as u32; + + let w_sum = 3u32 + 4u32; + let reorder_sum = 1u32 + 2u32; + + let mut out = [0u32; 21]; + out[0] = a.0; + out[1] = a.1; + out[2] = sum_a; + out[3] = mix_b; + out[4] = mix_c; + out[5] = d_and; + out[6] = d_or; + out[7] = d_xor; + out[8] = d_sum; + out[9] = e_sum_u32; + out[10] = w_sum; + out[11] = reorder_sum; + + let p: (i32, u32) = (-123, 456); + let p_mix = (p.0 as u32).wrapping_add(p.1); + let q: (f32, f32) = (0.75, -1.5); + let q_mix = q.0.to_bits() ^ q.1.to_bits(); + let r_mix = 8u32 ^ 16u32 ^ 0.5f32.to_bits() ^ (-2.0f32).to_bits(); + out[12] = p_mix; + out[13] = q_mix; + out[14] = r_mix; + + let tt_a_sum = 1u32 + 2 + 3 + 4; + let tt_b_mix = 5u32 ^ 1.5f32.to_bits() ^ 2.25f32.to_bits() ^ 9u32; + out[15] = tt_a_sum; + out[16] = tt_b_mix; + + let call_sum = 11u32.wrapping_add(22u32); + let call_mix = 13u32 ^ (-0.75f32).to_bits(); + out[17] = call_sum; + out[18] = call_mix; + + // Push constants equivalent values (must match GPU push data) + let pc0: u32 = 100; + let pc1: u32 = 200; + let pc_sum = pc0.wrapping_add(pc1); + let pc_xor = pc0 ^ pc1; + out[19] = pc_sum; + out[20] = pc_xor; + + let mut file = fs::File::create(&config.output_path).expect("Failed to create output file"); + file.write_all(bytemuck::cast_slice(&out)) + .expect("Failed to write output"); + config + .write_metadata(&TestMetadata::u32()) + .expect("Failed to write metadata"); +} diff --git a/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-gpu/Cargo.toml b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-gpu/Cargo.toml new file mode 100644 index 0000000000..2b8f3539a6 --- /dev/null +++ b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-gpu/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "scalar_pair-gpu" +edition.workspace = true + +[lints] +workspace = true + +[lib] +crate-type = ["dylib"] + +# GPU deps +[dependencies] +spirv-std.workspace = true + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true +bytemuck.workspace = true diff --git a/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-gpu/src/lib.rs b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-gpu/src/lib.rs new file mode 100644 index 0000000000..90bdb394ef --- /dev/null +++ b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-gpu/src/lib.rs @@ -0,0 +1,81 @@ +#![no_std] + +use spirv_std::spirv; + +#[inline(never)] +fn sum_pair_u32(p: (u32, u32)) -> u32 { + p.0.wrapping_add(p.1) +} + +#[inline(never)] +fn mix_pair_u32_f32(p: (u32, f32)) -> u32 { + p.0 ^ p.1.to_bits() +} + +#[spirv(compute(threads(1)))] +pub fn main_cs( + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] out: &mut [u32], + #[spirv(push_constant)] pc: &(u32, u32), +) { + let a: (u32, u32) = (10, 20); + let b: (u32, f32) = (7, 3.5); + let c: (f32, u32) = (2.25, 9); + let d: (u32, u32) = (0xDEADBEEF, 0x12345678); + let e: (i32, i32) = (-5, 12); + + let sum_a = a.0 + a.1; + let mix_b = b.0 ^ b.1.to_bits(); + let mix_c = c.1 ^ c.0.to_bits(); + let d_and = d.0 & d.1; + let d_or = d.0 | d.1; + let d_xor = d.0 ^ d.1; + let d_sum = d.0.wrapping_add(d.1); + let e_sum_u32 = e.0.wrapping_add(e.1) as u32; + + #[repr(transparent)] + struct Wrap((u32, u32)); + let w = Wrap((3, 4)); + let w_sum = (w.0).0 + (w.0).1; + + let reorder_sum = (1u32, 2u32).1 + (1u32, 2u32).0; + + out[0] = a.0; + out[1] = a.1; + out[2] = sum_a; + out[3] = mix_b; + out[4] = mix_c; + out[5] = d_and; + out[6] = d_or; + out[7] = d_xor; + out[8] = d_sum; + out[9] = e_sum_u32; + out[10] = w_sum; + out[11] = reorder_sum; + + let p: (i32, u32) = (-123, 456); + let p_mix = (p.0 as u32).wrapping_add(p.1); + let q: (f32, f32) = (0.75, -1.5); + let q_mix = q.0.to_bits() ^ q.1.to_bits(); + let r: ((u32, f32), (u32, f32)) = ((8, 0.5), (16, -2.0)); + let r_mix = (r.0).0 ^ (r.1).0 ^ (r.0).1.to_bits() ^ (r.1).1.to_bits(); + out[12] = p_mix; + out[13] = q_mix; + out[14] = r_mix; + + let tt_a: ((u32, u32), (u32, u32)) = ((1, 2), (3, 4)); + let tt_a_sum = tt_a.0.0 + tt_a.0.1 + tt_a.1.0 + tt_a.1.1; + let tt_b: ((u32, f32), (f32, u32)) = ((5, 1.5), (2.25, 9)); + let tt_b_mix = tt_b.0.0 ^ tt_b.0.1.to_bits() ^ tt_b.1.0.to_bits() ^ tt_b.1.1; + out[15] = tt_a_sum; + out[16] = tt_b_mix; + + let call_sum = sum_pair_u32((11, 22)); + let call_mix = mix_pair_u32_f32((13, -0.75)); + out[17] = call_sum; + out[18] = call_mix; + + let pc_sum = pc.0.wrapping_add(pc.1); + let pc_xor = pc.0 ^ pc.1; + out[19] = pc_sum; + out[20] = pc_xor; +} diff --git a/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-gpu/src/main.rs b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-gpu/src/main.rs new file mode 100644 index 0000000000..b931ffa32e --- /dev/null +++ b/tests/difftests/tests/lang/abi/scalar_pair/scalar_pair-gpu/src/main.rs @@ -0,0 +1,33 @@ +use difftest::config::{Config, TestMetadata}; +use difftest::scaffold::compute::{ + BufferConfig, BufferUsage, RustComputeShader, WgpuComputeTestPushConstants, +}; + +fn main() { + let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); + // One storage buffer (output), plus push constants data (pair of u32) + let sizes = [21 * 4u64]; + let push_data: Vec = { + let a: u32 = 100; + let b: u32 = 200; + let mut v = Vec::with_capacity(8); + v.extend_from_slice(&a.to_le_bytes()); + v.extend_from_slice(&b.to_le_bytes()); + v + }; + let test = WgpuComputeTestPushConstants::new( + RustComputeShader::default(), + [1, 1, 1], + vec![BufferConfig { + size: sizes[0], + usage: BufferUsage::Storage, + initial_data: None, + }], + 8, + push_data, + ); + test.run_test(&config).unwrap(); + config + .write_metadata(&TestMetadata::u32()) + .expect("Failed to write metadata"); +} 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..007ef92b04 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "abi-vector-layout-cpu" +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) +[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..6ed4a0796d --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/cpu_driver.rs @@ -0,0 +1,13 @@ +use crate::glam_features::GlamFeatures; +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 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/glam_features.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/glam_features.rs new file mode 100644 index 0000000000..7259525b71 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/glam_features.rs @@ -0,0 +1,35 @@ +#[repr(u32)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum GlamFeatures { + Default, + Cuda, + ScalarMath, +} + +impl GlamFeatures { + pub fn is(&self) -> bool { + match self { + // cuda: 16 (!) + // cuda + scalar-math: 8 + // scalar-math: (native) 4 + GlamFeatures::Default => align_of::() == 16 && !GlamFeatures::Cuda.is(), + // default, scalar-math: (native) 4 + GlamFeatures::Cuda => align_of::() == 8, + // default, cuda: 16 + GlamFeatures::ScalarMath => align_of::() == 4, + } + } + + pub fn assert(&self) { + for feature in [Self::Default, Self::Cuda, Self::ScalarMath] { + if *self == feature { + assert!(feature.is(), "Expected feature {feature:?} to be enabled"); + } else { + assert!( + !feature.is(), + "Feature {feature:?} mistakenly enabled! Too aggressive feature unification?" + ); + } + } + } +} diff --git a/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs new file mode 100644 index 0000000000..c9b660f63b --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/layout.rs @@ -0,0 +1,165 @@ +use core::mem::offset_of; +use core::ops::Range; +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 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_RANGE.end * 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 + 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()), + + // IMPORTANT: when adding new rows, remember to adjust `LAYOUT_COUNT` to be the maximum + 1! + _ => {} + } +} + +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, + } + + #[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, + } +} 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..08e80c346f --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/lib.rs @@ -0,0 +1,9 @@ +#![cfg_attr(target_arch = "spirv", no_std)] + +#[cfg(not(target_arch = "spirv"))] +pub mod cpu_driver; +pub mod glam_features; +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..bcd5f7c800 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/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::Default); +} 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..e6d10ffa3d --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/cpu/src/shader_driver.rs @@ -0,0 +1,16 @@ +use crate::glam_features::GlamFeatures; +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_RANGE.end 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..5ab8e426b5 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout/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::Default); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/Cargo.toml new file mode 100644 index 0000000000..4a3223d9db --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "abi-vector-layout-cuda-cpu" +edition.workspace = true + +[lints] +workspace = true + +# GPU deps +[dependencies] +abi-vector-layout-cpu = { path = "../../vector_layout/cpu", features = ["cuda"] } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/src/main.rs new file mode 100644 index 0000000000..84861065a3 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/cpu/src/main.rs @@ -0,0 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + +fn main() { + abi_vector_layout_cpu::cpu_driver::run(GlamFeatures::Cuda); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/Cargo.toml new file mode 100644 index 0000000000..2ae5c7a13f --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "abi-vector-layout-cuda-rust-gpu" +edition.workspace = true + +[lib] +crate-type = ["lib", "dylib"] + +[lints] +workspace = true + +[dependencies] +abi-vector-layout-cpu = { path = "../../vector_layout/cpu", features = ["cuda"] } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/lib.rs b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/lib.rs new file mode 100644 index 0000000000..219229aa2e --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/lib.rs @@ -0,0 +1,3 @@ +#![cfg_attr(target_arch = "spirv", no_std)] + +pub use abi_vector_layout_cpu::shader::main_cs; diff --git a/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/main.rs new file mode 100644 index 0000000000..647d8c3385 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_cuda/rust-gpu/src/main.rs @@ -0,0 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + +fn main() { + abi_vector_layout_cpu::shader_driver::run(GlamFeatures::Cuda); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/Cargo.toml new file mode 100644 index 0000000000..fa834aa419 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "abi-vector-layout-scalar-math-cpu" +edition.workspace = true + +[lints] +workspace = true + +# GPU deps +[dependencies] +abi-vector-layout-cpu = { path = "../../vector_layout/cpu", features = ["scalar-math"] } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/src/main.rs new file mode 100644 index 0000000000..044b1b025f --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/cpu/src/main.rs @@ -0,0 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + +fn main() { + abi_vector_layout_cpu::cpu_driver::run(GlamFeatures::ScalarMath); +} diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/Cargo.toml b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/Cargo.toml new file mode 100644 index 0000000000..225553fd24 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "abi-vector-layout-scalar-math-rust-gpu" +edition.workspace = true + +[lib] +crate-type = ["lib", "dylib"] + +[lints] +workspace = true + +[dependencies] +abi-vector-layout-cpu = { path = "../../vector_layout/cpu", features = ["scalar-math"] } + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/lib.rs b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/lib.rs new file mode 100644 index 0000000000..219229aa2e --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/lib.rs @@ -0,0 +1,3 @@ +#![cfg_attr(target_arch = "spirv", no_std)] + +pub use abi_vector_layout_cpu::shader::main_cs; diff --git a/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/main.rs b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/main.rs new file mode 100644 index 0000000000..c0265cc7e3 --- /dev/null +++ b/tests/difftests/tests/lang/abi/vector_layout_scalar_math/rust-gpu/src/main.rs @@ -0,0 +1,5 @@ +use abi_vector_layout_cpu::glam_features::GlamFeatures; + +fn main() { + abi_vector_layout_cpu::shader_driver::run(GlamFeatures::ScalarMath); +}