1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 2; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s 3 4define void @test1(i32 %n) { 5; CHECK-LABEL: define void @test1( 6; CHECK-SAME: i32 [[N:%.*]]) { 7; CHECK-NEXT: [[ENTRY:.*]]: 8; CHECK-NEXT: br label %[[FOR_COND:.*]] 9; CHECK: [[FOR_COND]]: 10; CHECK-NEXT: [[A:%.*]] = phi i32 [ [[N]], %[[ENTRY]] ], [ [[SHR:%.*]], %[[FOR_BODY:.*]] ] 11; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A]], 1 12; CHECK-NEXT: br i1 [[CMP]], label %[[FOR_BODY]], label %[[FOR_END:.*]] 13; CHECK: [[FOR_BODY]]: 14; CHECK-NEXT: [[SHR]] = lshr i32 [[A]], 5 15; CHECK-NEXT: br label %[[FOR_COND]] 16; CHECK: [[FOR_END]]: 17; CHECK-NEXT: ret void 18; 19entry: 20 br label %for.cond 21 22for.cond: ; preds = %for.body, %entry 23 %a = phi i32 [ %n, %entry ], [ %shr, %for.body ] 24 %cmp = icmp sgt i32 %a, 1 25 br i1 %cmp, label %for.body, label %for.end 26 27for.body: ; preds = %for.cond 28 %shr = ashr i32 %a, 5 29 br label %for.cond 30 31for.end: ; preds = %for.cond 32 ret void 33} 34 35;; Negative test to show transform doesn't happen unless n > 0. 36define void @test2(i32 %n) { 37; CHECK-LABEL: define void @test2( 38; CHECK-SAME: i32 [[N:%.*]]) { 39; CHECK-NEXT: [[ENTRY:.*]]: 40; CHECK-NEXT: br label %[[FOR_COND:.*]] 41; CHECK: [[FOR_COND]]: 42; CHECK-NEXT: [[A:%.*]] = phi i32 [ [[N]], %[[ENTRY]] ], [ [[SHR:%.*]], %[[FOR_BODY:.*]] ] 43; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A]], -2 44; CHECK-NEXT: br i1 [[CMP]], label %[[FOR_BODY]], label %[[FOR_END:.*]] 45; CHECK: [[FOR_BODY]]: 46; CHECK-NEXT: [[SHR]] = ashr i32 [[A]], 2 47; CHECK-NEXT: br label %[[FOR_COND]] 48; CHECK: [[FOR_END]]: 49; CHECK-NEXT: ret void 50; 51entry: 52 br label %for.cond 53 54for.cond: ; preds = %for.body, %entry 55 %a = phi i32 [ %n, %entry ], [ %shr, %for.body ] 56 %cmp = icmp sgt i32 %a, -2 57 br i1 %cmp, label %for.body, label %for.end 58 59for.body: ; preds = %for.cond 60 %shr = ashr i32 %a, 2 61 br label %for.cond 62 63for.end: ; preds = %for.cond 64 ret void 65} 66 67;; Non looping test case. 68define void @test3(i32 %n) { 69; CHECK-LABEL: define void @test3( 70; CHECK-SAME: i32 [[N:%.*]]) { 71; CHECK-NEXT: [[ENTRY:.*:]] 72; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[N]], 0 73; CHECK-NEXT: br i1 [[CMP]], label %[[BB:.*]], label %[[EXIT:.*]] 74; CHECK: [[BB]]: 75; CHECK-NEXT: [[SHR:%.*]] = lshr exact i32 [[N]], 4 76; CHECK-NEXT: br label %[[EXIT]] 77; CHECK: [[EXIT]]: 78; CHECK-NEXT: ret void 79; 80entry: 81 %cmp = icmp sgt i32 %n, 0 82 br i1 %cmp, label %bb, label %exit 83 84bb: 85 %shr = ashr exact i32 %n, 4 86 br label %exit 87 88exit: 89 ret void 90} 91 92; looping case where loop has exactly one block 93; at the point of ashr, we know that the operand is always greater than 0, 94; because of the guard before it, so we can transform it to lshr. 95declare void @llvm.experimental.guard(i1,...) 96define void @test4(i32 %n) { 97; CHECK-LABEL: define void @test4( 98; CHECK-SAME: i32 [[N:%.*]]) { 99; CHECK-NEXT: [[ENTRY:.*]]: 100; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[N]], 0 101; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP:.*]], label %[[EXIT:.*]] 102; CHECK: [[LOOP]]: 103; CHECK-NEXT: [[A:%.*]] = phi i32 [ [[N]], %[[ENTRY]] ], [ [[SHR:%.*]], %[[LOOP]] ] 104; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[A]], 2 105; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND]]) [ "deopt"() ] 106; CHECK-NEXT: [[SHR]] = lshr i32 [[A]], 1 107; CHECK-NEXT: br i1 [[COND]], label %[[LOOP]], label %[[EXIT]] 108; CHECK: [[EXIT]]: 109; CHECK-NEXT: ret void 110; 111entry: 112 %cmp = icmp sgt i32 %n, 0 113 br i1 %cmp, label %loop, label %exit 114 115loop: 116 %a = phi i32 [ %n, %entry ], [ %shr, %loop ] 117 %cond = icmp sgt i32 %a, 2 118 call void(i1,...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ] 119 %shr = ashr i32 %a, 1 120 br i1 %cond, label %loop, label %exit 121 122exit: 123 ret void 124} 125 126; same test as above with assume instead of guard. 127declare void @llvm.assume(i1) 128define void @test5(i32 %n) { 129; CHECK-LABEL: define void @test5( 130; CHECK-SAME: i32 [[N:%.*]]) { 131; CHECK-NEXT: [[ENTRY:.*]]: 132; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[N]], 0 133; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP:.*]], label %[[EXIT:.*]] 134; CHECK: [[LOOP]]: 135; CHECK-NEXT: [[A:%.*]] = phi i32 [ [[N]], %[[ENTRY]] ], [ [[SHR:%.*]], %[[LOOP]] ] 136; CHECK-NEXT: [[COND:%.*]] = icmp samesign ugt i32 [[A]], 4 137; CHECK-NEXT: call void @llvm.assume(i1 [[COND]]) 138; CHECK-NEXT: [[SHR]] = lshr i32 [[A]], 1 139; CHECK-NEXT: [[LOOPCOND:%.*]] = icmp samesign ugt i32 [[SHR]], 8 140; CHECK-NEXT: br i1 [[LOOPCOND]], label %[[LOOP]], label %[[EXIT]] 141; CHECK: [[EXIT]]: 142; CHECK-NEXT: ret void 143; 144entry: 145 %cmp = icmp sgt i32 %n, 0 146 br i1 %cmp, label %loop, label %exit 147 148loop: 149 %a = phi i32 [ %n, %entry ], [ %shr, %loop ] 150 %cond = icmp sgt i32 %a, 4 151 call void @llvm.assume(i1 %cond) 152 %shr = ashr i32 %a, 1 153 %loopcond = icmp sgt i32 %shr, 8 154 br i1 %loopcond, label %loop, label %exit 155 156exit: 157 ret void 158} 159 160; check that ashr of -1 or 0 is optimized away 161define i32 @test6(i32 %f, i32 %g) { 162; CHECK-LABEL: define range(i32 -1, 1) i32 @test6( 163; CHECK-SAME: i32 [[F:%.*]], i32 [[G:%.*]]) { 164; CHECK-NEXT: [[ENTRY:.*:]] 165; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[F]], 1 166; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[TMP0]], 2 167; CHECK-NEXT: tail call void @llvm.assume(i1 [[TMP1]]) 168; CHECK-NEXT: ret i32 [[F]] 169; 170entry: 171 %0 = add i32 %f, 1 172 %1 = icmp ult i32 %0, 2 173 tail call void @llvm.assume(i1 %1) 174 %shr = ashr i32 %f, %g 175 ret i32 %shr 176} 177 178; same test as above with different numbers 179define i32 @test7(i32 %f, i32 %g) { 180; CHECK-LABEL: define range(i32 -1, 1) i32 @test7( 181; CHECK-SAME: i32 [[F:%.*]], i32 [[G:%.*]]) { 182; CHECK-NEXT: [[ENTRY:.*:]] 183; CHECK-NEXT: [[TMP0:%.*]] = and i32 [[F]], -2 184; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 6 185; CHECK-NEXT: tail call void @llvm.assume(i1 [[TMP1]]) 186; CHECK-NEXT: [[SUB:%.*]] = add nsw i32 [[F]], -7 187; CHECK-NEXT: ret i32 [[SUB]] 188; 189entry: 190 %0 = and i32 %f, -2 191 %1 = icmp eq i32 %0, 6 192 tail call void @llvm.assume(i1 %1) 193 %sub = add nsw i32 %f, -7 194 %shr = ashr i32 %sub, %g 195 ret i32 %shr 196} 197 198; check that ashr of -2 or 1 is not optimized away 199define i32 @test8(i32 %f, i32 %g, i1 %s) { 200; CHECK-LABEL: define range(i32 -2, 2) i32 @test8( 201; CHECK-SAME: i32 [[F:%.*]], i32 [[G:%.*]], i1 [[S:%.*]]) { 202; CHECK-NEXT: [[ENTRY:.*:]] 203; CHECK-NEXT: [[TMP0:%.*]] = ashr i32 -2, [[F]] 204; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 1, [[G]] 205; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[S]], i32 [[TMP0]], i32 [[TMP1]] 206; CHECK-NEXT: ret i32 [[TMP2]] 207; 208entry: 209 %0 = ashr i32 -2, %f 210 %1 = ashr i32 1, %g 211 %2 = select i1 %s, i32 %0, i32 %1 212 ret i32 %2 213} 214 215define i32 @may_including_undef(i1 %c.1, i1 %c.2) { 216; CHECK-LABEL: define range(i32 -1073741824, 1073741824) i32 @may_including_undef( 217; CHECK-SAME: i1 [[C_1:%.*]], i1 [[C_2:%.*]]) { 218; CHECK-NEXT: br i1 [[C_1]], label %[[TRUE_1:.*]], label %[[FALSE:.*]] 219; CHECK: [[TRUE_1]]: 220; CHECK-NEXT: br i1 [[C_2]], label %[[TRUE_2:.*]], label %[[EXIT:.*]] 221; CHECK: [[TRUE_2]]: 222; CHECK-NEXT: br label %[[EXIT]] 223; CHECK: [[FALSE]]: 224; CHECK-NEXT: br label %[[EXIT]] 225; CHECK: [[EXIT]]: 226; CHECK-NEXT: [[P:%.*]] = phi i32 [ 2, %[[TRUE_1]] ], [ 4, %[[TRUE_2]] ], [ undef, %[[FALSE]] ] 227; CHECK-NEXT: [[R:%.*]] = ashr i32 [[P]], 1 228; CHECK-NEXT: ret i32 [[R]] 229; 230 br i1 %c.1, label %true.1, label %false 231 232true.1: 233 br i1 %c.2, label %true.2, label %exit 234 235true.2: 236 br label %exit 237 238false: 239 br label %exit 240 241exit: 242 %p = phi i32 [ 2, %true.1 ], [ 4, %true.2], [ undef, %false ] 243 %r = ashr i32 %p, 1 244 ret i32 %r 245} 246