Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 70b0c3e

Browse files
committedMar 4, 2025
Don't drop Rvalue::WrapUnsafeBinder during GVN
1 parent fd17dea commit 70b0c3e

File tree

4 files changed

+127
-32
lines changed

4 files changed

+127
-32
lines changed
 

‎compiler/rustc_mir_transform/src/gvn.rs

+33-32
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ enum Value<'tcx> {
240240
from: Ty<'tcx>,
241241
to: Ty<'tcx>,
242242
},
243+
WrapUnsafeBinder(VnIndex, Ty<'tcx>),
243244
}
244245

245246
struct VnState<'body, 'tcx> {
@@ -580,36 +581,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
580581
let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?;
581582
res.into()
582583
}
583-
CastKind::Transmute => {
584-
let value = self.evaluated[value].as_ref()?;
585-
let to = self.ecx.layout_of(to).ok()?;
586-
// `offset` for immediates generally only supports projections that match the
587-
// type of the immediate. However, as a HACK, we exploit that it can also do
588-
// limited transmutes: it only works between types with the same layout, and
589-
// cannot transmute pointers to integers.
590-
if value.as_mplace_or_imm().is_right() {
591-
let can_transmute = match (value.layout.backend_repr, to.backend_repr) {
592-
(BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => {
593-
s1.size(&self.ecx) == s2.size(&self.ecx)
594-
&& !matches!(s1.primitive(), Primitive::Pointer(..))
595-
}
596-
(BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
597-
a1.size(&self.ecx) == a2.size(&self.ecx) &&
598-
b1.size(&self.ecx) == b2.size(&self.ecx) &&
599-
// The alignment of the second component determines its offset, so that also needs to match.
600-
b1.align(&self.ecx) == b2.align(&self.ecx) &&
601-
// None of the inputs may be a pointer.
602-
!matches!(a1.primitive(), Primitive::Pointer(..))
603-
&& !matches!(b1.primitive(), Primitive::Pointer(..))
604-
}
605-
_ => false,
606-
};
607-
if !can_transmute {
608-
return None;
609-
}
610-
}
611-
value.offset(Size::ZERO, to, &self.ecx).discard_err()?
612-
}
584+
CastKind::Transmute => self.transmute(value, to)?,
613585
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => {
614586
let src = self.evaluated[value].as_ref()?;
615587
let to = self.ecx.layout_of(to).ok()?;
@@ -635,10 +607,38 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
635607
}
636608
_ => return None,
637609
},
610+
WrapUnsafeBinder(value, ty) => self.transmute(value, ty)?,
638611
};
639612
Some(op)
640613
}
641614

615+
fn transmute(&mut self, value: VnIndex, to: Ty<'tcx>) -> Option<OpTy<'tcx>> {
616+
let value = self.evaluated[value].as_ref()?;
617+
let to = self.ecx.layout_of(to).ok()?;
618+
if value.as_mplace_or_imm().is_right() {
619+
let can_transmute = match (value.layout.backend_repr, to.backend_repr) {
620+
(BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => {
621+
s1.size(&self.ecx) == s2.size(&self.ecx)
622+
&& !matches!(s1.primitive(), Primitive::Pointer(..))
623+
}
624+
(BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
625+
a1.size(&self.ecx) == a2.size(&self.ecx) &&
626+
b1.size(&self.ecx) == b2.size(&self.ecx) &&
627+
// The alignment of the second component determines its offset, so that also needs to match.
628+
b1.align(&self.ecx) == b2.align(&self.ecx) &&
629+
// None of the inputs may be a pointer.
630+
!matches!(a1.primitive(), Primitive::Pointer(..))
631+
&& !matches!(b1.primitive(), Primitive::Pointer(..))
632+
}
633+
_ => false,
634+
};
635+
if !can_transmute {
636+
return None;
637+
}
638+
}
639+
Some(value.offset(Size::ZERO, to, &self.ecx).discard_err()?)
640+
}
641+
642642
fn project(
643643
&mut self,
644644
place: PlaceRef<'tcx>,
@@ -872,8 +872,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
872872
self.simplify_place_projection(place, location);
873873
return self.new_pointer(*place, AddressKind::Address(mutbl));
874874
}
875-
Rvalue::WrapUnsafeBinder(ref mut op, _) => {
876-
return self.simplify_operand(op, location);
875+
Rvalue::WrapUnsafeBinder(ref mut op, ty) => {
876+
let value = self.simplify_operand(op, location)?;
877+
Value::WrapUnsafeBinder(value, ty)
877878
}
878879

879880
// Operations.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
- // MIR for `propagate` before GVN
2+
+ // MIR for `propagate` after GVN
3+
4+
fn propagate() -> unsafe<> i32 {
5+
let mut _0: unsafe<> i32;
6+
let _1: i32;
7+
let mut _3: i32;
8+
scope 1 {
9+
debug x => _1;
10+
let _2: unsafe<> i32;
11+
scope 2 {
12+
debug binder => _2;
13+
}
14+
}
15+
16+
bb0: {
17+
- StorageLive(_1);
18+
+ nop;
19+
_1 = const 1_i32;
20+
StorageLive(_2);
21+
StorageLive(_3);
22+
- _3 = copy _1;
23+
- _2 = wrap_binder!(move _3; unsafe<> i32);
24+
+ _3 = const 1_i32;
25+
+ _2 = const {transmute(0x00000001): unsafe<> i32};
26+
StorageDead(_3);
27+
- _0 = move _2;
28+
+ _0 = const {transmute(0x00000001): unsafe<> i32};
29+
StorageDead(_2);
30+
- StorageDead(_1);
31+
+ nop;
32+
return;
33+
}
34+
}
35+

‎tests/mir-opt/gvn_on_unsafe_binder.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// skip-filecheck
2+
//@ test-mir-pass: GVN
3+
4+
// EMIT_MIR gvn_on_unsafe_binder.test.GVN.diff
5+
// EMIT_MIR gvn_on_unsafe_binder.propagate.GVN.diff
6+
7+
#![feature(unsafe_binders)]
8+
9+
use std::unsafe_binder::wrap_binder;
10+
11+
// Test for ICE <https://github.com/rust-lang/rust/issues/137846>.
12+
fn test() {
13+
unsafe {
14+
let x = 1;
15+
let binder: unsafe<'a> &'a i32 = wrap_binder!(&x);
16+
}
17+
}
18+
19+
// Test that GVN propagates const values through unsafe binders.
20+
//
21+
// The lifetime `'a` is redundant (and doesn't print when we print out the type).
22+
// However, we need it so that rustfmt doesn't rip out the `unsafe<>` part for now.
23+
fn propagate() -> unsafe<'a> i32 {
24+
unsafe {
25+
let x = 1;
26+
let binder: unsafe<'a> i32 = wrap_binder!(x);
27+
binder
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
- // MIR for `test` before GVN
2+
+ // MIR for `test` after GVN
3+
4+
fn test() -> () {
5+
let mut _0: ();
6+
let _1: i32;
7+
let mut _3: &i32;
8+
scope 1 {
9+
debug x => _1;
10+
let _2: unsafe<'a> &'a i32;
11+
scope 2 {
12+
debug binder => _2;
13+
}
14+
}
15+
16+
bb0: {
17+
StorageLive(_1);
18+
_1 = const 1_i32;
19+
StorageLive(_2);
20+
StorageLive(_3);
21+
_3 = &_1;
22+
_2 = wrap_binder!(move _3; unsafe<'a> &'a i32);
23+
StorageDead(_3);
24+
_0 = const ();
25+
StorageDead(_2);
26+
StorageDead(_1);
27+
return;
28+
}
29+
}
30+

0 commit comments

Comments
 (0)
Failed to load comments.