Skip to content

Commit 9e4a7bc

Browse files
committed
Fix passing/returning structs with the 64-bit SPARC ABI
1 parent c83e217 commit 9e4a7bc

File tree

4 files changed

+210
-176
lines changed

4 files changed

+210
-176
lines changed

compiler/rustc_abi/src/callconv/reg.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ impl Reg {
3535

3636
reg_ctor!(f32, Float, 32);
3737
reg_ctor!(f64, Float, 64);
38+
reg_ctor!(f128, Float, 128);
3839
}
3940

4041
impl Reg {
Lines changed: 130 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -1,210 +1,170 @@
1-
// FIXME: This needs an audit for correctness and completeness.
2-
31
use rustc_abi::{
4-
BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Scalar, Size, TyAbiInterface,
5-
TyAndLayout,
2+
Align, BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface,
3+
TyAndLayout, Variants,
64
};
75

86
use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform};
97
use crate::spec::HasTargetSpec;
108

11-
#[derive(Clone, Debug)]
12-
struct Sdata {
13-
pub prefix: [Option<Reg>; 8],
14-
pub prefix_index: usize,
15-
pub last_offset: Size,
16-
pub has_float: bool,
17-
pub arg_attribute: ArgAttribute,
9+
#[derive(Copy, Clone)]
10+
enum DoubleWord {
11+
F64,
12+
F128Start,
13+
F128End,
14+
Words([Word; 2]),
1815
}
1916

20-
fn arg_scalar<C>(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> Sdata
21-
where
22-
C: HasDataLayout,
23-
{
24-
let dl = cx.data_layout();
25-
26-
if !matches!(scalar.primitive(), Primitive::Float(Float::F32 | Float::F64)) {
27-
return data;
28-
}
29-
30-
data.has_float = true;
31-
32-
if !data.last_offset.is_aligned(dl.f64_align.abi) && data.last_offset < offset {
33-
if data.prefix_index == data.prefix.len() {
34-
return data;
35-
}
36-
data.prefix[data.prefix_index] = Some(Reg::i32());
37-
data.prefix_index += 1;
38-
data.last_offset = data.last_offset + Reg::i32().size;
39-
}
40-
41-
for _ in 0..((offset - data.last_offset).bits() / 64)
42-
.min((data.prefix.len() - data.prefix_index) as u64)
43-
{
44-
data.prefix[data.prefix_index] = Some(Reg::i64());
45-
data.prefix_index += 1;
46-
data.last_offset = data.last_offset + Reg::i64().size;
47-
}
48-
49-
if data.last_offset < offset {
50-
if data.prefix_index == data.prefix.len() {
51-
return data;
52-
}
53-
data.prefix[data.prefix_index] = Some(Reg::i32());
54-
data.prefix_index += 1;
55-
data.last_offset = data.last_offset + Reg::i32().size;
56-
}
57-
58-
if data.prefix_index == data.prefix.len() {
59-
return data;
60-
}
61-
62-
if scalar.primitive() == Primitive::Float(Float::F32) {
63-
data.arg_attribute = ArgAttribute::InReg;
64-
data.prefix[data.prefix_index] = Some(Reg::f32());
65-
data.last_offset = offset + Reg::f32().size;
66-
} else {
67-
data.prefix[data.prefix_index] = Some(Reg::f64());
68-
data.last_offset = offset + Reg::f64().size;
69-
}
70-
data.prefix_index += 1;
71-
data
17+
#[derive(Copy, Clone)]
18+
enum Word {
19+
F32,
20+
Integer,
7221
}
7322

74-
fn arg_scalar_pair<C>(
23+
fn classify<'a, Ty, C>(
7524
cx: &C,
76-
scalar1: &Scalar,
77-
scalar2: &Scalar,
78-
mut offset: Size,
79-
mut data: Sdata,
80-
) -> Sdata
81-
where
82-
C: HasDataLayout,
83-
{
84-
data = arg_scalar(cx, scalar1, offset, data);
85-
match (scalar1.primitive(), scalar2.primitive()) {
86-
(Primitive::Float(Float::F32), _) => offset += Reg::f32().size,
87-
(_, Primitive::Float(Float::F64)) => offset += Reg::f64().size,
88-
(Primitive::Int(i, _signed), _) => offset += i.size(),
89-
(Primitive::Pointer(_), _) => offset += Reg::i64().size,
90-
_ => {}
91-
}
92-
93-
if !offset.bytes().is_multiple_of(4)
94-
&& matches!(scalar2.primitive(), Primitive::Float(Float::F32 | Float::F64))
95-
{
96-
offset += Size::from_bytes(4 - (offset.bytes() % 4));
97-
}
98-
data = arg_scalar(cx, scalar2, offset, data);
99-
data
100-
}
101-
102-
fn parse_structure<'a, Ty, C>(
103-
cx: &C,
104-
layout: TyAndLayout<'a, Ty>,
105-
mut data: Sdata,
106-
mut offset: Size,
107-
) -> Sdata
108-
where
25+
arg_layout: &TyAndLayout<'a, Ty>,
26+
offset: Size,
27+
double_words: &mut [DoubleWord; 4],
28+
) where
10929
Ty: TyAbiInterface<'a, C> + Copy,
11030
C: HasDataLayout,
11131
{
112-
if let FieldsShape::Union(_) = layout.fields {
113-
return data;
114-
}
115-
116-
match layout.backend_repr {
117-
BackendRepr::Scalar(scalar) => {
118-
data = arg_scalar(cx, &scalar, offset, data);
119-
}
120-
BackendRepr::Memory { .. } => {
121-
for i in 0..layout.fields.count() {
122-
if offset < layout.fields.offset(i) {
123-
offset = layout.fields.offset(i);
32+
match arg_layout.backend_repr {
33+
BackendRepr::Scalar(scalar) => match scalar.primitive() {
34+
Primitive::Float(float)
35+
if offset.is_aligned(float.align(cx).abi.min(Align::from_bytes(8).unwrap())) =>
36+
{
37+
let index = offset.bytes_usize() / 8;
38+
match float {
39+
Float::F128 => {
40+
double_words[index] = DoubleWord::F128Start;
41+
double_words[index + 1] = DoubleWord::F128End;
42+
}
43+
Float::F64 => {
44+
double_words[index] = DoubleWord::F64;
45+
}
46+
Float::F32 => {
47+
if let DoubleWord::Words(words) = &mut double_words[index] {
48+
words[(offset.bytes_usize() % 8) / 4] = Word::F32;
49+
} else {
50+
unreachable!();
51+
}
52+
}
53+
Float::F16 => {
54+
// FIXME(llvm/llvm-project#97981): f16 doesn't have a proper ABI in LLVM on
55+
// sparc64 yet. Once it does, double-check if it needs to be passed in a
56+
// floating-point register here.
57+
}
12458
}
125-
data = parse_structure(cx, layout.field(cx, i), data.clone(), offset);
12659
}
127-
}
128-
_ => {
129-
if let BackendRepr::ScalarPair(scalar1, scalar2) = &layout.backend_repr {
130-
data = arg_scalar_pair(cx, scalar1, scalar2, offset, data);
60+
_ => {}
61+
},
62+
BackendRepr::SimdVector { .. } => {}
63+
BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields {
64+
FieldsShape::Primitive => {
65+
unreachable!("aggregates can't have `FieldsShape::Primitive`")
13166
}
132-
}
67+
FieldsShape::Union(_) => {
68+
if !arg_layout.is_zst() {
69+
if arg_layout.is_transparent() {
70+
let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1;
71+
classify(cx, &non_1zst_elem, offset, double_words);
72+
}
73+
}
74+
}
75+
FieldsShape::Array { .. } => {}
76+
FieldsShape::Arbitrary { .. } => match arg_layout.variants {
77+
Variants::Multiple { .. } => {}
78+
Variants::Single { .. } | Variants::Empty => {
79+
for i in arg_layout.fields.index_by_increasing_offset() {
80+
classify(
81+
cx,
82+
&arg_layout.field(cx, i),
83+
offset + arg_layout.fields.offset(i),
84+
double_words,
85+
);
86+
}
87+
}
88+
},
89+
},
13390
}
134-
135-
data
13691
}
13792

138-
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size)
139-
where
93+
fn classify_arg<'a, Ty, C>(
94+
cx: &C,
95+
arg: &mut ArgAbi<'a, Ty>,
96+
in_registers_max: Size,
97+
total_double_word_count: &mut usize,
98+
) where
14099
Ty: TyAbiInterface<'a, C> + Copy,
141100
C: HasDataLayout,
142101
{
102+
let pad = !total_double_word_count.is_multiple_of(2) && arg.layout.align.abi.bytes() == 16;
103+
let double_word_count = arg.layout.size.bytes_usize().div_ceil(8);
104+
let start_double_word_count = *total_double_word_count + usize::from(pad);
143105
if !arg.layout.is_aggregate() {
144106
arg.extend_integer_width_to(64);
107+
*total_double_word_count = start_double_word_count + double_word_count;
145108
return;
146109
}
147110

148111
let total = arg.layout.size;
149112
if total > in_registers_max {
150113
arg.make_indirect();
114+
*total_double_word_count += 1;
151115
return;
152116
}
153117

154-
match arg.layout.fields {
155-
FieldsShape::Primitive => unreachable!(),
156-
FieldsShape::Array { .. } => {
157-
// Arrays are passed indirectly
158-
arg.make_indirect();
159-
return;
160-
}
161-
FieldsShape::Union(_) => {
162-
// Unions and are always treated as a series of 64-bit integer chunks
163-
}
164-
FieldsShape::Arbitrary { .. } => {
165-
// Structures with floating point numbers need special care.
118+
*total_double_word_count = start_double_word_count + double_word_count;
166119

167-
let mut data = parse_structure(
168-
cx,
169-
arg.layout,
170-
Sdata {
171-
prefix: [None; 8],
172-
prefix_index: 0,
173-
last_offset: Size::ZERO,
174-
has_float: false,
175-
arg_attribute: ArgAttribute::default(),
176-
},
177-
Size::ZERO,
178-
);
120+
let mut double_words = [DoubleWord::Words([Word::Integer; 2]); 4];
121+
classify(cx, &arg.layout, Size::ZERO, &mut double_words);
179122

180-
if data.has_float {
181-
// Structure { float, int, int } doesn't like to be handled like
182-
// { float, long int }. Other way around it doesn't mind.
183-
if data.last_offset < arg.layout.size
184-
&& !data.last_offset.bytes().is_multiple_of(8)
185-
&& data.prefix_index < data.prefix.len()
186-
{
187-
data.prefix[data.prefix_index] = Some(Reg::i32());
188-
data.prefix_index += 1;
189-
data.last_offset += Reg::i32().size;
190-
}
123+
let mut regs = [None; 8];
124+
let mut i = 0;
125+
let mut push = |reg| {
126+
regs[i] = Some(reg);
127+
i += 1;
128+
};
129+
let mut attrs = ArgAttribute::empty();
191130

192-
let mut rest_size = arg.layout.size - data.last_offset;
193-
if !rest_size.bytes().is_multiple_of(8) && data.prefix_index < data.prefix.len() {
194-
data.prefix[data.prefix_index] = Some(Reg::i32());
195-
rest_size = rest_size - Reg::i32().size;
131+
for (index, double_word) in double_words.into_iter().enumerate() {
132+
if arg.layout.size.bytes_usize() <= index * 8 {
133+
break;
134+
}
135+
match double_word {
136+
// `f128` must be aligned to be assigned a float register.
137+
DoubleWord::F128Start if (start_double_word_count + index).is_multiple_of(2) => {
138+
push(Reg::f128());
139+
}
140+
DoubleWord::F128Start => {
141+
push(Reg::i64());
142+
push(Reg::i64());
143+
}
144+
DoubleWord::F128End => {} // Already handled by `F128Start`
145+
DoubleWord::F64 => push(Reg::f64()),
146+
DoubleWord::Words([Word::Integer, Word::Integer]) => push(Reg::i64()),
147+
DoubleWord::Words(words) => {
148+
attrs |= ArgAttribute::InReg;
149+
for word in words {
150+
match word {
151+
Word::F32 => push(Reg::f32()),
152+
Word::Integer => push(Reg::i32()),
153+
}
196154
}
197-
198-
arg.cast_to(
199-
CastTarget::prefixed(data.prefix, Uniform::new(Reg::i64(), rest_size))
200-
.with_attrs(data.arg_attribute.into()),
201-
);
202-
return;
203155
}
204156
}
205157
}
206158

207-
arg.cast_to(Uniform::new(Reg::i64(), total));
159+
if let [Some(reg), None, ..] = regs {
160+
arg.cast_to_and_pad_i32(CastTarget::from(reg).with_attrs(attrs.into()), pad);
161+
} else {
162+
arg.cast_to_and_pad_i32(
163+
CastTarget::prefixed(regs, Uniform::new(Reg::i8(), Size::ZERO))
164+
.with_attrs(attrs.into()),
165+
pad,
166+
);
167+
}
208168
}
209169

210170
pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
@@ -213,9 +173,10 @@ where
213173
C: HasDataLayout + HasTargetSpec,
214174
{
215175
if !fn_abi.ret.is_ignore() {
216-
classify_arg(cx, &mut fn_abi.ret, Size::from_bytes(32));
176+
classify_arg(cx, &mut fn_abi.ret, Size::from_bytes(32), &mut 0);
217177
}
218178

179+
let mut double_word_count = 0;
219180
for arg in fn_abi.args.iter_mut() {
220181
if arg.is_ignore() {
221182
// sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
@@ -224,9 +185,10 @@ where
224185
&& arg.layout.is_zst()
225186
{
226187
arg.make_indirect_from_ignore();
188+
double_word_count += 1;
227189
}
228-
return;
190+
continue;
229191
}
230-
classify_arg(cx, arg, Size::from_bytes(16));
192+
classify_arg(cx, arg, Size::from_bytes(16), &mut double_word_count);
231193
}
232194
}

0 commit comments

Comments
 (0)