Skip to content

Commit 744dd6c

Browse files
committed
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
2 parents 204c0a4 + 40b1473 commit 744dd6c

File tree

6 files changed

+151
-44
lines changed

6 files changed

+151
-44
lines changed

src/librustc_trans/cabi_powerpc64.rs

+51-13
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,26 @@
1414

1515
use abi::{FnType, ArgType, LayoutExt, Reg, RegKind, Uniform};
1616
use context::CrateContext;
17+
use rustc::ty::layout;
1718

18-
fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>)
19+
#[derive(Debug, Clone, Copy, PartialEq)]
20+
enum ABI {
21+
ELFv1, // original ABI used for powerpc64 (big-endian)
22+
ELFv2, // newer ABI used for powerpc64le
23+
}
24+
use self::ABI::*;
25+
26+
fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
27+
arg: &mut ArgType<'tcx>,
28+
abi: ABI)
1929
-> Option<Uniform> {
2030
arg.layout.homogeneous_aggregate(ccx).and_then(|unit| {
2131
let size = arg.layout.size(ccx);
2232

23-
// Ensure we have at most eight uniquely addressable members.
24-
if size > unit.size.checked_mul(8, ccx).unwrap() {
33+
// ELFv1 only passes one-member aggregates transparently.
34+
// ELFv2 passes up to eight uniquely addressable members.
35+
if (abi == ELFv1 && size > unit.size)
36+
|| size > unit.size.checked_mul(8, ccx).unwrap() {
2537
return None;
2638
}
2739

@@ -42,21 +54,23 @@ fn is_homogeneous_aggregate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut Ar
4254
})
4355
}
4456

45-
fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
57+
fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tcx>, abi: ABI) {
4658
if !ret.layout.is_aggregate() {
4759
ret.extend_integer_width_to(64);
4860
return;
4961
}
5062

51-
// The PowerPC64 big endian ABI doesn't return aggregates in registers
52-
if ccx.sess().target.target.target_endian == "big" {
63+
// The ELFv1 ABI doesn't return aggregates in registers
64+
if abi == ELFv1 {
5365
ret.make_indirect(ccx);
66+
return;
5467
}
5568

56-
if let Some(uniform) = is_homogeneous_aggregate(ccx, ret) {
69+
if let Some(uniform) = is_homogeneous_aggregate(ccx, ret, abi) {
5770
ret.cast_to(ccx, uniform);
5871
return;
5972
}
73+
6074
let size = ret.layout.size(ccx);
6175
let bits = size.bits();
6276
if bits <= 128 {
@@ -80,31 +94,55 @@ fn classify_ret_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ret: &mut ArgType<'tc
8094
ret.make_indirect(ccx);
8195
}
8296

83-
fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
97+
fn classify_arg_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, arg: &mut ArgType<'tcx>, abi: ABI) {
8498
if !arg.layout.is_aggregate() {
8599
arg.extend_integer_width_to(64);
86100
return;
87101
}
88102

89-
if let Some(uniform) = is_homogeneous_aggregate(ccx, arg) {
103+
if let Some(uniform) = is_homogeneous_aggregate(ccx, arg, abi) {
90104
arg.cast_to(ccx, uniform);
91105
return;
92106
}
93107

94-
let total = arg.layout.size(ccx);
108+
let size = arg.layout.size(ccx);
109+
let (unit, total) = match abi {
110+
ELFv1 => {
111+
// In ELFv1, aggregates smaller than a doubleword should appear in
112+
// the least-significant bits of the parameter doubleword. The rest
113+
// should be padded at their tail to fill out multiple doublewords.
114+
if size.bits() <= 64 {
115+
(Reg { kind: RegKind::Integer, size }, size)
116+
} else {
117+
let align = layout::Align::from_bits(64, 64).unwrap();
118+
(Reg::i64(), size.abi_align(align))
119+
}
120+
},
121+
ELFv2 => {
122+
// In ELFv2, we can just cast directly.
123+
(Reg::i64(), size)
124+
},
125+
};
126+
95127
arg.cast_to(ccx, Uniform {
96-
unit: Reg::i64(),
128+
unit,
97129
total
98130
});
99131
}
100132

101133
pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fty: &mut FnType<'tcx>) {
134+
let abi = match ccx.sess().target.target.target_endian.as_str() {
135+
"big" => ELFv1,
136+
"little" => ELFv2,
137+
_ => unimplemented!(),
138+
};
139+
102140
if !fty.ret.is_ignore() {
103-
classify_ret_ty(ccx, &mut fty.ret);
141+
classify_ret_ty(ccx, &mut fty.ret, abi);
104142
}
105143

106144
for arg in &mut fty.args {
107145
if arg.is_ignore() { continue; }
108-
classify_arg_ty(ccx, arg);
146+
classify_arg_ty(ccx, arg, abi);
109147
}
110148
}

src/librustc_trans/cabi_x86.rs

+35-6
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,30 @@
1111
use abi::{ArgAttribute, FnType, LayoutExt, Reg, RegKind};
1212
use common::CrateContext;
1313

14+
use rustc::ty::layout::{self, Layout, TyLayout};
15+
1416
#[derive(PartialEq)]
1517
pub enum Flavor {
1618
General,
1719
Fastcall
1820
}
1921

22+
fn is_single_fp_element<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
23+
layout: TyLayout<'tcx>) -> bool {
24+
match *layout {
25+
Layout::Scalar { value: layout::F32, .. } |
26+
Layout::Scalar { value: layout::F64, .. } => true,
27+
Layout::Univariant { .. } => {
28+
if layout.field_count() == 1 {
29+
is_single_fp_element(ccx, layout.field(ccx, 0))
30+
} else {
31+
false
32+
}
33+
}
34+
_ => false
35+
}
36+
}
37+
2038
pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
2139
fty: &mut FnType<'tcx>,
2240
flavor: Flavor) {
@@ -33,12 +51,23 @@ pub fn compute_abi_info<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
3351
if t.options.is_like_osx || t.options.is_like_windows
3452
|| t.options.is_like_openbsd {
3553
let size = fty.ret.layout.size(ccx);
36-
match size.bytes() {
37-
1 => fty.ret.cast_to(ccx, Reg::i8()),
38-
2 => fty.ret.cast_to(ccx, Reg::i16()),
39-
4 => fty.ret.cast_to(ccx, Reg::i32()),
40-
8 => fty.ret.cast_to(ccx, Reg::i64()),
41-
_ => fty.ret.make_indirect(ccx)
54+
55+
// According to Clang, everyone but MSVC returns single-element
56+
// float aggregates directly in a floating-point register.
57+
if !t.options.is_like_msvc && is_single_fp_element(ccx, fty.ret.layout) {
58+
match size.bytes() {
59+
4 => fty.ret.cast_to(ccx, Reg::f32()),
60+
8 => fty.ret.cast_to(ccx, Reg::f64()),
61+
_ => fty.ret.make_indirect(ccx)
62+
}
63+
} else {
64+
match size.bytes() {
65+
1 => fty.ret.cast_to(ccx, Reg::i8()),
66+
2 => fty.ret.cast_to(ccx, Reg::i16()),
67+
4 => fty.ret.cast_to(ccx, Reg::i32()),
68+
8 => fty.ret.cast_to(ccx, Reg::i64()),
69+
_ => fty.ret.make_indirect(ccx)
70+
}
4271
}
4372
} else {
4473
fty.ret.make_indirect(ccx);

src/test/run-make/extern-fn-struct-passing-abi/test.c

+31-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ struct FloatPoint {
4343
double y;
4444
};
4545

46+
struct FloatOne {
47+
double x;
48+
};
49+
50+
struct IntOdd {
51+
int8_t a;
52+
int8_t b;
53+
int8_t c;
54+
};
55+
4656
// System V x86_64 ABI:
4757
// a, b, c, d, e should be in registers
4858
// s should be byval pointer
@@ -283,7 +293,7 @@ struct Huge huge_struct(struct Huge s) {
283293
// p should be in registers
284294
// return should be in registers
285295
//
286-
// Win64 ABI:
296+
// Win64 ABI and 64-bit PowerPC ELFv1 ABI:
287297
// p should be a byval pointer
288298
// return should be in a hidden sret pointer
289299
struct FloatPoint float_point(struct FloatPoint p) {
@@ -292,3 +302,23 @@ struct FloatPoint float_point(struct FloatPoint p) {
292302

293303
return p;
294304
}
305+
306+
// 64-bit PowerPC ELFv1 ABI:
307+
// f1 should be in a register
308+
// return should be in a hidden sret pointer
309+
struct FloatOne float_one(struct FloatOne f1) {
310+
assert(f1.x == 7.);
311+
312+
return f1;
313+
}
314+
315+
// 64-bit PowerPC ELFv1 ABI:
316+
// i should be in the least-significant bits of a register
317+
// return should be in a hidden sret pointer
318+
struct IntOdd int_odd(struct IntOdd i) {
319+
assert(i.a == 1);
320+
assert(i.b == 2);
321+
assert(i.c == 3);
322+
323+
return i;
324+
}

src/test/run-make/extern-fn-struct-passing-abi/test.rs

+27
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ struct FloatPoint {
5353
y: f64
5454
}
5555

56+
#[derive(Clone, Copy, Debug, PartialEq)]
57+
#[repr(C)]
58+
struct FloatOne {
59+
x: f64,
60+
}
61+
62+
#[derive(Clone, Copy, Debug, PartialEq)]
63+
#[repr(C)]
64+
struct IntOdd {
65+
a: i8,
66+
b: i8,
67+
c: i8,
68+
}
69+
5670
#[link(name = "test", kind = "static")]
5771
extern {
5872
fn byval_rect(a: i32, b: i32, c: i32, d: i32, e: i32, s: Rect);
@@ -83,6 +97,10 @@ extern {
8397
fn huge_struct(s: Huge) -> Huge;
8498

8599
fn float_point(p: FloatPoint) -> FloatPoint;
100+
101+
fn float_one(f: FloatOne) -> FloatOne;
102+
103+
fn int_odd(i: IntOdd) -> IntOdd;
86104
}
87105

88106
fn main() {
@@ -91,6 +109,8 @@ fn main() {
91109
let u = FloatRect { a: 3489, b: 3490, c: 8. };
92110
let v = Huge { a: 5647, b: 5648, c: 5649, d: 5650, e: 5651 };
93111
let p = FloatPoint { x: 5., y: -3. };
112+
let f1 = FloatOne { x: 7. };
113+
let i = IntOdd { a: 1, b: 2, c: 3 };
94114

95115
unsafe {
96116
byval_rect(1, 2, 3, 4, 5, s);
@@ -113,5 +133,12 @@ fn main() {
113133
assert_eq!(sret_byval_struct(1, 2, 3, 4, s), t);
114134
assert_eq!(sret_split_struct(1, 2, s), t);
115135
assert_eq!(float_point(p), p);
136+
assert_eq!(int_odd(i), i);
137+
138+
// MSVC/GCC/Clang are not consistent in the ABI of single-float aggregates.
139+
// x86_64: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82028
140+
// i686: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82041
141+
#[cfg(not(all(windows, target_env = "gnu")))]
142+
assert_eq!(float_one(f1), f1);
116143
}
117144
}

src/test/run-make/extern-fn-with-packed-struct/test.c

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// ignore-license
22
// Pragma needed cause of gcc bug on windows: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991
33

4+
#include <assert.h>
5+
46
#ifdef _MSC_VER
57
#pragma pack(push,1)
68
struct Foo {
@@ -18,5 +20,8 @@ struct __attribute__((packed)) Foo {
1820
#endif
1921

2022
struct Foo foo(struct Foo foo) {
23+
assert(foo.a == 1);
24+
assert(foo.b == 2);
25+
assert(foo.c == 3);
2126
return foo;
2227
}

src/test/run-make/extern-fn-with-packed-struct/test.rs

+2-24
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,14 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use std::fmt;
12-
13-
#[repr(packed)]
14-
#[derive(Copy, Clone)]
11+
#[repr(C, packed)]
12+
#[derive(Copy, Clone, Debug, PartialEq)]
1513
struct Foo {
1614
a: i8,
1715
b: i16,
1816
c: i8
1917
}
2018

21-
impl PartialEq for Foo {
22-
fn eq(&self, other: &Foo) -> bool {
23-
self.a == other.a && self.b == other.b && self.c == other.c
24-
}
25-
}
26-
27-
impl fmt::Debug for Foo {
28-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29-
let a = self.a;
30-
let b = self.b;
31-
let c = self.c;
32-
33-
f.debug_struct("Foo")
34-
.field("a", &a)
35-
.field("b", &b)
36-
.field("c", &c)
37-
.finish()
38-
}
39-
}
40-
4119
#[link(name = "test", kind = "static")]
4220
extern {
4321
fn foo(f: Foo) -> Foo;

0 commit comments

Comments
 (0)