diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 126387db1d9..412cfc1fc7a 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -289,19 +289,7 @@ impl<'tcx> UnOp { pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> { match self { UnOp::Not | UnOp::Neg => arg_ty, - UnOp::PtrMetadata => { - let pointee_ty = arg_ty - .builtin_deref(true) - .unwrap_or_else(|| bug!("PtrMetadata of non-dereferenceable ty {arg_ty:?}")); - if pointee_ty.is_trivially_sized(tcx) { - tcx.types.unit - } else { - let Some(metadata_def_id) = tcx.lang_items().metadata_type() else { - bug!("No metadata_type lang item while looking at {arg_ty:?}") - }; - Ty::new_projection(tcx, metadata_def_id, [pointee_ty]) - } - } + UnOp::PtrMetadata => arg_ty.pointee_metadata_ty_or_projection(tcx), } } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index a3cb84d561d..ff40a726fbc 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1647,6 +1647,34 @@ impl<'tcx> Ty<'tcx> { } } + /// Given a pointer or reference type, returns the type of the *pointee*'s + /// metadata. If it can't be determined exactly (perhaps due to still + /// being generic) then a projection through `ptr::Pointee` will be returned. + /// + /// This is particularly useful for getting the type of the result of + /// [`UnOp::PtrMetadata`](crate::mir::UnOp::PtrMetadata). + /// + /// Panics if `self` is not dereferencable. + #[track_caller] + pub fn pointee_metadata_ty_or_projection(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + let Some(pointee_ty) = self.builtin_deref(true) else { + bug!("Type {self:?} is not a pointer or reference type") + }; + if pointee_ty.is_trivially_sized(tcx) { + tcx.types.unit + } else { + match pointee_ty.ptr_metadata_ty_or_tail(tcx, |x| x) { + Ok(metadata_ty) => metadata_ty, + Err(tail_ty) => { + let Some(metadata_def_id) = tcx.lang_items().metadata_type() else { + bug!("No metadata_type lang item while looking at {self:?}") + }; + Ty::new_projection(tcx, metadata_def_id, [tail_ty]) + } + } + } + } + /// When we create a closure, we record its kind (i.e., what trait /// it implements, constrained by how it uses its borrows) into its /// [`ty::ClosureArgs`] or [`ty::CoroutineClosureArgs`] using a type diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 32c0d27f635..7e401b5482f 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -60,7 +60,15 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, _location: Location) { match rvalue { - Rvalue::NullaryOp(NullOp::UbChecks, ..) if !self.tcx.sess.ub_checks() => { + Rvalue::NullaryOp(NullOp::UbChecks, ..) + if !self + .tcx + .sess + .opts + .unstable_opts + .inline_mir_preserve_debug + .unwrap_or(self.tcx.sess.ub_checks()) => + { // If this is in optimized MIR it's because it's used later, // so if we don't need UB checks this session, give a bonus // here to offset the cost of the call later. @@ -111,12 +119,19 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { } } TerminatorKind::Assert { unwind, msg, .. } => { - self.penalty += - if msg.is_optional_overflow_check() && !self.tcx.sess.overflow_checks() { - INSTR_COST - } else { - CALL_PENALTY - }; + self.penalty += if msg.is_optional_overflow_check() + && !self + .tcx + .sess + .opts + .unstable_opts + .inline_mir_preserve_debug + .unwrap_or(self.tcx.sess.overflow_checks()) + { + INSTR_COST + } else { + CALL_PENALTY + }; if let UnwindAction::Cleanup(_) = unwind { self.penalty += LANDINGPAD_PENALTY; } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bfdefd5a7d6..936a7e2d9de 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -823,18 +823,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return self.simplify_cast(kind, value, to, location); } Rvalue::BinaryOp(op, box (ref mut lhs, ref mut rhs)) => { - let ty = lhs.ty(self.local_decls, self.tcx); - let lhs = self.simplify_operand(lhs, location); - let rhs = self.simplify_operand(rhs, location); - // Only short-circuit options after we called `simplify_operand` - // on both operands for side effect. - let lhs = lhs?; - let rhs = rhs?; - - if let Some(value) = self.simplify_binary(op, ty, lhs, rhs) { - return Some(value); - } - Value::BinaryOp(op, lhs, rhs) + return self.simplify_binary(op, lhs, rhs, location); } Rvalue::UnaryOp(op, ref mut arg_op) => { return self.simplify_unary(op, arg_op, location); @@ -987,23 +976,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // `*const [T]` -> `*const T` which remove metadata. // We run on potentially-generic MIR, though, so unlike codegen // we can't always know exactly what the metadata are. - // Thankfully, equality on `ptr_metadata_ty_or_tail` gives us - // what we need: `Ok(meta_ty)` if the metadata is known, or - // `Err(tail_ty)` if not. Matching metadata is ok, but if - // that's not known, then matching tail types is also ok, - // allowing things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`. - // FIXME: Would it be worth trying to normalize, rather than - // passing the identity closure? Or are the types in the - // Cast realistically about as normalized as we can get anyway? + // To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`, + // it's fine to get a projection as the type. Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to } - if from - .builtin_deref(true) - .unwrap() - .ptr_metadata_ty_or_tail(self.tcx, |t| t) - == to - .builtin_deref(true) - .unwrap() - .ptr_metadata_ty_or_tail(self.tcx, |t| t) => + if self.pointers_have_same_metadata(*from, *to) => { arg_index = *inner; was_updated = true; @@ -1068,6 +1044,52 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn simplify_binary( + &mut self, + op: BinOp, + lhs_operand: &mut Operand<'tcx>, + rhs_operand: &mut Operand<'tcx>, + location: Location, + ) -> Option { + let lhs = self.simplify_operand(lhs_operand, location); + let rhs = self.simplify_operand(rhs_operand, location); + // Only short-circuit options after we called `simplify_operand` + // on both operands for side effect. + let mut lhs = lhs?; + let mut rhs = rhs?; + + let lhs_ty = lhs_operand.ty(self.local_decls, self.tcx); + + // If we're comparing pointers, remove `PtrToPtr` casts if the from + // types of both casts and the metadata all match. + if let BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge = op + && lhs_ty.is_any_ptr() + && let Value::Cast { + kind: CastKind::PtrToPtr, value: lhs_value, from: lhs_from, .. + } = self.get(lhs) + && let Value::Cast { + kind: CastKind::PtrToPtr, value: rhs_value, from: rhs_from, .. + } = self.get(rhs) + && lhs_from == rhs_from + && self.pointers_have_same_metadata(*lhs_from, lhs_ty) + { + lhs = *lhs_value; + rhs = *rhs_value; + if let Some(op) = self.try_as_operand(lhs, location) { + *lhs_operand = op; + } + if let Some(op) = self.try_as_operand(rhs, location) { + *rhs_operand = op; + } + } + + if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) { + return Some(value); + } + let value = Value::BinaryOp(op, lhs, rhs); + Some(self.insert(value)) + } + + fn simplify_binary_inner( &mut self, op: BinOp, lhs_ty: Ty<'tcx>, @@ -1228,6 +1250,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } + // PtrToPtr-then-PtrToPtr can skip the intermediate step if let PtrToPtr = kind && let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } = *self.get(value) @@ -1235,7 +1258,25 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { { from = inner_from; value = inner_value; - *kind = PtrToPtr; + was_updated = true; + if inner_from == to { + return Some(inner_value); + } + } + + // PtrToPtr-then-Transmute can just transmute the original, so long as the + // PtrToPtr didn't change metadata (and thus the size of the pointer) + if let Transmute = kind + && let Value::Cast { + kind: PtrToPtr, + value: inner_value, + from: inner_from, + to: inner_to, + } = *self.get(value) + && self.pointers_have_same_metadata(inner_from, inner_to) + { + from = inner_from; + value = inner_value; was_updated = true; if inner_from == to { return Some(inner_value); @@ -1289,6 +1330,21 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Fallback: a symbolic `Len`. Some(self.insert(Value::Len(inner))) } + + fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool { + let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx); + let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx); + if left_meta_ty == right_meta_ty { + true + } else if let Ok(left) = + self.tcx.try_normalize_erasing_regions(self.param_env, left_meta_ty) + && let Ok(right) = self.tcx.try_normalize_erasing_regions(self.param_env, right_meta_ty) + { + left == right + } else { + false + } + } } fn op_to_prop_const<'tcx>( diff --git a/tests/coverage/closure_macro.cov-map b/tests/coverage/closure_macro.cov-map index 21fad22f58f..156947f4e21 100644 --- a/tests/coverage/closure_macro.cov-map +++ b/tests/coverage/closure_macro.cov-map @@ -22,19 +22,19 @@ Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: closure_macro::main::{closure#0} -Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] +Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 Number of expressions: 3 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add) -- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Counter(2), rhs = Zero Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 16, 28) to (start + 3, 33) - Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39) - Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22) = (c0 - c1) -- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30) +- Code(Zero) at (prev + 0, 23) to (start + 0, 30) - Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c1 + (c2 + c3)) + = (c1 + (c2 + Zero)) diff --git a/tests/coverage/closure_macro_async.cov-map b/tests/coverage/closure_macro_async.cov-map index f2efd550591..0f2b4e01748 100644 --- a/tests/coverage/closure_macro_async.cov-map +++ b/tests/coverage/closure_macro_async.cov-map @@ -30,19 +30,19 @@ Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: closure_macro_async::test::{closure#0}::{closure#0} -Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 12, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] +Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 12, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 Number of expressions: 3 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add) -- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Counter(2), rhs = Zero Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 18, 28) to (start + 3, 33) - Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39) - Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22) = (c0 - c1) -- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30) +- Code(Zero) at (prev + 0, 23) to (start + 0, 30) - Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c1 + (c2 + c3)) + = (c1 + (c2 + Zero)) diff --git a/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-abort.diff b/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-abort.diff new file mode 100644 index 00000000000..757ab959813 --- /dev/null +++ b/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-abort.diff @@ -0,0 +1,134 @@ +- // MIR for `cast_pointer_eq` before GVN ++ // MIR for `cast_pointer_eq` after GVN + + fn cast_pointer_eq(_1: *mut u8, _2: *mut u32, _3: *mut u32, _4: *mut [u32]) -> () { + debug p1 => _1; + debug p2 => _2; + debug p3 => _3; + debug p4 => _4; + let mut _0: (); + let _5: *const u32; + let mut _6: *mut u8; + let mut _8: *const u32; + let mut _9: *mut u32; + let mut _11: *const u32; + let mut _12: *mut u32; + let mut _14: *mut [u32]; + let mut _16: *const u32; + let mut _17: *const u32; + let mut _19: *const u32; + let mut _20: *const u32; + let mut _22: *const u32; + let mut _23: *const u32; + scope 1 { + debug m1 => _5; + let _7: *const u32; + scope 2 { + debug m2 => _7; + let _10: *const u32; + scope 3 { + debug m3 => _10; + let _13: *const u32; + scope 4 { + debug m4 => _13; + let _15: bool; + scope 5 { + debug eq_different_thing => _15; + let _18: bool; + scope 6 { + debug eq_optimize => _18; + let _21: bool; + scope 7 { + debug eq_thin_fat => _21; + } + } + } + } + } + } + } + + bb0: { +- StorageLive(_5); ++ nop; + StorageLive(_6); + _6 = _1; +- _5 = move _6 as *const u32 (PtrToPtr); ++ _5 = _1 as *const u32 (PtrToPtr); + StorageDead(_6); + StorageLive(_7); +- StorageLive(_8); ++ nop; + StorageLive(_9); + _9 = _2; +- _8 = move _9 as *const u32 (PtrToPtr); ++ _8 = _2 as *const u32 (PtrToPtr); + StorageDead(_9); +- _7 = move _8 as *const u32 (PtrToPtr); +- StorageDead(_8); ++ _7 = _8; ++ nop; + StorageLive(_10); +- StorageLive(_11); ++ nop; + StorageLive(_12); + _12 = _3; +- _11 = move _12 as *const u32 (PtrToPtr); ++ _11 = _3 as *const u32 (PtrToPtr); + StorageDead(_12); +- _10 = move _11 as *const u32 (PtrToPtr); +- StorageDead(_11); +- StorageLive(_13); ++ _10 = _11; ++ nop; ++ nop; + StorageLive(_14); + _14 = _4; +- _13 = move _14 as *const u32 (PtrToPtr); ++ _13 = _4 as *const u32 (PtrToPtr); + StorageDead(_14); + StorageLive(_15); + StorageLive(_16); + _16 = _5; + StorageLive(_17); +- _17 = _7; +- _15 = Eq(move _16, move _17); ++ _17 = _8; ++ _15 = Eq(_5, _8); + StorageDead(_17); + StorageDead(_16); + StorageLive(_18); + StorageLive(_19); +- _19 = _7; ++ _19 = _8; + StorageLive(_20); +- _20 = _10; +- _18 = Eq(move _19, move _20); ++ _20 = _11; ++ _18 = Eq(_2, _3); + StorageDead(_20); + StorageDead(_19); + StorageLive(_21); + StorageLive(_22); +- _22 = _10; ++ _22 = _11; + StorageLive(_23); + _23 = _13; +- _21 = Eq(move _22, move _23); ++ _21 = Eq(_11, _13); + StorageDead(_23); + StorageDead(_22); + _0 = const (); + StorageDead(_21); + StorageDead(_18); + StorageDead(_15); +- StorageDead(_13); ++ nop; + StorageDead(_10); + StorageDead(_7); +- StorageDead(_5); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-unwind.diff b/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-unwind.diff new file mode 100644 index 00000000000..757ab959813 --- /dev/null +++ b/tests/mir-opt/gvn.cast_pointer_eq.GVN.panic-unwind.diff @@ -0,0 +1,134 @@ +- // MIR for `cast_pointer_eq` before GVN ++ // MIR for `cast_pointer_eq` after GVN + + fn cast_pointer_eq(_1: *mut u8, _2: *mut u32, _3: *mut u32, _4: *mut [u32]) -> () { + debug p1 => _1; + debug p2 => _2; + debug p3 => _3; + debug p4 => _4; + let mut _0: (); + let _5: *const u32; + let mut _6: *mut u8; + let mut _8: *const u32; + let mut _9: *mut u32; + let mut _11: *const u32; + let mut _12: *mut u32; + let mut _14: *mut [u32]; + let mut _16: *const u32; + let mut _17: *const u32; + let mut _19: *const u32; + let mut _20: *const u32; + let mut _22: *const u32; + let mut _23: *const u32; + scope 1 { + debug m1 => _5; + let _7: *const u32; + scope 2 { + debug m2 => _7; + let _10: *const u32; + scope 3 { + debug m3 => _10; + let _13: *const u32; + scope 4 { + debug m4 => _13; + let _15: bool; + scope 5 { + debug eq_different_thing => _15; + let _18: bool; + scope 6 { + debug eq_optimize => _18; + let _21: bool; + scope 7 { + debug eq_thin_fat => _21; + } + } + } + } + } + } + } + + bb0: { +- StorageLive(_5); ++ nop; + StorageLive(_6); + _6 = _1; +- _5 = move _6 as *const u32 (PtrToPtr); ++ _5 = _1 as *const u32 (PtrToPtr); + StorageDead(_6); + StorageLive(_7); +- StorageLive(_8); ++ nop; + StorageLive(_9); + _9 = _2; +- _8 = move _9 as *const u32 (PtrToPtr); ++ _8 = _2 as *const u32 (PtrToPtr); + StorageDead(_9); +- _7 = move _8 as *const u32 (PtrToPtr); +- StorageDead(_8); ++ _7 = _8; ++ nop; + StorageLive(_10); +- StorageLive(_11); ++ nop; + StorageLive(_12); + _12 = _3; +- _11 = move _12 as *const u32 (PtrToPtr); ++ _11 = _3 as *const u32 (PtrToPtr); + StorageDead(_12); +- _10 = move _11 as *const u32 (PtrToPtr); +- StorageDead(_11); +- StorageLive(_13); ++ _10 = _11; ++ nop; ++ nop; + StorageLive(_14); + _14 = _4; +- _13 = move _14 as *const u32 (PtrToPtr); ++ _13 = _4 as *const u32 (PtrToPtr); + StorageDead(_14); + StorageLive(_15); + StorageLive(_16); + _16 = _5; + StorageLive(_17); +- _17 = _7; +- _15 = Eq(move _16, move _17); ++ _17 = _8; ++ _15 = Eq(_5, _8); + StorageDead(_17); + StorageDead(_16); + StorageLive(_18); + StorageLive(_19); +- _19 = _7; ++ _19 = _8; + StorageLive(_20); +- _20 = _10; +- _18 = Eq(move _19, move _20); ++ _20 = _11; ++ _18 = Eq(_2, _3); + StorageDead(_20); + StorageDead(_19); + StorageLive(_21); + StorageLive(_22); +- _22 = _10; ++ _22 = _11; + StorageLive(_23); + _23 = _13; +- _21 = Eq(move _22, move _23); ++ _21 = Eq(_11, _13); + StorageDead(_23); + StorageDead(_22); + _0 = const (); + StorageDead(_21); + StorageDead(_18); + StorageDead(_15); +- StorageDead(_13); ++ nop; + StorageDead(_10); + StorageDead(_7); +- StorageDead(_5); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-abort.diff b/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-abort.diff new file mode 100644 index 00000000000..8133f6e0b00 --- /dev/null +++ b/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-abort.diff @@ -0,0 +1,47 @@ +- // MIR for `cast_pointer_then_transmute` before GVN ++ // MIR for `cast_pointer_then_transmute` after GVN + + fn cast_pointer_then_transmute(_1: *mut u32, _2: *mut [u8]) -> () { + debug thin => _1; + debug fat => _2; + let mut _0: (); + let _3: usize; + let mut _4: *const (); + let mut _5: *mut u32; + let mut _7: *const (); + let mut _8: *mut [u8]; + scope 1 { + debug thin_addr => _3; + let _6: usize; + scope 2 { + debug fat_addr => _6; + } + } + + bb0: { + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + _5 = _1; +- _4 = move _5 as *const () (PtrToPtr); ++ _4 = _1 as *const () (PtrToPtr); + StorageDead(_5); +- _3 = move _4 as usize (Transmute); ++ _3 = _1 as usize (Transmute); + StorageDead(_4); + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = _2; +- _7 = move _8 as *const () (PtrToPtr); ++ _7 = _2 as *const () (PtrToPtr); + StorageDead(_8); + _6 = move _7 as usize (Transmute); + StorageDead(_7); + _0 = const (); + StorageDead(_6); + StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-unwind.diff b/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-unwind.diff new file mode 100644 index 00000000000..8133f6e0b00 --- /dev/null +++ b/tests/mir-opt/gvn.cast_pointer_then_transmute.GVN.panic-unwind.diff @@ -0,0 +1,47 @@ +- // MIR for `cast_pointer_then_transmute` before GVN ++ // MIR for `cast_pointer_then_transmute` after GVN + + fn cast_pointer_then_transmute(_1: *mut u32, _2: *mut [u8]) -> () { + debug thin => _1; + debug fat => _2; + let mut _0: (); + let _3: usize; + let mut _4: *const (); + let mut _5: *mut u32; + let mut _7: *const (); + let mut _8: *mut [u8]; + scope 1 { + debug thin_addr => _3; + let _6: usize; + scope 2 { + debug fat_addr => _6; + } + } + + bb0: { + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + _5 = _1; +- _4 = move _5 as *const () (PtrToPtr); ++ _4 = _1 as *const () (PtrToPtr); + StorageDead(_5); +- _3 = move _4 as usize (Transmute); ++ _3 = _1 as usize (Transmute); + StorageDead(_4); + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = _2; +- _7 = move _8 as *const () (PtrToPtr); ++ _7 = _2 as *const () (PtrToPtr); + StorageDead(_8); + _6 = move _7 as usize (Transmute); + StorageDead(_7); + _0 = const (); + StorageDead(_6); + StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 8bc2550b8f5..86f42d23f38 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -883,6 +883,49 @@ fn generic_cast_metadata(ps: *const [T], pa: *const A, } } +fn cast_pointer_eq(p1: *mut u8, p2: *mut u32, p3: *mut u32, p4: *mut [u32]) { + // CHECK-LABEL: fn cast_pointer_eq + // CHECK: debug p1 => [[P1:_1]]; + // CHECK: debug p2 => [[P2:_2]]; + // CHECK: debug p3 => [[P3:_3]]; + // CHECK: debug p4 => [[P4:_4]]; + + // CHECK: [[M1:_.+]] = [[P1]] as *const u32 (PtrToPtr); + // CHECK: [[M2:_.+]] = [[P2]] as *const u32 (PtrToPtr); + // CHECK: [[M3:_.+]] = [[P3]] as *const u32 (PtrToPtr); + // CHECK: [[M4:_.+]] = [[P4]] as *const u32 (PtrToPtr); + let m1 = p1 as *const u32; + let m2 = p2 as *const u32; + let m3 = p3 as *const u32; + let m4 = p4 as *const u32; + + // CHECK-NOT: Eq + // CHECK: Eq([[M1]], [[M2]]) + // CHECK-NOT: Eq + // CHECK: Eq([[P2]], [[P3]]) + // CHECK-NOT: Eq + // CHECK: Eq([[M3]], [[M4]]) + // CHECK-NOT: Eq + let eq_different_thing = m1 == m2; + let eq_optimize = m2 == m3; + let eq_thin_fat = m3 == m4; + + // CHECK: _0 = const (); +} + +// Transmuting can skip a pointer cast so long as it wasn't a fat-to-thin cast. +unsafe fn cast_pointer_then_transmute(thin: *mut u32, fat: *mut [u8]) { + // CHECK-LABEL: fn cast_pointer_then_transmute + + // CHECK: [[UNUSED:_.+]] = _1 as *const () (PtrToPtr); + // CHECK: = _1 as usize (Transmute); + let thin_addr: usize = std::intrinsics::transmute(thin as *const ()); + + // CHECK: [[TEMP2:_.+]] = _2 as *const () (PtrToPtr); + // CHECK: = move [[TEMP2]] as usize (Transmute); + let fat_addr: usize = std::intrinsics::transmute(fat as *const ()); +} + fn main() { subexpression_elimination(2, 4, 5); wrap_unwrap(5); @@ -950,3 +993,5 @@ fn identity(x: T) -> T { // EMIT_MIR gvn.manual_slice_mut_len.GVN.diff // EMIT_MIR gvn.array_len.GVN.diff // EMIT_MIR gvn.generic_cast_metadata.GVN.diff +// EMIT_MIR gvn.cast_pointer_eq.GVN.diff +// EMIT_MIR gvn.cast_pointer_then_transmute.GVN.diff diff --git a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff index 6c0c7a1d438..7aca2cb0007 100644 --- a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff @@ -15,10 +15,12 @@ let mut _10: bool; bb0: { - StorageLive(_3); +- StorageLive(_3); ++ nop; StorageLive(_4); _4 = _1; - StorageLive(_5); +- StorageLive(_5); ++ nop; StorageLive(_6); StorageLive(_7); _7 = &(*_2); @@ -32,12 +34,14 @@ bb1: { StorageDead(_6); - _3 = Lt(move _4, move _5); -+ _3 = Lt(_1, move _5); - switchInt(move _3) -> [0: bb4, otherwise: bb2]; +- switchInt(move _3) -> [0: bb4, otherwise: bb2]; ++ _3 = Lt(_1, const N); ++ switchInt(_3) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageDead(_5); +- StorageDead(_5); ++ nop; StorageDead(_4); StorageLive(_8); _8 = _1; @@ -45,8 +49,8 @@ - _10 = Lt(_8, _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable]; + _9 = const N; -+ _10 = Lt(_1, const N); -+ assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable]; ++ _10 = _3; ++ assert(_3, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable]; } bb3: { @@ -57,14 +61,16 @@ } bb4: { - StorageDead(_5); +- StorageDead(_5); ++ nop; StorageDead(_4); _0 = const 42_u8; goto -> bb5; } bb5: { - StorageDead(_3); +- StorageDead(_3); ++ nop; return; } } diff --git a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff index ed41703c873..ed39c11319a 100644 --- a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff @@ -15,10 +15,12 @@ let mut _10: bool; bb0: { - StorageLive(_3); +- StorageLive(_3); ++ nop; StorageLive(_4); _4 = _1; - StorageLive(_5); +- StorageLive(_5); ++ nop; StorageLive(_6); StorageLive(_7); _7 = &(*_2); @@ -32,12 +34,14 @@ bb1: { StorageDead(_6); - _3 = Lt(move _4, move _5); -+ _3 = Lt(_1, move _5); - switchInt(move _3) -> [0: bb4, otherwise: bb2]; +- switchInt(move _3) -> [0: bb4, otherwise: bb2]; ++ _3 = Lt(_1, const N); ++ switchInt(_3) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageDead(_5); +- StorageDead(_5); ++ nop; StorageDead(_4); StorageLive(_8); _8 = _1; @@ -45,8 +49,8 @@ - _10 = Lt(_8, _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue]; + _9 = const N; -+ _10 = Lt(_1, const N); -+ assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue]; ++ _10 = _3; ++ assert(_3, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue]; } bb3: { @@ -57,14 +61,16 @@ } bb4: { - StorageDead(_5); +- StorageDead(_5); ++ nop; StorageDead(_4); _0 = const 42_u8; goto -> bb5; } bb5: { - StorageDead(_3); +- StorageDead(_3); ++ nop; return; } } diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff index 80e8ea37f41..734d28e9546 100644 --- a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff @@ -18,10 +18,12 @@ let mut _13: bool; bb0: { - StorageLive(_3); +- StorageLive(_3); ++ nop; StorageLive(_4); _4 = _1; - StorageLive(_5); +- StorageLive(_5); ++ nop; StorageLive(_6); StorageLive(_7); _7 = &(*_2); @@ -35,12 +37,14 @@ bb1: { StorageDead(_6); - _3 = Lt(move _4, move _5); -+ _3 = Lt(_1, move _5); - switchInt(move _3) -> [0: bb4, otherwise: bb2]; +- switchInt(move _3) -> [0: bb4, otherwise: bb2]; ++ _3 = Lt(_1, const N); ++ switchInt(_3) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageDead(_5); +- StorageDead(_5); ++ nop; StorageDead(_4); StorageLive(_8); _8 = _1; @@ -48,8 +52,8 @@ - _10 = Lt(_8, _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind unreachable]; + _9 = const N; -+ _10 = Lt(_1, const N); -+ assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable]; ++ _10 = _3; ++ assert(_3, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind unreachable]; } bb3: { @@ -60,7 +64,8 @@ } bb4: { - StorageDead(_5); +- StorageDead(_5); ++ nop; StorageDead(_4); StorageLive(_11); _11 = const 0_usize; @@ -81,7 +86,8 @@ } bb6: { - StorageDead(_3); +- StorageDead(_3); ++ nop; return; } } diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff index 6e67a6c17ef..ec569ab5042 100644 --- a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff @@ -18,10 +18,12 @@ let mut _13: bool; bb0: { - StorageLive(_3); +- StorageLive(_3); ++ nop; StorageLive(_4); _4 = _1; - StorageLive(_5); +- StorageLive(_5); ++ nop; StorageLive(_6); StorageLive(_7); _7 = &(*_2); @@ -35,12 +37,14 @@ bb1: { StorageDead(_6); - _3 = Lt(move _4, move _5); -+ _3 = Lt(_1, move _5); - switchInt(move _3) -> [0: bb4, otherwise: bb2]; +- switchInt(move _3) -> [0: bb4, otherwise: bb2]; ++ _3 = Lt(_1, const N); ++ switchInt(_3) -> [0: bb4, otherwise: bb2]; } bb2: { - StorageDead(_5); +- StorageDead(_5); ++ nop; StorageDead(_4); StorageLive(_8); _8 = _1; @@ -48,8 +52,8 @@ - _10 = Lt(_8, _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, _8) -> [success: bb3, unwind continue]; + _9 = const N; -+ _10 = Lt(_1, const N); -+ assert(move _10, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue]; ++ _10 = _3; ++ assert(_3, "index out of bounds: the length is {} but the index is {}", const N, _1) -> [success: bb3, unwind continue]; } bb3: { @@ -60,7 +64,8 @@ } bb4: { - StorageDead(_5); +- StorageDead(_5); ++ nop; StorageDead(_4); StorageLive(_11); _11 = const 0_usize; @@ -81,7 +86,8 @@ } bb6: { - StorageDead(_3); +- StorageDead(_3); ++ nop; return; } } diff --git a/tests/mir-opt/lower_array_len.rs b/tests/mir-opt/lower_array_len.rs index caa598d067a..f7ed376726c 100644 --- a/tests/mir-opt/lower_array_len.rs +++ b/tests/mir-opt/lower_array_len.rs @@ -5,16 +5,20 @@ // EMIT_MIR lower_array_len.array_bound.GVN.diff pub fn array_bound(index: usize, slice: &[u8; N]) -> u8 { // CHECK-LABEL: fn array_bound( - // CHECK: [[len:_.*]] = const N; - // CHECK: Lt(_1, move [[len]]); + // CHECK-NOT: Lt + // CHECK: Lt(_1, const N); + // CHECK-NOT: Lt if index < slice.len() { slice[index] } else { 42 } } // EMIT_MIR lower_array_len.array_bound_mut.GVN.diff pub fn array_bound_mut(index: usize, slice: &mut [u8; N]) -> u8 { // CHECK-LABEL: fn array_bound_mut( - // CHECK: [[len:_.*]] = const N; - // CHECK: Lt(_1, move [[len]]); + // CHECK-NOT: Lt + // CHECK: Lt(_1, const N); + // CHECK-NOT: Lt + // CHECK: Lt(const 0_usize, const N) + // CHECK-NOT: Lt if index < slice.len() { slice[index] } else { diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index fbb887fe76a..79c5bcfe9cd 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -7,19 +7,90 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _11: std::slice::Iter<'_, T>; let mut _12: std::iter::Rev>; let mut _13: std::iter::Rev>; - let mut _15: std::option::Option<&T>; - let mut _16: isize; - let mut _18: &impl Fn(&T); - let mut _19: (&T,); - let _20: (); + let mut _37: std::option::Option<&T>; + let mut _39: &impl Fn(&T); + let mut _40: (&T,); + let _41: (); scope 1 { debug iter => _13; - let _17: &T; + let _38: &T; scope 2 { - debug x => _17; + debug x => _38; } scope 17 (inlined > as Iterator>::next) { - let mut _14: &mut std::slice::Iter<'_, T>; + scope 18 (inlined as DoubleEndedIterator>::next_back) { + let mut _14: *const *const T; + let mut _15: *const std::ptr::NonNull; + let mut _20: bool; + let mut _21: *const T; + let _36: &T; + scope 19 { + let _16: std::ptr::NonNull; + let _22: usize; + scope 20 { + } + scope 21 { + scope 25 (inlined as PartialEq>::eq) { + let mut _17: std::ptr::NonNull; + scope 26 (inlined NonNull::::as_ptr) { + let mut _18: *const T; + } + scope 27 (inlined NonNull::::as_ptr) { + let mut _19: *const T; + } + } + } + scope 22 (inlined std::ptr::const_ptr::::addr) { + scope 23 (inlined std::ptr::const_ptr::::cast::<()>) { + } + } + scope 24 (inlined std::ptr::const_ptr::::cast::>) { + } + } + scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { + let _29: std::ptr::NonNull; + scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { + let mut _23: *mut *const T; + let mut _24: *mut std::ptr::NonNull; + let mut _25: std::ptr::NonNull; + let mut _28: std::ptr::NonNull; + let mut _30: *mut *const T; + let mut _31: *mut usize; + let mut _32: usize; + let mut _33: usize; + scope 30 { + scope 31 { + } + scope 32 { + scope 35 (inlined NonNull::::sub) { + scope 36 (inlined core::num::::unchecked_neg) { + scope 37 (inlined core::ub_checks::check_language_ub) { + scope 38 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 39 (inlined NonNull::::offset) { + let mut _26: *const T; + let mut _27: *const T; + } + } + } + scope 33 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 34 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 40 (inlined NonNull::::as_ref::<'_>) { + let mut _34: std::ptr::NonNull; + scope 41 (inlined NonNull::::as_ptr) { + let mut _35: *const T; + } + scope 42 (inlined std::ptr::mut_ptr::::cast_const) { + } + } + } + } } } scope 3 (inlined core::slice::::iter) { @@ -107,45 +178,147 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb4: { - StorageLive(_15); - StorageLive(_14); - _14 = &mut (_13.0: std::slice::Iter<'_, T>); - _15 = as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind unreachable]; + StorageLive(_37); + StorageLive(_22); + StorageLive(_21); + StorageLive(_16); + StorageLive(_36); + StorageLive(_20); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb6]; } bb5: { + StorageLive(_15); + StorageLive(_14); + _14 = &raw const ((_13.0: std::slice::Iter<'_, T>).1: *const T); + _15 = _14 as *const std::ptr::NonNull (PtrToPtr); StorageDead(_14); - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + _16 = (*_15); + StorageDead(_15); + StorageLive(_18); + StorageLive(_19); + StorageLive(_17); + _17 = ((_13.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + _18 = (_17.0: *const T); + StorageDead(_17); + _19 = (_16.0: *const T); + _20 = Eq(_18, _19); + StorageDead(_19); + StorageDead(_18); + goto -> bb7; } bb6: { - StorageDead(_15); - StorageDead(_13); - drop(_2) -> [return: bb7, unwind unreachable]; + _21 = ((_13.0: std::slice::Iter<'_, T>).1: *const T); + _22 = _21 as usize (Transmute); + _20 = Eq(_22, const 0_usize); + goto -> bb7; } bb7: { - return; + switchInt(move _20) -> [0: bb8, otherwise: bb16]; } bb8: { - _17 = ((_15 as Some).0: &T); - StorageLive(_18); - _18 = &_2; - StorageLive(_19); - _19 = (_17,); - _20 = >::call(move _18, move _19) -> [return: bb9, unwind unreachable]; + StorageLive(_35); + StorageLive(_29); + StorageLive(_31); + StorageLive(_24); + switchInt(const ::IS_ZST) -> [0: bb9, otherwise: bb13]; } bb9: { - StorageDead(_19); - StorageDead(_18); - StorageDead(_15); - goto -> bb4; + StorageLive(_23); + _23 = &raw mut ((_13.0: std::slice::Iter<'_, T>).1: *const T); + _24 = _23 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_23); + StorageLive(_28); + _25 = (*_24); + switchInt(const ::IS_ZST) -> [0: bb10, otherwise: bb11]; } bb10: { - unreachable; + StorageLive(_27); + StorageLive(_26); + _26 = (_25.0: *const T); + _27 = Offset(move _26, const -1_isize); + StorageDead(_26); + _28 = NonNull:: { pointer: move _27 }; + StorageDead(_27); + goto -> bb12; + } + + bb11: { + _28 = _25; + goto -> bb12; + } + + bb12: { + (*_24) = move _28; + StorageDead(_28); + _29 = (*_24); + goto -> bb14; + } + + bb13: { + StorageLive(_30); + _30 = &raw mut ((_13.0: std::slice::Iter<'_, T>).1: *const T); + _31 = _30 as *mut usize (PtrToPtr); + StorageDead(_30); + StorageLive(_33); + StorageLive(_32); + _32 = (*_31); + _33 = SubUnchecked(move _32, const 1_usize); + StorageDead(_32); + (*_31) = move _33; + StorageDead(_33); + _29 = ((_13.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + goto -> bb14; + } + + bb14: { + StorageDead(_24); + StorageDead(_31); + StorageLive(_34); + _34 = _29; + _35 = (_34.0: *const T); + StorageDead(_34); + _36 = &(*_35); + StorageDead(_29); + StorageDead(_35); + _37 = Option::<&T>::Some(_36); + StorageDead(_20); + StorageDead(_36); + StorageDead(_16); + StorageDead(_21); + StorageDead(_22); + _38 = ((_37 as Some).0: &T); + StorageLive(_39); + _39 = &_2; + StorageLive(_40); + _40 = (_38,); + _41 = >::call(move _39, move _40) -> [return: bb15, unwind unreachable]; + } + + bb15: { + StorageDead(_40); + StorageDead(_39); + StorageDead(_37); + goto -> bb4; + } + + bb16: { + StorageDead(_20); + StorageDead(_36); + StorageDead(_16); + StorageDead(_21); + StorageDead(_22); + StorageDead(_37); + StorageDead(_13); + drop(_2) -> [return: bb17, unwind unreachable]; + } + + bb17: { + return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index db9409f72ab..7c107a23f9e 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -7,19 +7,90 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let mut _11: std::slice::Iter<'_, T>; let mut _12: std::iter::Rev>; let mut _13: std::iter::Rev>; - let mut _15: std::option::Option<&T>; - let mut _16: isize; - let mut _18: &impl Fn(&T); - let mut _19: (&T,); - let _20: (); + let mut _37: std::option::Option<&T>; + let mut _39: &impl Fn(&T); + let mut _40: (&T,); + let _41: (); scope 1 { debug iter => _13; - let _17: &T; + let _38: &T; scope 2 { - debug x => _17; + debug x => _38; } scope 17 (inlined > as Iterator>::next) { - let mut _14: &mut std::slice::Iter<'_, T>; + scope 18 (inlined as DoubleEndedIterator>::next_back) { + let mut _14: *const *const T; + let mut _15: *const std::ptr::NonNull; + let mut _20: bool; + let mut _21: *const T; + let _36: &T; + scope 19 { + let _16: std::ptr::NonNull; + let _22: usize; + scope 20 { + } + scope 21 { + scope 25 (inlined as PartialEq>::eq) { + let mut _17: std::ptr::NonNull; + scope 26 (inlined NonNull::::as_ptr) { + let mut _18: *const T; + } + scope 27 (inlined NonNull::::as_ptr) { + let mut _19: *const T; + } + } + } + scope 22 (inlined std::ptr::const_ptr::::addr) { + scope 23 (inlined std::ptr::const_ptr::::cast::<()>) { + } + } + scope 24 (inlined std::ptr::const_ptr::::cast::>) { + } + } + scope 28 (inlined std::slice::Iter::<'_, T>::next_back_unchecked) { + let _29: std::ptr::NonNull; + scope 29 (inlined std::slice::Iter::<'_, T>::pre_dec_end) { + let mut _23: *mut *const T; + let mut _24: *mut std::ptr::NonNull; + let mut _25: std::ptr::NonNull; + let mut _28: std::ptr::NonNull; + let mut _30: *mut *const T; + let mut _31: *mut usize; + let mut _32: usize; + let mut _33: usize; + scope 30 { + scope 31 { + } + scope 32 { + scope 35 (inlined NonNull::::sub) { + scope 36 (inlined core::num::::unchecked_neg) { + scope 37 (inlined core::ub_checks::check_language_ub) { + scope 38 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 39 (inlined NonNull::::offset) { + let mut _26: *const T; + let mut _27: *const T; + } + } + } + scope 33 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 34 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 40 (inlined NonNull::::as_ref::<'_>) { + let mut _34: std::ptr::NonNull; + scope 41 (inlined NonNull::::as_ptr) { + let mut _35: *const T; + } + scope 42 (inlined std::ptr::mut_ptr::::cast_const) { + } + } + } + } } } scope 3 (inlined core::slice::::iter) { @@ -107,53 +178,155 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb4: { - StorageLive(_15); - StorageLive(_14); - _14 = &mut (_13.0: std::slice::Iter<'_, T>); - _15 = as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind: bb11]; + StorageLive(_37); + StorageLive(_22); + StorageLive(_21); + StorageLive(_16); + StorageLive(_36); + StorageLive(_20); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb6]; } bb5: { + StorageLive(_15); + StorageLive(_14); + _14 = &raw const ((_13.0: std::slice::Iter<'_, T>).1: *const T); + _15 = _14 as *const std::ptr::NonNull (PtrToPtr); StorageDead(_14); - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + _16 = (*_15); + StorageDead(_15); + StorageLive(_18); + StorageLive(_19); + StorageLive(_17); + _17 = ((_13.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + _18 = (_17.0: *const T); + StorageDead(_17); + _19 = (_16.0: *const T); + _20 = Eq(_18, _19); + StorageDead(_19); + StorageDead(_18); + goto -> bb7; } bb6: { - StorageDead(_15); - StorageDead(_13); - drop(_2) -> [return: bb7, unwind continue]; + _21 = ((_13.0: std::slice::Iter<'_, T>).1: *const T); + _22 = _21 as usize (Transmute); + _20 = Eq(_22, const 0_usize); + goto -> bb7; } bb7: { - return; + switchInt(move _20) -> [0: bb8, otherwise: bb18]; } bb8: { - _17 = ((_15 as Some).0: &T); - StorageLive(_18); - _18 = &_2; - StorageLive(_19); - _19 = (_17,); - _20 = >::call(move _18, move _19) -> [return: bb9, unwind: bb11]; + StorageLive(_35); + StorageLive(_29); + StorageLive(_31); + StorageLive(_24); + switchInt(const ::IS_ZST) -> [0: bb9, otherwise: bb13]; } bb9: { - StorageDead(_19); - StorageDead(_18); - StorageDead(_15); - goto -> bb4; + StorageLive(_23); + _23 = &raw mut ((_13.0: std::slice::Iter<'_, T>).1: *const T); + _24 = _23 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_23); + StorageLive(_28); + _25 = (*_24); + switchInt(const ::IS_ZST) -> [0: bb10, otherwise: bb11]; } bb10: { - unreachable; + StorageLive(_27); + StorageLive(_26); + _26 = (_25.0: *const T); + _27 = Offset(move _26, const -1_isize); + StorageDead(_26); + _28 = NonNull:: { pointer: move _27 }; + StorageDead(_27); + goto -> bb12; } - bb11 (cleanup): { - drop(_2) -> [return: bb12, unwind terminate(cleanup)]; + bb11: { + _28 = _25; + goto -> bb12; } - bb12 (cleanup): { + bb12: { + (*_24) = move _28; + StorageDead(_28); + _29 = (*_24); + goto -> bb14; + } + + bb13: { + StorageLive(_30); + _30 = &raw mut ((_13.0: std::slice::Iter<'_, T>).1: *const T); + _31 = _30 as *mut usize (PtrToPtr); + StorageDead(_30); + StorageLive(_33); + StorageLive(_32); + _32 = (*_31); + _33 = SubUnchecked(move _32, const 1_usize); + StorageDead(_32); + (*_31) = move _33; + StorageDead(_33); + _29 = ((_13.0: std::slice::Iter<'_, T>).0: std::ptr::NonNull); + goto -> bb14; + } + + bb14: { + StorageDead(_24); + StorageDead(_31); + StorageLive(_34); + _34 = _29; + _35 = (_34.0: *const T); + StorageDead(_34); + _36 = &(*_35); + StorageDead(_29); + StorageDead(_35); + _37 = Option::<&T>::Some(_36); + StorageDead(_20); + StorageDead(_36); + StorageDead(_16); + StorageDead(_21); + StorageDead(_22); + _38 = ((_37 as Some).0: &T); + StorageLive(_39); + _39 = &_2; + StorageLive(_40); + _40 = (_38,); + _41 = >::call(move _39, move _40) -> [return: bb15, unwind: bb16]; + } + + bb15: { + StorageDead(_40); + StorageDead(_39); + StorageDead(_37); + goto -> bb4; + } + + bb16 (cleanup): { + drop(_2) -> [return: bb17, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { resume; } + + bb18: { + StorageDead(_20); + StorageDead(_36); + StorageDead(_16); + StorageDead(_21); + StorageDead(_22); + StorageDead(_37); + StorageDead(_13); + drop(_2) -> [return: bb19, unwind continue]; + } + + bb19: { + return; + } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.rs b/tests/mir-opt/pre-codegen/slice_iter.rs index 86f37ca4d13..3f89ab0e6f3 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.rs +++ b/tests/mir-opt/pre-codegen/slice_iter.rs @@ -1,8 +1,10 @@ // skip-filecheck //@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 +//@ only-64bit (constants for `None::<&T>` show in the output) // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] +#![feature(exact_size_is_empty)] // When this test was added, the MIR for `next` was 174 lines just for the basic // blocks -- far more if you counted the scopes. The goal of having this here @@ -13,6 +15,11 @@ // As such, feel free to `--bless` whatever changes you get here, so long as // doing so doesn't add substantially more MIR. +// EMIT_MIR slice_iter.slice_iter_generic_is_empty.PreCodegen.after.mir +pub fn slice_iter_generic_is_empty(it: &std::slice::Iter<'_, T>) -> bool { + it.is_empty() +} + // EMIT_MIR slice_iter.slice_iter_next.PreCodegen.after.mir pub fn slice_iter_next<'a, T>(it: &mut std::slice::Iter<'a, T>) -> Option<&'a T> { it.next() diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir new file mode 100644 index 00000000000..96e71c298e0 --- /dev/null +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir @@ -0,0 +1,76 @@ +// MIR for `slice_iter_generic_is_empty` after PreCodegen + +fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { + debug it => _1; + let mut _0: bool; + scope 1 (inlined as ExactSizeIterator>::is_empty) { + let mut _2: *const *const T; + let mut _3: *const std::ptr::NonNull; + let mut _8: *const T; + scope 2 { + let _4: std::ptr::NonNull; + let _9: usize; + scope 3 { + } + scope 4 { + scope 8 (inlined as PartialEq>::eq) { + let mut _5: std::ptr::NonNull; + scope 9 (inlined NonNull::::as_ptr) { + let mut _6: *const T; + } + scope 10 (inlined NonNull::::as_ptr) { + let mut _7: *const T; + } + } + } + scope 5 (inlined std::ptr::const_ptr::::addr) { + scope 6 (inlined std::ptr::const_ptr::::cast::<()>) { + } + } + scope 7 (inlined std::ptr::const_ptr::::cast::>) { + } + } + } + + bb0: { + StorageLive(_9); + StorageLive(_8); + StorageLive(_4); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + StorageLive(_3); + StorageLive(_2); + _2 = &raw const ((*_1).1: *const T); + _3 = _2 as *const std::ptr::NonNull (PtrToPtr); + StorageDead(_2); + _4 = (*_3); + StorageDead(_3); + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); + _5 = ((*_1).0: std::ptr::NonNull); + _6 = (_5.0: *const T); + StorageDead(_5); + _7 = (_4.0: *const T); + _0 = Eq(_6, _7); + StorageDead(_7); + StorageDead(_6); + goto -> bb3; + } + + bb2: { + _8 = ((*_1).1: *const T); + _9 = _8 as usize (Transmute); + _0 = Eq(_9, const 0_usize); + goto -> bb3; + } + + bb3: { + StorageDead(_4); + StorageDead(_8); + StorageDead(_9); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir new file mode 100644 index 00000000000..96e71c298e0 --- /dev/null +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir @@ -0,0 +1,76 @@ +// MIR for `slice_iter_generic_is_empty` after PreCodegen + +fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool { + debug it => _1; + let mut _0: bool; + scope 1 (inlined as ExactSizeIterator>::is_empty) { + let mut _2: *const *const T; + let mut _3: *const std::ptr::NonNull; + let mut _8: *const T; + scope 2 { + let _4: std::ptr::NonNull; + let _9: usize; + scope 3 { + } + scope 4 { + scope 8 (inlined as PartialEq>::eq) { + let mut _5: std::ptr::NonNull; + scope 9 (inlined NonNull::::as_ptr) { + let mut _6: *const T; + } + scope 10 (inlined NonNull::::as_ptr) { + let mut _7: *const T; + } + } + } + scope 5 (inlined std::ptr::const_ptr::::addr) { + scope 6 (inlined std::ptr::const_ptr::::cast::<()>) { + } + } + scope 7 (inlined std::ptr::const_ptr::::cast::>) { + } + } + } + + bb0: { + StorageLive(_9); + StorageLive(_8); + StorageLive(_4); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + StorageLive(_3); + StorageLive(_2); + _2 = &raw const ((*_1).1: *const T); + _3 = _2 as *const std::ptr::NonNull (PtrToPtr); + StorageDead(_2); + _4 = (*_3); + StorageDead(_3); + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); + _5 = ((*_1).0: std::ptr::NonNull); + _6 = (_5.0: *const T); + StorageDead(_5); + _7 = (_4.0: *const T); + _0 = Eq(_6, _7); + StorageDead(_7); + StorageDead(_6); + goto -> bb3; + } + + bb2: { + _8 = ((*_1).1: *const T); + _9 = _8 as usize (Transmute); + _0 = Eq(_9, const 0_usize); + goto -> bb3; + } + + bb3: { + StorageDead(_4); + StorageDead(_8); + StorageDead(_9); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir index 78f96bf4195..2df346c081c 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir @@ -3,12 +3,205 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut T> { debug it => _1; let mut _0: std::option::Option<&mut T>; + scope 1 (inlined as DoubleEndedIterator>::next_back) { + let mut _2: *const *mut T; + let mut _3: *const std::ptr::NonNull; + let mut _8: bool; + let mut _9: *mut T; + let mut _25: &mut T; + scope 2 { + let _4: std::ptr::NonNull; + let _10: usize; + scope 3 { + } + scope 4 { + scope 8 (inlined as PartialEq>::eq) { + let mut _5: std::ptr::NonNull; + scope 9 (inlined NonNull::::as_ptr) { + let mut _6: *const T; + } + scope 10 (inlined NonNull::::as_ptr) { + let mut _7: *const T; + } + } + } + scope 5 (inlined std::ptr::mut_ptr::::addr) { + scope 6 (inlined std::ptr::mut_ptr::::cast::<()>) { + } + } + scope 7 (inlined std::ptr::const_ptr::::cast::>) { + } + } + scope 11 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { + let mut _17: std::ptr::NonNull; + scope 12 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { + let mut _11: *mut *mut T; + let mut _12: *mut std::ptr::NonNull; + let mut _13: std::ptr::NonNull; + let mut _16: std::ptr::NonNull; + let mut _18: *mut *mut T; + let mut _19: *mut usize; + let mut _20: usize; + let mut _21: usize; + scope 13 { + scope 14 { + } + scope 15 { + scope 18 (inlined NonNull::::sub) { + scope 19 (inlined core::num::::unchecked_neg) { + scope 20 (inlined core::ub_checks::check_language_ub) { + scope 21 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 22 (inlined NonNull::::offset) { + let mut _14: *const T; + let mut _15: *const T; + } + } + } + scope 16 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 17 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 23 (inlined NonNull::::as_mut::<'_>) { + let mut _22: std::ptr::NonNull; + let mut _24: *mut T; + scope 24 (inlined NonNull::::as_ptr) { + let mut _23: *const T; + } + } + } + } bb0: { - _0 = as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind unreachable]; + StorageLive(_10); + StorageLive(_9); + StorageLive(_4); + StorageLive(_25); + StorageLive(_8); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { + StorageLive(_3); + StorageLive(_2); + _2 = &raw const ((*_1).1: *mut T); + _3 = _2 as *const std::ptr::NonNull (PtrToPtr); + StorageDead(_2); + _4 = (*_3); + StorageDead(_3); + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); + _5 = ((*_1).0: std::ptr::NonNull); + _6 = (_5.0: *const T); + StorageDead(_5); + _7 = (_4.0: *const T); + _8 = Eq(_6, _7); + StorageDead(_7); + StorageDead(_6); + goto -> bb3; + } + + bb2: { + _9 = ((*_1).1: *mut T); + _10 = _9 as usize (Transmute); + _8 = Eq(_10, const 0_usize); + goto -> bb3; + } + + bb3: { + switchInt(move _8) -> [0: bb4, otherwise: bb11]; + } + + bb4: { + StorageLive(_24); + StorageLive(_17); + StorageLive(_19); + StorageLive(_12); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb9]; + } + + bb5: { + StorageLive(_11); + _11 = &raw mut ((*_1).1: *mut T); + _12 = _11 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_11); + StorageLive(_16); + _13 = (*_12); + switchInt(const ::IS_ZST) -> [0: bb6, otherwise: bb7]; + } + + bb6: { + StorageLive(_15); + StorageLive(_14); + _14 = (_13.0: *const T); + _15 = Offset(move _14, const -1_isize); + StorageDead(_14); + _16 = NonNull:: { pointer: move _15 }; + StorageDead(_15); + goto -> bb8; + } + + bb7: { + _16 = _13; + goto -> bb8; + } + + bb8: { + (*_12) = move _16; + StorageDead(_16); + _17 = (*_12); + goto -> bb10; + } + + bb9: { + StorageLive(_18); + _18 = &raw mut ((*_1).1: *mut T); + _19 = _18 as *mut usize (PtrToPtr); + StorageDead(_18); + StorageLive(_21); + StorageLive(_20); + _20 = (*_19); + _21 = SubUnchecked(move _20, const 1_usize); + StorageDead(_20); + (*_19) = move _21; + StorageDead(_21); + _17 = ((*_1).0: std::ptr::NonNull); + goto -> bb10; + } + + bb10: { + StorageDead(_12); + StorageDead(_19); + StorageLive(_22); + _22 = _17; + StorageLive(_23); + _23 = (_22.0: *const T); + _24 = move _23 as *mut T (PtrToPtr); + StorageDead(_23); + StorageDead(_22); + _25 = &mut (*_24); + StorageDead(_17); + StorageDead(_24); + _0 = Option::<&mut T>::Some(_25); + goto -> bb12; + } + + bb11: { + _0 = const {transmute(0x0000000000000000): Option<&mut T>}; + goto -> bb12; + } + + bb12: { + StorageDead(_8); + StorageDead(_25); + StorageDead(_4); + StorageDead(_9); + StorageDead(_10); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir index dfe5e206fad..2df346c081c 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir @@ -3,12 +3,205 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut T> { debug it => _1; let mut _0: std::option::Option<&mut T>; + scope 1 (inlined as DoubleEndedIterator>::next_back) { + let mut _2: *const *mut T; + let mut _3: *const std::ptr::NonNull; + let mut _8: bool; + let mut _9: *mut T; + let mut _25: &mut T; + scope 2 { + let _4: std::ptr::NonNull; + let _10: usize; + scope 3 { + } + scope 4 { + scope 8 (inlined as PartialEq>::eq) { + let mut _5: std::ptr::NonNull; + scope 9 (inlined NonNull::::as_ptr) { + let mut _6: *const T; + } + scope 10 (inlined NonNull::::as_ptr) { + let mut _7: *const T; + } + } + } + scope 5 (inlined std::ptr::mut_ptr::::addr) { + scope 6 (inlined std::ptr::mut_ptr::::cast::<()>) { + } + } + scope 7 (inlined std::ptr::const_ptr::::cast::>) { + } + } + scope 11 (inlined std::slice::IterMut::<'_, T>::next_back_unchecked) { + let mut _17: std::ptr::NonNull; + scope 12 (inlined std::slice::IterMut::<'_, T>::pre_dec_end) { + let mut _11: *mut *mut T; + let mut _12: *mut std::ptr::NonNull; + let mut _13: std::ptr::NonNull; + let mut _16: std::ptr::NonNull; + let mut _18: *mut *mut T; + let mut _19: *mut usize; + let mut _20: usize; + let mut _21: usize; + scope 13 { + scope 14 { + } + scope 15 { + scope 18 (inlined NonNull::::sub) { + scope 19 (inlined core::num::::unchecked_neg) { + scope 20 (inlined core::ub_checks::check_language_ub) { + scope 21 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + } + scope 22 (inlined NonNull::::offset) { + let mut _14: *const T; + let mut _15: *const T; + } + } + } + scope 16 (inlined std::ptr::mut_ptr::::cast::) { + } + scope 17 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + } + scope 23 (inlined NonNull::::as_mut::<'_>) { + let mut _22: std::ptr::NonNull; + let mut _24: *mut T; + scope 24 (inlined NonNull::::as_ptr) { + let mut _23: *const T; + } + } + } + } bb0: { - _0 = as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind continue]; + StorageLive(_10); + StorageLive(_9); + StorageLive(_4); + StorageLive(_25); + StorageLive(_8); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { + StorageLive(_3); + StorageLive(_2); + _2 = &raw const ((*_1).1: *mut T); + _3 = _2 as *const std::ptr::NonNull (PtrToPtr); + StorageDead(_2); + _4 = (*_3); + StorageDead(_3); + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); + _5 = ((*_1).0: std::ptr::NonNull); + _6 = (_5.0: *const T); + StorageDead(_5); + _7 = (_4.0: *const T); + _8 = Eq(_6, _7); + StorageDead(_7); + StorageDead(_6); + goto -> bb3; + } + + bb2: { + _9 = ((*_1).1: *mut T); + _10 = _9 as usize (Transmute); + _8 = Eq(_10, const 0_usize); + goto -> bb3; + } + + bb3: { + switchInt(move _8) -> [0: bb4, otherwise: bb11]; + } + + bb4: { + StorageLive(_24); + StorageLive(_17); + StorageLive(_19); + StorageLive(_12); + switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb9]; + } + + bb5: { + StorageLive(_11); + _11 = &raw mut ((*_1).1: *mut T); + _12 = _11 as *mut std::ptr::NonNull (PtrToPtr); + StorageDead(_11); + StorageLive(_16); + _13 = (*_12); + switchInt(const ::IS_ZST) -> [0: bb6, otherwise: bb7]; + } + + bb6: { + StorageLive(_15); + StorageLive(_14); + _14 = (_13.0: *const T); + _15 = Offset(move _14, const -1_isize); + StorageDead(_14); + _16 = NonNull:: { pointer: move _15 }; + StorageDead(_15); + goto -> bb8; + } + + bb7: { + _16 = _13; + goto -> bb8; + } + + bb8: { + (*_12) = move _16; + StorageDead(_16); + _17 = (*_12); + goto -> bb10; + } + + bb9: { + StorageLive(_18); + _18 = &raw mut ((*_1).1: *mut T); + _19 = _18 as *mut usize (PtrToPtr); + StorageDead(_18); + StorageLive(_21); + StorageLive(_20); + _20 = (*_19); + _21 = SubUnchecked(move _20, const 1_usize); + StorageDead(_20); + (*_19) = move _21; + StorageDead(_21); + _17 = ((*_1).0: std::ptr::NonNull); + goto -> bb10; + } + + bb10: { + StorageDead(_12); + StorageDead(_19); + StorageLive(_22); + _22 = _17; + StorageLive(_23); + _23 = (_22.0: *const T); + _24 = move _23 as *mut T (PtrToPtr); + StorageDead(_23); + StorageDead(_22); + _25 = &mut (*_24); + StorageDead(_17); + StorageDead(_24); + _0 = Option::<&mut T>::Some(_25); + goto -> bb12; + } + + bb11: { + _0 = const {transmute(0x0000000000000000): Option<&mut T>}; + goto -> bb12; + } + + bb12: { + StorageDead(_8); + StorageDead(_25); + StorageDead(_4); + StorageDead(_9); + StorageDead(_10); return; } }