| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt < %s -passes='unify-loop-exits' -S | FileCheck %s | |
| define void @loop_1_callbr(i1 %PredEntry, i1 %PredB, i1 %PredC, i1 %PredD) { | |
| ; CHECK-LABEL: define void @loop_1_callbr( | |
| ; CHECK-SAME: i1 [[PREDENTRY:%.*]], i1 [[PREDB:%.*]], i1 [[PREDC:%.*]], i1 [[PREDD:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: br i1 [[PREDENTRY]], label %[[A:.*]], label %[[G:.*]] | |
| ; CHECK: [[A]]: | |
| ; CHECK-NEXT: br label %[[B:.*]] | |
| ; CHECK: [[B]]: | |
| ; CHECK-NEXT: callbr void asm "", "r,!i,!i"(i1 [[PREDB]]) | |
| ; CHECK-NEXT: to label %[[C:.*]] [label %[[B_TARGET_E:.*]], label %[[B_TARGET_E]]] | |
| ; CHECK: [[C]]: | |
| ; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDC]]) | |
| ; CHECK-NEXT: to label %[[D:.*]] [label %[[C_TARGET_F:.*]]] | |
| ; CHECK: [[D]]: | |
| ; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PREDD]]) | |
| ; CHECK-NEXT: to label %[[A]] [label %[[D_TARGET_F:.*]]] | |
| ; CHECK: [[E:.*]]: | |
| ; CHECK-NEXT: br label %[[EXIT:.*]] | |
| ; CHECK: [[F:.*]]: | |
| ; CHECK-NEXT: br label %[[EXIT]] | |
| ; CHECK: [[G]]: | |
| ; CHECK-NEXT: br label %[[F]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[B_TARGET_E]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD:.*]] | |
| ; CHECK: [[C_TARGET_F]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[D_TARGET_F]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[LOOP_EXIT_GUARD]]: | |
| ; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, %[[B_TARGET_E]] ], [ false, %[[C_TARGET_F]] ], [ false, %[[D_TARGET_F]] ] | |
| ; CHECK-NEXT: br i1 [[GUARD_E]], label %[[E]], label %[[F]] | |
| ; | |
| entry: | |
| br i1 %PredEntry, label %A, label %G | |
| A: | |
| br label %B | |
| B: | |
| callbr void asm "", "r,!i,!i"(i1 %PredB) to label %C [label %E, label %E] | |
| C: | |
| callbr void asm "", "r,!i"(i1 %PredC) to label %D [label %F] | |
| D: | |
| callbr void asm "", "r,!i"(i1 %PredD) to label %A [label %F] | |
| E: | |
| br label %exit | |
| F: | |
| br label %exit | |
| G: | |
| br label %F | |
| exit: | |
| ret void | |
| } | |
| define void @loop_1_switch(i1 %PredEntry, i32 %PredB, i32 %PredC, i32 %PredD) { | |
| ; CHECK-LABEL: define void @loop_1_switch( | |
| ; CHECK-SAME: i1 [[PREDENTRY:%.*]], i32 [[PREDB:%.*]], i32 [[PREDC:%.*]], i32 [[PREDD:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: br i1 [[PREDENTRY]], label %[[A:.*]], label %[[G:.*]] | |
| ; CHECK: [[A]]: | |
| ; CHECK-NEXT: br label %[[B:.*]] | |
| ; CHECK: [[B]]: | |
| ; CHECK-NEXT: switch i32 [[PREDB]], label %[[C:.*]] [ | |
| ; CHECK-NEXT: i32 0, label %[[B_TARGET_E:.*]] | |
| ; CHECK-NEXT: i32 1, label %[[B_TARGET_E]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[C]]: | |
| ; CHECK-NEXT: switch i32 [[PREDC]], label %[[D:.*]] [ | |
| ; CHECK-NEXT: i32 0, label %[[C_TARGET_F:.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[D]]: | |
| ; CHECK-NEXT: switch i32 [[PREDD]], label %[[A]] [ | |
| ; CHECK-NEXT: i32 0, label %[[D_TARGET_F:.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[E:.*]]: | |
| ; CHECK-NEXT: br label %[[EXIT:.*]] | |
| ; CHECK: [[F:.*]]: | |
| ; CHECK-NEXT: br label %[[EXIT]] | |
| ; CHECK: [[G]]: | |
| ; CHECK-NEXT: br label %[[F]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[B_TARGET_E]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD:.*]] | |
| ; CHECK: [[C_TARGET_F]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[D_TARGET_F]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[LOOP_EXIT_GUARD]]: | |
| ; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, %[[B_TARGET_E]] ], [ false, %[[C_TARGET_F]] ], [ false, %[[D_TARGET_F]] ] | |
| ; CHECK-NEXT: br i1 [[GUARD_E]], label %[[E]], label %[[F]] | |
| ; | |
| entry: | |
| br i1 %PredEntry, label %A, label %G | |
| A: | |
| br label %B | |
| B: | |
| switch i32 %PredB, label %C [ | |
| i32 0, label %E | |
| i32 1, label %E | |
| ] | |
| C: | |
| switch i32 %PredC, label %D [ | |
| i32 0, label %F | |
| ] | |
| D: | |
| switch i32 %PredD, label %A [ | |
| i32 0, label %F | |
| ] | |
| E: | |
| br label %exit | |
| F: | |
| br label %exit | |
| G: | |
| br label %F | |
| exit: | |
| ret void | |
| } | |
| define void @multiedge_both_callbr(i1 %Pred) { | |
| ; CHECK-LABEL: define void @multiedge_both_callbr( | |
| ; CHECK-SAME: i1 [[PRED:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: br label %[[LOOP:.*]] | |
| ; CHECK: [[LOOP]]: | |
| ; CHECK-NEXT: callbr void asm "", "r,!i"(i1 [[PRED]]) | |
| ; CHECK-NEXT: to label %[[LOOP_NEXT:.*]] [label %[[LOOP_NEXT]]] | |
| ; CHECK: [[LOOP_NEXT]]: | |
| ; CHECK-NEXT: [[PHI_INTERNAL:%.*]] = phi i32 [ 1, %[[LOOP]] ], [ 1, %[[LOOP]] ] | |
| ; CHECK-NEXT: callbr void asm "", "r,!i,!i"(i1 [[PRED]]) | |
| ; CHECK-NEXT: to label %[[LOOP]] [label %[[LOOP_NEXT_TARGET_EXIT:.*]], label %[[LOOP_NEXT_TARGET_EXIT]]] | |
| ; CHECK: [[EXIT:.*]]: | |
| ; CHECK-NEXT: [[PHI_EXTERNAL:%.*]] = phi i32 [ 2, %[[LOOP_NEXT_TARGET_EXIT]] ] | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[LOOP_NEXT_TARGET_EXIT]]: | |
| ; CHECK-NEXT: br label %[[EXIT]] | |
| ; | |
| entry: | |
| br label %loop | |
| loop: | |
| callbr void asm "", "r,!i"(i1 %Pred) to label %loop.next [label %loop.next] | |
| loop.next: | |
| %phi.internal = phi i32 [ 1, %loop ], [ 1, %loop ] | |
| callbr void asm "", "r,!i,!i"(i1 %Pred) to label %loop [label %exit, label %exit] | |
| exit: | |
| %phi.external = phi i32 [ 2, %loop.next ], [ 2, %loop.next ] | |
| ret void | |
| } | |
| define void @multiedge_both_switch(i32 %Pred) { | |
| ; CHECK-LABEL: define void @multiedge_both_switch( | |
| ; CHECK-SAME: i32 [[PRED:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: br label %[[LOOP:.*]] | |
| ; CHECK: [[LOOP]]: | |
| ; CHECK-NEXT: switch i32 [[PRED]], label %[[LOOP_NEXT:.*]] [ | |
| ; CHECK-NEXT: i32 0, label %[[LOOP_NEXT]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[LOOP_NEXT]]: | |
| ; CHECK-NEXT: [[PHI_INTERNAL:%.*]] = phi i32 [ 1, %[[LOOP]] ], [ 1, %[[LOOP]] ] | |
| ; CHECK-NEXT: switch i32 [[PRED]], label %[[LOOP]] [ | |
| ; CHECK-NEXT: i32 0, label %[[LOOP_NEXT_TARGET_EXIT:.*]] | |
| ; CHECK-NEXT: i32 1, label %[[LOOP_NEXT_TARGET_EXIT]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[EXIT:.*]]: | |
| ; CHECK-NEXT: [[PHI_EXTERNAL:%.*]] = phi i32 [ 2, %[[LOOP_NEXT_TARGET_EXIT]] ] | |
| ; CHECK-NEXT: ret void | |
| ; CHECK: [[LOOP_NEXT_TARGET_EXIT]]: | |
| ; CHECK-NEXT: br label %[[EXIT]] | |
| ; | |
| entry: | |
| br label %loop | |
| loop: | |
| switch i32 %Pred, label %loop.next [ | |
| i32 0, label %loop.next | |
| ] | |
| loop.next: | |
| %phi.internal = phi i32 [ 1, %loop ], [ 1, %loop ] | |
| switch i32 %Pred, label %loop [ | |
| i32 0, label %exit | |
| i32 1, label %exit | |
| ] | |
| exit: | |
| %phi.external = phi i32 [ 2, %loop.next ], [ 2, %loop.next ] | |
| ret void | |
| } | |
| define i32 @multiedge_restore_ssa_callbr() { | |
| ; CHECK-LABEL: define i32 @multiedge_restore_ssa_callbr() { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: br label %[[HEADER:.*]] | |
| ; CHECK: [[HEADER]]: | |
| ; CHECK-NEXT: [[VAL:%.*]] = add i32 0, 0 | |
| ; CHECK-NEXT: callbr void asm "", "r,!i,!i"(i1 false) | |
| ; CHECK-NEXT: to label %[[HEADER_TARGET_EXIT1:.*]] [label %[[HEADER_TARGET_COMMON_RET:.*]], label %[[SECOND_EXITING:.*]]] | |
| ; CHECK: [[SECOND_EXITING]]: | |
| ; CHECK-NEXT: callbr void asm "", "r,!i,!i"(i1 false) | |
| ; CHECK-NEXT: to label %[[HEADER]] [label %[[SECOND_EXITING_TARGET_EXIT1:.*]], label %[[SECOND_EXITING_TARGET_COMMON_RET:.*]]] | |
| ; CHECK: [[COMMON_RET:.*]]: | |
| ; CHECK-NEXT: ret i32 0 | |
| ; CHECK: [[EXIT1:.*]]: | |
| ; CHECK-NEXT: [[USE1:%.*]] = or i32 [[VAL_MOVED:%.*]], 0 | |
| ; CHECK-NEXT: ret i32 0 | |
| ; CHECK: [[HEADER_TARGET_EXIT1]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD:.*]] | |
| ; CHECK: [[HEADER_TARGET_COMMON_RET]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[SECOND_EXITING_TARGET_EXIT1]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[SECOND_EXITING_TARGET_COMMON_RET]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[LOOP_EXIT_GUARD]]: | |
| ; CHECK-NEXT: [[VAL_MOVED]] = phi i32 [ [[VAL]], %[[HEADER_TARGET_EXIT1]] ], [ [[VAL]], %[[SECOND_EXITING_TARGET_EXIT1]] ], [ [[VAL]], %[[HEADER_TARGET_COMMON_RET]] ], [ [[VAL]], %[[SECOND_EXITING_TARGET_COMMON_RET]] ] | |
| ; CHECK-NEXT: [[GUARD_EXIT1:%.*]] = phi i1 [ true, %[[HEADER_TARGET_EXIT1]] ], [ false, %[[HEADER_TARGET_COMMON_RET]] ], [ true, %[[SECOND_EXITING_TARGET_EXIT1]] ], [ false, %[[SECOND_EXITING_TARGET_COMMON_RET]] ] | |
| ; CHECK-NEXT: br i1 [[GUARD_EXIT1]], label %[[EXIT1]], label %[[COMMON_RET]] | |
| ; | |
| entry: | |
| br label %header | |
| header: ; preds = %second_exiting, %entry | |
| %val = add i32 0, 0 | |
| callbr void asm "", "r,!i,!i"(i1 false) to label %exit1 [label %common.ret, label %second_exiting] | |
| second_exiting: ; preds = %header | |
| callbr void asm "", "r,!i,!i"(i1 false) to label %header [label %exit1, label %common.ret] | |
| common.ret: ; preds = %second_exiting, %header | |
| ret i32 0 | |
| exit1: ; preds = %second_exiting, %header | |
| %use1 = or i32 %val, 0 | |
| ret i32 0 | |
| } | |
| define i32 @multiedge_restore_ssa_switch() { | |
| ; CHECK-LABEL: define i32 @multiedge_restore_ssa_switch() { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: br label %[[HEADER:.*]] | |
| ; CHECK: [[HEADER]]: | |
| ; CHECK-NEXT: [[VAL:%.*]] = add i32 0, 0 | |
| ; CHECK-NEXT: switch i32 0, label %[[HEADER_TARGET_EXIT1:.*]] [ | |
| ; CHECK-NEXT: i32 0, label %[[HEADER_TARGET_COMMON_RET:.*]] | |
| ; CHECK-NEXT: i32 1, label %[[SECOND_EXITING:.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[SECOND_EXITING]]: | |
| ; CHECK-NEXT: switch i32 0, label %[[HEADER]] [ | |
| ; CHECK-NEXT: i32 0, label %[[SECOND_EXITING_TARGET_EXIT1:.*]] | |
| ; CHECK-NEXT: i32 1, label %[[SECOND_EXITING_TARGET_COMMON_RET:.*]] | |
| ; CHECK-NEXT: ] | |
| ; CHECK: [[COMMON_RET:.*]]: | |
| ; CHECK-NEXT: ret i32 0 | |
| ; CHECK: [[EXIT1:.*]]: | |
| ; CHECK-NEXT: [[USE1:%.*]] = or i32 [[VAL_MOVED:%.*]], 0 | |
| ; CHECK-NEXT: ret i32 0 | |
| ; CHECK: [[HEADER_TARGET_EXIT1]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD:.*]] | |
| ; CHECK: [[HEADER_TARGET_COMMON_RET]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[SECOND_EXITING_TARGET_EXIT1]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[SECOND_EXITING_TARGET_COMMON_RET]]: | |
| ; CHECK-NEXT: br label %[[LOOP_EXIT_GUARD]] | |
| ; CHECK: [[LOOP_EXIT_GUARD]]: | |
| ; CHECK-NEXT: [[VAL_MOVED]] = phi i32 [ [[VAL]], %[[HEADER_TARGET_EXIT1]] ], [ [[VAL]], %[[SECOND_EXITING_TARGET_EXIT1]] ], [ [[VAL]], %[[HEADER_TARGET_COMMON_RET]] ], [ [[VAL]], %[[SECOND_EXITING_TARGET_COMMON_RET]] ] | |
| ; CHECK-NEXT: [[GUARD_EXIT1:%.*]] = phi i1 [ true, %[[HEADER_TARGET_EXIT1]] ], [ false, %[[HEADER_TARGET_COMMON_RET]] ], [ true, %[[SECOND_EXITING_TARGET_EXIT1]] ], [ false, %[[SECOND_EXITING_TARGET_COMMON_RET]] ] | |
| ; CHECK-NEXT: br i1 [[GUARD_EXIT1]], label %[[EXIT1]], label %[[COMMON_RET]] | |
| ; | |
| entry: | |
| br label %header | |
| header: ; preds = %second_exiting, %entry | |
| %val = add i32 0, 0 | |
| switch i32 0, label %exit1 [ | |
| i32 0, label %common.ret | |
| i32 1, label %second_exiting | |
| ] | |
| second_exiting: ; preds = %header | |
| switch i32 0, label %header [ | |
| i32 0, label %exit1 | |
| i32 1, label %common.ret | |
| ] | |
| common.ret: ; preds = %second_exiting, %header | |
| ret i32 0 | |
| exit1: ; preds = %second_exiting, %header | |
| %use1 = or i32 %val, 0 | |
| ret i32 0 | |
| } |