Skip to content

Commit 71ed06f

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

6 files changed

Lines changed: 246 additions & 213 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: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,74 @@
11
OpCapability Shader
22
OpMemoryModel Logical Simple
33
OpEntryPoint Vertex %1 "main" %2 %3 %4
4-
OpName %6 "A"
5-
OpMemberName %6 0 "0"
4+
OpName %6 "AT"
5+
OpName %7 "A"
6+
OpMemberName %7 0 "0"
67
OpName %2 "tid"
8+
OpName %8 "AT"
79
OpName %3 "a"
810
OpName %4 "at"
911
OpDecorate %2 BuiltIn LocalInvocationIndex
10-
OpDecorate %7 ArrayStride 4
12+
OpDecorate %8 ArrayStride 4
1113
OpDecorate %3 Location 0
1214
OpDecorate %4 Location 3
13-
%8 = OpTypeInt 32 0
14-
%9 = OpConstant %8 3
15-
%10 = OpTypeArray %8 %9
16-
%6 = OpTypeStruct %10
17-
%11 = OpTypePointer Output %6
18-
%12 = OpTypePointer Input %8
19-
%13 = OpTypeVoid
20-
%14 = OpTypeFunction %13
21-
%2 = OpVariable %12 Input
22-
%7 = OpTypeArray %8 %9
23-
%15 = OpConstant %8 1
24-
%16 = OpConstant %8 2
25-
%17 = OpTypePointer Output %10
15+
%9 = OpTypeInt 32 0
16+
%10 = OpConstant %9 3
17+
%6 = OpTypeArray %9 %10
18+
%7 = OpTypeStruct %6
19+
%11 = OpTypePointer Output %7
20+
%12 = OpTypePointer Output %6
21+
%13 = OpTypePointer Input %9
22+
%14 = OpTypeVoid
23+
%15 = OpTypeFunction %14
24+
%2 = OpVariable %13 Input
25+
%8 = OpTypeArray %9 %10
26+
%16 = OpConstant %9 1
27+
%17 = OpConstant %9 2
2628
%3 = OpVariable %11 Output
27-
%18 = OpConstant %8 0
28-
%19 = OpTypePointer Output %8
29-
%20 = OpConstant %8 4
30-
%4 = OpVariable %11 Output
31-
%21 = OpConstant %8 5
32-
%1 = OpFunction %13 None %14
29+
%18 = OpConstant %9 0
30+
%19 = OpTypePointer Output %9
31+
%20 = OpConstant %9 4
32+
%4 = OpVariable %12 Output
33+
%21 = OpConstant %9 5
34+
%1 = OpFunction %14 None %15
3335
%22 = OpLabel
34-
%23 = OpLoad %8 %2
35-
%24 = OpCompositeConstruct %7 %23 %15 %16
36-
%25 = OpInBoundsAccessChain %17 %3 %18
37-
%26 = OpCompositeExtract %8 %24 0
38-
%27 = OpCompositeExtract %8 %24 1
39-
%28 = OpCompositeExtract %8 %24 2
40-
%29 = OpCompositeConstruct %10 %26 %27 %28
36+
%23 = OpLoad %9 %2
37+
%24 = OpCompositeConstruct %8 %23 %16 %17
38+
%25 = OpInBoundsAccessChain %12 %3 %18
39+
%26 = OpCompositeExtract %9 %24 0
40+
%27 = OpCompositeExtract %9 %24 1
41+
%28 = OpCompositeExtract %9 %24 2
42+
%29 = OpCompositeConstruct %6 %26 %27 %28
4143
OpStore %25 %29
4244
%30 = OpInBoundsAccessChain %19 %3 %18 %18
4345
OpStore %30 %23
44-
%31 = OpInBoundsAccessChain %19 %3 %18 %15
45-
OpStore %31 %15
46-
%32 = OpInBoundsAccessChain %19 %3 %18 %16
47-
OpStore %32 %16
48-
%33 = OpInBoundsAccessChain %19 %3 %18 %16
49-
%34 = OpInBoundsAccessChain %19 %3 %18 %16
50-
%35 = OpLoad %8 %34
51-
%36 = OpIAdd %8 %35 %20
46+
%31 = OpInBoundsAccessChain %19 %3 %18 %16
47+
OpStore %31 %16
48+
%32 = OpInBoundsAccessChain %19 %3 %18 %17
49+
OpStore %32 %17
50+
%33 = OpInBoundsAccessChain %19 %3 %18 %17
51+
%34 = OpInBoundsAccessChain %19 %3 %18 %17
52+
%35 = OpLoad %9 %34
53+
%36 = OpIAdd %9 %35 %20
5254
OpStore %33 %36
53-
%37 = OpCompositeConstruct %7 %23 %15 %16
54-
%38 = OpInBoundsAccessChain %17 %4 %18
55-
%39 = OpCompositeExtract %8 %37 0
56-
%40 = OpCompositeExtract %8 %37 1
57-
%41 = OpCompositeExtract %8 %37 2
58-
%42 = OpCompositeConstruct %10 %39 %40 %41
59-
OpStore %38 %42
60-
%43 = OpInBoundsAccessChain %19 %4 %18 %18
61-
OpStore %43 %23
62-
%44 = OpInBoundsAccessChain %19 %4 %18 %15
63-
OpStore %44 %15
64-
%45 = OpInBoundsAccessChain %19 %4 %18 %16
65-
OpStore %45 %16
66-
%46 = OpInBoundsAccessChain %19 %4 %18 %16
67-
%47 = OpInBoundsAccessChain %19 %4 %18 %16
68-
%48 = OpLoad %8 %47
69-
%49 = OpIAdd %8 %48 %21
70-
OpStore %46 %49
55+
%37 = OpCompositeConstruct %8 %23 %16 %17
56+
%38 = OpCompositeExtract %9 %37 0
57+
%39 = OpCompositeExtract %9 %37 1
58+
%40 = OpCompositeExtract %9 %37 2
59+
%41 = OpCompositeConstruct %6 %38 %39 %40
60+
OpStore %4 %41
61+
%42 = OpInBoundsAccessChain %19 %4 %18
62+
OpStore %42 %23
63+
%43 = OpInBoundsAccessChain %19 %4 %16
64+
OpStore %43 %16
65+
%44 = OpInBoundsAccessChain %19 %4 %17
66+
OpStore %44 %17
67+
%45 = OpInBoundsAccessChain %19 %4 %17
68+
%46 = OpInBoundsAccessChain %19 %4 %17
69+
%47 = OpLoad %9 %46
70+
%48 = OpIAdd %9 %47 %21
71+
OpStore %45 %48
7172
OpNoLine
7273
OpReturn
7374
OpFunctionEnd
Lines changed: 47 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,70 @@
11
OpCapability Shader
22
OpMemoryModel Logical Simple
33
OpEntryPoint Vertex %1 "main" %2 %3 %4
4-
OpName %7 "A"
5-
OpMemberName %7 0 "0"
6-
OpName %8 "B"
4+
OpName %7 "AT"
5+
OpName %8 "A"
76
OpMemberName %8 0 "0"
8-
OpName %2 "tid"
9-
OpName %9 "A"
7+
OpName %9 "B"
108
OpMemberName %9 0 "0"
11-
OpName %10 "B"
9+
OpName %2 "tid"
10+
OpName %10 "A"
1211
OpMemberName %10 0 "0"
12+
OpName %11 "B"
13+
OpMemberName %11 0 "0"
1314
OpName %3 "a"
1415
OpName %4 "at"
1516
OpDecorate %2 BuiltIn LocalInvocationIndex
16-
OpMemberDecorate %9 0 Offset 0
1717
OpMemberDecorate %10 0 Offset 0
18+
OpMemberDecorate %11 0 Offset 0
1819
OpDecorate %3 Location 0
1920
OpDecorate %4 Location 1
20-
%11 = OpTypeInt 32 0
21-
%12 = OpTypeVector %11 3
22-
%7 = OpTypeStruct %12
21+
%12 = OpTypeInt 32 0
22+
%7 = OpTypeVector %12 3
2323
%8 = OpTypeStruct %7
24-
%13 = OpTypePointer Output %8
25-
%14 = OpTypePointer Input %11
26-
%15 = OpTypeVoid
27-
%16 = OpTypeFunction %15
28-
%2 = OpVariable %14 Input
29-
%17 = OpConstant %11 1
30-
%18 = OpConstant %11 2
31-
%9 = OpTypeStruct %12
32-
%10 = OpTypeStruct %9
33-
%19 = OpUndef %10
34-
%20 = OpTypePointer Output %12
24+
%9 = OpTypeStruct %8
25+
%13 = OpTypePointer Output %9
26+
%14 = OpTypePointer Output %7
27+
%15 = OpTypePointer Input %12
28+
%16 = OpTypeVoid
29+
%17 = OpTypeFunction %16
30+
%2 = OpVariable %15 Input
31+
%18 = OpConstant %12 1
32+
%19 = OpConstant %12 2
33+
%10 = OpTypeStruct %7
34+
%11 = OpTypeStruct %10
35+
%20 = OpUndef %11
3536
%3 = OpVariable %13 Output
36-
%21 = OpConstant %11 0
37-
%22 = OpTypePointer Output %11
38-
%23 = OpConstant %11 4
39-
%4 = OpVariable %13 Output
40-
%24 = OpConstant %11 5
41-
%1 = OpFunction %15 None %16
37+
%21 = OpConstant %12 0
38+
%22 = OpTypePointer Output %12
39+
%23 = OpConstant %12 4
40+
%4 = OpVariable %14 Output
41+
%24 = OpConstant %12 5
42+
%1 = OpFunction %16 None %17
4243
%25 = OpLabel
43-
%26 = OpLoad %11 %2
44-
%27 = OpCompositeConstruct %12 %26 %17 %18
45-
%28 = OpCompositeInsert %10 %27 %19 0 0
46-
%29 = OpInBoundsAccessChain %20 %3 %21 %21
47-
%30 = OpCompositeExtract %12 %28 0 0
44+
%26 = OpLoad %12 %2
45+
%27 = OpCompositeConstruct %7 %26 %18 %19
46+
%28 = OpCompositeInsert %11 %27 %20 0 0
47+
%29 = OpInBoundsAccessChain %14 %3 %21 %21
48+
%30 = OpCompositeExtract %7 %28 0 0
4849
OpStore %29 %30
49-
%31 = OpInBoundsAccessChain %22 %3 %21 %21 %17
50-
%32 = OpIAdd %11 %26 %17
50+
%31 = OpInBoundsAccessChain %22 %3 %21 %21 %18
51+
%32 = OpIAdd %12 %26 %18
5152
OpStore %31 %32
5253
%33 = OpInBoundsAccessChain %22 %3 %21 %21 %21
53-
%34 = OpLoad %11 %33
54-
%35 = OpIAdd %11 %34 %23
54+
%34 = OpLoad %12 %33
55+
%35 = OpIAdd %12 %34 %23
5556
%36 = OpInBoundsAccessChain %22 %3 %21 %21 %21
5657
OpStore %36 %35
57-
%37 = OpCompositeConstruct %12 %26 %17 %18
58-
%38 = OpCompositeInsert %10 %37 %19 0 0
59-
%39 = OpInBoundsAccessChain %20 %4 %21 %21
60-
%40 = OpCompositeExtract %12 %38 0 0
61-
OpStore %39 %40
62-
%41 = OpInBoundsAccessChain %22 %4 %21 %21 %17
63-
%42 = OpIAdd %11 %26 %18
64-
OpStore %41 %42
65-
%43 = OpInBoundsAccessChain %22 %4 %21 %21 %21
66-
%44 = OpLoad %11 %43
67-
%45 = OpIAdd %11 %44 %24
68-
%46 = OpInBoundsAccessChain %22 %4 %21 %21 %21
69-
OpStore %46 %45
58+
%37 = OpCompositeConstruct %7 %26 %18 %19
59+
OpStore %4 %37
60+
%38 = OpInBoundsAccessChain %22 %4 %18
61+
%39 = OpIAdd %12 %26 %19
62+
OpStore %38 %39
63+
%40 = OpInBoundsAccessChain %22 %4 %21
64+
%41 = OpLoad %12 %40
65+
%42 = OpIAdd %12 %41 %24
66+
%43 = OpInBoundsAccessChain %22 %4 %21
67+
OpStore %43 %42
7068
OpNoLine
7169
OpReturn
7270
OpFunctionEnd

0 commit comments

Comments
 (0)