|
1 | 1 | use arrayvec::ArrayVec; |
2 | 2 | use rustc_abi::{ |
3 | | - BackendRepr, FieldsShape, Float, HasDataLayout, Numeric, Primitive, Reg, RegKind, Size, |
4 | | - TyAbiInterface, |
| 3 | + BackendRepr, FieldsShape, Float, HasDataLayout, Integer, Numeric, Primitive, Reg, RegKind, |
| 4 | + Size, TyAbiInterface, |
5 | 5 | }; |
6 | 6 |
|
7 | 7 | use crate::callconv::{ArgAbi, ArgAttribute, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; |
@@ -59,14 +59,17 @@ where |
59 | 59 | let bits = size.bits(); |
60 | 60 | if bits <= 128 { |
61 | 61 | if let Some(component) = ret.layout.complex_number(cx) { |
62 | | - if matches!(component, Numeric::Int(..)) && size <= cx.data_layout().pointer_size() { |
63 | | - // Return a Complex<{integer}> packed into a single register when that fits. |
64 | | - // We match GCC, not Clang, see https://github.com/llvm/llvm-project/issues/212109. |
65 | | - ret.cast_to(Reg { kind: RegKind::Integer, size }); |
66 | | - } else { |
67 | | - // Otherwise pass in 2 registers. |
68 | | - let reg = Reg { kind: component.reg_kind(), size: component.size() }; |
69 | | - ret.cast_to(CastTarget::pair(reg, reg)); |
| 62 | + match component { |
| 63 | + Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { |
| 64 | + // Return a Complex<{integer}> packed into a single register when that fits. |
| 65 | + // We match GCC, not Clang, see https://github.com/llvm/llvm-project/issues/212109. |
| 66 | + ret.cast_to(Reg { kind: RegKind::Integer, size }); |
| 67 | + } |
| 68 | + _ => { |
| 69 | + // Otherwise pass in 2 registers. |
| 70 | + let reg = Reg { kind: component.reg_kind(), size: component.size() }; |
| 71 | + ret.cast_to(CastTarget::pair(reg, reg)); |
| 72 | + } |
70 | 73 | } |
71 | 74 | return; |
72 | 75 | } |
@@ -118,33 +121,38 @@ where |
118 | 121 | extend_integer_width_mips(arg, 64); |
119 | 122 | } else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { |
120 | 123 | arg.make_indirect(); |
121 | | - } else if arg.layout.complex_float(cx).is_some() && size > dl.pointer_size() * 2 { |
122 | | - // Complex<f128> is passed in 4 GPRs, but aligned to 16 so may need padding. |
123 | | - let reg = Reg { kind: RegKind::Float, size: arg.layout.field(cx, 0).size }; |
124 | | - arg.cast_to_and_pad_i32(CastTarget::pair(reg, reg), pad_i32); |
125 | | - } else if arg.layout.is_complex_number(cx) && size <= dl.pointer_size() * 2 { |
| 124 | + } else if let Some(component) = arg.layout.complex_number(cx) { |
126 | 125 | let slot = dl.pointer_size(); |
127 | 126 | let curr_offset = offset.align_to(align); |
128 | 127 |
|
129 | | - if arg.layout.complex_float(cx).is_some() { |
130 | | - // Only pass a Complex<f32>/Complex<f64> in FPRs when two argument slots are free, |
131 | | - // otherwise pack it into GPRs (or the stack) like an integer of the same size. |
132 | | - if curr_offset.bytes() / slot.bytes() + 2 <= NUM_ARG_SLOTS { |
133 | | - // The default `PassMode::Pair` already passes one component per register. Both |
134 | | - // components claim a slot, even for a Complex<f32> which could fit into one slot. |
135 | | - *offset = curr_offset + slot * 2; |
136 | | - return; |
| 128 | + match component { |
| 129 | + Numeric::Float(Float::F128) => { |
| 130 | + // Complex<f128> is passed in 4 GPRs, but aligned to 16 so may need padding. |
| 131 | + let reg = Reg { kind: RegKind::Float, size: arg.layout.field(cx, 0).size }; |
| 132 | + arg.cast_to_and_pad_i32(CastTarget::pair(reg, reg), pad_i32); |
137 | 133 | } |
| 134 | + Numeric::Float(_) => { |
| 135 | + // Only pass a Complex<f32>/Complex<f64> in FPRs when two argument slots are free, |
| 136 | + if curr_offset.bytes() / slot.bytes() + 2 <= NUM_ARG_SLOTS { |
| 137 | + // The default `PassMode::Pair` already passes one component per register. Both |
| 138 | + // components claim a slot, even a Complex<f32> which could fit into one slot. |
| 139 | + *offset = curr_offset + slot * 2; |
| 140 | + return; |
| 141 | + } |
138 | 142 |
|
139 | | - arg.cast_to_and_pad_i32(Uniform::new(Reg::i64(), size), pad_i32); |
140 | | - } else if size > slot { |
141 | | - // Complex<i64> is passed as 2 separate arguments, which is what the default |
142 | | - // `PassMode::Pair` already does. |
143 | | - } else { |
144 | | - // Cast Complex<i8> into i16, Complex<i16> to i32, etc. The inreg attribute is required |
145 | | - // to make the bits land in the right (upper) bits on big endian targets. |
146 | | - let cast_target = CastTarget::from(Reg { kind: RegKind::Integer, size }); |
147 | | - arg.cast_to(cast_target.with_attrs(ArgAttribute::InReg.into())); |
| 143 | + // Otherwise pack it into GPRs (or the stack) like an integer of the same size. |
| 144 | + arg.cast_to_and_pad_i32(Uniform::new(Reg::i64(), size), pad_i32); |
| 145 | + } |
| 146 | + Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { |
| 147 | + // Cast Complex<i8> into i16, Complex<i16> to i32, etc. |
| 148 | + let cast_target = CastTarget::from(Reg { kind: RegKind::Integer, size }); |
| 149 | + // The inreg attribute makes the bits land in the right (upper) bits on BE targets. |
| 150 | + arg.cast_to(cast_target.with_attrs(ArgAttribute::InReg.into())); |
| 151 | + } |
| 152 | + Numeric::Int(Integer::I64 | Integer::I128, _) => { |
| 153 | + // Complex<i64> is passed as 2 separate arguments, which is what the default |
| 154 | + // `PassMode::Pair` already does. |
| 155 | + } |
148 | 156 | } |
149 | 157 | } else { |
150 | 158 | match arg.layout.fields { |
|
0 commit comments