; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -passes=unify-loop-exits -max-booleans-in-control-flow-hub=1 -S | FileCheck %s ; RUN: opt < %s -passes=unify-loop-exits -S | FileCheck --check-prefix=BOOLEAN %s ; A loop with multiple exit blocks. define void @loop_two_exits(i1 %PredEntry, i1 %PredA) { ; CHECK-LABEL: @loop_two_exits( ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[E:%.*]] ; CHECK: A: ; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ] ; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C]] ; CHECK: B: ; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0:[0-9]+]] ; CHECK-NEXT: br label [[D:%.*]] ; CHECK: C: ; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10 ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] ; CHECK: D: ; CHECK-NEXT: unreachable ; CHECK: E: ; CHECK-NEXT: ret void ; CHECK: loop.exit.guard: ; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A]] ], [ 1, [[C]] ] ; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0 ; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[E]] ; ; BOOLEAN-LABEL: @loop_two_exits( ; BOOLEAN-NEXT: entry: ; BOOLEAN-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[E:%.*]] ; BOOLEAN: A: ; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ] ; BOOLEAN-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C]] ; BOOLEAN: B: ; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0:[0-9]+]] ; BOOLEAN-NEXT: br label [[D:%.*]] ; BOOLEAN: C: ; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1 ; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10 ; BOOLEAN-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] ; BOOLEAN: D: ; BOOLEAN-NEXT: unreachable ; BOOLEAN: E: ; BOOLEAN-NEXT: ret void ; BOOLEAN: loop.exit.guard: ; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ] ; BOOLEAN-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[E]] ; entry: br i1 %PredEntry, label %A, label %E A: %inc1 = phi i32 [ 0, %entry ], [ %inc2, %C ] br i1 %PredA, label %B, label %C B: tail call fastcc void @check(i32 1) #0 br label %D C: %inc2 = add i32 %inc1, 1 %cmp = icmp ult i32 %inc2, 10 br i1 %cmp, label %A, label %E D: unreachable E: ret void } ; The loop exit blocks appear in an inner loop. define void @inner_loop(i1 %PredEntry, i1 %PredA, i1 %PredB) { ; CHECK-LABEL: @inner_loop( ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[I:%.*]] ; CHECK: A: ; CHECK-NEXT: [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ] ; CHECK-NEXT: br label [[B:%.*]] ; CHECK: B: ; CHECK-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ] ; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]] ; CHECK: C: ; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] ; CHECK-NEXT: br label [[H:%.*]] ; CHECK: D: ; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD2]], label [[F]] ; CHECK: E: ; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] ; CHECK-NEXT: br label [[H]] ; CHECK: F: ; CHECK-NEXT: [[INNER2]] = add i32 [[INNER1]], 1 ; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20 ; CHECK-NEXT: br i1 [[CMP1]], label [[B]], label [[LOOP_EXIT_GUARD2]] ; CHECK: G: ; CHECK-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1 ; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10 ; CHECK-NEXT: br i1 [[CMP2]], label [[A]], label [[LOOP_EXIT_GUARD:%.*]] ; CHECK: H: ; CHECK-NEXT: unreachable ; CHECK: I: ; CHECK-NEXT: ret void ; CHECK: loop.exit.guard: ; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 2, [[G]] ], [ [[MERGED_BB_IDX_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ] ; CHECK-NEXT: [[C_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0 ; CHECK-NEXT: br i1 [[C_PREDICATE]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]] ; CHECK: loop.exit.guard1: ; CHECK-NEXT: [[E_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 1 ; CHECK-NEXT: br i1 [[E_PREDICATE]], label [[E:%.*]], label [[I]] ; CHECK: loop.exit.guard2: ; CHECK-NEXT: [[MERGED_BB_IDX_MOVED]] = phi i32 [ 0, [[B]] ], [ 1, [[D]] ], [ undef, [[F]] ] ; CHECK-NEXT: [[MERGED_BB_IDX3:%.*]] = phi i32 [ 0, [[B]] ], [ 0, [[D]] ], [ 1, [[F]] ] ; CHECK-NEXT: [[LOOP_EXIT_GUARD_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX3]], 0 ; CHECK-NEXT: br i1 [[LOOP_EXIT_GUARD_PREDICATE]], label [[LOOP_EXIT_GUARD]], label [[G]] ; ; BOOLEAN-LABEL: @inner_loop( ; BOOLEAN-NEXT: entry: ; BOOLEAN-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[I:%.*]] ; BOOLEAN: A: ; BOOLEAN-NEXT: [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ] ; BOOLEAN-NEXT: br label [[B:%.*]] ; BOOLEAN: B: ; BOOLEAN-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ] ; BOOLEAN-NEXT: br i1 [[PREDA:%.*]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]] ; BOOLEAN: C: ; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] ; BOOLEAN-NEXT: br label [[H:%.*]] ; BOOLEAN: D: ; BOOLEAN-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD2]], label [[F]] ; BOOLEAN: E: ; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] ; BOOLEAN-NEXT: br label [[H]] ; BOOLEAN: F: ; BOOLEAN-NEXT: [[INNER2]] = add i32 [[INNER1]], 1 ; BOOLEAN-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20 ; BOOLEAN-NEXT: br i1 [[CMP1]], label [[B]], label [[LOOP_EXIT_GUARD2]] ; BOOLEAN: G: ; BOOLEAN-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1 ; BOOLEAN-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10 ; BOOLEAN-NEXT: br i1 [[CMP2]], label [[A]], label [[LOOP_EXIT_GUARD:%.*]] ; BOOLEAN: H: ; BOOLEAN-NEXT: unreachable ; BOOLEAN: I: ; BOOLEAN-NEXT: ret void ; BOOLEAN: loop.exit.guard: ; BOOLEAN-NEXT: [[GUARD_C:%.*]] = phi i1 [ false, [[G]] ], [ [[GUARD_C_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ] ; BOOLEAN-NEXT: [[GUARD_E:%.*]] = phi i1 [ false, [[G]] ], [ [[GUARD_E_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ] ; BOOLEAN-NEXT: br i1 [[GUARD_C]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]] ; BOOLEAN: loop.exit.guard1: ; BOOLEAN-NEXT: br i1 [[GUARD_E]], label [[E:%.*]], label [[I]] ; BOOLEAN: loop.exit.guard2: ; BOOLEAN-NEXT: [[GUARD_E_MOVED]] = phi i1 [ false, [[B]] ], [ true, [[D]] ], [ undef, [[F]] ] ; BOOLEAN-NEXT: [[GUARD_C_MOVED]] = phi i1 [ true, [[B]] ], [ false, [[D]] ], [ undef, [[F]] ] ; BOOLEAN-NEXT: [[GUARD_LOOP_EXIT_GUARD:%.*]] = phi i1 [ true, [[B]] ], [ true, [[D]] ], [ false, [[F]] ] ; BOOLEAN-NEXT: br i1 [[GUARD_LOOP_EXIT_GUARD]], label [[LOOP_EXIT_GUARD]], label [[G]] ; entry: br i1 %PredEntry, label %A, label %I A: %outer1 = phi i32 [ 0, %entry ], [ %outer2, %G ] br label %B B: %inner1 = phi i32 [ 0, %A ], [ %inner2, %F ] br i1 %PredA, label %D, label %C C: tail call fastcc void @check(i32 1) #0 br label %H D: br i1 %PredB, label %E, label %F E: tail call fastcc void @check(i32 2) #0 br label %H F: %inner2 = add i32 %inner1, 1 %cmp1 = icmp ult i32 %inner2, 20 br i1 %cmp1, label %B, label %G G: %outer2 = add i32 %outer1, 1 %cmp2 = icmp ult i32 %outer2, 10 br i1 %cmp2, label %A, label %I H: unreachable I: ret void } ; A loop with more exit blocks. define void @loop_five_exits(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %PredC, i1 %PredD) { ; CHECK-LABEL: @loop_five_exits( ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[L:%.*]] ; CHECK: A: ; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ] ; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C:%.*]] ; CHECK: B: ; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] ; CHECK-NEXT: br label [[J:%.*]] ; CHECK: C: ; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD]], label [[E:%.*]] ; CHECK: D: ; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] ; CHECK-NEXT: br label [[J]] ; CHECK: E: ; CHECK-NEXT: br i1 [[PREDC:%.*]], label [[LOOP_EXIT_GUARD]], label [[G:%.*]] ; CHECK: F: ; CHECK-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]] ; CHECK-NEXT: br label [[K:%.*]] ; CHECK: G: ; CHECK-NEXT: br i1 [[PREDD:%.*]], label [[LOOP_EXIT_GUARD]], label [[I]] ; CHECK: H: ; CHECK-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]] ; CHECK-NEXT: br label [[K]] ; CHECK: I: ; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10 ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] ; CHECK: J: ; CHECK-NEXT: br label [[L]] ; CHECK: K: ; CHECK-NEXT: br label [[L]] ; CHECK: L: ; CHECK-NEXT: ret void ; CHECK: loop.exit.guard: ; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A]] ], [ 1, [[C]] ], [ 2, [[E]] ], [ 3, [[G]] ], [ 4, [[I]] ] ; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0 ; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]] ; CHECK: loop.exit.guard1: ; CHECK-NEXT: [[D_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 1 ; CHECK-NEXT: br i1 [[D_PREDICATE]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]] ; CHECK: loop.exit.guard2: ; CHECK-NEXT: [[F_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 2 ; CHECK-NEXT: br i1 [[F_PREDICATE]], label [[F:%.*]], label [[LOOP_EXIT_GUARD3:%.*]] ; CHECK: loop.exit.guard3: ; CHECK-NEXT: [[H_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 3 ; CHECK-NEXT: br i1 [[H_PREDICATE]], label [[H:%.*]], label [[L]] ; ; BOOLEAN-LABEL: @loop_five_exits( ; BOOLEAN-NEXT: entry: ; BOOLEAN-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[L:%.*]] ; BOOLEAN: A: ; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ] ; BOOLEAN-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C:%.*]] ; BOOLEAN: B: ; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] ; BOOLEAN-NEXT: br label [[J:%.*]] ; BOOLEAN: C: ; BOOLEAN-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD]], label [[E:%.*]] ; BOOLEAN: D: ; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] ; BOOLEAN-NEXT: br label [[J]] ; BOOLEAN: E: ; BOOLEAN-NEXT: br i1 [[PREDC:%.*]], label [[LOOP_EXIT_GUARD]], label [[G:%.*]] ; BOOLEAN: F: ; BOOLEAN-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]] ; BOOLEAN-NEXT: br label [[K:%.*]] ; BOOLEAN: G: ; BOOLEAN-NEXT: br i1 [[PREDD:%.*]], label [[LOOP_EXIT_GUARD]], label [[I]] ; BOOLEAN: H: ; BOOLEAN-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]] ; BOOLEAN-NEXT: br label [[K]] ; BOOLEAN: I: ; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1 ; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10 ; BOOLEAN-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] ; BOOLEAN: J: ; BOOLEAN-NEXT: br label [[L]] ; BOOLEAN: K: ; BOOLEAN-NEXT: br label [[L]] ; BOOLEAN: L: ; BOOLEAN-NEXT: ret void ; BOOLEAN: loop.exit.guard: ; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ], [ false, [[E]] ], [ false, [[G]] ], [ false, [[I]] ] ; BOOLEAN-NEXT: [[GUARD_D:%.*]] = phi i1 [ false, [[A]] ], [ true, [[C]] ], [ false, [[E]] ], [ false, [[G]] ], [ false, [[I]] ] ; BOOLEAN-NEXT: [[GUARD_F:%.*]] = phi i1 [ false, [[A]] ], [ false, [[C]] ], [ true, [[E]] ], [ false, [[G]] ], [ false, [[I]] ] ; BOOLEAN-NEXT: [[GUARD_H:%.*]] = phi i1 [ false, [[A]] ], [ false, [[C]] ], [ false, [[E]] ], [ true, [[G]] ], [ false, [[I]] ] ; BOOLEAN-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]] ; BOOLEAN: loop.exit.guard1: ; BOOLEAN-NEXT: br i1 [[GUARD_D]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]] ; BOOLEAN: loop.exit.guard2: ; BOOLEAN-NEXT: br i1 [[GUARD_F]], label [[F:%.*]], label [[LOOP_EXIT_GUARD3:%.*]] ; BOOLEAN: loop.exit.guard3: ; BOOLEAN-NEXT: br i1 [[GUARD_H]], label [[H:%.*]], label [[L]] ; entry: br i1 %PredEntry, label %A, label %L A: %inc1 = phi i32 [ 0, %entry ], [ %inc2, %I ] br i1 %PredA, label %B, label %C B: tail call fastcc void @check(i32 1) #0 br label %J C: br i1 %PredB, label %D, label %E D: tail call fastcc void @check(i32 2) #0 br label %J E: br i1 %PredC, label %F, label %G F: tail call fastcc void @check(i32 3) #0 br label %K G: br i1 %PredD, label %H, label %I H: tail call fastcc void @check(i32 4) #0 br label %K I: %inc2 = add i32 %inc1, 1 %cmp = icmp ult i32 %inc2, 10 br i1 %cmp, label %A, label %L J: br label %L K: br label %L L: ret void } declare void @check(i32 noundef %i) #0 attributes #0 = { noreturn nounwind }