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