1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 2; RUN: opt < %s -passes=unify-loop-exits -max-booleans-in-control-flow-hub=1 -S | FileCheck %s 3; RUN: opt < %s -passes=unify-loop-exits -S | FileCheck --check-prefix=BOOLEAN %s 4 5; A loop with multiple exit blocks. 6 7define void @loop_two_exits(i1 %PredEntry, i1 %PredA) { 8; CHECK-LABEL: @loop_two_exits( 9; CHECK-NEXT: entry: 10; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[E:%.*]] 11; CHECK: A: 12; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ] 13; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C]] 14; CHECK: B: 15; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0:[0-9]+]] 16; CHECK-NEXT: br label [[D:%.*]] 17; CHECK: C: 18; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1 19; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10 20; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] 21; CHECK: D: 22; CHECK-NEXT: unreachable 23; CHECK: E: 24; CHECK-NEXT: ret void 25; CHECK: loop.exit.guard: 26; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A]] ], [ 1, [[C]] ] 27; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0 28; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[E]] 29; 30; BOOLEAN-LABEL: @loop_two_exits( 31; BOOLEAN-NEXT: entry: 32; BOOLEAN-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[E:%.*]] 33; BOOLEAN: A: 34; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ] 35; BOOLEAN-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C]] 36; BOOLEAN: B: 37; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0:[0-9]+]] 38; BOOLEAN-NEXT: br label [[D:%.*]] 39; BOOLEAN: C: 40; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1 41; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10 42; BOOLEAN-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] 43; BOOLEAN: D: 44; BOOLEAN-NEXT: unreachable 45; BOOLEAN: E: 46; BOOLEAN-NEXT: ret void 47; BOOLEAN: loop.exit.guard: 48; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ] 49; BOOLEAN-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[E]] 50; 51entry: 52 br i1 %PredEntry, label %A, label %E 53 54A: 55 %inc1 = phi i32 [ 0, %entry ], [ %inc2, %C ] 56 br i1 %PredA, label %B, label %C 57 58B: 59 tail call fastcc void @check(i32 1) #0 60 br label %D 61 62C: 63 %inc2 = add i32 %inc1, 1 64 %cmp = icmp ult i32 %inc2, 10 65 br i1 %cmp, label %A, label %E 66 67D: 68 unreachable 69 70E: 71 ret void 72} 73 74; The loop exit blocks appear in an inner loop. 75 76define void @inner_loop(i1 %PredEntry, i1 %PredA, i1 %PredB) { 77; CHECK-LABEL: @inner_loop( 78; CHECK-NEXT: entry: 79; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[I:%.*]] 80; CHECK: A: 81; CHECK-NEXT: [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ] 82; CHECK-NEXT: br label [[B:%.*]] 83; CHECK: B: 84; CHECK-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ] 85; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]] 86; CHECK: C: 87; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] 88; CHECK-NEXT: br label [[H:%.*]] 89; CHECK: D: 90; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD2]], label [[F]] 91; CHECK: E: 92; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] 93; CHECK-NEXT: br label [[H]] 94; CHECK: F: 95; CHECK-NEXT: [[INNER2]] = add i32 [[INNER1]], 1 96; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20 97; CHECK-NEXT: br i1 [[CMP1]], label [[B]], label [[LOOP_EXIT_GUARD2]] 98; CHECK: G: 99; CHECK-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1 100; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10 101; CHECK-NEXT: br i1 [[CMP2]], label [[A]], label [[LOOP_EXIT_GUARD:%.*]] 102; CHECK: H: 103; CHECK-NEXT: unreachable 104; CHECK: I: 105; CHECK-NEXT: ret void 106; CHECK: loop.exit.guard: 107; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 2, [[G]] ], [ [[MERGED_BB_IDX_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ] 108; CHECK-NEXT: [[C_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0 109; CHECK-NEXT: br i1 [[C_PREDICATE]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]] 110; CHECK: loop.exit.guard1: 111; CHECK-NEXT: [[E_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 1 112; CHECK-NEXT: br i1 [[E_PREDICATE]], label [[E:%.*]], label [[I]] 113; CHECK: loop.exit.guard2: 114; CHECK-NEXT: [[MERGED_BB_IDX_MOVED]] = phi i32 [ 0, [[B]] ], [ 1, [[D]] ], [ poison, [[F]] ] 115; CHECK-NEXT: [[MERGED_BB_IDX3:%.*]] = phi i32 [ 0, [[B]] ], [ 0, [[D]] ], [ 1, [[F]] ] 116; CHECK-NEXT: [[LOOP_EXIT_GUARD_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX3]], 0 117; CHECK-NEXT: br i1 [[LOOP_EXIT_GUARD_PREDICATE]], label [[LOOP_EXIT_GUARD]], label [[G]] 118; 119; BOOLEAN-LABEL: @inner_loop( 120; BOOLEAN-NEXT: entry: 121; BOOLEAN-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[I:%.*]] 122; BOOLEAN: A: 123; BOOLEAN-NEXT: [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ] 124; BOOLEAN-NEXT: br label [[B:%.*]] 125; BOOLEAN: B: 126; BOOLEAN-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ] 127; BOOLEAN-NEXT: br i1 [[PREDA:%.*]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]] 128; BOOLEAN: C: 129; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] 130; BOOLEAN-NEXT: br label [[H:%.*]] 131; BOOLEAN: D: 132; BOOLEAN-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD2]], label [[F]] 133; BOOLEAN: E: 134; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] 135; BOOLEAN-NEXT: br label [[H]] 136; BOOLEAN: F: 137; BOOLEAN-NEXT: [[INNER2]] = add i32 [[INNER1]], 1 138; BOOLEAN-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20 139; BOOLEAN-NEXT: br i1 [[CMP1]], label [[B]], label [[LOOP_EXIT_GUARD2]] 140; BOOLEAN: G: 141; BOOLEAN-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1 142; BOOLEAN-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10 143; BOOLEAN-NEXT: br i1 [[CMP2]], label [[A]], label [[LOOP_EXIT_GUARD:%.*]] 144; BOOLEAN: H: 145; BOOLEAN-NEXT: unreachable 146; BOOLEAN: I: 147; BOOLEAN-NEXT: ret void 148; BOOLEAN: loop.exit.guard: 149; BOOLEAN-NEXT: [[GUARD_C:%.*]] = phi i1 [ false, [[G]] ], [ [[GUARD_C_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ] 150; BOOLEAN-NEXT: [[GUARD_E:%.*]] = phi i1 [ false, [[G]] ], [ [[GUARD_E_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ] 151; BOOLEAN-NEXT: br i1 [[GUARD_C]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]] 152; BOOLEAN: loop.exit.guard1: 153; BOOLEAN-NEXT: br i1 [[GUARD_E]], label [[E:%.*]], label [[I]] 154; BOOLEAN: loop.exit.guard2: 155; BOOLEAN-NEXT: [[GUARD_E_MOVED]] = phi i1 [ false, [[B]] ], [ true, [[D]] ], [ poison, [[F]] ] 156; BOOLEAN-NEXT: [[GUARD_C_MOVED]] = phi i1 [ true, [[B]] ], [ false, [[D]] ], [ poison, [[F]] ] 157; BOOLEAN-NEXT: [[GUARD_LOOP_EXIT_GUARD:%.*]] = phi i1 [ true, [[B]] ], [ true, [[D]] ], [ false, [[F]] ] 158; BOOLEAN-NEXT: br i1 [[GUARD_LOOP_EXIT_GUARD]], label [[LOOP_EXIT_GUARD]], label [[G]] 159; 160entry: 161 br i1 %PredEntry, label %A, label %I 162 163A: 164 %outer1 = phi i32 [ 0, %entry ], [ %outer2, %G ] 165 br label %B 166 167B: 168 %inner1 = phi i32 [ 0, %A ], [ %inner2, %F ] 169 br i1 %PredA, label %D, label %C 170 171C: 172 tail call fastcc void @check(i32 1) #0 173 br label %H 174 175D: 176 br i1 %PredB, label %E, label %F 177 178E: 179 tail call fastcc void @check(i32 2) #0 180 br label %H 181 182F: 183 %inner2 = add i32 %inner1, 1 184 %cmp1 = icmp ult i32 %inner2, 20 185 br i1 %cmp1, label %B, label %G 186 187G: 188 %outer2 = add i32 %outer1, 1 189 %cmp2 = icmp ult i32 %outer2, 10 190 br i1 %cmp2, label %A, label %I 191 192H: 193 unreachable 194 195I: 196 ret void 197} 198 199; A loop with more exit blocks. 200 201define void @loop_five_exits(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %PredC, i1 %PredD) { 202; CHECK-LABEL: @loop_five_exits( 203; CHECK-NEXT: entry: 204; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[L:%.*]] 205; CHECK: A: 206; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ] 207; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C:%.*]] 208; CHECK: B: 209; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] 210; CHECK-NEXT: br label [[J:%.*]] 211; CHECK: C: 212; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD]], label [[E:%.*]] 213; CHECK: D: 214; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] 215; CHECK-NEXT: br label [[J]] 216; CHECK: E: 217; CHECK-NEXT: br i1 [[PREDC:%.*]], label [[LOOP_EXIT_GUARD]], label [[G:%.*]] 218; CHECK: F: 219; CHECK-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]] 220; CHECK-NEXT: br label [[K:%.*]] 221; CHECK: G: 222; CHECK-NEXT: br i1 [[PREDD:%.*]], label [[LOOP_EXIT_GUARD]], label [[I]] 223; CHECK: H: 224; CHECK-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]] 225; CHECK-NEXT: br label [[K]] 226; CHECK: I: 227; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1 228; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10 229; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] 230; CHECK: J: 231; CHECK-NEXT: br label [[L]] 232; CHECK: K: 233; CHECK-NEXT: br label [[L]] 234; CHECK: L: 235; CHECK-NEXT: ret void 236; CHECK: loop.exit.guard: 237; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A]] ], [ 1, [[C]] ], [ 2, [[E]] ], [ 3, [[G]] ], [ 4, [[I]] ] 238; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0 239; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]] 240; CHECK: loop.exit.guard1: 241; CHECK-NEXT: [[D_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 1 242; CHECK-NEXT: br i1 [[D_PREDICATE]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]] 243; CHECK: loop.exit.guard2: 244; CHECK-NEXT: [[F_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 2 245; CHECK-NEXT: br i1 [[F_PREDICATE]], label [[F:%.*]], label [[LOOP_EXIT_GUARD3:%.*]] 246; CHECK: loop.exit.guard3: 247; CHECK-NEXT: [[H_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 3 248; CHECK-NEXT: br i1 [[H_PREDICATE]], label [[H:%.*]], label [[L]] 249; 250; BOOLEAN-LABEL: @loop_five_exits( 251; BOOLEAN-NEXT: entry: 252; BOOLEAN-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[L:%.*]] 253; BOOLEAN: A: 254; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ] 255; BOOLEAN-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C:%.*]] 256; BOOLEAN: B: 257; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]] 258; BOOLEAN-NEXT: br label [[J:%.*]] 259; BOOLEAN: C: 260; BOOLEAN-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD]], label [[E:%.*]] 261; BOOLEAN: D: 262; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]] 263; BOOLEAN-NEXT: br label [[J]] 264; BOOLEAN: E: 265; BOOLEAN-NEXT: br i1 [[PREDC:%.*]], label [[LOOP_EXIT_GUARD]], label [[G:%.*]] 266; BOOLEAN: F: 267; BOOLEAN-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]] 268; BOOLEAN-NEXT: br label [[K:%.*]] 269; BOOLEAN: G: 270; BOOLEAN-NEXT: br i1 [[PREDD:%.*]], label [[LOOP_EXIT_GUARD]], label [[I]] 271; BOOLEAN: H: 272; BOOLEAN-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]] 273; BOOLEAN-NEXT: br label [[K]] 274; BOOLEAN: I: 275; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1 276; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10 277; BOOLEAN-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] 278; BOOLEAN: J: 279; BOOLEAN-NEXT: br label [[L]] 280; BOOLEAN: K: 281; BOOLEAN-NEXT: br label [[L]] 282; BOOLEAN: L: 283; BOOLEAN-NEXT: ret void 284; BOOLEAN: loop.exit.guard: 285; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ], [ false, [[E]] ], [ false, [[G]] ], [ false, [[I]] ] 286; BOOLEAN-NEXT: [[GUARD_D:%.*]] = phi i1 [ false, [[A]] ], [ true, [[C]] ], [ false, [[E]] ], [ false, [[G]] ], [ false, [[I]] ] 287; BOOLEAN-NEXT: [[GUARD_F:%.*]] = phi i1 [ false, [[A]] ], [ false, [[C]] ], [ true, [[E]] ], [ false, [[G]] ], [ false, [[I]] ] 288; BOOLEAN-NEXT: [[GUARD_H:%.*]] = phi i1 [ false, [[A]] ], [ false, [[C]] ], [ false, [[E]] ], [ true, [[G]] ], [ false, [[I]] ] 289; BOOLEAN-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]] 290; BOOLEAN: loop.exit.guard1: 291; BOOLEAN-NEXT: br i1 [[GUARD_D]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]] 292; BOOLEAN: loop.exit.guard2: 293; BOOLEAN-NEXT: br i1 [[GUARD_F]], label [[F:%.*]], label [[LOOP_EXIT_GUARD3:%.*]] 294; BOOLEAN: loop.exit.guard3: 295; BOOLEAN-NEXT: br i1 [[GUARD_H]], label [[H:%.*]], label [[L]] 296; 297entry: 298 br i1 %PredEntry, label %A, label %L 299 300A: 301 %inc1 = phi i32 [ 0, %entry ], [ %inc2, %I ] 302 br i1 %PredA, label %B, label %C 303 304B: 305 tail call fastcc void @check(i32 1) #0 306 br label %J 307 308C: 309 br i1 %PredB, label %D, label %E 310 311D: 312 tail call fastcc void @check(i32 2) #0 313 br label %J 314 315E: 316 br i1 %PredC, label %F, label %G 317 318F: 319 tail call fastcc void @check(i32 3) #0 320 br label %K 321 322G: 323 br i1 %PredD, label %H, label %I 324 325H: 326 tail call fastcc void @check(i32 4) #0 327 br label %K 328 329I: 330 %inc2 = add i32 %inc1, 1 331 %cmp = icmp ult i32 %inc2, 10 332 br i1 %cmp, label %A, label %L 333 334J: 335 br label %L 336 337K: 338 br label %L 339 340L: 341 ret void 342} 343 344 345declare void @check(i32 noundef %i) #0 346 347attributes #0 = { noreturn nounwind } 348 349