]> git.lizzy.rs Git - rust.git/blob - src/abi/pass_mode.rs
Use the new cranelift-module interface
[rust.git] / src / abi / pass_mode.rs
1 //! Argument passing
2
3 use crate::prelude::*;
4
5 pub(super) use EmptySinglePair::*;
6
7 #[derive(Copy, Clone, Debug)]
8 pub(super) enum PassMode {
9     NoPass,
10     ByVal(Type),
11     ByValPair(Type, Type),
12     ByRef { size: Option<Size> },
13 }
14
15 #[derive(Copy, Clone, Debug)]
16 pub(super) enum EmptySinglePair<T> {
17     Empty,
18     Single(T),
19     Pair(T, T),
20 }
21
22 impl<T> EmptySinglePair<T> {
23     pub(super) fn into_iter(self) -> EmptySinglePairIter<T> {
24         EmptySinglePairIter(self)
25     }
26
27     pub(super) fn map<U>(self, mut f: impl FnMut(T) -> U) -> EmptySinglePair<U> {
28         match self {
29             Empty => Empty,
30             Single(v) => Single(f(v)),
31             Pair(a, b) => Pair(f(a), f(b)),
32         }
33     }
34 }
35
36 pub(super) struct EmptySinglePairIter<T>(EmptySinglePair<T>);
37
38 impl<T> Iterator for EmptySinglePairIter<T> {
39     type Item = T;
40
41     fn next(&mut self) -> Option<T> {
42         match std::mem::replace(&mut self.0, Empty) {
43             Empty => None,
44             Single(v) => Some(v),
45             Pair(a, b) => {
46                 self.0 = Single(b);
47                 Some(a)
48             }
49         }
50     }
51 }
52
53 impl<T: std::fmt::Debug> EmptySinglePair<T> {
54     pub(super) fn assert_single(self) -> T {
55         match self {
56             Single(v) => v,
57             _ => panic!("Called assert_single on {:?}", self),
58         }
59     }
60
61     pub(super) fn assert_pair(self) -> (T, T) {
62         match self {
63             Pair(a, b) => (a, b),
64             _ => panic!("Called assert_pair on {:?}", self),
65         }
66     }
67 }
68
69 impl PassMode {
70     pub(super) fn get_param_ty(self, tcx: TyCtxt<'_>) -> EmptySinglePair<Type> {
71         match self {
72             PassMode::NoPass => Empty,
73             PassMode::ByVal(clif_type) => Single(clif_type),
74             PassMode::ByValPair(a, b) => Pair(a, b),
75             PassMode::ByRef { size: Some(_) } => Single(pointer_ty(tcx)),
76             PassMode::ByRef { size: None } => Pair(pointer_ty(tcx), pointer_ty(tcx)),
77         }
78     }
79 }
80
81 pub(super) fn get_pass_mode<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> PassMode {
82     if layout.is_zst() {
83         // WARNING zst arguments must never be passed, as that will break CastKind::ClosureFnPointer
84         PassMode::NoPass
85     } else {
86         match &layout.abi {
87             Abi::Uninhabited => PassMode::NoPass,
88             Abi::Scalar(scalar) => PassMode::ByVal(scalar_to_clif_type(tcx, scalar.clone())),
89             Abi::ScalarPair(a, b) => {
90                 let a = scalar_to_clif_type(tcx, a.clone());
91                 let b = scalar_to_clif_type(tcx, b.clone());
92                 if a == types::I128 && b == types::I128 {
93                     // Returning (i128, i128) by-val-pair would take 4 regs, while only 3 are
94                     // available on x86_64. Cranelift gets confused when too many return params
95                     // are used.
96                     PassMode::ByRef {
97                         size: Some(layout.size),
98                     }
99                 } else {
100                     PassMode::ByValPair(a, b)
101                 }
102             }
103
104             // FIXME implement Vector Abi in a cg_llvm compatible way
105             Abi::Vector { .. } => {
106                 if let Some(vector_ty) = crate::intrinsics::clif_vector_type(tcx, layout) {
107                     PassMode::ByVal(vector_ty)
108                 } else {
109                     PassMode::ByRef {
110                         size: Some(layout.size),
111                     }
112                 }
113             }
114
115             Abi::Aggregate { sized: true } => PassMode::ByRef {
116                 size: Some(layout.size),
117             },
118             Abi::Aggregate { sized: false } => PassMode::ByRef { size: None },
119         }
120     }
121 }
122
123 /// Get a set of values to be passed as function arguments.
124 pub(super) fn adjust_arg_for_abi<'tcx>(
125     fx: &mut FunctionCx<'_, 'tcx, impl Module>,
126     arg: CValue<'tcx>,
127 ) -> EmptySinglePair<Value> {
128     match get_pass_mode(fx.tcx, arg.layout()) {
129         PassMode::NoPass => Empty,
130         PassMode::ByVal(_) => Single(arg.load_scalar(fx)),
131         PassMode::ByValPair(_, _) => {
132             let (a, b) = arg.load_scalar_pair(fx);
133             Pair(a, b)
134         }
135         PassMode::ByRef { size: _ } => match arg.force_stack(fx) {
136             (ptr, None) => Single(ptr.get_addr(fx)),
137             (ptr, Some(meta)) => Pair(ptr.get_addr(fx), meta),
138         },
139     }
140 }
141
142 /// Create a [`CValue`] containing the value of a function parameter adding clif function parameters
143 /// as necessary.
144 pub(super) fn cvalue_for_param<'tcx>(
145     fx: &mut FunctionCx<'_, 'tcx, impl Module>,
146     start_block: Block,
147     #[cfg_attr(not(debug_assertions), allow(unused_variables))] local: Option<mir::Local>,
148     #[cfg_attr(not(debug_assertions), allow(unused_variables))] local_field: Option<usize>,
149     arg_ty: Ty<'tcx>,
150 ) -> Option<CValue<'tcx>> {
151     let layout = fx.layout_of(arg_ty);
152     let pass_mode = get_pass_mode(fx.tcx, layout);
153
154     if let PassMode::NoPass = pass_mode {
155         return None;
156     }
157
158     let clif_types = pass_mode.get_param_ty(fx.tcx);
159     let block_params = clif_types.map(|t| fx.bcx.append_block_param(start_block, t));
160
161     #[cfg(debug_assertions)]
162     crate::abi::comments::add_arg_comment(
163         fx,
164         "arg",
165         local,
166         local_field,
167         block_params,
168         pass_mode,
169         arg_ty,
170     );
171
172     match pass_mode {
173         PassMode::NoPass => unreachable!(),
174         PassMode::ByVal(_) => Some(CValue::by_val(block_params.assert_single(), layout)),
175         PassMode::ByValPair(_, _) => {
176             let (a, b) = block_params.assert_pair();
177             Some(CValue::by_val_pair(a, b, layout))
178         }
179         PassMode::ByRef { size: Some(_) } => Some(CValue::by_ref(
180             Pointer::new(block_params.assert_single()),
181             layout,
182         )),
183         PassMode::ByRef { size: None } => {
184             let (ptr, meta) = block_params.assert_pair();
185             Some(CValue::by_ref_unsized(Pointer::new(ptr), meta, layout))
186         }
187     }
188 }