1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 2; RUN: opt -S -passes=guard-widening,dce < %s | FileCheck %s 3 4declare void @llvm.experimental.guard(i1,...) 5declare i1 @dummy() 6 7; This tests shows the incorrect behavior of guard widening in terms of 8; interaction with poison values. 9 10; Let x incoming parameter is used for rane checks. 11; Test generates 5 checks. One of them (c2) is used to get the corretness 12; of nuw/nsw flags for x3 and x5. Others are used in guards and represent 13; the checks x + 10 u< L, x + 15 u< L, x + 20 u< L and x + 3 u< L. 14; The first two checks are in the first basic block and guard widening 15; considers them as profitable to combine. 16; When c4 and c3 are considered, number of check becomes more than two 17; and combineRangeCheck consider them as profitable even if they are in 18; different basic blocks. 19; Accoding to algorithm of combineRangeCheck it detects that c3 and c4 20; are enough to cover c1 and c5, so it ends up with guard of c3 && c4 21; while both of them are poison at entry. This is a bug. 22 23define void @combine_range_checks(i32 %x) { 24; CHECK-LABEL: @combine_range_checks( 25; CHECK-NEXT: entry: 26; CHECK-NEXT: [[X_GW_FR:%.*]] = freeze i32 [[X:%.*]] 27; CHECK-NEXT: [[X2:%.*]] = add i32 [[X_GW_FR]], 0 28; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[X2]], 200 29; CHECK-NEXT: [[X3:%.*]] = add i32 [[X_GW_FR]], 3 30; CHECK-NEXT: [[C3:%.*]] = icmp ult i32 [[X3]], 100 31; CHECK-NEXT: [[X4:%.*]] = add i32 [[X_GW_FR]], 20 32; CHECK-NEXT: [[C4:%.*]] = icmp ult i32 [[X4]], 100 33; CHECK-NEXT: [[WIDE_CHK2:%.*]] = and i1 [[C4]], [[C3]] 34; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK2]]) [ "deopt"(i64 1) ] 35; CHECK-NEXT: br i1 [[C2]], label [[OK:%.*]], label [[OUT:%.*]] 36; CHECK: ok: 37; CHECK-NEXT: br label [[OUT]] 38; CHECK: out: 39; CHECK-NEXT: ret void 40; 41entry: 42 %x1 = add i32 %x, 10 43 %c1 = icmp ult i32 %x1, 100 44 %x2 = add i32 %x, 0 45 %c2 = icmp ult i32 %x2, 200 46 %x3 = add nuw nsw i32 %x, 3 47 %c3 = icmp ult i32 %x3, 100 48 %x4 = add nuw nsw i32 %x, 20 49 %c4 = icmp ult i32 %x4, 100 50 %x5 = add i32 %x, 15 51 %c5 = icmp ult i32 %x5, 100 52 call void(i1, ...) @llvm.experimental.guard(i1 %c1) [ "deopt"(i64 1) ] 53 call void(i1, ...) @llvm.experimental.guard(i1 %c5) [ "deopt"(i64 5) ] 54 br i1 %c2, label %ok, label %out 55ok: 56 call void(i1, ...) @llvm.experimental.guard(i1 %c4) [ "deopt"(i64 4) ] 57 call void(i1, ...) @llvm.experimental.guard(i1 %c3) [ "deopt"(i64 3) ] 58 br label %out 59out: 60 ret void 61} 62 63; This is similar to @combine_range_checks but shows that simple freeze 64; over c3 and c4 will not help due to with X = SMAX_INT, guard with c1 will 65; go to deoptimization. But after guard widening freeze of c3 and c4 may return 66; true due to c3 and c4 are poisons and we pass guard executing side effect store 67; which never been executed in original program. 68define void @combine_range_checks_with_side_effect(i32 %x, ptr %p) { 69; CHECK-LABEL: @combine_range_checks_with_side_effect( 70; CHECK-NEXT: entry: 71; CHECK-NEXT: [[X_GW_FR:%.*]] = freeze i32 [[X:%.*]] 72; CHECK-NEXT: [[X2:%.*]] = add i32 [[X_GW_FR]], 0 73; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[X2]], 200 74; CHECK-NEXT: [[X3:%.*]] = add i32 [[X_GW_FR]], 3 75; CHECK-NEXT: [[C3:%.*]] = icmp ult i32 [[X3]], 100 76; CHECK-NEXT: [[X4:%.*]] = add i32 [[X_GW_FR]], 20 77; CHECK-NEXT: [[C4:%.*]] = icmp ult i32 [[X4]], 100 78; CHECK-NEXT: [[WIDE_CHK2:%.*]] = and i1 [[C4]], [[C3]] 79; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK2]]) [ "deopt"(i64 1) ] 80; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4 81; CHECK-NEXT: br i1 [[C2]], label [[OK:%.*]], label [[OUT:%.*]] 82; CHECK: ok: 83; CHECK-NEXT: br label [[OUT]] 84; CHECK: out: 85; CHECK-NEXT: ret void 86; 87entry: 88 %x1 = add i32 %x, 10 89 %c1 = icmp ult i32 %x1, 100 90 %x2 = add i32 %x, 0 91 %c2 = icmp ult i32 %x2, 200 92 %x3 = add nuw nsw i32 %x, 3 93 %c3 = icmp ult i32 %x3, 100 94 %x4 = add nuw nsw i32 %x, 20 95 %c4 = icmp ult i32 %x4, 100 96 %x5 = add i32 %x, 15 97 %c5 = icmp ult i32 %x5, 100 98 call void(i1, ...) @llvm.experimental.guard(i1 %c1) [ "deopt"(i64 1) ] 99 call void(i1, ...) @llvm.experimental.guard(i1 %c5) [ "deopt"(i64 5) ] 100 store i32 0, ptr %p 101 br i1 %c2, label %ok, label %out 102ok: 103 call void(i1, ...) @llvm.experimental.guard(i1 %c4) [ "deopt"(i64 4) ] 104 call void(i1, ...) @llvm.experimental.guard(i1 %c3) [ "deopt"(i64 3) ] 105 br label %out 106out: 107 ret void 108} 109 110 111; The test shows the bug in guard widening. Critical pieces. 112; There is a %cond_1 check which provides the correctness of nuw nsw in %b.shift. 113; %b.shift and %cond_2 are poisons and after guard widening it leads to UB 114; for both arithmetic and logcal and. 115define void @simple_case(i32 %a, i32 %b, i1 %cnd) { 116; CHECK-LABEL: @simple_case( 117; CHECK-NEXT: entry: 118; CHECK-NEXT: [[B_GW_FR:%.*]] = freeze i32 [[B:%.*]] 119; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10 120; CHECK-NEXT: [[B_SHIFT:%.*]] = add i32 [[B_GW_FR]], 5 121; CHECK-NEXT: [[COND_2:%.*]] = icmp ult i32 [[B_SHIFT]], 10 122; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_2]] 123; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] 124; CHECK-NEXT: br label [[LOOP:%.*]] 125; CHECK: loop: 126; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B_GW_FR]], 10 127; CHECK-NEXT: br i1 [[COND_1]], label [[OK:%.*]], label [[LEAVE_LOOPEXIT:%.*]] 128; CHECK: ok: 129; CHECK-NEXT: br i1 [[CND:%.*]], label [[LOOP]], label [[LEAVE_LOOPEXIT]] 130; CHECK: leave.loopexit: 131; CHECK-NEXT: br label [[LEAVE:%.*]] 132; CHECK: leave: 133; CHECK-NEXT: ret void 134; 135entry: 136 %cond_0 = icmp ult i32 %a, 10 137 %b.shift = add nuw nsw i32 %b, 5 138 %cond_2 = icmp ult i32 %b.shift, 10 139 call void (i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] 140 br label %loop 141 142loop: 143 %cond_1 = icmp ult i32 %b, 10 144 br i1 %cond_1, label %ok, label %leave.loopexit 145ok: 146 call void (i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"() ] 147 br i1 %cnd, label %loop, label %leave.loopexit 148 149leave.loopexit: 150 br label %leave 151 152leave: 153 ret void 154} 155 156declare ptr @fake_personality_function() 157 158define void @case_with_invoke(i1 %c, i1 %gc) personality ptr @fake_personality_function { 159; CHECK-LABEL: @case_with_invoke( 160; CHECK-NEXT: entry: 161; CHECK-NEXT: br i1 [[C:%.*]], label [[NORMAL:%.*]], label [[INVOK:%.*]] 162; CHECK: invok: 163; CHECK-NEXT: [[INVOKE_RESULT:%.*]] = invoke i1 @dummy() 164; CHECK-NEXT: to label [[NORMAL]] unwind label [[EXCEPTION:%.*]] 165; CHECK: normal: 166; CHECK-NEXT: [[PHI_C:%.*]] = phi i1 [ true, [[ENTRY:%.*]] ], [ [[INVOKE_RESULT]], [[INVOK]] ] 167; CHECK-NEXT: [[PHI_C_GW_FR:%.*]] = freeze i1 [[PHI_C]] 168; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[GC:%.*]], [[PHI_C_GW_FR]] 169; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] 170; CHECK-NEXT: ret void 171; CHECK: exception: 172; CHECK-NEXT: [[LANDING_PAD:%.*]] = landingpad { ptr, i32 } 173; CHECK-NEXT: cleanup 174; CHECK-NEXT: ret void 175; 176entry: 177 br i1 %c, label %normal, label %invok 178 179invok: 180 %invoke.result = invoke i1 @dummy() to label %normal unwind label %exception 181 182normal: 183 %phi.c = phi i1 [true, %entry], [%invoke.result, %invok] 184 call void (i1, ...) @llvm.experimental.guard(i1 %gc) [ "deopt"() ] 185 call void (i1, ...) @llvm.experimental.guard(i1 %phi.c) [ "deopt"() ] 186 ret void 187 188exception: 189 %landing_pad = landingpad { ptr, i32 } cleanup 190 ret void 191} 192 193define void @case_with_invoke_in_latch(i1 %c, i1 %gc) personality ptr @fake_personality_function { 194; CHECK-LABEL: @case_with_invoke_in_latch( 195; CHECK-NEXT: entry: 196; CHECK-NEXT: br label [[HEADER:%.*]] 197; CHECK: header: 198; CHECK-NEXT: [[PHI_C:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[INVOKE_RESULT:%.*]], [[HEADER]] ] 199; CHECK-NEXT: [[PHI_C_GW_FR:%.*]] = freeze i1 [[PHI_C]] 200; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[GC:%.*]], [[PHI_C_GW_FR]] 201; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] 202; CHECK-NEXT: [[INVOKE_RESULT]] = invoke i1 @dummy() 203; CHECK-NEXT: to label [[HEADER]] unwind label [[EXCEPTION:%.*]] 204; CHECK: exception: 205; CHECK-NEXT: [[LANDING_PAD:%.*]] = landingpad { ptr, i32 } 206; CHECK-NEXT: cleanup 207; CHECK-NEXT: ret void 208; 209entry: 210 br label %header 211 212header: 213 %phi.c = phi i1 [false, %entry], [%invoke.result, %header] 214 call void (i1, ...) @llvm.experimental.guard(i1 %gc) [ "deopt"() ] 215 call void (i1, ...) @llvm.experimental.guard(i1 %phi.c) [ "deopt"() ] 216 %invoke.result = invoke i1 @dummy() to label %header unwind label %exception 217 218exception: 219 %landing_pad = landingpad { ptr, i32 } cleanup 220 ret void 221} 222 223declare void @dummy_vec(<4 x i1> %arg) 224 225define void @freeze_poison(i1 %c, i1 %g) { 226; CHECK-LABEL: @freeze_poison( 227; CHECK-NEXT: entry: 228; CHECK-NEXT: [[DOTGW_FR:%.*]] = freeze i1 poison 229; CHECK-NEXT: br i1 [[C:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]] 230; CHECK: left: 231; CHECK-NEXT: call void @dummy_vec(<4 x i1> <i1 false, i1 poison, i1 poison, i1 poison>) 232; CHECK-NEXT: ret void 233; CHECK: right: 234; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[G:%.*]], [[DOTGW_FR]] 235; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] 236; CHECK-NEXT: ret void 237; 238entry: 239 br i1 %c, label %left, label %right 240 241left: 242 call void @dummy_vec(<4 x i1> <i1 0, i1 poison, i1 poison, i1 poison>) 243 ret void 244 245 246right: 247 call void (i1, ...) @llvm.experimental.guard(i1 %g) [ "deopt"() ] 248 call void (i1, ...) @llvm.experimental.guard(i1 poison) [ "deopt"() ] 249 ret void 250 251} 252 253