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> {
get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]>13 fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]>;
get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>)14 fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>);
15 }
16
reg_to_abi_param(reg: Reg) -> AbiParam17 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
apply_arg_attrs_to_abi_param(mut param: AbiParam, arg_attrs: ArgAttributes) -> AbiParam32 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
cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[AbiParam; 2]>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_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>> {
get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]>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);
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(ref cast, pad_i32) => {
104 assert!(!pad_i32, "padding support not yet implemented");
105 cast_target_to_abi_params(cast)
106 }
107 PassMode::Indirect { attrs, extra_attrs: None, on_stack } => {
108 if on_stack {
109 // Abi requires aligning struct size to pointer size
110 let size = self.layout.size.align_to(tcx.data_layout.pointer_align.abi);
111 let size = u32::try_from(size.bytes()).unwrap();
112 smallvec![apply_arg_attrs_to_abi_param(
113 AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructArgument(size),),
114 attrs
115 )]
116 } else {
117 smallvec![apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs)]
118 }
119 }
120 PassMode::Indirect { attrs, extra_attrs: Some(extra_attrs), on_stack } => {
121 assert!(!on_stack);
122 smallvec![
123 apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs),
124 apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), extra_attrs),
125 ]
126 }
127 }
128 }
129
get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>)130 fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>) {
131 match self.mode {
132 PassMode::Ignore => (None, vec![]),
133 PassMode::Direct(_) => match self.layout.abi {
134 Abi::Scalar(scalar) => {
135 (None, vec![AbiParam::new(scalar_to_clif_type(tcx, scalar))])
136 }
137 Abi::Vector { .. } => {
138 let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout);
139 (None, vec![AbiParam::new(vector_ty)])
140 }
141 _ => unreachable!("{:?}", self.layout.abi),
142 },
143 PassMode::Pair(_, _) => match self.layout.abi {
144 Abi::ScalarPair(a, b) => {
145 let a = scalar_to_clif_type(tcx, a);
146 let b = scalar_to_clif_type(tcx, b);
147 (None, vec![AbiParam::new(a), AbiParam::new(b)])
148 }
149 _ => unreachable!("{:?}", self.layout.abi),
150 },
151 PassMode::Cast(ref cast, _) => {
152 (None, cast_target_to_abi_params(cast).into_iter().collect())
153 }
154 PassMode::Indirect { attrs: _, extra_attrs: None, on_stack } => {
155 assert!(!on_stack);
156 (Some(AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructReturn)), vec![])
157 }
158 PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
159 unreachable!("unsized return value")
160 }
161 }
162 }
163 }
164
to_casted_value<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, arg: CValue<'tcx>, cast: &CastTarget, ) -> SmallVec<[Value; 2]>165 pub(super) fn to_casted_value<'tcx>(
166 fx: &mut FunctionCx<'_, '_, 'tcx>,
167 arg: CValue<'tcx>,
168 cast: &CastTarget,
169 ) -> SmallVec<[Value; 2]> {
170 let (ptr, meta) = arg.force_stack(fx);
171 assert!(meta.is_none());
172 let mut offset = 0;
173 cast_target_to_abi_params(cast)
174 .into_iter()
175 .map(|param| {
176 let val = ptr.offset_i64(fx, offset).load(fx, param.value_type, MemFlags::new());
177 offset += i64::from(param.value_type.bytes());
178 val
179 })
180 .collect()
181 }
182
from_casted_value<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, block_params: &[Value], layout: TyAndLayout<'tcx>, cast: &CastTarget, ) -> CValue<'tcx>183 pub(super) fn from_casted_value<'tcx>(
184 fx: &mut FunctionCx<'_, '_, 'tcx>,
185 block_params: &[Value],
186 layout: TyAndLayout<'tcx>,
187 cast: &CastTarget,
188 ) -> CValue<'tcx> {
189 let abi_params = cast_target_to_abi_params(cast);
190 let abi_param_size: u32 = abi_params.iter().map(|param| param.value_type.bytes()).sum();
191 let layout_size = u32::try_from(layout.size.bytes()).unwrap();
192 let stack_slot = fx.bcx.create_sized_stack_slot(StackSlotData {
193 kind: StackSlotKind::ExplicitSlot,
194 // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to
195 // specify stack slot alignment.
196 // Stack slot size may be bigger for example `[u8; 3]` which is packed into an `i32`.
197 // It may also be smaller for example when the type is a wrapper around an integer with a
198 // larger alignment than the integer.
199 size: (std::cmp::max(abi_param_size, layout_size) + 15) / 16 * 16,
200 });
201 let ptr = Pointer::stack_slot(stack_slot);
202 let mut offset = 0;
203 let mut block_params_iter = block_params.iter().copied();
204 for param in abi_params {
205 let val = ptr.offset_i64(fx, offset).store(
206 fx,
207 block_params_iter.next().unwrap(),
208 MemFlags::new(),
209 );
210 offset += i64::from(param.value_type.bytes());
211 val
212 }
213 assert_eq!(block_params_iter.next(), None, "Leftover block param");
214 CValue::by_ref(ptr, layout)
215 }
216
217 /// Get a set of values to be passed as function arguments.
adjust_arg_for_abi<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, arg: CValue<'tcx>, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, is_owned: bool, ) -> SmallVec<[Value; 2]>218 pub(super) fn adjust_arg_for_abi<'tcx>(
219 fx: &mut FunctionCx<'_, '_, 'tcx>,
220 arg: CValue<'tcx>,
221 arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
222 is_owned: bool,
223 ) -> SmallVec<[Value; 2]> {
224 assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty, 16);
225 match arg_abi.mode {
226 PassMode::Ignore => smallvec![],
227 PassMode::Direct(_) => smallvec![arg.load_scalar(fx)],
228 PassMode::Pair(_, _) => {
229 let (a, b) = arg.load_scalar_pair(fx);
230 smallvec![a, b]
231 }
232 PassMode::Cast(ref cast, _) => to_casted_value(fx, arg, cast),
233 PassMode::Indirect { .. } => {
234 if is_owned {
235 match arg.force_stack(fx) {
236 (ptr, None) => smallvec![ptr.get_addr(fx)],
237 (ptr, Some(meta)) => smallvec![ptr.get_addr(fx), meta],
238 }
239 } else {
240 // Ownership of the value at the backing storage for an argument is passed to the
241 // callee per the ABI, so we must make a copy of the argument unless the argument
242 // local is moved.
243 let place = CPlace::new_stack_slot(fx, arg.layout());
244 place.write_cvalue(fx, arg);
245 smallvec![place.to_ptr().get_addr(fx)]
246 }
247 }
248 }
249 }
250
251 /// Create a [`CValue`] containing the value of a function parameter adding clif function parameters
252 /// as necessary.
cvalue_for_param<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, local: Option<mir::Local>, local_field: Option<usize>, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, block_params_iter: &mut impl Iterator<Item = Value>, ) -> Option<CValue<'tcx>>253 pub(super) fn cvalue_for_param<'tcx>(
254 fx: &mut FunctionCx<'_, '_, 'tcx>,
255 local: Option<mir::Local>,
256 local_field: Option<usize>,
257 arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
258 block_params_iter: &mut impl Iterator<Item = Value>,
259 ) -> Option<CValue<'tcx>> {
260 let block_params = arg_abi
261 .get_abi_param(fx.tcx)
262 .into_iter()
263 .map(|abi_param| {
264 let block_param = block_params_iter.next().unwrap();
265 assert_eq!(fx.bcx.func.dfg.value_type(block_param), abi_param.value_type);
266 block_param
267 })
268 .collect::<SmallVec<[_; 2]>>();
269
270 crate::abi::comments::add_arg_comment(
271 fx,
272 "arg",
273 local,
274 local_field,
275 &block_params,
276 &arg_abi.mode,
277 arg_abi.layout,
278 );
279
280 match arg_abi.mode {
281 PassMode::Ignore => None,
282 PassMode::Direct(_) => {
283 assert_eq!(block_params.len(), 1, "{:?}", block_params);
284 Some(CValue::by_val(block_params[0], arg_abi.layout))
285 }
286 PassMode::Pair(_, _) => {
287 assert_eq!(block_params.len(), 2, "{:?}", block_params);
288 Some(CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout))
289 }
290 PassMode::Cast(ref cast, _) => {
291 Some(from_casted_value(fx, &block_params, arg_abi.layout, cast))
292 }
293 PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
294 assert_eq!(block_params.len(), 1, "{:?}", block_params);
295 Some(CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout))
296 }
297 PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
298 assert_eq!(block_params.len(), 2, "{:?}", block_params);
299 Some(CValue::by_ref_unsized(
300 Pointer::new(block_params[0]),
301 block_params[1],
302 arg_abi.layout,
303 ))
304 }
305 }
306 }
307