diff --git a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff index 9c84aff5361..f290da84e5d 100644 --- a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff +++ b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff @@ -3,14 +3,11 @@ fn disappearing_bb(_1: u8) -> u8 { let mut _0: u8; - let mut _2: i8; + let mut _2: bool; let mut _3: bool; - let mut _4: bool; bb0: { - _4 = const false; - _3 = const false; - _4 = const true; + _2 = const true; _3 = const true; switchInt(_1) -> [0: bb3, 1: bb3, 2: bb1, otherwise: bb2]; } @@ -26,7 +23,7 @@ } bb3: { - _4 = const false; + _2 = const false; goto -> bb4; } @@ -35,7 +32,7 @@ } bb5: { - switchInt(_4) -> [0: bb6, otherwise: bb8]; + switchInt(_2) -> [0: bb6, otherwise: bb8]; } bb6: { diff --git a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff index 9c84aff5361..f290da84e5d 100644 --- a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff @@ -3,14 +3,11 @@ fn disappearing_bb(_1: u8) -> u8 { let mut _0: u8; - let mut _2: i8; + let mut _2: bool; let mut _3: bool; - let mut _4: bool; bb0: { - _4 = const false; - _3 = const false; - _4 = const true; + _2 = const true; _3 = const true; switchInt(_1) -> [0: bb3, 1: bb3, 2: bb1, otherwise: bb2]; } @@ -26,7 +23,7 @@ } bb3: { - _4 = const false; + _2 = const false; goto -> bb4; } @@ -35,7 +32,7 @@ } bb5: { - switchInt(_4) -> [0: bb6, otherwise: bb8]; + switchInt(_2) -> [0: bb6, otherwise: bb8]; } bb6: { diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index dcf61773565..852dcd0db01 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -1,5 +1,5 @@ // unit-test: JumpThreading -// compile-flags: -Zmir-enable-passes=+Inline -Zinline-mir +// compile-flags: -Zmir-enable-passes=+Inline // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![feature(control_flow_enum)] @@ -10,6 +10,31 @@ use std::intrinsics::mir::*; use std::ops::ControlFlow; fn too_complex(x: Result) -> Option { + // CHECK-LABEL: fn too_complex( + // CHECK: bb0: { + // CHECK: switchInt(move {{_.*}}) -> [0: bb3, 1: bb1, otherwise: bb2]; + // CHECK: bb1: { + // CHECK: [[controlflow:_.*]] = ControlFlow::::Break( + // CHECK: goto -> bb8; + // CHECK: bb2: { + // CHECK: unreachable; + // CHECK: bb3: { + // CHECK: [[controlflow]] = ControlFlow::::Continue( + // CHECK: goto -> bb4; + // CHECK: bb4: { + // CHECK: goto -> bb6; + // CHECK: bb5: { + // CHECK: {{_.*}} = (([[controlflow]] as Break).0: usize); + // CHECK: _0 = Option::::None; + // CHECK: goto -> bb7; + // CHECK: bb6: { + // CHECK: {{_.*}} = (([[controlflow]] as Continue).0: i32); + // CHECK: _0 = Option::::Some( + // CHECK: goto -> bb7; + // CHECK: bb7: { + // CHECK: return; + // CHECK: bb8: { + // CHECK: goto -> bb5; match { match x { Ok(v) => ControlFlow::Continue(v), @@ -22,6 +47,36 @@ fn too_complex(x: Result) -> Option { } fn identity(x: Result) -> Result { + // CHECK-LABEL: fn identity( + // CHECK: bb0: { + // CHECK: [[x:_.*]] = _1; + // CHECK: switchInt(move {{_.*}}) -> [0: bb8, 1: bb6, otherwise: bb7]; + // CHECK: bb1: { + // CHECK: goto -> bb2; + // CHECK: bb2: { + // CHECK: {{_.*}} = (([[controlflow:_.*]] as Continue).0: i32); + // CHECK: _0 = Result::::Ok( + // CHECK: goto -> bb5; + // CHECK: bb3: { + // CHECK: unreachable; + // CHECK: bb4: { + // CHECK: {{_.*}} = (([[controlflow]] as Break).0: std::result::Result); + // CHECK: _0 = Result::::Err( + // CHECK: goto -> bb5; + // CHECK: bb5: { + // CHECK: return; + // CHECK: bb6: { + // CHECK: {{_.*}} = move (([[x]] as Err).0: i32); + // CHECK: [[controlflow]] = ControlFlow::, i32>::Break( + // CHECK: goto -> bb9; + // CHECK: bb7: { + // CHECK: unreachable; + // CHECK: bb8: { + // CHECK: {{_.*}} = move (([[x]] as Ok).0: i32); + // CHECK: [[controlflow]] = ControlFlow::, i32>::Continue( + // CHECK: goto -> bb1; + // CHECK: bb9: { + // CHECK: goto -> bb4; Ok(x?) } @@ -32,7 +87,28 @@ enum DFA { D, } +/// Check that we do not thread through a loop header, +/// to avoid creating an irreducible CFG. fn dfa() { + // CHECK-LABEL: fn dfa( + // CHECK: bb0: { + // CHECK: {{_.*}} = DFA::A; + // CHECK: goto -> bb1; + // CHECK: bb1: { + // CHECK: switchInt({{.*}}) -> [0: bb4, 1: bb5, 2: bb6, 3: bb2, otherwise: bb3]; + // CHECK: bb2: { + // CHECK: return; + // CHECK: bb3: { + // CHECK: unreachable; + // CHECK: bb4: { + // CHECK: {{_.*}} = DFA::B; + // CHECK: goto -> bb1; + // CHECK: bb5: { + // CHECK: {{_.*}} = DFA::C; + // CHECK: goto -> bb1; + // CHECK: bb6: { + // CHECK: {{_.*}} = DFA::D; + // CHECK: goto -> bb1; let mut state = DFA::A; loop { match state { @@ -51,7 +127,29 @@ enum CustomDiscr { C = 99, } +/// Verify that we correctly match the discriminant value, and not its index. fn custom_discr(x: bool) -> u8 { + // CHECK-LABEL: fn custom_discr( + // CHECK: bb0: { + // CHECK: switchInt({{.*}}) -> [0: bb2, otherwise: bb1]; + // CHECK: bb1: { + // CHECK: {{_.*}} = CustomDiscr::A; + // CHECK: goto -> bb7; + // CHECK: bb2: { + // CHECK: {{_.*}} = CustomDiscr::B; + // CHECK: goto -> bb3; + // CHECK: bb3: { + // CHECK: goto -> bb4; + // CHECK: bb4: { + // CHECK: _0 = const 13_u8; + // CHECK: goto -> bb6; + // CHECK: bb5: { + // CHECK: _0 = const 5_u8; + // CHECK: goto -> bb6; + // CHECK: bb6: { + // CHECK: return; + // CHECK: bb7: { + // CHECK: goto -> bb5; match if x { CustomDiscr::A } else { CustomDiscr::B } { CustomDiscr::A => 5, _ => 13, @@ -60,76 +158,125 @@ fn custom_discr(x: bool) -> u8 { #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn multiple_match(x: u8) -> u8 { + // CHECK-LABEL: fn multiple_match( mir!( { + // CHECK: bb0: { + // CHECK: switchInt([[x:_.*]]) -> [3: bb1, otherwise: bb2]; match x { 3 => bb1, _ => bb2 } } bb1 = { // We know `x == 3`, so we can take `bb3`. + // CHECK: bb1: { + // CHECK: {{_.*}} = [[x]]; + // CHECK: goto -> bb3; let y = x; match y { 3 => bb3, _ => bb4 } } bb2 = { // We know `x != 3`, so we can take `bb6`. + // CHECK: bb2: { + // CHECK: [[z:_.*]] = [[x]]; + // CHECK: goto -> bb6; let z = x; match z { 3 => bb5, _ => bb6 } } bb3 = { + // CHECK: bb3: { + // CHECK: _0 = const 5_u8; + // CHECK: return; RET = 5; Return() } bb4 = { + // CHECK: bb4: { + // CHECK: _0 = const 7_u8; + // CHECK: return; RET = 7; Return() } bb5 = { + // CHECK: bb5: { + // CHECK: _0 = const 9_u8; + // CHECK: return; RET = 9; Return() } bb6 = { // We know `z != 3`, so we CANNOT take `bb7`. + // CHECK: bb6: { + // CHECK: switchInt([[z]]) -> [1: bb7, otherwise: bb8]; match z { 1 => bb7, _ => bb8 } } bb7 = { + // CHECK: bb7: { + // CHECK: _0 = const 9_u8; + // CHECK: return; RET = 9; Return() } bb8 = { + // CHECK: bb8: { + // CHECK: _0 = const 11_u8; + // CHECK: return; RET = 11; Return() } ) } +/// Both 1-3-4 and 2-3-4 are threadable. As 1 and 2 are the only predecessors of 3, +/// verify that we only thread the 3-4 part. #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn duplicate_chain(x: bool) -> u8 { + // CHECK-LABEL: fn duplicate_chain( mir!( let a: u8; { + // CHECK: bb0: { + // CHECK: switchInt({{.*}}) -> [1: bb1, otherwise: bb2]; match x { true => bb1, _ => bb2 } } bb1 = { + // CHECK: bb1: { + // CHECK: [[a:_.*]] = const 5_u8; + // CHECK: goto -> bb3; a = 5; Goto(bb3) } bb2 = { + // CHECK: bb2: { + // CHECK: [[a]] = const 5_u8; + // CHECK: goto -> bb3; a = 5; Goto(bb3) } - // Verify that we do not create multiple copied of `bb3`. bb3 = { + // CHECK: bb3: { + // CHECK: {{_.*}} = const 13_i32; + // CHECK: goto -> bb4; let b = 13; Goto(bb4) } bb4 = { + // CHECK: bb4: { + // CHECK: {{_.*}} = const 15_i32; + // CHECK-NOT: switchInt( + // CHECK: goto -> bb5; let c = 15; match a { 5 => bb5, _ => bb6 } } bb5 = { + // CHECK: bb5: { + // CHECK: _0 = const 7_u8; + // CHECK: return; RET = 7; Return() } bb6 = { + // CHECK: bb6: { + // CHECK: _0 = const 9_u8; + // CHECK: return; RET = 9; Return() } @@ -140,8 +287,13 @@ fn duplicate_chain(x: bool) -> u8 { #[rustc_nonnull_optimization_guaranteed] struct NonZeroUsize(usize); +/// Verify that we correctly discard threads that may mutate a discriminant by aliasing. #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn mutate_discriminant() -> u8 { + // CHECK-LABEL: fn mutate_discriminant( + // CHECK-NOT: goto -> {{bb.*}}; + // CHECK: switchInt( + // CHECK-NOT: goto -> {{bb.*}}; mir!( let x: Option; { @@ -166,8 +318,16 @@ fn mutate_discriminant() -> u8 { ) } -// Verify that we do not try to reason when there are mutable pointers involved. +/// Verify that we do not try to reason when there are mutable pointers involved. fn mutable_ref() -> bool { + // CHECK-LABEL: fn mutable_ref( + // CHECK-NOT: goto -> {{bb.*}}; + // CHECK: switchInt( + // CHECK: goto -> [[bbret:bb.*]]; + // CHECK: goto -> [[bbret]]; + // CHECK: [[bbret]]: { + // CHECK-NOT: {{bb.*}}: { + // CHECK: return; let mut x = 5; let a = std::ptr::addr_of_mut!(x); x = 7; @@ -179,80 +339,105 @@ fn mutable_ref() -> bool { } } +/// This function has 2 TOs: 1-3-4 and 0-1-3-4-6. +/// We verify that the second TO does not modify 3 once the first has been applied. #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn renumbered_bb(x: bool) -> u8 { - // This function has 2 TOs: 1-3-4 and 0-1-3-4-6. - // We verify that the second TO does not modify 3 once the first has been applied. + // CHECK-LABEL: fn renumbered_bb( mir!( let a: bool; let b: bool; { + // CHECK: bb0: { + // CHECK: switchInt({{.*}}) -> [1: bb1, otherwise: bb2]; b = false; match x { true => bb1, _ => bb2 } } bb1 = { + // CHECK: bb1: { + // CHECK: goto -> bb8; a = false; Goto(bb3) } bb2 = { + // CHECK: bb2: { + // CHECK: goto -> bb3; a = x; b = x; Goto(bb3) } bb3 = { + // CHECK: bb3: { + // CHECK: switchInt({{.*}}) -> [0: bb4, otherwise: bb5]; match a { false => bb4, _ => bb5 } } bb4 = { + // CHECK: bb4: { + // CHECK: switchInt({{.*}}) -> [0: bb6, otherwise: bb7]; match b { false => bb6, _ => bb7 } } bb5 = { + // CHECK: bb5: { + // CHECK: _0 = const 7_u8; RET = 7; Return() } bb6 = { + // CHECK: bb6: { + // CHECK: _0 = const 9_u8; RET = 9; Return() } bb7 = { + // CHECK: bb7: { + // CHECK: _0 = const 11_u8; RET = 11; Return() } + // Duplicate of bb3. + // CHECK: bb8: { + // CHECK-NEXT: goto -> bb9; + // Duplicate of bb4. + // CHECK: bb9: { + // CHECK-NEXT: goto -> bb6; ) } +/// This function has 3 TOs: 1-4-5, 0-1-4-7-5-8 and 3-4-7-5-6 +/// After applying the first TO, we create bb9 to replace 4, and rename 1-4 edge by 1-9. The +/// second TO may try to thread non-existing edge 9-4. +/// This test verifies that we preserve semantics by bailing out of this second TO. #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn disappearing_bb(x: u8) -> u8 { - // This function has 3 TOs: 1-4-5, 0-1-4-7-5-8 and 3-4-7-5-6 - // After applying the first TO, we create bb9 to replace 4, and rename 1-4 edge by 1-9. The - // second TO may try to thread non-existing edge 9-4. - // This test verifies that we preserve semantics by bailing out of this second TO. + // CHECK-LABEL: fn disappearing_bb( mir!( - let _11: i8; - let _12: bool; - let _13: bool; + let a: bool; + let b: bool; { - _13 = false; - _12 = false; - _13 = true; - _12 = true; + a = true; + b = true; match x { 0 => bb3, 1 => bb3, 2 => bb1, _ => bb2 } } bb1 = { - _12 = false; + // CHECK: bb1: { + // CHECK: goto -> bb9; + b = false; Goto(bb4) } bb2 = { Unreachable() } bb3 = { - _13 = false; + // CHECK: bb3: { + // CHECK: goto -> bb10; + a = false; Goto(bb4) } bb4 = { - match _12 { false => bb5, _ => bb7 } + match b { false => bb5, _ => bb7 } } bb5 = { - match _13 { false => bb6, _ => bb8 } + match a { false => bb6, _ => bb8 } } bb6 = { Return() @@ -263,6 +448,10 @@ fn disappearing_bb(x: u8) -> u8 { bb8 = { Goto(bb6) } + // CHECK: bb9: { + // CHECK: goto -> bb5; + // CHECK: bb10: { + // CHECK: goto -> bb6; ) }