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