Skip to content

Commit 8a01401

Browse files
Distinguish repr(C) ZSTs from others in ABI computation
1 parent 54f67d2 commit 8a01401

39 files changed

Lines changed: 421 additions & 77 deletions

compiler/rustc_abi/src/layout.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
196196
size,
197197
max_repr_align: None,
198198
unadjusted_abi_align: element.align.abi,
199+
repr_c: element.repr_c,
199200
randomization_seed: element.randomization_seed.wrapping_add(Hash64::new(count)),
200201
})
201202
}
@@ -499,6 +500,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
499500
return Err(LayoutCalculatorError::EmptyUnion);
500501
};
501502

503+
let repr_c =
504+
repr.c() || (repr.transparent() && only_variant.iter().any(|field| field.repr_c));
505+
502506
let combined_seed = only_variant
503507
.iter()
504508
.map(|v| v.randomization_seed)
@@ -514,6 +518,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
514518
size: size.align_to(align),
515519
max_repr_align,
516520
unadjusted_abi_align,
521+
repr_c,
517522
randomization_seed: combined_seed,
518523
})
519524
}
@@ -786,6 +791,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
786791
align: AbiAlign::new(align),
787792
max_repr_align,
788793
unadjusted_abi_align,
794+
repr_c: repr.c(),
789795
randomization_seed: combined_seed,
790796
};
791797

@@ -1133,6 +1139,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
11331139
size,
11341140
max_repr_align,
11351141
unadjusted_abi_align,
1142+
repr_c: repr.c(),
11361143
randomization_seed: combined_seed,
11371144
};
11381145

@@ -1482,6 +1489,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
14821489
unadjusted_abi_align
14831490
};
14841491

1492+
let repr_c = repr.c() || (repr.transparent() && fields.iter().any(|field| field.repr_c));
1493+
14851494
let seed = field_seed.wrapping_add(repr.field_shuffle_seed);
14861495

14871496
Ok(LayoutData {
@@ -1494,6 +1503,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
14941503
size,
14951504
max_repr_align,
14961505
unadjusted_abi_align,
1506+
repr_c,
14971507
randomization_seed: seed,
14981508
})
14991509
}
@@ -1594,6 +1604,7 @@ where
15941604
align: AbiAlign::new(align),
15951605
max_repr_align: None,
15961606
unadjusted_abi_align: elt.align.abi,
1607+
repr_c: false,
15971608
randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)),
15981609
})
15991610
}

compiler/rustc_abi/src/layout/coroutine.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ pub(super) fn layout<
311311
align,
312312
max_repr_align: None,
313313
unadjusted_abi_align: align.abi,
314+
repr_c: false,
314315
randomization_seed: Default::default(),
315316
})
316317
}

compiler/rustc_abi/src/layout/simple.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
2525
size: Size::ZERO,
2626
max_repr_align: None,
2727
unadjusted_abi_align: dl.i8_align,
28+
repr_c: false,
2829
randomization_seed: Hash64::new(0),
2930
}
3031
}
@@ -42,6 +43,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
4243
size: Size::ZERO,
4344
max_repr_align: None,
4445
unadjusted_abi_align: dl.i8_align,
46+
repr_c: false,
4547
randomization_seed: Hash64::ZERO,
4648
}
4749
}
@@ -84,6 +86,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
8486
align,
8587
max_repr_align: None,
8688
unadjusted_abi_align: align.abi,
89+
repr_c: false,
8790
randomization_seed: Hash64::new(randomization_seed),
8891
}
8992
}
@@ -117,6 +120,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
117120
size,
118121
max_repr_align: None,
119122
unadjusted_abi_align: align,
123+
repr_c: false,
120124
randomization_seed: Hash64::new(combined_seed),
121125
}
122126
}
@@ -143,6 +147,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
143147
size: Size::ZERO,
144148
max_repr_align: None,
145149
unadjusted_abi_align: dl.i8_align,
150+
repr_c: false,
146151
randomization_seed: Hash64::ZERO,
147152
}
148153
}

compiler/rustc_abi/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,6 +2142,12 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
21422142
/// in some cases.
21432143
pub unadjusted_abi_align: Align,
21442144

2145+
/// Whether this type is `repr(C)`, or a`repr(transparent)` wrapper around such,
2146+
/// or an array of such.
2147+
/// Some C ABIs pass `repr(C)` ZSTs by pointer, but `repr(Rust)` ZSTs should always
2148+
/// be ignored.
2149+
pub repr_c: bool,
2150+
21452151
/// The randomization seed based on this type's own repr and its fields.
21462152
///
21472153
/// Since randomization is toggled on a per-crate basis even crates that do not have randomization
@@ -2170,6 +2176,12 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
21702176
pub fn is_uninhabited(&self) -> bool {
21712177
self.uninhabited
21722178
}
2179+
2180+
/// Returns `true` if this is a `repr(C)` type,
2181+
/// or an array of such, or a `repr(transparent)` wrapper around such
2182+
pub fn is_repr_c(&self) -> bool {
2183+
self.repr_c
2184+
}
21732185
}
21742186

21752187
impl<FieldIdx: Idx, VariantIdx: Idx> fmt::Debug for LayoutData<FieldIdx, VariantIdx>
@@ -2191,6 +2203,7 @@ where
21912203
variants,
21922204
max_repr_align,
21932205
unadjusted_abi_align,
2206+
repr_c,
21942207
randomization_seed,
21952208
} = self;
21962209
f.debug_struct("Layout")
@@ -2203,6 +2216,7 @@ where
22032216
.field("variants", variants)
22042217
.field("max_repr_align", max_repr_align)
22052218
.field("unadjusted_abi_align", unadjusted_abi_align)
2219+
.field("repr_c", repr_c)
22062220
.field("randomization_seed", randomization_seed)
22072221
.finish()
22082222
}

compiler/rustc_target/src/callconv/powerpc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ where
1919
// powerpc-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
2020
if cx.target_spec().os == Os::Linux
2121
&& matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc)
22-
&& arg.layout.is_zst()
22+
&& arg.layout.is_repr_c()
2323
{
2424
arg.make_indirect_from_ignore();
2525
}

compiler/rustc_target/src/callconv/s390x.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ where
3131
// s390x-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
3232
if cx.target_spec().os == Os::Linux
3333
&& matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc)
34-
&& arg.layout.is_zst()
34+
&& arg.layout.is_repr_c()
3535
{
3636
arg.make_indirect_from_ignore();
3737
}

compiler/rustc_target/src/callconv/sparc64.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ where
219219
continue;
220220
}
221221
if arg.is_ignore() {
222-
if passes_zsts && arg.layout.is_zst() {
222+
if passes_zsts && arg.layout.is_repr_c() {
223223
arg.make_indirect_from_ignore();
224224
double_word_count += 1;
225225
}

compiler/rustc_target/src/callconv/x86_win64.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,25 @@ where
4848
}
4949
};
5050

51-
if !fn_abi.ret.is_ignore() {
51+
// Windows ABIs do not talk about ZST since such types do not exist in MSVC.
52+
// However, clang and gcc allow ZST is their windows-gnu targets, and pass them by pointer indierction.
53+
// We follow that for `repr(C)` ZSTs (and `repr(transparent)` wrappers around them),
54+
// but `repr(Rust)` ones are always ignored (ensuring that `()` matches C `void`).
55+
56+
if fn_abi.ret.is_ignore() {
57+
if fn_abi.ret.layout.is_repr_c() {
58+
fn_abi.ret.make_indirect_from_ignore();
59+
}
60+
} else {
5261
fixup(&mut fn_abi.ret, true);
5362
}
5463

5564
for arg in fn_abi.args.iter_mut() {
56-
if arg.is_ignore() && arg.layout.is_zst() {
57-
// Windows ABIs do not talk about ZST since such types do not exist in MSVC.
58-
// In that sense we can do whatever we want here, and maybe we should throw an error
59-
// (but of course that would be a massive breaking change now).
60-
// We try to match clang and gcc (which allow ZST is their windows-gnu targets), so we
61-
// pass ZST via pointer indirection.
62-
arg.make_indirect_from_ignore();
65+
if arg.is_ignore() {
66+
if arg.layout.is_repr_c() {
67+
arg.make_indirect_from_ignore();
68+
}
69+
6370
continue;
6471
}
6572
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
@@ -68,7 +75,4 @@ where
6875
}
6976
fixup(arg, false);
7077
}
71-
// FIXME: We should likely also do something about ZST return types, similar to above.
72-
// However, that's non-trivial due to `()`.
73-
// See <https://github.com/rust-lang/unsafe-code-guidelines/issues/552>.
7478
}

library/core/src/primitive_docs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1842,7 +1842,8 @@ mod prim_ref {}
18421842
/// call will be valid ABI-wise. The callee receives the result of transmuting the function pointer
18431843
/// from `fn()` to `fn(i32)`; that transmutation is itself a well-defined operation, it's just
18441844
/// almost certainly UB to later call that function pointer.)
1845-
/// - Any two types with size 0 and alignment 1 are ABI-compatible.
1845+
/// - Any two types with size 0 and alignment 1 are ABI-compatible, unless they are `repr(C)`
1846+
/// or a `repr(transparent)` wrapper around `repr(C)`.
18461847
/// - A `repr(transparent)` type `T` is ABI-compatible with its unique non-trivial field, i.e., the
18471848
/// unique field that doesn't have size 0 and alignment 1 (if there is such a field).
18481849
/// - `i32` is ABI-compatible with `NonZero<i32>`, and similar for all other integer types.

tests/codegen-llvm/abi-win64-zst.rs

Lines changed: 110 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,33 +21,129 @@
2121
extern crate minicore;
2222
use minicore::*;
2323

24-
// Make sure the argument is always passed when explicitly requesting a Windows ABI.
24+
#[repr(C)]
25+
struct CZst;
26+
27+
#[repr(transparent)]
28+
struct CZst2((), CZst, ());
29+
30+
// Make sure the argument is always passed when explicitly requesting a Windows ABI,
31+
// and it is `repr(C)` - but not if it is `repr(Rust)`.
2532
// Our goal here is to match clang: <https://clang.godbolt.org/z/Wr4jMWq3P>.
2633

27-
// CHECK: define win64cc void @pass_zst_win64(ptr {{[^,]*}})
34+
// CHECK: define win64cc void @pass_rust_zst_win64()
35+
#[no_mangle]
36+
extern "win64" fn pass_rust_zst_win64(_: ()) {}
37+
38+
// CHECK: define win64cc void @pass_c_zst_win64(ptr {{[^,]*}})
39+
#[no_mangle]
40+
extern "win64" fn pass_c_zst_win64(_: CZst) {}
41+
42+
// CHECK: define win64cc void @pass_c_zst_2_win64(ptr {{[^,]*}})
43+
#[no_mangle]
44+
extern "win64" fn pass_c_zst_2_win64(_: CZst2) {}
45+
46+
// CHECK: define x86_vectorcallcc void @pass_rust_zst_vectorcall()
47+
#[no_mangle]
48+
extern "vectorcall" fn pass_rust_zst_vectorcall(_: ()) {}
49+
50+
// CHECK: define x86_vectorcallcc void @pass_c_zst_vectorcall(ptr {{[^,]*}})
2851
#[no_mangle]
29-
extern "win64" fn pass_zst_win64(_: ()) {}
52+
extern "vectorcall" fn pass_c_zst_vectorcall(_: CZst) {}
3053

31-
// CHECK: define x86_vectorcallcc void @pass_zst_vectorcall(ptr {{[^,]*}})
54+
// CHECK: define x86_vectorcallcc void @pass_c_zst_2_vectorcall(ptr {{[^,]*}})
3255
#[no_mangle]
33-
extern "vectorcall" fn pass_zst_vectorcall(_: ()) {}
56+
extern "vectorcall" fn pass_c_zst_2_vectorcall(_: CZst2) {}
3457

35-
// windows-gnu: define void @pass_zst_fastcall(ptr {{[^,]*}})
36-
// windows-msvc: define void @pass_zst_fastcall(ptr {{[^,]*}})
58+
// windows-gnu: define void @pass_rust_zst_fastcall()
59+
// windows-msvc: define void @pass_rust_zst_fastcall()
3760
#[no_mangle]
3861
#[cfg(windows)] // "fastcall" is not valid on 64bit Linux
39-
extern "fastcall" fn pass_zst_fastcall(_: ()) {}
62+
extern "fastcall" fn pass_rust_zst_fastcall(_: ()) {}
63+
64+
// windows-gnu: define void @pass_c_zst_fastcall(ptr {{[^,]*}})
65+
// windows-msvc: define void @pass_c_zst_fastcall(ptr {{[^,]*}})
66+
#[no_mangle]
67+
#[cfg(windows)] // "fastcall" is not valid on 64bit Linux
68+
extern "fastcall" fn pass_c_zst_fastcall(_: CZst) {}
69+
70+
// windows-gnu: define void @pass_c_zst_2_fastcall(ptr {{[^,]*}})
71+
// windows-msvc: define void @pass_c_zst_2_fastcall(ptr {{[^,]*}})
72+
#[no_mangle]
73+
#[cfg(windows)] // "fastcall" is not valid on 64bit Linux
74+
extern "fastcall" fn pass_c_zst_2_fastcall(_: CZst2) {}
4075

4176
// The sysv64 ABI ignores ZST.
4277

43-
// CHECK: define x86_64_sysvcc void @pass_zst_sysv64()
78+
// CHECK: define x86_64_sysvcc void @pass_rust_zst_sysv64()
79+
#[no_mangle]
80+
extern "sysv64" fn pass_rust_zst_sysv64(_: ()) {}
81+
82+
// CHECK: define x86_64_sysvcc void @pass_c_zst_sysv64()
83+
#[no_mangle]
84+
extern "sysv64" fn pass_c_zst_sysv64(_: CZst) {}
85+
86+
// CHECK: define x86_64_sysvcc void @pass_c_zst_2_sysv64()
4487
#[no_mangle]
45-
extern "sysv64" fn pass_zst_sysv64(_: ()) {}
88+
extern "sysv64" fn pass_c_zst_2_sysv64(_: CZst2) {}
4689

4790
// For `extern "C"` functions, ZST are ignored on Linux put passed on Windows.
4891

49-
// linux: define void @pass_zst_c()
50-
// windows-msvc: define void @pass_zst_c(ptr {{[^,]*}})
51-
// windows-gnu: define void @pass_zst_c(ptr {{[^,]*}})
92+
// linux: define void @pass_rust_zst_c()
93+
// windows-msvc: define void @pass_rust_zst_c()
94+
// windows-gnu: define void @pass_rust_zst_c()
95+
#[no_mangle]
96+
extern "C" fn pass_rust_zst_c(_: ()) {}
97+
98+
// linux: define void @pass_c_zst_c()
99+
// windows-msvc: define void @pass_c_zst_c(ptr {{[^,]*}})
100+
// windows-gnu: define void @pass_c_zst_c(ptr {{[^,]*}})
101+
#[no_mangle]
102+
extern "C" fn pass_c_zst_c(_: CZst) {}
103+
104+
// linux: define void @pass_c_zst_2_c()
105+
// windows-msvc: define void @pass_c_zst_2_c(ptr {{[^,]*}})
106+
// windows-gnu: define void @pass_c_zst_2_c(ptr {{[^,]*}})
107+
#[no_mangle]
108+
extern "C" fn pass_c_zst_2_c(_: CZst2) {}
109+
110+
// Now check `repr(C)` return types.
111+
// Again, we seek to match clang: <https://clang.godbolt.org/z/hKv74ThnE>
112+
113+
// CHECK: define win64cc void @ret_c_zst_win64(ptr {{[^,]*}})
114+
#[no_mangle]
115+
extern "win64" fn ret_c_zst_win64() -> CZst {
116+
CZst
117+
}
118+
119+
// CHECK: define x86_vectorcallcc void @ret_c_zst_vectorcall(ptr {{[^,]*}})
120+
#[no_mangle]
121+
extern "vectorcall" fn ret_c_zst_vectorcall() -> CZst {
122+
CZst
123+
}
124+
125+
// windows-gnu: define void @ret_c_zst_fastcall(ptr {{[^,]*}})
126+
// windows-msvc: define void @ret_c_zst_fastcall(ptr {{[^,]*}})
127+
#[no_mangle]
128+
#[cfg(windows)] // "fastcall" is not valid on 64bit Linux
129+
extern "fastcall" fn ret_c_zst_fastcall() -> CZst {
130+
CZst
131+
}
132+
133+
// The sysv64 ABI ignores ZST.
134+
135+
// CHECK: define x86_64_sysvcc void @ret_c_zst_sysv64()
136+
#[no_mangle]
137+
extern "sysv64" fn ret_c_zst_sysv64() -> CZst {
138+
CZst
139+
}
140+
141+
// For `extern "C"` functions, ZST are ignored on Linux put reted on Windows.
142+
143+
// linux: define void @ret_c_zst_c()
144+
// windows-msvc: define void @ret_c_zst_c(ptr {{[^,]*}})
145+
// windows-gnu: define void @ret_c_zst_c(ptr {{[^,]*}})
52146
#[no_mangle]
53-
extern "C" fn pass_zst_c(_: ()) {}
147+
extern "C" fn ret_c_zst_c() -> CZst {
148+
CZst
149+
}

0 commit comments

Comments
 (0)