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::common::IntPredicate;
18use crate::mir::operand::{OperandRef, OperandRefBuilder, OperandValue};
19use crate::mir::place::PlaceRef;
20use crate::mir::{FunctionCx, bug};
21use crate::traits::{
22 BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, StaticCodegenMethods,
23};
24use crate::{RetagFlags, RetagInfo};
25
26pub(crate) fn rvalue_needs_retag(rvalue: &Rvalue<'_>) -> bool {
27 !#[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))
29}
30
31#[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 {
Self::EmitRetag(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"EmitRetag", &__self_0),
Self::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)]
33enum RetagPlan<V> {
34 EmitRetag(RetagInfo<V>),
36 Recurse {
39 field_plans: Option<RetagFields<V>>,
40 variant_plans: FxIndexMap<VariantIdx, RetagPlan<V>>,
41 },
42}
43
44#[derive(#[automatically_derived]
impl<V: ::core::fmt::Debug> ::core::fmt::Debug for RetagFields<V> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Arbitrary(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Arbitrary", &__self_0),
Self::Array(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Array",
&__self_0),
}
}
}Debug)]
50enum RetagFields<V> {
51 Arbitrary(FxIndexMap<FieldIdx, RetagPlan<V>>),
52 Array(Box<RetagPlan<V>>),
53}
54
55impl<V> RetagPlan<V> {
56 fn for_field(self, ix: FieldIdx) -> Self {
58 let mut field_plans = FxIndexMap::default();
59 field_plans.insert(ix, self);
60 RetagPlan::Recurse {
61 field_plans: Some(RetagFields::Arbitrary(field_plans)),
62 variant_plans: FxIndexMap::default(),
63 }
64 }
65}
66
67impl<'a, 'tcx, V> RetagPlan<V> {
68 fn build<Bx: BuilderMethods<'a, 'tcx>>(
70 bx: &mut Bx,
71 layout: TyAndLayout<'tcx>,
72 is_fn_entry: bool,
73 ) -> Option<RetagPlan<Bx::Value>> {
74 if layout.is_sized() && layout.size < bx.tcx().data_layout.pointer_size() {
78 return None;
79 }
80 match layout.ty.kind() {
82 &ty::Ref(_, pointee, mt) => {
83 let pointee_layout = bx.layout_of(pointee);
84 Self::emit_retag(bx, pointee_layout, Some(mt), is_fn_entry)
85 }
86 &ty::RawPtr(_, _) => None,
87 ty::Adt(adt, _) if adt.is_box() => Self::visit_box(bx, layout, is_fn_entry),
90 _ if layout.ty.is_like_maybe_dangling() => None,
92 _ => Self::walk_value(bx, layout, is_fn_entry),
93 }
94 }
95
96 fn walk_value<Bx: BuilderMethods<'a, 'tcx>>(
98 bx: &mut Bx,
99 layout: TyAndLayout<'tcx>,
100 is_fn_entry: bool,
101 ) -> Option<RetagPlan<Bx::Value>> {
102 let field_plans = match &layout.fields {
103 FieldsShape::Union(_) | FieldsShape::Primitive => None,
104 FieldsShape::Arbitrary { .. } => {
105 let mut field_plans = FxIndexMap::default();
106 for ix in layout.fields.index_by_increasing_offset() {
107 let field_layout = layout.field(bx, ix);
108 if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
109 field_plans.insert(FieldIdx::from_usize(ix), plan);
110 }
111 }
112 (!field_plans.is_empty()).then_some(RetagFields::Arbitrary(field_plans))
113 }
114 FieldsShape::Array { count, .. } if *count > 0 => {
115 let field_layout = layout.field(bx, 0);
116 Self::build(bx, field_layout, is_fn_entry)
117 .map(|plan| RetagFields::Array(Box::new(plan)))
118 }
119 FieldsShape::Array { .. } => None,
120 };
121
122 let mut variant_plans = FxIndexMap::default();
123 match &layout.variants {
124 Variants::Single { .. } | Variants::Empty => {}
125 Variants::Multiple { variants, .. } => {
126 for ix in variants.indices() {
127 let variant_layout = layout.for_variant(bx, ix);
128 if let Some(plan) = Self::build(bx, variant_layout, is_fn_entry) {
129 variant_plans.insert(ix, plan);
130 }
131 }
132 }
133 }
134
135 (field_plans.is_some() || !variant_plans.is_empty())
136 .then(|| RetagPlan::Recurse { field_plans, variant_plans })
137 }
138
139 fn visit_box<Bx: BuilderMethods<'a, 'tcx>>(
141 bx: &mut Bx,
142 layout: TyAndLayout<'tcx>,
143 is_fn_entry: bool,
144 ) -> Option<RetagPlan<Bx::Value>> {
145 if !layout.ty.is_box() {
::core::panicking::panic("assertion failed: layout.ty.is_box()")
};assert!(layout.ty.is_box());
146 {
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");
147 let mut field_plans = FxIndexMap::default();
148
149 if layout.ty.is_box_global(bx.tcx()) {
151 let boxed_ty = layout.ty.expect_boxed_ty();
152 let boxed_layout = bx.layout_of(boxed_ty);
153 if let Some(mut plan) = Self::emit_retag(bx, boxed_layout, None, is_fn_entry) {
154 let unique = layout.field(bx, 0);
156 {
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);
157 plan = plan.for_field(FieldIdx::ZERO);
158
159 let nonnull = unique.field(bx, 0);
161 {
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);
162 plan = plan.for_field(FieldIdx::ZERO);
163
164 let pattern = nonnull.field(bx, 0);
166 let ty::Pat(base, _) = pattern.ty.kind() else {
167 {
::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")
168 };
169 {
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));
170
171 field_plans.insert(FieldIdx::ZERO, plan);
172 }
173 }
174
175 let field_layout = layout.field(bx, 1);
177 if let Some(plan) = Self::build(bx, field_layout, is_fn_entry) {
178 field_plans.insert(FieldIdx::ONE, plan);
179 }
180
181 (!field_plans.is_empty()).then(|| RetagPlan::Recurse {
182 field_plans: Some(RetagFields::Arbitrary(field_plans)),
183 variant_plans: FxIndexMap::default(),
184 })
185 }
186
187 fn emit_retag<Bx: BuilderMethods<'a, 'tcx>>(
192 bx: &mut Bx,
193 pointee_layout: TyAndLayout<'tcx>,
194 ptr_kind: Option<Mutability>,
195 is_fn_entry: bool,
196 ) -> Option<RetagPlan<Bx::Value>> {
197 let tcx = bx.tcx();
198 let retag_opts = tcx.sess.opts.unstable_opts.codegen_emit_retag.unwrap_or_default();
199
200 let pointee_ty = pointee_layout.ty;
201
202 let is_mutable = #[allow(non_exhaustive_omitted_patterns)] match ptr_kind {
Some(Mutability::Mut) | None => true,
_ => false,
}matches!(ptr_kind, Some(Mutability::Mut) | None);
203 let is_unpin = UnsafePinnedRanges::excludes(bx, pointee_ty);
204 let is_freeze = UnsafeCellRanges::excludes(bx, pointee_ty);
205 let is_box = ptr_kind.is_none();
206
207 let is_protected = is_fn_entry && (!is_mutable || is_unpin);
209
210 let pin_ranges = UnsafePinnedRanges::collect(bx, pointee_layout, retag_opts.no_precise_pin);
211
212 if is_mutable {
213 let all_pinned = #[allow(non_exhaustive_omitted_patterns)] match pin_ranges.as_slice() {
[(Size::ZERO, size)] if *size == pointee_layout.size => true,
_ => false,
}matches!(
216 pin_ranges.as_slice(),
217 [(Size::ZERO, size)] if *size == pointee_layout.size,
218 );
219
220 let implicitly_pinned = pin_ranges.is_empty() && !is_unpin;
224
225 if all_pinned || implicitly_pinned {
226 return None;
227 }
228 }
229
230 if is_mutable && !is_unpin {
231 return None;
232 }
233
234 let im_ranges = UnsafeCellRanges::collect(bx, pointee_layout, retag_opts.no_precise_im);
235 let all_im = #[allow(non_exhaustive_omitted_patterns)] match im_ranges.as_slice() {
[(Size::ZERO, size)] if *size == pointee_layout.size => true,
_ => false,
}matches!(
236 im_ranges.as_slice(),
237 [(Size::ZERO, size)] if *size == pointee_layout.size,
238 );
239
240 let pin_layout = Self::alloc_ranges(bx, pin_ranges);
241
242 let im_layout =
246 if all_im { bx.const_null(bx.type_ptr()) } else { Self::alloc_ranges(bx, im_ranges) };
247
248 let mut flags = RetagFlags::empty();
249 flags.set(RetagFlags::IS_PROTECTED, is_protected);
250 flags.set(RetagFlags::IS_MUTABLE, is_mutable);
251 flags.set(RetagFlags::IS_FREEZE, is_freeze);
255 flags.set(RetagFlags::IS_BOX, is_box);
256
257 Some(RetagPlan::EmitRetag(RetagInfo {
258 size: pointee_layout.size,
259 im_layout,
260 pin_layout,
261 flags,
262 }))
263 }
264
265 fn alloc_ranges<Bx: BuilderMethods<'a, 'tcx>>(
269 bx: &mut Bx,
270 ranges: Vec<(Size, Size)>,
271 ) -> Bx::Value {
272 let tcx = bx.tcx();
273 let data_layout = &tcx.data_layout;
274
275 if ranges.is_empty() {
276 return bx.const_null(bx.type_ptr());
277 }
278
279 let mut bytes: Vec<u8> = ::alloc::vec::Vec::new()vec![];
280 for (start, end) in ranges.iter() {
281 bytes.extend_from_slice(&start.bytes().to_ne_bytes());
282 bytes.extend_from_slice(&end.bytes().to_ne_bytes());
283 }
284
285 let intptr_ty = data_layout.ptr_sized_integer();
286 let align = intptr_ty.align(data_layout).abi;
287
288 let alloc = Allocation::from_bytes(&bytes, align, Mutability::Not, ());
289 let const_alloc = tcx.mk_const_alloc(alloc);
290
291 let alloc_id = tcx.reserve_and_set_memory_alloc(const_alloc);
294 let global_alloc = tcx.global_alloc(alloc_id);
295 let global_mem = global_alloc.unwrap_memory();
296
297 bx.cx().static_addr_of(global_mem, None)
298 }
299}
300
301trait PerByteTracking<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
303 fn excludes(bx: &mut Bx, ty: Ty<'tcx>) -> bool;
307
308 fn contains(bx: &mut Bx, ty: Ty<'tcx>) -> bool;
310
311 fn visit_layout(
317 bx: &mut Bx,
318 offset: Size,
319 ranges: &mut RangeSet<Size>,
320 layout: TyAndLayout<'tcx>,
321 imprecise: bool,
322 ) {
323 if layout.is_zst() {
324 return;
325 }
326
327 if Self::excludes(bx, layout.ty) {
328 return;
329 }
330
331 if imprecise {
332 return ranges.add_range(offset, layout.size);
333 }
334
335 let union_or_primitive =
336 #[allow(non_exhaustive_omitted_patterns)] match layout.fields {
FieldsShape::Union(..) | FieldsShape::Primitive => true,
_ => false,
}matches!(layout.fields, FieldsShape::Union(..) | FieldsShape::Primitive);
337 let has_multiple_variants = #[allow(non_exhaustive_omitted_patterns)] match layout.variants {
Variants::Multiple { .. } => true,
_ => false,
}matches!(layout.variants, Variants::Multiple { .. });
338
339 if Self::contains(bx, layout.ty) || union_or_primitive || has_multiple_variants {
340 ranges.add_range(offset, layout.size);
341 } else {
342 for ix in layout.fields.index_by_increasing_offset() {
344 let field_offset = layout.fields.offset(ix);
348 let layout_offset = field_offset + offset;
349
350 let field = layout.field(bx, ix);
351 Self::visit_layout(bx, layout_offset, ranges, field, imprecise);
352 }
353 }
354 }
355 fn collect(bx: &mut Bx, layout: TyAndLayout<'tcx>, imprecise: bool) -> Vec<(Size, Size)> {
357 let mut ranges = RangeSet::<Size>::new();
358 Self::visit_layout(bx, Size::ZERO, &mut ranges, layout, imprecise);
359 ranges.0
360 }
361}
362
363struct UnsafeCellRanges;
365
366impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> PerByteTracking<'a, 'tcx, Bx> for UnsafeCellRanges {
367 fn excludes(bx: &mut Bx, ty: Ty<'tcx>) -> bool {
368 ty.is_freeze(bx.tcx(), bx.cx().typing_env())
369 }
370
371 fn contains(bx: &mut Bx, ty: Ty<'tcx>) -> bool {
372 let tcx = bx.tcx();
373 match ty.kind() {
374 ty::Adt(adt, _) => Some(adt.did()) == tcx.lang_items().unsafe_cell_type(),
375 _ => false,
376 }
377 }
378}
379
380struct UnsafePinnedRanges;
382
383impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> PerByteTracking<'a, 'tcx, Bx> for UnsafePinnedRanges {
384 fn excludes(bx: &mut Bx, ty: Ty<'tcx>) -> bool {
385 ty.is_unpin(bx.tcx(), bx.typing_env()) && ty.is_unsafe_unpin(bx.tcx(), bx.typing_env())
386 }
387
388 fn contains(bx: &mut Bx, ty: Ty<'tcx>) -> bool {
389 let tcx = bx.tcx();
390 match ty.kind() {
391 ty::Adt(adt, _) => Some(adt.did()) == tcx.lang_items().unsafe_pinned_type(),
392 _ => false,
393 }
394 }
395}
396
397impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
398 pub(crate) fn codegen_retag_operand(
400 &mut self,
401 bx: &mut Bx,
402 operand: OperandRef<'tcx, Bx::Value>,
403 is_fn_entry: bool,
404 ) -> OperandRef<'tcx, Bx::Value> {
405 if let OperandValue::Ref(place_ref) = operand.val {
406 let place_ref = place_ref.with_type(operand.layout);
407 self.codegen_retag_place(bx, place_ref, is_fn_entry);
408 } else if let Some(plan) = RetagPlan::<Bx::Value>::build(bx, operand.layout, is_fn_entry) {
409 let mut builder = OperandRefBuilder::from_existing(operand);
410 self.retag_operand(bx, &plan, operand, &mut builder, Size::ZERO);
411 return builder.build(bx.cx());
412 }
413 operand
414 }
415
416 pub(crate) fn codegen_retag_place(
418 &mut self,
419 bx: &mut Bx,
420 place_ref: PlaceRef<'tcx, Bx::Value>,
421 is_fn_entry: bool,
422 ) {
423 if let Some(plan) = RetagPlan::<Bx::Value>::build(bx, place_ref.layout, is_fn_entry) {
424 self.retag_place(bx, &plan, place_ref);
425 }
426 }
427
428 fn retag_operand(
429 &mut self,
430 bx: &mut Bx,
431 plan: &RetagPlan<Bx::Value>,
432 curr_operand: OperandRef<'tcx, Bx::Value>,
433 builder: &mut OperandRefBuilder<'tcx, Bx::Value>,
434 offset: Size,
435 ) {
436 match plan {
437 RetagPlan::EmitRetag(info) => {
438 let (pointer, _) = curr_operand.val.pointer_parts();
439 let retagged_pointer = bx.retag_reg(pointer, info);
440 builder.update_imm(offset, retagged_pointer);
441 }
442 RetagPlan::Recurse { field_plans, variant_plans } => {
443 let layout = curr_operand.layout;
444 if let Some(plans) = field_plans {
445 let mut retag_field = |ix: FieldIdx, plan: &RetagPlan<Bx::Value>| {
446 let inner_offset = layout.fields.offset(ix.as_usize());
447 let field_offset = offset + inner_offset;
448 let field_layout = curr_operand.layout.field(bx, ix.index());
449 if curr_operand.layout.is_ssa_standalone()
450 && !field_layout.is_ssa_standalone()
451 {
452 } else {
456 let field_operand = curr_operand.extract_field(self, bx, ix.as_usize());
457 self.retag_operand(bx, plan, field_operand, builder, field_offset);
458 }
459 };
460
461 match plans {
462 RetagFields::Arbitrary(field_plans) => {
463 for (ix, plan) in field_plans {
464 retag_field(*ix, plan);
465 }
466 }
467 RetagFields::Array(plan) => {
468 let FieldsShape::Array { count, .. } = layout.fields else {
469 ::rustc_span::macros::bug_impl(None,
format_args!("Expected `FieldsShape::Array` but found {0:?}",
layout.fields), Location::caller());bug!("Expected `FieldsShape::Array` but found {:?}", layout.fields);
470 };
471 for idx in 0..count {
472 let idx = FieldIdx::from_usize(idx as usize);
473 retag_field(idx, plan)
474 }
475 }
476 }
477 }
478
479 if !variant_plans.is_empty() {
480 let discr_ty = layout.ty.discriminant_ty(bx.tcx());
481 let discr_val = curr_operand.codegen_get_discr(self, bx, discr_ty);
482
483 if let Some(val) = bx.const_to_opt_u128(discr_val, false) {
484 let ix = VariantIdx::from_usize(val as usize);
485 if let Some(plan) = variant_plans.get(&ix) {
486 let mut variant_op = curr_operand;
487 variant_op.layout = curr_operand.layout.for_variant(bx, ix);
488
489 self.retag_operand(bx, plan, variant_op, builder, offset);
490 }
491 } else {
492 let scratch = PlaceRef::alloca(bx, curr_operand.layout);
495 scratch.storage_live(bx);
496 curr_operand.store_with_annotation(bx, scratch);
497
498 self.retag_variants(bx, scratch, discr_val, variant_plans);
500
501 let updated_op = bx.load_operand(scratch);
503 scratch.storage_dead(bx);
504
505 match updated_op.val {
506 OperandValue::ZeroSized | OperandValue::Ref(_) => {}
507 OperandValue::Immediate(imm) => builder.update_imm(offset, imm),
508 OperandValue::Pair(fst, snd) => {
509 builder.update_imm(offset, fst);
510 builder.update_imm(offset + Size::from_bytes(1), snd)
511 }
512 }
513 }
514 }
515 }
516 }
517 }
518
519 fn retag_place(
520 &mut self,
521 bx: &mut Bx,
522 plan: &RetagPlan<Bx::Value>,
523 place: PlaceRef<'tcx, Bx::Value>,
524 ) {
525 match plan {
526 RetagPlan::EmitRetag(info) => {
527 bx.retag_mem(place.val.llval, info);
528 }
529 RetagPlan::Recurse { field_plans, variant_plans } => {
530 if let Some(field_plans) = field_plans {
531 let mut retag_field = |ix: FieldIdx, plan: &RetagPlan<Bx::Value>| {
532 let field_place = place.project_field(bx, ix.as_usize());
533 self.retag_place(bx, plan, field_place);
534 };
535 match field_plans {
536 RetagFields::Arbitrary(fields) => {
537 for (ix, plan) in fields {
538 retag_field(*ix, plan);
539 }
540 }
541 RetagFields::Array(plan) => {
542 let FieldsShape::Array { count, .. } = place.layout.fields else {
543 ::rustc_span::macros::bug_impl(None,
format_args!("Expected `FieldsShape::Array` but found {0:?}",
place.layout.fields), Location::caller());bug!(
544 "Expected `FieldsShape::Array` but found {:?}",
545 place.layout.fields
546 );
547 };
548 within_loop(bx, count, |bx: &mut Bx, ix: Bx::Value| {
552 let field_place = place.project_index(bx, ix);
553 self.retag_place(bx, plan, field_place);
554 });
555 }
556 }
557 }
558 if !variant_plans.is_empty() {
559 let operand = bx.load_operand(place);
560 let discr_ty = place.layout.ty.discriminant_ty(bx.tcx());
561 let discr_val = operand.codegen_get_discr(self, bx, discr_ty);
562 self.retag_variants(bx, place, discr_val, variant_plans);
563 }
564 }
565 }
566 }
567
568 fn retag_variants(
570 &mut self,
571 bx: &mut Bx,
572 place: PlaceRef<'tcx, Bx::Value>,
573 discr: Bx::Value,
574 variant_plans: &FxIndexMap<VariantIdx, RetagPlan<Bx::Value>>,
575 ) {
576 let layout = place.layout;
577
578 let root_block = bx.llbb();
579 let mut variant_blocks = Vec::with_capacity(variant_plans.len());
580 let join_block = bx.append_sibling_block("retag_join");
581
582 for (ix, plan) in variant_plans {
583 let variant_discr = layout.ty.discriminant_for_variant(bx.tcx(), *ix);
584 let variant_discr_val = variant_discr.expect("Invalid variant index.").val;
585
586 let variant_block = bx.append_sibling_block("retag_variant");
587 bx.switch_to_block(variant_block);
588
589 let variant_place = place.project_downcast(bx, *ix);
590 self.retag_place(bx, plan, variant_place);
591
592 variant_blocks.push((variant_discr_val, variant_block));
593 bx.br(join_block);
594 }
595
596 bx.switch_to_block(root_block);
597 bx.switch(discr, join_block, variant_blocks.into_iter());
598 bx.switch_to_block(join_block);
599 }
600}
601
602fn within_loop<'a, 'tcx, F, Bx: BuilderMethods<'a, 'tcx>>(bx: &mut Bx, max_iter: u64, mut op: F)
606where
607 F: FnMut(&mut Bx, Bx::Value),
608{
609 if max_iter == 0 {
611 return;
612 }
613
614 let counter_layout = bx.layout_of(bx.tcx().types.usize);
616 let counter_alloca = PlaceRef::alloca(bx, counter_layout);
617 counter_alloca.storage_live(bx);
618 bx.store_to_place(bx.const_usize(0), counter_alloca.val);
619
620 let loop_body = bx.append_sibling_block("retag_loop");
622 bx.br(loop_body);
623
624 bx.switch_to_block(loop_body);
625
626 let curr_count = bx.load_from_place(bx.type_isize(), counter_alloca.val);
628 op(bx, curr_count);
629
630 let next_count = bx.unchecked_uadd(curr_count, bx.const_usize(1));
631 bx.store_to_place(next_count, counter_alloca.val);
632
633 let max_count = bx.const_usize(max_iter);
636 let loop_exit = bx.append_sibling_block("retag_loop_exit");
637 let should_exit_loop = bx.icmp(IntPredicate::IntEQ, next_count, max_count);
638 bx.cond_br(should_exit_loop, loop_exit, loop_body);
639
640 bx.switch_to_block(loop_exit);
641 counter_alloca.storage_dead(bx);
642}