]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
f4ad76b3bab3c77edeb007f2c8b8c6dd9ef1e193
[rust.git] / compiler / rustc_codegen_cranelift / src / abi / pass_mode.rs
1 //! Argument passing
2
3 use crate::prelude::*;
4 use crate::value_and_place::assert_assignable;
5
6 use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose};
7 use rustc_target::abi::call::{
8     ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode, Reg, RegKind,
9 };
10 use smallvec::{smallvec, SmallVec};
11
12 pub(super) trait ArgAbiExt<'tcx> {
13     fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]>;
14     fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>);
15 }
16
17 fn reg_to_abi_param(reg: Reg) -> AbiParam {
18     let clif_ty = match (reg.kind, reg.size.bytes()) {
19         (RegKind::Integer, 1) => types::I8,
20         (RegKind::Integer, 2) => types::I16,
21         (RegKind::Integer, 3..=4) => types::I32,
22         (RegKind::Integer, 5..=8) => types::I64,
23         (RegKind::Integer, 9..=16) => types::I128,
24         (RegKind::Float, 4) => types::F32,
25         (RegKind::Float, 8) => types::F64,
26         (RegKind::Vector, size) => types::I8.by(u32::try_from(size).unwrap()).unwrap(),
27         _ => unreachable!("{:?}", reg),
28     };
29     AbiParam::new(clif_ty)
30 }
31
32 fn apply_arg_attrs_to_abi_param(mut param: AbiParam, arg_attrs: ArgAttributes) -> AbiParam {
33     match arg_attrs.arg_ext {
34         RustcArgExtension::None => {}
35         RustcArgExtension::Zext => param.extension = ArgumentExtension::Uext,
36         RustcArgExtension::Sext => param.extension = ArgumentExtension::Sext,
37     }
38     param
39 }
40
41 fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> {
42     let (rest_count, rem_bytes) = if cast.rest.unit.size.bytes() == 0 {
43         (0, 0)
44     } else {
45         (
46             cast.rest.total.bytes() / cast.rest.unit.size.bytes(),
47             cast.rest.total.bytes() % cast.rest.unit.size.bytes(),
48         )
49     };
50
51     // Note: Unlike the LLVM equivalent of this code we don't have separate branches for when there
52     // is no prefix as a single unit, an array and a heterogeneous struct are not represented using
53     // different types in Cranelift IR. Instead a single array of primitive types is used.
54
55     // Create list of fields in the main structure
56     let mut args = cast
57         .prefix
58         .iter()
59         .flatten()
60         .map(|&reg| reg_to_abi_param(reg))
61         .chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit)))
62         .collect::<SmallVec<_>>();
63
64     // Append final integer
65     if rem_bytes != 0 {
66         // Only integers can be really split further.
67         assert_eq!(cast.rest.unit.kind, RegKind::Integer);
68         args.push(reg_to_abi_param(Reg {
69             kind: RegKind::Integer,
70             size: Size::from_bytes(rem_bytes),
71         }));
72     }
73
74     args
75 }
76
77 impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
78     fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]> {
79         match self.mode {
80             PassMode::Ignore => smallvec![],
81             PassMode::Direct(attrs) => match self.layout.abi {
82                 Abi::Scalar(scalar) => smallvec![apply_arg_attrs_to_abi_param(
83                     AbiParam::new(scalar_to_clif_type(tcx, scalar)),
84                     attrs
85                 )],
86                 Abi::Vector { .. } => {
87                     let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap();
88                     smallvec![AbiParam::new(vector_ty)]
89                 }
90                 _ => unreachable!("{:?}", self.layout.abi),
91             },
92             PassMode::Pair(attrs_a, attrs_b) => match self.layout.abi {
93                 Abi::ScalarPair(a, b) => {
94                     let a = scalar_to_clif_type(tcx, a);
95                     let b = scalar_to_clif_type(tcx, b);
96                     smallvec![
97                         apply_arg_attrs_to_abi_param(AbiParam::new(a), attrs_a),
98                         apply_arg_attrs_to_abi_param(AbiParam::new(b), attrs_b),
99                     ]
100                 }
101                 _ => unreachable!("{:?}", self.layout.abi),
102             },
103             PassMode::Cast(cast) => cast_target_to_abi_params(cast),
104             PassMode::Indirect { attrs, extra_attrs: None, on_stack } => {
105                 if on_stack {
106                     // Abi requires aligning struct size to pointer size
107                     let size = self.layout.size.align_to(tcx.data_layout.pointer_align.abi);
108                     let size = u32::try_from(size.bytes()).unwrap();
109                     smallvec![apply_arg_attrs_to_abi_param(
110                         AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructArgument(size),),
111                         attrs
112                     )]
113                 } else {
114                     smallvec![apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs)]
115                 }
116             }
117             PassMode::Indirect { attrs, extra_attrs: Some(extra_attrs), on_stack } => {
118                 assert!(!on_stack);
119                 smallvec![
120                     apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs),
121                     apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), extra_attrs),
122                 ]
123             }
124         }
125     }
126
127     fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>) {
128         match self.mode {
129             PassMode::Ignore => (None, vec![]),
130             PassMode::Direct(_) => match self.layout.abi {
131                 Abi::Scalar(scalar) => {
132                     (None, vec![AbiParam::new(scalar_to_clif_type(tcx, scalar))])
133                 }
134                 Abi::Vector { .. } => {
135                     let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap();
136                     (None, vec![AbiParam::new(vector_ty)])
137                 }
138                 _ => unreachable!("{:?}", self.layout.abi),
139             },
140             PassMode::Pair(_, _) => match self.layout.abi {
141                 Abi::ScalarPair(a, b) => {
142                     let a = scalar_to_clif_type(tcx, a);
143                     let b = scalar_to_clif_type(tcx, b);
144                     (None, vec![AbiParam::new(a), AbiParam::new(b)])
145                 }
146                 _ => unreachable!("{:?}", self.layout.abi),
147             },
148             PassMode::Cast(cast) => (None, cast_target_to_abi_params(cast).into_iter().collect()),
149             PassMode::Indirect { attrs: _, extra_attrs: None, on_stack } => {
150                 assert!(!on_stack);
151                 (Some(AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructReturn)), vec![])
152             }
153             PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
154                 unreachable!("unsized return value")
155             }
156         }
157     }
158 }
159
160 pub(super) fn to_casted_value<'tcx>(
161     fx: &mut FunctionCx<'_, '_, 'tcx>,
162     arg: CValue<'tcx>,
163     cast: CastTarget,
164 ) -> SmallVec<[Value; 2]> {
165     let (ptr, meta) = arg.force_stack(fx);
166     assert!(meta.is_none());
167     let mut offset = 0;
168     cast_target_to_abi_params(cast)
169         .into_iter()
170         .map(|param| {
171             let val = ptr.offset_i64(fx, offset).load(fx, param.value_type, MemFlags::new());
172             offset += i64::from(param.value_type.bytes());
173             val
174         })
175         .collect()
176 }
177
178 pub(super) fn from_casted_value<'tcx>(
179     fx: &mut FunctionCx<'_, '_, 'tcx>,
180     block_params: &[Value],
181     layout: TyAndLayout<'tcx>,
182     cast: CastTarget,
183 ) -> CValue<'tcx> {
184     let abi_params = cast_target_to_abi_params(cast);
185     let abi_param_size: u32 = abi_params.iter().map(|param| param.value_type.bytes()).sum();
186     let layout_size = u32::try_from(layout.size.bytes()).unwrap();
187     let stack_slot = fx.bcx.create_sized_stack_slot(StackSlotData {
188         kind: StackSlotKind::ExplicitSlot,
189         // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to
190         // specify stack slot alignment.
191         // Stack slot size may be bigger for for example `[u8; 3]` which is packed into an `i32`.
192         // It may also be smaller for example when the type is a wrapper around an integer with a
193         // larger alignment than the integer.
194         size: (std::cmp::max(abi_param_size, layout_size) + 15) / 16 * 16,
195     });
196     let ptr = Pointer::stack_slot(stack_slot);
197     let mut offset = 0;
198     let mut block_params_iter = block_params.iter().copied();
199     for param in abi_params {
200         let val = ptr.offset_i64(fx, offset).store(
201             fx,
202             block_params_iter.next().unwrap(),
203             MemFlags::new(),
204         );
205         offset += i64::from(param.value_type.bytes());
206         val
207     }
208     assert_eq!(block_params_iter.next(), None, "Leftover block param");
209     CValue::by_ref(ptr, layout)
210 }
211
212 /// Get a set of values to be passed as function arguments.
213 pub(super) fn adjust_arg_for_abi<'tcx>(
214     fx: &mut FunctionCx<'_, '_, 'tcx>,
215     arg: CValue<'tcx>,
216     arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
217     is_owned: bool,
218 ) -> SmallVec<[Value; 2]> {
219     assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty, 16);
220     match arg_abi.mode {
221         PassMode::Ignore => smallvec![],
222         PassMode::Direct(_) => smallvec![arg.load_scalar(fx)],
223         PassMode::Pair(_, _) => {
224             let (a, b) = arg.load_scalar_pair(fx);
225             smallvec![a, b]
226         }
227         PassMode::Cast(cast) => to_casted_value(fx, arg, cast),
228         PassMode::Indirect { .. } => {
229             if is_owned {
230                 match arg.force_stack(fx) {
231                     (ptr, None) => smallvec![ptr.get_addr(fx)],
232                     (ptr, Some(meta)) => smallvec![ptr.get_addr(fx), meta],
233                 }
234             } else {
235                 // Ownership of the value at the backing storage for an argument is passed to the
236                 // callee per the ABI, so we must make a copy of the argument unless the argument
237                 // local is moved.
238                 let place = CPlace::new_stack_slot(fx, arg.layout());
239                 place.write_cvalue(fx, arg);
240                 smallvec![place.to_ptr().get_addr(fx)]
241             }
242         }
243     }
244 }
245
246 /// Create a [`CValue`] containing the value of a function parameter adding clif function parameters
247 /// as necessary.
248 pub(super) fn cvalue_for_param<'tcx>(
249     fx: &mut FunctionCx<'_, '_, 'tcx>,
250     local: Option<mir::Local>,
251     local_field: Option<usize>,
252     arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
253     block_params_iter: &mut impl Iterator<Item = Value>,
254 ) -> Option<CValue<'tcx>> {
255     let block_params = arg_abi
256         .get_abi_param(fx.tcx)
257         .into_iter()
258         .map(|abi_param| {
259             let block_param = block_params_iter.next().unwrap();
260             assert_eq!(fx.bcx.func.dfg.value_type(block_param), abi_param.value_type);
261             block_param
262         })
263         .collect::<SmallVec<[_; 2]>>();
264
265     crate::abi::comments::add_arg_comment(
266         fx,
267         "arg",
268         local,
269         local_field,
270         &block_params,
271         arg_abi.mode,
272         arg_abi.layout,
273     );
274
275     match arg_abi.mode {
276         PassMode::Ignore => None,
277         PassMode::Direct(_) => {
278             assert_eq!(block_params.len(), 1, "{:?}", block_params);
279             Some(CValue::by_val(block_params[0], arg_abi.layout))
280         }
281         PassMode::Pair(_, _) => {
282             assert_eq!(block_params.len(), 2, "{:?}", block_params);
283             Some(CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout))
284         }
285         PassMode::Cast(cast) => Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)),
286         PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
287             assert_eq!(block_params.len(), 1, "{:?}", block_params);
288             Some(CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout))
289         }
290         PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
291             assert_eq!(block_params.len(), 2, "{:?}", block_params);
292             Some(CValue::by_ref_unsized(
293                 Pointer::new(block_params[0]),
294                 block_params[1],
295                 arg_abi.layout,
296             ))
297         }
298     }
299 }