diff options
| author | Josh Stone <jistone@redhat.com> | 2017-08-23 17:38:45 -0400 |
|---|---|---|
| committer | Josh Stone <jistone@redhat.com> | 2017-09-01 18:21:29 -0700 |
| commit | a9bb599fb14fab9a9acee13c590b11a47c392cf6 (patch) | |
| tree | ef67f8dcbfb516516472802cabc7d81aed5b0c28 /src/librustc_trans | |
| parent | f861b6ee46465097eec266c160ac53e230df7cf0 (diff) | |
| download | rust-a9bb599fb14fab9a9acee13c590b11a47c392cf6.tar.gz rust-a9bb599fb14fab9a9acee13c590b11a47c392cf6.zip | |
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
Diffstat (limited to 'src/librustc_trans')
| -rw-r--r-- | src/librustc_trans/cabi_powerpc64.rs | 64 |
1 files changed, 51 insertions, 13 deletions
diff --git a/src/librustc_trans/cabi_powerpc64.rs b/src/librustc_trans/cabi_powerpc64.rs index 5c695387236..fb5472eb6ae 100644 --- a/src/librustc_trans/cabi_powerpc64.rs +++ b/src/librustc_trans/cabi_powerpc64.rs @@ -14,14 +14,26 @@ use abi::{FnType, ArgType, LayoutExt, Reg, RegKind, Uniform}; 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> { arg.layout.homogeneous_aggregate(ccx).and_then(|unit| { let size = arg.layout.size(ccx); - // Ensure we have at most eight uniquely addressable members. - if size > unit.size.checked_mul(8, ccx).unwrap() { + // ELFv1 only passes one-member aggregates transparently. + // ELFv2 passes up to eight uniquely addressable members. + if (abi == ELFv1 && size > unit.size) + || size > unit.size.checked_mul(8, ccx).unwrap() { 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() { ret.extend_integer_width_to(64); return; } - // The PowerPC64 big endian ABI doesn't return aggregates in registers - if ccx.sess().target.target.target_endian == "big" { + // The ELFv1 ABI doesn't return aggregates in registers + if abi == ELFv1 { 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); return; } + let size = ret.layout.size(ccx); let bits = size.bits(); 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); } -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() { arg.extend_integer_width_to(64); 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); 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 { - unit: Reg::i64(), + unit, total }); } 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() { - classify_ret_ty(ccx, &mut fty.ret); + classify_ret_ty(ccx, &mut fty.ret, abi); } for arg in &mut fty.args { if arg.is_ignore() { continue; } - classify_arg_ty(ccx, arg); + classify_arg_ty(ccx, arg, abi); } } |
