From ed317e4a479b4bacf102188445d03fabfb81a2c5 Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Sat, 20 May 2023 16:11:09 -0400 Subject: [PATCH] i686-windows: pass arguments with requested alignment > 4 indirectly --- compiler/rustc_middle/src/ty/layout.rs | 9 ++++++ compiler/rustc_target/src/abi/call/x86.rs | 29 ++++++++++++++++--- compiler/rustc_target/src/abi/mod.rs | 8 +++++ compiler/rustc_target/src/lib.rs | 1 + .../extern-fn-struct-passing-abi/test.rs | 6 +++- 5 files changed, 48 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d95b05ef754..dc116e616fa 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1104,6 +1104,15 @@ where fn is_unit(this: TyAndLayout<'tcx>) -> bool { matches!(this.ty.kind(), ty::Tuple(list) if list.len() == 0) } + + fn repr_options(this: TyAndLayout<'tcx>) -> ReprOptions { + match *this.ty.kind() { + ty::Adt(def, ..) => def.repr(), + _ => { + bug!("TyAndLayout::repr_options({:?}): not applicable", this) + } + } + } } /// Calculates whether a function's ABI can unwind or not. diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs index d2c604fafa6..dd52b3cb520 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -54,7 +54,31 @@ where continue; } - if arg.layout.is_aggregate() { + // FIXME: MSVC 2015+ will pass the first 3 vector arguments in [XYZ]MM0-2 + // See https://reviews.llvm.org/D72114 for Clang behavior + + let t = cx.target_spec(); + let align_4 = Align::from_bytes(4).unwrap(); + let align_16 = Align::from_bytes(16).unwrap(); + + if t.is_like_msvc + && arg.layout.is_adt() + && let Some(requested_align) = arg.layout.repr_options().align + && requested_align > align_4 + { + // MSVC has special rules for overaligned arguments: https://reviews.llvm.org/D72114. + // Summarized here: + // - Arguments with _requested_ alignment > 4 are passed indirectly. + // - For backwards compatibility, arguments with natural alignment > 4 are still passed + // on stack (via `byval`). For example, this includes `double`, `int64_t`, + // and structs containing them, provided they lack an explicit alignment attribute. + assert!(arg.layout.align.abi >= requested_align, + "abi alignment {:?} less than requested alignment {:?}", + arg.layout.align.abi, + requested_align + ); + arg.make_indirect(); + } else if arg.layout.is_aggregate() { // We need to compute the alignment of the `byval` argument. The rules can be found in // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized // here, they are: @@ -87,9 +111,6 @@ where } } - let t = cx.target_spec(); - let align_4 = Align::from_bytes(4).unwrap(); - let align_16 = Align::from_bytes(16).unwrap(); let byval_align = if arg.layout.align.abi < align_4 { // (1.) align_4 diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 589cd3cf96b..aecf8b339f0 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -55,6 +55,7 @@ pub trait TyAbiInterface<'a, C>: Sized { fn is_never(this: TyAndLayout<'a, Self>) -> bool; fn is_tuple(this: TyAndLayout<'a, Self>) -> bool; fn is_unit(this: TyAndLayout<'a, Self>) -> bool; + fn repr_options(this: TyAndLayout<'a, Self>) -> ReprOptions; } impl<'a, Ty> TyAndLayout<'a, Ty> { @@ -125,6 +126,13 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::is_unit(self) } + pub fn repr_options(self) -> ReprOptions + where + Ty: TyAbiInterface<'a, C>, + { + Ty::repr_options(self) + } + pub fn offset_of_subfield(self, cx: &C, indices: impl Iterator) -> Size where Ty: TyAbiInterface<'a, C>, diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index a7b54766bc6..3307244a217 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -12,6 +12,7 @@ #![feature(associated_type_bounds)] #![feature(exhaustive_patterns)] #![feature(iter_intersperse)] +#![feature(let_chains)] #![feature(min_specialization)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/tests/run-make/extern-fn-struct-passing-abi/test.rs b/tests/run-make/extern-fn-struct-passing-abi/test.rs index d58254301cc..99e079f98a8 100644 --- a/tests/run-make/extern-fn-struct-passing-abi/test.rs +++ b/tests/run-make/extern-fn-struct-passing-abi/test.rs @@ -89,7 +89,11 @@ extern "C" { fn byval_rect_with_many_huge(a: Huge, b: Huge, c: Huge, d: Huge, e: Huge, f: Huge, g: Rect); - fn byval_rect_with_many_huge64(a: Huge64, b: Huge64, c: Huge64, d: Huge64, e: Huge64, f: Huge64, g: Rect); + fn byval_rect_with_many_huge64( + a: Huge64, b: Huge64, c: Huge64, + d: Huge64, e: Huge64, f: Huge64, + g: Rect, + ); fn split_rect(a: i32, b: i32, s: Rect);