Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit edbdda3

Browse files
committedNov 22, 2021
fix sparc64 ABI for aggregates with floating point members
1 parent 6414e0b commit edbdda3

File tree

8 files changed

+277
-58
lines changed

8 files changed

+277
-58
lines changed
 

‎compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> {
7171
.prefix
7272
.iter()
7373
.flatten()
74-
.map(|&kind| reg_to_abi_param(Reg { kind, size: cast.prefix_chunk_size }))
74+
.map(|&reg| reg_to_abi_param(reg))
7575
.chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit)))
7676
.collect::<SmallVec<_>>();
7777

‎compiler/rustc_codegen_gcc/src/abi.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ impl GccType for CastTarget {
4848
let mut args: Vec<_> = self
4949
.prefix
5050
.iter()
51-
.flat_map(|option_kind| {
52-
option_kind.map(|kind| Reg { kind, size: self.prefix_chunk_size }.gcc_type(cx))
51+
.flat_map(|option_reg| {
52+
option_reg.map(|reg| reg.gcc_type(cx))
5353
})
5454
.chain((0..rest_count).map(|_| rest_gcc_unit))
5555
.collect();

‎compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,7 @@ impl LlvmType for CastTarget {
181181
let mut args: Vec<_> = self
182182
.prefix
183183
.iter()
184-
.flat_map(|option_kind| {
185-
option_kind.map(|kind| Reg { kind, size: self.prefix_chunk_size }.llvm_type(cx))
186-
})
184+
.flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx)))
187185
.chain((0..rest_count).map(|_| rest_ll_unit))
188186
.collect();
189187

@@ -466,6 +464,9 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
466464
);
467465
}
468466
}
467+
PassMode::Cast(cast) => {
468+
cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn);
469+
}
469470
_ => {}
470471
}
471472
for arg in &self.args {
@@ -497,8 +498,8 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
497498
apply(a);
498499
apply(b);
499500
}
500-
PassMode::Cast(_) => {
501-
apply(&ArgAttributes::new());
501+
PassMode::Cast(cast) => {
502+
apply(&cast.attrs);
502503
}
503504
}
504505
}
@@ -533,6 +534,13 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
533534
);
534535
}
535536
}
537+
PassMode::Cast(cast) => {
538+
cast.attrs.apply_attrs_to_callsite(
539+
llvm::AttributePlace::ReturnValue,
540+
&bx.cx,
541+
callsite,
542+
);
543+
}
536544
_ => {}
537545
}
538546
if let abi::Abi::Scalar(scalar) = self.ret.layout.abi {
@@ -577,8 +585,8 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
577585
apply(bx.cx, a);
578586
apply(bx.cx, b);
579587
}
580-
PassMode::Cast(_) => {
581-
apply(bx.cx, &ArgAttributes::new());
588+
PassMode::Cast(cast) => {
589+
apply(bx.cx, &cast.attrs);
582590
}
583591
}
584592
}

‎compiler/rustc_target/src/abi/call/mips64.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
1+
use crate::abi::call::{
2+
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, Reg, Uniform,
3+
};
24
use crate::abi::{self, HasDataLayout, Size, TyAbiInterface};
35

46
fn extend_integer_width_mips<Ty>(arg: &mut ArgAbi<'_, Ty>, bits: u64) {
@@ -115,15 +117,15 @@ where
115117
for _ in 0..((offset - last_offset).bits() / 64)
116118
.min((prefix.len() - prefix_index) as u64)
117119
{
118-
prefix[prefix_index] = Some(RegKind::Integer);
120+
prefix[prefix_index] = Some(Reg::i64());
119121
prefix_index += 1;
120122
}
121123

122124
if prefix_index == prefix.len() {
123125
break;
124126
}
125127

126-
prefix[prefix_index] = Some(RegKind::Float);
128+
prefix[prefix_index] = Some(Reg::f64());
127129
prefix_index += 1;
128130
last_offset = offset + Reg::f64().size;
129131
}
@@ -137,8 +139,13 @@ where
137139
let rest_size = size - Size::from_bytes(8) * prefix_index as u64;
138140
arg.cast_to(CastTarget {
139141
prefix,
140-
prefix_chunk_size: Size::from_bytes(8),
141142
rest: Uniform { unit: Reg::i64(), total: rest_size },
143+
attrs: ArgAttributes {
144+
regular: ArgAttribute::default(),
145+
arg_ext: ArgExtension::None,
146+
pointee_size: Size::ZERO,
147+
pointee_align: None,
148+
},
142149
});
143150
}
144151

‎compiler/rustc_target/src/abi/call/mod.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,9 @@ impl Uniform {
214214

215215
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
216216
pub struct CastTarget {
217-
pub prefix: [Option<RegKind>; 8],
218-
pub prefix_chunk_size: Size,
217+
pub prefix: [Option<Reg>; 8],
219218
pub rest: Uniform,
219+
pub attrs: ArgAttributes,
220220
}
221221

222222
impl From<Reg> for CastTarget {
@@ -227,29 +227,48 @@ impl From<Reg> for CastTarget {
227227

228228
impl From<Uniform> for CastTarget {
229229
fn from(uniform: Uniform) -> CastTarget {
230-
CastTarget { prefix: [None; 8], prefix_chunk_size: Size::ZERO, rest: uniform }
230+
CastTarget {
231+
prefix: [None; 8],
232+
rest: uniform,
233+
attrs: ArgAttributes {
234+
regular: ArgAttribute::default(),
235+
arg_ext: ArgExtension::None,
236+
pointee_size: Size::ZERO,
237+
pointee_align: None,
238+
},
239+
}
231240
}
232241
}
233242

234243
impl CastTarget {
235244
pub fn pair(a: Reg, b: Reg) -> CastTarget {
236245
CastTarget {
237-
prefix: [Some(a.kind), None, None, None, None, None, None, None],
238-
prefix_chunk_size: a.size,
246+
prefix: [Some(a), None, None, None, None, None, None, None],
239247
rest: Uniform::from(b),
248+
attrs: ArgAttributes {
249+
regular: ArgAttribute::default(),
250+
arg_ext: ArgExtension::None,
251+
pointee_size: Size::ZERO,
252+
pointee_align: None,
253+
},
240254
}
241255
}
242256

243-
pub fn size<C: HasDataLayout>(&self, cx: &C) -> Size {
244-
(self.prefix_chunk_size * self.prefix.iter().filter(|x| x.is_some()).count() as u64)
245-
.align_to(self.rest.align(cx))
246-
+ self.rest.total
257+
pub fn size<C: HasDataLayout>(&self, _cx: &C) -> Size {
258+
let mut size = self.rest.total;
259+
for i in 0..self.prefix.iter().count() {
260+
match self.prefix[i] {
261+
Some(v) => size += Size { raw: v.size.bytes() },
262+
None => {}
263+
}
264+
}
265+
return size;
247266
}
248267

249268
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
250269
self.prefix
251270
.iter()
252-
.filter_map(|x| x.map(|kind| Reg { kind, size: self.prefix_chunk_size }.align(cx)))
271+
.filter_map(|x| x.map(|reg| reg.align(cx)))
253272
.fold(cx.data_layout().aggregate_align.abi.max(self.rest.align(cx)), |acc, align| {
254273
acc.max(align)
255274
})
Lines changed: 93 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// FIXME: This needs an audit for correctness and completeness.
22

3-
use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
4-
use crate::abi::{HasDataLayout, TyAbiInterface};
3+
use crate::abi::call::{
4+
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, RegKind, Uniform,
5+
};
6+
use crate::abi::{self, HasDataLayout, Size, TyAbiInterface};
57

68
fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
79
where
@@ -16,41 +18,15 @@ where
1618

1719
let valid_unit = match unit.kind {
1820
RegKind::Integer => false,
19-
RegKind::Float => true,
21+
RegKind::Float => false,
2022
RegKind::Vector => arg.layout.size.bits() == 128,
2123
};
2224

2325
valid_unit.then_some(Uniform { unit, total: arg.layout.size })
2426
})
2527
}
2628

27-
fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>)
28-
where
29-
Ty: TyAbiInterface<'a, C> + Copy,
30-
C: HasDataLayout,
31-
{
32-
if !ret.layout.is_aggregate() {
33-
ret.extend_integer_width_to(64);
34-
return;
35-
}
36-
37-
if let Some(uniform) = is_homogeneous_aggregate(cx, ret) {
38-
ret.cast_to(uniform);
39-
return;
40-
}
41-
let size = ret.layout.size;
42-
let bits = size.bits();
43-
if bits <= 256 {
44-
let unit = Reg::i64();
45-
ret.cast_to(Uniform { unit, total: size });
46-
return;
47-
}
48-
49-
// don't return aggregates in registers
50-
ret.make_indirect();
51-
}
52-
53-
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
29+
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size)
5430
where
5531
Ty: TyAbiInterface<'a, C> + Copy,
5632
C: HasDataLayout,
@@ -60,13 +36,97 @@ where
6036
return;
6137
}
6238

39+
// This doesn't intentionally handle structures with floats which needs
40+
// special care below.
6341
if let Some(uniform) = is_homogeneous_aggregate(cx, arg) {
6442
arg.cast_to(uniform);
6543
return;
6644
}
6745

46+
if let abi::FieldsShape::Arbitrary { .. } = arg.layout.fields {
47+
let dl = cx.data_layout();
48+
let size = arg.layout.size;
49+
let mut prefix = [None; 8];
50+
let mut prefix_index = 0;
51+
let mut last_offset = Size::ZERO;
52+
let mut has_float = false;
53+
let mut arg_attribute = ArgAttribute::default();
54+
55+
for i in 0..arg.layout.fields.count() {
56+
let field = arg.layout.field(cx, i);
57+
let offset = arg.layout.fields.offset(i);
58+
59+
if let abi::Abi::Scalar(scalar) = &field.abi {
60+
if scalar.value == abi::F32 || scalar.value == abi::F64 {
61+
has_float = true;
62+
63+
if !last_offset.is_aligned(dl.f64_align.abi) && last_offset < offset {
64+
if prefix_index == prefix.len() {
65+
break;
66+
}
67+
prefix[prefix_index] = Some(Reg::i32());
68+
prefix_index += 1;
69+
last_offset = last_offset + Reg::i32().size;
70+
}
71+
72+
for _ in 0..((offset - last_offset).bits() / 64)
73+
.min((prefix.len() - prefix_index) as u64)
74+
{
75+
prefix[prefix_index] = Some(Reg::i64());
76+
prefix_index += 1;
77+
last_offset = last_offset + Reg::i64().size;
78+
}
79+
80+
if last_offset < offset {
81+
if prefix_index == prefix.len() {
82+
break;
83+
}
84+
prefix[prefix_index] = Some(Reg::i32());
85+
prefix_index += 1;
86+
last_offset = last_offset + Reg::i32().size;
87+
}
88+
89+
if prefix_index == prefix.len() {
90+
break;
91+
}
92+
93+
if scalar.value == abi::F32 {
94+
arg_attribute = ArgAttribute::InReg;
95+
prefix[prefix_index] = Some(Reg::f32());
96+
last_offset = offset + Reg::f32().size;
97+
} else {
98+
prefix[prefix_index] = Some(Reg::f64());
99+
last_offset = offset + Reg::f64().size;
100+
}
101+
prefix_index += 1;
102+
}
103+
}
104+
}
105+
106+
if has_float && arg.layout.size <= in_registers_max {
107+
let mut rest_size = size - last_offset;
108+
109+
if (rest_size.raw % 8) != 0 && prefix_index < prefix.len() {
110+
prefix[prefix_index] = Some(Reg::i32());
111+
rest_size = rest_size - Reg::i32().size;
112+
}
113+
114+
arg.cast_to(CastTarget {
115+
prefix,
116+
rest: Uniform { unit: Reg::i64(), total: rest_size },
117+
attrs: ArgAttributes {
118+
regular: arg_attribute,
119+
arg_ext: ArgExtension::None,
120+
pointee_size: Size::ZERO,
121+
pointee_align: None,
122+
},
123+
});
124+
return;
125+
}
126+
}
127+
68128
let total = arg.layout.size;
69-
if total.bits() > 128 {
129+
if total > in_registers_max {
70130
arg.make_indirect();
71131
return;
72132
}
@@ -80,13 +140,13 @@ where
80140
C: HasDataLayout,
81141
{
82142
if !fn_abi.ret.is_ignore() {
83-
classify_ret(cx, &mut fn_abi.ret);
143+
classify_arg(cx, &mut fn_abi.ret, Size { raw: 32 });
84144
}
85145

86146
for arg in &mut fn_abi.args {
87147
if arg.is_ignore() {
88148
continue;
89149
}
90-
classify_arg(cx, arg);
150+
classify_arg(cx, arg, Size { raw: 16 });
91151
}
92152
}

‎src/test/assembly/sparc-struct-abi.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Test SPARC64 ABI
2+
// - float structure members are passes in floating point registers
3+
// (#86163)
4+
5+
// assembly-output: emit-asm
6+
// needs-llvm-components: sparc
7+
// compile-flags: --target=sparcv9-sun-solaris
8+
#![crate_type = "lib"]
9+
#![feature(no_core, lang_items)]
10+
#![no_core]
11+
12+
#[lang = "sized"]
13+
pub trait Sized {}
14+
#[lang = "copy"]
15+
pub trait Copy {}
16+
17+
#[repr(C)]
18+
pub struct Franta {
19+
a: f32,
20+
b: f32,
21+
c: f32,
22+
d: f32,
23+
}
24+
25+
// NB: due to delay slots the `ld` following the call is actually executed before the call.
26+
#[no_mangle]
27+
pub unsafe extern "C" fn callee(arg: Franta) {
28+
// CHECK-LABEL: callee:
29+
// CHECK: st %f3, [[PLACE_D:.*]]
30+
// CHECK: st %f2, [[PLACE_C:.*]]
31+
// CHECK: st %f1, [[PLACE_B:.*]]
32+
// CHECK: st %f0, [[PLACE_A:.*]]
33+
// CHECK: call tst_use
34+
// CHECK-NEXT: ld [[PLACE_A]], %f1
35+
// CHECK: call tst_use
36+
// CHECK-NEXT: ld [[PLACE_B]], %f1
37+
// CHECK: call tst_use
38+
// CHECK-NEXT: ld [[PLACE_C]], %f1
39+
// CHECK: call tst_use
40+
// CHECK-NEXT: ld [[PLACE_D]], %f1
41+
clobber();
42+
tst_use(arg.a);
43+
tst_use(arg.b);
44+
tst_use(arg.c);
45+
tst_use(arg.d);
46+
}
47+
48+
extern "C" {
49+
fn opaque_callee(arg: Franta, intarg: i32);
50+
fn tst_use(arg: f32);
51+
fn clobber();
52+
}
53+
54+
#[no_mangle]
55+
pub unsafe extern "C" fn caller() {
56+
// CHECK-LABEL: caller:
57+
// CHECK: ld [{{.*}}], %f0
58+
// CHECK: ld [{{.*}}], %f1
59+
// CHECK: ld [{{.*}}], %f2
60+
// CHECK: ld [{{.*}}], %f3
61+
// CHECK: call opaque_callee
62+
// CHECK: mov 3, %o2
63+
opaque_callee(Franta { a: 1.0, b: 2.0, c: 3.0, d: 4.0 }, 3);
64+
}

‎src/test/codegen/sparc-struct-abi.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Checks that we correctly codegen extern "C" functions returning structs.
2-
// See issue #52638.
2+
// See issues #52638 and #86163.
33

44
// compile-flags: -O --target=sparc64-unknown-linux-gnu --crate-type=rlib
55
// needs-llvm-components: sparc
@@ -25,3 +25,64 @@ pub struct Bool {
2525
pub extern "C" fn structbool() -> Bool {
2626
Bool { b: true }
2727
}
28+
29+
30+
#[repr(C)]
31+
pub struct BoolFloat {
32+
b: bool,
33+
f: f32,
34+
}
35+
36+
// CHECK: define inreg { i32, float } @structboolfloat()
37+
// CHECK-NEXT: start:
38+
// CHECK-NEXT: ret { i32, float } { i32 16777216, float 0x40091EB860000000 }
39+
#[no_mangle]
40+
pub extern "C" fn structboolfloat() -> BoolFloat {
41+
BoolFloat { b: true, f: 3.14 }
42+
}
43+
44+
// CHECK: define void @structboolfloat_input({ i32, float } inreg %0)
45+
// CHECK-NEXT: start:
46+
#[no_mangle]
47+
pub extern "C" fn structboolfloat_input(a: BoolFloat) { }
48+
49+
50+
#[repr(C)]
51+
pub struct ShortDouble {
52+
s: i16,
53+
d: f64,
54+
}
55+
56+
// CHECK: define { i64, double } @structshortdouble()
57+
// CHECK-NEXT: start:
58+
// CHECK-NEXT: ret { i64, double } { i64 34621422135410688, double 3.140000e+00 }
59+
#[no_mangle]
60+
pub extern "C" fn structshortdouble() -> ShortDouble {
61+
ShortDouble { s: 123, d: 3.14 }
62+
}
63+
64+
// CHECK: define void @structshortdouble_input({ i64, double } %0)
65+
// CHECK-NEXT: start:
66+
#[no_mangle]
67+
pub extern "C" fn structshortdouble_input(a: ShortDouble) { }
68+
69+
70+
#[repr(C)]
71+
pub struct FloatLongFloat {
72+
f: f32,
73+
i: i64,
74+
g: f32,
75+
}
76+
77+
// CHECK: define inreg { float, i32, i64, float, i32 } @structfloatlongfloat()
78+
// CHECK-NEXT: start:
79+
// CHECK-NEXT: ret { float, i32, i64, float, i32 } { float 0x3FB99999A0000000, i32 undef, i64 123, float 0x40091EB860000000, i32 undef }
80+
#[no_mangle]
81+
pub extern "C" fn structfloatlongfloat() -> FloatLongFloat {
82+
FloatLongFloat { f: 0.1, i: 123, g: 3.14 }
83+
}
84+
85+
// CHECK: define void @structfloatlongfloat_input(%FloatLongFloat* noalias nocapture dereferenceable(24) %a)
86+
// CHECK-NEXT: start:
87+
#[no_mangle]
88+
pub extern "C" fn structfloatlongfloat_input(a: FloatLongFloat) { }

0 commit comments

Comments
 (0)
Please sign in to comment.