Skip to content

Commit f5116e7

Browse files
authored
Rollup merge of rust-lang#47964 - jcowgill:mips64-abi, r=eddyb
rustc_trans: rewrite mips64 ABI code This PR rewrites the ABI handling code for 64-bit MIPS and should fix various FFI issues including rust-lang#47290. To accomodate the 64-bit ABI I have had to add a new `CastTarget` variant which I've called `Chunked` (though maybe this isn't the best name). This allows an ABI to cast to some arbitrary structure of `Reg` types. This is required on MIPS which might need to cast to a structure containing a mixture of `i64` and `f64` types.
2 parents 026339e + 47c33f7 commit f5116e7

File tree

3 files changed

+188
-76
lines changed

3 files changed

+188
-76
lines changed

src/librustc_trans/abi.rs

+54-52
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use rustc::ty::layout::{self, Align, Size, TyLayout};
4040
use rustc::ty::layout::{HasDataLayout, LayoutOf};
4141

4242
use libc::c_uint;
43-
use std::{cmp, iter};
43+
use std::cmp;
4444

4545
pub use syntax::abi::Abi;
4646
pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
@@ -279,30 +279,6 @@ impl Uniform {
279279
pub fn align(&self, cx: &CodegenCx) -> Align {
280280
self.unit.align(cx)
281281
}
282-
283-
pub fn llvm_type(&self, cx: &CodegenCx) -> Type {
284-
let llunit = self.unit.llvm_type(cx);
285-
286-
if self.total <= self.unit.size {
287-
return llunit;
288-
}
289-
290-
let count = self.total.bytes() / self.unit.size.bytes();
291-
let rem_bytes = self.total.bytes() % self.unit.size.bytes();
292-
293-
if rem_bytes == 0 {
294-
return Type::array(&llunit, count);
295-
}
296-
297-
// Only integers can be really split further.
298-
assert_eq!(self.unit.kind, RegKind::Integer);
299-
300-
let args: Vec<_> = (0..count).map(|_| llunit)
301-
.chain(iter::once(Type::ix(cx, rem_bytes * 8)))
302-
.collect();
303-
304-
Type::struct_(cx, &args, false)
305-
}
306282
}
307283

308284
pub trait LayoutExt<'tcx> {
@@ -405,55 +381,81 @@ impl<'tcx> LayoutExt<'tcx> for TyLayout<'tcx> {
405381
}
406382

407383
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
408-
pub enum CastTarget {
409-
Uniform(Uniform),
410-
Pair(Reg, Reg)
384+
pub struct CastTarget {
385+
pub prefix: [Option<RegKind>; 8],
386+
pub prefix_chunk: Size,
387+
pub rest: Uniform,
411388
}
412389

413390
impl From<Reg> for CastTarget {
414391
fn from(unit: Reg) -> CastTarget {
415-
CastTarget::Uniform(Uniform::from(unit))
392+
CastTarget::from(Uniform::from(unit))
416393
}
417394
}
418395

419396
impl From<Uniform> for CastTarget {
420397
fn from(uniform: Uniform) -> CastTarget {
421-
CastTarget::Uniform(uniform)
398+
CastTarget {
399+
prefix: [None; 8],
400+
prefix_chunk: Size::from_bytes(0),
401+
rest: uniform
402+
}
422403
}
423404
}
424405

425406
impl CastTarget {
426-
pub fn size(&self, cx: &CodegenCx) -> Size {
427-
match *self {
428-
CastTarget::Uniform(u) => u.total,
429-
CastTarget::Pair(a, b) => {
430-
(a.size.abi_align(a.align(cx)) + b.size)
431-
.abi_align(self.align(cx))
432-
}
407+
pub fn pair(a: Reg, b: Reg) -> CastTarget {
408+
CastTarget {
409+
prefix: [Some(a.kind), None, None, None, None, None, None, None],
410+
prefix_chunk: a.size,
411+
rest: Uniform::from(b)
433412
}
434413
}
435414

415+
pub fn size(&self, cx: &CodegenCx) -> Size {
416+
(self.prefix_chunk * self.prefix.iter().filter(|x| x.is_some()).count() as u64)
417+
.abi_align(self.rest.align(cx)) + self.rest.total
418+
}
419+
436420
pub fn align(&self, cx: &CodegenCx) -> Align {
437-
match *self {
438-
CastTarget::Uniform(u) => u.align(cx),
439-
CastTarget::Pair(a, b) => {
440-
cx.data_layout().aggregate_align
441-
.max(a.align(cx))
442-
.max(b.align(cx))
443-
}
444-
}
421+
self.prefix.iter()
422+
.filter_map(|x| x.map(|kind| Reg { kind: kind, size: self.prefix_chunk }.align(cx)))
423+
.fold(cx.data_layout().aggregate_align.max(self.rest.align(cx)),
424+
|acc, align| acc.max(align))
445425
}
446426

447427
pub fn llvm_type(&self, cx: &CodegenCx) -> Type {
448-
match *self {
449-
CastTarget::Uniform(u) => u.llvm_type(cx),
450-
CastTarget::Pair(a, b) => {
451-
Type::struct_(cx, &[
452-
a.llvm_type(cx),
453-
b.llvm_type(cx)
454-
], false)
428+
let rest_ll_unit = self.rest.unit.llvm_type(cx);
429+
let rest_count = self.rest.total.bytes() / self.rest.unit.size.bytes();
430+
let rem_bytes = self.rest.total.bytes() % self.rest.unit.size.bytes();
431+
432+
if self.prefix.iter().all(|x| x.is_none()) {
433+
// Simplify to a single unit when there is no prefix and size <= unit size
434+
if self.rest.total <= self.rest.unit.size {
435+
return rest_ll_unit;
436+
}
437+
438+
// Simplify to array when all chunks are the same size and type
439+
if rem_bytes == 0 {
440+
return Type::array(&rest_ll_unit, rest_count);
455441
}
456442
}
443+
444+
// Create list of fields in the main structure
445+
let mut args: Vec<_> =
446+
self.prefix.iter().flat_map(|option_kind| option_kind.map(
447+
|kind| Reg { kind: kind, size: self.prefix_chunk }.llvm_type(cx)))
448+
.chain((0..rest_count).map(|_| rest_ll_unit))
449+
.collect();
450+
451+
// Append final integer
452+
if rem_bytes != 0 {
453+
// Only integers can be really split further.
454+
assert_eq!(self.rest.unit.kind, RegKind::Integer);
455+
args.push(Type::ix(cx, rem_bytes * 8));
456+
}
457+
458+
Type::struct_(cx, &args, false)
457459
}
458460
}
459461

src/librustc_trans/cabi_mips64.rs

+133-23
Original file line numberDiff line numberDiff line change
@@ -8,50 +8,160 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use abi::{ArgType, FnType, LayoutExt, Reg, Uniform};
11+
use abi::{ArgAttribute, ArgType, CastTarget, FnType, LayoutExt, PassMode, Reg, RegKind, Uniform};
1212
use context::CodegenCx;
13+
use rustc::ty::layout::{self, Size};
1314

14-
use rustc::ty::layout::Size;
15+
fn extend_integer_width_mips(arg: &mut ArgType, bits: u64) {
16+
// Always sign extend u32 values on 64-bit mips
17+
if let layout::Abi::Scalar(ref scalar) = arg.layout.abi {
18+
if let layout::Int(i, signed) = scalar.value {
19+
if !signed && i.size().bits() == 32 {
20+
if let PassMode::Direct(ref mut attrs) = arg.mode {
21+
attrs.set(ArgAttribute::SExt);
22+
return;
23+
}
24+
}
25+
}
26+
}
27+
28+
arg.extend_integer_width_to(bits);
29+
}
30+
31+
fn bits_to_int_reg(bits: u64) -> Reg {
32+
if bits <= 8 {
33+
Reg::i8()
34+
} else if bits <= 16 {
35+
Reg::i16()
36+
} else if bits <= 32 {
37+
Reg::i32()
38+
} else {
39+
Reg::i64()
40+
}
41+
}
1542

16-
fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
17-
ret: &mut ArgType<'tcx>,
18-
offset: &mut Size) {
43+
fn float_reg<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &ArgType<'tcx>, i: usize) -> Option<Reg> {
44+
match ret.layout.field(cx, i).abi {
45+
layout::Abi::Scalar(ref scalar) => match scalar.value {
46+
layout::F32 => Some(Reg::f32()),
47+
layout::F64 => Some(Reg::f64()),
48+
_ => None
49+
},
50+
_ => None
51+
}
52+
}
53+
54+
fn classify_ret_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, ret: &mut ArgType<'tcx>) {
1955
if !ret.layout.is_aggregate() {
20-
ret.extend_integer_width_to(64);
56+
extend_integer_width_mips(ret, 64);
57+
return;
58+
}
59+
60+
let size = ret.layout.size;
61+
let bits = size.bits();
62+
if bits <= 128 {
63+
// Unlike other architectures which return aggregates in registers, MIPS n64 limits the
64+
// use of float registers to structures (not unions) containing exactly one or two
65+
// float fields.
66+
67+
if let layout::FieldPlacement::Arbitrary { .. } = ret.layout.fields {
68+
if ret.layout.fields.count() == 1 {
69+
if let Some(reg) = float_reg(cx, ret, 0) {
70+
ret.cast_to(reg);
71+
return;
72+
}
73+
} else if ret.layout.fields.count() == 2 {
74+
if let Some(reg0) = float_reg(cx, ret, 0) {
75+
if let Some(reg1) = float_reg(cx, ret, 1) {
76+
ret.cast_to(CastTarget::pair(reg0, reg1));
77+
return;
78+
}
79+
}
80+
}
81+
}
82+
83+
// Cast to a uniform int structure
84+
ret.cast_to(Uniform {
85+
unit: bits_to_int_reg(bits),
86+
total: size
87+
});
2188
} else {
2289
ret.make_indirect();
23-
*offset += cx.tcx.data_layout.pointer_size;
2490
}
2591
}
2692

27-
fn classify_arg_ty(cx: &CodegenCx, arg: &mut ArgType, offset: &mut Size) {
93+
fn classify_arg_ty<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, arg: &mut ArgType<'tcx>) {
94+
if !arg.layout.is_aggregate() {
95+
extend_integer_width_mips(arg, 64);
96+
return;
97+
}
98+
2899
let dl = &cx.tcx.data_layout;
29100
let size = arg.layout.size;
30-
let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align);
101+
let mut prefix = [None; 8];
102+
let mut prefix_index = 0;
31103

32-
if arg.layout.is_aggregate() {
33-
arg.cast_to(Uniform {
34-
unit: Reg::i64(),
35-
total: size
36-
});
37-
if !offset.is_abi_aligned(align) {
38-
arg.pad_with(Reg::i64());
104+
match arg.layout.fields {
105+
layout::FieldPlacement::Array { .. } => {
106+
// Arrays are passed indirectly
107+
arg.make_indirect();
108+
return;
39109
}
40-
} else {
41-
arg.extend_integer_width_to(64);
42-
}
110+
layout::FieldPlacement::Union(_) => {
111+
// Unions and are always treated as a series of 64-bit integer chunks
112+
},
113+
layout::FieldPlacement::Arbitrary { .. } => {
114+
// Structures are split up into a series of 64-bit integer chunks, but any aligned
115+
// doubles not part of another aggregate are passed as floats.
116+
let mut last_offset = Size::from_bytes(0);
117+
118+
for i in 0..arg.layout.fields.count() {
119+
let field = arg.layout.field(cx, i);
120+
let offset = arg.layout.fields.offset(i);
121+
122+
// We only care about aligned doubles
123+
if let layout::Abi::Scalar(ref scalar) = field.abi {
124+
if let layout::F64 = scalar.value {
125+
if offset.is_abi_aligned(dl.f64_align) {
126+
// Insert enough integers to cover [last_offset, offset)
127+
assert!(last_offset.is_abi_aligned(dl.f64_align));
128+
for _ in 0..((offset - last_offset).bits() / 64)
129+
.min((prefix.len() - prefix_index) as u64) {
130+
131+
prefix[prefix_index] = Some(RegKind::Integer);
132+
prefix_index += 1;
133+
}
134+
135+
if prefix_index == prefix.len() {
136+
break;
137+
}
138+
139+
prefix[prefix_index] = Some(RegKind::Float);
140+
prefix_index += 1;
141+
last_offset = offset + Reg::f64().size;
142+
}
143+
}
144+
}
145+
}
146+
}
147+
};
43148

44-
*offset = offset.abi_align(align) + size.abi_align(align);
149+
// Extract first 8 chunks as the prefix
150+
let rest_size = size - Size::from_bytes(8) * prefix_index as u64;
151+
arg.cast_to(CastTarget {
152+
prefix: prefix,
153+
prefix_chunk: Size::from_bytes(8),
154+
rest: Uniform { unit: Reg::i64(), total: rest_size }
155+
});
45156
}
46157

47158
pub fn compute_abi_info<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, fty: &mut FnType<'tcx>) {
48-
let mut offset = Size::from_bytes(0);
49159
if !fty.ret.is_ignore() {
50-
classify_ret_ty(cx, &mut fty.ret, &mut offset);
160+
classify_ret_ty(cx, &mut fty.ret);
51161
}
52162

53163
for arg in &mut fty.args {
54164
if arg.is_ignore() { continue; }
55-
classify_arg_ty(cx, arg, &mut offset);
165+
classify_arg_ty(cx, arg);
56166
}
57167
}

src/librustc_trans/cabi_x86_64.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
171171
let mut target = CastTarget::from(lo);
172172
if size > offset {
173173
if let Some(hi) = reg_component(cls, &mut i, size - offset) {
174-
target = CastTarget::Pair(lo, hi);
174+
target = CastTarget::pair(lo, hi);
175175
}
176176
}
177177
assert_eq!(reg_component(cls, &mut i, Size::from_bytes(0)), None);

0 commit comments

Comments
 (0)