1//! Experimental support for emitting retags as function calls in generated code.
2//!
3//! We attempt to retag every argument and return value of a function, and every rvalue
4//! of an assignment. The first step to retagging is to generate a [`RetagPlan`], which
5//! describes which pointers within the place or operand can be retagged.
67#![allow(unused)]
8use rustc_abi::{BackendRepr, FieldIdx, FieldsShape, VariantIdx, Variants};
9use rustc_ast::Mutability;
10use rustc_data_structures::fx::FxIndexMap;
11use rustc_middle::mir::{Rvalue, WithRetag};
12use rustc_middle::ty;
13use rustc_middle::ty::layout::TyAndLayout;
1415use crate::RetagInfo;
16use crate::mir::FunctionCx;
17use crate::mir::operand::OperandRef;
18use crate::mir::place::PlaceRef;
19use crate::traits::BuilderMethods;
2021pub(crate) fn rvalue_needs_retag(rvalue: &Rvalue<'_>) -> bool {
22// `Ref` has its own internal retagging
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))24}
2526/// A description of the pointers within a type that need to be retagged.
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)]
28enum RetagPlan<V> {
29/// Indicates that a pointer should be retagged.
30EmitRetag(RetagInfo<V>),
31/// Indicates that one or more fields or variants of this type
32 /// contain pointers that need to be retagged.
33Recurse {
34 field_plans: FxIndexMap<FieldIdx, RetagPlan<V>>,
35 variant_plans: FxIndexMap<VariantIdx, RetagPlan<V>>,
36 },
37}
3839impl<V> RetagPlan<V> {
40/// A helper function to move a [`RetagPlan`] into a particular field.
41fn for_field(self, ix: FieldIdx) -> Self {
42let mut field_plans = FxIndexMap::default();
43field_plans.insert(ix, self);
44 RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() }
45 }
46}
4748impl<'a, 'tcx, V> RetagPlan<V> {
49/// Attempts to create a [`RetagPlan`] for a place or operand with the given layout.
50fn build<Bx: BuilderMethods<'a, 'tcx>>(
51 bx: &mut Bx,
52 layout: TyAndLayout<'tcx>,
53 is_fn_entry: bool,
54 ) -> Option<RetagPlan<Bx::Value>> {
55// If the value being retagged is smaller than a pointer, then it can't contain any
56 // pointers we need to retag, so we can stop recursion early. This optimization is
57 // crucial for ZSTs, because they can contain way more fields than we can ever visit.
58if layout.is_sized() && layout.size < bx.tcx().data_layout.pointer_size() {
59return None;
60 }
61// SIMD vectors may only contain raw pointers, integers, and floating point values,
62 // which do not need to be retagged.
63if #[allow(non_exhaustive_omitted_patterns)] match layout.backend_repr {
BackendRepr::SimdVector { .. } => true,
_ => false,
}matches!(layout.backend_repr, BackendRepr::SimdVector { .. }) {
64return None;
65 }
6667// Check the type of this value to see what to do with it (retag, or recurse).
68match layout.ty.kind() {
69&ty::Ref(_, pointee, mt) => {
70let pointee_layout = bx.layout_of(pointee);
71Self::emit_retag(bx, pointee_layout, Some(mt), is_fn_entry)
72 }
73&ty::RawPtr(_, _) => None,
74// `Box` needs special handling, since the innermost pointer is what gets retagged, but
75 // the outermost `Box` is what determines the permission that gets created.
76ty::Adt(adt, _) if adt.is_box() => Self::visit_box(bx, layout, is_fn_entry),
77// Skip traversing for everything inside of `MaybeDangling`
78ty::Adt(adt, _) if adt.is_maybe_dangling() => None,
79_ => Self::walk_value(bx, layout, is_fn_entry),
80 }
81 }
8283/// Recurses through the fields and variants of a value in memory order to create a [`RetagPlan`].
84fn walk_value<Bx: BuilderMethods<'a, 'tcx>>(
85 bx: &mut Bx,
86 layout: TyAndLayout<'tcx>,
87 is_fn_entry: bool,
88 ) -> Option<RetagPlan<Bx::Value>> {
89let mut field_plans = FxIndexMap::default();
90let mut variant_plans = FxIndexMap::default();
9192match &layout.fields {
93 FieldsShape::Union(_) | FieldsShape::Primitive => {}
94_ => {
95for ix in layout.fields.index_by_increasing_offset() {
96let field_layout = layout.field(bx, ix);
97if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
98 field_plans.insert(FieldIdx::from_usize(ix), plan);
99 }
100 }
101 }
102 }
103104match &layout.variants {
105 Variants::Single { .. } | Variants::Empty => {}
106 Variants::Multiple { variants, .. } => {
107for ix in variants.indices() {
108let variant_layout = layout.for_variant(bx, ix);
109if let Some(plan) = Self::build(bx, variant_layout, is_fn_entry) {
110 variant_plans.insert(ix, plan);
111 }
112 }
113 }
114 }
115116 (!field_plans.is_empty() || !variant_plans.is_empty())
117 .then(|| RetagPlan::Recurse { field_plans, variant_plans })
118 }
119120/// Emits a retag for a `Box`.
121fn visit_box<Bx: BuilderMethods<'a, 'tcx>>(
122 bx: &mut Bx,
123 layout: TyAndLayout<'tcx>,
124 is_fn_entry: bool,
125 ) -> Option<RetagPlan<Bx::Value>> {
126if !layout.ty.is_box() {
::core::panicking::panic("assertion failed: layout.ty.is_box()")
};assert!(layout.ty.is_box());
127match (&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");
128let mut field_plans = FxIndexMap::default();
129130// Only retag the inner pointer of a `Box` if it came from the global allocator.
131if layout.ty.is_box_global(bx.tcx()) {
132let boxed_ty = layout.ty.expect_boxed_ty();
133let boxed_layout = bx.layout_of(boxed_ty);
134if let Some(mut plan) = Self::emit_retag(bx, boxed_layout, None, is_fn_entry) {
135// `Unique<T>`
136let unique = layout.field(bx, 0);
137match (&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);
138plan = plan.for_field(FieldIdx::ZERO);
139140// `NonNull<T>`
141let nonnull = unique.field(bx, 0);
142match (&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);
143plan = plan.for_field(FieldIdx::ZERO);
144145// `*mut T is !null`
146let pattern = nonnull.field(bx, 0);
147let ty::Pat(base, _) = pattern.ty.kind() else {
148{
::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")149 };
150match (&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));
151152field_plans.insert(FieldIdx::ZERO, plan);
153 }
154 }
155156// We always try to retag the second field (the allocator)
157let field_layout = layout.field(bx, 1);
158if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
159field_plans.insert(FieldIdx::ONE, plan);
160 }
161162 (!field_plans.is_empty())
163 .then(|| RetagPlan::Recurse { field_plans, variant_plans: FxIndexMap::default() })
164 }
165166/// Determines if a pointer needs to be retagged, when it points to
167 /// a type with the given layout. Returns `None` for mutable pointers
168 /// to types that are entirely covered by `UnsafePinned`, for which retags
169 /// are a no-op.
170fn emit_retag<Bx: BuilderMethods<'a, 'tcx>>(
171 _bx: &mut Bx,
172 _pointee_layout: TyAndLayout<'tcx>,
173 _ptr_kind: Option<Mutability>,
174 _is_fn_entry: bool,
175 ) -> Option<RetagPlan<Bx::Value>> {
176None177 }
178}
179180impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
181/// Retags the pointers within an [`OperandRef`].
182pub(crate) fn codegen_retag_operand(
183&mut self,
184 _bx: &mut Bx,
185 operand: OperandRef<'tcx, Bx::Value>,
186 _is_fn_entry: bool,
187 ) -> OperandRef<'tcx, Bx::Value> {
188operand189 }
190191/// Retags the pointers within a [`PlaceRef`].
192pub(crate) fn codegen_retag_place(
193&mut self,
194 _bx: &mut Bx,
195 _place_ref: PlaceRef<'tcx, Bx::Value>,
196 _is_fn_entry: bool,
197 ) {
198 }
199}