1 // unit-test: JumpThreading
2 // compile-flags: -Zmir-enable-passes=+Inline
3 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
5 #![feature(control_flow_enum)]
6 #![feature(try_trait_v2)]
7 #![feature(custom_mir, core_intrinsics, rustc_attrs)]
9 use std
::intrinsics
::mir
::*;
10 use std
::ops
::ControlFlow
;
12 fn too_complex(x
: Result
<i32, usize>) -> Option
<i32> {
13 // CHECK-LABEL: fn too_complex(
15 // CHECK: switchInt(move {{_.*}}) -> [0: bb3, 1: bb1, otherwise: bb2];
17 // CHECK: [[controlflow:_.*]] = ControlFlow::<usize, i32>::Break(
18 // CHECK: goto -> bb8;
20 // CHECK: unreachable;
22 // CHECK: [[controlflow]] = ControlFlow::<usize, i32>::Continue(
23 // CHECK: goto -> bb4;
25 // CHECK: goto -> bb6;
27 // CHECK: {{_.*}} = (([[controlflow]] as Break).0: usize);
28 // CHECK: _0 = Option::<i32>::None;
29 // CHECK: goto -> bb7;
31 // CHECK: {{_.*}} = (([[controlflow]] as Continue).0: i32);
32 // CHECK: _0 = Option::<i32>::Some(
33 // CHECK: goto -> bb7;
37 // CHECK: goto -> bb5;
40 Ok(v
) => ControlFlow
::Continue(v
),
41 Err(r
) => ControlFlow
::Break(r
),
44 ControlFlow
::Continue(v
) => Some(v
),
45 ControlFlow
::Break(r
) => None
,
49 fn identity(x
: Result
<i32, i32>) -> Result
<i32, i32> {
50 // CHECK-LABEL: fn identity(
52 // CHECK: [[x:_.*]] = _1;
53 // CHECK: switchInt(move {{_.*}}) -> [0: bb8, 1: bb6, otherwise: bb7];
55 // CHECK: {{_.*}} = (([[controlflow:_.*]] as Continue).0: i32);
56 // CHECK: _0 = Result::<i32, i32>::Ok(
57 // CHECK: goto -> bb4;
59 // CHECK: unreachable;
61 // CHECK: {{_.*}} = (([[controlflow]] as Break).0: std::result::Result<std::convert::Infallible, i32>);
62 // CHECK: _0 = Result::<i32, i32>::Err(
63 // CHECK: goto -> bb4;
67 // CHECK: goto -> bb1;
69 // CHECK: {{_.*}} = move (([[x]] as Err).0: i32);
70 // CHECK: [[controlflow]] = ControlFlow::<Result<Infallible, i32>, i32>::Break(
71 // CHECK: goto -> bb9;
73 // CHECK: unreachable;
75 // CHECK: {{_.*}} = move (([[x]] as Ok).0: i32);
76 // CHECK: [[controlflow]] = ControlFlow::<Result<Infallible, i32>, i32>::Continue(
77 // CHECK: goto -> bb5;
79 // CHECK: goto -> bb3;
90 /// Check that we do not thread through a loop header,
91 /// to avoid creating an irreducible CFG.
93 // CHECK-LABEL: fn dfa(
95 // CHECK: {{_.*}} = DFA::A;
96 // CHECK: goto -> bb1;
98 // CHECK: switchInt({{.*}}) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3];
102 // CHECK: unreachable;
104 // CHECK: {{_.*}} = DFA::B;
105 // CHECK: goto -> bb1;
107 // CHECK: {{_.*}} = DFA::C;
108 // CHECK: goto -> bb1;
110 // CHECK: {{_.*}} = DFA::D;
111 // CHECK: goto -> bb1;
112 let mut state
= DFA
::A
;
115 DFA
::A
=> state
= DFA
::B
,
116 DFA
::B
=> state
= DFA
::C
,
117 DFA
::C
=> state
= DFA
::D
,
130 /// Verify that we correctly match the discriminant value, and not its index.
131 fn custom_discr(x
: bool
) -> u8 {
132 // CHECK-LABEL: fn custom_discr(
134 // CHECK: switchInt({{.*}}) -> [0: bb2, otherwise: bb1];
136 // CHECK: {{_.*}} = CustomDiscr::A;
137 // CHECK: goto -> bb7;
139 // CHECK: {{_.*}} = CustomDiscr::B;
140 // CHECK: goto -> bb3;
142 // CHECK: goto -> bb4;
144 // CHECK: _0 = const 13_u8;
145 // CHECK: goto -> bb6;
147 // CHECK: _0 = const 5_u8;
148 // CHECK: goto -> bb6;
152 // CHECK: goto -> bb5;
153 match if x { CustomDiscr::A }
else { CustomDiscr::B }
{
159 #[custom_mir(dialect = "runtime", phase = "post-cleanup")]
160 fn multiple_match(x
: u8) -> u8 {
161 // CHECK-LABEL: fn multiple_match(
165 // CHECK: switchInt([[x:_.*]]) -> [3: bb1, otherwise: bb2];
166 match x { 3 => bb1, _ => bb2 }
169 // We know `x == 3`, so we can take `bb3`.
171 // CHECK: {{_.*}} = [[x]];
172 // CHECK: goto -> bb3;
174 match y { 3 => bb3, _ => bb4 }
177 // We know `x != 3`, so we can take `bb6`.
179 // CHECK: [[z:_.*]] = [[x]];
180 // CHECK: goto -> bb6;
182 match z { 3 => bb5, _ => bb6 }
186 // CHECK: _0 = const 5_u8;
193 // CHECK: _0 = const 7_u8;
200 // CHECK: _0 = const 9_u8;
206 // We know `z != 3`, so we CANNOT take `bb7`.
208 // CHECK: switchInt([[z]]) -> [1: bb7, otherwise: bb8];
209 match z { 1 => bb7, _ => bb8 }
213 // CHECK: _0 = const 9_u8;
220 // CHECK: _0 = const 11_u8;
228 /// Both 1-3-4 and 2-3-4 are threadable. As 1 and 2 are the only predecessors of 3,
229 /// verify that we only thread the 3-4 part.
230 #[custom_mir(dialect = "runtime", phase = "post-cleanup")]
231 fn duplicate_chain(x
: bool
) -> u8 {
232 // CHECK-LABEL: fn duplicate_chain(
237 // CHECK: switchInt({{.*}}) -> [1: bb1, otherwise: bb2];
238 match x { true => bb1, _ => bb2 }
242 // CHECK: [[a:_.*]] = const 5_u8;
243 // CHECK: goto -> bb3;
249 // CHECK: [[a]] = const 5_u8;
250 // CHECK: goto -> bb3;
256 // CHECK: {{_.*}} = const 13_i32;
257 // CHECK: goto -> bb4;
263 // CHECK: {{_.*}} = const 15_i32;
264 // CHECK-NOT: switchInt(
265 // CHECK: goto -> bb5;
267 match a { 5 => bb5, _ => bb6 }
271 // CHECK: _0 = const 7_u8;
278 // CHECK: _0 = const 9_u8;
286 #[rustc_layout_scalar_valid_range_start(1)]
287 #[rustc_nonnull_optimization_guaranteed]
288 struct NonZeroUsize(usize);
290 /// Verify that we correctly discard threads that may mutate a discriminant by aliasing.
291 #[custom_mir(dialect = "runtime", phase = "post-cleanup")]
292 fn mutate_discriminant() -> u8 {
293 // CHECK-LABEL: fn mutate_discriminant(
294 // CHECK-NOT: goto -> {{bb.*}};
296 // CHECK-NOT: goto -> {{bb.*}};
298 let x
: Option
<NonZeroUsize
>;
300 SetDiscriminant(x
, 1);
301 // This assignment overwrites the niche in which the discriminant is stored.
302 place
!(Field(Field(Variant(x
, 1), 0), 0)) = 0_usize
;
303 // So we cannot know the value of this discriminant.
304 let a
= Discriminant(x
);
321 /// Verify that we do not try to reason when there are mutable pointers involved.
322 fn mutable_ref() -> bool
{
323 // CHECK-LABEL: fn mutable_ref(
324 // CHECK-NOT: goto -> {{bb.*}};
326 // CHECK: goto -> [[bbret:bb.*]];
327 // CHECK: goto -> [[bbret]];
328 // CHECK: [[bbret]]: {
329 // CHECK-NOT: {{bb.*}}: {
332 let a
= std
::ptr
::addr_of_mut
!(x
);
342 /// This function has 2 TOs: 1-3-4 and 0-1-3-4-6.
343 /// We verify that the second TO does not modify 3 once the first has been applied.
344 #[custom_mir(dialect = "runtime", phase = "post-cleanup")]
345 fn renumbered_bb(x
: bool
) -> u8 {
346 // CHECK-LABEL: fn renumbered_bb(
352 // CHECK: switchInt({{.*}}) -> [1: bb1, otherwise: bb2];
354 match x { true => bb1, _ => bb2 }
358 // CHECK: goto -> bb8;
364 // CHECK: goto -> bb3;
371 // CHECK: switchInt({{.*}}) -> [0: bb4, otherwise: bb5];
372 match a { false => bb4, _ => bb5 }
376 // CHECK: switchInt({{.*}}) -> [0: bb6, otherwise: bb7];
377 match b { false => bb6, _ => bb7 }
381 // CHECK: _0 = const 7_u8;
387 // CHECK: _0 = const 9_u8;
393 // CHECK: _0 = const 11_u8;
399 // CHECK-NEXT: goto -> bb9;
402 // CHECK-NEXT: goto -> bb6;
406 /// This function has 3 TOs: 1-4-5, 0-1-4-7-5-8 and 3-4-7-5-6
407 /// After applying the first TO, we create bb9 to replace 4, and rename 1-4 edge by 1-9. The
408 /// second TO may try to thread non-existing edge 9-4.
409 /// This test verifies that we preserve semantics by bailing out of this second TO.
410 #[custom_mir(dialect = "runtime", phase = "post-cleanup")]
411 fn disappearing_bb(x
: u8) -> u8 {
412 // CHECK-LABEL: fn disappearing_bb(
419 match x { 0 => bb3, 1 => bb3, 2 => bb1, _ => bb2 }
423 // CHECK: goto -> bb9;
432 // CHECK: goto -> bb10;
437 match b { false => bb5, _ => bb7 }
440 match a { false => bb6, _ => bb8 }
452 // CHECK: goto -> bb5;
454 // CHECK: goto -> bb6;
464 duplicate_chain(false);
465 mutate_discriminant();
471 // EMIT_MIR jump_threading.too_complex.JumpThreading.diff
472 // EMIT_MIR jump_threading.identity.JumpThreading.diff
473 // EMIT_MIR jump_threading.custom_discr.JumpThreading.diff
474 // EMIT_MIR jump_threading.dfa.JumpThreading.diff
475 // EMIT_MIR jump_threading.multiple_match.JumpThreading.diff
476 // EMIT_MIR jump_threading.duplicate_chain.JumpThreading.diff
477 // EMIT_MIR jump_threading.mutate_discriminant.JumpThreading.diff
478 // EMIT_MIR jump_threading.mutable_ref.JumpThreading.diff
479 // EMIT_MIR jump_threading.renumbered_bb.JumpThreading.diff
480 // EMIT_MIR jump_threading.disappearing_bb.JumpThreading.diff