]> git.lizzy.rs Git - rust.git/blob - src/abi/returning.rs
Replace EmptySinglePair with SmallVec
[rust.git] / src / abi / returning.rs
1 //! Return value handling
2
3 use crate::prelude::*;
4
5 use rustc_middle::ty::layout::FnAbiExt;
6 use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
7 use smallvec::{SmallVec, smallvec};
8
9 /// Can the given type be returned into an ssa var or does it need to be returned on the stack.
10 pub(crate) fn can_return_to_ssa_var<'tcx>(
11     fx: &FunctionCx<'_, 'tcx, impl Module>,
12     func: &mir::Operand<'tcx>,
13     args: &[mir::Operand<'tcx>],
14 ) -> bool {
15     let fn_ty = fx.monomorphize(func.ty(fx.mir, fx.tcx));
16     let fn_sig = fx
17         .tcx
18         .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), fn_ty.fn_sig(fx.tcx));
19
20     // Handle special calls like instrinsics and empty drop glue.
21     let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() {
22         let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs)
23             .unwrap()
24             .unwrap()
25             .polymorphize(fx.tcx);
26
27         match instance.def {
28             InstanceDef::Intrinsic(_) | InstanceDef::DropGlue(_, _) => {
29                 return true;
30             }
31             _ => Some(instance),
32         }
33     } else {
34         None
35     };
36
37     let extra_args = &args[fn_sig.inputs().len()..];
38     let extra_args = extra_args
39         .iter()
40         .map(|op_arg| fx.monomorphize(op_arg.ty(fx.mir, fx.tcx)))
41         .collect::<Vec<_>>();
42     let fn_abi = if let Some(instance) = instance {
43         FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), instance, &extra_args)
44     } else {
45         FnAbi::of_fn_ptr(
46             &RevealAllLayoutCx(fx.tcx),
47             fn_ty.fn_sig(fx.tcx),
48             &extra_args,
49         )
50     };
51     match fn_abi.ret.mode {
52         PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) => true,
53         // FIXME Make it possible to return Cast and Indirect to an ssa var.
54         PassMode::Cast(_) | PassMode::Indirect { .. } => false,
55     }
56 }
57
58 /// Return a place where the return value of the current function can be written to. If necessary
59 /// this adds an extra parameter pointing to where the return value needs to be stored.
60 pub(super) fn codegen_return_param<'tcx>(
61     fx: &mut FunctionCx<'_, 'tcx, impl Module>,
62     ssa_analyzed: &rustc_index::vec::IndexVec<Local, crate::analyze::SsaKind>,
63     start_block: Block,
64 ) -> CPlace<'tcx> {
65     let (ret_place, ret_param): (_, SmallVec<[_; 2]>) = match fx.fn_abi.as_ref().unwrap().ret.mode {
66         PassMode::Ignore => (
67             CPlace::no_place(fx.fn_abi.as_ref().unwrap().ret.layout),
68             smallvec![],
69         ),
70         PassMode::Direct(_) | PassMode::Pair(_, _) => {
71             let is_ssa = ssa_analyzed[RETURN_PLACE] == crate::analyze::SsaKind::Ssa;
72             (
73                 super::make_local_place(
74                     fx,
75                     RETURN_PLACE,
76                     fx.fn_abi.as_ref().unwrap().ret.layout,
77                     is_ssa,
78                 ),
79                 smallvec![],
80             )
81         }
82         PassMode::Cast(_)
83         | PassMode::Indirect {
84             attrs: _,
85             extra_attrs: None,
86             on_stack: _,
87         } => {
88             let ret_param = fx.bcx.append_block_param(start_block, fx.pointer_type);
89             (
90                 CPlace::for_ptr(
91                     Pointer::new(ret_param),
92                     fx.fn_abi.as_ref().unwrap().ret.layout,
93                 ),
94                 smallvec![ret_param],
95             )
96         }
97         PassMode::Indirect {
98             attrs: _,
99             extra_attrs: Some(_),
100             on_stack: _,
101         } => unreachable!("unsized return value"),
102     };
103
104     #[cfg(not(debug_assertions))]
105     let _ = ret_param;
106
107     #[cfg(debug_assertions)]
108     crate::abi::comments::add_arg_comment(
109         fx,
110         "ret",
111         Some(RETURN_PLACE),
112         None,
113         &ret_param,
114         fx.fn_abi.as_ref().unwrap().ret.mode,
115         fx.fn_abi.as_ref().unwrap().ret.layout,
116     );
117
118     ret_place
119 }
120
121 /// Invokes the closure with if necessary a value representing the return pointer. When the closure
122 /// returns the call return value(s) if any are written to the correct place.
123 pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>(
124     fx: &mut FunctionCx<'_, 'tcx, M>,
125     ret_arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
126     ret_place: Option<CPlace<'tcx>>,
127     f: impl FnOnce(&mut FunctionCx<'_, 'tcx, M>, Option<Value>) -> (Inst, T),
128 ) -> (Inst, T) {
129     let return_ptr = match ret_arg_abi.mode {
130         PassMode::Ignore => None,
131         PassMode::Cast(_)
132         | PassMode::Indirect {
133             attrs: _,
134             extra_attrs: None,
135             on_stack: _,
136         } => match ret_place {
137             Some(ret_place) => Some(ret_place.to_ptr().get_addr(fx)),
138             None => Some(fx.bcx.ins().iconst(fx.pointer_type, 43)), // FIXME allocate temp stack slot
139         },
140         PassMode::Indirect {
141             attrs: _,
142             extra_attrs: Some(_),
143             on_stack: _,
144         } => unreachable!("unsized return value"),
145         PassMode::Direct(_) | PassMode::Pair(_, _) => None,
146     };
147
148     let (call_inst, meta) = f(fx, return_ptr);
149
150     match ret_arg_abi.mode {
151         PassMode::Ignore => {}
152         PassMode::Direct(_) => {
153             if let Some(ret_place) = ret_place {
154                 let ret_val = fx.bcx.inst_results(call_inst)[0];
155                 ret_place.write_cvalue(fx, CValue::by_val(ret_val, ret_arg_abi.layout));
156             }
157         }
158         PassMode::Pair(_, _) => {
159             if let Some(ret_place) = ret_place {
160                 let ret_val_a = fx.bcx.inst_results(call_inst)[0];
161                 let ret_val_b = fx.bcx.inst_results(call_inst)[1];
162                 ret_place.write_cvalue(
163                     fx,
164                     CValue::by_val_pair(ret_val_a, ret_val_b, ret_arg_abi.layout),
165                 );
166             }
167         }
168         PassMode::Cast(_)
169         | PassMode::Indirect {
170             attrs: _,
171             extra_attrs: None,
172             on_stack: _,
173         } => {}
174         PassMode::Indirect {
175             attrs: _,
176             extra_attrs: Some(_),
177             on_stack: _,
178         } => unreachable!("unsized return value"),
179     }
180
181     (call_inst, meta)
182 }
183
184 /// Codegen a return instruction with the right return value(s) if any.
185 pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, impl Module>) {
186     match fx.fn_abi.as_ref().unwrap().ret.mode {
187         PassMode::Ignore
188         | PassMode::Cast(_)
189         | PassMode::Indirect {
190             attrs: _,
191             extra_attrs: None,
192             on_stack: _,
193         } => {
194             fx.bcx.ins().return_(&[]);
195         }
196         PassMode::Indirect {
197             attrs: _,
198             extra_attrs: Some(_),
199             on_stack: _,
200         } => unreachable!("unsized return value"),
201         PassMode::Direct(_) => {
202             let place = fx.get_local_place(RETURN_PLACE);
203             let ret_val = place.to_cvalue(fx).load_scalar(fx);
204             fx.bcx.ins().return_(&[ret_val]);
205         }
206         PassMode::Pair(_, _) => {
207             let place = fx.get_local_place(RETURN_PLACE);
208             let (ret_val_a, ret_val_b) = place.to_cvalue(fx).load_scalar_pair(fx);
209             fx.bcx.ins().return_(&[ret_val_a, ret_val_b]);
210         }
211     }
212 }