1use rustc_abi::{FieldIdx, FieldsShape, Size, VariantIdx, Variants};
9use rustc_ast::Mutability;
10use rustc_data_structures::fx::FxIndexMap;
11use rustc_data_structures::range_set::RangeSet;
12use rustc_middle::mir::interpret::Allocation;
13use rustc_middle::mir::{Rvalue, WithRetag};
14use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
15use rustc_middle::ty::{self, Ty};
16
17use crate::mir::FunctionCx;
18use crate::mir::operand::{OperandRef, OperandRefBuilder, OperandValue};
19use crate::mir::place::PlaceRef;
20use crate::traits::{
21 BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, StaticCodegenMethods,
22};
23use crate::{RetagFlags, RetagInfo};
24
25pub(crate) fn rvalue_needs_retag(rvalue: &Rvalue<'_>) -> bool {
26 !#[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))
28}
29
30#[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)]
32enum RetagPlan<V> {
33 EmitRetag(RetagInfo<V>),
35 Recurse {
38 field_plans: FxIndexMap<FieldIdx, RetagPlan<V>>,
39 variant_plans: FxIndexMap<VariantIdx, RetagPlan<V>>,
40 },
41}
42
43impl<V> RetagPlan<V> {
44 fn for_field(self, ix: FieldIdx) -> Self {
46 let mut field_plans = FxIndexMap::default();
47 field_plans.insert(ix, self);
48 RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() }
49 }
50}
51
52impl<'a, 'tcx, V> RetagPlan<V> {
53 fn build<Bx: BuilderMethods<'a, 'tcx>>(
55 bx: &mut Bx,
56 layout: TyAndLayout<'tcx>,
57 is_fn_entry: bool,
58 ) -> Option<RetagPlan<Bx::Value>> {
59 if layout.is_sized() && layout.size < bx.tcx().data_layout.pointer_size() {
63 return None;
64 }
65 match layout.ty.kind() {
67 &ty::Ref(_, pointee, mt) => {
68 let pointee_layout = bx.layout_of(pointee);
69 Self::emit_retag(bx, pointee_layout, Some(mt), is_fn_entry)
70 }
71 &ty::RawPtr(_, _) => None,
72 ty::Adt(adt, _) if adt.is_box() => Self::visit_box(bx, layout, is_fn_entry),
75 ty::Adt(adt, _) if adt.is_maybe_dangling() => None,
77 _ => Self::walk_value(bx, layout, is_fn_entry),
78 }
79 }
80
81 fn walk_value<Bx: BuilderMethods<'a, 'tcx>>(
83 bx: &mut Bx,
84 layout: TyAndLayout<'tcx>,
85 is_fn_entry: bool,
86 ) -> Option<RetagPlan<Bx::Value>> {
87 let mut field_plans = FxIndexMap::default();
88 let mut variant_plans = FxIndexMap::default();
89
90 match &layout.fields {
91 FieldsShape::Union(_) | FieldsShape::Primitive => {}
92 _ => {
93 for ix in layout.fields.index_by_increasing_offset() {
94 let field_layout = layout.field(bx, ix);
95 if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
96 field_plans.insert(FieldIdx::from_usize(ix), plan);
97 }
98 }
99 }
100 }
101
102 match &layout.variants {
103 Variants::Single { .. } | Variants::Empty => {}
104 Variants::Multiple { variants, .. } => {
105 for ix in variants.indices() {
106 let variant_layout = layout.for_variant(bx, ix);
107 if let Some(plan) = Self::build(bx, variant_layout, is_fn_entry) {
108 variant_plans.insert(ix, plan);
109 }
110 }
111 }
112 }
113
114 (!field_plans.is_empty() || !variant_plans.is_empty())
115 .then(|| RetagPlan::Recurse { field_plans, variant_plans })
116 }
117
118 fn visit_box<Bx: BuilderMethods<'a, 'tcx>>(
120 bx: &mut Bx,
121 layout: TyAndLayout<'tcx>,
122 is_fn_entry: bool,
123 ) -> Option<RetagPlan<Bx::Value>> {
124 if !layout.ty.is_box() {
::core::panicking::panic("assertion failed: layout.ty.is_box()")
};assert!(layout.ty.is_box());
125 {
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");
126 let mut field_plans = FxIndexMap::default();
127
128 if layout.ty.is_box_global(bx.tcx()) {
130 let boxed_ty = layout.ty.expect_boxed_ty();
131 let boxed_layout = bx.layout_of(boxed_ty);
132 if let Some(mut plan) = Self::emit_retag(bx, boxed_layout, None, is_fn_entry) {
133 let unique = layout.field(bx, 0);
135 {
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);
136 plan = plan.for_field(FieldIdx::ZERO);
137
138 let nonnull = unique.field(bx, 0);
140 {
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);
141 plan = plan.for_field(FieldIdx::ZERO);
142
143 let pattern = nonnull.field(bx, 0);
145 let ty::Pat(base, _) = pattern.ty.kind() else {
146 {
::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")
147 };
148 {
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));
149
150 field_plans.insert(FieldIdx::ZERO, plan);
151 }
152 }
153
154 let field_layout = layout.field(bx, 1);
156 if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
157 field_plans.insert(FieldIdx::ONE, plan);
158 }
159
160 (!field_plans.is_empty())
161 .then(|| RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() })
162 }
163
164 fn emit_retag<Bx: BuilderMethods<'a, 'tcx>>(
169 bx: &mut Bx,
170 pointee_layout: TyAndLayout<'tcx>,
171 ptr_kind: Option<Mutability>,
172 is_fn_entry: bool,
173 ) -> Option<RetagPlan<Bx::Value>> {
174 let tcx = bx.tcx();
175 let retag_opts = tcx.sess.opts.unstable_opts.codegen_emit_retag.unwrap_or_default();
176
177 let pointee_ty = pointee_layout.ty;
178
179 let is_mutable = #[allow(non_exhaustive_omitted_patterns)] match ptr_kind {
Some(Mutability::Mut) | None => true,
_ => false,
}matches!(ptr_kind, Some(Mutability::Mut) | None);
180 let is_unpin = UnsafePinnedRanges::excludes(bx, pointee_ty);
181 let is_freeze = UnsafeCellRanges::excludes(bx, pointee_ty);
182 let is_box = ptr_kind.is_none();
183
184 let is_protected = is_fn_entry && (!is_mutable || is_unpin);
186
187 let pin_ranges = UnsafePinnedRanges::collect(bx, pointee_layout, retag_opts.no_precise_pin);
188
189 if is_mutable {
190 let all_pinned = #[allow(non_exhaustive_omitted_patterns)] match pin_ranges.as_slice() {
[(Size::ZERO, size)] if *size == pointee_layout.size => true,
_ => false,
}matches!(
193 pin_ranges.as_slice(),
194 [(Size::ZERO, size)] if *size == pointee_layout.size,
195 );
196
197 let implicitly_pinned = pin_ranges.is_empty() && !is_unpin;
201
202 if all_pinned || implicitly_pinned {
203 return None;
204 }
205 }
206
207 if is_mutable && !is_unpin {
208 return None;
209 }
210
211 let im_ranges = UnsafeCellRanges::collect(bx, pointee_layout, retag_opts.no_precise_im);
212 let all_im = #[allow(non_exhaustive_omitted_patterns)] match im_ranges.as_slice() {
[(Size::ZERO, size)] if *size == pointee_layout.size => true,
_ => false,
}matches!(
213 im_ranges.as_slice(),
214 [(Size::ZERO, size)] if *size == pointee_layout.size,
215 );
216
217 let pin_layout = Self::alloc_ranges(bx, pin_ranges);
218
219 let im_layout =
223 if all_im { bx.const_null(bx.type_ptr()) } else { Self::alloc_ranges(bx, im_ranges) };
224
225 let mut flags = RetagFlags::empty();
226 flags.set(RetagFlags::IS_PROTECTED, is_protected);
227 flags.set(RetagFlags::IS_MUTABLE, is_mutable);
228 flags.set(RetagFlags::IS_FREEZE, is_freeze);
232 flags.set(RetagFlags::IS_BOX, is_box);
233
234 Some(RetagPlan::EmitRetag(RetagInfo {
235 size: pointee_layout.size,
236 im_layout,
237 pin_layout,
238 flags,
239 }))
240 }
241
242 fn alloc_ranges<Bx: BuilderMethods<'a, 'tcx>>(
246 bx: &mut Bx,
247 ranges: Vec<(Size, Size)>,
248 ) -> Bx::Value {
249 let tcx = bx.tcx();
250 let data_layout = &tcx.data_layout;
251
252 if ranges.is_empty() {
253 return bx.const_null(bx.type_ptr());
254 }
255
256 let mut bytes: Vec<u8> = ::alloc::vec::Vec::new()vec![];
257 for (start, end) in ranges.iter() {
258 bytes.extend_from_slice(&start.bytes().to_ne_bytes());
259 bytes.extend_from_slice(&end.bytes().to_ne_bytes());
260 }
261
262 let intptr_ty = data_layout.ptr_sized_integer();
263 let align = intptr_ty.align(data_layout).abi;
264
265 let alloc = Allocation::from_bytes(&bytes, align, Mutability::Not, ());
266 let const_alloc = tcx.mk_const_alloc(alloc);
267
268 let alloc_id = tcx.reserve_and_set_memory_alloc(const_alloc);
271 let global_alloc = tcx.global_alloc(alloc_id);
272 let global_mem = global_alloc.unwrap_memory();
273
274 bx.cx().static_addr_of(global_mem, None)
275 }
276}
277
278trait PerByteTracking<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
280 fn excludes(bx: &mut Bx, ty: Ty<'tcx>) -> bool;
284
285 fn contains(bx: &mut Bx, ty: Ty<'tcx>) -> bool;
287
288 fn visit_layout(
294 bx: &mut Bx,
295 offset: Size,
296 ranges: &mut RangeSet<Size>,
297 layout: TyAndLayout<'tcx>,
298 imprecise: bool,
299 ) {
300 if layout.is_zst() {
301 return;
302 }
303
304 if Self::excludes(bx, layout.ty) {
305 return;
306 }
307
308 if imprecise {
309 return ranges.add_range(offset, layout.size);
310 }
311
312 let union_or_primitive =
313 #[allow(non_exhaustive_omitted_patterns)] match layout.fields {
FieldsShape::Union(..) | FieldsShape::Primitive => true,
_ => false,
}matches!(layout.fields, FieldsShape::Union(..) | FieldsShape::Primitive);
314 let has_multiple_variants = #[allow(non_exhaustive_omitted_patterns)] match layout.variants {
Variants::Multiple { .. } => true,
_ => false,
}matches!(layout.variants, Variants::Multiple { .. });
315
316 if Self::contains(bx, layout.ty) || union_or_primitive || has_multiple_variants {
317 ranges.add_range(offset, layout.size);
318 } else {
319 for ix in layout.fields.index_by_increasing_offset() {
321 let field_offset = layout.fields.offset(ix);
325 let layout_offset = field_offset + offset;
326
327 let field = layout.field(bx, ix);
328 Self::visit_layout(bx, layout_offset, ranges, field, imprecise);
329 }
330 }
331 }
332 fn collect(bx: &mut Bx, layout: TyAndLayout<'tcx>, imprecise: bool) -> Vec<(Size, Size)> {
334 let mut ranges = RangeSet::<Size>::new();
335 Self::visit_layout(bx, Size::ZERO, &mut ranges, layout, imprecise);
336 ranges.0
337 }
338}
339
340struct UnsafeCellRanges;
342
343impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> PerByteTracking<'a, 'tcx, Bx> for UnsafeCellRanges {
344 fn excludes(bx: &mut Bx, ty: Ty<'tcx>) -> bool {
345 ty.is_freeze(bx.tcx(), bx.cx().typing_env())
346 }
347
348 fn contains(bx: &mut Bx, ty: Ty<'tcx>) -> bool {
349 let tcx = bx.tcx();
350 match ty.kind() {
351 ty::Adt(adt, _) => Some(adt.did()) == tcx.lang_items().unsafe_cell_type(),
352 _ => false,
353 }
354 }
355}
356
357struct UnsafePinnedRanges;
359
360impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> PerByteTracking<'a, 'tcx, Bx> for UnsafePinnedRanges {
361 fn excludes(bx: &mut Bx, ty: Ty<'tcx>) -> bool {
362 ty.is_unpin(bx.tcx(), bx.typing_env()) && ty.is_unsafe_unpin(bx.tcx(), bx.typing_env())
363 }
364
365 fn contains(bx: &mut Bx, ty: Ty<'tcx>) -> bool {
366 let tcx = bx.tcx();
367 match ty.kind() {
368 ty::Adt(adt, _) => Some(adt.did()) == tcx.lang_items().unsafe_pinned_type(),
369 _ => false,
370 }
371 }
372}
373
374impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
375 pub(crate) fn codegen_retag_operand(
377 &mut self,
378 bx: &mut Bx,
379 operand: OperandRef<'tcx, Bx::Value>,
380 is_fn_entry: bool,
381 ) -> OperandRef<'tcx, Bx::Value> {
382 if let OperandValue::Ref(place_ref) = operand.val {
383 let place_ref = place_ref.with_type(operand.layout);
384 self.codegen_retag_place(bx, place_ref, is_fn_entry);
385 } else if let Some(plan) = RetagPlan::<Bx::Value>::build(bx, operand.layout, is_fn_entry) {
386 let mut builder = OperandRefBuilder::from_existing(operand);
387 self.retag_operand(bx, &plan, operand, &mut builder, Size::ZERO);
388 return builder.build(bx.cx());
389 }
390 operand
391 }
392
393 pub(crate) fn codegen_retag_place(
395 &mut self,
396 bx: &mut Bx,
397 place_ref: PlaceRef<'tcx, Bx::Value>,
398 is_fn_entry: bool,
399 ) {
400 if let Some(plan) = RetagPlan::<Bx::Value>::build(bx, place_ref.layout, is_fn_entry) {
401 self.retag_place(bx, &plan, place_ref);
402 }
403 }
404
405 fn retag_operand(
406 &mut self,
407 bx: &mut Bx,
408 plan: &RetagPlan<Bx::Value>,
409 curr_operand: OperandRef<'tcx, Bx::Value>,
410 builder: &mut OperandRefBuilder<'tcx, Bx::Value>,
411 offset: Size,
412 ) {
413 match plan {
414 RetagPlan::EmitRetag(info) => {
415 let (pointer, _) = curr_operand.val.pointer_parts();
416 let retagged_pointer = bx.retag_reg(pointer, info);
417 builder.update_imm(offset, retagged_pointer);
418 }
419 RetagPlan::Recurse { field_plans, variant_plans } => {
420 let layout = curr_operand.layout;
421 for (ix, plan) in field_plans {
422 let inner_offset = layout.fields.offset(ix.as_usize());
423 let field_offset = offset + inner_offset;
424
425 let field_layout = curr_operand.layout.field(bx, ix.index());
426 if curr_operand.layout.is_ssa_standalone() && !field_layout.is_ssa_standalone()
428 {
429 } else {
434 let field_operand = curr_operand.extract_field(self, bx, ix.as_usize());
435 self.retag_operand(bx, &plan, field_operand, builder, field_offset);
436 }
437 }
438
439 if !variant_plans.is_empty() {
440 let discr_ty = layout.ty.discriminant_ty(bx.tcx());
441 let discr_val = curr_operand.codegen_get_discr(self, bx, discr_ty);
442
443 if let Some(val) = bx.const_to_opt_u128(discr_val, false) {
444 let ix = VariantIdx::from_usize(val as usize);
445 if let Some(plan) = variant_plans.get(&ix) {
446 let mut variant_op = curr_operand;
447 variant_op.layout = curr_operand.layout.for_variant(bx, ix);
448
449 self.retag_operand(bx, plan, variant_op, builder, offset);
450 }
451 } else {
452 let scratch = PlaceRef::alloca(bx, curr_operand.layout);
455 scratch.storage_live(bx);
456 curr_operand.store_with_annotation(bx, scratch);
457
458 self.retag_variants(bx, scratch, discr_val, variant_plans);
460
461 let updated_op = bx.load_operand(scratch);
463 scratch.storage_dead(bx);
464
465 match updated_op.val {
466 OperandValue::ZeroSized | OperandValue::Ref(_) => {}
467 OperandValue::Immediate(imm) => builder.update_imm(offset, imm),
468 OperandValue::Pair(fst, snd) => {
469 builder.update_imm(offset, fst);
470 builder.update_imm(offset + Size::from_bytes(1), snd)
471 }
472 }
473 }
474 }
475 }
476 }
477 }
478
479 fn retag_place(
480 &mut self,
481 bx: &mut Bx,
482 plan: &RetagPlan<Bx::Value>,
483 place: PlaceRef<'tcx, Bx::Value>,
484 ) {
485 match plan {
486 RetagPlan::EmitRetag(info) => {
487 bx.retag_mem(place.val.llval, info);
488 }
489 RetagPlan::Recurse { field_plans, variant_plans } => {
490 for (ix, plan) in field_plans {
491 let field_place = place.project_field(bx, ix.as_usize());
492 self.retag_place(bx, &plan, field_place);
493 }
494 if !variant_plans.is_empty() {
495 let operand = bx.load_operand(place);
496 let discr_ty = place.layout.ty.discriminant_ty(bx.tcx());
497 let discr_val = operand.codegen_get_discr(self, bx, discr_ty);
498 self.retag_variants(bx, place, discr_val, variant_plans);
499 }
500 }
501 }
502 }
503
504 fn retag_variants(
506 &mut self,
507 bx: &mut Bx,
508 place: PlaceRef<'tcx, Bx::Value>,
509 discr: Bx::Value,
510 variant_plans: &FxIndexMap<VariantIdx, RetagPlan<Bx::Value>>,
511 ) {
512 let layout = place.layout;
513
514 let root_block = bx.llbb();
515 let mut variant_blocks = Vec::with_capacity(variant_plans.len());
516 let join_block = bx.append_sibling_block("retag_join");
517
518 for (ix, plan) in variant_plans {
519 let variant_discr = layout.ty.discriminant_for_variant(bx.tcx(), *ix);
520 let variant_discr_val = variant_discr.expect("Invalid variant index.").val;
521
522 let variant_block = bx.append_sibling_block("retag_variant");
523 bx.switch_to_block(variant_block);
524
525 let variant_place = place.project_downcast(bx, *ix);
526 self.retag_place(bx, plan, variant_place);
527
528 variant_blocks.push((variant_discr_val, variant_block));
529 bx.br(join_block);
530 }
531
532 bx.switch_to_block(root_block);
533 bx.switch(discr, join_block, variant_blocks.into_iter());
534 bx.switch_to_block(join_block);
535 }
536}