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::{Align, HasDataLayout, TyAbiInterface};
use crate::abi::{Abi, Align, HasDataLayout, TyAbiInterface, TyAndLayout};
use crate::spec::HasTargetSpec;
#[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() {
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
// `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.
//
// 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.)
//
// 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();
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 {
align_4
} 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
// determine if it contains SIMD vector values--but I think it's fine?
align_16
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:
//
// 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 only pass aggregates via `byval`, not vectors.
//
// 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.
fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
match layout.abi {
Abi::Uninhabited | Abi::Scalar(_) | Abi::ScalarPair(..) => false,
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 {
align_4
};
arg.make_indirect_byval(Some(byval_align));
arg.extend_integer_width_to(32);
}
}
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 },
})
}
}