|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 |
| -use abi::{ArgType, FnType, LayoutExt, Reg, Uniform}; |
| 11 | +use abi::{ArgAttribute, ArgType, CastTarget, FnType, LayoutExt, PassMode, Reg, RegKind, Uniform}; |
12 | 12 | use context::CodegenCx;
|
| 13 | +use rustc::ty::layout::{self, Size}; |
13 | 14 |
|
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 | +} |
15 | 42 |
|
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>) { |
19 | 55 | 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 | + }); |
21 | 88 | } else {
|
22 | 89 | ret.make_indirect();
|
23 |
| - *offset += cx.tcx.data_layout.pointer_size; |
24 | 90 | }
|
25 | 91 | }
|
26 | 92 |
|
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 | + |
28 | 99 | let dl = &cx.tcx.data_layout;
|
29 | 100 | 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; |
31 | 103 |
|
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; |
39 | 109 | }
|
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 | + }; |
43 | 148 |
|
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 | + }); |
45 | 156 | }
|
46 | 157 |
|
47 | 158 | pub fn compute_abi_info<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, fty: &mut FnType<'tcx>) {
|
48 |
| - let mut offset = Size::from_bytes(0); |
49 | 159 | if !fty.ret.is_ignore() {
|
50 |
| - classify_ret_ty(cx, &mut fty.ret, &mut offset); |
| 160 | + classify_ret_ty(cx, &mut fty.ret); |
51 | 161 | }
|
52 | 162 |
|
53 | 163 | for arg in &mut fty.args {
|
54 | 164 | if arg.is_ignore() { continue; }
|
55 |
| - classify_arg_ty(cx, arg, &mut offset); |
| 165 | + classify_arg_ty(cx, arg); |
56 | 166 | }
|
57 | 167 | }
|
0 commit comments