Skip to content

Commit a639684

Browse files
novacrazyFirestar99
authored andcommitted
Collapse #[repr(transparent)] newtypes in SPIR-V type translation
1 parent eae625a commit a639684

4 files changed

Lines changed: 172 additions & 138 deletions

File tree

crates/rustc_codegen_spirv/src/abi.rs

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use crate::attr::{AggregatedSpirvAttributes, IntrinsicType};
55
use crate::codegen_cx::CodegenCx;
6-
use crate::spirv_type::SpirvType;
6+
use crate::spirv_type::{SpirvType, name_type_id};
77
use itertools::Itertools;
88
use rspirv::spirv::{Dim, ImageFormat, StorageClass, Word};
99
use rustc_abi::ExternAbi as Abi;
@@ -321,6 +321,33 @@ impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
321321
}
322322
}
323323

324+
/// If `layout` has exactly one non-ZST field positioned at offset 0 with size and
325+
/// alignment matching the outer layout, returns that field.
326+
///
327+
/// This captures the structural shape of a "newtype wrapper" — a single meaningful
328+
/// field padded out to the outer type, which can be substituted for the outer type
329+
/// in a SPIR-V type graph as long as the caller has *independently* verified that
330+
/// the ABIs match (either via `BackendRepr::eq_up_to_validity`, or via
331+
/// `#[repr(transparent)]`, which guarantees full ABI identity by construction).
332+
fn sole_structural_newtype_field<'tcx>(
333+
cx: &CodegenCx<'tcx>,
334+
layout: TyAndLayout<'tcx>,
335+
) -> Option<TyAndLayout<'tcx>> {
336+
let mut non_zst = (0..layout.fields.count()).filter(|&i| !layout.field(cx, i).is_zst());
337+
let i = non_zst.next()?;
338+
if non_zst.next().is_some() {
339+
return None;
340+
}
341+
let field = layout.field(cx, i);
342+
// Only unpack a newtype if the field and the newtype line up
343+
// perfectly, in every way that could potentially affect ABI.
344+
(layout.fields.offset(i) == Size::ZERO
345+
&& field.size == layout.size
346+
&& field.align.abi == layout.align.abi
347+
&& field.backend_repr.eq_up_to_validity(&layout.backend_repr))
348+
.then_some(field)
349+
}
350+
324351
impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
325352
fn spirv_type(&self, mut span: Span, cx: &CodegenCx<'tcx>) -> Word {
326353
if let TyKind::Adt(adt, args) = *self.ty.kind() {
@@ -379,23 +406,8 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
379406
// a new one, offering the `(a, b)` shape `rustc_codegen_ssa`
380407
// expects, while letting noop pointercasts access the sole
381408
// `BackendRepr::ScalarPair` field - this is the approach taken here
382-
let mut non_zst_fields = (0..self.fields.count())
383-
.map(|i| (i, self.field(cx, i)))
384-
.filter(|(_, field)| !field.is_zst());
385-
let sole_non_zst_field = match (non_zst_fields.next(), non_zst_fields.next()) {
386-
(Some(field), None) => Some(field),
387-
_ => None,
388-
};
389-
if let Some((i, field)) = sole_non_zst_field {
390-
// Only unpack a newtype if the field and the newtype line up
391-
// perfectly, in every way that could potentially affect ABI.
392-
if self.fields.offset(i) == Size::ZERO
393-
&& field.size == self.size
394-
&& field.align.abi == self.align.abi
395-
&& field.backend_repr.eq_up_to_validity(&self.backend_repr)
396-
{
397-
return field.spirv_type(span, cx);
398-
}
409+
if let Some(field) = sole_structural_newtype_field(cx, *self) {
410+
return field.spirv_type(span, cx);
399411
}
400412

401413
// Note: We can't use auto_struct_layout here because the spirv types here might be undefined due to
@@ -447,7 +459,24 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
447459
.tcx
448460
.dcx()
449461
.fatal("scalable vectors are not supported in SPIR-V backend"),
450-
BackendRepr::Memory { sized: _ } => trans_aggregate(cx, span, *self),
462+
BackendRepr::Memory { sized: _ } => {
463+
// For `#[repr(transparent)]` newtypes, reuse the single non-ZST
464+
// field's SPIR-V type directly instead of wrapping it in an
465+
// `OpTypeStruct`, if the type is `#[repr(transparent)]`.
466+
// Otherwise, we're manipulating the abi too much and the
467+
// format args decompiler fails.
468+
if let TyKind::Adt(adt, _) = self.ty.kind()
469+
&& adt.repr().transparent()
470+
&& let Some(field) = sole_structural_newtype_field(cx, *self)
471+
{
472+
let inner_id = field.spirv_type(span, cx);
473+
// Preserve the wrapper's name as an `OpName` alias on the
474+
// inner SPIR-V type so disassembly still shows it.
475+
name_type_id(cx, inner_id, TyLayoutNameKey::from(*self));
476+
return inner_id;
477+
}
478+
trans_aggregate(cx, span, *self)
479+
}
451480
}
452481
}
453482
}

crates/rustc_codegen_spirv/src/spirv_type.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,17 @@ pub enum SpirvType<'tcx> {
9292
RayQueryKhr,
9393
}
9494

95+
/// Emit an `OpName` for a type `id`, deduplicated per `(id, name_key)` pair.
96+
/// Unlike [`SpirvType::def_with_name`], this operates on an existing `id` - useful
97+
/// for types reused across multiple Rust types (e.g. `#[repr(transparent)]`
98+
/// newtype collapse, where the wrapper's name is attached to the inner's id).
99+
pub fn name_type_id<'tcx>(cx: &CodegenCx<'tcx>, id: Word, name_key: TyLayoutNameKey<'tcx>) {
100+
let mut type_names = cx.type_cache.type_names.borrow_mut();
101+
if type_names.entry(id).or_default().insert(name_key) {
102+
cx.emit_global().name(id, name_key.to_string());
103+
}
104+
}
105+
95106
impl SpirvType<'_> {
96107
/// Note: `Builder::type_*` should be called *nowhere else* but here, to ensure
97108
/// `CodegenCx::type_defs` stays up-to-date
@@ -266,13 +277,7 @@ impl SpirvType<'_> {
266277
name_key: TyLayoutNameKey<'tcx>,
267278
) -> Word {
268279
let id = self.def(def_span, cx);
269-
270-
// Only emit `OpName` if this is the first time we see this name.
271-
let mut type_names = cx.type_cache.type_names.borrow_mut();
272-
if type_names.entry(id).or_default().insert(name_key) {
273-
cx.emit_global().name(id, name_key.to_string());
274-
}
275-
280+
name_type_id(cx, id, name_key);
276281
id
277282
}
278283

Lines changed: 70 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,100 @@
11
OpCapability Shader
22
OpMemoryModel Logical Simple
33
OpEntryPoint Vertex %1 "main" %2 %3 %4 %5 %6 %7 %8 %9 %10 %11
4-
OpName %13 "B"
5-
OpMemberName %13 0 "0"
6-
OpName %14 "SomeStruct"
7-
OpMemberName %14 0 "a"
8-
OpMemberName %14 1 "b"
9-
OpMemberName %14 2 "c"
10-
OpName %15 "C"
11-
OpMemberName %15 0 "0"
4+
OpName %13 "BTrans"
5+
OpName %14 "B"
6+
OpMemberName %14 0 "0"
7+
OpName %15 "SomeStruct"
8+
OpMemberName %15 0 "a"
9+
OpMemberName %15 1 "b"
10+
OpMemberName %15 2 "c"
11+
OpName %16 "C"
12+
OpMemberName %16 0 "0"
1213
OpName %2 "a"
1314
OpName %3 "a_trans"
1415
OpName %4 "b"
1516
OpName %5 "b_trans"
16-
OpName %16 "SomeStruct"
17-
OpMemberName %16 0 "a"
18-
OpMemberName %16 1 "b"
19-
OpMemberName %16 2 "c"
17+
OpName %17 "SomeStruct"
18+
OpMemberName %17 0 "a"
19+
OpMemberName %17 1 "b"
20+
OpMemberName %17 2 "c"
2021
OpName %6 "c"
2122
OpName %7 "c_trans"
2223
OpName %8 "d"
2324
OpName %9 "d_trans"
2425
OpName %10 "e"
2526
OpName %11 "e_trans"
26-
OpName %17 "<core::marker::PhantomData<&()> as core::default::Default>::default"
27+
OpName %18 "<core::marker::PhantomData<&()> as core::default::Default>::default"
2728
OpDecorate %2 Location 0
2829
OpDecorate %3 Location 1
2930
OpDecorate %4 Location 2
3031
OpDecorate %5 Location 3
31-
OpMemberDecorate %16 0 Offset 0
32-
OpMemberDecorate %16 1 Offset 4
33-
OpMemberDecorate %16 2 Offset 8
32+
OpMemberDecorate %17 0 Offset 0
33+
OpMemberDecorate %17 1 Offset 4
34+
OpMemberDecorate %17 2 Offset 8
3435
OpDecorate %6 Location 4
3536
OpDecorate %7 Location 7
3637
OpDecorate %8 Location 10
3738
OpDecorate %9 Location 11
3839
OpDecorate %10 Location 12
3940
OpDecorate %11 Location 13
40-
%18 = OpTypeInt 32 0
41-
%19 = OpTypePointer Output %18
42-
%20 = OpTypeFloat 32
43-
%21 = OpTypeVector %20 3
44-
%13 = OpTypeStruct %21
45-
%22 = OpTypePointer Output %13
46-
%14 = OpTypeStruct %18 %20 %21
47-
%15 = OpTypeStruct %14
48-
%23 = OpTypePointer Output %15
49-
%24 = OpTypeVoid
50-
%25 = OpTypeFunction %24
51-
%2 = OpVariable %19 Output
52-
%26 = OpConstant %18 0
53-
%3 = OpVariable %19 Output
54-
%27 = OpTypePointer Output %21
41+
%19 = OpTypeInt 32 0
42+
%20 = OpTypePointer Output %19
43+
%21 = OpTypeFloat 32
44+
%13 = OpTypeVector %21 3
45+
%14 = OpTypeStruct %13
46+
%22 = OpTypePointer Output %14
47+
%23 = OpTypePointer Output %13
48+
%15 = OpTypeStruct %19 %21 %13
49+
%16 = OpTypeStruct %15
50+
%24 = OpTypePointer Output %16
51+
%25 = OpTypePointer Output %15
52+
%26 = OpTypeVoid
53+
%27 = OpTypeFunction %26
54+
%2 = OpVariable %20 Output
55+
%28 = OpConstant %19 0
56+
%3 = OpVariable %20 Output
5557
%4 = OpVariable %22 Output
56-
%28 = OpConstant %20 0
57-
%29 = OpConstantComposite %21 %28 %28 %28
58-
%5 = OpVariable %22 Output
59-
%16 = OpTypeStruct %18 %20 %21
60-
%30 = OpTypePointer Output %14
61-
%6 = OpVariable %23 Output
62-
%7 = OpVariable %23 Output
63-
%8 = OpVariable %19 Output
64-
%9 = OpVariable %19 Output
65-
%10 = OpVariable %19 Output
66-
%11 = OpVariable %19 Output
67-
%1 = OpFunction %24 None %25
58+
%29 = OpConstant %21 0
59+
%30 = OpConstantComposite %13 %29 %29 %29
60+
%5 = OpVariable %23 Output
61+
%17 = OpTypeStruct %19 %21 %13
62+
%6 = OpVariable %24 Output
63+
%7 = OpVariable %25 Output
64+
%8 = OpVariable %20 Output
65+
%9 = OpVariable %20 Output
66+
%10 = OpVariable %20 Output
67+
%11 = OpVariable %20 Output
68+
%1 = OpFunction %26 None %27
6869
%31 = OpLabel
69-
OpStore %2 %26
70-
OpStore %3 %26
71-
%32 = OpInBoundsAccessChain %27 %4 %26
72-
OpStore %32 %29
73-
%33 = OpInBoundsAccessChain %27 %5 %26
74-
OpStore %33 %29
75-
%34 = OpCompositeConstruct %16 %26 %28 %29
76-
%35 = OpInBoundsAccessChain %30 %6 %26
77-
%36 = OpCompositeExtract %18 %34 0
78-
%37 = OpCompositeExtract %20 %34 1
79-
%38 = OpCompositeExtract %21 %34 2
80-
%39 = OpCompositeConstruct %14 %36 %37 %38
81-
OpStore %35 %39
82-
%40 = OpCompositeConstruct %16 %26 %28 %29
83-
%41 = OpInBoundsAccessChain %30 %7 %26
84-
%42 = OpCompositeExtract %18 %40 0
85-
%43 = OpCompositeExtract %20 %40 1
86-
%44 = OpCompositeExtract %21 %40 2
87-
%45 = OpCompositeConstruct %14 %42 %43 %44
88-
OpStore %41 %45
89-
%46 = OpFunctionCall %24 %17
90-
OpStore %8 %26
91-
%47 = OpFunctionCall %24 %17
92-
OpStore %9 %26
93-
OpStore %10 %26
94-
OpStore %11 %26
70+
OpStore %2 %28
71+
OpStore %3 %28
72+
%32 = OpInBoundsAccessChain %23 %4 %28
73+
OpStore %32 %30
74+
OpStore %5 %30
75+
%33 = OpCompositeConstruct %17 %28 %29 %30
76+
%34 = OpInBoundsAccessChain %25 %6 %28
77+
%35 = OpCompositeExtract %19 %33 0
78+
%36 = OpCompositeExtract %21 %33 1
79+
%37 = OpCompositeExtract %13 %33 2
80+
%38 = OpCompositeConstruct %15 %35 %36 %37
81+
OpStore %34 %38
82+
%39 = OpCompositeConstruct %17 %28 %29 %30
83+
%40 = OpCompositeExtract %19 %39 0
84+
%41 = OpCompositeExtract %21 %39 1
85+
%42 = OpCompositeExtract %13 %39 2
86+
%43 = OpCompositeConstruct %15 %40 %41 %42
87+
OpStore %7 %43
88+
%44 = OpFunctionCall %26 %18
89+
OpStore %8 %28
90+
%45 = OpFunctionCall %26 %18
91+
OpStore %9 %28
92+
OpStore %10 %28
93+
OpStore %11 %28
9594
OpNoLine
9695
OpReturn
9796
OpFunctionEnd
98-
%17 = OpFunction %24 None %25
99-
%48 = OpLabel
97+
%18 = OpFunction %26 None %27
98+
%46 = OpLabel
10099
OpReturn
101100
OpFunctionEnd

0 commit comments

Comments
 (0)