mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-09 21:42:44 +00:00
Auto merge of #44066 - cuviper:powerpc64-extern-abi, r=alexcrichton
powerpc64: improve extern struct ABI These fixes all have to do with the 64-bit PowerPC ELF ABI for big-endian targets. The ELF v2 ABI for powerpc64le already worked well. - Return after marking return aggregates indirect. Fixes #42757. - Pass one-member float aggregates as direct argument values. - Aggregate arguments less than 64-bit must be written in the least- significant bits of the parameter space. - Larger aggregates are instead padded at the tail. (i.e. filling MSBs, padding the remaining LSBs.) New tests were also added for the single-float aggregate, and a 3-byte aggregate to check that it's filled into LSBs. Overall, at least these formerly-failing tests now pass on powerpc64: - run-make/extern-fn-struct-passing-abi - run-make/extern-fn-with-packed-struct - run-pass/extern-pass-TwoU16s.rs - run-pass/extern-pass-TwoU8s.rs - run-pass/struct-return.rs
This commit is contained in:
commit
744dd6c1d5
@ -14,14 +14,26 @@
|
|||||||
|
|
||||||
use abi::{FnType, ArgType, LayoutExt, Reg, RegKind, Uniform};
|
use abi::{FnType, ArgType, LayoutExt, Reg, RegKind, Uniform};
|
||||||
use context::CrateContext;
|
use context::CrateContext;
|
||||||
|
use rustc::ty::layout;
|
||||||
|
|
||||||
fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>)
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum ABI {
|
||||||
|
ELFv1, // original ABI used for powerpc64 (big-endian)
|
||||||
|
ELFv2, // newer ABI used for powerpc64le
|
||||||
|
}
|
||||||
|
use self::ABI::*;
|
||||||
|
|
||||||
|
fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
|
arg: &mut ArgType<'tcx>,
|
||||||
|
abi: ABI)
|
||||||
-> Option<Uniform> {
|
-> Option<Uniform> {
|
||||||
arg.layout.homogeneous_aggregate(ccx).and_then(|unit| {
|
arg.layout.homogeneous_aggregate(ccx).and_then(|unit| {
|
||||||
let size = arg.layout.size(ccx);
|
let size = arg.layout.size(ccx);
|
||||||
|
|
||||||
// Ensure we have at most eight uniquely addressable members.
|
// ELFv1 only passes one-member aggregates transparently.
|
||||||
if size > unit.size.checked_mul(8, ccx).unwrap() {
|
// ELFv2 passes up to eight uniquely addressable members.
|
||||||
|
if (abi == ELFv1 && size > unit.size)
|
||||||
|
|| size > unit.size.checked_mul(8, ccx).unwrap() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,21 +54,23 @@ fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut Ar
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
|
fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>, abi: ABI) {
|
||||||
if !ret.layout.is_aggregate() {
|
if !ret.layout.is_aggregate() {
|
||||||
ret.extend_integer_width_to(64);
|
ret.extend_integer_width_to(64);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The PowerPC64 big endian ABI doesn't return aggregates in registers
|
// The ELFv1 ABI doesn't return aggregates in registers
|
||||||
if ccx.sess().target.target.target_endian == "big" {
|
if abi == ELFv1 {
|
||||||
ret.make_indirect(ccx);
|
ret.make_indirect(ccx);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(uniform) = is_homogeneous_aggregate(ccx, ret) {
|
if let Some(uniform) = is_homogeneous_aggregate(ccx, ret, abi) {
|
||||||
ret.cast_to(ccx, uniform);
|
ret.cast_to(ccx, uniform);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = ret.layout.size(ccx);
|
let size = ret.layout.size(ccx);
|
||||||
let bits = size.bits();
|
let bits = size.bits();
|
||||||
if bits <= 128 {
|
if bits <= 128 {
|
||||||
@ -80,31 +94,55 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc
|
|||||||
ret.make_indirect(ccx);
|
ret.make_indirect(ccx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
|
fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>, abi: ABI) {
|
||||||
if !arg.layout.is_aggregate() {
|
if !arg.layout.is_aggregate() {
|
||||||
arg.extend_integer_width_to(64);
|
arg.extend_integer_width_to(64);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(uniform) = is_homogeneous_aggregate(ccx, arg) {
|
if let Some(uniform) = is_homogeneous_aggregate(ccx, arg, abi) {
|
||||||
arg.cast_to(ccx, uniform);
|
arg.cast_to(ccx, uniform);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let total = arg.layout.size(ccx);
|
let size = arg.layout.size(ccx);
|
||||||
|
let (unit, total) = match abi {
|
||||||
|
ELFv1 => {
|
||||||
|
// In ELFv1, aggregates smaller than a doubleword should appear in
|
||||||
|
// the least-significant bits of the parameter doubleword. The rest
|
||||||
|
// should be padded at their tail to fill out multiple doublewords.
|
||||||
|
if size.bits() <= 64 {
|
||||||
|
(Reg { kind: RegKind::Integer, size }, size)
|
||||||
|
} else {
|
||||||
|
let align = layout::Align::from_bits(64, 64).unwrap();
|
||||||
|
(Reg::i64(), size.abi_align(align))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ELFv2 => {
|
||||||
|
// In ELFv2, we can just cast directly.
|
||||||
|
(Reg::i64(), size)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
arg.cast_to(ccx, Uniform {
|
arg.cast_to(ccx, Uniform {
|
||||||
unit: Reg::i64(),
|
unit,
|
||||||
total
|
total
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
|
pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
|
||||||
|
let abi = match ccx.sess().target.target.target_endian.as_str() {
|
||||||
|
"big" => ELFv1,
|
||||||
|
"little" => ELFv2,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
if !fty.ret.is_ignore() {
|
if !fty.ret.is_ignore() {
|
||||||
classify_ret_ty(ccx, &mut fty.ret);
|
classify_ret_ty(ccx, &mut fty.ret, abi);
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in &mut fty.args {
|
for arg in &mut fty.args {
|
||||||
if arg.is_ignore() { continue; }
|
if arg.is_ignore() { continue; }
|
||||||
classify_arg_ty(ccx, arg);
|
classify_arg_ty(ccx, arg, abi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,30 @@
|
|||||||
use abi::{ArgAttribute, FnType, LayoutExt, Reg, RegKind};
|
use abi::{ArgAttribute, FnType, LayoutExt, Reg, RegKind};
|
||||||
use common::CrateContext;
|
use common::CrateContext;
|
||||||
|
|
||||||
|
use rustc::ty::layout::{self, Layout, TyLayout};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum Flavor {
|
pub enum Flavor {
|
||||||
General,
|
General,
|
||||||
Fastcall
|
Fastcall
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_single_fp_element<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
|
layout: TyLayout<'tcx>) -> bool {
|
||||||
|
match *layout {
|
||||||
|
Layout::Scalar { value: layout::F32, .. } |
|
||||||
|
Layout::Scalar { value: layout::F64, .. } => true,
|
||||||
|
Layout::Univariant { .. } => {
|
||||||
|
if layout.field_count() == 1 {
|
||||||
|
is_single_fp_element(ccx, layout.field(ccx, 0))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
fty: &mut FnType<'tcx>,
|
fty: &mut FnType<'tcx>,
|
||||||
flavor: Flavor) {
|
flavor: Flavor) {
|
||||||
@ -33,12 +51,23 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
|||||||
if t.options.is_like_osx || t.options.is_like_windows
|
if t.options.is_like_osx || t.options.is_like_windows
|
||||||
|| t.options.is_like_openbsd {
|
|| t.options.is_like_openbsd {
|
||||||
let size = fty.ret.layout.size(ccx);
|
let size = fty.ret.layout.size(ccx);
|
||||||
match size.bytes() {
|
|
||||||
1 => fty.ret.cast_to(ccx, Reg::i8()),
|
// According to Clang, everyone but MSVC returns single-element
|
||||||
2 => fty.ret.cast_to(ccx, Reg::i16()),
|
// float aggregates directly in a floating-point register.
|
||||||
4 => fty.ret.cast_to(ccx, Reg::i32()),
|
if !t.options.is_like_msvc && is_single_fp_element(ccx, fty.ret.layout) {
|
||||||
8 => fty.ret.cast_to(ccx, Reg::i64()),
|
match size.bytes() {
|
||||||
_ => fty.ret.make_indirect(ccx)
|
4 => fty.ret.cast_to(ccx, Reg::f32()),
|
||||||
|
8 => fty.ret.cast_to(ccx, Reg::f64()),
|
||||||
|
_ => fty.ret.make_indirect(ccx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match size.bytes() {
|
||||||
|
1 => fty.ret.cast_to(ccx, Reg::i8()),
|
||||||
|
2 => fty.ret.cast_to(ccx, Reg::i16()),
|
||||||
|
4 => fty.ret.cast_to(ccx, Reg::i32()),
|
||||||
|
8 => fty.ret.cast_to(ccx, Reg::i64()),
|
||||||
|
_ => fty.ret.make_indirect(ccx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fty.ret.make_indirect(ccx);
|
fty.ret.make_indirect(ccx);
|
||||||
|
@ -43,6 +43,16 @@ struct FloatPoint {
|
|||||||
double y;
|
double y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FloatOne {
|
||||||
|
double x;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntOdd {
|
||||||
|
int8_t a;
|
||||||
|
int8_t b;
|
||||||
|
int8_t c;
|
||||||
|
};
|
||||||
|
|
||||||
// System V x86_64 ABI:
|
// System V x86_64 ABI:
|
||||||
// a, b, c, d, e should be in registers
|
// a, b, c, d, e should be in registers
|
||||||
// s should be byval pointer
|
// s should be byval pointer
|
||||||
@ -283,7 +293,7 @@ struct Huge huge_struct(struct Huge s) {
|
|||||||
// p should be in registers
|
// p should be in registers
|
||||||
// return should be in registers
|
// return should be in registers
|
||||||
//
|
//
|
||||||
// Win64 ABI:
|
// Win64 ABI and 64-bit PowerPC ELFv1 ABI:
|
||||||
// p should be a byval pointer
|
// p should be a byval pointer
|
||||||
// return should be in a hidden sret pointer
|
// return should be in a hidden sret pointer
|
||||||
struct FloatPoint float_point(struct FloatPoint p) {
|
struct FloatPoint float_point(struct FloatPoint p) {
|
||||||
@ -292,3 +302,23 @@ struct FloatPoint float_point(struct FloatPoint p) {
|
|||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 64-bit PowerPC ELFv1 ABI:
|
||||||
|
// f1 should be in a register
|
||||||
|
// return should be in a hidden sret pointer
|
||||||
|
struct FloatOne float_one(struct FloatOne f1) {
|
||||||
|
assert(f1.x == 7.);
|
||||||
|
|
||||||
|
return f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 64-bit PowerPC ELFv1 ABI:
|
||||||
|
// i should be in the least-significant bits of a register
|
||||||
|
// return should be in a hidden sret pointer
|
||||||
|
struct IntOdd int_odd(struct IntOdd i) {
|
||||||
|
assert(i.a == 1);
|
||||||
|
assert(i.b == 2);
|
||||||
|
assert(i.c == 3);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
@ -53,6 +53,20 @@ struct FloatPoint {
|
|||||||
y: f64
|
y: f64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct FloatOne {
|
||||||
|
x: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct IntOdd {
|
||||||
|
a: i8,
|
||||||
|
b: i8,
|
||||||
|
c: i8,
|
||||||
|
}
|
||||||
|
|
||||||
#[link(name = "test", kind = "static")]
|
#[link(name = "test", kind = "static")]
|
||||||
extern {
|
extern {
|
||||||
fn byval_rect(a: i32, b: i32, c: i32, d: i32, e: i32, s: Rect);
|
fn byval_rect(a: i32, b: i32, c: i32, d: i32, e: i32, s: Rect);
|
||||||
@ -83,6 +97,10 @@ extern {
|
|||||||
fn huge_struct(s: Huge) -> Huge;
|
fn huge_struct(s: Huge) -> Huge;
|
||||||
|
|
||||||
fn float_point(p: FloatPoint) -> FloatPoint;
|
fn float_point(p: FloatPoint) -> FloatPoint;
|
||||||
|
|
||||||
|
fn float_one(f: FloatOne) -> FloatOne;
|
||||||
|
|
||||||
|
fn int_odd(i: IntOdd) -> IntOdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -91,6 +109,8 @@ fn main() {
|
|||||||
let u = FloatRect { a: 3489, b: 3490, c: 8. };
|
let u = FloatRect { a: 3489, b: 3490, c: 8. };
|
||||||
let v = Huge { a: 5647, b: 5648, c: 5649, d: 5650, e: 5651 };
|
let v = Huge { a: 5647, b: 5648, c: 5649, d: 5650, e: 5651 };
|
||||||
let p = FloatPoint { x: 5., y: -3. };
|
let p = FloatPoint { x: 5., y: -3. };
|
||||||
|
let f1 = FloatOne { x: 7. };
|
||||||
|
let i = IntOdd { a: 1, b: 2, c: 3 };
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
byval_rect(1, 2, 3, 4, 5, s);
|
byval_rect(1, 2, 3, 4, 5, s);
|
||||||
@ -113,5 +133,12 @@ fn main() {
|
|||||||
assert_eq!(sret_byval_struct(1, 2, 3, 4, s), t);
|
assert_eq!(sret_byval_struct(1, 2, 3, 4, s), t);
|
||||||
assert_eq!(sret_split_struct(1, 2, s), t);
|
assert_eq!(sret_split_struct(1, 2, s), t);
|
||||||
assert_eq!(float_point(p), p);
|
assert_eq!(float_point(p), p);
|
||||||
|
assert_eq!(int_odd(i), i);
|
||||||
|
|
||||||
|
// MSVC/GCC/Clang are not consistent in the ABI of single-float aggregates.
|
||||||
|
// x86_64: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82028
|
||||||
|
// i686: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82041
|
||||||
|
#[cfg(not(all(windows, target_env = "gnu")))]
|
||||||
|
assert_eq!(float_one(f1), f1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// ignore-license
|
// ignore-license
|
||||||
// Pragma needed cause of gcc bug on windows: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991
|
// Pragma needed cause of gcc bug on windows: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma pack(push,1)
|
#pragma pack(push,1)
|
||||||
struct Foo {
|
struct Foo {
|
||||||
@ -18,5 +20,8 @@ struct __attribute__((packed)) Foo {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct Foo foo(struct Foo foo) {
|
struct Foo foo(struct Foo foo) {
|
||||||
|
assert(foo.a == 1);
|
||||||
|
assert(foo.b == 2);
|
||||||
|
assert(foo.c == 3);
|
||||||
return foo;
|
return foo;
|
||||||
}
|
}
|
||||||
|
@ -8,36 +8,14 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use std::fmt;
|
#[repr(C, packed)]
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
#[repr(packed)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
a: i8,
|
a: i8,
|
||||||
b: i16,
|
b: i16,
|
||||||
c: i8
|
c: i8
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Foo {
|
|
||||||
fn eq(&self, other: &Foo) -> bool {
|
|
||||||
self.a == other.a && self.b == other.b && self.c == other.c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Foo {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let a = self.a;
|
|
||||||
let b = self.b;
|
|
||||||
let c = self.c;
|
|
||||||
|
|
||||||
f.debug_struct("Foo")
|
|
||||||
.field("a", &a)
|
|
||||||
.field("b", &b)
|
|
||||||
.field("c", &c)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[link(name = "test", kind = "static")]
|
#[link(name = "test", kind = "static")]
|
||||||
extern {
|
extern {
|
||||||
fn foo(f: Foo) -> Foo;
|
fn foo(f: Foo) -> Foo;
|
||||||
|
Loading…
Reference in New Issue
Block a user