; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -passes='fix-irreducible,verify' -S | FileCheck %s ; RUN: opt < %s -passes='verify,fix-irreducible,verify' -S | FileCheck %s ; RUN: opt < %s -passes='print' -disable-output 2>&1 | FileCheck %s --check-prefix CYCLES-BEFORE ; RUN: opt < %s -passes='fix-irreducible,print' -disable-output 2>&1 | FileCheck %s --check-prefix CYCLES-AFTER ; CYCLES-BEFORE: CycleInfo for function: callbr_entry ; CYCLES-BEFORE-NEXT: depth=1: entries(indirect fallthrough) ; CYCLES-AFTER: CycleInfo for function: callbr_entry ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) indirect fallthrough ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_entry_targets_with_phi_nodes ; CYCLES-BEFORE-NEXT: depth=1: entries(block1 block) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_entry_targets_with_phi_nodes ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) block1 block ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_entry_multiple_indirect_targets ; CYCLES-BEFORE-NEXT: depth=1: entries(indirect fallthrough) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_entry_multiple_indirect_targets ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) indirect fallthrough ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_entry_multiple_indirect_targets1 ; CYCLES-BEFORE-NEXT: depth=1: entries(indirect1 indirect fallthrough) ; CYCLES-BEFORE-NEXT: depth=2: entries(indirect fallthrough) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_entry_multiple_indirect_targets1 ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) indirect1 indirect fallthrough irr.guard1 irr.guard2 ; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) indirect fallthrough ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_header_no_indirect ; CYCLES-BEFORE-NEXT: depth=1: entries(fallthrough callbr) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_header_no_indirect ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) fallthrough callbr callbr.target.fallthrough ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_header ; CYCLES-BEFORE-NEXT: depth=1: entries(fallthrough callbr) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_header ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) fallthrough callbr callbr.target.fallthrough ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_header_multiple_indirect_targets ; CYCLES-BEFORE-NEXT: depth=1: entries(fallthrough callbr) indirect1 ; CYCLES-BEFORE-NEXT: depth=2: entries(callbr) indirect1 ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_header_multiple_indirect_targets ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) fallthrough callbr indirect1 callbr.target.fallthrough ; CYCLES-AFTER-NEXT: depth=2: entries(callbr) indirect1 ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_regular ; CYCLES-BEFORE-NEXT: depth=1: entries(fallthrough2 fallthrough1) ; CYCLES-BEFORE-NEXT: depth=1: entries(indirect2 indirect1) ; CYCLES-BEFORE-NEXT: depth=1: entries(nocallbr2 nocallbr1) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_regular ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) fallthrough2 fallthrough1 ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard1) indirect2 indirect1 ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard2) nocallbr2 nocallbr1 ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_regular1 ; CYCLES-BEFORE-NEXT: depth=1: entries(callbr nocallbr) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_regular1 ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) callbr nocallbr ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_regular2 ; CYCLES-BEFORE-NEXT: depth=1: entries(callbr nocallbr) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_regular2 ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) callbr nocallbr ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_header_and_regular ; CYCLES-BEFORE-NEXT: depth=1: entries(callbr_header) callbr_regular mid ; CYCLES-BEFORE-NEXT: depth=2: entries(callbr_regular mid) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_header_and_regular ; CYCLES-AFTER-NEXT: depth=1: entries(callbr_header) callbr_regular mid callbr_header.target.mid callbr_header.target.callbr_regular irr.guard ; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard) callbr_regular mid ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_only ; CYCLES-BEFORE-NEXT: depth=1: entries(callbr_block callbr_header) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_only ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) callbr_block callbr_header callbr_header.target.callbr_block ; CYCLES-BEFORE-NEXT: CycleInfo for function: entry_multiple_callbr ; CYCLES-BEFORE-NEXT: depth=1: entries(cb2 block block1) ; CYCLES-BEFORE-NEXT: depth=2: entries(block block1) ; CYCLES-AFTER-NEXT: CycleInfo for function: entry_multiple_callbr ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) cb2 block block1 irr.guard1 cb2.target.block1 cb2.target.block irr.guard2 ; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) block block1 ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_exit_with_separate_entries ; CYCLES-BEFORE-NEXT: depth=1: entries(l2 l1) cb ; CYCLES-BEFORE-NEXT: depth=2: entries(l1 cb) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_exit_with_separate_entries ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l2 l1 cb cb.target.l1 irr.guard1 ; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard1) l1 cb cb.target.l1 ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_exit_with_separate_entries1 ; CYCLES-BEFORE-NEXT: depth=1: entries(loop2 loop1) cb ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_exit_with_separate_entries1 ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) loop2 loop1 cb cb.target.loop2 ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_only_multiple ; CYCLES-BEFORE-NEXT: depth=1: entries(cb3 cb1 cb2) ; CYCLES-BEFORE-NEXT: depth=2: entries(cb1 cb2) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_only_multiple ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) cb3 cb1 cb2 cb2.target.cb3 cb1.target.cb3 irr.guard1 cb2.target.cb1 cb3.target.cb1 irr.guard2 ; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) cb1 cb2 cb2.target.cb1 ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_bypass ; CYCLES-BEFORE-NEXT: depth=1: entries(l1 cb) l2 ; CYCLES-BEFORE-NEXT: depth=2: entries(cb l2) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_bypass ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l1 cb l2 cb.target.l1 irr.guard1 ; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard1) cb l2 ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_multiple_with_exit ; CYCLES-BEFORE-NEXT: depth=1: entries(l3 l1 l2) ; CYCLES-BEFORE-NEXT: depth=2: entries(l1 l2) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_multiple_with_exit ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) l3 l1 l2 irr.guard1 irr.guard2 ; CYCLES-AFTER-NEXT: depth=2: entries(irr.guard2) l1 l2 ; CYCLES-BEFORE-NEXT: CycleInfo for function: callbr_nested ; CYCLES-BEFORE-NEXT: depth=1: entries(bb bh) ; CYCLES-BEFORE-NEXT: depth=1: entries(b h) ; CYCLES-AFTER-NEXT: CycleInfo for function: callbr_nested ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard) bb bh ; CYCLES-AFTER-NEXT: depth=1: entries(irr.guard1) b h ; Fix the irreducible loop in which callbr is the entry (see description at the ; top of FixIrreducible.cpp). define void @callbr_entry(i1 %c) { ; CHECK-LABEL: define void @callbr_entry( ; CHECK-SAME: i1 [[C:%.*]]) { ; CHECK-NEXT: [[CALLBR:.*:]] ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CALLBR_TARGET_FALLTHROUGH:.*]] [label %callbr.target.indirect] ; CHECK: [[FALLTHROUGH:.*]]: ; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD:.*]], label %[[RET:.*]] ; CHECK: [[INDIRECT:.*]]: ; CHECK-NEXT: br label %[[FALLTHROUGH]] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[CALLBR_TARGET_FALLTHROUGH]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CALLBR_TARGET_INDIRECT:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_INDIRECT:%.*]] = phi i1 [ true, %[[FALLTHROUGH]] ], [ false, %[[CALLBR_TARGET_FALLTHROUGH]] ], [ true, %[[CALLBR_TARGET_INDIRECT]] ] ; CHECK-NEXT: br i1 [[GUARD_INDIRECT]], label %[[INDIRECT]], label %[[FALLTHROUGH]] ; callbr: callbr void asm "", "!i"() to label %fallthrough [label %indirect] fallthrough: br i1 %c, label %indirect, label %ret indirect: br label %fallthrough ret: ret void } define i32 @callbr_entry_targets_with_phi_nodes(i1 %c) { ; CHECK-LABEL: define i32 @callbr_entry_targets_with_phi_nodes( ; CHECK-SAME: i1 [[C:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[ENTRY_TARGET_BLOCK:.*]] [label %entry.target.block1] ; CHECK: [[BLOCK:.*]]: ; CHECK-NEXT: [[A:%.*]] = phi i32 [ 1, %[[BLOCK1:.*]] ], [ [[A_MOVED:%.*]], %[[IRR_GUARD:.*]] ] ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[BLOCK1]]: ; CHECK-NEXT: br i1 [[C]], label %[[BLOCK]], label %[[RET:.*]] ; CHECK: [[RET]]: ; CHECK-NEXT: ret i32 [[B_MOVED:%.*]] ; CHECK: [[ENTRY_TARGET_BLOCK]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[ENTRY_TARGET_BLOCK1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[A_MOVED]] = phi i32 [ poison, %[[BLOCK]] ], [ 42, %[[ENTRY_TARGET_BLOCK]] ], [ poison, %[[ENTRY_TARGET_BLOCK1]] ] ; CHECK-NEXT: [[B_MOVED]] = phi i32 [ [[A]], %[[BLOCK]] ], [ poison, %[[ENTRY_TARGET_BLOCK]] ], [ 43, %[[ENTRY_TARGET_BLOCK1]] ] ; CHECK-NEXT: [[GUARD_BLOCK1:%.*]] = phi i1 [ true, %[[BLOCK]] ], [ false, %[[ENTRY_TARGET_BLOCK]] ], [ true, %[[ENTRY_TARGET_BLOCK1]] ] ; CHECK-NEXT: br i1 [[GUARD_BLOCK1]], label %[[BLOCK1]], label %[[BLOCK]] ; entry: callbr void asm "", "!i"() to label %block [label %block1] block: %a = phi i32 [42, %entry], [1, %block1] br label %block1 block1: %b = phi i32 [43, %entry], [%a, %block] br i1 %c, label %block, label %ret ret: ret i32 %b } define void @callbr_entry_multiple_indirect_targets(i1 %c) { ; CHECK-LABEL: define void @callbr_entry_multiple_indirect_targets( ; CHECK-SAME: i1 [[C:%.*]]) { ; CHECK-NEXT: [[CALLBR:.*:]] ; CHECK-NEXT: callbr void asm "", "!i,!i,!i"() ; CHECK-NEXT: to label %[[CALLBR_TARGET_FALLTHROUGH:.*]] [label %[[CALLBR_TARGET_INDIRECT:.*]], label %[[INDIRECT1:.*]], label %indirect2] ; CHECK: [[INDIRECT3:.*]]: ; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD:.*]], label %[[RET:.*]] ; CHECK: [[INDIRECT:.*]]: ; CHECK-NEXT: br label %[[INDIRECT3]] ; CHECK: [[INDIRECT1]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[INDIRECT2:.*:]] ; CHECK-NEXT: br label %[[RET]] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[CALLBR_TARGET_FALLTHROUGH]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CALLBR_TARGET_INDIRECT]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_INDIRECT:%.*]] = phi i1 [ true, %[[INDIRECT3]] ], [ true, %[[INDIRECT1]] ], [ false, %[[CALLBR_TARGET_FALLTHROUGH]] ], [ true, %[[CALLBR_TARGET_INDIRECT]] ] ; CHECK-NEXT: br i1 [[GUARD_INDIRECT]], label %[[INDIRECT]], label %[[INDIRECT3]] ; callbr: callbr void asm "", "!i,!i,!i"() to label %fallthrough [label %indirect, label %indirect1, label %indirect2] fallthrough: br i1 %c, label %indirect, label %ret indirect: br label %fallthrough indirect1: br label %indirect indirect2: br label %ret ret: ret void } define void @callbr_entry_multiple_indirect_targets1(i1 %c, i1 %d) { ; CHECK-LABEL: define void @callbr_entry_multiple_indirect_targets1( ; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]]) { ; CHECK-NEXT: [[CALLBR:.*:]] ; CHECK-NEXT: callbr void asm "", "!i,!i,!i"() ; CHECK-NEXT: to label %[[CALLBR_TARGET_FALLTHROUGH:.*]] [label %[[CALLBR_TARGET_INDIRECT:.*]], label %[[CALLBR_TARGET_INDIRECT1:.*]], label %indirect2] ; CHECK: [[INDIRECT3:.*]]: ; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD2:.*]], label %[[RET:.*]] ; CHECK: [[INDIRECT:.*]]: ; CHECK-NEXT: br i1 [[D]], label %[[INDIRECT3]], label %[[IRR_GUARD:.*]] ; CHECK: [[INDIRECT1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD2]] ; CHECK: [[INDIRECT2:.*:]] ; CHECK-NEXT: br label %[[RET]] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[CALLBR_TARGET_FALLTHROUGH]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CALLBR_TARGET_INDIRECT]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CALLBR_TARGET_INDIRECT1]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_INDIRECT1:%.*]] = phi i1 [ true, %[[INDIRECT]] ], [ false, %[[CALLBR_TARGET_FALLTHROUGH]] ], [ false, %[[CALLBR_TARGET_INDIRECT]] ], [ true, %[[CALLBR_TARGET_INDIRECT1]] ] ; CHECK-NEXT: [[GUARD_FALLTHROUGH:%.*]] = phi i1 [ false, %[[INDIRECT]] ], [ true, %[[CALLBR_TARGET_FALLTHROUGH]] ], [ false, %[[CALLBR_TARGET_INDIRECT]] ], [ false, %[[CALLBR_TARGET_INDIRECT1]] ] ; CHECK-NEXT: [[GUARD_FALLTHROUGH_INV:%.*]] = xor i1 [[GUARD_FALLTHROUGH]], true ; CHECK-NEXT: br i1 [[GUARD_INDIRECT1]], label %[[INDIRECT1]], label %[[IRR_GUARD1:.*]] ; CHECK: [[IRR_GUARD1]]: ; CHECK-NEXT: br label %[[IRR_GUARD2]] ; CHECK: [[IRR_GUARD2]]: ; CHECK-NEXT: [[GUARD_INDIRECT:%.*]] = phi i1 [ true, %[[INDIRECT3]] ], [ [[GUARD_FALLTHROUGH_INV]], %[[IRR_GUARD1]] ], [ true, %[[INDIRECT1]] ] ; CHECK-NEXT: br i1 [[GUARD_INDIRECT]], label %[[INDIRECT]], label %[[INDIRECT3]] ; callbr: callbr void asm "", "!i,!i,!i"() to label %fallthrough [label %indirect, label %indirect1, label %indirect2] fallthrough: br i1 %c, label %indirect, label %ret indirect: br i1 %d, label %fallthrough, label %indirect1 indirect1: br label %indirect indirect2: br label %ret ret: ret void } ; Fix the irreducible loop in which callbr is the header (see the example at the ; top of FixIrreducible.cpp). define void @callbr_header_no_indirect(i1 %c, i1 %d) { ; CHECK-LABEL: define void @callbr_header_no_indirect( ; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]]) { ; CHECK-NEXT: [[D_INV:%.*]] = xor i1 [[D]], true ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[CALLBR:.*]]: ; CHECK-NEXT: callbr void asm "", ""() ; CHECK-NEXT: to label %[[CALLBR_TARGET_FALLTHROUGH:.*]] [] ; CHECK: [[FALLTHROUGH:.*]]: ; CHECK-NEXT: br i1 [[C]], label %[[CALLBR]], label %[[RET:.*]] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[CALLBR_TARGET_FALLTHROUGH]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_FALLTHROUGH:%.*]] = phi i1 [ true, %[[CALLBR_TARGET_FALLTHROUGH]] ], [ [[D_INV]], [[TMP0:%.*]] ] ; CHECK-NEXT: br i1 [[GUARD_FALLTHROUGH]], label %[[FALLTHROUGH]], label %[[CALLBR]] ; br i1 %d, label %callbr, label %fallthrough callbr: callbr void asm "", ""() to label %fallthrough [] fallthrough: br i1 %c, label %callbr, label %ret ret: ret void } ; Fix the irreducible loop in which callbr is the header. define void @callbr_header(i1 %c, i1 %d) { ; CHECK-LABEL: define void @callbr_header( ; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]]) { ; CHECK-NEXT: [[D_INV:%.*]] = xor i1 [[D]], true ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[CALLBR:.*]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CALLBR_TARGET_FALLTHROUGH:.*]] [label %indirect] ; CHECK: [[INDIRECT:.*:]] ; CHECK-NEXT: br label %[[RET:.*]] ; CHECK: [[FALLTHROUGH:.*]]: ; CHECK-NEXT: br i1 [[C]], label %[[CALLBR]], label %[[RET]] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[CALLBR_TARGET_FALLTHROUGH]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_FALLTHROUGH:%.*]] = phi i1 [ true, %[[CALLBR_TARGET_FALLTHROUGH]] ], [ [[D_INV]], [[TMP0:%.*]] ] ; CHECK-NEXT: br i1 [[GUARD_FALLTHROUGH]], label %[[FALLTHROUGH]], label %[[CALLBR]] ; br i1 %d, label %callbr, label %fallthrough callbr: callbr void asm "", "!i"() to label %fallthrough [label %indirect] indirect: br label %ret fallthrough: br i1 %c, label %callbr, label %ret ret: ret void } define void @callbr_header_multiple_indirect_targets(i1 %c, i1 %d) { ; CHECK-LABEL: define void @callbr_header_multiple_indirect_targets( ; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]]) { ; CHECK-NEXT: [[D_INV:%.*]] = xor i1 [[D]], true ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[CALLBR:.*]]: ; CHECK-NEXT: callbr void asm "", "!i,!i"() ; CHECK-NEXT: to label %[[CALLBR_TARGET_FALLTHROUGH:.*]] [label %[[INDIRECT1:.*]], label %indirect1] ; CHECK: [[INDIRECT1]]: ; CHECK-NEXT: br label %[[RET:.*]] ; CHECK: [[INDIRECT2:.*:]] ; CHECK-NEXT: br label %[[CALLBR]] ; CHECK: [[FALLTHROUGH:.*]]: ; CHECK-NEXT: br i1 [[C]], label %[[CALLBR]], label %[[RET]] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[CALLBR_TARGET_FALLTHROUGH]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_FALLTHROUGH:%.*]] = phi i1 [ true, %[[CALLBR_TARGET_FALLTHROUGH]] ], [ [[D_INV]], [[TMP0:%.*]] ] ; CHECK-NEXT: br i1 [[GUARD_FALLTHROUGH]], label %[[FALLTHROUGH]], label %[[CALLBR]] ; br i1 %d, label %callbr, label %fallthrough callbr: callbr void asm "", "!i,!i"() to label %fallthrough [label %indirect, label %indirect1] indirect: br label %ret indirect1: br label %callbr fallthrough: br i1 %c, label %callbr, label %ret ret: ret void } ; Fix the three usual irreducible loops (callbr isn't a part of one of them): ; - fallthrough, fallthrough1, fallthrough2 ; - indirect, indirect1, indirect2 ; - nocallbr, nocallbr1, nocallbr2 define void @callbr_regular(i1 %c, i1 %d) { ; CHECK-LABEL: define void @callbr_regular( ; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]]) { ; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true ; CHECK-NEXT: br i1 [[D]], label %[[CALLBR:.*]], label %[[NOCALLBR:.*]] ; CHECK: [[CALLBR]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[FALLTHROUGH:.*]] [label %indirect] ; CHECK: [[FALLTHROUGH]]: ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[FALLTHROUGH1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[FALLTHROUGH2:.*]]: ; CHECK-NEXT: br i1 [[D]], label %[[FALLTHROUGH1]], label %[[RET:.*]] ; CHECK: [[INDIRECT:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD1:.*]] ; CHECK: [[INDIRECT1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD1]] ; CHECK: [[INDIRECT2:.*]]: ; CHECK-NEXT: br i1 [[D]], label %[[INDIRECT1]], label %[[RET]] ; CHECK: [[NOCALLBR]]: ; CHECK-NEXT: br label %[[IRR_GUARD2:.*]] ; CHECK: [[NOCALLBR1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD2]] ; CHECK: [[NOCALLBR2:.*]]: ; CHECK-NEXT: br i1 [[D]], label %[[NOCALLBR1]], label %[[RET]] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_FALLTHROUGH2:%.*]] = phi i1 [ true, %[[FALLTHROUGH1]] ], [ [[C_INV]], %[[FALLTHROUGH]] ] ; CHECK-NEXT: br i1 [[GUARD_FALLTHROUGH2]], label %[[FALLTHROUGH2]], label %[[FALLTHROUGH1]] ; CHECK: [[IRR_GUARD1]]: ; CHECK-NEXT: [[GUARD_INDIRECT2:%.*]] = phi i1 [ true, %[[INDIRECT1]] ], [ [[C_INV]], %[[INDIRECT]] ] ; CHECK-NEXT: br i1 [[GUARD_INDIRECT2]], label %[[INDIRECT2]], label %[[INDIRECT1]] ; CHECK: [[IRR_GUARD2]]: ; CHECK-NEXT: [[GUARD_NOCALLBR2:%.*]] = phi i1 [ true, %[[NOCALLBR1]] ], [ [[C_INV]], %[[NOCALLBR]] ] ; CHECK-NEXT: br i1 [[GUARD_NOCALLBR2]], label %[[NOCALLBR2]], label %[[NOCALLBR1]] ; br i1 %d, label %callbr, label %nocallbr callbr: callbr void asm "", "!i"() to label %fallthrough [label %indirect] fallthrough: br i1 %c, label %fallthrough1, label %fallthrough2 fallthrough1: br label %fallthrough2 fallthrough2: br i1 %d, label %fallthrough1, label %ret indirect: br i1 %c, label %indirect1, label %indirect2 indirect1: br label %indirect2 indirect2: br i1 %d, label %indirect1, label %ret nocallbr: br i1 %c, label %nocallbr1, label %nocallbr2 nocallbr1: br label %nocallbr2 nocallbr2: br i1 %d, label %nocallbr1, label %ret ret: ret void } ; Fix an irreducible loop in which callbr is a regular block (neither entry nor ; header). See the example at the top of FixIrreducible.cpp. define void @callbr_regular1(i1 %c) { ; CHECK-LABEL: define void @callbr_regular1( ; CHECK-SAME: i1 [[C:%.*]]) { ; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[NOCALLBR:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CALLBR:.*]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[RET:.*]] [label %nocallbr] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_CALLBR:%.*]] = phi i1 [ true, %[[NOCALLBR]] ], [ [[C_INV]], [[TMP0:%.*]] ] ; CHECK-NEXT: br i1 [[GUARD_CALLBR]], label %[[CALLBR]], label %[[NOCALLBR]] ; br i1 %c, label %nocallbr, label %callbr nocallbr: br label %callbr callbr: callbr void asm "", "!i"() to label %ret [label %nocallbr] ret: ret void } ; Fix an irreducible loop in which callbr is a regular block (neither entry nor ; header). See the example at the top of FixIrreducible.cpp. define void @callbr_regular2(i1 %c) { ; CHECK-LABEL: define void @callbr_regular2( ; CHECK-SAME: i1 [[C:%.*]]) { ; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[NOCALLBR:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CALLBR:.*]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[NOCALLBR]] [label %ret] ; CHECK: [[RET:.*:]] ; CHECK-NEXT: ret void ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_CALLBR:%.*]] = phi i1 [ true, %[[NOCALLBR]] ], [ [[C_INV]], [[TMP0:%.*]] ] ; CHECK-NEXT: br i1 [[GUARD_CALLBR]], label %[[CALLBR]], label %[[NOCALLBR]] ; br i1 %c, label %nocallbr, label %callbr nocallbr: br label %callbr callbr: callbr void asm "", "!i"() to label %nocallbr [label %ret] ret: ret void } ; Fix an irreducible loop with two callbr blocks, one as header and one as regular block. define void @callbr_header_and_regular(i1 %c) { ; CHECK-LABEL: define void @callbr_header_and_regular( ; CHECK-SAME: i1 [[C:%.*]]) { ; CHECK-NEXT: br label %[[CALLBR_HEADER:.*]] ; CHECK: [[CALLBR_HEADER]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CALLBR_HEADER_TARGET_MID:.*]] [label %callbr_header.target.callbr_regular] ; CHECK: [[MID:.*]]: ; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD:.*]], label %[[RET:.*]] ; CHECK: [[CALLBR_REGULAR:.*]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CALLBR_HEADER]] [label %mid] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[CALLBR_HEADER_TARGET_MID]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CALLBR_HEADER_TARGET_CALLBR_REGULAR:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_CALLBR_REGULAR:%.*]] = phi i1 [ true, %[[MID]] ], [ false, %[[CALLBR_HEADER_TARGET_MID]] ], [ true, %[[CALLBR_HEADER_TARGET_CALLBR_REGULAR]] ] ; CHECK-NEXT: br i1 [[GUARD_CALLBR_REGULAR]], label %[[CALLBR_REGULAR]], label %[[MID]] ; br label %callbr_header callbr_header: callbr void asm "", "!i"() to label %mid [label %callbr_regular] mid: br i1 %c, label %callbr_regular, label %ret callbr_regular: callbr void asm "", "!i"() to label %callbr_header [label %mid] ret: ret void } ; Fix an irreducible loop consisting only of callbr blocks (and ret). See the ; example at the top of FixIrreducible.cpp. define void @callbr_only(i1 %c) { ; CHECK-LABEL: define void @callbr_only( ; CHECK-SAME: i1 [[C:%.*]]) { ; CHECK-NEXT: [[CALLBR:.*:]] ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CALLBR_ENTRY_TARGET_CALLBR_HEADER:.*]] [label %callbr_entry.target.callbr_block] ; CHECK: [[CALLBR_HEADER:.*]]: ; CHECK-NEXT: callbr void asm "", ""() ; CHECK-NEXT: to label %[[CALLBR_HEADER_TARGET_CALLBR_BLOCK:.*]] [] ; CHECK: [[CALLBR_BLOCK:.*]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CALLBR_HEADER]] [label %ret] ; CHECK: [[RET:.*:]] ; CHECK-NEXT: ret void ; CHECK: [[CALLBR_HEADER_TARGET_CALLBR_BLOCK]]: ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[CALLBR_ENTRY_TARGET_CALLBR_HEADER]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CALLBR_ENTRY_TARGET_CALLBR_BLOCK:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_CALLBR_BLOCK:%.*]] = phi i1 [ true, %[[CALLBR_HEADER_TARGET_CALLBR_BLOCK]] ], [ false, %[[CALLBR_ENTRY_TARGET_CALLBR_HEADER]] ], [ true, %[[CALLBR_ENTRY_TARGET_CALLBR_BLOCK]] ] ; CHECK-NEXT: br i1 [[GUARD_CALLBR_BLOCK]], label %[[CALLBR_BLOCK]], label %[[CALLBR_HEADER]] ; callbr_entry: callbr void asm "", "!i"() to label %callbr_header [label %callbr_block] callbr_header: callbr void asm "", ""() to label %callbr_block [] callbr_block: callbr void asm "", "!i"() to label %callbr_header [label %ret] ret: ret void } ; Irreducible loop: entry leading to multiple callbr blocks. define void @entry_multiple_callbr(i1 %a, i1 %b, i1 %c) { ; CHECK-LABEL: define void @entry_multiple_callbr( ; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br i1 [[A]], label %[[CB1:.*]], label %[[IRR_GUARD:.*]] ; CHECK: [[CB1]]: ; CHECK-NEXT: callbr void asm "", "!i,!i"() ; CHECK-NEXT: to label %[[CB1_TARGET_BLOCK:.*]] [label %[[CB1_TARGET_CB2:.*]], label %cb1.target.block1] ; CHECK: [[BLOCK:.*]]: ; CHECK-NEXT: br i1 [[B]], label %[[IRR_GUARD]], label %[[BLOCK1:.*]] ; CHECK: [[CB2:.*]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CB2_TARGET_BLOCK1:.*]] [label %cb2.target.block] ; CHECK: [[BLOCK1]]: ; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD2:.*]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; CHECK: [[CB1_TARGET_BLOCK]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CB1_TARGET_CB2]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[CB1_TARGET_BLOCK1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_CB2:%.*]] = phi i1 [ true, %[[BLOCK]] ], [ false, %[[CB1_TARGET_BLOCK]] ], [ true, %[[CB1_TARGET_CB2]] ], [ false, %[[CB1_TARGET_BLOCK1]] ], [ true, %[[ENTRY]] ] ; CHECK-NEXT: [[GUARD_BLOCK:%.*]] = phi i1 [ false, %[[BLOCK]] ], [ true, %[[CB1_TARGET_BLOCK]] ], [ false, %[[CB1_TARGET_CB2]] ], [ false, %[[CB1_TARGET_BLOCK1]] ], [ false, %[[ENTRY]] ] ; CHECK-NEXT: br i1 [[GUARD_CB2]], label %[[CB2]], label %[[IRR_GUARD1:.*]] ; CHECK: [[IRR_GUARD1]]: ; CHECK-NEXT: br label %[[IRR_GUARD2]] ; CHECK: [[CB2_TARGET_BLOCK1]]: ; CHECK-NEXT: br label %[[IRR_GUARD2]] ; CHECK: [[CB2_TARGET_BLOCK:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD2]] ; CHECK: [[IRR_GUARD2]]: ; CHECK-NEXT: [[GUARD_BLOCK3:%.*]] = phi i1 [ true, %[[BLOCK1]] ], [ [[GUARD_BLOCK]], %[[IRR_GUARD1]] ], [ false, %[[CB2_TARGET_BLOCK1]] ], [ true, %[[CB2_TARGET_BLOCK]] ] ; CHECK-NEXT: br i1 [[GUARD_BLOCK3]], label %[[BLOCK]], label %[[BLOCK1]] ; entry: br i1 %a, label %cb1, label %cb2 cb1: callbr void asm "", "!i,!i"() to label %block [label %cb2, label %block1] block: br i1 %b, label %cb2, label %block1 cb2: callbr void asm "", "!i"() to label %block1 [label %block] block1: br i1 %c, label %block, label %exit exit: ret void } ; Irreducible loop: callbr as loop exit, with multiple entries define void @callbr_exit_with_separate_entries(i1 %a, i1 %b, i1 %c) { ; CHECK-LABEL: define void @callbr_exit_with_separate_entries( ; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true ; CHECK-NEXT: [[A_INV:%.*]] = xor i1 [[A]], true ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[L1:.*]]: ; CHECK-NEXT: br i1 [[B]], label %[[CB:.*]], label %[[IRR_GUARD]] ; CHECK: [[L2:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD1:.*]] ; CHECK: [[CB]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[EXIT:.*]] [label %cb.target.l1] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_L2:%.*]] = phi i1 [ true, %[[L1]] ], [ [[A_INV]], %[[ENTRY]] ] ; CHECK-NEXT: br i1 [[GUARD_L2]], label %[[L2]], label %[[IRR_GUARD1]] ; CHECK: [[CB_TARGET_L1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD1]] ; CHECK: [[IRR_GUARD1]]: ; CHECK-NEXT: [[GUARD_L1:%.*]] = phi i1 [ true, %[[CB_TARGET_L1]] ], [ true, %[[IRR_GUARD]] ], [ [[C_INV]], %[[L2]] ] ; CHECK-NEXT: br i1 [[GUARD_L1]], label %[[L1]], label %[[CB]] ; entry: br i1 %a, label %l1, label %l2 l1: br i1 %b, label %cb, label %l2 l2: br i1 %c, label %cb, label %l1 cb: callbr void asm "", "!i"() to label %exit [label %l1] exit: ret void } define void @callbr_exit_with_separate_entries1(i1 %a, i1 %b) { ; CHECK-LABEL: define void @callbr_exit_with_separate_entries1( ; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[A_INV:%.*]] = xor i1 [[A]], true ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[LOOP1:.*]]: ; CHECK-NEXT: br i1 [[B]], label %[[CB:.*]], label %[[IRR_GUARD]] ; CHECK: [[LOOP2:.*]]: ; CHECK-NEXT: br label %[[LOOP1]] ; CHECK: [[CB]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[EXIT:.*]] [label %cb.target.loop2] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; CHECK: [[CB_TARGET_LOOP2:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_LOOP2:%.*]] = phi i1 [ true, %[[CB_TARGET_LOOP2]] ], [ true, %[[LOOP1]] ], [ [[A_INV]], %[[ENTRY]] ] ; CHECK-NEXT: br i1 [[GUARD_LOOP2]], label %[[LOOP2]], label %[[LOOP1]] ; entry: br i1 %a, label %loop1, label %loop2 loop1: br i1 %b, label %cb, label %loop2 loop2: br label %loop1 cb: callbr void asm "", "!i"() to label %exit [label %loop2] exit: ret void } ; Irreducible loop: all blocks are callbrs, with cross-edges define void @callbr_only_multiple(i1 %a, i1 %b, i1 %c) { ; CHECK-LABEL: define void @callbr_only_multiple( ; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: callbr void asm "", "!i,!i"() ; CHECK-NEXT: to label %[[ENTRY_TARGET_CB1:.*]] [label %[[ENTRY_TARGET_CB2:.*]], label %entry.target.cb3] ; CHECK: [[CB1:.*]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CB2:.*]] [label %cb1.target.cb3] ; CHECK: [[CB2]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CB2_TARGET_CB3:.*]] [label %cb2.target.cb1] ; CHECK: [[CB3:.*]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[CB3_TARGET_CB1:.*]] [label %exit] ; CHECK: [[EXIT:.*:]] ; CHECK-NEXT: ret void ; CHECK: [[CB2_TARGET_CB3]]: ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[CB1_TARGET_CB3:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[ENTRY_TARGET_CB1]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[ENTRY_TARGET_CB2]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[ENTRY_TARGET_CB3:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_CB3:%.*]] = phi i1 [ true, %[[CB2_TARGET_CB3]] ], [ true, %[[CB1_TARGET_CB3]] ], [ false, %[[ENTRY_TARGET_CB1]] ], [ false, %[[ENTRY_TARGET_CB2]] ], [ true, %[[ENTRY_TARGET_CB3]] ] ; CHECK-NEXT: [[GUARD_CB1:%.*]] = phi i1 [ false, %[[CB2_TARGET_CB3]] ], [ false, %[[CB1_TARGET_CB3]] ], [ true, %[[ENTRY_TARGET_CB1]] ], [ false, %[[ENTRY_TARGET_CB2]] ], [ false, %[[ENTRY_TARGET_CB3]] ] ; CHECK-NEXT: br i1 [[GUARD_CB3]], label %[[CB3]], label %[[IRR_GUARD1:.*]] ; CHECK: [[IRR_GUARD1]]: ; CHECK-NEXT: br label %[[IRR_GUARD2:.*]] ; CHECK: [[CB2_TARGET_CB1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD2]] ; CHECK: [[CB3_TARGET_CB1]]: ; CHECK-NEXT: br label %[[IRR_GUARD2]] ; CHECK: [[IRR_GUARD2]]: ; CHECK-NEXT: [[GUARD_CB13:%.*]] = phi i1 [ true, %[[CB2_TARGET_CB1]] ], [ [[GUARD_CB1]], %[[IRR_GUARD1]] ], [ true, %[[CB3_TARGET_CB1]] ] ; CHECK-NEXT: br i1 [[GUARD_CB13]], label %[[CB1]], label %[[CB2]] ; entry: callbr void asm "", "!i,!i"() to label %cb1 [label %cb2, label %cb3] cb1: callbr void asm "", "!i"() to label %cb2 [label %cb3] cb2: callbr void asm "", "!i"() to label %cb3 [label %cb1] cb3: callbr void asm "", "!i"() to label %cb1 [label %exit] exit: ret void } ; Irreducible loop: callbr as a "bypass" block define void @callbr_bypass(i1 %a, i1 %b, i1 %c) { ; CHECK-LABEL: define void @callbr_bypass( ; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[B_INV:%.*]] = xor i1 [[B]], true ; CHECK-NEXT: [[A_INV:%.*]] = xor i1 [[A]], true ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[CB:.*]]: ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[L2:.*]] [label %cb.target.l1] ; CHECK: [[L1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD1:.*]] ; CHECK: [[L2]]: ; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD1]], label %[[EXIT:.*]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; CHECK: [[CB_TARGET_L1:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_L1:%.*]] = phi i1 [ true, %[[CB_TARGET_L1]] ], [ [[A_INV]], %[[ENTRY]] ] ; CHECK-NEXT: br i1 [[GUARD_L1]], label %[[L1]], label %[[IRR_GUARD1]] ; CHECK: [[IRR_GUARD1]]: ; CHECK-NEXT: [[GUARD_CB:%.*]] = phi i1 [ true, %[[L2]] ], [ true, %[[IRR_GUARD]] ], [ [[B_INV]], %[[L1]] ] ; CHECK-NEXT: br i1 [[GUARD_CB]], label %[[CB]], label %[[L2]] ; entry: br i1 %a, label %cb, label %l1 cb: callbr void asm "", "!i"() to label %l2 [label %l1] l1: br i1 %b, label %l2, label %cb l2: br i1 %c, label %cb, label %exit exit: ret void } ; Irreducible loop: callbr with multiple indirect targets, some looping, some exiting define void @callbr_multiple_with_exit(i1 %a, i1 %b, i1 %c) { ; CHECK-LABEL: define void @callbr_multiple_with_exit( ; CHECK-SAME: i1 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: callbr void asm "", "!i,!i,!i"() ; CHECK-NEXT: to label %[[ENTRY_TARGET_L1:.*]] [label %[[ENTRY_TARGET_L2:.*]], label %[[EXIT:.*]], label %entry.target.l3] ; CHECK: [[L1:.*]]: ; CHECK-NEXT: br i1 [[A]], label %[[L2:.*]], label %[[IRR_GUARD:.*]] ; CHECK: [[L2]]: ; CHECK-NEXT: br i1 [[B]], label %[[IRR_GUARD2:.*]], label %[[EXIT]] ; CHECK: [[L3:.*]]: ; CHECK-NEXT: br i1 [[C]], label %[[IRR_GUARD2]], label %[[EXIT]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; CHECK: [[ENTRY_TARGET_L1]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[ENTRY_TARGET_L2]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[ENTRY_TARGET_L3:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_L3:%.*]] = phi i1 [ true, %[[L1]] ], [ false, %[[ENTRY_TARGET_L1]] ], [ false, %[[ENTRY_TARGET_L2]] ], [ true, %[[ENTRY_TARGET_L3]] ] ; CHECK-NEXT: [[GUARD_L1:%.*]] = phi i1 [ false, %[[L1]] ], [ true, %[[ENTRY_TARGET_L1]] ], [ false, %[[ENTRY_TARGET_L2]] ], [ false, %[[ENTRY_TARGET_L3]] ] ; CHECK-NEXT: br i1 [[GUARD_L3]], label %[[L3]], label %[[IRR_GUARD1:.*]] ; CHECK: [[IRR_GUARD1]]: ; CHECK-NEXT: br label %[[IRR_GUARD2]] ; CHECK: [[IRR_GUARD2]]: ; CHECK-NEXT: [[GUARD_L13:%.*]] = phi i1 [ true, %[[L2]] ], [ [[GUARD_L1]], %[[IRR_GUARD1]] ], [ true, %[[L3]] ] ; CHECK-NEXT: br i1 [[GUARD_L13]], label %[[L1]], label %[[L2]] ; entry: callbr void asm "", "!i,!i,!i"() to label %l1 [label %l2, label %exit, label %l3] l1: br i1 %a, label %l2, label %l3 l2: br i1 %b, label %l1, label %exit l3: br i1 %c, label %l1, label %exit exit: ret void } define void @callbr_nested(i1 %c, i1 %d) { ; CHECK-LABEL: define void @callbr_nested( ; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: callbr void asm "", "!i"() ; CHECK-NEXT: to label %[[ENTRY_TARGET_H:.*]] [label %entry.target.b] ; CHECK: [[H:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD1:.*]] ; CHECK: [[B:.*]]: ; CHECK-NEXT: callbr void asm "", "!i,!i"() ; CHECK-NEXT: to label %[[H]] [label %[[B_TARGET_BH:.*]], label %b.target.bb] ; CHECK: [[BH:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD:.*]] ; CHECK: [[BB:.*]]: ; CHECK-NEXT: br i1 [[C]], label %[[BH]], label %[[RET:.*]] ; CHECK: [[RET]]: ; CHECK-NEXT: ret void ; CHECK: [[B_TARGET_BH]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[B_TARGET_BB:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD]] ; CHECK: [[IRR_GUARD]]: ; CHECK-NEXT: [[GUARD_BB:%.*]] = phi i1 [ true, %[[BH]] ], [ false, %[[B_TARGET_BH]] ], [ true, %[[B_TARGET_BB]] ] ; CHECK-NEXT: br i1 [[GUARD_BB]], label %[[BB]], label %[[BH]] ; CHECK: [[ENTRY_TARGET_H]]: ; CHECK-NEXT: br label %[[IRR_GUARD1]] ; CHECK: [[ENTRY_TARGET_B:.*]]: ; CHECK-NEXT: br label %[[IRR_GUARD1]] ; CHECK: [[IRR_GUARD1]]: ; CHECK-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, %[[H]] ], [ false, %[[ENTRY_TARGET_H]] ], [ true, %[[ENTRY_TARGET_B]] ] ; CHECK-NEXT: br i1 [[GUARD_B]], label %[[B]], label %[[H]] ; entry: callbr void asm "","!i"() to label %h [label %b] h: br label %b b: callbr void asm "","!i,!i"() to label %h [label %bh, label %bb] bh: br label %bb bb: br i1 %c, label %bh, label %ret ret: ret void } ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: ; LOOPS-AFTER: {{.*}} ; LOOPS-BEFORE: {{.*}}