Skip to content

Commit acd27bb

Browse files
committed
Auto merge of #109612 - scottmcm:instcombine-transmutes, r=cjgillot
Simplify transmutes in MIR InstCombine Thanks to the combination of #108246 and #108442 it could already remove identity transmutes. With this PR, it can also simplify them to `IntToInt` casts, `Discriminant` reads, or `Field` projections.
2 parents cdbbce0 + f20af8d commit acd27bb

5 files changed

+340
-4
lines changed

compiler/rustc_mir_transform/src/instcombine.rs

+50-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
use crate::MirPass;
44
use rustc_hir::Mutability;
55
use rustc_middle::mir::{
6-
BinOp, Body, Constant, ConstantKind, LocalDecls, Operand, Place, ProjectionElem, Rvalue,
7-
SourceInfo, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, UnOp,
6+
BinOp, Body, CastKind, Constant, ConstantKind, Field, LocalDecls, Operand, Place,
7+
ProjectionElem, Rvalue, SourceInfo, Statement, StatementKind, SwitchTargets, Terminator,
8+
TerminatorKind, UnOp,
89
};
910
use rustc_middle::ty::layout::ValidityRequirement;
11+
use rustc_middle::ty::util::IntTypeExt;
1012
use rustc_middle::ty::{self, ParamEnv, SubstsRef, Ty, TyCtxt};
1113
use rustc_span::symbol::Symbol;
1214

@@ -145,9 +147,53 @@ impl<'tcx> InstCombineContext<'tcx, '_> {
145147
}
146148

147149
fn combine_cast(&self, _source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
148-
if let Rvalue::Cast(_kind, operand, ty) = rvalue {
149-
if operand.ty(self.local_decls, self.tcx) == *ty {
150+
if let Rvalue::Cast(kind, operand, cast_ty) = rvalue {
151+
let operand_ty = operand.ty(self.local_decls, self.tcx);
152+
if operand_ty == *cast_ty {
150153
*rvalue = Rvalue::Use(operand.clone());
154+
} else if *kind == CastKind::Transmute {
155+
// Transmuting an integer to another integer is just a signedness cast
156+
if let (ty::Int(int), ty::Uint(uint)) | (ty::Uint(uint), ty::Int(int)) = (operand_ty.kind(), cast_ty.kind())
157+
&& int.bit_width() == uint.bit_width()
158+
{
159+
// The width check isn't strictly necessary, as different widths
160+
// are UB and thus we'd be allowed to turn it into a cast anyway.
161+
// But let's keep the UB around for codegen to exploit later.
162+
// (If `CastKind::Transmute` ever becomes *not* UB for mismatched sizes,
163+
// then the width check is necessary for big-endian correctness.)
164+
*kind = CastKind::IntToInt;
165+
return;
166+
}
167+
168+
// Transmuting a fieldless enum to its repr is a discriminant read
169+
if let ty::Adt(adt_def, ..) = operand_ty.kind()
170+
&& adt_def.is_enum()
171+
&& adt_def.is_payloadfree()
172+
&& let Some(place) = operand.place()
173+
&& let Some(repr_int) = adt_def.repr().int
174+
&& repr_int.to_ty(self.tcx) == *cast_ty
175+
{
176+
*rvalue = Rvalue::Discriminant(place);
177+
return;
178+
}
179+
180+
// Transmuting a transparent struct/union to a field's type is a projection
181+
if let ty::Adt(adt_def, substs) = operand_ty.kind()
182+
&& adt_def.repr().transparent()
183+
&& (adt_def.is_struct() || adt_def.is_union())
184+
&& let Some(place) = operand.place()
185+
{
186+
let variant = adt_def.non_enum_variant();
187+
for (i, field) in variant.fields.iter().enumerate() {
188+
let field_ty = field.ty(self.tcx, substs);
189+
if field_ty == *cast_ty {
190+
let place = place.project_deeper(&[ProjectionElem::Field(Field::from_usize(i), *cast_ty)], self.tcx);
191+
let operand = if operand.is_move() { Operand::Move(place) } else { Operand::Copy(place) };
192+
*rvalue = Rvalue::Use(operand);
193+
return;
194+
}
195+
}
196+
}
151197
}
152198
}
153199
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
- // MIR for `adt_transmutes` before InstCombine
2+
+ // MIR for `adt_transmutes` after InstCombine
3+
4+
fn adt_transmutes() -> () {
5+
let mut _0: (); // return place in scope 0 at $DIR/combine_transmutes.rs:+0:32: +0:32
6+
let _1: u8; // in scope 0 at $DIR/combine_transmutes.rs:+1:9: +1:11
7+
let mut _2: EnumNoRepr; // in scope 0 at $DIR/combine_transmutes.rs:+1:28: +1:41
8+
let mut _4: EnumNoRepr; // in scope 0 at $DIR/combine_transmutes.rs:+2:28: +2:41
9+
let mut _6: EnumReprIsize; // in scope 0 at $DIR/combine_transmutes.rs:+3:31: +3:47
10+
let mut _8: EnumReprIsize; // in scope 0 at $DIR/combine_transmutes.rs:+4:31: +4:47
11+
let mut _10: std::cmp::Ordering; // in scope 0 at $DIR/combine_transmutes.rs:+5:28: +5:52
12+
let mut _12: std::cmp::Ordering; // in scope 0 at $DIR/combine_transmutes.rs:+6:28: +6:52
13+
let mut _14: std::option::Option<std::num::NonZeroU8>; // in scope 0 at $DIR/combine_transmutes.rs:+7:28: +7:58
14+
let mut _16: std::num::Wrapping<i16>; // in scope 0 at $DIR/combine_transmutes.rs:+8:29: +8:54
15+
let mut _18: std::num::Wrapping<i16>; // in scope 0 at $DIR/combine_transmutes.rs:+9:29: +9:54
16+
let mut _20: Union32; // in scope 0 at $DIR/combine_transmutes.rs:+10:29: +10:47
17+
let mut _22: Union32; // in scope 0 at $DIR/combine_transmutes.rs:+11:29: +11:47
18+
let mut _24: std::mem::MaybeUninit<std::string::String>; // in scope 0 at $DIR/combine_transmutes.rs:+12:46: +12:77
19+
scope 1 {
20+
debug _a => _1; // in scope 1 at $DIR/combine_transmutes.rs:+1:9: +1:11
21+
let _3: i8; // in scope 1 at $DIR/combine_transmutes.rs:+2:9: +2:11
22+
scope 2 {
23+
debug _a => _3; // in scope 2 at $DIR/combine_transmutes.rs:+2:9: +2:11
24+
let _5: usize; // in scope 2 at $DIR/combine_transmutes.rs:+3:9: +3:11
25+
scope 3 {
26+
debug _a => _5; // in scope 3 at $DIR/combine_transmutes.rs:+3:9: +3:11
27+
let _7: isize; // in scope 3 at $DIR/combine_transmutes.rs:+4:9: +4:11
28+
scope 4 {
29+
debug _a => _7; // in scope 4 at $DIR/combine_transmutes.rs:+4:9: +4:11
30+
let _9: u8; // in scope 4 at $DIR/combine_transmutes.rs:+5:9: +5:11
31+
scope 5 {
32+
debug _a => _9; // in scope 5 at $DIR/combine_transmutes.rs:+5:9: +5:11
33+
let _11: i8; // in scope 5 at $DIR/combine_transmutes.rs:+6:9: +6:11
34+
scope 6 {
35+
debug _a => _11; // in scope 6 at $DIR/combine_transmutes.rs:+6:9: +6:11
36+
let _13: u8; // in scope 6 at $DIR/combine_transmutes.rs:+7:9: +7:11
37+
scope 7 {
38+
debug _a => _13; // in scope 7 at $DIR/combine_transmutes.rs:+7:9: +7:11
39+
let _15: i16; // in scope 7 at $DIR/combine_transmutes.rs:+8:9: +8:11
40+
scope 8 {
41+
debug _a => _15; // in scope 8 at $DIR/combine_transmutes.rs:+8:9: +8:11
42+
let _17: u16; // in scope 8 at $DIR/combine_transmutes.rs:+9:9: +9:11
43+
scope 9 {
44+
debug _a => _17; // in scope 9 at $DIR/combine_transmutes.rs:+9:9: +9:11
45+
let _19: u32; // in scope 9 at $DIR/combine_transmutes.rs:+10:9: +10:11
46+
scope 10 {
47+
debug _a => _19; // in scope 10 at $DIR/combine_transmutes.rs:+10:9: +10:11
48+
let _21: i32; // in scope 10 at $DIR/combine_transmutes.rs:+11:9: +11:11
49+
scope 11 {
50+
debug _a => _21; // in scope 11 at $DIR/combine_transmutes.rs:+11:9: +11:11
51+
let _23: std::mem::ManuallyDrop<std::string::String>; // in scope 11 at $DIR/combine_transmutes.rs:+12:9: +12:11
52+
scope 12 {
53+
debug _a => _23; // in scope 12 at $DIR/combine_transmutes.rs:+12:9: +12:11
54+
}
55+
}
56+
}
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}
63+
}
64+
}
65+
}
66+
67+
bb0: {
68+
StorageLive(_1); // scope 0 at $DIR/combine_transmutes.rs:+1:9: +1:11
69+
StorageLive(_2); // scope 0 at $DIR/combine_transmutes.rs:+1:28: +1:41
70+
_2 = EnumNoRepr::A; // scope 0 at $DIR/combine_transmutes.rs:+1:28: +1:41
71+
_1 = move _2 as u8 (Transmute); // scope 0 at $DIR/combine_transmutes.rs:+1:18: +1:42
72+
StorageDead(_2); // scope 0 at $DIR/combine_transmutes.rs:+1:41: +1:42
73+
StorageLive(_3); // scope 1 at $DIR/combine_transmutes.rs:+2:9: +2:11
74+
StorageLive(_4); // scope 1 at $DIR/combine_transmutes.rs:+2:28: +2:41
75+
_4 = EnumNoRepr::B; // scope 1 at $DIR/combine_transmutes.rs:+2:28: +2:41
76+
_3 = move _4 as i8 (Transmute); // scope 1 at $DIR/combine_transmutes.rs:+2:18: +2:42
77+
StorageDead(_4); // scope 1 at $DIR/combine_transmutes.rs:+2:41: +2:42
78+
StorageLive(_5); // scope 2 at $DIR/combine_transmutes.rs:+3:9: +3:11
79+
StorageLive(_6); // scope 2 at $DIR/combine_transmutes.rs:+3:31: +3:47
80+
_6 = EnumReprIsize::A; // scope 2 at $DIR/combine_transmutes.rs:+3:31: +3:47
81+
_5 = move _6 as usize (Transmute); // scope 2 at $DIR/combine_transmutes.rs:+3:21: +3:48
82+
StorageDead(_6); // scope 2 at $DIR/combine_transmutes.rs:+3:47: +3:48
83+
StorageLive(_7); // scope 3 at $DIR/combine_transmutes.rs:+4:9: +4:11
84+
StorageLive(_8); // scope 3 at $DIR/combine_transmutes.rs:+4:31: +4:47
85+
_8 = EnumReprIsize::B; // scope 3 at $DIR/combine_transmutes.rs:+4:31: +4:47
86+
- _7 = move _8 as isize (Transmute); // scope 3 at $DIR/combine_transmutes.rs:+4:21: +4:48
87+
+ _7 = discriminant(_8); // scope 3 at $DIR/combine_transmutes.rs:+4:21: +4:48
88+
StorageDead(_8); // scope 3 at $DIR/combine_transmutes.rs:+4:47: +4:48
89+
StorageLive(_9); // scope 4 at $DIR/combine_transmutes.rs:+5:9: +5:11
90+
StorageLive(_10); // scope 4 at $DIR/combine_transmutes.rs:+5:28: +5:52
91+
_10 = Less; // scope 4 at $DIR/combine_transmutes.rs:+5:28: +5:52
92+
_9 = move _10 as u8 (Transmute); // scope 4 at $DIR/combine_transmutes.rs:+5:18: +5:53
93+
StorageDead(_10); // scope 4 at $DIR/combine_transmutes.rs:+5:52: +5:53
94+
StorageLive(_11); // scope 5 at $DIR/combine_transmutes.rs:+6:9: +6:11
95+
StorageLive(_12); // scope 5 at $DIR/combine_transmutes.rs:+6:28: +6:52
96+
_12 = Less; // scope 5 at $DIR/combine_transmutes.rs:+6:28: +6:52
97+
- _11 = move _12 as i8 (Transmute); // scope 5 at $DIR/combine_transmutes.rs:+6:18: +6:53
98+
+ _11 = discriminant(_12); // scope 5 at $DIR/combine_transmutes.rs:+6:18: +6:53
99+
StorageDead(_12); // scope 5 at $DIR/combine_transmutes.rs:+6:52: +6:53
100+
StorageLive(_13); // scope 6 at $DIR/combine_transmutes.rs:+7:9: +7:11
101+
StorageLive(_14); // scope 6 at $DIR/combine_transmutes.rs:+7:28: +7:58
102+
_14 = Option::<NonZeroU8>::Some(const _); // scope 6 at $DIR/combine_transmutes.rs:+7:28: +7:58
103+
// mir::Constant
104+
// + span: $DIR/combine_transmutes.rs:41:33: 41:57
105+
// + literal: Const { ty: NonZeroU8, val: Unevaluated(NonZeroU8::MAX, [], None) }
106+
_13 = move _14 as u8 (Transmute); // scope 6 at $DIR/combine_transmutes.rs:+7:18: +7:59
107+
StorageDead(_14); // scope 6 at $DIR/combine_transmutes.rs:+7:58: +7:59
108+
StorageLive(_15); // scope 7 at $DIR/combine_transmutes.rs:+8:9: +8:11
109+
StorageLive(_16); // scope 7 at $DIR/combine_transmutes.rs:+8:29: +8:54
110+
_16 = Wrapping::<i16>(const 0_i16); // scope 7 at $DIR/combine_transmutes.rs:+8:29: +8:54
111+
- _15 = move _16 as i16 (Transmute); // scope 7 at $DIR/combine_transmutes.rs:+8:19: +8:55
112+
+ _15 = move (_16.0: i16); // scope 7 at $DIR/combine_transmutes.rs:+8:19: +8:55
113+
StorageDead(_16); // scope 7 at $DIR/combine_transmutes.rs:+8:54: +8:55
114+
StorageLive(_17); // scope 8 at $DIR/combine_transmutes.rs:+9:9: +9:11
115+
StorageLive(_18); // scope 8 at $DIR/combine_transmutes.rs:+9:29: +9:54
116+
_18 = Wrapping::<i16>(const 0_i16); // scope 8 at $DIR/combine_transmutes.rs:+9:29: +9:54
117+
_17 = move _18 as u16 (Transmute); // scope 8 at $DIR/combine_transmutes.rs:+9:19: +9:55
118+
StorageDead(_18); // scope 8 at $DIR/combine_transmutes.rs:+9:54: +9:55
119+
StorageLive(_19); // scope 9 at $DIR/combine_transmutes.rs:+10:9: +10:11
120+
StorageLive(_20); // scope 9 at $DIR/combine_transmutes.rs:+10:29: +10:47
121+
_20 = Union32 { u32: const 0_i32 }; // scope 9 at $DIR/combine_transmutes.rs:+10:29: +10:47
122+
_19 = move _20 as u32 (Transmute); // scope 9 at $DIR/combine_transmutes.rs:+10:19: +10:48
123+
StorageDead(_20); // scope 9 at $DIR/combine_transmutes.rs:+10:47: +10:48
124+
StorageLive(_21); // scope 10 at $DIR/combine_transmutes.rs:+11:9: +11:11
125+
StorageLive(_22); // scope 10 at $DIR/combine_transmutes.rs:+11:29: +11:47
126+
_22 = Union32 { u32: const 0_u32 }; // scope 10 at $DIR/combine_transmutes.rs:+11:29: +11:47
127+
_21 = move _22 as i32 (Transmute); // scope 10 at $DIR/combine_transmutes.rs:+11:19: +11:48
128+
StorageDead(_22); // scope 10 at $DIR/combine_transmutes.rs:+11:47: +11:48
129+
StorageLive(_23); // scope 11 at $DIR/combine_transmutes.rs:+12:9: +12:11
130+
StorageLive(_24); // scope 11 at $DIR/combine_transmutes.rs:+12:46: +12:77
131+
_24 = MaybeUninit::<String>::uninit() -> bb1; // scope 11 at $DIR/combine_transmutes.rs:+12:46: +12:77
132+
// mir::Constant
133+
// + span: $DIR/combine_transmutes.rs:46:46: 46:75
134+
// + user_ty: UserType(23)
135+
// + literal: Const { ty: fn() -> MaybeUninit<String> {MaybeUninit::<String>::uninit}, val: Value(<ZST>) }
136+
}
137+
138+
bb1: {
139+
- _23 = move _24 as std::mem::ManuallyDrop<std::string::String> (Transmute); // scope 11 at $DIR/combine_transmutes.rs:+12:36: +12:78
140+
+ _23 = move (_24.1: std::mem::ManuallyDrop<std::string::String>); // scope 11 at $DIR/combine_transmutes.rs:+12:36: +12:78
141+
StorageDead(_24); // scope 11 at $DIR/combine_transmutes.rs:+12:77: +12:78
142+
_0 = const (); // scope 0 at $DIR/combine_transmutes.rs:+0:32: +13:2
143+
StorageDead(_23); // scope 11 at $DIR/combine_transmutes.rs:+13:1: +13:2
144+
StorageDead(_21); // scope 10 at $DIR/combine_transmutes.rs:+13:1: +13:2
145+
StorageDead(_19); // scope 9 at $DIR/combine_transmutes.rs:+13:1: +13:2
146+
StorageDead(_17); // scope 8 at $DIR/combine_transmutes.rs:+13:1: +13:2
147+
StorageDead(_15); // scope 7 at $DIR/combine_transmutes.rs:+13:1: +13:2
148+
StorageDead(_13); // scope 6 at $DIR/combine_transmutes.rs:+13:1: +13:2
149+
StorageDead(_11); // scope 5 at $DIR/combine_transmutes.rs:+13:1: +13:2
150+
StorageDead(_9); // scope 4 at $DIR/combine_transmutes.rs:+13:1: +13:2
151+
StorageDead(_7); // scope 3 at $DIR/combine_transmutes.rs:+13:1: +13:2
152+
StorageDead(_5); // scope 2 at $DIR/combine_transmutes.rs:+13:1: +13:2
153+
StorageDead(_3); // scope 1 at $DIR/combine_transmutes.rs:+13:1: +13:2
154+
StorageDead(_1); // scope 0 at $DIR/combine_transmutes.rs:+13:1: +13:2
155+
return; // scope 0 at $DIR/combine_transmutes.rs:+13:2: +13:2
156+
}
157+
}
158+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
- // MIR for `identity_transmutes` before InstCombine
2+
+ // MIR for `identity_transmutes` after InstCombine
3+
4+
fn identity_transmutes() -> () {
5+
let mut _0: (); // return place in scope 0 at $DIR/combine_transmutes.rs:+0:37: +0:37
6+
let _1: i32; // in scope 0 at $DIR/combine_transmutes.rs:+2:9: +2:11
7+
let mut _3: std::vec::Vec<i32>; // in scope 0 at $DIR/combine_transmutes.rs:+3:46: +3:56
8+
scope 1 {
9+
debug _a => _1; // in scope 1 at $DIR/combine_transmutes.rs:+2:9: +2:11
10+
let _2: std::vec::Vec<i32>; // in scope 1 at $DIR/combine_transmutes.rs:+3:9: +3:11
11+
scope 2 {
12+
debug _a => _2; // in scope 2 at $DIR/combine_transmutes.rs:+3:9: +3:11
13+
}
14+
}
15+
16+
bb0: {
17+
StorageLive(_1); // scope 0 at $DIR/combine_transmutes.rs:+2:9: +2:11
18+
- _1 = const 1_i32 as i32 (Transmute); // scope 0 at $DIR/combine_transmutes.rs:+2:14: +2:38
19+
+ _1 = const 1_i32; // scope 0 at $DIR/combine_transmutes.rs:+2:14: +2:38
20+
StorageLive(_2); // scope 1 at $DIR/combine_transmutes.rs:+3:9: +3:11
21+
StorageLive(_3); // scope 1 at $DIR/combine_transmutes.rs:+3:46: +3:56
22+
_3 = Vec::<i32>::new() -> bb1; // scope 1 at $DIR/combine_transmutes.rs:+3:46: +3:56
23+
// mir::Constant
24+
// + span: $DIR/combine_transmutes.rs:15:46: 15:54
25+
// + user_ty: UserType(0)
26+
// + literal: Const { ty: fn() -> Vec<i32> {Vec::<i32>::new}, val: Value(<ZST>) }
27+
}
28+
29+
bb1: {
30+
- _2 = move _3 as std::vec::Vec<i32> (Transmute); // scope 1 at $DIR/combine_transmutes.rs:+3:14: +3:57
31+
+ _2 = move _3; // scope 1 at $DIR/combine_transmutes.rs:+3:14: +3:57
32+
StorageDead(_3); // scope 1 at $DIR/combine_transmutes.rs:+3:56: +3:57
33+
_0 = const (); // scope 0 at $DIR/combine_transmutes.rs:+0:37: +4:2
34+
drop(_2) -> bb2; // scope 1 at $DIR/combine_transmutes.rs:+4:1: +4:2
35+
}
36+
37+
bb2: {
38+
StorageDead(_2); // scope 1 at $DIR/combine_transmutes.rs:+4:1: +4:2
39+
StorageDead(_1); // scope 0 at $DIR/combine_transmutes.rs:+4:1: +4:2
40+
return; // scope 0 at $DIR/combine_transmutes.rs:+4:2: +4:2
41+
}
42+
}
43+

0 commit comments

Comments
 (0)