Skip to content

Commit 75c68cf

Browse files
committed
Auto merge of rust-lang#119675 - cjgillot:set-no-discriminant, r=tmiasko
Skip threading over no-op SetDiscriminant. Fixes rust-lang#119674
2 parents d8b44d2 + 41eb9a4 commit 75c68cf

File tree

4 files changed

+150
-2
lines changed

4 files changed

+150
-2
lines changed

compiler/rustc_mir_transform/src/jump_threading.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use rustc_middle::mir::visit::Visitor;
4343
use rustc_middle::mir::*;
4444
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
4545
use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem};
46+
use rustc_target::abi::{TagEncoding, Variants};
4647

4748
use crate::cost_checker::CostChecker;
4849

@@ -391,8 +392,25 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
391392
StatementKind::SetDiscriminant { box place, variant_index } => {
392393
let discr_target = self.map.find_discr(place.as_ref())?;
393394
let enum_ty = place.ty(self.body, self.tcx).ty;
394-
let discr = discriminant_for_variant(enum_ty, *variant_index)?;
395-
self.process_operand(bb, discr_target, &discr, state)?;
395+
// `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
396+
// of a niche encoding. If we cannot ensure that we write to the discriminant, do
397+
// nothing.
398+
let enum_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?;
399+
let writes_discriminant = match enum_layout.variants {
400+
Variants::Single { index } => {
401+
assert_eq!(index, *variant_index);
402+
true
403+
}
404+
Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => true,
405+
Variants::Multiple {
406+
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
407+
..
408+
} => *variant_index != untagged_variant,
409+
};
410+
if writes_discriminant {
411+
let discr = discriminant_for_variant(enum_ty, *variant_index)?;
412+
self.process_operand(bb, discr_target, &discr, state)?;
413+
}
396414
}
397415
// If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
398416
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
- // MIR for `f` before JumpThreading
2+
+ // MIR for `f` after JumpThreading
3+
4+
fn f() -> usize {
5+
let mut _0: usize;
6+
let mut _1: isize;
7+
let mut _2: E<char>;
8+
9+
bb0: {
10+
_2 = E::<char>::A;
11+
discriminant(_2) = 1;
12+
_1 = discriminant(_2);
13+
switchInt(_1) -> [0: bb1, otherwise: bb2];
14+
}
15+
16+
bb1: {
17+
_0 = const 0_usize;
18+
return;
19+
}
20+
21+
bb2: {
22+
_0 = const 1_usize;
23+
return;
24+
}
25+
}
26+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
- // MIR for `generic` before JumpThreading
2+
+ // MIR for `generic` after JumpThreading
3+
4+
fn generic() -> usize {
5+
let mut _0: usize;
6+
let mut _1: isize;
7+
let mut _2: E<T>;
8+
9+
bb0: {
10+
_2 = E::<T>::A;
11+
discriminant(_2) = 1;
12+
_1 = discriminant(_2);
13+
switchInt(_1) -> [0: bb1, otherwise: bb2];
14+
}
15+
16+
bb1: {
17+
_0 = const 0_usize;
18+
return;
19+
}
20+
21+
bb2: {
22+
_0 = const 1_usize;
23+
return;
24+
}
25+
}
26+

tests/mir-opt/set_no_discriminant.rs

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// `SetDiscriminant` does not actually write anything if the chosen variant is the untagged variant
2+
// of a niche encoding. Verify that we do not thread over this case.
3+
// unit-test: JumpThreading
4+
5+
#![feature(custom_mir)]
6+
#![feature(core_intrinsics)]
7+
8+
use std::intrinsics::mir::*;
9+
10+
enum E<T> {
11+
A,
12+
B(T),
13+
}
14+
15+
// EMIT_MIR set_no_discriminant.f.JumpThreading.diff
16+
#[custom_mir(dialect = "runtime")]
17+
pub fn f() -> usize {
18+
// CHECK-LABEL: fn f(
19+
// CHECK-NOT: goto
20+
// CHECK: switchInt(
21+
// CHECK-NOT: goto
22+
mir!(
23+
let a: isize;
24+
let e: E<char>;
25+
{
26+
e = E::A;
27+
SetDiscriminant(e, 1);
28+
a = Discriminant(e);
29+
match a {
30+
0 => bb0,
31+
_ => bb1,
32+
}
33+
}
34+
bb0 = {
35+
RET = 0;
36+
Return()
37+
}
38+
bb1 = {
39+
RET = 1;
40+
Return()
41+
}
42+
)
43+
}
44+
45+
// EMIT_MIR set_no_discriminant.generic.JumpThreading.diff
46+
#[custom_mir(dialect = "runtime")]
47+
pub fn generic<T>() -> usize {
48+
// CHECK-LABEL: fn generic(
49+
// CHECK-NOT: goto
50+
// CHECK: switchInt(
51+
// CHECK-NOT: goto
52+
mir!(
53+
let a: isize;
54+
let e: E<T>;
55+
{
56+
e = E::A;
57+
SetDiscriminant(e, 1);
58+
a = Discriminant(e);
59+
match a {
60+
0 => bb0,
61+
_ => bb1,
62+
}
63+
}
64+
bb0 = {
65+
RET = 0;
66+
Return()
67+
}
68+
bb1 = {
69+
RET = 1;
70+
Return()
71+
}
72+
)
73+
}
74+
75+
fn main() {
76+
assert_eq!(f(), 0);
77+
assert_eq!(generic::<char>(), 0);
78+
}

0 commit comments

Comments
 (0)