442 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py2; RUN: opt -S -passes=jump-threading,dce < %s | FileCheck %s3 4declare void @llvm.experimental.guard(i1, ...)5 6declare i32 @f1()7declare i32 @f2()8 9define i32 @branch_implies_guard(i32 %a) {10; CHECK-LABEL: @branch_implies_guard(11; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[A:%.*]], 1012; CHECK-NEXT: br i1 [[COND]], label [[T1_SPLIT:%.*]], label [[F1_SPLIT:%.*]]13; CHECK: T1.split:14; CHECK-NEXT: [[V1:%.*]] = call i32 @f1()15; CHECK-NEXT: [[RETVAL3:%.*]] = add i32 [[V1]], 1016; CHECK-NEXT: br label [[MERGE:%.*]]17; CHECK: F1.split:18; CHECK-NEXT: [[V2:%.*]] = call i32 @f2()19; CHECK-NEXT: [[RETVAL1:%.*]] = add i32 [[V2]], 1020; CHECK-NEXT: [[CONDGUARD2:%.*]] = icmp slt i32 [[A]], 2021; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD2]]) [ "deopt"() ]22; CHECK-NEXT: br label [[MERGE]]23; CHECK: Merge:24; CHECK-NEXT: [[TMP1:%.*]] = phi i32 [ [[RETVAL3]], [[T1_SPLIT]] ], [ [[RETVAL1]], [[F1_SPLIT]] ]25; CHECK-NEXT: ret i32 [[TMP1]]26;27 %cond = icmp slt i32 %a, 1028 br i1 %cond, label %T1, label %F129 30T1:31 %v1 = call i32 @f1()32 br label %Merge33 34F1:35 %v2 = call i32 @f2()36 br label %Merge37 38Merge:39 %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]40 %retVal = add i32 %retPhi, 1041 %condGuard = icmp slt i32 %a, 2042 call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]43 ret i32 %retVal44}45 46define i32 @not_branch_implies_guard(i32 %a) {47; CHECK-LABEL: @not_branch_implies_guard(48; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[A:%.*]], 2049; CHECK-NEXT: br i1 [[COND]], label [[T1_SPLIT:%.*]], label [[F1_SPLIT:%.*]]50; CHECK: T1.split:51; CHECK-NEXT: [[V1:%.*]] = call i32 @f1()52; CHECK-NEXT: [[RETVAL1:%.*]] = add i32 [[V1]], 1053; CHECK-NEXT: [[CONDGUARD2:%.*]] = icmp sgt i32 [[A]], 1054; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD2]]) [ "deopt"() ]55; CHECK-NEXT: br label [[MERGE:%.*]]56; CHECK: F1.split:57; CHECK-NEXT: [[V2:%.*]] = call i32 @f2()58; CHECK-NEXT: [[RETVAL3:%.*]] = add i32 [[V2]], 1059; CHECK-NEXT: br label [[MERGE]]60; CHECK: Merge:61; CHECK-NEXT: [[TMP1:%.*]] = phi i32 [ [[RETVAL3]], [[F1_SPLIT]] ], [ [[RETVAL1]], [[T1_SPLIT]] ]62; CHECK-NEXT: ret i32 [[TMP1]]63;64 %cond = icmp slt i32 %a, 2065 br i1 %cond, label %T1, label %F166 67T1:68 %v1 = call i32 @f1()69 br label %Merge70 71F1:72 %v2 = call i32 @f2()73 br label %Merge74 75Merge:76 %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]77 %retVal = add i32 %retPhi, 1078 %condGuard = icmp sgt i32 %a, 1079 call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]80 ret i32 %retVal81}82 83define i32 @branch_overlaps_guard(i32 %a) {84; CHECK-LABEL: @branch_overlaps_guard(85; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[A:%.*]], 2086; CHECK-NEXT: br i1 [[COND]], label [[T1:%.*]], label [[F1:%.*]]87; CHECK: T1:88; CHECK-NEXT: [[V1:%.*]] = call i32 @f1()89; CHECK-NEXT: br label [[MERGE:%.*]]90; CHECK: F1:91; CHECK-NEXT: [[V2:%.*]] = call i32 @f2()92; CHECK-NEXT: br label [[MERGE]]93; CHECK: Merge:94; CHECK-NEXT: [[RETPHI:%.*]] = phi i32 [ [[V1]], [[T1]] ], [ [[V2]], [[F1]] ]95; CHECK-NEXT: [[RETVAL:%.*]] = add i32 [[RETPHI]], 1096; CHECK-NEXT: [[CONDGUARD:%.*]] = icmp slt i32 [[A]], 1097; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD]]) [ "deopt"() ]98; CHECK-NEXT: ret i32 [[RETVAL]]99;100 %cond = icmp slt i32 %a, 20101 br i1 %cond, label %T1, label %F1102 103T1:104 %v1 = call i32 @f1()105 br label %Merge106 107F1:108 %v2 = call i32 @f2()109 br label %Merge110 111Merge:112 %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]113 %retVal = add i32 %retPhi, 10114 %condGuard = icmp slt i32 %a, 10115 call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]116 ret i32 %retVal117}118 119define i32 @branch_doesnt_overlap_guard(i32 %a) {120; CHECK-LABEL: @branch_doesnt_overlap_guard(121; CHECK-NEXT: [[COND:%.*]] = icmp slt i32 [[A:%.*]], 10122; CHECK-NEXT: br i1 [[COND]], label [[T1:%.*]], label [[F1:%.*]]123; CHECK: T1:124; CHECK-NEXT: [[V1:%.*]] = call i32 @f1()125; CHECK-NEXT: br label [[MERGE:%.*]]126; CHECK: F1:127; CHECK-NEXT: [[V2:%.*]] = call i32 @f2()128; CHECK-NEXT: br label [[MERGE]]129; CHECK: Merge:130; CHECK-NEXT: [[RETPHI:%.*]] = phi i32 [ [[V1]], [[T1]] ], [ [[V2]], [[F1]] ]131; CHECK-NEXT: [[RETVAL:%.*]] = add i32 [[RETPHI]], 10132; CHECK-NEXT: [[CONDGUARD:%.*]] = icmp sgt i32 [[A]], 20133; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD]]) [ "deopt"() ]134; CHECK-NEXT: ret i32 [[RETVAL]]135;136 %cond = icmp slt i32 %a, 10137 br i1 %cond, label %T1, label %F1138 139T1:140 %v1 = call i32 @f1()141 br label %Merge142 143F1:144 %v2 = call i32 @f2()145 br label %Merge146 147Merge:148 %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]149 %retVal = add i32 %retPhi, 10150 %condGuard = icmp sgt i32 %a, 20151 call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]152 ret i32 %retVal153}154 155define i32 @not_a_diamond1(i32 %a, i1 %cond1) {156; CHECK-LABEL: @not_a_diamond1(157; CHECK-NEXT: br i1 [[COND1:%.*]], label [[PRED:%.*]], label [[EXIT:%.*]]158; CHECK: Pred:159; CHECK-NEXT: switch i32 [[A:%.*]], label [[EXIT]] [160; CHECK-NEXT: i32 10, label [[MERGE:%.*]]161; CHECK-NEXT: i32 20, label [[MERGE]]162; CHECK-NEXT: ]163; CHECK: Merge:164; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND1]]) [ "deopt"() ]165; CHECK-NEXT: br label [[EXIT]]166; CHECK: Exit:167; CHECK-NEXT: ret i32 [[A]]168;169 br i1 %cond1, label %Pred, label %Exit170 171Pred:172 switch i32 %a, label %Exit [173 i32 10, label %Merge174 i32 20, label %Merge175 ]176 177Merge:178 call void(i1, ...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]179 br label %Exit180 181Exit:182 ret i32 %a183}184 185define void @not_a_diamond2(i32 %a, i1 %cond1) {186; CHECK-LABEL: @not_a_diamond2(187; CHECK-NEXT: Pred:188; CHECK-NEXT: switch i32 [[A:%.*]], label [[EXIT:%.*]] [189; CHECK-NEXT: i32 10, label [[MERGE:%.*]]190; CHECK-NEXT: i32 20, label [[MERGE]]191; CHECK-NEXT: ]192; CHECK: Merge:193; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND1:%.*]]) [ "deopt"() ]194; CHECK-NEXT: ret void195; CHECK: Exit:196; CHECK-NEXT: ret void197;198 br label %Parent199 200Merge:201 call void(i1, ...) @llvm.experimental.guard(i1 %cond1)[ "deopt"() ]202 ret void203 204Pred:205 switch i32 %a, label %Exit [206 i32 10, label %Merge207 i32 20, label %Merge208 ]209 210Parent:211 br label %Pred212 213Exit:214 ret void215}216 217declare void @never_called(i1)218 219; LVI uses guard to identify value of %c2 in branch as true, we cannot replace that220; guard with guard(true & c1).221define void @dont_fold_guard(ptr %addr, i32 %i, i32 %length) {222; CHECK-LABEL: @dont_fold_guard(223; CHECK-NEXT: BB1:224; CHECK-NEXT: [[C1:%.*]] = icmp ult i32 [[I:%.*]], [[LENGTH:%.*]]225; CHECK-NEXT: [[C2:%.*]] = icmp eq i32 [[I]], 0226; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[C1]], [[C2]]227; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]228; CHECK-NEXT: call void @never_called(i1 true)229; CHECK-NEXT: ret void230;231 %c1 = icmp ult i32 %i, %length232 %c2 = icmp eq i32 %i, 0233 %wide.chk = and i1 %c1, %c2234 call void(i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]235 br i1 %c2, label %BB1, label %BB2236 237BB1:238 call void @never_called(i1 %c2)239 ret void240 241BB2:242 ret void243}244 245declare void @dummy(i1) nounwind willreturn246; same as dont_fold_guard1 but there's a use immediately after guard and before247; branch. We can fold that use.248define void @dont_fold_guard2(ptr %addr, i32 %i, i32 %length) {249; CHECK-LABEL: @dont_fold_guard2(250; CHECK-NEXT: BB1:251; CHECK-NEXT: [[C1:%.*]] = icmp ult i32 [[I:%.*]], [[LENGTH:%.*]]252; CHECK-NEXT: [[C2:%.*]] = icmp eq i32 [[I]], 0253; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[C1]], [[C2]]254; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]255; CHECK-NEXT: call void @dummy(i1 true)256; CHECK-NEXT: call void @never_called(i1 true)257; CHECK-NEXT: ret void258;259 %c1 = icmp ult i32 %i, %length260 %c2 = icmp eq i32 %i, 0261 %wide.chk = and i1 %c1, %c2262 call void(i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]263 call void @dummy(i1 %c2)264 br i1 %c2, label %BB1, label %BB2265 266BB1:267 call void @never_called(i1 %c2)268 ret void269 270BB2:271 ret void272}273 274; same as dont_fold_guard1 but condition %cmp is not an instruction.275; We cannot fold the guard under any circumstance.276; FIXME: We can merge unreachableBB2 into not_zero.277define void @dont_fold_guard3(ptr %addr, i1 %cmp, i32 %i, i32 %length) {278; CHECK-LABEL: @dont_fold_guard3(279; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP:%.*]]) [ "deopt"() ]280; CHECK-NEXT: br i1 [[CMP]], label [[BB1:%.*]], label [[BB2:%.*]]281; CHECK: BB1:282; CHECK-NEXT: call void @never_called(i1 [[CMP]])283; CHECK-NEXT: ret void284; CHECK: BB2:285; CHECK-NEXT: ret void286;287 call void(i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ]288 br i1 %cmp, label %BB1, label %BB2289 290BB1:291 call void @never_called(i1 %cmp)292 ret void293 294BB2:295 ret void296}297 298declare void @f(i1)299; Same as dont_fold_guard1 but use switch instead of branch.300; triggers source code `ProcessThreadableEdges`.301define void @dont_fold_guard4(i1 %cmp1, i32 %i) nounwind {302; CHECK-LABEL: @dont_fold_guard4(303; CHECK-NEXT: entry:304; CHECK-NEXT: br i1 [[CMP1:%.*]], label [[L2:%.*]], label [[L3:%.*]]305; CHECK: L2:306; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[I:%.*]], 0307; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ]308; CHECK-NEXT: call void @dummy(i1 true)309; CHECK-NEXT: call void @f(i1 true)310; CHECK-NEXT: ret void311; CHECK: L3:312; CHECK-NEXT: ret void313;314entry:315 br i1 %cmp1, label %L0, label %L3316L0:317 %cmp = icmp eq i32 %i, 0318 call void(i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ]319 call void @dummy(i1 %cmp)320 switch i1 %cmp, label %L3 [321 i1 false, label %L1322 i1 true, label %L2323 ]324 325L1:326 ret void327L2:328 call void @f(i1 %cmp)329 ret void330L3:331 ret void332}333 334; Make sure that we don't PRE a non-speculable load across a guard.335define void @unsafe_pre_across_guard(ptr %p, i1 %load.is.valid) {336; CHECK-LABEL: @unsafe_pre_across_guard(337; CHECK-NEXT: entry:338; CHECK-NEXT: br label [[LOOP:%.*]]339; CHECK: loop:340; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[LOAD_IS_VALID:%.*]]) [ "deopt"() ]341; CHECK-NEXT: [[LOADED:%.*]] = load i8, ptr [[P:%.*]], align 1342; CHECK-NEXT: [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0343; CHECK-NEXT: br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]344; CHECK: exit:345; CHECK-NEXT: ret void346;347entry:348 br label %loop349 350loop: ; preds = %loop, %entry351 call void (i1, ...) @llvm.experimental.guard(i1 %load.is.valid) [ "deopt"() ]352 %loaded = load i8, ptr %p353 %continue = icmp eq i8 %loaded, 0354 br i1 %continue, label %exit, label %loop355 356exit: ; preds = %loop357 ret void358}359 360; Make sure that we can safely PRE a speculable load across a guard.361define void @safe_pre_across_guard(ptr noalias nocapture readonly dereferenceable(8) %p, i1 %load.is.valid) nofree nosync {362; CHECK-LABEL: @safe_pre_across_guard(363; CHECK-NEXT: entry:364; CHECK-NEXT: [[LOADED_PR:%.*]] = load i8, ptr [[P:%.*]], align 1365; CHECK-NEXT: br label [[LOOP:%.*]]366; CHECK: loop:367; CHECK-NEXT: [[LOADED:%.*]] = phi i8 [ [[LOADED]], [[LOOP]] ], [ [[LOADED_PR]], [[ENTRY:%.*]] ]368; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[LOAD_IS_VALID:%.*]]) [ "deopt"() ]369; CHECK-NEXT: [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0370; CHECK-NEXT: br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]371; CHECK: exit:372; CHECK-NEXT: ret void373;374 375entry:376 br label %loop377 378loop: ; preds = %loop, %entry379 call void (i1, ...) @llvm.experimental.guard(i1 %load.is.valid) [ "deopt"() ]380 %loaded = load i8, ptr %p381 %continue = icmp eq i8 %loaded, 0382 br i1 %continue, label %exit, label %loop383 384exit: ; preds = %loop385 ret void386}387 388; Make sure that we don't PRE a non-speculable load across a call which may389; alias with the load.390define void @unsafe_pre_across_call(ptr %p) {391; CHECK-LABEL: @unsafe_pre_across_call(392; CHECK-NEXT: entry:393; CHECK-NEXT: br label [[LOOP:%.*]]394; CHECK: loop:395; CHECK-NEXT: [[TMP0:%.*]] = call i32 @f1()396; CHECK-NEXT: [[LOADED:%.*]] = load i8, ptr [[P:%.*]], align 1397; CHECK-NEXT: [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0398; CHECK-NEXT: br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]399; CHECK: exit:400; CHECK-NEXT: ret void401;402entry:403 br label %loop404 405loop: ; preds = %loop, %entry406 call i32 @f1()407 %loaded = load i8, ptr %p408 %continue = icmp eq i8 %loaded, 0409 br i1 %continue, label %exit, label %loop410 411exit: ; preds = %loop412 ret void413}414 415; Make sure that we can safely PRE a speculable load across a call.416define void @safe_pre_across_call(ptr noalias nocapture readonly dereferenceable(8) %p) nofree nosync {417; CHECK-LABEL: @safe_pre_across_call(418; CHECK-NEXT: entry:419; CHECK-NEXT: [[LOADED_PR:%.*]] = load i8, ptr [[P:%.*]], align 1420; CHECK-NEXT: br label [[LOOP:%.*]]421; CHECK: loop:422; CHECK-NEXT: [[LOADED:%.*]] = phi i8 [ [[LOADED]], [[LOOP]] ], [ [[LOADED_PR]], [[ENTRY:%.*]] ]423; CHECK-NEXT: [[TMP0:%.*]] = call i32 @f1()424; CHECK-NEXT: [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0425; CHECK-NEXT: br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]426; CHECK: exit:427; CHECK-NEXT: ret void428;429 430entry:431 br label %loop432 433loop: ; preds = %loop, %entry434 call i32 @f1()435 %loaded = load i8, ptr %p436 %continue = icmp eq i8 %loaded, 0437 br i1 %continue, label %exit, label %loop438 439exit: ; preds = %loop440 ret void441}442