]>
Commit | Line | Data |
---|---|---|
29967ef6 XL |
1 | //! Codegen of the [`PointerCast::Unsize`] operation. |
2 | //! | |
3 | //! [`PointerCast::Unsize`]: `rustc_middle::ty::adjustment::PointerCast::Unsize` | |
4 | ||
5 | use crate::prelude::*; | |
6 | ||
7 | // Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/base.rs#L159-L307 | |
8 | ||
9 | /// Retrieve the information we are losing (making dynamic) in an unsizing | |
10 | /// adjustment. | |
11 | /// | |
12 | /// The `old_info` argument is a bit funny. It is intended for use | |
13 | /// in an upcast, where the new vtable for an object will be derived | |
14 | /// from the old one. | |
15 | pub(crate) fn unsized_info<'tcx>( | |
6a06907d | 16 | fx: &mut FunctionCx<'_, '_, 'tcx>, |
29967ef6 XL |
17 | source: Ty<'tcx>, |
18 | target: Ty<'tcx>, | |
19 | old_info: Option<Value>, | |
20 | ) -> Value { | |
21 | let (source, target) = | |
6a06907d | 22 | fx.tcx.struct_lockstep_tails_erasing_lifetimes(source, target, ParamEnv::reveal_all()); |
29967ef6 | 23 | match (&source.kind(), &target.kind()) { |
6a06907d XL |
24 | (&ty::Array(_, len), &ty::Slice(_)) => fx |
25 | .bcx | |
26 | .ins() | |
27 | .iconst(fx.pointer_type, len.eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64), | |
29967ef6 XL |
28 | (&ty::Dynamic(..), &ty::Dynamic(..)) => { |
29 | // For now, upcasts are limited to changes in marker | |
30 | // traits, and hence never actually require an actual | |
31 | // change to the vtable. | |
32 | old_info.expect("unsized_info: missing old info for trait upcast") | |
33 | } | |
34 | (_, &ty::Dynamic(ref data, ..)) => { | |
35 | crate::vtable::get_vtable(fx, fx.layout_of(source), data.principal()) | |
36 | } | |
6a06907d | 37 | _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), |
29967ef6 XL |
38 | } |
39 | } | |
40 | ||
41 | /// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. | |
42 | fn unsize_thin_ptr<'tcx>( | |
6a06907d | 43 | fx: &mut FunctionCx<'_, '_, 'tcx>, |
29967ef6 XL |
44 | src: Value, |
45 | src_layout: TyAndLayout<'tcx>, | |
46 | dst_layout: TyAndLayout<'tcx>, | |
47 | ) -> (Value, Value) { | |
48 | match (&src_layout.ty.kind(), &dst_layout.ty.kind()) { | |
49 | (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) | |
50 | | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | |
51 | | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { | |
52 | assert!(!fx.layout_of(a).is_unsized()); | |
53 | (src, unsized_info(fx, a, b, None)) | |
54 | } | |
55 | (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { | |
56 | let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty()); | |
57 | assert!(!fx.layout_of(a).is_unsized()); | |
58 | (src, unsized_info(fx, a, b, None)) | |
59 | } | |
60 | (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { | |
61 | assert_eq!(def_a, def_b); | |
62 | ||
63 | let mut result = None; | |
64 | for i in 0..src_layout.fields.count() { | |
65 | let src_f = src_layout.field(fx, i); | |
66 | assert_eq!(src_layout.fields.offset(i).bytes(), 0); | |
67 | assert_eq!(dst_layout.fields.offset(i).bytes(), 0); | |
68 | if src_f.is_zst() { | |
69 | continue; | |
70 | } | |
71 | assert_eq!(src_layout.size, src_f.size); | |
72 | ||
73 | let dst_f = dst_layout.field(fx, i); | |
74 | assert_ne!(src_f.ty, dst_f.ty); | |
75 | assert_eq!(result, None); | |
76 | result = Some(unsize_thin_ptr(fx, src, src_f, dst_f)); | |
77 | } | |
78 | result.unwrap() | |
79 | } | |
80 | _ => bug!("unsize_thin_ptr: called on bad types"), | |
81 | } | |
82 | } | |
83 | ||
84 | /// Coerce `src`, which is a reference to a value of type `src_ty`, | |
85 | /// to a value of type `dst_ty` and store the result in `dst` | |
86 | pub(crate) fn coerce_unsized_into<'tcx>( | |
6a06907d | 87 | fx: &mut FunctionCx<'_, '_, 'tcx>, |
29967ef6 XL |
88 | src: CValue<'tcx>, |
89 | dst: CPlace<'tcx>, | |
90 | ) { | |
91 | let src_ty = src.layout().ty; | |
92 | let dst_ty = dst.layout().ty; | |
93 | let mut coerce_ptr = || { | |
6a06907d XL |
94 | let (base, info) = |
95 | if fx.layout_of(src.layout().ty.builtin_deref(true).unwrap().ty).is_unsized() { | |
96 | // fat-ptr to fat-ptr unsize preserves the vtable | |
97 | // i.e., &'a fmt::Debug+Send => &'a fmt::Debug | |
98 | src.load_scalar_pair(fx) | |
99 | } else { | |
100 | let base = src.load_scalar(fx); | |
101 | unsize_thin_ptr(fx, base, src.layout(), dst.layout()) | |
102 | }; | |
29967ef6 XL |
103 | dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout())); |
104 | }; | |
105 | match (&src_ty.kind(), &dst_ty.kind()) { | |
106 | (&ty::Ref(..), &ty::Ref(..)) | |
107 | | (&ty::Ref(..), &ty::RawPtr(..)) | |
108 | | (&ty::RawPtr(..), &ty::RawPtr(..)) => coerce_ptr(), | |
109 | (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { | |
110 | assert_eq!(def_a, def_b); | |
111 | ||
112 | for i in 0..def_a.variants[VariantIdx::new(0)].fields.len() { | |
113 | let src_f = src.value_field(fx, mir::Field::new(i)); | |
114 | let dst_f = dst.place_field(fx, mir::Field::new(i)); | |
115 | ||
116 | if dst_f.layout().is_zst() { | |
117 | continue; | |
118 | } | |
119 | ||
120 | if src_f.layout().ty == dst_f.layout().ty { | |
121 | dst_f.write_cvalue(fx, src_f); | |
122 | } else { | |
123 | coerce_unsized_into(fx, src_f, dst_f); | |
124 | } | |
125 | } | |
126 | } | |
6a06907d | 127 | _ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}", src_ty, dst_ty), |
29967ef6 XL |
128 | } |
129 | } | |
130 | ||
131 | // Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/glue.rs | |
132 | ||
133 | pub(crate) fn size_and_align_of_dst<'tcx>( | |
6a06907d | 134 | fx: &mut FunctionCx<'_, '_, 'tcx>, |
29967ef6 XL |
135 | layout: TyAndLayout<'tcx>, |
136 | info: Value, | |
137 | ) -> (Value, Value) { | |
138 | if !layout.is_unsized() { | |
6a06907d XL |
139 | let size = fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64); |
140 | let align = fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64); | |
29967ef6 XL |
141 | return (size, align); |
142 | } | |
143 | match layout.ty.kind() { | |
144 | ty::Dynamic(..) => { | |
145 | // load size/align from vtable | |
6a06907d | 146 | (crate::vtable::size_of_obj(fx, info), crate::vtable::min_align_of_obj(fx, info)) |
29967ef6 XL |
147 | } |
148 | ty::Slice(_) | ty::Str => { | |
149 | let unit = layout.field(fx, 0); | |
150 | // The info in this case is the length of the str, so the size is that | |
151 | // times the unit size. | |
152 | ( | |
153 | fx.bcx.ins().imul_imm(info, unit.size.bytes() as i64), | |
6a06907d | 154 | fx.bcx.ins().iconst(fx.pointer_type, unit.align.abi.bytes() as i64), |
29967ef6 XL |
155 | ) |
156 | } | |
157 | _ => { | |
158 | // First get the size of all statically known fields. | |
159 | // Don't use size_of because it also rounds up to alignment, which we | |
160 | // want to avoid, as the unsized field's alignment could be smaller. | |
161 | assert!(!layout.ty.is_simd()); | |
162 | ||
163 | let i = layout.fields.count() - 1; | |
164 | let sized_size = layout.fields.offset(i).bytes(); | |
165 | let sized_align = layout.align.abi.bytes(); | |
166 | let sized_align = fx.bcx.ins().iconst(fx.pointer_type, sized_align as i64); | |
167 | ||
168 | // Recurse to get the size of the dynamically sized field (must be | |
169 | // the last field). | |
170 | let field_layout = layout.field(fx, i); | |
171 | let (unsized_size, mut unsized_align) = size_and_align_of_dst(fx, field_layout, info); | |
172 | ||
173 | // FIXME (#26403, #27023): We should be adding padding | |
174 | // to `sized_size` (to accommodate the `unsized_align` | |
175 | // required of the unsized field that follows) before | |
176 | // summing it with `sized_size`. (Note that since #26403 | |
177 | // is unfixed, we do not yet add the necessary padding | |
178 | // here. But this is where the add would go.) | |
179 | ||
180 | // Return the sum of sizes and max of aligns. | |
181 | let size = fx.bcx.ins().iadd_imm(unsized_size, sized_size as i64); | |
182 | ||
183 | // Packed types ignore the alignment of their fields. | |
184 | if let ty::Adt(def, _) = layout.ty.kind() { | |
185 | if def.repr.packed() { | |
186 | unsized_align = sized_align; | |
187 | } | |
188 | } | |
189 | ||
190 | // Choose max of two known alignments (combined value must | |
191 | // be aligned according to more restrictive of the two). | |
6a06907d | 192 | let cmp = fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, sized_align, unsized_align); |
29967ef6 XL |
193 | let align = fx.bcx.ins().select(cmp, sized_align, unsized_align); |
194 | ||
195 | // Issue #27023: must add any necessary padding to `size` | |
196 | // (to make it a multiple of `align`) before returning it. | |
197 | // | |
198 | // Namely, the returned size should be, in C notation: | |
199 | // | |
200 | // `size + ((size & (align-1)) ? align : 0)` | |
201 | // | |
202 | // emulated via the semi-standard fast bit trick: | |
203 | // | |
204 | // `(size + (align-1)) & -align` | |
205 | let addend = fx.bcx.ins().iadd_imm(align, -1); | |
206 | let add = fx.bcx.ins().iadd(size, addend); | |
207 | let neg = fx.bcx.ins().ineg(align); | |
208 | let size = fx.bcx.ins().band(add, neg); | |
209 | ||
210 | (size, align) | |
211 | } | |
212 | } | |
213 | } |