Skip to content

Commit 7a202a9

Browse files
committed
Auto merge of rust-lang#135204 - RalfJung:win64-zst, r=SparrowLii
fix handling of ZST in win64 ABI on windows-msvc targets The Microsoft calling conventions do not really say anything about ZST since they do not seem to exist in MSVC. However, both GCC and clang allow passing ZST over `__attribute__((ms_abi))` functions (which matches our `extern "win64" fn`) on `windows-gnu` targets, and therefore implicitly define a de-facto ABI for these types (and lucky enough they seem to define the same ABI). This ABI should be the same for windows-msvc and windows-gnu targets, so we use this as a hint for how to implement this ABI everywhere: we always pass ZST by-ref. The best alternative would be to just reject compiling functions which cannot exist in MSVC, but that would be a breaking change. Cc `@programmerjake` `@ChrisDenton` Fixes rust-lang#132893
2 parents 3ff1b64 + 675a103 commit 7a202a9

File tree

9 files changed

+91
-271
lines changed

9 files changed

+91
-271
lines changed

compiler/rustc_abi/src/extern_abi/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ pub fn is_enabled(
192192
s
193193
}
194194

195+
/// Returns whether the ABI is stable to use.
196+
///
197+
/// Note that there is a separate check determining whether the ABI is even supported
198+
/// on the current target; see `fn is_abi_supported` in `rustc_target::spec`.
195199
pub fn is_stable(name: &str) -> Result<(), AbiDisabled> {
196200
match name {
197201
// Stable

compiler/rustc_target/src/callconv/mod.rs

+14-15
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
use std::str::FromStr;
22
use std::{fmt, iter};
33

4-
pub use rustc_abi::{Reg, RegKind};
4+
pub use rustc_abi::{ExternAbi, Reg, RegKind};
55
use rustc_macros::HashStable_Generic;
66
use rustc_span::Symbol;
77

88
use crate::abi::{
99
self, AddressSpace, Align, BackendRepr, HasDataLayout, Pointer, Size, TyAbiInterface,
1010
TyAndLayout,
1111
};
12-
use crate::spec::abi::Abi as SpecAbi;
13-
use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi};
12+
use crate::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi};
1413

1514
mod aarch64;
1615
mod amdgpu;
@@ -627,20 +626,20 @@ impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> {
627626
#[derive(Copy, Clone, Debug, HashStable_Generic)]
628627
pub enum AdjustForForeignAbiError {
629628
/// Target architecture doesn't support "foreign" (i.e. non-Rust) ABIs.
630-
Unsupported { arch: Symbol, abi: spec::abi::Abi },
629+
Unsupported { arch: Symbol, abi: ExternAbi },
631630
}
632631

633632
impl<'a, Ty> FnAbi<'a, Ty> {
634633
pub fn adjust_for_foreign_abi<C>(
635634
&mut self,
636635
cx: &C,
637-
abi: spec::abi::Abi,
636+
abi: ExternAbi,
638637
) -> Result<(), AdjustForForeignAbiError>
639638
where
640639
Ty: TyAbiInterface<'a, C> + Copy,
641640
C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt + HasX86AbiOpt,
642641
{
643-
if abi == spec::abi::Abi::X86Interrupt {
642+
if abi == ExternAbi::X86Interrupt {
644643
if let Some(arg) = self.args.first_mut() {
645644
arg.pass_by_stack_offset(None);
646645
}
@@ -651,12 +650,10 @@ impl<'a, Ty> FnAbi<'a, Ty> {
651650
match &spec.arch[..] {
652651
"x86" => {
653652
let (flavor, regparm) = match abi {
654-
spec::abi::Abi::Fastcall { .. } | spec::abi::Abi::Vectorcall { .. } => {
653+
ExternAbi::Fastcall { .. } | ExternAbi::Vectorcall { .. } => {
655654
(x86::Flavor::FastcallOrVectorcall, None)
656655
}
657-
spec::abi::Abi::C { .. }
658-
| spec::abi::Abi::Cdecl { .. }
659-
| spec::abi::Abi::Stdcall { .. } => {
656+
ExternAbi::C { .. } | ExternAbi::Cdecl { .. } | ExternAbi::Stdcall { .. } => {
660657
(x86::Flavor::General, cx.x86_abi_opt().regparm)
661658
}
662659
_ => (x86::Flavor::General, None),
@@ -666,8 +663,10 @@ impl<'a, Ty> FnAbi<'a, Ty> {
666663
x86::compute_abi_info(cx, self, opts);
667664
}
668665
"x86_64" => match abi {
669-
spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self),
670-
spec::abi::Abi::Win64 { .. } => x86_win64::compute_abi_info(cx, self),
666+
ExternAbi::SysV64 { .. } => x86_64::compute_abi_info(cx, self),
667+
ExternAbi::Win64 { .. } | ExternAbi::Vectorcall { .. } => {
668+
x86_win64::compute_abi_info(cx, self)
669+
}
671670
_ => {
672671
if cx.target_spec().is_like_windows {
673672
x86_win64::compute_abi_info(cx, self)
@@ -701,7 +700,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
701700
"sparc" => sparc::compute_abi_info(cx, self),
702701
"sparc64" => sparc64::compute_abi_info(cx, self),
703702
"nvptx64" => {
704-
if cx.target_spec().adjust_abi(abi, self.c_variadic) == spec::abi::Abi::PtxKernel {
703+
if cx.target_spec().adjust_abi(abi, self.c_variadic) == ExternAbi::PtxKernel {
705704
nvptx64::compute_ptx_kernel_abi_info(cx, self)
706705
} else {
707706
nvptx64::compute_abi_info(self)
@@ -730,7 +729,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
730729
Ok(())
731730
}
732731

733-
pub fn adjust_for_rust_abi<C>(&mut self, cx: &C, abi: SpecAbi)
732+
pub fn adjust_for_rust_abi<C>(&mut self, cx: &C, abi: ExternAbi)
734733
where
735734
Ty: TyAbiInterface<'a, C> + Copy,
736735
C: HasDataLayout + HasTargetSpec,
@@ -821,7 +820,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
821820
// that's how we connect up to LLVM and it's unstable
822821
// anyway, we control all calls to it in libstd.
823822
BackendRepr::Vector { .. }
824-
if abi != SpecAbi::RustIntrinsic && spec.simd_types_indirect =>
823+
if abi != ExternAbi::RustIntrinsic && spec.simd_types_indirect =>
825824
{
826825
arg.make_indirect();
827826
continue;

compiler/rustc_target/src/callconv/x86_win64.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::spec::HasTargetSpec;
55

66
// Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing
77

8-
pub(crate) fn compute_abi_info<Ty>(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) {
8+
pub(crate) fn compute_abi_info<Ty>(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) {
99
let fixup = |a: &mut ArgAbi<'_, Ty>| {
1010
match a.layout.backend_repr {
1111
BackendRepr::Uninhabited | BackendRepr::Memory { sized: false } => {}
@@ -40,16 +40,18 @@ pub(crate) fn compute_abi_info<Ty>(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'
4040
fixup(&mut fn_abi.ret);
4141
}
4242
for arg in fn_abi.args.iter_mut() {
43-
if arg.is_ignore() {
44-
// x86_64-pc-windows-gnu doesn't ignore ZSTs.
45-
if cx.target_spec().os == "windows"
46-
&& cx.target_spec().env == "gnu"
47-
&& arg.layout.is_zst()
48-
{
49-
arg.make_indirect_from_ignore();
50-
}
43+
if arg.is_ignore() && arg.layout.is_zst() {
44+
// Windows ABIs do not talk about ZST since such types do not exist in MSVC.
45+
// In that sense we can do whatever we want here, and maybe we should throw an error
46+
// (but of course that would be a massive breaking change now).
47+
// We try to match clang and gcc (which allow ZST is their windows-gnu targets), so we
48+
// pass ZST via pointer indirection.
49+
arg.make_indirect_from_ignore();
5150
continue;
5251
}
5352
fixup(arg);
5453
}
54+
// FIXME: We should likely also do something about ZST return types, similar to above.
55+
// However, that's non-trivial due to `()`.
56+
// See <https://github.com/rust-lang/unsafe-code-guidelines/issues/552>.
5557
}

compiler/rustc_target/src/spec/mod.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -2815,12 +2815,17 @@ impl Target {
28152815
Abi::EfiApi if self.arch == "x86_64" => Abi::Win64 { unwind: false },
28162816
Abi::EfiApi => Abi::C { unwind: false },
28172817

2818-
// See commentary in `is_abi_supported`.
2819-
Abi::Stdcall { .. } | Abi::Thiscall { .. } if self.arch == "x86" => abi,
2820-
Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => Abi::C { unwind },
2821-
Abi::Fastcall { .. } if self.arch == "x86" => abi,
2818+
// See commentary in `is_abi_supported`: we map these ABIs to "C" when they do not make sense.
2819+
Abi::Stdcall { .. } | Abi::Thiscall { .. } | Abi::Fastcall { .. }
2820+
if self.arch == "x86" =>
2821+
{
2822+
abi
2823+
}
28222824
Abi::Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => abi,
2823-
Abi::Fastcall { unwind } | Abi::Vectorcall { unwind } => Abi::C { unwind },
2825+
Abi::Stdcall { unwind }
2826+
| Abi::Thiscall { unwind }
2827+
| Abi::Fastcall { unwind }
2828+
| Abi::Vectorcall { unwind } => Abi::C { unwind },
28242829

28252830
// The Windows x64 calling convention we use for `extern "Rust"`
28262831
// <https://learn.microsoft.com/en-us/cpp/build/x64-software-conventions#register-volatility-and-preservation>

tests/codegen/abi-win64-zst.rs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//@ compile-flags: -Z merge-functions=disabled
2+
3+
//@ revisions: windows-gnu
4+
//@[windows-gnu] compile-flags: --target x86_64-pc-windows-gnu
5+
//@[windows-gnu] needs-llvm-components: x86
6+
7+
//@ revisions: windows-msvc
8+
//@[windows-msvc] compile-flags: --target x86_64-pc-windows-msvc
9+
//@[windows-msvc] needs-llvm-components: x86
10+
11+
// Also test what happens when using a Windows ABI on Linux.
12+
//@ revisions: linux
13+
//@[linux] compile-flags: --target x86_64-unknown-linux-gnu
14+
//@[linux] needs-llvm-components: x86
15+
16+
#![feature(no_core, lang_items, rustc_attrs, abi_vectorcall)]
17+
#![no_core]
18+
#![crate_type = "lib"]
19+
20+
#[lang = "sized"]
21+
trait Sized {}
22+
23+
// Make sure the argument is always passed when explicitly requesting a Windows ABI.
24+
// Our goal here is to match clang: <https://clang.godbolt.org/z/Wr4jMWq3P>.
25+
26+
// CHECK: define win64cc void @pass_zst_win64(ptr {{[^,]*}})
27+
#[no_mangle]
28+
extern "win64" fn pass_zst_win64(_: ()) {}
29+
30+
// CHECK: define x86_vectorcallcc void @pass_zst_vectorcall(ptr {{[^,]*}})
31+
#[no_mangle]
32+
extern "vectorcall" fn pass_zst_vectorcall(_: ()) {}
33+
34+
// windows-gnu: define void @pass_zst_fastcall(ptr {{[^,]*}})
35+
// windows-msvc: define void @pass_zst_fastcall(ptr {{[^,]*}})
36+
#[no_mangle]
37+
#[cfg(windows)] // "fastcall" is not valid on 64bit Linux
38+
extern "fastcall" fn pass_zst_fastcall(_: ()) {}
39+
40+
// The sysv64 ABI ignores ZST.
41+
42+
// CHECK: define x86_64_sysvcc void @pass_zst_sysv64()
43+
#[no_mangle]
44+
extern "sysv64" fn pass_zst_sysv64(_: ()) {}
45+
46+
// For `extern "C"` functions, ZST are ignored on Linux put passed on Windows.
47+
48+
// linux: define void @pass_zst_c()
49+
// windows-msvc: define void @pass_zst_c(ptr {{[^,]*}})
50+
// windows-gnu: define void @pass_zst_c(ptr {{[^,]*}})
51+
#[no_mangle]
52+
extern "C" fn pass_zst_c(_: ()) {}

tests/ui/abi/win64-zst.rs

-24
This file was deleted.

tests/ui/abi/win64-zst.x86_64-linux.stderr

-69
This file was deleted.

tests/ui/abi/win64-zst.x86_64-windows-gnu.stderr

-80
This file was deleted.

0 commit comments

Comments
 (0)