From 28df0a62f6816b8efdd650f7c59d049ade6d09b6 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 16 Jan 2024 22:42:35 +0000 Subject: [PATCH] Compute binary ops between pointers in GVN. --- .../src/dataflow_const_prop.rs | 66 +++++++++++++++++-- .../gvn.wide_ptr_ops.GVN.panic-abort.diff | 37 ++++++----- .../gvn.wide_ptr_ops.GVN.panic-unwind.diff | 37 ++++++----- 3 files changed, 104 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 6a37047a693..547375d47db 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -2,7 +2,9 @@ //! //! Currently, this pass only propagates scalar values. -use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable}; +use rustc_const_eval::interpret::{ + ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Pointer, PointerArithmetic, Projectable, +}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar}; @@ -936,12 +938,64 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm } fn binary_ptr_op( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _bin_op: BinOp, - _left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, - _right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, + ecx: &InterpCx<'mir, 'tcx, Self>, + bin_op: BinOp, + left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, + right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> { - throw_machine_stop_str!("can't do pointer arithmetic"); + use rustc_middle::mir::BinOp::*; + Ok(match bin_op { + Eq | Ne | Lt | Le | Gt | Ge => { + assert_eq!(left.layout.abi, right.layout.abi); // types an differ, e.g. fn ptrs with different `for` + let size = ecx.pointer_size(); + // Just compare the bits. ScalarPairs are compared lexicographically. + // We thus always compare pairs and simply fill scalars up with 0. + let left = match **left { + Immediate::Scalar(l) => (l.to_bits(size)?, 0), + Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let right = match **right { + Immediate::Scalar(r) => (r.to_bits(size)?, 0), + Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let res = match bin_op { + Eq => left == right, + Ne => left != right, + Lt => left < right, + Le => left <= right, + Gt => left > right, + Ge => left >= right, + _ => bug!(), + }; + (ImmTy::from_bool(res, *ecx.tcx), false) + } + + // Some more operations are possible with atomics. + // The return value always has the provenance of the *left* operand. + Add | Sub | BitOr | BitAnd | BitXor => { + assert!(left.layout.ty.is_unsafe_ptr()); + assert!(right.layout.ty.is_unsafe_ptr()); + let ptr = left.to_scalar().to_pointer(ecx)?; + // We do the actual operation with usize-typed scalars. + let usize_layout = ecx.layout_of(ecx.tcx.types.usize).unwrap(); + let left = ImmTy::from_uint(ptr.addr().bytes(), usize_layout); + let right = ImmTy::from_uint(right.to_scalar().to_target_usize(ecx)?, usize_layout); + let (result, overflowing) = ecx.overflowing_binary_op(bin_op, &left, &right)?; + // Construct a new pointer with the provenance of `ptr` (the LHS). + let result_ptr = Pointer::new( + ptr.provenance, + Size::from_bytes(result.to_scalar().to_target_usize(ecx)?), + ); + ( + ImmTy::from_scalar(Scalar::from_maybe_pointer(result_ptr, ecx), left.layout), + overflowing, + ) + } + + _ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op), + }) } fn expose_ptr( diff --git a/tests/mir-opt/gvn.wide_ptr_ops.GVN.panic-abort.diff b/tests/mir-opt/gvn.wide_ptr_ops.GVN.panic-abort.diff index e49d759b8fc..9ffac0f88de 100644 --- a/tests/mir-opt/gvn.wide_ptr_ops.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.wide_ptr_ops.GVN.panic-abort.diff @@ -247,13 +247,14 @@ - _45 = _39; - _43 = Eq(move _44, move _45); + _45 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _43 = Eq(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _43 = const false; StorageDead(_45); StorageDead(_44); - _42 = Not(move _43); +- _42 = Not(move _43); ++ _42 = const true; StorageDead(_43); - _41 = opaque::(move _42) -> [return: bb1, unwind unreachable]; -+ _41 = opaque::(_42) -> [return: bb1, unwind unreachable]; ++ _41 = opaque::(const true) -> [return: bb1, unwind unreachable]; } bb1: { @@ -269,11 +270,11 @@ - _49 = _39; - _47 = Ne(move _48, move _49); + _49 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _47 = _42; ++ _47 = const true; StorageDead(_49); StorageDead(_48); - _46 = opaque::(move _47) -> [return: bb2, unwind unreachable]; -+ _46 = opaque::(_42) -> [return: bb2, unwind unreachable]; ++ _46 = opaque::(const true) -> [return: bb2, unwind unreachable]; } bb2: { @@ -288,10 +289,11 @@ - _53 = _39; - _51 = Le(move _52, move _53); + _53 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _51 = Le(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _51 = const true; StorageDead(_53); StorageDead(_52); - _50 = opaque::(move _51) -> [return: bb3, unwind unreachable]; +- _50 = opaque::(move _51) -> [return: bb3, unwind unreachable]; ++ _50 = opaque::(const true) -> [return: bb3, unwind unreachable]; } bb3: { @@ -306,10 +308,11 @@ - _57 = _39; - _55 = Lt(move _56, move _57); + _57 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _55 = Lt(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _55 = const true; StorageDead(_57); StorageDead(_56); - _54 = opaque::(move _55) -> [return: bb4, unwind unreachable]; +- _54 = opaque::(move _55) -> [return: bb4, unwind unreachable]; ++ _54 = opaque::(const true) -> [return: bb4, unwind unreachable]; } bb4: { @@ -325,12 +328,14 @@ - _62 = _39; - _60 = Ge(move _61, move _62); + _62 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _60 = Ge(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _60 = const false; StorageDead(_62); StorageDead(_61); - _59 = Not(move _60); +- _59 = Not(move _60); ++ _59 = const true; StorageDead(_60); - _58 = opaque::(move _59) -> [return: bb5, unwind unreachable]; +- _58 = opaque::(move _59) -> [return: bb5, unwind unreachable]; ++ _58 = opaque::(const true) -> [return: bb5, unwind unreachable]; } bb5: { @@ -346,12 +351,14 @@ - _67 = _39; - _65 = Gt(move _66, move _67); + _67 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _65 = Gt(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _65 = const false; StorageDead(_67); StorageDead(_66); - _64 = Not(move _65); +- _64 = Not(move _65); ++ _64 = const true; StorageDead(_65); - _63 = opaque::(move _64) -> [return: bb6, unwind unreachable]; +- _63 = opaque::(move _64) -> [return: bb6, unwind unreachable]; ++ _63 = opaque::(const true) -> [return: bb6, unwind unreachable]; } bb6: { diff --git a/tests/mir-opt/gvn.wide_ptr_ops.GVN.panic-unwind.diff b/tests/mir-opt/gvn.wide_ptr_ops.GVN.panic-unwind.diff index 4e5608a4425..96234cb14ef 100644 --- a/tests/mir-opt/gvn.wide_ptr_ops.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.wide_ptr_ops.GVN.panic-unwind.diff @@ -247,13 +247,14 @@ - _45 = _39; - _43 = Eq(move _44, move _45); + _45 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _43 = Eq(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _43 = const false; StorageDead(_45); StorageDead(_44); - _42 = Not(move _43); +- _42 = Not(move _43); ++ _42 = const true; StorageDead(_43); - _41 = opaque::(move _42) -> [return: bb1, unwind continue]; -+ _41 = opaque::(_42) -> [return: bb1, unwind continue]; ++ _41 = opaque::(const true) -> [return: bb1, unwind continue]; } bb1: { @@ -269,11 +270,11 @@ - _49 = _39; - _47 = Ne(move _48, move _49); + _49 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _47 = _42; ++ _47 = const true; StorageDead(_49); StorageDead(_48); - _46 = opaque::(move _47) -> [return: bb2, unwind continue]; -+ _46 = opaque::(_42) -> [return: bb2, unwind continue]; ++ _46 = opaque::(const true) -> [return: bb2, unwind continue]; } bb2: { @@ -288,10 +289,11 @@ - _53 = _39; - _51 = Le(move _52, move _53); + _53 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _51 = Le(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _51 = const true; StorageDead(_53); StorageDead(_52); - _50 = opaque::(move _51) -> [return: bb3, unwind continue]; +- _50 = opaque::(move _51) -> [return: bb3, unwind continue]; ++ _50 = opaque::(const true) -> [return: bb3, unwind continue]; } bb3: { @@ -306,10 +308,11 @@ - _57 = _39; - _55 = Lt(move _56, move _57); + _57 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _55 = Lt(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _55 = const true; StorageDead(_57); StorageDead(_56); - _54 = opaque::(move _55) -> [return: bb4, unwind continue]; +- _54 = opaque::(move _55) -> [return: bb4, unwind continue]; ++ _54 = opaque::(const true) -> [return: bb4, unwind continue]; } bb4: { @@ -325,12 +328,14 @@ - _62 = _39; - _60 = Ge(move _61, move _62); + _62 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _60 = Ge(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _60 = const false; StorageDead(_62); StorageDead(_61); - _59 = Not(move _60); +- _59 = Not(move _60); ++ _59 = const true; StorageDead(_60); - _58 = opaque::(move _59) -> [return: bb5, unwind continue]; +- _58 = opaque::(move _59) -> [return: bb5, unwind continue]; ++ _58 = opaque::(const true) -> [return: bb5, unwind continue]; } bb5: { @@ -346,12 +351,14 @@ - _67 = _39; - _65 = Gt(move _66, move _67); + _67 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]; -+ _65 = Gt(const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [u8], const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [u8]); ++ _65 = const false; StorageDead(_67); StorageDead(_66); - _64 = Not(move _65); +- _64 = Not(move _65); ++ _64 = const true; StorageDead(_65); - _63 = opaque::(move _64) -> [return: bb6, unwind continue]; +- _63 = opaque::(move _64) -> [return: bb6, unwind continue]; ++ _63 = opaque::(const true) -> [return: bb6, unwind continue]; } bb6: {