implement vector-containing aggregate alignment for x86 darwin

This commit is contained in:
Erik Desjardins 2023-05-15 00:49:12 -04:00
parent be1d4e3e0b
commit a07eb0abbd
2 changed files with 109 additions and 31 deletions

View File

@ -1,5 +1,5 @@
use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind}; use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind};
use crate::abi::{Align, HasDataLayout, TyAbiInterface}; use crate::abi::{Abi, Align, HasDataLayout, TyAbiInterface, TyAndLayout};
use crate::spec::HasTargetSpec; use crate::spec::HasTargetSpec;
#[derive(PartialEq)] #[derive(PartialEq)]
@ -53,38 +53,58 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: F
if arg.is_ignore() { if arg.is_ignore() {
continue; continue;
} }
if !arg.layout.is_aggregate() {
arg.extend_integer_width_to(32);
continue;
}
// We need to compute the alignment of the `byval` argument. The rules can be found in if arg.layout.is_aggregate() {
// `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized here, // We need to compute the alignment of the `byval` argument. The rules can be found in
// they are: // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized
// // here, they are:
// 1. If the natural alignment of the type is less than or equal to 4, the alignment is 4. //
// // 1. If the natural alignment of the type is <= 4, the alignment is 4.
// 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment. //
// (This doesn't matter here because we ensure we have an aggregate with the check above.) // 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment.
// // This doesn't matter here because we only pass aggregates via `byval`, not vectors.
// 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector type //
// is 16. // 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector
// // type is 16.
// 4. If none of these conditions are true, the alignment is 4. //
let t = cx.target_spec(); // 4. If none of these conditions are true, the alignment is 4.
let align_4 = Align::from_bytes(4).unwrap();
let align_16 = Align::from_bytes(16).unwrap(); fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool
let byval_align = if arg.layout.align.abi < align_4 { where
align_4 Ty: TyAbiInterface<'a, C> + Copy,
} else if t.is_like_osx && arg.layout.align.abi >= align_16 { {
// FIXME(pcwalton): This is dubious--we should actually be looking inside the type to match layout.abi {
// determine if it contains SIMD vector values--but I think it's fine? Abi::Uninhabited | Abi::Scalar(_) | Abi::ScalarPair(..) => false,
align_16 Abi::Vector { .. } => true,
Abi::Aggregate { .. } => {
for i in 0..layout.fields.count() {
if contains_vector(cx, layout.field(cx, i)) {
return true;
}
}
false
}
}
}
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
} else if t.is_like_osx && contains_vector(cx, arg.layout) {
// (3.)
align_16
} else {
// (4.)
align_4
};
arg.make_indirect_byval(Some(byval_align));
} else { } else {
align_4 arg.extend_integer_width_to(32);
}; }
arg.make_indirect_byval(Some(byval_align));
} }
if flavor == Flavor::FastcallOrVectorcall { if flavor == Flavor::FastcallOrVectorcall {

View File

@ -0,0 +1,58 @@
// revisions:x86-linux x86-darwin
//[x86-linux] compile-flags: --target i686-unknown-linux-gnu
//[x86-linux] needs-llvm-components: x86
//[x86-darwin] compile-flags: --target i686-apple-darwin
//[x86-darwin] needs-llvm-components: x86
// Tests that aggregates containing vector types get their alignment increased to 16 on Darwin.
#![feature(no_core, lang_items, repr_simd, simd_ffi)]
#![crate_type = "lib"]
#![no_std]
#![no_core]
#![allow(non_camel_case_types)]
#[lang = "sized"]
trait Sized {}
#[lang = "freeze"]
trait Freeze {}
#[lang = "copy"]
trait Copy {}
#[repr(simd)]
pub struct i32x4(i32, i32, i32, i32);
#[repr(C)]
pub struct Foo {
a: i32x4,
b: i8,
}
// This tests that we recursively check for vector types, not just at the top level.
#[repr(C)]
pub struct DoubleFoo {
one: Foo,
two: Foo,
}
extern "C" {
// x86-linux: declare void @f({{.*}}byval(%Foo) align 4{{.*}})
// x86-darwin: declare void @f({{.*}}byval(%Foo) align 16{{.*}})
fn f(foo: Foo);
// x86-linux: declare void @g({{.*}}byval(%DoubleFoo) align 4{{.*}})
// x86-darwin: declare void @g({{.*}}byval(%DoubleFoo) align 16{{.*}})
fn g(foo: DoubleFoo);
}
pub fn main() {
unsafe { f(Foo { a: i32x4(1, 2, 3, 4), b: 0 }) }
unsafe {
g(DoubleFoo {
one: Foo { a: i32x4(1, 2, 3, 4), b: 0 },
two: Foo { a: i32x4(1, 2, 3, 4), b: 0 },
})
}
}