Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fold pointer operations in GVN #120405

Merged
merged 7 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}

fn unsize_into(
pub fn unsize_into(
&mut self,
src: &OpTy<'tcx, M::Provenance>,
cast_ty: TyAndLayout<'tcx>,
Expand Down
52 changes: 46 additions & 6 deletions compiler/rustc_mir_transform/src/dataflow_const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, PointerArithmetic, Projectable,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar};
Expand Down Expand Up @@ -936,12 +938,50 @@ 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 => {
// Types can differ, e.g. fn ptrs with different `for`.
assert_eq!(left.layout.abi, right.layout.abi);
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.
// If the pointer has provenance, `to_bits` will return `Err` and we bail out.
let left = match **left {
Immediate::Scalar(l) => (l.to_bits(size)?, 0),
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
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 => {
throw_machine_stop_str!("pointer arithmetic is not handled")
}

_ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op),
})
}

fn expose_ptr(
Expand Down
82 changes: 69 additions & 13 deletions compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ use rustc_index::IndexVec;
use rustc_middle::mir::interpret::GlobalAlloc;
use rustc_middle::mir::visit::*;
use rustc_middle::mir::*;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut};
use rustc_span::def_id::DefId;
Expand Down Expand Up @@ -552,6 +551,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
value.offset(Size::ZERO, to, &self.ecx).ok()?
}
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize) => {
let src = self.evaluated[value].as_ref()?;
let to = self.ecx.layout_of(to).ok()?;
let dest = self.ecx.allocate(to, MemoryKind::Stack).ok()?;
self.ecx.unsize_into(src, to, &dest.clone().into()).ok()?;
self.ecx
.alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id())
.ok()?;
dest.into()
}
CastKind::FnPtrToPtr
| CastKind::PtrToPtr
| CastKind::PointerCoercion(
ty::adjustment::PointerCoercion::MutToConstPointer
| ty::adjustment::PointerCoercion::ArrayToPointer
| ty::adjustment::PointerCoercion::UnsafeFnPointer,
) => {
let src = self.evaluated[value].as_ref()?;
let src = self.ecx.read_immediate(src).ok()?;
let to = self.ecx.layout_of(to).ok()?;
let ret = self.ecx.ptr_to_ptr(&src, to).ok()?;
ret.into()
}
_ => return None,
},
};
Expand Down Expand Up @@ -778,18 +800,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {

// Operations.
Rvalue::Len(ref mut place) => return self.simplify_len(place, location),
Rvalue::Cast(kind, ref mut value, to) => {
let from = value.ty(self.local_decls, self.tcx);
let value = self.simplify_operand(value, location)?;
if let CastKind::PointerCoercion(
PointerCoercion::ReifyFnPointer | PointerCoercion::ClosureFnPointer(_),
) = kind
{
// Each reification of a generic fn may get a different pointer.
// Do not try to merge them.
return self.new_opaque();
}
Value::Cast { kind, value, from, to }
Rvalue::Cast(ref mut kind, ref mut value, to) => {
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);
Expand Down Expand Up @@ -1035,6 +1047,50 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
}

fn simplify_cast(
&mut self,
kind: &mut CastKind,
operand: &mut Operand<'tcx>,
to: Ty<'tcx>,
location: Location,
) -> Option<VnIndex> {
use rustc_middle::ty::adjustment::PointerCoercion::*;
use CastKind::*;

let mut from = operand.ty(self.local_decls, self.tcx);
let mut value = self.simplify_operand(operand, location)?;
if from == to {
return Some(value);
}

if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_)) = kind {
// Each reification of a generic fn may get a different pointer.
// Do not try to merge them.
return self.new_opaque();
}

if let PtrToPtr | PointerCoercion(MutToConstPointer) = kind
&& let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } =
*self.get(value)
&& let PtrToPtr | PointerCoercion(MutToConstPointer) = inner_kind
{
from = inner_from;
value = inner_value;
*kind = PtrToPtr;
if inner_from == to {
return Some(inner_value);
}
if let Some(const_) = self.try_as_constant(value) {
*operand = Operand::Constant(Box::new(const_));
} else if let Some(local) = self.try_as_local(value, location) {
*operand = Operand::Copy(local.into());
self.reused_locals.insert(local);
}
}

Some(self.insert(Value::Cast { kind: *kind, value, from, to }))
}

fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> {
// Trivial case: we are fetching a statically known length.
let place_ty = place.ty(self.local_decls, self.tcx).ty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@
StorageDead(_3);
StorageLive(_6);
_6 = const 1_usize;
_7 = Len((*_2));
- _7 = Len((*_2));
- _8 = Lt(_6, _7);
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
+ _8 = Lt(const 1_usize, _7);
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind unreachable];
+ _7 = const 3_usize;
+ _8 = const true;
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
}

bb1: {
- _1 = (*_2)[_6];
+ _1 = (*_2)[1 of 2];
+ _1 = const 2_u32;
StorageDead(_6);
StorageDead(_4);
StorageDead(_2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@
StorageDead(_3);
StorageLive(_6);
_6 = const 1_usize;
_7 = Len((*_2));
- _7 = Len((*_2));
- _8 = Lt(_6, _7);
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
+ _8 = Lt(const 1_usize, _7);
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind continue];
+ _7 = const 3_usize;
+ _8 = const true;
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
}

bb1: {
- _1 = (*_2)[_6];
+ _1 = (*_2)[1 of 2];
+ _1 = const 2_u32;
StorageDead(_6);
StorageDead(_4);
StorageDead(_2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@
StorageDead(_3);
StorageLive(_6);
_6 = const 1_usize;
_7 = Len((*_2));
- _7 = Len((*_2));
- _8 = Lt(_6, _7);
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
+ _8 = Lt(const 1_usize, _7);
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind unreachable];
+ _7 = const 3_usize;
+ _8 = const true;
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
}

bb1: {
- _1 = (*_2)[_6];
+ _1 = (*_2)[1 of 2];
+ _1 = const 2_u32;
StorageDead(_6);
StorageDead(_4);
StorageDead(_2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@
StorageDead(_3);
StorageLive(_6);
_6 = const 1_usize;
_7 = Len((*_2));
- _7 = Len((*_2));
- _8 = Lt(_6, _7);
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
+ _8 = Lt(const 1_usize, _7);
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind continue];
+ _7 = const 3_usize;
+ _8 = const true;
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
}

bb1: {
- _1 = (*_2)[_6];
+ _1 = (*_2)[1 of 2];
+ _1 = const 2_u32;
StorageDead(_6);
StorageDead(_4);
StorageDead(_2);
Expand Down
6 changes: 2 additions & 4 deletions tests/mir-opt/const_prop/slice_len.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ fn main() {
// CHECK-LABEL: fn main(
// CHECK: debug a => [[a:_.*]];
// CHECK: [[slice:_.*]] = const {{.*}} as &[u32] (PointerCoercion(Unsize));
// FIXME(cjgillot) simplify Len and projection into unsized slice.
// CHECK-NOT: assert(const true,
// CHECK: [[a]] = (*[[slice]])[1 of 2];
// CHECK-NOT: [[a]] = const 2_u32;
// CHECK: assert(const true,
// CHECK: [[a]] = const 2_u32;
let a = (&[1u32, 2, 3] as &[u32])[1];
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,28 +69,40 @@
}

bb2: {
_10 = const {0x1 as *mut [bool; 0]} as *mut () (PtrToPtr);
_9 = NonNull::<T>::new_unchecked::precondition_check(move _10) -> [return: bb3, unwind unreachable];
_10 = const {0x1 as *mut ()};
_9 = NonNull::<T>::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable];
}

bb3: {
StorageDead(_8);
_11 = const {0x1 as *mut [bool; 0]} as *const [bool; 0] (PointerCoercion(MutToConstPointer));
_5 = NonNull::<[bool; 0]> { pointer: _11 };
_11 = const {0x1 as *const [bool; 0]};
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
StorageDead(_11);
StorageDead(_10);
StorageDead(_6);
_4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
_4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
StorageDead(_5);
_3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize));
_3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
StorageDead(_4);
_2 = Box::<[bool]>(_3, const std::alloc::Global);
_2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
StorageDead(_9);
StorageDead(_3);
_1 = A { foo: move _2 };
_1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
StorageDead(_2);
_0 = const ();
drop(_1) -> [return: bb1, unwind unreachable];
}
}

ALLOC2 (size: 8, align: 4) {
01 00 00 00 00 00 00 00 │ ........
}

ALLOC1 (size: 8, align: 4) {
01 00 00 00 00 00 00 00 │ ........
}

ALLOC0 (size: 8, align: 4) {
01 00 00 00 00 00 00 00 │ ........
}

Original file line number Diff line number Diff line change
Expand Up @@ -73,28 +73,40 @@
}

bb3: {
_10 = const {0x1 as *mut [bool; 0]} as *mut () (PtrToPtr);
_9 = NonNull::<T>::new_unchecked::precondition_check(move _10) -> [return: bb4, unwind unreachable];
_10 = const {0x1 as *mut ()};
_9 = NonNull::<T>::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb4, unwind unreachable];
}

bb4: {
StorageDead(_8);
_11 = const {0x1 as *mut [bool; 0]} as *const [bool; 0] (PointerCoercion(MutToConstPointer));
_5 = NonNull::<[bool; 0]> { pointer: _11 };
_11 = const {0x1 as *const [bool; 0]};
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
StorageDead(_11);
StorageDead(_10);
StorageDead(_6);
_4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
_4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
StorageDead(_5);
_3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize));
_3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
StorageDead(_4);
_2 = Box::<[bool]>(_3, const std::alloc::Global);
_2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
StorageDead(_9);
StorageDead(_3);
_1 = A { foo: move _2 };
_1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
StorageDead(_2);
_0 = const ();
drop(_1) -> [return: bb1, unwind: bb2];
}
}

ALLOC2 (size: 8, align: 4) {
01 00 00 00 00 00 00 00 │ ........
}

ALLOC1 (size: 8, align: 4) {
01 00 00 00 00 00 00 00 │ ........
}

ALLOC0 (size: 8, align: 4) {
01 00 00 00 00 00 00 00 │ ........
}

Loading
Loading