1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 2; RUN: opt -S -aa-pipeline=basic-aa -passes='require<opt-remark-emit>,make-guards-explicit,loop-mssa(licm)' < %s | FileCheck %s 3 4declare void @llvm.experimental.guard(i1,...) 5declare void @maythrow() 6declare void @llvm.experimental.deoptimize.isVoid(...) 7declare i1 @llvm.experimental.widenable.condition() 8 9; Make sure that we do not hoist widenable_cond out of loop. 10; Widenable conditions don't actually alias anything or throw, however 11; hoisting them is not profitable because it can prevent guard widening 12; in loop. 13; If we want to widen guards in branch form, we need to generate them 14; at the point of widenable_condition call 15; (see https://github.com/llvm/llvm-project/issues/60234). Because of 16; this, hoisting a widenable condition out of loop can make widening of 17; in-loop conditions impossible (e.g. if they depend on loop variant). On 18; the other hand, hoisting of the WC doesn't really see profitable, 19; because it will be optimized away later anyways. So the test checks that 20; LICM will not move it out of loop. 21define void @do_not_hoist_widenable_cond(i1 %cond, i32 %N, i32 %M) { 22; CHECK-LABEL: @do_not_hoist_widenable_cond( 23; CHECK-NEXT: entry: 24; CHECK-NEXT: br label [[LOOP:%.*]] 25; CHECK: loop: 26; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[GUARDED:%.*]] ] 27; CHECK-NEXT: [[GUARD_COND:%.*]] = icmp slt i32 [[IV]], [[N:%.*]] 28; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() 29; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[GUARD_COND]], [[WIDENABLE_COND]] 30; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED]], label [[DEOPT:%.*]], !prof [[PROF0:![0-9]+]] 31; CHECK: deopt: 32; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] 33; CHECK-NEXT: ret void 34; CHECK: guarded: 35; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp slt i32 [[IV]], [[M:%.*]] 36; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 37; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] 38; CHECK: exit: 39; CHECK-NEXT: ret void 40; 41entry: 42 br label %loop 43 44loop: 45 %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] 46 %guard_cond = icmp slt i32 %iv, %N 47 call void(i1, ...) @llvm.experimental.guard(i1 %guard_cond) [ "deopt"() ] 48 %loop_cond = icmp slt i32 %iv, %M 49 %iv.next = add i32 %iv, 1 50 br i1 %loop_cond, label %loop, label %exit 51 52exit: 53 ret void 54} 55 56define void @do_not_hoist_widenable_cond_speculate(i1 %cond, i32 %N, i32 %M) { 57; CHECK-LABEL: @do_not_hoist_widenable_cond_speculate( 58; CHECK-NEXT: entry: 59; CHECK-NEXT: br label [[LOOP:%.*]] 60; CHECK: loop: 61; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[GUARDED:%.*]] ] 62; CHECK-NEXT: [[GUARD_COND:%.*]] = icmp slt i32 [[IV]], [[N:%.*]] 63; CHECK-NEXT: call void @maythrow() 64; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() 65; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[GUARD_COND]], [[WIDENABLE_COND]] 66; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED]], label [[DEOPT:%.*]], !prof [[PROF0]] 67; CHECK: deopt: 68; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] 69; CHECK-NEXT: ret void 70; CHECK: guarded: 71; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp slt i32 [[IV]], [[M:%.*]] 72; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 73; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] 74; CHECK: exit: 75; CHECK-NEXT: ret void 76; 77entry: 78 br label %loop 79 80loop: 81 %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] 82 %guard_cond = icmp slt i32 %iv, %N 83 call void @maythrow() 84 call void(i1, ...) @llvm.experimental.guard(i1 %guard_cond) [ "deopt"() ] 85 %loop_cond = icmp slt i32 %iv, %M 86 %iv.next = add i32 %iv, 1 87 br i1 %loop_cond, label %loop, label %exit 88 89exit: 90 ret void 91} 92 93 94define void @hoist_invariant_load(i1 %cond, ptr %np, i32 %M) { 95; CHECK-LABEL: @hoist_invariant_load( 96; CHECK-NEXT: entry: 97; CHECK-NEXT: [[N:%.*]] = load i32, ptr [[NP:%.*]], align 4 98; CHECK-NEXT: br label [[LOOP:%.*]] 99; CHECK: loop: 100; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[GUARDED:%.*]] ] 101; CHECK-NEXT: [[GUARD_COND:%.*]] = icmp slt i32 [[IV]], [[N]] 102; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() 103; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[GUARD_COND]], [[WIDENABLE_COND]] 104; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED]], label [[DEOPT:%.*]], !prof [[PROF0]] 105; CHECK: deopt: 106; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] 107; CHECK-NEXT: ret void 108; CHECK: guarded: 109; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp slt i32 [[IV]], [[M:%.*]] 110; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 111; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] 112; CHECK: exit: 113; CHECK-NEXT: ret void 114; 115entry: 116 br label %loop 117 118loop: 119 %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] 120 %N = load i32, ptr %np 121 %guard_cond = icmp slt i32 %iv, %N 122 call void(i1, ...) @llvm.experimental.guard(i1 %guard_cond) [ "deopt"() ] 123 %loop_cond = icmp slt i32 %iv, %M 124 %iv.next = add i32 %iv, 1 125 br i1 %loop_cond, label %loop, label %exit 126 127exit: 128 ret void 129} 130 131; TODO: Hoist widenable condition out of loop since it's the only 132; non-invariant operand of its user. If we hoist wc here, we can hoist 133; `and` as well, otherwise it prevents us from doing that. 134define void @hoist_widenable_cond_1(ptr %p, i32 %x) { 135; CHECK-LABEL: @hoist_widenable_cond_1( 136; CHECK-NEXT: entry: 137; CHECK-NEXT: [[LOAD:%.*]] = load atomic i32, ptr [[P:%.*]] unordered, align 8 138; CHECK-NEXT: [[ICMP7:%.*]] = icmp ult i32 0, [[LOAD]] 139; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] 140; CHECK: loop.header: 141; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[GUARDED:%.*]] ], [ 0, [[ENTRY:%.*]] ] 142; CHECK-NEXT: [[ICMP:%.*]] = icmp ult i32 [[IV]], [[X:%.*]] 143; CHECK-NEXT: br i1 [[ICMP]], label [[LOOP_BODY:%.*]], label [[EXIT:%.*]] 144; CHECK: loop.body: 145; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() 146; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[ICMP7]], [[WIDENABLE_COND]] 147; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED]], label [[DEOPT:%.*]] 148; CHECK: deopt: 149; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid(i32 13) [ "deopt"() ] 150; CHECK-NEXT: ret void 151; CHECK: guarded: 152; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 153; CHECK-NEXT: br label [[LOOP_HEADER]] 154; CHECK: exit: 155; CHECK-NEXT: ret void 156; 157entry: 158 %load = load atomic i32, ptr %p unordered, align 8 159 br label %loop.header 160 161loop.header: 162 %iv = phi i32 [ %iv.next, %guarded ], [ 0, %entry ] 163 %icmp = icmp ult i32 %iv, %x 164 br i1 %icmp, label %loop.body, label %exit 165 166loop.body: 167 %icmp7 = icmp ult i32 0, %load 168 %widenable_cond = call i1 @llvm.experimental.widenable.condition() 169 %exiplicit_guard_cond = and i1 %icmp7, %widenable_cond 170 br i1 %exiplicit_guard_cond, label %guarded, label %deopt 171 172deopt: 173 call void (...) @llvm.experimental.deoptimize.isVoid(i32 13) [ "deopt"() ] 174 ret void 175 176guarded: 177 %iv.next = add nuw nsw i32 %iv, 1 178 br label %loop.header 179 180exit: 181 ret void 182} 183 184; Same as hoist_widenable_cond_1 test, but widenable condition goes before 185; icmp - the second operand of wc's user. 186; TODO: Hoist widenable condition out of loop. 187define void @hoist_widenable_cond_2(ptr %p, i32 %x) { 188; CHECK-LABEL: @hoist_widenable_cond_2( 189; CHECK-NEXT: entry: 190; CHECK-NEXT: [[LOAD:%.*]] = load atomic i32, ptr [[P:%.*]] unordered, align 8 191; CHECK-NEXT: [[ICMP7:%.*]] = icmp ult i32 0, [[LOAD]] 192; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] 193; CHECK: loop.header: 194; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[GUARDED:%.*]] ], [ 0, [[ENTRY:%.*]] ] 195; CHECK-NEXT: [[ICMP:%.*]] = icmp ult i32 [[IV]], [[X:%.*]] 196; CHECK-NEXT: br i1 [[ICMP]], label [[LOOP_BODY:%.*]], label [[EXIT:%.*]] 197; CHECK: loop.body: 198; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() 199; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[ICMP7]], [[WIDENABLE_COND]] 200; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED]], label [[DEOPT:%.*]] 201; CHECK: deopt: 202; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid(i32 13) [ "deopt"() ] 203; CHECK-NEXT: ret void 204; CHECK: guarded: 205; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1 206; CHECK-NEXT: br label [[LOOP_HEADER]] 207; CHECK: exit: 208; CHECK-NEXT: ret void 209; 210entry: 211 %load = load atomic i32, ptr %p unordered, align 8 212 br label %loop.header 213 214loop.header: 215 %iv = phi i32 [ %iv.next, %guarded ], [ 0, %entry ] 216 %icmp = icmp ult i32 %iv, %x 217 br i1 %icmp, label %loop.body, label %exit 218 219loop.body: 220 %widenable_cond = call i1 @llvm.experimental.widenable.condition() 221 %icmp7 = icmp ult i32 0, %load 222 %exiplicit_guard_cond = and i1 %icmp7, %widenable_cond 223 br i1 %exiplicit_guard_cond, label %guarded, label %deopt 224 225deopt: 226 call void (...) @llvm.experimental.deoptimize.isVoid(i32 13) [ "deopt"() ] 227 ret void 228 229guarded: 230 %iv.next = add nuw nsw i32 %iv, 1 231 br label %loop.header 232 233exit: 234 ret void 235} 236