]> git.lizzy.rs Git - rust.git/blob - src/value_and_place.rs
Don't copy ByRef passed types to local stack slot when not necessary
[rust.git] / src / value_and_place.rs
1 use crate::prelude::*;
2
3 fn codegen_field<'tcx>(
4     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
5     base: Value,
6     layout: TyLayout<'tcx>,
7     field: mir::Field,
8 ) -> (Value, TyLayout<'tcx>) {
9     let field_offset = layout.fields.offset(field.index());
10     let field_ty = layout.field(&*fx, field.index());
11     if field_offset.bytes() > 0 {
12         (
13             fx.bcx.ins().iadd_imm(base, field_offset.bytes() as i64),
14             field_ty,
15         )
16     } else {
17         (base, field_ty)
18     }
19 }
20
21 fn scalar_pair_calculate_b_offset(tcx: TyCtxt<'_>, a_scalar: &Scalar, b_scalar: &Scalar) -> i32 {
22     let b_offset = a_scalar.value.size(&tcx).align_to(b_scalar.value.align(&tcx).abi);
23     b_offset.bytes().try_into().unwrap()
24 }
25
26 /// A read-only value
27 #[derive(Debug, Copy, Clone)]
28 pub struct CValue<'tcx>(CValueInner, TyLayout<'tcx>);
29
30 #[derive(Debug, Copy, Clone)]
31 enum CValueInner {
32     ByRef(Value),
33     ByVal(Value),
34     ByValPair(Value, Value),
35 }
36
37 impl<'tcx> CValue<'tcx> {
38     pub fn by_ref(value: Value, layout: TyLayout<'tcx>) -> CValue<'tcx> {
39         CValue(CValueInner::ByRef(value), layout)
40     }
41
42     pub fn by_val(value: Value, layout: TyLayout<'tcx>) -> CValue<'tcx> {
43         CValue(CValueInner::ByVal(value), layout)
44     }
45
46     pub fn by_val_pair(value: Value, extra: Value, layout: TyLayout<'tcx>) -> CValue<'tcx> {
47         CValue(CValueInner::ByValPair(value, extra), layout)
48     }
49
50     pub fn layout(&self) -> TyLayout<'tcx> {
51         self.1
52     }
53
54     pub fn force_stack<'a>(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>) -> Value {
55         let layout = self.1;
56         match self.0 {
57             CValueInner::ByRef(value) => value,
58             CValueInner::ByVal(_) | CValueInner::ByValPair(_, _) => {
59                 let cplace = CPlace::new_stack_slot(fx, layout.ty);
60                 cplace.write_cvalue(fx, self);
61                 cplace.to_addr(fx)
62             }
63         }
64     }
65
66     pub fn try_to_addr(self) -> Option<Value> {
67         match self.0 {
68             CValueInner::ByRef(addr) => Some(addr),
69             CValueInner::ByVal(_) | CValueInner::ByValPair(_, _) => None,
70         }
71     }
72
73     /// Load a value with layout.abi of scalar
74     pub fn load_scalar<'a>(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>) -> Value {
75         let layout = self.1;
76         match self.0 {
77             CValueInner::ByRef(addr) => {
78                 let scalar = match layout.abi {
79                     layout::Abi::Scalar(ref scalar) => scalar.clone(),
80                     _ => unreachable!(),
81                 };
82                 let clif_ty = scalar_to_clif_type(fx.tcx, scalar);
83                 fx.bcx.ins().load(clif_ty, MemFlags::new(), addr, 0)
84             }
85             CValueInner::ByVal(value) => value,
86             CValueInner::ByValPair(_, _) => bug!("Please use load_scalar_pair for ByValPair"),
87         }
88     }
89
90     /// Load a value pair with layout.abi of scalar pair
91     pub fn load_scalar_pair<'a>(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>) -> (Value, Value) {
92         let layout = self.1;
93         match self.0 {
94             CValueInner::ByRef(addr) => {
95                 let (a_scalar, b_scalar) = match &layout.abi {
96                     layout::Abi::ScalarPair(a, b) => (a, b),
97                     _ => unreachable!(),
98                 };
99                 let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar);
100                 let clif_ty1 = scalar_to_clif_type(fx.tcx, a_scalar.clone());
101                 let clif_ty2 = scalar_to_clif_type(fx.tcx, b_scalar.clone());
102                 let val1 = fx.bcx.ins().load(clif_ty1, MemFlags::new(), addr, 0);
103                 let val2 = fx.bcx.ins().load(
104                     clif_ty2,
105                     MemFlags::new(),
106                     addr,
107                     b_offset,
108                 );
109                 (val1, val2)
110             }
111             CValueInner::ByVal(_) => bug!("Please use load_scalar for ByVal"),
112             CValueInner::ByValPair(val1, val2) => (val1, val2),
113         }
114     }
115
116     pub fn value_field<'a>(
117         self,
118         fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
119         field: mir::Field,
120     ) -> CValue<'tcx> {
121         let layout = self.1;
122         let base = match self.0 {
123             CValueInner::ByRef(addr) => addr,
124             _ => bug!("place_field for {:?}", self),
125         };
126
127         let (field_ptr, field_layout) = codegen_field(fx, base, layout, field);
128         CValue::by_ref(field_ptr, field_layout)
129     }
130
131     pub fn unsize_value<'a>(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>, dest: CPlace<'tcx>) {
132         crate::unsize::coerce_unsized_into(fx, self, dest);
133     }
134
135     /// If `ty` is signed, `const_val` must already be sign extended.
136     pub fn const_val<'a>(
137         fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
138         ty: Ty<'tcx>,
139         const_val: u128,
140     ) -> CValue<'tcx> {
141         let clif_ty = fx.clif_type(ty).unwrap();
142         let layout = fx.layout_of(ty);
143
144         let val = match ty.sty {
145             ty::TyKind::Uint(UintTy::U128) | ty::TyKind::Int(IntTy::I128) => {
146                 let lsb = fx.bcx.ins().iconst(types::I64, const_val as u64 as i64);
147                 let msb = fx.bcx.ins().iconst(types::I64, (const_val >> 64) as u64 as i64);
148                 fx.bcx.ins().iconcat(lsb, msb)
149             }
150             ty::TyKind::Bool => {
151                 assert!(const_val == 0 || const_val == 1, "Invalid bool 0x{:032X}", const_val);
152                 fx.bcx.ins().iconst(types::I8, const_val as i64)
153             }
154             ty::TyKind::Uint(_) | ty::TyKind::Ref(..) | ty::TyKind::RawPtr(.. )=> {
155                 fx.bcx.ins().iconst(clif_ty, u64::try_from(const_val).expect("uint") as i64)
156             }
157             ty::TyKind::Int(_) => {
158                 fx.bcx.ins().iconst(clif_ty, const_val as i128 as i64)
159             }
160             _ => panic!("CValue::const_val for non bool/integer/pointer type {:?} is not allowed", ty),
161         };
162
163         CValue::by_val(val, layout)
164     }
165
166     pub fn unchecked_cast_to(self, layout: TyLayout<'tcx>) -> Self {
167         CValue(self.0, layout)
168     }
169 }
170
171 /// A place where you can write a value to or read a value from
172 #[derive(Debug, Copy, Clone)]
173 pub struct CPlace<'tcx> {
174     inner: CPlaceInner,
175     layout: TyLayout<'tcx>,
176 }
177
178 #[derive(Debug, Copy, Clone)]
179 pub enum CPlaceInner {
180     Var(Local),
181     Addr(Value, Option<Value>),
182     Stack(StackSlot),
183     NoPlace,
184 }
185
186 impl<'tcx> CPlace<'tcx> {
187     pub fn layout(&self) -> TyLayout<'tcx> {
188         self.layout
189     }
190
191     pub fn inner(&self) -> &CPlaceInner {
192         &self.inner
193     }
194
195     pub fn no_place(layout: TyLayout<'tcx>) -> CPlace<'tcx> {
196         CPlace{
197             inner: CPlaceInner::NoPlace,
198             layout
199         }
200     }
201
202     pub fn new_stack_slot(
203         fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
204         ty: Ty<'tcx>,
205     ) -> CPlace<'tcx> {
206         let layout = fx.layout_of(ty);
207         assert!(!layout.is_unsized());
208         if layout.size.bytes() == 0 {
209             return CPlace {
210                 inner: CPlaceInner::NoPlace,
211                 layout,
212             };
213         }
214
215         let stack_slot = fx.bcx.create_stack_slot(StackSlotData {
216             kind: StackSlotKind::ExplicitSlot,
217             size: layout.size.bytes() as u32,
218             offset: None,
219         });
220         CPlace {
221             inner: CPlaceInner::Stack(stack_slot),
222             layout,
223         }
224     }
225
226     pub fn new_var(
227         fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
228         local: Local,
229         layout: TyLayout<'tcx>,
230     ) -> CPlace<'tcx> {
231         fx.bcx
232             .declare_var(mir_var(local), fx.clif_type(layout.ty).unwrap());
233         CPlace {
234             inner: CPlaceInner::Var(local),
235             layout,
236         }
237     }
238
239     pub fn for_addr(addr: Value, layout: TyLayout<'tcx>) -> CPlace<'tcx> {
240         CPlace {
241             inner: CPlaceInner::Addr(addr, None),
242             layout,
243         }
244     }
245
246     pub fn for_addr_with_extra(addr: Value, extra: Value, layout: TyLayout<'tcx>) -> CPlace<'tcx> {
247         CPlace {
248             inner: CPlaceInner::Addr(addr, Some(extra)),
249             layout,
250         }
251     }
252
253     pub fn to_cvalue(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>) -> CValue<'tcx> {
254         let layout = self.layout();
255         match self.inner {
256             CPlaceInner::Var(var) => CValue::by_val(fx.bcx.use_var(mir_var(var)), layout),
257             CPlaceInner::Addr(addr, extra) => {
258                 assert!(extra.is_none(), "unsized values are not yet supported");
259                 CValue::by_ref(addr, layout)
260             }
261             CPlaceInner::Stack(stack_slot) => CValue::by_ref(
262                 fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0),
263                 layout,
264             ),
265             CPlaceInner::NoPlace => CValue::by_ref(
266                 fx.bcx
267                     .ins()
268                     .iconst(fx.pointer_type, fx.pointer_type.bytes() as i64),
269                 layout,
270             ),
271         }
272     }
273
274     pub fn to_addr(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>) -> Value {
275         match self.to_addr_maybe_unsized(fx) {
276             (addr, None) => addr,
277             (_, Some(_)) => bug!("Expected sized cplace, found {:?}", self),
278         }
279     }
280
281     pub fn to_addr_maybe_unsized(
282         self,
283         fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
284     ) -> (Value, Option<Value>) {
285         match self.inner {
286             CPlaceInner::Addr(addr, extra) => (addr, extra),
287             CPlaceInner::Stack(stack_slot) => (
288                 fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0),
289                 None,
290             ),
291             CPlaceInner::NoPlace => (fx.bcx.ins().iconst(fx.pointer_type, 45), None),
292             CPlaceInner::Var(_) => bug!("Expected CPlace::Addr, found CPlace::Var"),
293         }
294     }
295
296     pub fn write_cvalue(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>, from: CValue<'tcx>) {
297         use rustc::hir::Mutability::*;
298
299         let from_ty = from.layout().ty;
300         let to_ty = self.layout().ty;
301
302         fn assert_assignable<'tcx>(fx: &FunctionCx<'_, 'tcx, impl Backend>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) {
303             match (&from_ty.sty, &to_ty.sty) {
304                 (ty::Ref(_, t, MutImmutable), ty::Ref(_, u, MutImmutable))
305                 | (ty::Ref(_, t, MutMutable), ty::Ref(_, u, MutImmutable))
306                 | (ty::Ref(_, t, MutMutable), ty::Ref(_, u, MutMutable)) => {
307                     assert_assignable(fx, t, u);
308                     // &mut T -> &T is allowed
309                     // &'a T -> &'b T is allowed
310                 }
311                 (ty::Ref(_, _, MutImmutable), ty::Ref(_, _, MutMutable)) => {
312                     panic!("Cant assign value of type {} to place of type {}", from_ty, to_ty)
313                 }
314                 (ty::FnPtr(_), ty::FnPtr(_)) => {
315                     let from_sig = fx.tcx.normalize_erasing_late_bound_regions(
316                         ParamEnv::reveal_all(),
317                         &from_ty.fn_sig(fx.tcx),
318                     );
319                     let to_sig = fx.tcx.normalize_erasing_late_bound_regions(
320                         ParamEnv::reveal_all(),
321                         &to_ty.fn_sig(fx.tcx),
322                     );
323                     assert_eq!(
324                         from_sig, to_sig,
325                         "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}",
326                         from_sig, to_sig, fx,
327                     );
328                     // fn(&T) -> for<'l> fn(&'l T) is allowed
329                 }
330                 (ty::Dynamic(from_traits, _), ty::Dynamic(to_traits, _)) => {
331                     let from_traits = fx.tcx.normalize_erasing_late_bound_regions(
332                         ParamEnv::reveal_all(),
333                         from_traits,
334                     );
335                     let to_traits = fx.tcx.normalize_erasing_late_bound_regions(
336                         ParamEnv::reveal_all(),
337                         to_traits,
338                     );
339                     assert_eq!(
340                         from_traits, to_traits,
341                         "Can't write trait object of incompatible traits {:?} to place with traits {:?}\n\n{:#?}",
342                         from_traits, to_traits, fx,
343                     );
344                     // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed
345                 }
346                 _ => {
347                     assert_eq!(
348                         from_ty,
349                         to_ty,
350                         "Can't write value with incompatible type {:?} to place with type {:?}\n\n{:#?}",
351                         from_ty,
352                         to_ty,
353                         fx,
354                     );
355                 }
356             }
357         }
358
359         assert_assignable(fx, from_ty, to_ty);
360
361         let dst_layout = self.layout();
362         let addr = match self.inner {
363             CPlaceInner::Var(var) => {
364                 let data = from.load_scalar(fx);
365                 fx.bcx.def_var(mir_var(var), data);
366                 return;
367             }
368             CPlaceInner::Addr(addr, None) => addr,
369             CPlaceInner::Stack(stack_slot) => {
370                 fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0)
371             }
372             CPlaceInner::NoPlace => {
373                 if dst_layout.abi != Abi::Uninhabited {
374                     assert_eq!(dst_layout.size.bytes(), 0, "{:?}", dst_layout);
375                 }
376                 return;
377             }
378             CPlaceInner::Addr(_, Some(_)) => bug!("Can't write value to unsized place {:?}", self),
379         };
380
381         match from.0 {
382             CValueInner::ByVal(val) => {
383                 fx.bcx.ins().store(MemFlags::new(), val, addr, 0);
384             }
385             CValueInner::ByValPair(value, extra) => {
386                 match dst_layout.abi {
387                     Abi::ScalarPair(ref a_scalar, ref b_scalar) => {
388                         let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar);
389                         fx.bcx.ins().store(MemFlags::new(), value, addr, 0);
390                         fx.bcx.ins().store(
391                             MemFlags::new(),
392                             extra,
393                             addr,
394                             b_offset,
395                         );
396                     }
397                     _ => bug!(
398                         "Non ScalarPair abi {:?} for ByValPair CValue",
399                         dst_layout.abi
400                     ),
401                 }
402             }
403             CValueInner::ByRef(from_addr) => {
404                 let src_layout = from.1;
405                 let size = dst_layout.size.bytes();
406                 let src_align = src_layout.align.abi.bytes() as u8;
407                 let dst_align = dst_layout.align.abi.bytes() as u8;
408                 fx.bcx.emit_small_memcpy(
409                     fx.module.target_config(),
410                     addr,
411                     from_addr,
412                     size,
413                     dst_align,
414                     src_align,
415                 );
416             }
417         }
418     }
419
420     pub fn place_field(
421         self,
422         fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
423         field: mir::Field,
424     ) -> CPlace<'tcx> {
425         let layout = self.layout();
426         let (base, extra) = self.to_addr_maybe_unsized(fx);
427
428         let (field_ptr, field_layout) = codegen_field(fx, base, layout, field);
429         if field_layout.is_unsized() {
430             CPlace::for_addr_with_extra(field_ptr, extra.unwrap(), field_layout)
431         } else {
432             CPlace::for_addr(field_ptr, field_layout)
433         }
434     }
435
436     pub fn place_index(
437         self,
438         fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
439         index: Value,
440     ) -> CPlace<'tcx> {
441         let (elem_layout, addr) = match self.layout().ty.sty {
442             ty::Array(elem_ty, _) => (fx.layout_of(elem_ty), self.to_addr(fx)),
443             ty::Slice(elem_ty) => (fx.layout_of(elem_ty), self.to_addr_maybe_unsized(fx).0),
444             _ => bug!("place_index({:?})", self.layout().ty),
445         };
446
447         let offset = fx
448             .bcx
449             .ins()
450             .imul_imm(index, elem_layout.size.bytes() as i64);
451
452         CPlace::for_addr(fx.bcx.ins().iadd(addr, offset), elem_layout)
453     }
454
455     pub fn place_deref(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>) -> CPlace<'tcx> {
456         let inner_layout = fx.layout_of(self.layout().ty.builtin_deref(true).unwrap().ty);
457         if !inner_layout.is_unsized() {
458             CPlace::for_addr(self.to_cvalue(fx).load_scalar(fx), inner_layout)
459         } else {
460             let (addr, extra) = self.to_cvalue(fx).load_scalar_pair(fx);
461             CPlace::for_addr_with_extra(addr, extra, inner_layout)
462         }
463     }
464
465     pub fn write_place_ref(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>, dest: CPlace<'tcx>) {
466         if !self.layout().is_unsized() {
467             let ptr = CValue::by_val(self.to_addr(fx), dest.layout());
468             dest.write_cvalue(fx, ptr);
469         } else {
470             let (value, extra) = self.to_addr_maybe_unsized(fx);
471             let ptr = CValue::by_val_pair(value, extra.expect("unsized type without metadata"), dest.layout());
472             dest.write_cvalue(fx, ptr);
473         }
474     }
475
476     pub fn unchecked_cast_to(self, layout: TyLayout<'tcx>) -> Self {
477         assert!(!self.layout().is_unsized());
478         match self.inner {
479             CPlaceInner::NoPlace => {
480                 assert!(layout.size.bytes() == 0);
481             }
482             _ => {}
483         }
484         CPlace {
485             inner: self.inner,
486             layout,
487         }
488     }
489
490     pub fn downcast_variant(
491         self,
492         fx: &FunctionCx<'_, 'tcx, impl Backend>,
493         variant: VariantIdx,
494     ) -> Self {
495         let layout = self.layout().for_variant(fx, variant);
496         self.unchecked_cast_to(layout)
497     }
498 }