Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 48 additions & 19 deletions crates/rustc_codegen_spirv/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use crate::attr::{AggregatedSpirvAttributes, IntrinsicType};
use crate::codegen_cx::CodegenCx;
use crate::spirv_type::SpirvType;
use crate::spirv_type::{SpirvType, name_type_id};
use itertools::Itertools;
use rspirv::spirv::{Dim, ImageFormat, StorageClass, Word};
use rustc_abi::ExternAbi as Abi;
Expand Down Expand Up @@ -321,6 +321,33 @@ impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
}
}

/// If `layout` has exactly one non-ZST field positioned at offset 0 with size and
/// alignment matching the outer layout, returns that field.
///
/// This captures the structural shape of a "newtype wrapper" — a single meaningful
/// field padded out to the outer type, which can be substituted for the outer type
/// in a SPIR-V type graph as long as the caller has *independently* verified that
/// the ABIs match (either via `BackendRepr::eq_up_to_validity`, or via
/// `#[repr(transparent)]`, which guarantees full ABI identity by construction).
fn sole_structural_newtype_field<'tcx>(
cx: &CodegenCx<'tcx>,
layout: TyAndLayout<'tcx>,
) -> Option<TyAndLayout<'tcx>> {
let mut non_zst = (0..layout.fields.count()).filter(|&i| !layout.field(cx, i).is_zst());
let i = non_zst.next()?;
if non_zst.next().is_some() {
return None;
}
let field = layout.field(cx, i);
// Only unpack a newtype if the field and the newtype line up
// perfectly, in every way that could potentially affect ABI.
(layout.fields.offset(i) == Size::ZERO
&& field.size == layout.size
&& field.align.abi == layout.align.abi
&& field.backend_repr.eq_up_to_validity(&layout.backend_repr))
.then_some(field)
}

impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
fn spirv_type(&self, mut span: Span, cx: &CodegenCx<'tcx>) -> Word {
if let TyKind::Adt(adt, args) = *self.ty.kind() {
Expand Down Expand Up @@ -379,23 +406,8 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
// a new one, offering the `(a, b)` shape `rustc_codegen_ssa`
// expects, while letting noop pointercasts access the sole
// `BackendRepr::ScalarPair` field - this is the approach taken here
let mut non_zst_fields = (0..self.fields.count())
.map(|i| (i, self.field(cx, i)))
.filter(|(_, field)| !field.is_zst());
let sole_non_zst_field = match (non_zst_fields.next(), non_zst_fields.next()) {
(Some(field), None) => Some(field),
_ => None,
};
if let Some((i, field)) = sole_non_zst_field {
// Only unpack a newtype if the field and the newtype line up
// perfectly, in every way that could potentially affect ABI.
if self.fields.offset(i) == Size::ZERO
&& field.size == self.size
&& field.align.abi == self.align.abi
&& field.backend_repr.eq_up_to_validity(&self.backend_repr)
{
return field.spirv_type(span, cx);
}
if let Some(field) = sole_structural_newtype_field(cx, *self) {
return field.spirv_type(span, cx);
}

// Note: We can't use auto_struct_layout here because the spirv types here might be undefined due to
Expand Down Expand Up @@ -447,7 +459,24 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
.tcx
.dcx()
.fatal("scalable vectors are not supported in SPIR-V backend"),
BackendRepr::Memory { sized: _ } => trans_aggregate(cx, span, *self),
BackendRepr::Memory { sized: _ } => {
// For `#[repr(transparent)]` newtypes, reuse the single non-ZST
// field's SPIR-V type directly instead of wrapping it in an
// `OpTypeStruct`, if the type is `#[repr(transparent)]`.
// Otherwise, we're manipulating the abi too much and the
// format args decompiler fails.
if let TyKind::Adt(adt, _) = self.ty.kind()
&& adt.repr().transparent()
&& let Some(field) = sole_structural_newtype_field(cx, *self)
{
let inner_id = field.spirv_type(span, cx);
// Preserve the wrapper's name as an `OpName` alias on the
// inner SPIR-V type so disassembly still shows it.
name_type_id(cx, inner_id, TyLayoutNameKey::from(*self));
return inner_id;
}
trans_aggregate(cx, span, *self)
}
}
}
}
Expand Down
19 changes: 12 additions & 7 deletions crates/rustc_codegen_spirv/src/spirv_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ pub enum SpirvType<'tcx> {
RayQueryKhr,
}

/// Emit an `OpName` for a type `id`, deduplicated per `(id, name_key)` pair.
/// Unlike [`SpirvType::def_with_name`], this operates on an existing `id` - useful
/// for types reused across multiple Rust types (e.g. `#[repr(transparent)]`
/// newtype collapse, where the wrapper's name is attached to the inner's id).
pub fn name_type_id<'tcx>(cx: &CodegenCx<'tcx>, id: Word, name_key: TyLayoutNameKey<'tcx>) {
let mut type_names = cx.type_cache.type_names.borrow_mut();
if type_names.entry(id).or_default().insert(name_key) {
cx.emit_global().name(id, name_key.to_string());
}
}

impl SpirvType<'_> {
/// Note: `Builder::type_*` should be called *nowhere else* but here, to ensure
/// `CodegenCx::type_defs` stays up-to-date
Expand Down Expand Up @@ -266,13 +277,7 @@ impl SpirvType<'_> {
name_key: TyLayoutNameKey<'tcx>,
) -> Word {
let id = self.def(def_span, cx);

// Only emit `OpName` if this is the first time we see this name.
let mut type_names = cx.type_cache.type_names.borrow_mut();
if type_names.entry(id).or_default().insert(name_key) {
cx.emit_global().name(id, name_key.to_string());
}

name_type_id(cx, id, name_key);
id
}

Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ contents of `path/to/test.rs.stderr`.
* `// 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
* `// 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
2 changes: 1 addition & 1 deletion tests/compiletests/ui/dis/spec_constant_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// ignore-vulkan1.1

// compile-flags: -C llvm-args=--disassemble
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
Expand Down
1 change: 1 addition & 0 deletions tests/compiletests/ui/dis/spec_constant_array.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ OpMemoryModel Logical Simple
OpEntryPoint GLCompute %1 "main" %2
OpExecutionMode %1 LocalSize 1 1 1
%3 = OpString "$DIR/spec_constant_array.rs"
OpName %2 "out"
OpDecorate %4 Block
OpMemberDecorate %4 0 Offset 0
OpDecorate %2 Binding 0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/entry/asm/entry1_global_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-spv1.0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/entry/asm/entry1_reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-spv1.0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/entry/asm/entry2_global_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-spv1.0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/entry/asm/entry2_reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-spv1.0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/entry/asm/entry3_generic_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-spv1.0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/entry/asm/entry3_global_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-spv1.0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/entry/asm/entry3_reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-spv1.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-spv1.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-spv1.0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/glam/offsets_vec3_vec3a.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-vulkan1.0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/image/write_comp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-vulkan1.0
Expand Down
2 changes: 1 addition & 1 deletion tests/compiletests/ui/image/write_comp_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpLine .*\n" -> ""
// normalize-stderr-test "%\d+ = OpString .*\n" -> ""
// normalize-stderr-test "; .*\n" -> ""
// normalize-stderr-test "^(; .*\n)*" -> ""
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
// ignore-vulkan1.0
Expand Down
91 changes: 91 additions & 0 deletions tests/compiletests/ui/lang/abi/transparent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// 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-spv1.0
// ignore-spv1.1
// ignore-spv1.2
// ignore-spv1.3
// ignore-vulkan1.0
// ignore-vulkan1.1

use core::marker::PhantomData;
use spirv_std::glam::*;
use spirv_std::spirv;

#[repr(C)]
#[derive(Default)]
struct SomeStruct {
a: u32,
b: f32,
c: Vec3,
}

#[derive(Default)]
pub struct A(u32);
#[repr(transparent)]
#[derive(Default)]
pub struct ATrans(u32);

#[derive(Default)]
pub struct B(Vec3);
#[repr(transparent)]
#[derive(Default)]
pub struct BTrans(Vec3);

#[derive(Default)]
pub struct C(SomeStruct);
#[repr(transparent)]
#[derive(Default)]
pub struct CTrans(SomeStruct);

#[derive(Default)]
pub struct D<'a>(u32, PhantomData<&'a ()>);
#[repr(transparent)]
#[derive(Default)]
pub struct DTrans<'a>(u32, PhantomData<&'a ()>);

#[derive(Default)]
pub struct E(ATrans);
#[repr(transparent)]
#[derive(Default)]
pub struct ETrans(ATrans);

#[derive(Default)]
pub struct Zst;
#[repr(transparent)]
#[derive(Default)]
pub struct ZstTrans;

#[spirv(vertex)]
pub fn main(
a: &mut A,
a_trans: &mut ATrans,
b: &mut B,
b_trans: &mut BTrans,
c: &mut C,
c_trans: &mut CTrans,
d: &mut D<'static>,
d_trans: &mut DTrans<'static>,
e: &mut E,
e_trans: &mut ETrans,
zst: &mut Zst,
zst_trans: &mut ZstTrans,
) {
*a = Default::default();
*a_trans = Default::default();
*b = Default::default();
*b_trans = Default::default();
*c = Default::default();
*c_trans = Default::default();
*d = Default::default();
*d_trans = Default::default();
*e = Default::default();
*e_trans = Default::default();
*zst = Default::default();
*zst_trans = Default::default();
}
Loading
Loading