From 644c163ff1981208a97512598c319f40093fd327 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 12 Oct 2021 13:41:49 +0300 Subject: [PATCH 1/7] Move query hooks out of the crate root (and into `abi`). --- crates/rustc_codegen_spirv/src/abi.rs | 38 +++++++++++++++++++++++++++ crates/rustc_codegen_spirv/src/lib.rs | 35 ++---------------------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 07076f329e..15e0b984ce 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -10,6 +10,7 @@ use rustc_errors::ErrorReported; use rustc_index::vec::Idx; use rustc_middle::bug; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; +use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{ self, Const, FloatTy, GeneratorSubsts, IntTy, ParamEnv, PolyFnSig, Ty, TyKind, TypeAndMut, @@ -20,12 +21,49 @@ use rustc_span::Span; use rustc_span::DUMMY_SP; use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind}; use rustc_target::abi::{Abi, Align, FieldsShape, Primitive, Scalar, Size, VariantIdx, Variants}; +use rustc_target::spec::abi::Abi as SpecAbi; use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fmt; use num_traits::cast::FromPrimitive; +pub(crate) fn provide(providers: &mut Providers) { + // This is a lil weird: so, we obviously don't support C ABIs at all. However, libcore does declare some extern + // C functions: + // https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/library/core/src/slice/cmp.rs#L119 + // However, those functions will be implemented by compiler-builtins: + // https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/library/core/src/lib.rs#L23-L27 + // This theoretically then should be fine to leave as C, but, there's no backend hook for + // FnAbi::adjust_for_cabi, causing it to panic: + // https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/compiler/rustc_target/src/abi/call/mod.rs#L603 + // So, treat any extern "C" functions as actually being Rust ABI, to be able to compile libcore with arch=spirv. + providers.fn_sig = |tcx, def_id| { + // We can't capture the old fn_sig and just call that, because fn_sig is a `fn`, not a `Fn`, i.e. it can't + // capture variables. Fortunately, the defaults are exposed (thanks rustdoc), so use that instead. + let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_sig)(tcx, def_id); + result.map_bound(|mut inner| { + if let SpecAbi::C { .. } = inner.abi { + inner.abi = SpecAbi::Rust; + } + inner + }) + }; +} + +pub(crate) fn provide_extern(providers: &mut Providers) { + // See comments in provide(), only this time we use the default *extern* provider. + providers.fn_sig = |tcx, def_id| { + let result = (rustc_interface::DEFAULT_EXTERN_QUERY_PROVIDERS.fn_sig)(tcx, def_id); + result.map_bound(|mut inner| { + if let SpecAbi::C { .. } = inner.abi { + inner.abi = SpecAbi::Rust; + } + inner + }) + }; +} + /// If a struct contains a pointer to itself, even indirectly, then doing a naiive recursive walk /// of the fields will result in an infinite loop. Because pointers are the only thing that are /// allowed to be recursive, keep track of what pointers we've translated, or are currently in the diff --git a/crates/rustc_codegen_spirv/src/lib.rs b/crates/rustc_codegen_spirv/src/lib.rs index b196051dbc..d8a567d312 100644 --- a/crates/rustc_codegen_spirv/src/lib.rs +++ b/crates/rustc_codegen_spirv/src/lib.rs @@ -176,7 +176,6 @@ use rustc_middle::ty::{self, query, DefIdTree, Instance, InstanceDef, TyCtxt}; use rustc_session::config::{self, OptLevel, OutputFilenames, OutputType}; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; -use rustc_target::spec::abi::Abi; use rustc_target::spec::{Target, TargetTriple}; use std::any::Any; use std::env; @@ -286,42 +285,12 @@ impl CodegenBackend for SpirvCodegenBackend { } fn provide(&self, providers: &mut query::Providers) { - // This is a lil weird: so, we obviously don't support C ABIs at all. However, libcore does declare some extern - // C functions: - // https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/library/core/src/slice/cmp.rs#L119 - // However, those functions will be implemented by compiler-builtins: - // https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/library/core/src/lib.rs#L23-L27 - // This theoretically then should be fine to leave as C, but, there's no backend hook for - // FnAbi::adjust_for_cabi, causing it to panic: - // https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/compiler/rustc_target/src/abi/call/mod.rs#L603 - // So, treat any extern "C" functions as actually being Rust ABI, to be able to compile libcore with arch=spirv. - providers.fn_sig = |tcx, def_id| { - // We can't capture the old fn_sig and just call that, because fn_sig is a `fn`, not a `Fn`, i.e. it can't - // capture variables. Fortunately, the defaults are exposed (thanks rustdoc), so use that instead. - let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_sig)(tcx, def_id); - result.map_bound(|mut inner| { - if let Abi::C { .. } = inner.abi { - inner.abi = Abi::Rust; - } - inner - }) - }; - - // Extra hooks provided by other parts of `rustc_codegen_spirv`. + crate::abi::provide(providers); crate::attr::provide(providers); } fn provide_extern(&self, providers: &mut query::Providers) { - // See comments in provide(), only this time we use the default *extern* provider. - providers.fn_sig = |tcx, def_id| { - let result = (rustc_interface::DEFAULT_EXTERN_QUERY_PROVIDERS.fn_sig)(tcx, def_id); - result.map_bound(|mut inner| { - if let Abi::C { .. } = inner.abi { - inner.abi = Abi::Rust; - } - inner - }) - }; + crate::abi::provide_extern(providers); } fn codegen_crate( From 97bd328fd393aa6c319283ac49944ab3399d9e28 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 12 Oct 2021 13:48:26 +0300 Subject: [PATCH 2/7] abi: remove redundant `fn_sig` hook from `provide_extern`. --- crates/rustc_codegen_spirv/src/abi.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 15e0b984ce..61e4f73c73 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -52,16 +52,9 @@ pub(crate) fn provide(providers: &mut Providers) { } pub(crate) fn provide_extern(providers: &mut Providers) { - // See comments in provide(), only this time we use the default *extern* provider. - providers.fn_sig = |tcx, def_id| { - let result = (rustc_interface::DEFAULT_EXTERN_QUERY_PROVIDERS.fn_sig)(tcx, def_id); - result.map_bound(|mut inner| { - if let SpecAbi::C { .. } = inner.abi { - inner.abi = SpecAbi::Rust; - } - inner - }) - }; + // Reset providers overriden in `provide`, that need to still go through the + // `rustc_metadata::rmeta` decoding, as opposed to being locally computed. + providers.fn_sig = rustc_interface::DEFAULT_EXTERN_QUERY_PROVIDERS.fn_sig; } /// If a struct contains a pointer to itself, even indirectly, then doing a naiive recursive walk From 5acd7e5d05994f64cb8a53d63cdf90d9a100f4f1 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 12 Oct 2021 13:52:01 +0300 Subject: [PATCH 3/7] abi: replace `extern "C"` with `extern "unadjusted"`, not Rust ABI. --- crates/rustc_codegen_spirv/src/abi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 61e4f73c73..7296b2acca 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -37,14 +37,14 @@ pub(crate) fn provide(providers: &mut Providers) { // This theoretically then should be fine to leave as C, but, there's no backend hook for // FnAbi::adjust_for_cabi, causing it to panic: // https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/compiler/rustc_target/src/abi/call/mod.rs#L603 - // So, treat any extern "C" functions as actually being Rust ABI, to be able to compile libcore with arch=spirv. + // So, treat any `extern "C"` functions as `extern "unadjusted"`, to be able to compile libcore with arch=spirv. providers.fn_sig = |tcx, def_id| { // We can't capture the old fn_sig and just call that, because fn_sig is a `fn`, not a `Fn`, i.e. it can't // capture variables. Fortunately, the defaults are exposed (thanks rustdoc), so use that instead. let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_sig)(tcx, def_id); result.map_bound(|mut inner| { if let SpecAbi::C { .. } = inner.abi { - inner.abi = SpecAbi::Rust; + inner.abi = SpecAbi::Unadjusted; } inner }) From 42b3b050eae2926188c51ccd59d26f52546cdcd5 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 12 Oct 2021 14:21:23 +0300 Subject: [PATCH 4/7] abi: readjust `FnAbi`s to remove unsupported `PassMode`s, via query hooks. --- crates/rustc_codegen_spirv/src/abi.rs | 48 +++++++++++++++++-- tests/ui/dis/issue-723-indirect-input.stderr | 5 +- .../consts/nested-ref-in-composite.stderr | 4 +- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 7296b2acca..833a584e05 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -13,13 +13,13 @@ use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{ - self, Const, FloatTy, GeneratorSubsts, IntTy, ParamEnv, PolyFnSig, Ty, TyKind, TypeAndMut, - UintTy, + self, Const, FloatTy, GeneratorSubsts, IntTy, ParamEnv, PolyFnSig, Ty, TyCtxt, TyKind, + TypeAndMut, UintTy, }; use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_span::DUMMY_SP; -use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind}; +use rustc_target::abi::call::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode, Reg, RegKind}; use rustc_target::abi::{Abi, Align, FieldsShape, Primitive, Scalar, Size, VariantIdx, Variants}; use rustc_target::spec::abi::Abi as SpecAbi; use std::cell::RefCell; @@ -35,7 +35,7 @@ pub(crate) fn provide(providers: &mut Providers) { // However, those functions will be implemented by compiler-builtins: // https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/library/core/src/lib.rs#L23-L27 // This theoretically then should be fine to leave as C, but, there's no backend hook for - // FnAbi::adjust_for_cabi, causing it to panic: + // `FnAbi::adjust_for_cabi`, causing it to panic: // https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/compiler/rustc_target/src/abi/call/mod.rs#L603 // So, treat any `extern "C"` functions as `extern "unadjusted"`, to be able to compile libcore with arch=spirv. providers.fn_sig = |tcx, def_id| { @@ -49,6 +49,46 @@ pub(crate) fn provide(providers: &mut Providers) { inner }) }; + + // For the Rust ABI, `FnAbi` adjustments are backend-agnostic, but they will + // use features like `PassMode::Cast`, that are incompatible with SPIR-V. + // By hooking the queries computing `FnAbi`s, we can recompute the `FnAbi` + // from the return/args layouts, to e.g. prefer using `PassMode::Direct`. + fn readjust_fn_abi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, + ) -> &'tcx FnAbi<'tcx, Ty<'tcx>> { + let readjust_arg_abi = |arg: &ArgAbi<'tcx, Ty<'tcx>>| { + let mut arg = ArgAbi::new(&tcx, arg.layout, |_, _, _| ArgAttributes::new()); + + // Avoid pointlessly passing ZSTs, just like the official Rust ABI. + if arg.layout.is_zst() { + arg.mode = PassMode::Ignore; + } + + arg + }; + tcx.arena.alloc(FnAbi { + args: fn_abi.args.iter().map(readjust_arg_abi).collect(), + ret: readjust_arg_abi(&fn_abi.ret), + + // FIXME(eddyb) validate some of these, and report errors - however, + // we can't just emit errors from here, since we have no `Span`, so + // we should have instead a check on MIR for e.g. C variadic calls. + c_variadic: fn_abi.c_variadic, + fixed_count: fn_abi.fixed_count, + conv: fn_abi.conv, + can_unwind: fn_abi.can_unwind, + }) + } + providers.fn_abi_of_fn_ptr = |tcx, key| { + let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_abi_of_fn_ptr)(tcx, key); + Ok(readjust_fn_abi(tcx, result?)) + }; + providers.fn_abi_of_instance = |tcx, key| { + let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_abi_of_instance)(tcx, key); + Ok(readjust_fn_abi(tcx, result?)) + }; } pub(crate) fn provide_extern(providers: &mut Providers) { diff --git a/tests/ui/dis/issue-723-indirect-input.stderr b/tests/ui/dis/issue-723-indirect-input.stderr index d9be9f92bf..2b511a952b 100644 --- a/tests/ui/dis/issue-723-indirect-input.stderr +++ b/tests/ui/dis/issue-723-indirect-input.stderr @@ -17,6 +17,7 @@ OpDecorate %2 Location 0 %8 = OpTypeInt 32 0 %9 = OpConstant %8 3 %5 = OpTypeArray %7 %9 -%10 = OpTypePointer Input %5 +%10 = OpTypeFunction %6 %5 %11 = OpTypeFunction %6 -%2 = OpVariable %10 Input +%12 = OpTypePointer Input %5 +%2 = OpVariable %12 Input diff --git a/tests/ui/lang/consts/nested-ref-in-composite.stderr b/tests/ui/lang/consts/nested-ref-in-composite.stderr index 290574b2fa..4194fdffad 100644 --- a/tests/ui/lang/consts/nested-ref-in-composite.stderr +++ b/tests/ui/lang/consts/nested-ref-in-composite.stderr @@ -6,7 +6,7 @@ error: constant arrays/structs cannot contain pointers to other constants | = note: Stack: nested_ref_in_composite::main_pair - Unnamed function ID %26 + Unnamed function ID %24 error: constant arrays/structs cannot contain pointers to other constants --> $DIR/nested-ref-in-composite.rs:27:19 @@ -16,7 +16,7 @@ error: constant arrays/structs cannot contain pointers to other constants | = note: Stack: nested_ref_in_composite::main_array3 - Unnamed function ID %34 + Unnamed function ID %33 error: error:0:0 - No OpEntryPoint instruction was found. This is only allowed if the Linkage capability is being used. | From d472d27d356b1b1949455a921dacf9844a591c37 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 12 Oct 2021 14:36:25 +0300 Subject: [PATCH 5/7] abi: use `span_bug!` over `bug!` wherever a `Span` is available. --- crates/rustc_codegen_spirv/src/abi.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 833a584e05..a473c40dd5 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -8,7 +8,6 @@ use rspirv::spirv::{StorageClass, Word}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorReported; use rustc_index::vec::Idx; -use rustc_middle::bug; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::SubstsRef; @@ -16,6 +15,7 @@ use rustc_middle::ty::{ self, Const, FloatTy, GeneratorSubsts, IntTy, ParamEnv, PolyFnSig, Ty, TyCtxt, TyKind, TypeAndMut, UintTy, }; +use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_span::DUMMY_SP; @@ -152,7 +152,9 @@ impl<'tcx> RecursivePointeeCache<'tcx> { ) -> Word { match self.map.borrow_mut().entry(pointee) { // We should have hit begin() on this type already, which always inserts an entry. - Entry::Vacant(_) => bug!("RecursivePointeeCache::end should always have entry"), + Entry::Vacant(_) => { + span_bug!(span, "RecursivePointeeCache::end should always have entry") + } Entry::Occupied(mut entry) => match *entry.get() { // State: There have been no recursive references to this type while defining it, and so no // OpTypeForwardPointer has been emitted. This is the most common case. @@ -174,7 +176,7 @@ impl<'tcx> RecursivePointeeCache<'tcx> { .def_with_id(cx, span, id) } PointeeDefState::Defined(_) => { - bug!("RecursivePointeeCache::end defined pointer twice") + span_bug!(span, "RecursivePointeeCache::end defined pointer twice") } }, } @@ -466,7 +468,11 @@ pub fn scalar_pair_element_backend_type<'tcx>( ) -> Word { let [a, b] = match &ty.layout.abi { Abi::ScalarPair(a, b) => [a, b], - other => bug!("scalar_pair_element_backend_type invalid abi: {:?}", other), + other => span_bug!( + span, + "scalar_pair_element_backend_type invalid abi: {:?}", + other + ), }; let offset = match index { 0 => Size::ZERO, @@ -595,7 +601,8 @@ fn dig_scalar_pointee<'tcx>( fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word { match ty.fields { - FieldsShape::Primitive => bug!( + FieldsShape::Primitive => span_bug!( + span, "trans_aggregate called for FieldsShape::Primitive layout {:#?}", ty ), @@ -700,7 +707,7 @@ fn trans_struct<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) - } else { if let TyKind::Adt(_, _) = ty.ty.kind() { } else { - bug!("Variants::Multiple not TyKind::Adt"); + span_bug!(span, "Variants::Multiple not TyKind::Adt"); } if i == 0 { field_names.push("discriminant".to_string()); From 38f460a64bf5c805d20b79bfaaff38d1323c8dde Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 12 Oct 2021 15:39:06 +0300 Subject: [PATCH 6/7] Simplify `FnAbi` handling for the fewer `PassMode`s possible now. --- crates/rustc_codegen_spirv/src/abi.rs | 122 ++---------------- .../src/builder/intrinsics.rs | 25 ++-- crates/rustc_codegen_spirv/src/builder/mod.rs | 52 +++----- .../src/codegen_cx/entry.rs | 91 +++++++------ .../src/codegen_cx/type_.rs | 10 +- 5 files changed, 96 insertions(+), 204 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index a473c40dd5..003227e1cc 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -19,7 +19,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_span::DUMMY_SP; -use rustc_target::abi::call::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode, Reg, RegKind}; +use rustc_target::abi::call::{ArgAbi, ArgAttributes, FnAbi, PassMode}; use rustc_target::abi::{Abi, Align, FieldsShape, Primitive, Scalar, Size, VariantIdx, Variants}; use rustc_target::spec::abi::Abi as SpecAbi; use std::cell::RefCell; @@ -236,87 +236,6 @@ impl<'tcx> ConvSpirvType<'tcx> for PointeeTy<'tcx> { } } -impl<'tcx> ConvSpirvType<'tcx> for Reg { - fn spirv_type(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word { - match self.kind { - RegKind::Integer => SpirvType::Integer(self.size.bits() as u32, false).def(span, cx), - RegKind::Float => SpirvType::Float(self.size.bits() as u32).def(span, cx), - RegKind::Vector => SpirvType::Vector { - element: SpirvType::Integer(8, false).def(span, cx), - count: self.size.bytes() as u32, - } - .def(span, cx), - } - } -} - -impl<'tcx> ConvSpirvType<'tcx> for CastTarget { - fn spirv_type(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word { - let rest_ll_unit = self.rest.unit.spirv_type(span, cx); - let (rest_count, rem_bytes) = if self.rest.unit.size.bytes() == 0 { - (0, 0) - } else { - ( - self.rest.total.bytes() / self.rest.unit.size.bytes(), - self.rest.total.bytes() % self.rest.unit.size.bytes(), - ) - }; - - if self.prefix.iter().all(|x| x.is_none()) { - // Simplify to a single unit when there is no prefix and size <= unit size - if self.rest.total <= self.rest.unit.size { - return rest_ll_unit; - } - - // Simplify to array when all chunks are the same size and type - if rem_bytes == 0 { - return SpirvType::Array { - element: rest_ll_unit, - count: cx.constant_u32(span, rest_count as u32), - } - .def(span, cx); - } - } - - // Create list of fields in the main structure - let mut args: Vec<_> = self - .prefix - .iter() - .flatten() - .map(|&kind| { - Reg { - kind, - size: self.prefix_chunk_size, - } - .spirv_type(span, cx) - }) - .chain((0..rest_count).map(|_| rest_ll_unit)) - .collect(); - - // Append final integer - if rem_bytes != 0 { - // Only integers can be really split further. - assert_eq!(self.rest.unit.kind, RegKind::Integer); - args.push(SpirvType::Integer(rem_bytes as u32 * 8, false).def(span, cx)); - } - - let size = Some(self.size(cx)); - let align = self.align(cx); - let (field_offsets, computed_size, computed_align) = auto_struct_layout(cx, &args); - assert_eq!(size, computed_size, "{:#?}", self); - assert_eq!(align, computed_align, "{:#?}", self); - SpirvType::Adt { - def_id: None, - size, - align, - field_types: args, - field_offsets, - field_names: None, - } - .def(span, cx) - } -} - impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> { fn spirv_type(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word { let mut argument_types = Vec::new(); @@ -326,14 +245,11 @@ impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Direct(_) | PassMode::Pair(..) => { self.ret.layout.spirv_type_immediate(span, cx) } - PassMode::Cast(cast_target) => cast_target.spirv_type(span, cx), - PassMode::Indirect { .. } => { - let pointee = self.ret.layout.spirv_type(span, cx); - let pointer = SpirvType::Pointer { pointee }.def(span, cx); - // Important: the return pointer comes *first*, not last. - argument_types.push(pointer); - SpirvType::Void.def(span, cx) - } + PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!( + span, + "query hooks should've made this `PassMode` impossible: {:#?}", + self.ret + ), }; for arg in &self.args { @@ -349,27 +265,11 @@ impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> { )); continue; } - PassMode::Cast(cast_target) => cast_target.spirv_type(span, cx), - PassMode::Indirect { - extra_attrs: Some(_), - .. - } => { - let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty); - let ptr_layout = cx.layout_of(ptr_ty); - argument_types.push(scalar_pair_element_backend_type( - cx, span, ptr_layout, 0, true, - )); - argument_types.push(scalar_pair_element_backend_type( - cx, span, ptr_layout, 1, true, - )); - continue; - } - PassMode::Indirect { - extra_attrs: None, .. - } => { - let pointee = arg.layout.spirv_type(span, cx); - SpirvType::Pointer { pointee }.def(span, cx) - } + PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!( + span, + "query hooks should've made this `PassMode` impossible: {:#?}", + arg + ), }; argument_types.push(arg_type); } diff --git a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs index a12d4bfb7b..3633b6abb4 100644 --- a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs +++ b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::{FnDef, Instance, ParamEnv, Ty, TyKind}; use rustc_span::source_map::Span; use rustc_span::sym; use rustc_target::abi::call::{FnAbi, PassMode}; +use std::assert_matches::assert_matches; fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_>) -> Option<(u64, bool)> { match ty.kind() { @@ -100,16 +101,9 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { sym::volatile_load | sym::unaligned_volatile_load => { let ptr = args[0].immediate(); - if let PassMode::Cast(ty) = fn_abi.ret.mode { - let pointee = ty.spirv_type(self.span(), self); - let pointer = SpirvType::Pointer { pointee }.def(self.span(), self); - let ptr = self.pointercast(ptr, pointer); - self.volatile_load(pointee, ptr) - } else { - let layout = self.layout_of(substs.type_at(0)); - let load = self.volatile_load(layout.spirv_type(self.span(), self), ptr); - self.to_immediate(load, layout) - } + let layout = self.layout_of(substs.type_at(0)); + let load = self.volatile_load(layout.spirv_type(self.span(), self), ptr); + self.to_immediate(load, layout) } sym::prefetch_read_data @@ -330,13 +324,10 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { }; if !fn_abi.ret.is_ignore() { - if let PassMode::Cast(_ty) = fn_abi.ret.mode { - self.fatal("TODO: PassMode::Cast not implemented yet in intrinsics"); - } else { - OperandRef::from_immediate_or_packed_pair(self, value, result.layout) - .val - .store(self, result); - } + assert_matches!(fn_abi.ret.mode, PassMode::Direct(_) | PassMode::Pair(..)); + OperandRef::from_immediate_or_packed_pair(self, value, result.layout) + .val + .store(self, result); } } diff --git a/crates/rustc_codegen_spirv/src/builder/mod.rs b/crates/rustc_codegen_spirv/src/builder/mod.rs index 775ca382d2..5a8995de62 100644 --- a/crates/rustc_codegen_spirv/src/builder/mod.rs +++ b/crates/rustc_codegen_spirv/src/builder/mod.rs @@ -24,6 +24,7 @@ use rustc_errors::DiagnosticBuilder; use rustc_middle::mir::coverage::{ CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op, }; +use rustc_middle::span_bug; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout, @@ -286,27 +287,18 @@ impl<'a, 'tcx> ArgAbiMethods<'tcx> for Builder<'a, 'tcx> { val } match arg_abi.mode { - PassMode::Ignore => (), + PassMode::Ignore => {} + PassMode::Direct(_) => { + OperandValue::Immediate(next(self, idx)).store(self, dst); + } PassMode::Pair(..) => { OperandValue::Pair(next(self, idx), next(self, idx)).store(self, dst); } - PassMode::Indirect { - extra_attrs: Some(_), - .. - } => OperandValue::Ref( - next(self, idx), - Some(next(self, idx)), - arg_abi.layout.align.abi, - ) - .store(self, dst), - PassMode::Direct(_) - | PassMode::Indirect { - extra_attrs: None, .. - } - | PassMode::Cast(_) => { - let next_arg = next(self, idx); - self.store_arg(arg_abi, next_arg, dst); - } + PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!( + self.span(), + "query hooks should've made this `PassMode` impossible: {:#?}", + arg_abi + ), } } @@ -316,20 +308,16 @@ impl<'a, 'tcx> ArgAbiMethods<'tcx> for Builder<'a, 'tcx> { val: Self::Value, dst: PlaceRef<'tcx, Self::Value>, ) { - if arg_abi.is_ignore() { - return; - } - if arg_abi.is_sized_indirect() { - OperandValue::Ref(val, None, arg_abi.layout.align.abi).store(self, dst); - } else if arg_abi.is_unsized_indirect() { - self.fatal("unsized `ArgAbi` must be handled through `store_fn_arg`"); - } else if let PassMode::Cast(cast) = arg_abi.mode { - let cast_ty = cast.spirv_type(self.span(), self); - let cast_ptr_ty = SpirvType::Pointer { pointee: cast_ty }.def(self.span(), self); - let cast_dst = self.pointercast(dst.llval, cast_ptr_ty); - self.store(val, cast_dst, arg_abi.layout.align.abi); - } else { - OperandValue::Immediate(val).store(self, dst); + match arg_abi.mode { + PassMode::Ignore => {} + PassMode::Direct(_) | PassMode::Pair(..) => { + OperandValue::Immediate(val).store(self, dst); + } + PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!( + self.span(), + "query hooks should've made this `PassMode` impossible: {:#?}", + arg_abi + ), } } diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs index f1d83c2c89..75c8f845fd 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs @@ -11,13 +11,12 @@ use rspirv::spirv::{ use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; +use rustc_middle::span_bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; -use rustc_middle::ty::{Instance, Ty, TyKind}; +use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::Span; -use rustc_target::abi::{ - call::{ArgAbi, ArgAttribute, ArgAttributes, FnAbi, PassMode}, - Size, -}; +use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; +use std::assert_matches::assert_matches; impl<'tcx> CodegenCx<'tcx> { // Entry points declare their "interface" (all uniforms, inputs, outputs, etc.) as parameters. @@ -47,43 +46,53 @@ impl<'tcx> CodegenCx<'tcx> { let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(fn_hir_id)); body.params }; - const EMPTY: ArgAttribute = ArgAttribute::empty(); - for (abi, hir_param) in fn_abi.args.iter().zip(hir_params) { - match abi.mode { - PassMode::Direct(_) - | PassMode::Indirect { .. } - // plain DST/RTA/VLA - | PassMode::Pair( - ArgAttributes { - pointee_size: Size::ZERO, - .. - }, - ArgAttributes { regular: EMPTY, .. }, - ) - // DST struct with fields before the DST member - | PassMode::Pair( - ArgAttributes { .. }, - ArgAttributes { - pointee_size: Size::ZERO, - .. - }, - ) => {} - _ => self.tcx.sess.span_err( + 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.sess.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 + ), + ); + } + } + // 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.sess.span_err( + hir_param.ty_span, + &format!( + "entry point parameter type not yet supported \ + (`{}` has size `0`)", + arg_abi.layout.ty + ), + ), + _ => span_bug!( hir_param.ty_span, - &format!("PassMode {:?} invalid for entry point parameter", abi.mode), + "query hooks should've made this `PassMode` impossible: {:#?}", + arg_abi ), } } - if let PassMode::Ignore = fn_abi.ret.mode { + if fn_abi.ret.layout.ty.is_unit() { + assert_matches!(fn_abi.ret.mode, PassMode::Ignore); } else { self.tcx.sess.span_err( span, &format!( - "PassMode {:?} invalid for entry point return type", - fn_abi.ret.mode + "entry point should return `()`, not `{}`", + fn_abi.ret.layout.ty ), ); } + // let execution_model = entry.execution_model; let fn_id = self.shader_entry_stub( span, @@ -168,7 +177,7 @@ impl<'tcx> CodegenCx<'tcx> { // FIXME(eddyb) also check the type for compatibility with being // part of the interface, including potentially `Sync`ness etc. let (value_ty, mutbl, is_ref) = match *layout.ty.kind() { - TyKind::Ref(_, pointee_ty, mutbl) => (pointee_ty, mutbl, true), + ty::Ref(_, pointee_ty, mutbl) => (pointee_ty, mutbl, true), _ => (layout.ty, hir::Mutability::Not, false), }; let spirv_ty = self.layout_of(value_ty).spirv_type(hir_param.ty_span, self); @@ -396,7 +405,7 @@ impl<'tcx> CodegenCx<'tcx> { // Compute call argument(s) to match what the Rust entry `fn` expects, // starting from the `value_ptr` pointing to a `value_spirv_type` // (e.g. `Input` doesn't use indirection, so we have to load from it). - if let TyKind::Ref(..) = entry_arg_abi.layout.ty.kind() { + if let ty::Ref(..) = entry_arg_abi.layout.ty.kind() { call_args.push(value_ptr); match entry_arg_abi.mode { PassMode::Direct(_) => assert_eq!(value_len, None), @@ -405,16 +414,14 @@ impl<'tcx> CodegenCx<'tcx> { } } else { assert_eq!(storage_class, StorageClass::Input); + assert_matches!(entry_arg_abi.mode, PassMode::Direct(_)); - call_args.push(match entry_arg_abi.mode { - PassMode::Indirect { .. } => value_ptr, - PassMode::Direct(_) => bx.load( - entry_arg_abi.layout.spirv_type(hir_param.ty_span, bx), - value_ptr, - entry_arg_abi.layout.align.abi, - ), - _ => unreachable!(), - }); + let value = bx.load( + entry_arg_abi.layout.spirv_type(hir_param.ty_span, bx), + value_ptr, + entry_arg_abi.layout.align.abi, + ); + call_args.push(value); assert_eq!(value_len, None); } diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/type_.rs b/crates/rustc_codegen_spirv/src/codegen_cx/type_.rs index 3fba937071..379feaac98 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/type_.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/type_.rs @@ -72,7 +72,10 @@ impl<'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'tcx> { } fn cast_backend_type(&self, ty: &CastTarget) -> Self::Type { - ty.spirv_type(DUMMY_SP, self) + bug!( + "cast_backend_type({:?}): query hooks should've made `PassMode::Cast` impossible", + ty + ) } fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type { @@ -84,7 +87,10 @@ impl<'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'tcx> { } fn reg_backend_type(&self, ty: &Reg) -> Self::Type { - ty.spirv_type(DUMMY_SP, self) + bug!( + "reg_backend_type({:?}): query hooks should've made `PassMode::Cast` impossible", + ty + ) } fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type { From d0a8ecaa52c0ced807708f935a67f6d7e6560db2 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 12 Oct 2021 15:58:53 +0300 Subject: [PATCH 7/7] tests: adjust for lack of `PassMode::{Indirect, Cast}`. --- tests/ui/dis/entry-pass-mode-cast-array.rs | 16 ++++++++++ .../ui/dis/entry-pass-mode-cast-array.stderr | 21 +++++++++++++ tests/ui/dis/issue-373.rs | 23 ++++++++++++++ tests/ui/dis/issue-373.stderr | 11 +++++++ tests/ui/dis/issue-723-indirect-input.rs | 17 ----------- tests/ui/dis/issue-723-indirect-input.stderr | 23 -------------- tests/ui/dis/issue-731.rs | 14 +++++++++ tests/ui/dis/issue-731.stderr | 21 +++++++++++++ tests/ui/dis/pass-mode-cast-struct.rs | 30 +++++++++++++++++++ tests/ui/dis/pass-mode-cast-struct.stderr | 22 ++++++++++++++ 10 files changed, 158 insertions(+), 40 deletions(-) create mode 100644 tests/ui/dis/entry-pass-mode-cast-array.rs create mode 100644 tests/ui/dis/entry-pass-mode-cast-array.stderr create mode 100644 tests/ui/dis/issue-373.rs create mode 100644 tests/ui/dis/issue-373.stderr delete mode 100644 tests/ui/dis/issue-723-indirect-input.rs delete mode 100644 tests/ui/dis/issue-723-indirect-input.stderr create mode 100644 tests/ui/dis/issue-731.rs create mode 100644 tests/ui/dis/issue-731.stderr create mode 100644 tests/ui/dis/pass-mode-cast-struct.rs create mode 100644 tests/ui/dis/pass-mode-cast-struct.stderr diff --git a/tests/ui/dis/entry-pass-mode-cast-array.rs b/tests/ui/dis/entry-pass-mode-cast-array.rs new file mode 100644 index 0000000000..8b5f5d2720 --- /dev/null +++ b/tests/ui/dis/entry-pass-mode-cast-array.rs @@ -0,0 +1,16 @@ +// This is a similar setup to the `issue-731` test, but instead of "just" the +// missing copy out of the global (`Input`) `OpVariable`, small enough types +// would fail much earlier (by generating unsupported pointer casts). +// (Just like `issue-373`, the problem was the use of `PassMode::Cast`, through +// the default Rust ABI adjustments, that we now override through query hooks) + +// build-pass +// compile-flags: -C llvm-args=--disassemble-entry=main + +use spirv_std as _; + +#[spirv(fragment)] +pub fn main(mut in_array: [f32; 2], out_array: &mut [f32; 2]) { + in_array[0] += 1.0; + *out_array = in_array; +} diff --git a/tests/ui/dis/entry-pass-mode-cast-array.stderr b/tests/ui/dis/entry-pass-mode-cast-array.stderr new file mode 100644 index 0000000000..6e005555eb --- /dev/null +++ b/tests/ui/dis/entry-pass-mode-cast-array.stderr @@ -0,0 +1,21 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +%5 = OpVariable %6 Function +%7 = OpVariable %6 Function +OpLine %8 13 12 +%9 = OpLoad %10 %11 +OpLine %8 13 0 +OpStore %5 %9 +OpLine %8 14 4 +%12 = OpInBoundsAccessChain %13 %5 %14 +%15 = OpInBoundsAccessChain %13 %5 %14 +%16 = OpLoad %17 %15 +%18 = OpFAdd %17 %16 %19 +OpStore %12 %18 +OpLine %8 15 17 +OpCopyMemory %7 %5 +OpLine %8 15 4 +OpCopyMemory %20 %7 +OpLine %8 16 1 +OpReturn +OpFunctionEnd diff --git a/tests/ui/dis/issue-373.rs b/tests/ui/dis/issue-373.rs new file mode 100644 index 0000000000..5f15870316 --- /dev/null +++ b/tests/ui/dis/issue-373.rs @@ -0,0 +1,23 @@ +// Test that returning a single-scalar-field `#[repr(C)] struct` doesn't generate +// unsupported pointer casts (the problem was the use of `PassMode::Cast`, through +// the default Rust ABI adjustments, that we now override through query hooks). + +// build-pass +// compile-flags: -C llvm-args=--disassemble-entry=main + +use spirv_std as _; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct S { + x: f32, +} + +fn f() -> S { + S { x: 2.0 } +} + +#[spirv(fragment)] +pub fn main(out: &mut f32) { + *out = f().x; +} diff --git a/tests/ui/dis/issue-373.stderr b/tests/ui/dis/issue-373.stderr new file mode 100644 index 0000000000..ab8e636257 --- /dev/null +++ b/tests/ui/dis/issue-373.stderr @@ -0,0 +1,11 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpLine %5 22 11 +%6 = OpFunctionCall %7 %8 +OpLine %5 22 11 +%9 = OpCompositeExtract %10 %6 0 +OpLine %5 22 4 +OpStore %11 %9 +OpLine %5 23 1 +OpReturn +OpFunctionEnd diff --git a/tests/ui/dis/issue-723-indirect-input.rs b/tests/ui/dis/issue-723-indirect-input.rs deleted file mode 100644 index 6995b37e0b..0000000000 --- a/tests/ui/dis/issue-723-indirect-input.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Test that interface (global) `OpVariable`s mentioned by `OpEntryPoint` don't -// have to be used by the shader, for storage class inference to succeed. - -// NOTE(eddyb) this test will likely become useless (won't fail without the fix) -// once we start doing the copy out of the `Input` and into a `Function`-scoped -// `OpVariable` (see #731), that's why there is another `issue-723-*` test. - -// build-pass -// compile-flags: -C llvm-args=--disassemble-globals -// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> "" -// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> "" -// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple" - -use spirv_std as _; - -#[spirv(fragment)] -pub fn main(/* unused Input */ _: [f32; 3]) {} diff --git a/tests/ui/dis/issue-723-indirect-input.stderr b/tests/ui/dis/issue-723-indirect-input.stderr deleted file mode 100644 index 2b511a952b..0000000000 --- a/tests/ui/dis/issue-723-indirect-input.stderr +++ /dev/null @@ -1,23 +0,0 @@ -OpCapability Float64 -OpCapability Int16 -OpCapability Int64 -OpCapability Int8 -OpCapability ShaderClockKHR -OpCapability Shader -OpExtension "SPV_KHR_shader_clock" -OpMemoryModel Logical Simple -OpEntryPoint Fragment %1 "main" %2 -OpExecutionMode %1 OriginUpperLeft -%3 = OpString "$OPSTRING_FILENAME/issue-723-indirect-input.rs" -OpName %4 "issue_723_indirect_input::main" -OpDecorate %5 ArrayStride 4 -OpDecorate %2 Location 0 -%6 = OpTypeVoid -%7 = OpTypeFloat 32 -%8 = OpTypeInt 32 0 -%9 = OpConstant %8 3 -%5 = OpTypeArray %7 %9 -%10 = OpTypeFunction %6 %5 -%11 = OpTypeFunction %6 -%12 = OpTypePointer Input %5 -%2 = OpVariable %12 Input diff --git a/tests/ui/dis/issue-731.rs b/tests/ui/dis/issue-731.rs new file mode 100644 index 0000000000..2617c5ad85 --- /dev/null +++ b/tests/ui/dis/issue-731.rs @@ -0,0 +1,14 @@ +// Test that non-immediate (i.e. not one of scalar/scalar-pair/vector) inputs +// get properly copied out of the global (`Input`) `OpVariable` and mutation is +// only ever done on `fn`-local `OpVariable`s, not on the original global. + +// build-pass +// compile-flags: -C llvm-args=--disassemble-entry=main + +use spirv_std as _; + +#[spirv(fragment)] +pub fn main(mut in_array: [f32; 3], out_array: &mut [f32; 3]) { + in_array[0] += 1.0; + *out_array = in_array; +} diff --git a/tests/ui/dis/issue-731.stderr b/tests/ui/dis/issue-731.stderr new file mode 100644 index 0000000000..98540db57f --- /dev/null +++ b/tests/ui/dis/issue-731.stderr @@ -0,0 +1,21 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +%5 = OpVariable %6 Function +%7 = OpVariable %6 Function +OpLine %8 11 12 +%9 = OpLoad %10 %11 +OpLine %8 11 0 +OpStore %5 %9 +OpLine %8 12 4 +%12 = OpInBoundsAccessChain %13 %5 %14 +%15 = OpInBoundsAccessChain %13 %5 %14 +%16 = OpLoad %17 %15 +%18 = OpFAdd %17 %16 %19 +OpStore %12 %18 +OpLine %8 13 17 +OpCopyMemory %7 %5 +OpLine %8 13 4 +OpCopyMemory %20 %7 +OpLine %8 14 1 +OpReturn +OpFunctionEnd diff --git a/tests/ui/dis/pass-mode-cast-struct.rs b/tests/ui/dis/pass-mode-cast-struct.rs new file mode 100644 index 0000000000..f476c8635e --- /dev/null +++ b/tests/ui/dis/pass-mode-cast-struct.rs @@ -0,0 +1,30 @@ +// Test that a small enough `struct` doesn't generate unsupported pointer casts. +// (Just like `issue-373`, the problem was the use of `PassMode::Cast`, through +// the default Rust ABI adjustments, that we now override through query hooks) + +// build-pass +// compile-flags: -C llvm-args=--disassemble-entry=main + +use spirv_std as _; + +struct Foo { + a: u32, + b: u8, + c: u8, +} + +impl Foo { + fn unpack(data: u64) -> Self { + Self { + a: (data >> 16 & 0xffffff) as u32, + b: (data & 0xff >> 8) as u8, + c: (data & 0xff) as u8, + } + } +} + +#[spirv(fragment)] +pub fn main(in_packed: u64, out_sum: &mut u32) { + let foo = Foo::unpack(in_packed); + *out_sum = foo.a + (foo.b + foo.c) as u32; +} diff --git a/tests/ui/dis/pass-mode-cast-struct.stderr b/tests/ui/dis/pass-mode-cast-struct.stderr new file mode 100644 index 0000000000..5c44a83105 --- /dev/null +++ b/tests/ui/dis/pass-mode-cast-struct.stderr @@ -0,0 +1,22 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpLine %5 27 12 +%6 = OpLoad %7 %8 +OpLine %5 28 14 +%9 = OpFunctionCall %10 %11 %6 +OpLine %5 29 15 +%12 = OpCompositeExtract %13 %9 0 +OpLine %5 29 24 +%14 = OpCompositeExtract %15 %9 1 +OpLine %5 29 32 +%16 = OpCompositeExtract %15 %9 2 +OpLine %5 29 23 +%17 = OpIAdd %15 %14 %16 +OpLine %5 29 23 +%18 = OpUConvert %13 %17 +OpLine %5 29 4 +%19 = OpIAdd %13 %12 %18 +OpStore %20 %19 +OpLine %5 30 1 +OpReturn +OpFunctionEnd