diff --git a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp --- a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp +++ b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp @@ -145,6 +145,8 @@ // locate the exit blocks. SetVector ExitingBlocks; SetVector Exits; + // Record the exit blocks that branch to the same block. + MapVector > CommonSuccs; // We need SetVectors, but the Loop API takes a vector, so we use a temporary. SmallVector Temp; @@ -158,6 +160,11 @@ if (SL == L || L->contains(SL)) continue; Exits.insert(S); + // The typical case for reducing the number of guard blocks occurs when + // the exit block has a single predecessor and successor. + if (S->getSinglePredecessor()) + if (auto *Succ = S->getSingleSuccessor()) + CommonSuccs[Succ].insert(S); } } @@ -172,13 +179,39 @@ for (auto EB : ExitingBlocks) { dbgs() << " " << EB->getName(); } - dbgs() << "\n";); + dbgs() << "\n"; + + dbgs() << "Exit blocks with a common successor:\n"; + for (auto CS : CommonSuccs) { + dbgs() << " Succ " << CS.first->getName() << ", exits:"; + for (auto Exit : CS.second) + dbgs() << " " << Exit->getName(); + dbgs() << "\n"; + }); if (Exits.size() <= 1) { LLVM_DEBUG(dbgs() << "loop does not have multiple exits; nothing to do\n"); return false; } + // When multiple exit blocks branch to the same block, change the control + // flow hub to after the exit blocks rather than before. This reduces the + // number of guard blocks needed after the loop. + for (auto CS : CommonSuccs) { + auto CB = CS.first; + auto Preds = CS.second; + if (Exits.contains(CB)) + continue; + if (Preds.size() < 2 || Preds.size() == Exits.size()) + continue; + for (auto Exit : Preds) { + Exits.remove(Exit); + ExitingBlocks.remove(Exit->getSinglePredecessor()); + ExitingBlocks.insert(Exit); + } + Exits.insert(CB); + } + SmallVector GuardBlocks; DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager); auto LoopExitBlock = CreateControlFlowHub(&DTU, GuardBlocks, ExitingBlocks, @@ -198,6 +231,17 @@ if (auto ParentLoop = L->getParentLoop()) { for (auto G : GuardBlocks) { ParentLoop->addBasicBlockToLoop(G, LI); + // Ensure the guard block predecessors are in a valid loop. After the + // change to the control flow hub for common successors, a guard block + // predecessor may not be in a loop or may be in an outer loop. + for (auto Pred : predecessors(G)) { + auto PredLoop = LI.getLoopFor(Pred); + if (!ParentLoop->contains(PredLoop)) { + if (PredLoop) + LI.removeBlock(Pred); + ParentLoop->addBasicBlockToLoop(Pred, LI); + } + } } ParentLoop->verifyLoop(); } diff --git a/llvm/test/Transforms/UnifyLoopExits/reduce_guards.ll b/llvm/test/Transforms/UnifyLoopExits/reduce_guards.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/UnifyLoopExits/reduce_guards.ll @@ -0,0 +1,463 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -unify-loop-exits -enable-new-pm=0 -S | FileCheck %s +; RUN: opt < %s -passes=unify-loop-exits -S | FileCheck %s + +; A loop has exit blocks, B and D, that branch to the same successor. +; We reduce the number of guard blocks needed by creating the loop +; exit guard block after B and D rather than before B and D. + +define void @unreachable_from_loop(i1 %PredEntry, i1 %PredA, i1 %PredB) { +; CHECK-LABEL: @unreachable_from_loop( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[G:%.*]] +; CHECK: A: +; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[E:%.*]] ] +; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[B:%.*]], label [[C:%.*]] +; CHECK: B: +; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]] +; CHECK: C: +; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[D:%.*]], label [[E]] +; CHECK: D: +; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]] +; CHECK: E: +; 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: F: +; CHECK-NEXT: unreachable +; CHECK: G: +; CHECK-NEXT: ret void +; CHECK: loop.exit.guard: +; CHECK-NEXT: [[GUARD_G:%.*]] = phi i1 [ true, [[E]] ], [ false, [[B]] ], [ false, [[D]] ] +; CHECK-NEXT: br i1 [[GUARD_G]], label [[G]], label [[F:%.*]] +; +entry: + br i1 %PredEntry, label %A, label %G + +A: + %inc1 = phi i32 [ 0, %entry ], [ %inc2, %E ] + br i1 %PredA, label %B, label %C + +B: + tail call fastcc void @check(i32 1) #0 + br label %F + +C: + br i1 %PredB, label %D, label %E + +D: + tail call fastcc void @check(i32 2) #0 + br label %F + +E: + %inc2 = add i32 %inc1, 1 + %cmp = icmp ult i32 %inc2, 10 + br i1 %cmp, label %A, label %G + +F: + unreachable + +G: + ret void +} + +; A test when the loop exit blocks appear in an inner loop. + +define void @unreachable_from_inner_loop(i1 %PredEntry, i1 %PredA, i1 %PredB) { +; CHECK-LABEL: @unreachable_from_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 [[C:%.*]] +; CHECK: C: +; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD1:%.*]] +; CHECK: D: +; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[E:%.*]], label [[F]] +; CHECK: E: +; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD1]] +; 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_GUARD1]] +; 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: [[GUARD_I:%.*]] = phi i1 [ true, [[G]] ], [ [[GUARD_I_MOVED:%.*]], [[LOOP_EXIT_GUARD1]] ] +; CHECK-NEXT: br i1 [[GUARD_I]], label [[I]], label [[H:%.*]] +; CHECK: loop.exit.guard1: +; CHECK-NEXT: [[GUARD_G:%.*]] = phi i1 [ true, [[F]] ], [ false, [[C]] ], [ false, [[E]] ] +; CHECK-NEXT: [[GUARD_I_MOVED]] = phi i1 [ undef, [[F]] ], [ false, [[C]] ], [ false, [[E]] ] +; CHECK-NEXT: br i1 [[GUARD_G]], label [[G]], label [[LOOP_EXIT_GUARD]] +; +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 +} + +; Test with 3 nested loops to show the case when we need to change the loop for +; the predecessors of the loop guard. Since UnifyLoopExits works from the outer +; loop to the inner loop, the predecessors are added to the outermost loop. +; When a guard block is added to the exit of the inner loop, the predecessor +; block loops need to change to the middle loop. + +define void @three_loops(i1 %PredEntry, i1 %PredA, i1 %PredB) { +; CHECK-LABEL: @three_loops( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[L1:%.*]], label [[H:%.*]] +; CHECK: L1: +; CHECK-NEXT: [[I1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I2:%.*]], [[F:%.*]] ] +; CHECK-NEXT: br label [[L2:%.*]] +; CHECK: L2: +; CHECK-NEXT: [[J1:%.*]] = phi i32 [ 0, [[L1]] ], [ [[J2:%.*]], [[E:%.*]] ] +; CHECK-NEXT: br label [[L3:%.*]] +; CHECK: L3: +; CHECK-NEXT: [[K1:%.*]] = phi i32 [ 0, [[L2]] ], [ [[K2:%.*]], [[D:%.*]] ] +; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[A:%.*]], label [[B:%.*]] +; CHECK: A: +; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD2:%.*]] +; CHECK: B: +; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[C:%.*]], label [[D]] +; CHECK: C: +; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD2]] +; CHECK: D: +; CHECK-NEXT: [[K2]] = add i32 [[K1]], 1 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i32 [[K2]], 20 +; CHECK-NEXT: br i1 [[CMP1]], label [[L3]], label [[LOOP_EXIT_GUARD2]] +; CHECK: E: +; CHECK-NEXT: [[J2]] = add i32 [[J1]], 1 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[J2]], 10 +; CHECK-NEXT: br i1 [[CMP2]], label [[L2]], label [[LOOP_EXIT_GUARD1:%.*]] +; CHECK: F: +; CHECK-NEXT: [[I2]] = add i32 [[I1]], 1 +; CHECK-NEXT: [[CMP3:%.*]] = icmp ult i32 [[I2]], 5 +; CHECK-NEXT: br i1 [[CMP3]], label [[L1]], label [[LOOP_EXIT_GUARD:%.*]] +; CHECK: G: +; CHECK-NEXT: unreachable +; CHECK: H: +; CHECK-NEXT: ret void +; CHECK: loop.exit.guard: +; CHECK-NEXT: [[GUARD_H:%.*]] = phi i1 [ true, [[F]] ], [ [[GUARD_H_MOVED:%.*]], [[LOOP_EXIT_GUARD1]] ] +; CHECK-NEXT: br i1 [[GUARD_H]], label [[H]], label [[G:%.*]] +; CHECK: loop.exit.guard1: +; CHECK-NEXT: [[GUARD_F:%.*]] = phi i1 [ true, [[E]] ], [ [[GUARD_F_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ] +; CHECK-NEXT: [[GUARD_H_MOVED]] = phi i1 [ undef, [[E]] ], [ [[GUARD_H_MOVED_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ] +; CHECK-NEXT: br i1 [[GUARD_F]], label [[F]], label [[LOOP_EXIT_GUARD]] +; CHECK: loop.exit.guard2: +; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, [[D]] ], [ false, [[A]] ], [ false, [[C]] ] +; CHECK-NEXT: [[GUARD_F_MOVED]] = phi i1 [ undef, [[D]] ], [ false, [[A]] ], [ false, [[C]] ] +; CHECK-NEXT: [[GUARD_H_MOVED_MOVED]] = phi i1 [ undef, [[D]] ], [ false, [[A]] ], [ false, [[C]] ] +; CHECK-NEXT: br i1 [[GUARD_E]], label [[E]], label [[LOOP_EXIT_GUARD1]] +; +entry: + br i1 %PredEntry, label %L1, label %H + +L1: + %i1 = phi i32 [ 0, %entry ], [ %i2, %F ] + br label %L2 + +L2: + %j1 = phi i32 [ 0, %L1 ], [ %j2, %E ] + br label %L3 + +L3: + %k1 = phi i32 [ 0, %L2 ], [ %k2, %D ] + br i1 %PredA, label %A, label %B + +A: + tail call fastcc void @check(i32 1) #0 + br label %G + +B: + br i1 %PredB, label %C, label %D + +C: + tail call fastcc void @check(i32 2) #0 + br label %G + +D: + %k2 = add i32 %k1, 1 + %cmp1 = icmp ult i32 %k2, 20 + br i1 %cmp1, label %L3, label %E + +E: + %j2 = add i32 %j1, 1 + %cmp2 = icmp ult i32 %j2, 10 + br i1 %cmp2, label %L2, label %F + +F: + %i2 = add i32 %i1, 1 + %cmp3 = icmp ult i32 %i2, 5 + br i1 %cmp3, label %L1, label %H + +G: + unreachable + +H: + ret void +} + + +; The common successor does not have to be an unreachable block. + +define void @common_exit(i1 %PredEntry, i1 %PredA, i1 %PredB) { +; CHECK-LABEL: @common_exit( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[G:%.*]] +; CHECK: A: +; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[E:%.*]] ] +; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[B:%.*]], label [[C:%.*]] +; CHECK: B: +; CHECK-NEXT: tail call fastcc void @check1(i32 1) #[[ATTR1:[0-9]+]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]] +; CHECK: C: +; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[D:%.*]], label [[E]] +; CHECK: D: +; CHECK-NEXT: tail call fastcc void @check1(i32 2) #[[ATTR1]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]] +; CHECK: E: +; 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: F: +; CHECK-NEXT: br label [[G]] +; CHECK: G: +; CHECK-NEXT: ret void +; CHECK: loop.exit.guard: +; CHECK-NEXT: [[GUARD_G:%.*]] = phi i1 [ true, [[E]] ], [ false, [[B]] ], [ false, [[D]] ] +; CHECK-NEXT: br i1 [[GUARD_G]], label [[G]], label [[F:%.*]] +; +entry: + br i1 %PredEntry, label %A, label %G + +A: + %inc1 = phi i32 [ 0, %entry ], [ %inc2, %E ] + br i1 %PredA, label %B, label %C + +B: + tail call fastcc void @check1(i32 1) #1 + br label %F + +C: + br i1 %PredB, label %D, label %E + +D: + tail call fastcc void @check1(i32 2) #1 + br label %F + +E: + %inc2 = add i32 %inc1, 1 + %cmp = icmp ult i32 %inc2, 10 + br i1 %cmp, label %A, label %G + +F: + br label %G + +G: + ret void +} + +; A test with multiple common exits. + +define void @multiple_common_successors(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %PredC, i1 %PredD) { +; CHECK-LABEL: @multiple_common_successors( +; 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 [[B:%.*]], label [[C:%.*]] +; CHECK: B: +; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD:%.*]] +; CHECK: C: +; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[D:%.*]], label [[E:%.*]] +; CHECK: D: +; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]] +; CHECK: E: +; CHECK-NEXT: br i1 [[PREDC:%.*]], label [[F:%.*]], label [[G:%.*]] +; CHECK: F: +; CHECK-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]] +; CHECK: G: +; CHECK-NEXT: br i1 [[PREDD:%.*]], label [[H:%.*]], label [[I]] +; CHECK: H: +; CHECK-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]] +; CHECK-NEXT: br label [[LOOP_EXIT_GUARD]] +; 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: [[GUARD_L:%.*]] = phi i1 [ true, [[I]] ], [ false, [[B]] ], [ false, [[D]] ], [ false, [[F]] ], [ false, [[H]] ] +; CHECK-NEXT: [[GUARD_J:%.*]] = phi i1 [ false, [[I]] ], [ true, [[B]] ], [ true, [[D]] ], [ false, [[F]] ], [ false, [[H]] ] +; CHECK-NEXT: br i1 [[GUARD_L]], label [[L]], label [[LOOP_EXIT_GUARD1:%.*]] +; CHECK: loop.exit.guard1: +; CHECK-NEXT: br i1 [[GUARD_J]], label [[J:%.*]], label [[K:%.*]] +; +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 +} + +; Test when the loop exit block is tha same as a common successor +; block. The number of guard blocks is unchanged. + +define void @common_successor_exit(i1 %PredEntry, i1 %PredA, i1 %PredB) { +; CHECK-LABEL: @common_successor_exit( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[G:%.*]] +; CHECK: A: +; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[E:%.*]] ] +; 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 [[G]] +; 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 [[G]] +; CHECK: E: +; 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: G: +; CHECK-NEXT: ret void +; CHECK: loop.exit.guard: +; CHECK-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ], [ false, [[E]] ] +; CHECK-NEXT: [[GUARD_D:%.*]] = phi i1 [ false, [[A]] ], [ true, [[C]] ], [ false, [[E]] ] +; CHECK-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]] +; CHECK: loop.exit.guard1: +; CHECK-NEXT: br i1 [[GUARD_D]], label [[D:%.*]], label [[G]] +; +entry: + br i1 %PredEntry, label %A, label %G + +A: + %inc1 = phi i32 [ 0, %entry ], [ %inc2, %E ] + br i1 %PredA, label %B, label %C + +B: + tail call fastcc void @check(i32 1) #0 + br label %G + +C: + br i1 %PredB, label %D, label %E + +D: + tail call fastcc void @check(i32 2) #0 + br label %G + +E: + %inc2 = add i32 %inc1, 1 + %cmp = icmp ult i32 %inc2, 10 + br i1 %cmp, label %A, label %G + +G: + ret void +} + +declare void @check(i32 noundef %i) #0 +declare void @check1(i32 noundef %i) #1 + +attributes #0 = { noreturn nounwind } +attributes #1 = { nounwind } +