; RUN: opt -passes='loop(simple-loop-unswitch),verify<loops>' -S < %s | FileCheck %s ; RUN: opt -verify-memoryssa -passes='loop-mssa(simple-loop-unswitch),verify<loops>' -S < %s | FileCheck %s declare void @some_func() noreturn declare void @sink(i32) declare i1 @cond() declare i32 @cond.i32() ; This test contains two trivial unswitch condition in one loop. ; LoopUnswitch pass should be able to unswitch the second one ; after unswitching the first one. define i32 @test1(i32* %var, i1 %cond1, i1 %cond2) { ; CHECK-LABEL: @test1( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 %{{.*}}, label %entry.split, label %loop_exit.split ; ; CHECK: entry.split: ; CHECK-NEXT: br i1 %{{.*}}, label %entry.split.split, label %loop_exit ; ; CHECK: entry.split.split: ; CHECK-NEXT: br label %loop_begin loop_begin: br i1 %cond1, label %continue, label %loop_exit ; first trivial condition ; CHECK: loop_begin: ; CHECK-NEXT: br label %continue continue: %var_val = load i32, i32* %var br i1 %cond2, label %do_something, label %loop_exit ; second trivial condition ; CHECK: continue: ; CHECK-NEXT: load ; CHECK-NEXT: br label %do_something do_something: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: do_something: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: ret i32 0 ; CHECK: loop_exit: ; CHECK-NEXT: br label %loop_exit.split ; ; CHECK: loop_exit.split: ; CHECK-NEXT: ret } ; Test for two trivially unswitchable switches. define i32 @test3(i32* %var, i32 %cond1, i32 %cond2) { ; CHECK-LABEL: @test3( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: switch i32 %cond1, label %entry.split [ ; CHECK-NEXT: i32 0, label %loop_exit1 ; CHECK-NEXT: ] ; ; CHECK: entry.split: ; CHECK-NEXT: switch i32 %cond2, label %loop_exit2 [ ; CHECK-NEXT: i32 42, label %loop_exit2 ; CHECK-NEXT: i32 0, label %entry.split.split ; CHECK-NEXT: ] ; ; CHECK: entry.split.split: ; CHECK-NEXT: br label %loop_begin loop_begin: switch i32 %cond1, label %continue [ i32 0, label %loop_exit1 ] ; CHECK: loop_begin: ; CHECK-NEXT: br label %continue continue: %var_val = load i32, i32* %var switch i32 %cond2, label %loop_exit2 [ i32 0, label %do_something i32 42, label %loop_exit2 ] ; CHECK: continue: ; CHECK-NEXT: load ; CHECK-NEXT: br label %do_something do_something: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: do_something: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit1: ret i32 0 ; CHECK: loop_exit1: ; CHECK-NEXT: ret loop_exit2: ret i32 0 ; CHECK: loop_exit2: ; CHECK-NEXT: ret ; ; We shouldn't have any unreachable blocks here because the unswitched switches ; turn into branches instead. ; CHECK-NOT: unreachable } ; Test for a trivially unswitchable switch with multiple exiting cases and ; multiple looping cases. define i32 @test4(i32* %var, i32 %cond1, i32 %cond2) { ; CHECK-LABEL: @test4( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: switch i32 %cond2, label %loop_exit2 [ ; CHECK-NEXT: i32 13, label %loop_exit1 ; CHECK-NEXT: i32 42, label %loop_exit3 ; CHECK-NEXT: i32 0, label %entry.split ; CHECK-NEXT: i32 1, label %entry.split ; CHECK-NEXT: i32 2, label %entry.split ; CHECK-NEXT: ] ; ; CHECK: entry.split: ; CHECK-NEXT: br label %loop_begin loop_begin: %var_val = load i32, i32* %var switch i32 %cond2, label %loop_exit2 [ i32 0, label %loop0 i32 1, label %loop1 i32 13, label %loop_exit1 i32 2, label %loop2 i32 42, label %loop_exit3 ] ; CHECK: loop_begin: ; CHECK-NEXT: load ; CHECK-NEXT: switch i32 %cond2, label %loop2 [ ; CHECK-NEXT: i32 0, label %loop0 ; CHECK-NEXT: i32 1, label %loop1 ; CHECK-NEXT: ] loop0: call void @some_func() noreturn nounwind br label %loop_latch ; CHECK: loop0: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_latch loop1: call void @some_func() noreturn nounwind br label %loop_latch ; CHECK: loop1: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_latch loop2: call void @some_func() noreturn nounwind br label %loop_latch ; CHECK: loop2: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_latch loop_latch: br label %loop_begin ; CHECK: loop_latch: ; CHECK-NEXT: br label %loop_begin loop_exit1: ret i32 0 ; CHECK: loop_exit1: ; CHECK-NEXT: ret loop_exit2: ret i32 0 ; CHECK: loop_exit2: ; CHECK-NEXT: ret loop_exit3: ret i32 0 ; CHECK: loop_exit3: ; CHECK-NEXT: ret } ; This test contains a trivially unswitchable branch with an LCSSA phi node in ; a loop exit block. define i32 @test5(i1 %cond1, i32 %x, i32 %y) { ; CHECK-LABEL: @test5( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 %{{.*}}, label %entry.split, label %loop_exit ; ; CHECK: entry.split: ; CHECK-NEXT: br label %loop_begin loop_begin: br i1 %cond1, label %latch, label %loop_exit ; CHECK: loop_begin: ; CHECK-NEXT: br label %latch latch: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: latch: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: %result1 = phi i32 [ %x, %loop_begin ] %result2 = phi i32 [ %y, %loop_begin ] %result = add i32 %result1, %result2 ret i32 %result ; CHECK: loop_exit: ; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %x, %entry ] ; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %y, %entry ] ; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1]], %[[R2]] ; CHECK-NEXT: ret i32 %[[R]] } ; This test contains a trivially unswitchable branch with a real phi node in LCSSA ; position in a shared exit block where a different path through the loop ; produces a non-invariant input to the PHI node. define i32 @test6(i32* %var, i1 %cond1, i1 %cond2, i32 %x, i32 %y) { ; CHECK-LABEL: @test6( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 %{{.*}}, label %entry.split, label %loop_exit.split ; ; CHECK: entry.split: ; CHECK-NEXT: br label %loop_begin loop_begin: br i1 %cond1, label %continue, label %loop_exit ; CHECK: loop_begin: ; CHECK-NEXT: br label %continue continue: %var_val = load i32, i32* %var br i1 %cond2, label %latch, label %loop_exit ; CHECK: continue: ; CHECK-NEXT: load ; CHECK-NEXT: br i1 %cond2, label %latch, label %loop_exit latch: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: latch: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: %result1 = phi i32 [ %x, %loop_begin ], [ %var_val, %continue ] %result2 = phi i32 [ %var_val, %continue ], [ %y, %loop_begin ] %result = add i32 %result1, %result2 ret i32 %result ; CHECK: loop_exit: ; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %var_val, %continue ] ; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %var_val, %continue ] ; CHECK-NEXT: br label %loop_exit.split ; ; CHECK: loop_exit.split: ; CHECK-NEXT: %[[R1S:.*]] = phi i32 [ %x, %entry ], [ %[[R1]], %loop_exit ] ; CHECK-NEXT: %[[R2S:.*]] = phi i32 [ %y, %entry ], [ %[[R2]], %loop_exit ] ; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1S]], %[[R2S]] ; CHECK-NEXT: ret i32 %[[R]] } ; This test contains a trivially unswitchable switch with an LCSSA phi node in ; a loop exit block. define i32 @test7(i32 %cond1, i32 %x, i32 %y) { ; CHECK-LABEL: @test7( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: switch i32 %cond1, label %entry.split [ ; CHECK-NEXT: i32 0, label %loop_exit ; CHECK-NEXT: i32 1, label %loop_exit ; CHECK-NEXT: ] ; ; CHECK: entry.split: ; CHECK-NEXT: br label %loop_begin loop_begin: switch i32 %cond1, label %latch [ i32 0, label %loop_exit i32 1, label %loop_exit ] ; CHECK: loop_begin: ; CHECK-NEXT: br label %latch latch: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: latch: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: %result1 = phi i32 [ %x, %loop_begin ], [ %x, %loop_begin ] %result2 = phi i32 [ %y, %loop_begin ], [ %y, %loop_begin ] %result = add i32 %result1, %result2 ret i32 %result ; CHECK: loop_exit: ; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %x, %entry ], [ %x, %entry ] ; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %y, %entry ], [ %y, %entry ] ; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1]], %[[R2]] ; CHECK-NEXT: ret i32 %[[R]] } ; This test contains a trivially unswitchable switch with a real phi node in ; LCSSA position in a shared exit block where a different path through the loop ; produces a non-invariant input to the PHI node. define i32 @test8(i32* %var, i32 %cond1, i32 %cond2, i32 %x, i32 %y) { ; CHECK-LABEL: @test8( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: switch i32 %cond1, label %entry.split [ ; CHECK-NEXT: i32 0, label %loop_exit.split ; CHECK-NEXT: i32 1, label %loop_exit2 ; CHECK-NEXT: i32 2, label %loop_exit.split ; CHECK-NEXT: ] ; ; CHECK: entry.split: ; CHECK-NEXT: br label %loop_begin loop_begin: switch i32 %cond1, label %continue [ i32 0, label %loop_exit i32 1, label %loop_exit2 i32 2, label %loop_exit ] ; CHECK: loop_begin: ; CHECK-NEXT: br label %continue continue: %var_val = load i32, i32* %var switch i32 %cond2, label %latch [ i32 0, label %loop_exit ] ; CHECK: continue: ; CHECK-NEXT: load ; CHECK-NEXT: switch i32 %cond2, label %latch [ ; CHECK-NEXT: i32 0, label %loop_exit ; CHECK-NEXT: ] latch: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: latch: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: %result1.1 = phi i32 [ %x, %loop_begin ], [ %x, %loop_begin ], [ %var_val, %continue ] %result1.2 = phi i32 [ %var_val, %continue ], [ %y, %loop_begin ], [ %y, %loop_begin ] %result1 = add i32 %result1.1, %result1.2 ret i32 %result1 ; CHECK: loop_exit: ; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %var_val, %continue ] ; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %var_val, %continue ] ; CHECK-NEXT: br label %loop_exit.split ; ; CHECK: loop_exit.split: ; CHECK-NEXT: %[[R1S:.*]] = phi i32 [ %x, %entry ], [ %x, %entry ], [ %[[R1]], %loop_exit ] ; CHECK-NEXT: %[[R2S:.*]] = phi i32 [ %y, %entry ], [ %y, %entry ], [ %[[R2]], %loop_exit ] ; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1S]], %[[R2S]] ; CHECK-NEXT: ret i32 %[[R]] loop_exit2: %result2.1 = phi i32 [ %x, %loop_begin ] %result2.2 = phi i32 [ %y, %loop_begin ] %result2 = add i32 %result2.1, %result2.2 ret i32 %result2 ; CHECK: loop_exit2: ; CHECK-NEXT: %[[R1:.*]] = phi i32 [ %x, %entry ] ; CHECK-NEXT: %[[R2:.*]] = phi i32 [ %y, %entry ] ; CHECK-NEXT: %[[R:.*]] = add i32 %[[R1]], %[[R2]] ; CHECK-NEXT: ret i32 %[[R]] } ; This test, extracted from the LLVM test suite, has an interesting dominator ; tree to update as there are edges to sibling domtree nodes within child ; domtree nodes of the unswitched node. define void @xgets(i1 %cond1, i1* %cond2.ptr) { ; CHECK-LABEL: @xgets( entry: br label %for.cond.preheader ; CHECK: entry: ; CHECK-NEXT: br label %for.cond.preheader for.cond.preheader: br label %for.cond ; CHECK: for.cond.preheader: ; CHECK-NEXT: br i1 %cond1, label %for.cond.preheader.split, label %if.end17.thread.loopexit ; ; CHECK: for.cond.preheader.split: ; CHECK-NEXT: br label %for.cond for.cond: br i1 %cond1, label %land.lhs.true, label %if.end17.thread.loopexit ; CHECK: for.cond: ; CHECK-NEXT: br label %land.lhs.true land.lhs.true: br label %if.then20 ; CHECK: land.lhs.true: ; CHECK-NEXT: br label %if.then20 if.then20: %cond2 = load volatile i1, i1* %cond2.ptr br i1 %cond2, label %if.then23, label %if.else ; CHECK: if.then20: ; CHECK-NEXT: %[[COND2:.*]] = load volatile i1, i1* %cond2.ptr ; CHECK-NEXT: br i1 %[[COND2]], label %if.then23, label %if.else if.else: br label %for.cond ; CHECK: if.else: ; CHECK-NEXT: br label %for.cond if.end17.thread.loopexit: br label %if.end17.thread ; CHECK: if.end17.thread.loopexit: ; CHECK-NEXT: br label %if.end17.thread if.end17.thread: br label %cleanup ; CHECK: if.end17.thread: ; CHECK-NEXT: br label %cleanup if.then23: br label %cleanup ; CHECK: if.then23: ; CHECK-NEXT: br label %cleanup cleanup: ret void ; CHECK: cleanup: ; CHECK-NEXT: ret void } define i32 @test_partial_condition_unswitch_and(i32* %var, i1 %cond1, i1 %cond2) { ; CHECK-LABEL: @test_partial_condition_unswitch_and( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 %cond1, label %entry.split, label %loop_exit.split ; ; CHECK: entry.split: ; CHECK-NEXT: [[FROZEN:%.+]] = freeze i1 %cond2 ; CHECK-NEXT: br i1 [[FROZEN]], label %entry.split.split, label %loop_exit ; ; CHECK: entry.split.split: ; CHECK-NEXT: br label %loop_begin loop_begin: br i1 %cond1, label %continue, label %loop_exit ; CHECK: loop_begin: ; CHECK-NEXT: br label %continue continue: %var_val = load i32, i32* %var %var_cond = trunc i32 %var_val to i1 %cond_and = and i1 %var_cond, %cond2 br i1 %cond_and, label %do_something, label %loop_exit ; CHECK: continue: ; CHECK-NEXT: %[[VAR:.*]] = load i32 ; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1 ; CHECK-NEXT: %[[COND_AND:.*]] = and i1 %[[VAR_COND]], true ; CHECK-NEXT: br i1 %[[COND_AND]], label %do_something, label %loop_exit do_something: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: do_something: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: ret i32 0 ; CHECK: loop_exit: ; CHECK-NEXT: br label %loop_exit.split ; ; CHECK: loop_exit.split: ; CHECK-NEXT: ret } define i32 @test_partial_condition_unswitch_and_select(i32* %var, i1 %cond1, i1 %cond2) { ; CHECK-LABEL: @test_partial_condition_unswitch_and_select( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 %cond1, label %entry.split, label %loop_exit.split ; ; CHECK: entry.split: ; CHECK-NEXT: [[FROZEN:%.+]] = freeze i1 %cond2 ; CHECK-NEXT: br i1 [[FROZEN]], label %entry.split.split, label %loop_exit ; ; CHECK: entry.split.split: ; CHECK-NEXT: br label %loop_begin loop_begin: br i1 %cond1, label %continue, label %loop_exit ; CHECK: loop_begin: ; CHECK-NEXT: br label %continue continue: %var_val = load i32, i32* %var %var_cond = trunc i32 %var_val to i1 %cond_and = select i1 %var_cond, i1 %cond2, i1 false br i1 %cond_and, label %do_something, label %loop_exit ; CHECK: continue: ; CHECK-NEXT: %[[VAR:.*]] = load i32 ; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1 ; CHECK-NEXT: %[[COND_AND:.*]] = select i1 %[[VAR_COND]], i1 true, i1 false ; CHECK-NEXT: br i1 %[[COND_AND]], label %do_something, label %loop_exit do_something: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: do_something: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: ret i32 0 ; CHECK: loop_exit: ; CHECK-NEXT: br label %loop_exit.split ; ; CHECK: loop_exit.split: ; CHECK-NEXT: ret } define i32 @test_partial_condition_unswitch_or_simple_select(i32* %var, i1 %cond1, i1 %cond2) { ; CHECK-LABEL: @test_partial_condition_unswitch_or_simple_select( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 %cond1, label %entry.split, label %loop_exit.split ; ; CHECK: entry.split: ; CHECK-NEXT: [[FROZEN:%.+]] = freeze i1 %cond2 ; CHECK-NEXT: br i1 [[FROZEN]], label %loop_exit.split1, label %entry.split.split ; ; CHECK: entry.split.split: ; CHECK-NEXT: br label %loop_begin loop_begin: br i1 %cond1, label %continue, label %loop_exit ; CHECK: loop_begin: ; CHECK-NEXT: br label %continue continue: %var_val = load i32, i32* %var %var_cond = trunc i32 %var_val to i1 %cond_or = select i1 %var_cond, i1 true, i1 %cond2 br i1 %cond_or, label %loop_exit, label %do_something ; CHECK: continue: ; CHECK-NEXT: %[[VAR:.*]] = load i32 ; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1 ; CHECK-NEXT: %[[COND_OR:.*]] = select i1 %[[VAR_COND]], i1 true, i1 false ; CHECK-NEXT: br i1 %[[COND_OR]], label %loop_exit, label %do_something do_something: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: do_something: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: ret i32 0 ; CHECK: loop_exit: ; CHECK-NEXT: br label %loop_exit.split1 ; ; CHECK: loop_exit.split1: ; CHECK-NEXT: br label %loop_exit.split ; ; CHECK: loop_exit.split: ; CHECK-NEXT: ret } define i32 @test_partial_condition_unswitch_or(i32* %var, i1 %cond1, i1 %cond2, i1 %cond3, i1 %cond4, i1 %cond5, i1 %cond6) { ; CHECK-LABEL: @test_partial_condition_unswitch_or( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: %[[C4_FR:.+]] = freeze i1 %cond4 ; CHECK-NEXT: %[[C2_FR:.+]] = freeze i1 %cond2 ; CHECK-NEXT: %[[C3_FR:.+]] = freeze i1 %cond3 ; CHECK-NEXT: %[[C1_FR:.+]] = freeze i1 %cond1 ; CHECK-NEXT: %[[INV_OR1:.*]] = or i1 %[[C4_FR]], %[[C2_FR]] ; CHECK-NEXT: %[[INV_OR2:.*]] = or i1 %[[INV_OR1]], %[[C3_FR]] ; CHECK-NEXT: %[[INV_OR3:.*]] = or i1 %[[INV_OR2]], %[[C1_FR]] ; CHECK-NEXT: br i1 %[[INV_OR3]], label %loop_exit.split, label %entry.split ; ; CHECK: entry.split: ; CHECK-NEXT: br label %loop_begin loop_begin: %var_val = load i32, i32* %var %var_cond = trunc i32 %var_val to i1 %cond_or1 = or i1 %var_cond, %cond1 %cond_or2 = or i1 %cond2, %cond3 %cond_or3 = or i1 %cond_or1, %cond_or2 %cond_xor1 = xor i1 %cond5, %var_cond %cond_and1 = and i1 %cond6, %var_cond %cond_or4 = or i1 %cond_xor1, %cond_and1 %cond_or5 = or i1 %cond_or3, %cond_or4 %cond_or6 = or i1 %cond_or5, %cond4 br i1 %cond_or6, label %loop_exit, label %do_something ; CHECK: loop_begin: ; CHECK-NEXT: %[[VAR:.*]] = load i32 ; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1 ; CHECK-NEXT: %[[COND_OR1:.*]] = or i1 %[[VAR_COND]], false ; CHECK-NEXT: %[[COND_OR2:.*]] = or i1 false, false ; CHECK-NEXT: %[[COND_OR3:.*]] = or i1 %[[COND_OR1]], %[[COND_OR2]] ; CHECK-NEXT: %[[COND_XOR:.*]] = xor i1 %cond5, %[[VAR_COND]] ; CHECK-NEXT: %[[COND_AND:.*]] = and i1 %cond6, %[[VAR_COND]] ; CHECK-NEXT: %[[COND_OR4:.*]] = or i1 %[[COND_XOR]], %[[COND_AND]] ; CHECK-NEXT: %[[COND_OR5:.*]] = or i1 %[[COND_OR3]], %[[COND_OR4]] ; CHECK-NEXT: %[[COND_OR6:.*]] = or i1 %[[COND_OR5]], false ; CHECK-NEXT: br i1 %[[COND_OR6]], label %loop_exit, label %do_something do_something: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: do_something: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: ret i32 0 ; CHECK: loop_exit.split: ; CHECK-NEXT: ret } define i32 @test_partial_condition_unswitch_with_lcssa_phi1(i32* %var, i1 %cond, i32 %x) { ; CHECK-LABEL: @test_partial_condition_unswitch_with_lcssa_phi1( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: [[FROZEN:%.+]] = freeze i1 %cond ; CHECK-NEXT: br i1 [[FROZEN]], label %entry.split, label %loop_exit.split ; ; CHECK: entry.split: ; CHECK-NEXT: br label %loop_begin loop_begin: %var_val = load i32, i32* %var %var_cond = trunc i32 %var_val to i1 %cond_and = and i1 %var_cond, %cond br i1 %cond_and, label %do_something, label %loop_exit ; CHECK: loop_begin: ; CHECK-NEXT: %[[VAR:.*]] = load i32 ; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1 ; CHECK-NEXT: %[[COND_AND:.*]] = and i1 %[[VAR_COND]], true ; CHECK-NEXT: br i1 %[[COND_AND]], label %do_something, label %loop_exit do_something: call void @some_func() noreturn nounwind br label %loop_begin ; CHECK: do_something: ; CHECK-NEXT: call ; CHECK-NEXT: br label %loop_begin loop_exit: %x.lcssa = phi i32 [ %x, %loop_begin ] ret i32 %x.lcssa ; CHECK: loop_exit: ; CHECK-NEXT: %[[LCSSA:.*]] = phi i32 [ %x, %loop_begin ] ; CHECK-NEXT: br label %loop_exit.split ; ; CHECK: loop_exit.split: ; CHECK-NEXT: %[[LCSSA_SPLIT:.*]] = phi i32 [ %x, %entry ], [ %[[LCSSA]], %loop_exit ] ; CHECK-NEXT: ret i32 %[[LCSSA_SPLIT]] } define i32 @test_partial_condition_unswitch_with_lcssa_phi2(i32* %var, i1 %cond, i32 %x, i32 %y) { ; CHECK-LABEL: @test_partial_condition_unswitch_with_lcssa_phi2( entry: br label %loop_begin ; CHECK-NEXT: entry: ; CHECK-NEXT: [[FROZEN:%.+]] = freeze i1 %cond ; CHECK-NEXT: br i1 [[FROZEN]], label %entry.split, label %loop_exit.split ; ; CHECK: entry.split: ; CHECK-NEXT: br label %loop_begin loop_begin: %var_val = load i32, i32* %var %var_cond = trunc i32 %var_val to i1 %cond_and = and i1 %var_cond, %cond br i1 %cond_and, label %do_something, label %loop_exit ; CHECK: loop_begin: ; CHECK-NEXT: %[[VAR:.*]] = load i32 ; CHECK-NEXT: %[[VAR_COND:.*]] = trunc i32 %[[VAR]] to i1 ; CHECK-NEXT: %[[COND_AND:.*]] = and i1 %[[VAR_COND]], true ; CHECK-NEXT: br i1 %[[COND_AND]], label %do_something, label %loop_exit do_something: call void @some_func() noreturn nounwind br i1 %var_cond, label %loop_begin, label %loop_exit ; CHECK: do_something: ; CHECK-NEXT: call ; CHECK-NEXT: br i1 %[[VAR_COND]], label %loop_begin, label %loop_exit loop_exit: %xy.lcssa = phi i32 [ %x, %loop_begin ], [ %y, %do_something ] ret i32 %xy.lcssa ; CHECK: loop_exit: ; CHECK-NEXT: %[[LCSSA:.*]] = phi i32 [ %x, %loop_begin ], [ %y, %do_something ] ; CHECK-NEXT: br label %loop_exit.split ; ; CHECK: loop_exit.split: ; CHECK-NEXT: %[[LCSSA_SPLIT:.*]] = phi i32 [ %x, %entry ], [ %[[LCSSA]], %loop_exit ] ; CHECK-NEXT: ret i32 %[[LCSSA_SPLIT]] } ; Unswitch will not actually change the loop nest from: ; A < B < C define void @hoist_inner_loop0() { ; CHECK-LABEL: define void @hoist_inner_loop0( entry: br label %a.header ; CHECK: entry: ; CHECK-NEXT: br label %a.header a.header: br label %b.header ; CHECK: a.header: ; CHECK-NEXT: br label %b.header b.header: %v1 = call i1 @cond() br label %c.header ; CHECK: b.header: ; CHECK-NEXT: %v1 = call i1 @cond() ; CHECK-NEXT: br i1 %v1, label %[[B_LATCH_SPLIT:.*]], label %[[B_HEADER_SPLIT:.*]] ; ; CHECK: [[B_HEADER_SPLIT]]: ; CHECK-NEXT: br label %c.header c.header: br i1 %v1, label %b.latch, label %c.latch ; CHECK: c.header: ; CHECK-NEXT: br label %c.latch c.latch: %v2 = call i1 @cond() br i1 %v2, label %c.header, label %b.latch ; CHECK: c.latch: ; CHECK-NEXT: %v2 = call i1 @cond() ; CHECK-NEXT: br i1 %v2, label %c.header, label %b.latch b.latch: %v3 = call i1 @cond() br i1 %v3, label %b.header, label %a.latch ; CHECK: b.latch: ; CHECK-NEXT: br label %[[B_LATCH_SPLIT]] ; ; CHECK: [[B_LATCH_SPLIT]]: ; CHECK-NEXT: %v3 = call i1 @cond() ; CHECK-NEXT: br i1 %v3, label %b.header, label %a.latch a.latch: br label %a.header ; CHECK: a.latch: ; CHECK-NEXT: br label %a.header exit: ret void ; CHECK: exit: ; CHECK-NEXT: ret void } ; Unswitch will transform the loop nest from: ; A < B < C ; into ; A < (B, C) define void @hoist_inner_loop1(i32* %ptr) { ; CHECK-LABEL: define void @hoist_inner_loop1( entry: br label %a.header ; CHECK: entry: ; CHECK-NEXT: br label %a.header a.header: %x.a = load i32, i32* %ptr br label %b.header ; CHECK: a.header: ; CHECK-NEXT: %x.a = load i32, i32* %ptr ; CHECK-NEXT: br label %b.header b.header: %x.b = load i32, i32* %ptr %v1 = call i1 @cond() br label %c.header ; CHECK: b.header: ; CHECK-NEXT: %x.b = load i32, i32* %ptr ; CHECK-NEXT: %v1 = call i1 @cond() ; CHECK-NEXT: br i1 %v1, label %b.latch, label %[[B_HEADER_SPLIT:.*]] ; ; CHECK: [[B_HEADER_SPLIT]]: ; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %b.header ] ; CHECK-NEXT: br label %c.header c.header: br i1 %v1, label %b.latch, label %c.latch ; CHECK: c.header: ; CHECK-NEXT: br label %c.latch c.latch: ; Use values from other loops to check LCSSA form. store i32 %x.a, i32* %ptr store i32 %x.b, i32* %ptr %v2 = call i1 @cond() br i1 %v2, label %c.header, label %a.exit.c ; CHECK: c.latch: ; CHECK-NEXT: store i32 %x.a, i32* %ptr ; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr ; CHECK-NEXT: %v2 = call i1 @cond() ; CHECK-NEXT: br i1 %v2, label %c.header, label %a.exit.c b.latch: %v3 = call i1 @cond() br i1 %v3, label %b.header, label %a.exit.b ; CHECK: b.latch: ; CHECK-NEXT: %v3 = call i1 @cond() ; CHECK-NEXT: br i1 %v3, label %b.header, label %a.exit.b a.exit.c: br label %a.latch ; CHECK: a.exit.c ; CHECK-NEXT: br label %a.latch a.exit.b: br label %a.latch ; CHECK: a.exit.b: ; CHECK-NEXT: br label %a.latch a.latch: br label %a.header ; CHECK: a.latch: ; CHECK-NEXT: br label %a.header exit: ret void ; CHECK: exit: ; CHECK-NEXT: ret void } ; Unswitch will transform the loop nest from: ; A < B < C ; into ; (A < B), C define void @hoist_inner_loop2(i32* %ptr) { ; CHECK-LABEL: define void @hoist_inner_loop2( entry: br label %a.header ; CHECK: entry: ; CHECK-NEXT: br label %a.header a.header: %x.a = load i32, i32* %ptr br label %b.header ; CHECK: a.header: ; CHECK-NEXT: %x.a = load i32, i32* %ptr ; CHECK-NEXT: br label %b.header b.header: %x.b = load i32, i32* %ptr %v1 = call i1 @cond() br label %c.header ; CHECK: b.header: ; CHECK-NEXT: %x.b = load i32, i32* %ptr ; CHECK-NEXT: %v1 = call i1 @cond() ; CHECK-NEXT: br i1 %v1, label %b.latch, label %[[B_HEADER_SPLIT:.*]] ; ; CHECK: [[B_HEADER_SPLIT]]: ; CHECK-NEXT: %[[X_A_LCSSA:.*]] = phi i32 [ %x.a, %b.header ] ; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %b.header ] ; CHECK-NEXT: br label %c.header c.header: br i1 %v1, label %b.latch, label %c.latch ; CHECK: c.header: ; CHECK-NEXT: br label %c.latch c.latch: ; Use values from other loops to check LCSSA form. store i32 %x.a, i32* %ptr store i32 %x.b, i32* %ptr %v2 = call i1 @cond() br i1 %v2, label %c.header, label %exit ; CHECK: c.latch: ; CHECK-NEXT: store i32 %[[X_A_LCSSA]], i32* %ptr ; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr ; CHECK-NEXT: %v2 = call i1 @cond() ; CHECK-NEXT: br i1 %v2, label %c.header, label %exit b.latch: %v3 = call i1 @cond() br i1 %v3, label %b.header, label %a.latch ; CHECK: b.latch: ; CHECK-NEXT: %v3 = call i1 @cond() ; CHECK-NEXT: br i1 %v3, label %b.header, label %a.latch a.latch: br label %a.header ; CHECK: a.latch: ; CHECK-NEXT: br label %a.header exit: ret void ; CHECK: exit: ; CHECK-NEXT: ret void } ; Same as @hoist_inner_loop2 but with a nested loop inside the hoisted loop. ; Unswitch will transform the loop nest from: ; A < B < C < D ; into ; (A < B), (C < D) define void @hoist_inner_loop3(i32* %ptr) { ; CHECK-LABEL: define void @hoist_inner_loop3( entry: br label %a.header ; CHECK: entry: ; CHECK-NEXT: br label %a.header a.header: %x.a = load i32, i32* %ptr br label %b.header ; CHECK: a.header: ; CHECK-NEXT: %x.a = load i32, i32* %ptr ; CHECK-NEXT: br label %b.header b.header: %x.b = load i32, i32* %ptr %v1 = call i1 @cond() br label %c.header ; CHECK: b.header: ; CHECK-NEXT: %x.b = load i32, i32* %ptr ; CHECK-NEXT: %v1 = call i1 @cond() ; CHECK-NEXT: br i1 %v1, label %b.latch, label %[[B_HEADER_SPLIT:.*]] ; ; CHECK: [[B_HEADER_SPLIT]]: ; CHECK-NEXT: %[[X_A_LCSSA:.*]] = phi i32 [ %x.a, %b.header ] ; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %b.header ] ; CHECK-NEXT: br label %c.header c.header: br i1 %v1, label %b.latch, label %c.body ; CHECK: c.header: ; CHECK-NEXT: br label %c.body c.body: %x.c = load i32, i32* %ptr br label %d.header ; CHECK: c.body: ; CHECK-NEXT: %x.c = load i32, i32* %ptr ; CHECK-NEXT: br label %d.header d.header: ; Use values from other loops to check LCSSA form. store i32 %x.a, i32* %ptr store i32 %x.b, i32* %ptr store i32 %x.c, i32* %ptr %v2 = call i1 @cond() br i1 %v2, label %d.header, label %c.latch ; CHECK: d.header: ; CHECK-NEXT: store i32 %[[X_A_LCSSA]], i32* %ptr ; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr ; CHECK-NEXT: store i32 %x.c, i32* %ptr ; CHECK-NEXT: %v2 = call i1 @cond() ; CHECK-NEXT: br i1 %v2, label %d.header, label %c.latch c.latch: %v3 = call i1 @cond() br i1 %v3, label %c.header, label %exit ; CHECK: c.latch: ; CHECK-NEXT: %v3 = call i1 @cond() ; CHECK-NEXT: br i1 %v3, label %c.header, label %exit b.latch: %v4 = call i1 @cond() br i1 %v4, label %b.header, label %a.latch ; CHECK: b.latch: ; CHECK-NEXT: %v4 = call i1 @cond() ; CHECK-NEXT: br i1 %v4, label %b.header, label %a.latch a.latch: br label %a.header ; CHECK: a.latch: ; CHECK-NEXT: br label %a.header exit: ret void ; CHECK: exit: ; CHECK-NEXT: ret void } ; This test is designed to exercise checking multiple remaining exits from the ; loop being unswitched. ; Unswitch will transform the loop nest from: ; A < B < C < D ; into ; A < B < (C, D) define void @hoist_inner_loop4() { ; CHECK-LABEL: define void @hoist_inner_loop4( entry: br label %a.header ; CHECK: entry: ; CHECK-NEXT: br label %a.header a.header: br label %b.header ; CHECK: a.header: ; CHECK-NEXT: br label %b.header b.header: br label %c.header ; CHECK: b.header: ; CHECK-NEXT: br label %c.header c.header: %v1 = call i1 @cond() br label %d.header ; CHECK: c.header: ; CHECK-NEXT: %v1 = call i1 @cond() ; CHECK-NEXT: br i1 %v1, label %[[C_HEADER_SPLIT:.*]], label %c.latch ; ; CHECK: [[C_HEADER_SPLIT]]: ; CHECK-NEXT: br label %d.header d.header: br i1 %v1, label %d.exiting1, label %c.latch ; CHECK: d.header: ; CHECK-NEXT: br label %d.exiting1 d.exiting1: %v2 = call i1 @cond() br i1 %v2, label %d.exiting2, label %a.latch ; CHECK: d.exiting1: ; CHECK-NEXT: %v2 = call i1 @cond() ; CHECK-NEXT: br i1 %v2, label %d.exiting2, label %a.latch d.exiting2: %v3 = call i1 @cond() br i1 %v3, label %d.exiting3, label %loopexit.d ; CHECK: d.exiting2: ; CHECK-NEXT: %v3 = call i1 @cond() ; CHECK-NEXT: br i1 %v3, label %d.exiting3, label %loopexit.d d.exiting3: %v4 = call i1 @cond() br i1 %v4, label %d.latch, label %b.latch ; CHECK: d.exiting3: ; CHECK-NEXT: %v4 = call i1 @cond() ; CHECK-NEXT: br i1 %v4, label %d.latch, label %b.latch d.latch: br label %d.header ; CHECK: d.latch: ; CHECK-NEXT: br label %d.header c.latch: %v5 = call i1 @cond() br i1 %v5, label %c.header, label %loopexit.c ; CHECK: c.latch: ; CHECK-NEXT: %v5 = call i1 @cond() ; CHECK-NEXT: br i1 %v5, label %c.header, label %loopexit.c b.latch: br label %b.header ; CHECK: b.latch: ; CHECK-NEXT: br label %b.header a.latch: br label %a.header ; CHECK: a.latch: ; CHECK-NEXT: br label %a.header loopexit.d: br label %exit ; CHECK: loopexit.d: ; CHECK-NEXT: br label %exit loopexit.c: br label %exit ; CHECK: loopexit.c: ; CHECK-NEXT: br label %exit exit: ret void ; CHECK: exit: ; CHECK-NEXT: ret void } ; Unswitch will transform the loop nest from: ; A < B < C < D ; into ; A < ((B < C), D) define void @hoist_inner_loop5(i32* %ptr) { ; CHECK-LABEL: define void @hoist_inner_loop5( entry: br label %a.header ; CHECK: entry: ; CHECK-NEXT: br label %a.header a.header: %x.a = load i32, i32* %ptr br label %b.header ; CHECK: a.header: ; CHECK-NEXT: %x.a = load i32, i32* %ptr ; CHECK-NEXT: br label %b.header b.header: %x.b = load i32, i32* %ptr br label %c.header ; CHECK: b.header: ; CHECK-NEXT: %x.b = load i32, i32* %ptr ; CHECK-NEXT: br label %c.header c.header: %x.c = load i32, i32* %ptr %v1 = call i1 @cond() br label %d.header ; CHECK: c.header: ; CHECK-NEXT: %x.c = load i32, i32* %ptr ; CHECK-NEXT: %v1 = call i1 @cond() ; CHECK-NEXT: br i1 %v1, label %c.latch, label %[[C_HEADER_SPLIT:.*]] ; ; CHECK: [[C_HEADER_SPLIT]]: ; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %c.header ] ; CHECK-NEXT: %[[X_C_LCSSA:.*]] = phi i32 [ %x.c, %c.header ] ; CHECK-NEXT: br label %d.header d.header: br i1 %v1, label %c.latch, label %d.latch ; CHECK: d.header: ; CHECK-NEXT: br label %d.latch d.latch: ; Use values from other loops to check LCSSA form. store i32 %x.a, i32* %ptr store i32 %x.b, i32* %ptr store i32 %x.c, i32* %ptr %v2 = call i1 @cond() br i1 %v2, label %d.header, label %a.latch ; CHECK: d.latch: ; CHECK-NEXT: store i32 %x.a, i32* %ptr ; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr ; CHECK-NEXT: store i32 %[[X_C_LCSSA]], i32* %ptr ; CHECK-NEXT: %v2 = call i1 @cond() ; CHECK-NEXT: br i1 %v2, label %d.header, label %a.latch c.latch: %v3 = call i1 @cond() br i1 %v3, label %c.header, label %b.latch ; CHECK: c.latch: ; CHECK-NEXT: %v3 = call i1 @cond() ; CHECK-NEXT: br i1 %v3, label %c.header, label %b.latch b.latch: br label %b.header ; CHECK: b.latch: ; CHECK-NEXT: br label %b.header a.latch: br label %a.header ; CHECK: a.latch: ; CHECK-NEXT: br label %a.header exit: ret void ; CHECK: exit: ; CHECK-NEXT: ret void } ; Same as `@hoist_inner_loop2` but using a switch. ; Unswitch will transform the loop nest from: ; A < B < C ; into ; (A < B), C define void @hoist_inner_loop_switch(i32* %ptr) { ; CHECK-LABEL: define void @hoist_inner_loop_switch( entry: br label %a.header ; CHECK: entry: ; CHECK-NEXT: br label %a.header a.header: %x.a = load i32, i32* %ptr br label %b.header ; CHECK: a.header: ; CHECK-NEXT: %x.a = load i32, i32* %ptr ; CHECK-NEXT: br label %b.header b.header: %x.b = load i32, i32* %ptr %v1 = call i32 @cond.i32() br label %c.header ; CHECK: b.header: ; CHECK-NEXT: %x.b = load i32, i32* %ptr ; CHECK-NEXT: %v1 = call i32 @cond.i32() ; CHECK-NEXT: switch i32 %v1, label %[[B_HEADER_SPLIT:.*]] [ ; CHECK-NEXT: i32 1, label %b.latch ; CHECK-NEXT: i32 2, label %b.latch ; CHECK-NEXT: i32 3, label %b.latch ; CHECK-NEXT: ] ; ; CHECK: [[B_HEADER_SPLIT]]: ; CHECK-NEXT: %[[X_A_LCSSA:.*]] = phi i32 [ %x.a, %b.header ] ; CHECK-NEXT: %[[X_B_LCSSA:.*]] = phi i32 [ %x.b, %b.header ] ; CHECK-NEXT: br label %c.header c.header: switch i32 %v1, label %c.latch [ i32 1, label %b.latch i32 2, label %b.latch i32 3, label %b.latch ] ; CHECK: c.header: ; CHECK-NEXT: br label %c.latch c.latch: ; Use values from other loops to check LCSSA form. store i32 %x.a, i32* %ptr store i32 %x.b, i32* %ptr %v2 = call i1 @cond() br i1 %v2, label %c.header, label %exit ; CHECK: c.latch: ; CHECK-NEXT: store i32 %[[X_A_LCSSA]], i32* %ptr ; CHECK-NEXT: store i32 %[[X_B_LCSSA]], i32* %ptr ; CHECK-NEXT: %v2 = call i1 @cond() ; CHECK-NEXT: br i1 %v2, label %c.header, label %exit b.latch: %v3 = call i1 @cond() br i1 %v3, label %b.header, label %a.latch ; CHECK: b.latch: ; CHECK-NEXT: %v3 = call i1 @cond() ; CHECK-NEXT: br i1 %v3, label %b.header, label %a.latch a.latch: br label %a.header ; CHECK: a.latch: ; CHECK-NEXT: br label %a.header exit: ret void ; CHECK: exit: ; CHECK-NEXT: ret void } define void @test_unswitch_to_common_succ_with_phis(i32* %var, i32 %cond) { ; CHECK-LABEL: @test_unswitch_to_common_succ_with_phis( entry: br label %header ; CHECK-NEXT: entry: ; CHECK-NEXT: switch i32 %cond, label %loopexit1 [ ; CHECK-NEXT: i32 13, label %loopexit2 ; CHECK-NEXT: i32 0, label %entry.split ; CHECK-NEXT: i32 1, label %entry.split ; CHECK-NEXT: ] ; ; CHECK: entry.split: ; CHECK-NEXT: br label %header header: %var_val = load i32, i32* %var switch i32 %cond, label %loopexit1 [ i32 0, label %latch i32 1, label %latch i32 13, label %loopexit2 ] ; CHECK: header: ; CHECK-NEXT: load ; CHECK-NEXT: br label %latch latch: ; No-op PHI node to exercise weird PHI update scenarios. %phi = phi i32 [ %var_val, %header ], [ %var_val, %header ] call void @sink(i32 %phi) br label %header ; CHECK: latch: ; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %var_val, %header ] ; CHECK-NEXT: call void @sink(i32 %[[PHI]]) ; CHECK-NEXT: br label %header loopexit1: ret void ; CHECK: loopexit1: ; CHECK-NEXT: ret loopexit2: ret void ; CHECK: loopexit2: ; CHECK-NEXT: ret } define void @test_unswitch_to_default_common_succ_with_phis(i32* %var, i32 %cond) { ; CHECK-LABEL: @test_unswitch_to_default_common_succ_with_phis( entry: br label %header ; CHECK-NEXT: entry: ; CHECK-NEXT: switch i32 %cond, label %entry.split [ ; CHECK-NEXT: i32 13, label %loopexit ; CHECK-NEXT: ] ; ; CHECK: entry.split: ; CHECK-NEXT: br label %header header: %var_val = load i32, i32* %var switch i32 %cond, label %latch [ i32 0, label %latch i32 1, label %latch i32 13, label %loopexit ] ; CHECK: header: ; CHECK-NEXT: load ; CHECK-NEXT: br label %latch latch: ; No-op PHI node to exercise weird PHI update scenarios. %phi = phi i32 [ %var_val, %header ], [ %var_val, %header ], [ %var_val, %header ] call void @sink(i32 %phi) br label %header ; CHECK: latch: ; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %var_val, %header ] ; CHECK-NEXT: call void @sink(i32 %[[PHI]]) ; CHECK-NEXT: br label %header loopexit: ret void ; CHECK: loopexit: ; CHECK-NEXT: ret } declare void @f() declare void @g() define void @test_unswitch_switch_with_nonempty_unreachable() { ; CHECK-LABEL: @test_unswitch_switch_with_nonempty_unreachable() entry: br label %loop loop: %cleanup.dest.slot.0 = select i1 undef, i32 5, i32 undef br label %for.cond for.cond: switch i32 %cleanup.dest.slot.0, label %NonEmptyUnreachableBlock [ i32 0, label %for.cond i32 1, label %NonEmptyUnreachableBlock i32 2, label %loop.loopexit ] loop.loopexit: unreachable NonEmptyUnreachableBlock: call void @f() call void @g() unreachable ; CHECK:loop: ; CHECK-NEXT: %cleanup.dest.slot.0 = select i1 undef, i32 5, i32 undef ; CHECK-NEXT: switch i32 %cleanup.dest.slot.0, label %NonEmptyUnreachableBlock [ ; CHECK-NEXT: i32 1, label %NonEmptyUnreachableBlock ; CHECK-NEXT: i32 0, label %loop.split ; CHECK-NEXT: i32 2, label %loop.split ; CHECK-NEXT: ] ; CHECK:loop.split: ; CHECK-NEXT: br label %for.cond ; CHECK:for.cond: ; CHECK-NEXT: switch i32 %cleanup.dest.slot.0, label %loop.loopexit [ ; CHECK-NEXT: i32 0, label %for.cond ; CHECK-NEXT: ] ; CHECK:loop.loopexit: ; CHECK-NEXT: unreachable ; CHECK:NonEmptyUnreachableBlock: ; CHECK-NEXT: call void @f() ; CHECK-NEXT: call void @g() ; CHECK-NEXT: unreachable } define void @test_unswitch_switch_with_nonempty_unreachable2() { ; CHECK-LABEL: @test_unswitch_switch_with_nonempty_unreachable2() entry: br label %loop loop: %cleanup.dest.slot.0 = select i1 undef, i32 5, i32 undef br label %for.cond for.cond: switch i32 %cleanup.dest.slot.0, label %for.cond [ i32 0, label %for.cond i32 1, label %NonEmptyUnreachableBlock i32 2, label %loop.loopexit ] loop.loopexit: unreachable NonEmptyUnreachableBlock: call void @f() call void @g() unreachable ; CHECK:loop: ; CHECK-NEXT: %cleanup.dest.slot.0 = select i1 undef, i32 5, i32 undef ; CHECK-NEXT: switch i32 %cleanup.dest.slot.0, label %loop.split [ ; CHECK-NEXT: i32 1, label %NonEmptyUnreachableBlock ; CHECK-NEXT: ] ; CHECK:loop.split: ; CHECK-NEXT: br label %for.cond ; CHECK:for.cond: ; CHECK-NEXT: switch i32 %cleanup.dest.slot.0, label %for.cond.backedge [ ; CHECK-NEXT: i32 0, label %for.cond.backedge ; CHECK-NEXT: i32 2, label %loop.loopexit ; CHECK-NEXT: ] ; CHECK:for.cond.backedge: ; CHECK-NEXT: br label %for.cond ; CHECK:loop.loopexit: ; CHECK-NEXT: unreachable ; CHECK:NonEmptyUnreachableBlock: ; CHECK-NEXT: call void @f() ; CHECK-NEXT: call void @g() ; CHECK-NEXT: unreachable } ; PR45355 define void @test_unswitch_switch_with_duplicate_edge() { ; CHECK-LABEL: @test_unswitch_switch_with_duplicate_edge() entry: br label %lbl1 lbl1: ; preds = %entry %cleanup.dest.slot.0 = select i1 undef, i32 5, i32 undef br label %for.cond1 for.cond1: ; preds = %for.cond1, %lbl1 switch i32 %cleanup.dest.slot.0, label %UnifiedUnreachableBlock [ i32 0, label %for.cond1 i32 5, label %UnifiedUnreachableBlock i32 2, label %lbl1.loopexit ] UnifiedUnreachableBlock: ; preds = %for.cond1, %for.cond1 unreachable lbl1.loopexit: ; preds = %for.cond1 unreachable ; CHECK: for.cond1: ; CHECK-NEXT: switch i32 %cleanup.dest.slot.0, label %UnifiedUnreachableBlock [ ; CHECK-NEXT: i32 0, label %for.cond1 ; CHECK-NEXT: i32 5, label %UnifiedUnreachableBlock ; CHECK-NEXT: i32 2, label %lbl1.loopexit ; CHECK-NEXT: ] }