Skip to content

Commit 9efcef9

Browse files
committed
Auto merge of rust-lang#120405 - cjgillot:gvn-pointer, r=oli-obk
Fold pointer operations in GVN This PR proposes 2 combinations of cast operations in MIR GVN: - a chain of `PtrToPtr` or `MutToConstPointer` casts can be folded together into a single `PtrToPtr` cast; - we attempt to evaluate more ptr ops when there is no provenance. In particular, this allows to read from static slices. This is not yet sufficient to see through slice operations that use `PtrComponents` (because that's a union), but still a step forward. r? `@ghost`
2 parents 5518eaa + efc5814 commit 9efcef9

File tree

37 files changed

+2115
-1239
lines changed

37 files changed

+2115
-1239
lines changed

compiler/rustc_const_eval/src/interpret/cast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
415415
}
416416
}
417417

418-
fn unsize_into(
418+
pub fn unsize_into(
419419
&mut self,
420420
src: &OpTy<'tcx, M::Provenance>,
421421
cast_ty: TyAndLayout<'tcx>,

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

+46-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
//!
33
//! Currently, this pass only propagates scalar values.
44
5-
use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable};
5+
use rustc_const_eval::interpret::{
6+
ImmTy, Immediate, InterpCx, OpTy, PlaceTy, PointerArithmetic, Projectable,
7+
};
68
use rustc_data_structures::fx::FxHashMap;
79
use rustc_hir::def::DefKind;
810
use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar};
@@ -935,12 +937,50 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
935937
}
936938

937939
fn binary_ptr_op(
938-
_ecx: &InterpCx<'mir, 'tcx, Self>,
939-
_bin_op: BinOp,
940-
_left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
941-
_right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
940+
ecx: &InterpCx<'mir, 'tcx, Self>,
941+
bin_op: BinOp,
942+
left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
943+
right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
942944
) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> {
943-
throw_machine_stop_str!("can't do pointer arithmetic");
945+
use rustc_middle::mir::BinOp::*;
946+
Ok(match bin_op {
947+
Eq | Ne | Lt | Le | Gt | Ge => {
948+
// Types can differ, e.g. fn ptrs with different `for`.
949+
assert_eq!(left.layout.abi, right.layout.abi);
950+
let size = ecx.pointer_size();
951+
// Just compare the bits. ScalarPairs are compared lexicographically.
952+
// We thus always compare pairs and simply fill scalars up with 0.
953+
// If the pointer has provenance, `to_bits` will return `Err` and we bail out.
954+
let left = match **left {
955+
Immediate::Scalar(l) => (l.to_bits(size)?, 0),
956+
Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?),
957+
Immediate::Uninit => panic!("we should never see uninit data here"),
958+
};
959+
let right = match **right {
960+
Immediate::Scalar(r) => (r.to_bits(size)?, 0),
961+
Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?),
962+
Immediate::Uninit => panic!("we should never see uninit data here"),
963+
};
964+
let res = match bin_op {
965+
Eq => left == right,
966+
Ne => left != right,
967+
Lt => left < right,
968+
Le => left <= right,
969+
Gt => left > right,
970+
Ge => left >= right,
971+
_ => bug!(),
972+
};
973+
(ImmTy::from_bool(res, *ecx.tcx), false)
974+
}
975+
976+
// Some more operations are possible with atomics.
977+
// The return value always has the provenance of the *left* operand.
978+
Add | Sub | BitOr | BitAnd | BitXor => {
979+
throw_machine_stop_str!("pointer arithmetic is not handled")
980+
}
981+
982+
_ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op),
983+
})
944984
}
945985

946986
fn expose_ptr(

compiler/rustc_mir_transform/src/gvn.rs

+69-13
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ use rustc_index::IndexVec;
9393
use rustc_middle::mir::interpret::GlobalAlloc;
9494
use rustc_middle::mir::visit::*;
9595
use rustc_middle::mir::*;
96-
use rustc_middle::ty::adjustment::PointerCoercion;
9796
use rustc_middle::ty::layout::LayoutOf;
9897
use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut};
9998
use rustc_span::def_id::DefId;
@@ -551,6 +550,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
551550
}
552551
value.offset(Size::ZERO, to, &self.ecx).ok()?
553552
}
553+
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize) => {
554+
let src = self.evaluated[value].as_ref()?;
555+
let to = self.ecx.layout_of(to).ok()?;
556+
let dest = self.ecx.allocate(to, MemoryKind::Stack).ok()?;
557+
self.ecx.unsize_into(src, to, &dest.clone().into()).ok()?;
558+
self.ecx
559+
.alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id())
560+
.ok()?;
561+
dest.into()
562+
}
563+
CastKind::FnPtrToPtr
564+
| CastKind::PtrToPtr
565+
| CastKind::PointerCoercion(
566+
ty::adjustment::PointerCoercion::MutToConstPointer
567+
| ty::adjustment::PointerCoercion::ArrayToPointer
568+
| ty::adjustment::PointerCoercion::UnsafeFnPointer,
569+
) => {
570+
let src = self.evaluated[value].as_ref()?;
571+
let src = self.ecx.read_immediate(src).ok()?;
572+
let to = self.ecx.layout_of(to).ok()?;
573+
let ret = self.ecx.ptr_to_ptr(&src, to).ok()?;
574+
ret.into()
575+
}
554576
_ => return None,
555577
},
556578
};
@@ -777,18 +799,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
777799

778800
// Operations.
779801
Rvalue::Len(ref mut place) => return self.simplify_len(place, location),
780-
Rvalue::Cast(kind, ref mut value, to) => {
781-
let from = value.ty(self.local_decls, self.tcx);
782-
let value = self.simplify_operand(value, location)?;
783-
if let CastKind::PointerCoercion(
784-
PointerCoercion::ReifyFnPointer | PointerCoercion::ClosureFnPointer(_),
785-
) = kind
786-
{
787-
// Each reification of a generic fn may get a different pointer.
788-
// Do not try to merge them.
789-
return self.new_opaque();
790-
}
791-
Value::Cast { kind, value, from, to }
802+
Rvalue::Cast(ref mut kind, ref mut value, to) => {
803+
return self.simplify_cast(kind, value, to, location);
792804
}
793805
Rvalue::BinaryOp(op, box (ref mut lhs, ref mut rhs)) => {
794806
let ty = lhs.ty(self.local_decls, self.tcx);
@@ -1031,6 +1043,50 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
10311043
}
10321044
}
10331045

1046+
fn simplify_cast(
1047+
&mut self,
1048+
kind: &mut CastKind,
1049+
operand: &mut Operand<'tcx>,
1050+
to: Ty<'tcx>,
1051+
location: Location,
1052+
) -> Option<VnIndex> {
1053+
use rustc_middle::ty::adjustment::PointerCoercion::*;
1054+
use CastKind::*;
1055+
1056+
let mut from = operand.ty(self.local_decls, self.tcx);
1057+
let mut value = self.simplify_operand(operand, location)?;
1058+
if from == to {
1059+
return Some(value);
1060+
}
1061+
1062+
if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_)) = kind {
1063+
// Each reification of a generic fn may get a different pointer.
1064+
// Do not try to merge them.
1065+
return self.new_opaque();
1066+
}
1067+
1068+
if let PtrToPtr | PointerCoercion(MutToConstPointer) = kind
1069+
&& let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } =
1070+
*self.get(value)
1071+
&& let PtrToPtr | PointerCoercion(MutToConstPointer) = inner_kind
1072+
{
1073+
from = inner_from;
1074+
value = inner_value;
1075+
*kind = PtrToPtr;
1076+
if inner_from == to {
1077+
return Some(inner_value);
1078+
}
1079+
if let Some(const_) = self.try_as_constant(value) {
1080+
*operand = Operand::Constant(Box::new(const_));
1081+
} else if let Some(local) = self.try_as_local(value, location) {
1082+
*operand = Operand::Copy(local.into());
1083+
self.reused_locals.insert(local);
1084+
}
1085+
}
1086+
1087+
Some(self.insert(Value::Cast { kind: *kind, value, from, to }))
1088+
}
1089+
10341090
fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> {
10351091
// Trivial case: we are fetching a statically known length.
10361092
let place_ty = place.ty(self.local_decls, self.tcx).ty;

tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff

+5-4
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,17 @@
3131
StorageDead(_3);
3232
StorageLive(_6);
3333
_6 = const 1_usize;
34-
_7 = Len((*_2));
34+
- _7 = Len((*_2));
3535
- _8 = Lt(_6, _7);
3636
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
37-
+ _8 = Lt(const 1_usize, _7);
38-
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind unreachable];
37+
+ _7 = const 3_usize;
38+
+ _8 = const true;
39+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
3940
}
4041

4142
bb1: {
4243
- _1 = (*_2)[_6];
43-
+ _1 = (*_2)[1 of 2];
44+
+ _1 = const 2_u32;
4445
StorageDead(_6);
4546
StorageDead(_4);
4647
StorageDead(_2);

tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff

+5-4
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,17 @@
3131
StorageDead(_3);
3232
StorageLive(_6);
3333
_6 = const 1_usize;
34-
_7 = Len((*_2));
34+
- _7 = Len((*_2));
3535
- _8 = Lt(_6, _7);
3636
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
37-
+ _8 = Lt(const 1_usize, _7);
38-
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind continue];
37+
+ _7 = const 3_usize;
38+
+ _8 = const true;
39+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
3940
}
4041

4142
bb1: {
4243
- _1 = (*_2)[_6];
43-
+ _1 = (*_2)[1 of 2];
44+
+ _1 = const 2_u32;
4445
StorageDead(_6);
4546
StorageDead(_4);
4647
StorageDead(_2);

tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff

+5-4
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,17 @@
3131
StorageDead(_3);
3232
StorageLive(_6);
3333
_6 = const 1_usize;
34-
_7 = Len((*_2));
34+
- _7 = Len((*_2));
3535
- _8 = Lt(_6, _7);
3636
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
37-
+ _8 = Lt(const 1_usize, _7);
38-
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind unreachable];
37+
+ _7 = const 3_usize;
38+
+ _8 = const true;
39+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
3940
}
4041

4142
bb1: {
4243
- _1 = (*_2)[_6];
43-
+ _1 = (*_2)[1 of 2];
44+
+ _1 = const 2_u32;
4445
StorageDead(_6);
4546
StorageDead(_4);
4647
StorageDead(_2);

tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff

+5-4
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,17 @@
3131
StorageDead(_3);
3232
StorageLive(_6);
3333
_6 = const 1_usize;
34-
_7 = Len((*_2));
34+
- _7 = Len((*_2));
3535
- _8 = Lt(_6, _7);
3636
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
37-
+ _8 = Lt(const 1_usize, _7);
38-
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind continue];
37+
+ _7 = const 3_usize;
38+
+ _8 = const true;
39+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
3940
}
4041

4142
bb1: {
4243
- _1 = (*_2)[_6];
43-
+ _1 = (*_2)[1 of 2];
44+
+ _1 = const 2_u32;
4445
StorageDead(_6);
4546
StorageDead(_4);
4647
StorageDead(_2);

tests/mir-opt/const_prop/slice_len.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ fn main() {
88
// CHECK-LABEL: fn main(
99
// CHECK: debug a => [[a:_.*]];
1010
// CHECK: [[slice:_.*]] = const {{.*}} as &[u32] (PointerCoercion(Unsize));
11-
// FIXME(cjgillot) simplify Len and projection into unsized slice.
12-
// CHECK-NOT: assert(const true,
13-
// CHECK: [[a]] = (*[[slice]])[1 of 2];
14-
// CHECK-NOT: [[a]] = const 2_u32;
11+
// CHECK: assert(const true,
12+
// CHECK: [[a]] = const 2_u32;
1513
let a = (&[1u32, 2, 3] as &[u32])[1];
1614
}

tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff

+18-6
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,19 @@
7676
StorageLive(_8);
7777
StorageLive(_9);
7878
StorageLive(_10);
79-
_8 = const {0x1 as *mut [bool; 0]} as *const [bool; 0] (PointerCoercion(MutToConstPointer));
80-
_5 = NonNull::<[bool; 0]> { pointer: _8 };
79+
_8 = const {0x1 as *const [bool; 0]};
80+
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
8181
StorageDead(_10);
8282
StorageDead(_9);
8383
StorageDead(_8);
8484
StorageDead(_6);
85-
_4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
85+
_4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
8686
StorageDead(_5);
87-
_3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize));
87+
_3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
8888
StorageDead(_4);
89-
_2 = Box::<[bool]>(_3, const std::alloc::Global);
89+
_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);
9090
StorageDead(_3);
91-
_1 = A { foo: move _2 };
91+
_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) }};
9292
StorageDead(_2);
9393
_0 = const ();
9494
drop(_1) -> [return: bb1, unwind unreachable];
@@ -100,3 +100,15 @@
100100
}
101101
}
102102

103+
ALLOC2 (size: 8, align: 4) {
104+
01 00 00 00 00 00 00 00 │ ........
105+
}
106+
107+
ALLOC1 (size: 8, align: 4) {
108+
01 00 00 00 00 00 00 00 │ ........
109+
}
110+
111+
ALLOC0 (size: 8, align: 4) {
112+
01 00 00 00 00 00 00 00 │ ........
113+
}
114+

tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff

+18-6
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,19 @@
7676
StorageLive(_8);
7777
StorageLive(_9);
7878
StorageLive(_10);
79-
_8 = const {0x1 as *mut [bool; 0]} as *const [bool; 0] (PointerCoercion(MutToConstPointer));
80-
_5 = NonNull::<[bool; 0]> { pointer: _8 };
79+
_8 = const {0x1 as *const [bool; 0]};
80+
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
8181
StorageDead(_10);
8282
StorageDead(_9);
8383
StorageDead(_8);
8484
StorageDead(_6);
85-
_4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
85+
_4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
8686
StorageDead(_5);
87-
_3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize));
87+
_3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
8888
StorageDead(_4);
89-
_2 = Box::<[bool]>(_3, const std::alloc::Global);
89+
_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);
9090
StorageDead(_3);
91-
_1 = A { foo: move _2 };
91+
_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) }};
9292
StorageDead(_2);
9393
_0 = const ();
9494
drop(_1) -> [return: bb1, unwind: bb2];
@@ -104,3 +104,15 @@
104104
}
105105
}
106106

107+
ALLOC2 (size: 8, align: 4) {
108+
01 00 00 00 00 00 00 00 │ ........
109+
}
110+
111+
ALLOC1 (size: 8, align: 4) {
112+
01 00 00 00 00 00 00 00 │ ........
113+
}
114+
115+
ALLOC0 (size: 8, align: 4) {
116+
01 00 00 00 00 00 00 00 │ ........
117+
}
118+

0 commit comments

Comments
 (0)