1 //! Common helper code for ABI lowering.
2 //!
3 //! This module provides functions and data structures that are useful for implementing the
4 //! `TargetIsa::legalize_signature()` method.
5
6 use crate::ir::{AbiParam, ArgumentExtension, ArgumentLoc, Type};
7 use alloc::borrow::Cow;
8 use alloc::vec::Vec;
9 use core::cmp::Ordering;
10
11 /// Legalization action to perform on a single argument or return value when converting a
12 /// signature.
13 ///
14 /// An argument may go through a sequence of legalization steps before it reaches the final
15 /// `Assign` action.
16 #[derive(Clone, Copy, Debug)]
17 pub enum ArgAction {
18 /// Assign the argument to the given location.
19 Assign(ArgumentLoc),
20
21 /// Assign the argument to the given location and change the type to the specified type.
22 /// This is used by [`ArgumentPurpose::StructArgument`].
23 AssignAndChangeType(ArgumentLoc, Type),
24
25 /// Convert the argument, then call again.
26 ///
27 /// This action can split an integer type into two smaller integer arguments, or it can split a
28 /// SIMD vector into halves.
29 Convert(ValueConversion),
30 }
31
32 impl From<ArgumentLoc> for ArgAction {
from(x: ArgumentLoc) -> Self33 fn from(x: ArgumentLoc) -> Self {
34 Self::Assign(x)
35 }
36 }
37
38 impl From<ValueConversion> for ArgAction {
from(x: ValueConversion) -> Self39 fn from(x: ValueConversion) -> Self {
40 Self::Convert(x)
41 }
42 }
43
44 /// Legalization action to be applied to a value that is being passed to or from a legalized ABI.
45 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
46 pub enum ValueConversion {
47 /// Split an integer types into low and high parts, using `isplit`.
48 IntSplit,
49
50 /// Split a vector type into halves with identical lane types, using `vsplit`.
51 VectorSplit,
52
53 /// Bit-cast to an integer type of the same size.
54 IntBits,
55
56 /// Sign-extend integer value to the required type.
57 Sext(Type),
58
59 /// Unsigned zero-extend value to the required type.
60 Uext(Type),
61
62 /// Pass value by pointer of given integer type.
63 Pointer(Type),
64 }
65
66 impl ValueConversion {
67 /// Apply this conversion to a type, return the converted type.
apply(self, ty: Type) -> Type68 pub fn apply(self, ty: Type) -> Type {
69 match self {
70 Self::IntSplit => ty.half_width().expect("Integer type too small to split"),
71 Self::VectorSplit => ty.half_vector().expect("Not a vector"),
72 Self::IntBits => Type::int(ty.bits()).expect("Bad integer size"),
73 Self::Sext(nty) | Self::Uext(nty) | Self::Pointer(nty) => nty,
74 }
75 }
76
77 /// Is this a split conversion that results in two arguments?
is_split(self) -> bool78 pub fn is_split(self) -> bool {
79 match self {
80 Self::IntSplit | Self::VectorSplit => true,
81 _ => false,
82 }
83 }
84
85 /// Is this a conversion to pointer?
is_pointer(self) -> bool86 pub fn is_pointer(self) -> bool {
87 match self {
88 Self::Pointer(_) => true,
89 _ => false,
90 }
91 }
92 }
93
94 /// Common trait for assigning arguments to registers or stack locations.
95 ///
96 /// This will be implemented by individual ISAs.
97 pub trait ArgAssigner {
98 /// Pick an assignment action for function argument (or return value) `arg`.
assign(&mut self, arg: &AbiParam) -> ArgAction99 fn assign(&mut self, arg: &AbiParam) -> ArgAction;
100 }
101
102 /// Legalize the arguments in `args` using the given argument assigner.
103 ///
104 /// This function can be used for both arguments and return values.
legalize_args<AA: ArgAssigner>(args: &[AbiParam], aa: &mut AA) -> Option<Vec<AbiParam>>105 pub fn legalize_args<AA: ArgAssigner>(args: &[AbiParam], aa: &mut AA) -> Option<Vec<AbiParam>> {
106 let mut args = Cow::Borrowed(args);
107
108 // Iterate over the arguments.
109 // We may need to mutate the vector in place, so don't use a normal iterator, and clone the
110 // argument to avoid holding a reference.
111 let mut argno = 0;
112 while let Some(arg) = args.get(argno).cloned() {
113 // Leave the pre-assigned arguments alone.
114 // We'll assume that they don't interfere with our assignments.
115 if arg.location.is_assigned() {
116 argno += 1;
117 continue;
118 }
119
120 match aa.assign(&arg) {
121 // Assign argument to a location and move on to the next one.
122 ArgAction::Assign(loc) => {
123 args.to_mut()[argno].location = loc;
124 argno += 1;
125 }
126 // Assign argument to a location, change type to the requested one and move on to the
127 // next one.
128 ArgAction::AssignAndChangeType(loc, ty) => {
129 let arg = &mut args.to_mut()[argno];
130 arg.location = loc;
131 arg.value_type = ty;
132 argno += 1;
133 }
134 // Split this argument into two smaller ones. Then revisit both.
135 ArgAction::Convert(conv) => {
136 debug_assert!(
137 !arg.legalized_to_pointer,
138 "No more conversions allowed after conversion to pointer"
139 );
140 let value_type = conv.apply(arg.value_type);
141 args.to_mut()[argno].value_type = value_type;
142 if conv.is_pointer() {
143 args.to_mut()[argno].legalized_to_pointer = true;
144 } else if conv.is_split() {
145 let new_arg = AbiParam { value_type, ..arg };
146 args.to_mut().insert(argno + 1, new_arg);
147 }
148 }
149 }
150 }
151
152 match args {
153 Cow::Borrowed(_) => None,
154 Cow::Owned(a) => Some(a),
155 }
156 }
157
158 /// Determine the right action to take when passing a `have` value type to a call signature where
159 /// the next argument is `arg` which has a different value type.
160 ///
161 /// The signature legalization process in `legalize_args` above can replace a single argument value
162 /// with multiple arguments of smaller types. It can also change the type of an integer argument to
163 /// a larger integer type, requiring the smaller value to be sign- or zero-extended.
164 ///
165 /// The legalizer needs to repair the values at all ABI boundaries:
166 ///
167 /// - Incoming function arguments to the entry block.
168 /// - Function arguments passed to a call.
169 /// - Return values from a call.
170 /// - Return values passed to a return instruction.
171 ///
172 /// The `legalize_abi_value` function helps the legalizer with the process. When the legalizer
173 /// needs to pass a pre-legalized `have` argument, but the ABI argument `arg` has a different value
174 /// type, `legalize_abi_value(have, arg)` tells the legalizer how to create the needed value type
175 /// for the argument.
176 ///
177 /// It may be necessary to call `legalize_abi_value` more than once for a given argument before the
178 /// desired argument type appears. This will happen when a vector or integer type needs to be split
179 /// more than once, for example.
legalize_abi_value(have: Type, arg: &AbiParam) -> ValueConversion180 pub fn legalize_abi_value(have: Type, arg: &AbiParam) -> ValueConversion {
181 let have_bits = have.bits();
182 let arg_bits = arg.value_type.bits();
183
184 if arg.legalized_to_pointer {
185 return ValueConversion::Pointer(arg.value_type);
186 }
187
188 match have_bits.cmp(&arg_bits) {
189 // We have fewer bits than the ABI argument.
190 Ordering::Less => {
191 debug_assert!(
192 have.is_int() && arg.value_type.is_int(),
193 "Can only extend integer values"
194 );
195 match arg.extension {
196 ArgumentExtension::Uext => ValueConversion::Uext(arg.value_type),
197 ArgumentExtension::Sext => ValueConversion::Sext(arg.value_type),
198 _ => panic!("No argument extension specified"),
199 }
200 }
201 // We have the same number of bits as the argument.
202 Ordering::Equal => {
203 // This must be an integer vector that is split and then extended.
204 debug_assert!(arg.value_type.is_int());
205 debug_assert!(have.is_vector(), "expected vector type, got {}", have);
206 ValueConversion::VectorSplit
207 }
208 // We have more bits than the argument.
209 Ordering::Greater => {
210 if have.is_vector() {
211 ValueConversion::VectorSplit
212 } else if have.is_float() {
213 // Convert a float to int so it can be split the next time.
214 // ARM would do this to pass an `f64` in two registers.
215 ValueConversion::IntBits
216 } else {
217 ValueConversion::IntSplit
218 }
219 }
220 }
221 }
222
223 #[cfg(test)]
224 mod tests {
225 use super::*;
226 use crate::ir::types;
227 use crate::ir::AbiParam;
228
229 #[test]
legalize()230 fn legalize() {
231 let mut arg = AbiParam::new(types::I32);
232
233 assert_eq!(
234 legalize_abi_value(types::I64X2, &arg),
235 ValueConversion::VectorSplit
236 );
237 assert_eq!(
238 legalize_abi_value(types::I64, &arg),
239 ValueConversion::IntSplit
240 );
241
242 // Vector of integers is broken down, then sign-extended.
243 arg.extension = ArgumentExtension::Sext;
244 assert_eq!(
245 legalize_abi_value(types::I16X4, &arg),
246 ValueConversion::VectorSplit
247 );
248 assert_eq!(
249 legalize_abi_value(types::I16.by(2).unwrap(), &arg),
250 ValueConversion::VectorSplit
251 );
252 assert_eq!(
253 legalize_abi_value(types::I16, &arg),
254 ValueConversion::Sext(types::I32)
255 );
256
257 // 64-bit float is split as an integer.
258 assert_eq!(
259 legalize_abi_value(types::F64, &arg),
260 ValueConversion::IntBits
261 );
262
263 // Value is passed by reference
264 arg.legalized_to_pointer = true;
265 assert_eq!(
266 legalize_abi_value(types::F64, &arg),
267 ValueConversion::Pointer(types::I32)
268 );
269 }
270 }
271