1use rustc_abi::{FieldIdx, FieldsShape, Size, VariantIdx, Variants};
8use rustc_ast::Mutability;
9use rustc_data_structures::fx::FxIndexMap;
10use rustc_middle::mir::{Rvalue, WithRetag};
11use rustc_middle::ty;
12use rustc_middle::ty::layout::TyAndLayout;
13
14use crate::mir::FunctionCx;
15use crate::mir::operand::{OperandRef, OperandRefBuilder, OperandValue};
16use crate::mir::place::PlaceRef;
17use crate::traits::{
18 BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, LayoutTypeCodegenMethods,
19};
20use crate::{RetagFlags, RetagInfo};
21
22pub(crate) fn rvalue_needs_retag(rvalue: &Rvalue<'_>) -> bool {
23 !#[allow(non_exhaustive_omitted_patterns)] match rvalue {
Rvalue::Ref(..) => true,
_ => false,
}matches!(rvalue, Rvalue::Ref(..)) && !#[allow(non_exhaustive_omitted_patterns)] match rvalue {
Rvalue::Use(.., WithRetag::No) => true,
_ => false,
}matches!(rvalue, Rvalue::Use(.., WithRetag::No))
25}
26
27#[derive(#[automatically_derived]
impl<V: ::core::fmt::Debug> ::core::fmt::Debug for RetagPlan<V> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
RetagPlan::EmitRetag(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"EmitRetag", &__self_0),
RetagPlan::Recurse {
field_plans: __self_0, variant_plans: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"Recurse", "field_plans", __self_0, "variant_plans",
&__self_1),
}
}
}Debug)]
29enum RetagPlan<V> {
30 EmitRetag(RetagInfo<V>),
32 Recurse {
35 field_plans: FxIndexMap<FieldIdx, RetagPlan<V>>,
36 variant_plans: FxIndexMap<VariantIdx, RetagPlan<V>>,
37 },
38}
39
40impl<V> RetagPlan<V> {
41 fn for_field(self, ix: FieldIdx) -> Self {
43 let mut field_plans = FxIndexMap::default();
44 field_plans.insert(ix, self);
45 RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() }
46 }
47}
48
49impl<'a, 'tcx, V> RetagPlan<V> {
50 fn build<Bx: BuilderMethods<'a, 'tcx>>(
52 bx: &mut Bx,
53 layout: TyAndLayout<'tcx>,
54 is_fn_entry: bool,
55 ) -> Option<RetagPlan<Bx::Value>> {
56 if layout.is_sized() && layout.size < bx.tcx().data_layout.pointer_size() {
60 return None;
61 }
62 match layout.ty.kind() {
64 &ty::Ref(_, pointee, mt) => {
65 let pointee_layout = bx.layout_of(pointee);
66 Self::emit_retag(bx, pointee_layout, Some(mt), is_fn_entry)
67 }
68 &ty::RawPtr(_, _) => None,
69 ty::Adt(adt, _) if adt.is_box() => Self::visit_box(bx, layout, is_fn_entry),
72 ty::Adt(adt, _) if adt.is_maybe_dangling() => None,
74 _ => Self::walk_value(bx, layout, is_fn_entry),
75 }
76 }
77
78 fn walk_value<Bx: BuilderMethods<'a, 'tcx>>(
80 bx: &mut Bx,
81 layout: TyAndLayout<'tcx>,
82 is_fn_entry: bool,
83 ) -> Option<RetagPlan<Bx::Value>> {
84 let mut field_plans = FxIndexMap::default();
85 let mut variant_plans = FxIndexMap::default();
86
87 match &layout.fields {
88 FieldsShape::Union(_) | FieldsShape::Primitive => {}
89 _ => {
90 for ix in layout.fields.index_by_increasing_offset() {
91 let field_layout = layout.field(bx, ix);
92 if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
93 field_plans.insert(FieldIdx::from_usize(ix), plan);
94 }
95 }
96 }
97 }
98
99 match &layout.variants {
100 Variants::Single { .. } | Variants::Empty => {}
101 Variants::Multiple { variants, .. } => {
102 for ix in variants.indices() {
103 let variant_layout = layout.for_variant(bx, ix);
104 if let Some(plan) = Self::build(bx, variant_layout, is_fn_entry) {
105 variant_plans.insert(ix, plan);
106 }
107 }
108 }
109 }
110
111 (!field_plans.is_empty() || !variant_plans.is_empty())
112 .then(|| RetagPlan::Recurse { field_plans, variant_plans })
113 }
114
115 fn visit_box<Bx: BuilderMethods<'a, 'tcx>>(
117 bx: &mut Bx,
118 layout: TyAndLayout<'tcx>,
119 is_fn_entry: bool,
120 ) -> Option<RetagPlan<Bx::Value>> {
121 if !layout.ty.is_box() {
::core::panicking::panic("assertion failed: layout.ty.is_box()")
};assert!(layout.ty.is_box());
122 {
match (&layout.fields.count(), &2) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val,
::core::option::Option::Some(format_args!("`Box` must have exactly 2 fields")));
}
}
}
};assert_eq!(layout.fields.count(), 2, "`Box` must have exactly 2 fields");
123 let mut field_plans = FxIndexMap::default();
124
125 if layout.ty.is_box_global(bx.tcx()) {
127 let boxed_ty = layout.ty.expect_boxed_ty();
128 let boxed_layout = bx.layout_of(boxed_ty);
129 if let Some(mut plan) = Self::emit_retag(bx, boxed_layout, None, is_fn_entry) {
130 let unique = layout.field(bx, 0);
132 {
match (&unique.fields.count(), &2) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(unique.fields.count(), 2);
133 plan = plan.for_field(FieldIdx::ZERO);
134
135 let nonnull = unique.field(bx, 0);
137 {
match (&nonnull.fields.count(), &1) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(nonnull.fields.count(), 1);
138 plan = plan.for_field(FieldIdx::ZERO);
139
140 let pattern = nonnull.field(bx, 0);
142 let ty::Pat(base, _) = pattern.ty.kind() else {
143 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("`NonNull` should contain a pattern type")));
}unreachable!("`NonNull` should contain a pattern type")
144 };
145 {
match (&base.builtin_deref(true), &Some(boxed_ty)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(base.builtin_deref(true), Some(boxed_ty));
146
147 field_plans.insert(FieldIdx::ZERO, plan);
148 }
149 }
150
151 let field_layout = layout.field(bx, 1);
153 if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
154 field_plans.insert(FieldIdx::ONE, plan);
155 }
156
157 (!field_plans.is_empty())
158 .then(|| RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() })
159 }
160
161 fn emit_retag<Bx: BuilderMethods<'a, 'tcx>>(
166 bx: &mut Bx,
167 pointee_layout: TyAndLayout<'tcx>,
168 ptr_kind: Option<Mutability>,
169 is_fn_entry: bool,
170 ) -> Option<RetagPlan<Bx::Value>> {
171 let tcx = bx.tcx();
172
173 let pointee_ty = pointee_layout.ty;
174
175 let is_mutable = #[allow(non_exhaustive_omitted_patterns)] match ptr_kind {
Some(Mutability::Mut) | None => true,
_ => false,
}matches!(ptr_kind, Some(Mutability::Mut) | None);
176 let is_unpin = pointee_ty.is_unpin(tcx, bx.typing_env());
177 let is_freeze = pointee_ty.is_freeze(tcx, bx.typing_env());
178 let is_box = ptr_kind.is_none();
179
180 let is_protected = is_fn_entry && (!is_mutable || is_unpin);
182
183 if is_mutable && !is_unpin {
184 return None;
185 }
186
187 let im_layout = bx.const_null(bx.type_ptr());
188 let pin_layout = bx.const_null(bx.type_ptr());
189
190 let mut flags = RetagFlags::empty();
191 flags.set(RetagFlags::IS_PROTECTED, is_protected);
192 flags.set(RetagFlags::IS_MUTABLE, is_mutable);
193 flags.set(RetagFlags::IS_FREEZE, is_freeze);
194 flags.set(RetagFlags::IS_BOX, is_box);
195
196 Some(RetagPlan::EmitRetag(RetagInfo {
197 size: pointee_layout.size,
198 im_layout,
199 pin_layout,
200 flags,
201 }))
202 }
203}
204
205impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
206 pub(crate) fn codegen_retag_operand(
208 &mut self,
209 bx: &mut Bx,
210 operand: OperandRef<'tcx, Bx::Value>,
211 is_fn_entry: bool,
212 ) -> OperandRef<'tcx, Bx::Value> {
213 if let OperandValue::Ref(place_ref) = operand.val {
214 let place_ref = place_ref.with_type(operand.layout);
215 self.codegen_retag_place(bx, place_ref, is_fn_entry);
216 } else if let Some(plan) = RetagPlan::<Bx::Value>::build(bx, operand.layout, is_fn_entry) {
217 let mut builder = OperandRefBuilder::from_existing(operand);
218 self.retag_operand(bx, &plan, operand, &mut builder, Size::ZERO);
219 return builder.build(bx.cx());
220 }
221 operand
222 }
223
224 pub(crate) fn codegen_retag_place(
226 &mut self,
227 bx: &mut Bx,
228 place_ref: PlaceRef<'tcx, Bx::Value>,
229 is_fn_entry: bool,
230 ) {
231 if let Some(plan) = RetagPlan::<Bx::Value>::build(bx, place_ref.layout, is_fn_entry) {
232 self.retag_place(bx, &plan, place_ref);
233 }
234 }
235
236 fn retag_operand(
237 &mut self,
238 bx: &mut Bx,
239 plan: &RetagPlan<Bx::Value>,
240 curr_operand: OperandRef<'tcx, Bx::Value>,
241 builder: &mut OperandRefBuilder<'tcx, Bx::Value>,
242 offset: Size,
243 ) {
244 match plan {
245 RetagPlan::EmitRetag(info) => {
246 let (pointer, _) = curr_operand.val.pointer_parts();
247 let retagged_pointer = bx.retag_reg(pointer, info);
248 builder.update_imm(offset, retagged_pointer);
249 }
250 RetagPlan::Recurse { field_plans, variant_plans } => {
251 let layout = curr_operand.layout;
252 for (ix, plan) in field_plans {
253 let inner_offset = layout.fields.offset(ix.as_usize());
254 let field_offset = offset + inner_offset;
255
256 let field_layout = curr_operand.layout.field(bx, ix.index());
257 if !bx.is_backend_ref(curr_operand.layout) && bx.is_backend_ref(field_layout) {
259 } else {
261 let field_operand = curr_operand.extract_field(self, bx, ix.as_usize());
262 self.retag_operand(bx, &plan, field_operand, builder, field_offset);
263 }
264 }
265
266 if !variant_plans.is_empty() {
267 let discr_ty = layout.ty.discriminant_ty(bx.tcx());
268 let discr_val = curr_operand.codegen_get_discr(self, bx, discr_ty);
269
270 if let Some(val) = bx.const_to_opt_u128(discr_val, false) {
271 let ix = VariantIdx::from_usize(val as usize);
272 if let Some(plan) = variant_plans.get(&ix) {
273 let mut variant_op = curr_operand;
274 variant_op.layout = curr_operand.layout.for_variant(bx, ix);
275
276 self.retag_operand(bx, plan, variant_op, builder, offset);
277 }
278 } else {
279 let scratch = PlaceRef::alloca(bx, curr_operand.layout);
282 scratch.storage_live(bx);
283 curr_operand.store_with_annotation(bx, scratch);
284
285 self.retag_variants(bx, scratch, discr_val, variant_plans);
287
288 let updated_op = bx.load_operand(scratch);
290 scratch.storage_dead(bx);
291
292 match updated_op.val {
293 OperandValue::ZeroSized | OperandValue::Ref(_) => {}
294 OperandValue::Immediate(imm) => builder.update_imm(offset, imm),
295 OperandValue::Pair(fst, snd) => {
296 builder.update_imm(offset, fst);
297 builder.update_imm(offset + Size::from_bytes(1), snd)
298 }
299 }
300 }
301 }
302 }
303 }
304 }
305
306 fn retag_place(
307 &mut self,
308 bx: &mut Bx,
309 plan: &RetagPlan<Bx::Value>,
310 place: PlaceRef<'tcx, Bx::Value>,
311 ) {
312 match plan {
313 RetagPlan::EmitRetag(info) => {
314 bx.retag_mem(place.val.llval, info);
315 }
316 RetagPlan::Recurse { field_plans, variant_plans } => {
317 for (ix, plan) in field_plans {
318 let field_place = place.project_field(bx, ix.as_usize());
319 self.retag_place(bx, &plan, field_place);
320 }
321 if !variant_plans.is_empty() {
322 let operand = bx.load_operand(place);
323 let discr_ty = place.layout.ty.discriminant_ty(bx.tcx());
324 let discr_val = operand.codegen_get_discr(self, bx, discr_ty);
325 self.retag_variants(bx, place, discr_val, variant_plans);
326 }
327 }
328 }
329 }
330
331 fn retag_variants(
333 &mut self,
334 bx: &mut Bx,
335 place: PlaceRef<'tcx, Bx::Value>,
336 discr: Bx::Value,
337 variant_plans: &FxIndexMap<VariantIdx, RetagPlan<Bx::Value>>,
338 ) {
339 let layout = place.layout;
340
341 let root_block = bx.llbb();
342 let mut variant_blocks = Vec::with_capacity(variant_plans.len());
343 let join_block = bx.append_sibling_block("retag_join");
344
345 for (ix, plan) in variant_plans {
346 let variant_discr = layout.ty.discriminant_for_variant(bx.tcx(), *ix);
347 let variant_discr_val = variant_discr.expect("Invalid variant index.").val;
348
349 let variant_block = bx.append_sibling_block("retag_variant");
350 bx.switch_to_block(variant_block);
351
352 let variant_place = place.project_downcast(bx, *ix);
353 self.retag_place(bx, plan, variant_place);
354
355 variant_blocks.push((variant_discr_val, variant_block));
356 bx.br(join_block);
357 }
358
359 bx.switch_to_block(root_block);
360 bx.switch(discr, join_block, variant_blocks.into_iter());
361 bx.switch_to_block(join_block);
362 }
363}