| ; 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]] ], [ poison, [[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]] ], [ poison, [[F]] ] |
| ; BOOLEAN-NEXT: [[GUARD_C_MOVED]] = phi i1 [ true, [[B]] ], [ false, [[D]] ], [ poison, [[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 } |
| |