]>
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), | |
94222f64 XL |
28 | (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { |
29 | let old_info = | |
30 | old_info.expect("unsized_info: missing old info for trait upcasting coercion"); | |
31 | if data_a.principal_def_id() == data_b.principal_def_id() { | |
32 | return old_info; | |
33 | } | |
34 | ||
35 | // trait upcasting coercion | |
36 | let vptr_entry_idx = | |
37 | fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((source, target)); | |
38 | ||
39 | if let Some(entry_idx) = vptr_entry_idx { | |
40 | let entry_idx = u32::try_from(entry_idx).unwrap(); | |
41 | let entry_offset = entry_idx * fx.pointer_type.bytes(); | |
42 | let vptr_ptr = Pointer::new(old_info).offset_i64(fx, entry_offset.into()).load( | |
43 | fx, | |
44 | fx.pointer_type, | |
45 | crate::vtable::vtable_memflags(), | |
46 | ); | |
47 | vptr_ptr | |
48 | } else { | |
49 | old_info | |
50 | } | |
29967ef6 | 51 | } |
136023e0 | 52 | (_, &ty::Dynamic(ref data, ..)) => crate::vtable::get_vtable(fx, source, data.principal()), |
6a06907d | 53 | _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), |
29967ef6 XL |
54 | } |
55 | } | |
56 | ||
94222f64 XL |
57 | /// Coerce `src` to `dst_ty`. |
58 | fn unsize_ptr<'tcx>( | |
6a06907d | 59 | fx: &mut FunctionCx<'_, '_, 'tcx>, |
29967ef6 XL |
60 | src: Value, |
61 | src_layout: TyAndLayout<'tcx>, | |
62 | dst_layout: TyAndLayout<'tcx>, | |
94222f64 | 63 | old_info: Option<Value>, |
29967ef6 XL |
64 | ) -> (Value, Value) { |
65 | match (&src_layout.ty.kind(), &dst_layout.ty.kind()) { | |
66 | (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) | |
67 | | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | |
68 | | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { | |
5099ac24 | 69 | (src, unsized_info(fx, *a, *b, old_info)) |
29967ef6 XL |
70 | } |
71 | (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { | |
72 | let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty()); | |
94222f64 | 73 | (src, unsized_info(fx, a, b, old_info)) |
29967ef6 XL |
74 | } |
75 | (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { | |
76 | assert_eq!(def_a, def_b); | |
77 | ||
94222f64 XL |
78 | if src_layout == dst_layout { |
79 | return (src, old_info.unwrap()); | |
80 | } | |
81 | ||
29967ef6 XL |
82 | let mut result = None; |
83 | for i in 0..src_layout.fields.count() { | |
84 | let src_f = src_layout.field(fx, i); | |
85 | assert_eq!(src_layout.fields.offset(i).bytes(), 0); | |
86 | assert_eq!(dst_layout.fields.offset(i).bytes(), 0); | |
87 | if src_f.is_zst() { | |
88 | continue; | |
89 | } | |
90 | assert_eq!(src_layout.size, src_f.size); | |
91 | ||
92 | let dst_f = dst_layout.field(fx, i); | |
93 | assert_ne!(src_f.ty, dst_f.ty); | |
94 | assert_eq!(result, None); | |
94222f64 | 95 | result = Some(unsize_ptr(fx, src, src_f, dst_f, old_info)); |
29967ef6 XL |
96 | } |
97 | result.unwrap() | |
98 | } | |
94222f64 | 99 | _ => bug!("unsize_ptr: called on bad types"), |
29967ef6 XL |
100 | } |
101 | } | |
102 | ||
103 | /// Coerce `src`, which is a reference to a value of type `src_ty`, | |
104 | /// to a value of type `dst_ty` and store the result in `dst` | |
105 | pub(crate) fn coerce_unsized_into<'tcx>( | |
6a06907d | 106 | fx: &mut FunctionCx<'_, '_, 'tcx>, |
29967ef6 XL |
107 | src: CValue<'tcx>, |
108 | dst: CPlace<'tcx>, | |
109 | ) { | |
110 | let src_ty = src.layout().ty; | |
111 | let dst_ty = dst.layout().ty; | |
112 | let mut coerce_ptr = || { | |
6a06907d XL |
113 | let (base, info) = |
114 | if fx.layout_of(src.layout().ty.builtin_deref(true).unwrap().ty).is_unsized() { | |
94222f64 XL |
115 | let (old_base, old_info) = src.load_scalar_pair(fx); |
116 | unsize_ptr(fx, old_base, src.layout(), dst.layout(), Some(old_info)) | |
6a06907d XL |
117 | } else { |
118 | let base = src.load_scalar(fx); | |
94222f64 | 119 | unsize_ptr(fx, base, src.layout(), dst.layout(), None) |
6a06907d | 120 | }; |
29967ef6 XL |
121 | dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout())); |
122 | }; | |
123 | match (&src_ty.kind(), &dst_ty.kind()) { | |
124 | (&ty::Ref(..), &ty::Ref(..)) | |
125 | | (&ty::Ref(..), &ty::RawPtr(..)) | |
126 | | (&ty::RawPtr(..), &ty::RawPtr(..)) => coerce_ptr(), | |
127 | (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { | |
128 | assert_eq!(def_a, def_b); | |
129 | ||
5e7ed085 | 130 | for i in 0..def_a.variant(VariantIdx::new(0)).fields.len() { |
29967ef6 XL |
131 | let src_f = src.value_field(fx, mir::Field::new(i)); |
132 | let dst_f = dst.place_field(fx, mir::Field::new(i)); | |
133 | ||
134 | if dst_f.layout().is_zst() { | |
135 | continue; | |
136 | } | |
137 | ||
138 | if src_f.layout().ty == dst_f.layout().ty { | |
139 | dst_f.write_cvalue(fx, src_f); | |
140 | } else { | |
141 | coerce_unsized_into(fx, src_f, dst_f); | |
142 | } | |
143 | } | |
144 | } | |
6a06907d | 145 | _ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}", src_ty, dst_ty), |
29967ef6 XL |
146 | } |
147 | } | |
148 | ||
149 | // Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/glue.rs | |
150 | ||
151 | pub(crate) fn size_and_align_of_dst<'tcx>( | |
6a06907d | 152 | fx: &mut FunctionCx<'_, '_, 'tcx>, |
29967ef6 XL |
153 | layout: TyAndLayout<'tcx>, |
154 | info: Value, | |
155 | ) -> (Value, Value) { | |
064997fb | 156 | assert!(layout.is_unsized() || layout.abi == Abi::Uninhabited); |
29967ef6 XL |
157 | match layout.ty.kind() { |
158 | ty::Dynamic(..) => { | |
159 | // load size/align from vtable | |
6a06907d | 160 | (crate::vtable::size_of_obj(fx, info), crate::vtable::min_align_of_obj(fx, info)) |
29967ef6 XL |
161 | } |
162 | ty::Slice(_) | ty::Str => { | |
163 | let unit = layout.field(fx, 0); | |
164 | // The info in this case is the length of the str, so the size is that | |
165 | // times the unit size. | |
166 | ( | |
167 | fx.bcx.ins().imul_imm(info, unit.size.bytes() as i64), | |
6a06907d | 168 | fx.bcx.ins().iconst(fx.pointer_type, unit.align.abi.bytes() as i64), |
29967ef6 XL |
169 | ) |
170 | } | |
171 | _ => { | |
172 | // First get the size of all statically known fields. | |
173 | // Don't use size_of because it also rounds up to alignment, which we | |
174 | // want to avoid, as the unsized field's alignment could be smaller. | |
175 | assert!(!layout.ty.is_simd()); | |
176 | ||
177 | let i = layout.fields.count() - 1; | |
178 | let sized_size = layout.fields.offset(i).bytes(); | |
179 | let sized_align = layout.align.abi.bytes(); | |
180 | let sized_align = fx.bcx.ins().iconst(fx.pointer_type, sized_align as i64); | |
181 | ||
182 | // Recurse to get the size of the dynamically sized field (must be | |
183 | // the last field). | |
184 | let field_layout = layout.field(fx, i); | |
185 | let (unsized_size, mut unsized_align) = size_and_align_of_dst(fx, field_layout, info); | |
186 | ||
187 | // FIXME (#26403, #27023): We should be adding padding | |
188 | // to `sized_size` (to accommodate the `unsized_align` | |
189 | // required of the unsized field that follows) before | |
190 | // summing it with `sized_size`. (Note that since #26403 | |
191 | // is unfixed, we do not yet add the necessary padding | |
192 | // here. But this is where the add would go.) | |
193 | ||
194 | // Return the sum of sizes and max of aligns. | |
195 | let size = fx.bcx.ins().iadd_imm(unsized_size, sized_size as i64); | |
196 | ||
197 | // Packed types ignore the alignment of their fields. | |
198 | if let ty::Adt(def, _) = layout.ty.kind() { | |
5e7ed085 | 199 | if def.repr().packed() { |
29967ef6 XL |
200 | unsized_align = sized_align; |
201 | } | |
202 | } | |
203 | ||
204 | // Choose max of two known alignments (combined value must | |
205 | // be aligned according to more restrictive of the two). | |
6a06907d | 206 | let cmp = fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, sized_align, unsized_align); |
29967ef6 XL |
207 | let align = fx.bcx.ins().select(cmp, sized_align, unsized_align); |
208 | ||
209 | // Issue #27023: must add any necessary padding to `size` | |
210 | // (to make it a multiple of `align`) before returning it. | |
211 | // | |
212 | // Namely, the returned size should be, in C notation: | |
213 | // | |
214 | // `size + ((size & (align-1)) ? align : 0)` | |
215 | // | |
216 | // emulated via the semi-standard fast bit trick: | |
217 | // | |
218 | // `(size + (align-1)) & -align` | |
219 | let addend = fx.bcx.ins().iadd_imm(align, -1); | |
220 | let add = fx.bcx.ins().iadd(size, addend); | |
221 | let neg = fx.bcx.ins().ineg(align); | |
222 | let size = fx.bcx.ins().band(add, neg); | |
223 | ||
224 | (size, align) | |
225 | } | |
226 | } | |
227 | } |