diff --git a/llvm/include/llvm/Analysis/ValueLattice.h b/llvm/include/llvm/Analysis/ValueLattice.h --- a/llvm/include/llvm/Analysis/ValueLattice.h +++ b/llvm/include/llvm/Analysis/ValueLattice.h @@ -113,10 +113,16 @@ /// number of steps. bool CheckWiden; + /// The number of allowed widening steps (including setting the range + /// initially). + unsigned MaxWidenSteps; + MergeOptions() : MergeOptions(false, false) {} - MergeOptions(bool MayIncludeUndef, bool CheckWiden) - : MayIncludeUndef(MayIncludeUndef), CheckWiden(CheckWiden) {} + MergeOptions(bool MayIncludeUndef, bool CheckWiden, + unsigned MaxWidenSteps = 1) + : MayIncludeUndef(MayIncludeUndef), CheckWiden(CheckWiden), + MaxWidenSteps(MaxWidenSteps) {} MergeOptions &setMayIncludeUndef(bool V = true) { MayIncludeUndef = V; @@ -127,6 +133,12 @@ CheckWiden = V; return *this; } + + MergeOptions &setMaxWidenSteps(unsigned Steps = 1) { + CheckWiden = true; + MaxWidenSteps = Steps; + return *this; + } }; // ConstVal and Range are initialized on-demand. @@ -349,7 +361,7 @@ // Simple form of widening. If a range is extended multiple times, go to // overdefined. - if (Opts.CheckWiden && ++NumRangeExtensions == 1) + if (Opts.CheckWiden && ++NumRangeExtensions > Opts.MaxWidenSteps) return markOverdefined(); assert(NewR.contains(getConstantRange()) && @@ -458,6 +470,9 @@ return nullptr; } + + unsigned getNumRangeExtensions() const { return NumRangeExtensions; } + void setNumRangeExtensions(unsigned N) { NumRangeExtensions = N; } }; static_assert(sizeof(ValueLatticeElement) <= 40, diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp --- a/llvm/lib/Transforms/Scalar/SCCP.cpp +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp @@ -72,6 +72,15 @@ STATISTIC(IPNumArgsElimed ,"Number of arguments constant propagated by IPSCCP"); STATISTIC(IPNumGlobalConst, "Number of globals found to be constant by IPSCCP"); +// The maximum number of range extensions allowed for operations requiring +// widening. +static const unsigned MaxNumRangeExtensions = 10; + +/// Returns MergeOptions with MaxWidenSteps set to MaxNumRangeExtensions. +static ValueLatticeElement::MergeOptions getMaxWidenStepsOpts() { + return ValueLatticeElement::MergeOptions().setMaxWidenSteps( + MaxNumRangeExtensions); +} namespace { // Helper to check if \p LV is either a constant or a constant @@ -401,7 +410,7 @@ bool mergeInValue(ValueLatticeElement &IV, Value *V, ValueLatticeElement MergeWithV, ValueLatticeElement::MergeOptions Opts = { - /*MayIncludeUndef=*/false, /*CheckWiden=*/true}) { + /*MayIncludeUndef=*/false, /*CheckWiden=*/false}) { if (IV.mergeIn(MergeWithV, Opts)) { pushToWorkList(IV, V); LLVM_DEBUG(dbgs() << "Merged " << MergeWithV << " into " << *V << " : " @@ -413,7 +422,7 @@ bool mergeInValue(Value *V, ValueLatticeElement MergeWithV, ValueLatticeElement::MergeOptions Opts = { - /*MayIncludeUndef=*/false, /*CheckWiden=*/true}) { + /*MayIncludeUndef=*/false, /*CheckWiden=*/false}) { assert(!V->getType()->isStructTy() && "non-structs should use markConstant"); return mergeInValue(ValueState[V], V, MergeWithV, Opts); @@ -725,24 +734,36 @@ if (PN.getNumIncomingValues() > 64) return (void)markOverdefined(&PN); + unsigned NumActiveIncoming = 0; + // Look at all of the executable operands of the PHI node. If any of them // are overdefined, the PHI becomes overdefined as well. If they are all // constant, and they agree with each other, the PHI becomes the identical - // constant. If they are constant and don't agree, the PHI is overdefined. - // If there are no executable operands, the PHI remains unknown. - bool Changed = false; + // constant. If they are constant and don't agree, the PHI is a constant + // range. If there are no executable operands, the PHI remains unknown. + ValueLatticeElement PhiState = getValueState(&PN); for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) { - ValueLatticeElement IV = getValueState(PN.getIncomingValue(i)); if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent())) continue; - ValueLatticeElement &Res = getValueState(&PN); - Changed |= Res.mergeIn(IV); - if (Res.isOverdefined()) + ValueLatticeElement IV = getValueState(PN.getIncomingValue(i)); + PhiState.mergeIn(IV); + NumActiveIncoming++; + if (PhiState.isOverdefined()) break; } - if (Changed) - pushToWorkListMsg(ValueState[&PN], &PN); + + // We allow up to 1 range extension per active incoming value and one + // additional extension. Note that we manually adjust the number of range + // extensions to match the number of active incoming values. This helps to + // limit multiple extensions caused by the same incoming value, if other + // incoming values are equal. + mergeInValue(&PN, PhiState, + ValueLatticeElement::MergeOptions().setMaxWidenSteps( + NumActiveIncoming + 1)); + ValueLatticeElement &PhiStateRef = getValueState(&PN); + PhiStateRef.setNumRangeExtensions( + std::max(NumActiveIncoming, PhiStateRef.getNumRangeExtensions())); } void SCCPSolver::visitReturnInst(ReturnInst &I) { @@ -1112,8 +1133,7 @@ // If we are tracking this global, merge in the known value for it. auto It = TrackedGlobals.find(GV); if (It != TrackedGlobals.end()) { - mergeInValue(IV, &I, It->second, - ValueLatticeElement::MergeOptions().setCheckWiden(false)); + mergeInValue(IV, &I, It->second, getMaxWidenStepsOpts()); return; } } @@ -1201,11 +1221,11 @@ if (auto *STy = dyn_cast(AI->getType())) { for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) { ValueLatticeElement CallArg = getStructValueState(*CAI, i); - mergeInValue(getStructValueState(&*AI, i), &*AI, CallArg); + mergeInValue(getStructValueState(&*AI, i), &*AI, CallArg, + getMaxWidenStepsOpts()); } } else - mergeInValue(&*AI, getValueState(*CAI), - ValueLatticeElement::MergeOptions().setCheckWiden(false)); + mergeInValue(&*AI, getValueState(*CAI), getMaxWidenStepsOpts()); } } } @@ -1316,14 +1336,15 @@ // into this call site. for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) mergeInValue(getStructValueState(&CB, i), &CB, - TrackedMultipleRetVals[std::make_pair(F, i)]); + TrackedMultipleRetVals[std::make_pair(F, i)], + getMaxWidenStepsOpts()); } else { auto TFRVI = TrackedRetVals.find(F); if (TFRVI == TrackedRetVals.end()) return handleCallOverdefined(CB); // Not tracking this callee. // If so, propagate the return value of the callee into this call result. - mergeInValue(&CB, TFRVI->second); + mergeInValue(&CB, TFRVI->second, getMaxWidenStepsOpts()); } } diff --git a/llvm/test/Transforms/SCCP/constant-range-struct.ll b/llvm/test/Transforms/SCCP/constant-range-struct.ll --- a/llvm/test/Transforms/SCCP/constant-range-struct.ll +++ b/llvm/test/Transforms/SCCP/constant-range-struct.ll @@ -102,22 +102,14 @@ ; CHECK-NEXT: [[S:%.*]] = call { i64, i64 } @struct2() ; CHECK-NEXT: [[V1:%.*]] = extractvalue { i64, i64 } [[S]], 0 ; CHECK-NEXT: [[V2:%.*]] = extractvalue { i64, i64 } [[S]], 1 -; CHECK-NEXT: [[T_1:%.*]] = icmp ne i64 [[V1]], 10 -; CHECK-NEXT: call void @use(i1 [[T_1]]) -; CHECK-NEXT: [[T_2:%.*]] = icmp ult i64 [[V1]], 100 -; CHECK-NEXT: call void @use(i1 [[T_2]]) -; CHECK-NEXT: [[T_3:%.*]] = icmp ne i64 [[V2]], 0 -; CHECK-NEXT: call void @use(i1 [[T_3]]) -; CHECK-NEXT: [[T_4:%.*]] = icmp ult i64 [[V2]], 301 -; CHECK-NEXT: call void @use(i1 [[T_4]]) -; CHECK-NEXT: [[F_1:%.*]] = icmp eq i64 [[V1]], 10 -; CHECK-NEXT: call void @use(i1 [[F_1]]) -; CHECK-NEXT: [[F_2:%.*]] = icmp ult i64 [[V1]], 19 -; CHECK-NEXT: call void @use(i1 [[F_2]]) -; CHECK-NEXT: [[F_3:%.*]] = icmp eq i64 [[V2]], 50 -; CHECK-NEXT: call void @use(i1 [[F_3]]) -; CHECK-NEXT: [[F_4:%.*]] = icmp ugt i64 [[V2]], 301 -; CHECK-NEXT: call void @use(i1 [[F_4]]) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 false) +; CHECK-NEXT: call void @use(i1 false) +; CHECK-NEXT: call void @use(i1 false) +; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: [[C_1:%.*]] = icmp eq i64 [[V1]], 25 ; CHECK-NEXT: call void @use(i1 [[C_1]]) ; CHECK-NEXT: [[C_2:%.*]] = icmp ult i64 [[V1]], 25 diff --git a/llvm/test/Transforms/SCCP/ipsccp-cycles.ll b/llvm/test/Transforms/SCCP/ipsccp-cycles.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/SCCP/ipsccp-cycles.ll @@ -0,0 +1,242 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -ipsccp -S | FileCheck %s + +define internal i32 @test1a(i32 %A, i32 %b) { +; CHECK-LABEL: @test1a( +; CHECK-NEXT: [[X:%.*]] = add i32 [[A:%.*]], 1 +; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[X]], [[B:%.*]] +; CHECK-NEXT: br i1 [[C]], label [[BB_TRUE:%.*]], label [[BB_FALSE:%.*]] +; CHECK: bb.true: +; CHECK-NEXT: [[R:%.*]] = call i32 @test1a(i32 [[X]], i32 [[B]]) +; CHECK-NEXT: ret i32 [[R]] +; CHECK: bb.false: +; CHECK-NEXT: ret i32 [[A]] +; + %X = add i32 %A, 1 + %c = icmp eq i32 %X, %b + br i1 %c, label %bb.true, label %bb.false + +bb.true: + %r = call i32 @test1a(i32 %X, i32 %b) + ret i32 %r + +bb.false: + ret i32 %A +} + +define i32 @test1b(i32 %b) { +; CHECK-LABEL: @test1b( +; CHECK-NEXT: [[X:%.*]] = call i32 @test1a(i32 17, i32 [[B:%.*]]) +; CHECK-NEXT: ret i32 [[X]] +; + %X = call i32 @test1a( i32 17, i32 %b) + ret i32 %X +} + +@Getopt.optind = internal global i32 1, align 4 + +define i32 @test2(i32 %a) { +; CHECK-LABEL: @test2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[LV:%.*]] = load i32, i32* @Getopt.optind, align 4 +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[LV]], 1 +; CHECK-NEXT: store i32 [[ADD]], i32* @Getopt.optind, align 4 +; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[ADD]], [[A:%.*]] +; CHECK-NEXT: br i1 [[C]], label [[EXIT:%.*]], label [[LOOP]] +; CHECK: exit: +; CHECK-NEXT: ret i32 [[ADD]] +; +entry: + br label %loop + +loop: + %lv = load i32, i32* @Getopt.optind, align 4 + %add = add i32 %lv, 1 + store i32 %add, i32* @Getopt.optind + %c = icmp eq i32 %add, %a + br i1 %c, label %exit, label %loop + +exit: + ret i32 %add +} + + +define internal i32 @test3a(i32 %a) { +; CHECK-LABEL: @test3a( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = add i32 [[A:%.*]], 1 +; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[RES]], 1000 +; CHECK-NEXT: br i1 [[C]], label [[BB_TRUE:%.*]], label [[BB_FALSE:%.*]] +; CHECK: bb.true: +; CHECK-NEXT: ret i32 [[RES]] +; CHECK: bb.false: +; CHECK-NEXT: ret i32 0 +; +entry: + %res = add i32 %a, 1 + %c = icmp ult i32 %res, 1000 + br i1 %c, label %bb.true, label %bb.false + +bb.true: + ret i32 %res + +bb.false: + ret i32 0 +} + +define i32 @test3b(i32 %a) { +; CHECK-LABEL: @test3b( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[V1:%.*]] = call i32 @test3a(i32 0) +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[V2:%.*]] = call i32 @test3a(i32 [[V1]]) +; CHECK-NEXT: [[V3:%.*]] = add i32 [[V2]], 1 +; CHECK-NEXT: [[V4:%.*]] = call i32 @test3a(i32 [[V3]]) +; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[V4]], [[A:%.*]] +; CHECK-NEXT: br i1 [[C]], label [[EXIT:%.*]], label [[LOOP]] +; CHECK: exit: +; CHECK-NEXT: ret i32 [[V4]] +; +entry: + %v1 = call i32 @test3a(i32 0) + br label %loop + +loop: + %v2 = call i32 @test3a(i32 %v1) + %v3 = add i32 %v2, 1 + %v4 = call i32 @test3a(i32 %v3) + %c = icmp eq i32 %v4, %a + br i1 %c, label %exit, label %loop + +exit: + ret i32 %v4 +} + +%struct.S = type { i32, i32 } + +; Check for a range extension cycle through a struct argument. +define internal i32 @test4a(%struct.S %s) { +; CHECK-LABEL: @test4a( +; CHECK-NEXT: [[A:%.*]] = extractvalue [[STRUCT_S:%.*]] %s, 0 +; CHECK-NEXT: [[B:%.*]] = extractvalue [[STRUCT_S]] %s, 1 +; CHECK-NEXT: [[X:%.*]] = add i32 [[A]], 1 +; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[X]], [[B]] +; CHECK-NEXT: br i1 [[C]], label [[BB_TRUE:%.*]], label [[BB_FALSE:%.*]] +; CHECK: bb.true: +; CHECK-NEXT: [[S2:%.*]] = insertvalue [[STRUCT_S]] %s, i32 [[X]], 0 +; CHECK-NEXT: [[R:%.*]] = call i32 @test4a(%struct.S [[S2]]) +; CHECK-NEXT: ret i32 [[R]] +; CHECK: bb.false: +; CHECK-NEXT: ret i32 [[A]] +; + %a = extractvalue %struct.S %s, 0 + %b = extractvalue %struct.S %s, 1 + + %x = add i32 %a, 1 + %c = icmp eq i32 %x, %b + br i1 %c, label %bb.true, label %bb.false + +bb.true: + %s2 = insertvalue %struct.S %s, i32 %x, 0 + %r = call i32 @test4a(%struct.S %s2) + ret i32 %r + +bb.false: + ret i32 %a +} + +define i32 @test4b(i32 %b) { +; CHECK-LABEL: @test4b( +; CHECK-NEXT: [[S2:%.*]] = insertvalue [[STRUCT_S:%.*]] { i32 17, i32 undef }, i32 [[B:%.*]], 1 +; CHECK-NEXT: [[X:%.*]] = call i32 @test4a(%struct.S [[S2]]) +; CHECK-NEXT: ret i32 [[X]] +; + %s1 = insertvalue %struct.S undef, i32 17, 0 + %s2 = insertvalue %struct.S %s1, i32 %b, 1 + %X = call i32 @test4a(%struct.S %s2) + ret i32 %X +} + +; Check for a range extension cycle through a returned value. + +define internal i32 @test5a(i8* %arg, i32 %arg1, i32 %arg2) { +; CHECK-LABEL: @test5a( +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP:%.*]] = icmp eq i8* [[ARG:%.*]], null +; CHECK-NEXT: br i1 [[TMP]], label [[BB6:%.*]], label [[BB3:%.*]] +; CHECK: bb3: +; CHECK-NEXT: [[TMP4:%.*]] = tail call i32 @test5a(i8* [[ARG]], i32 0, i32 -1) +; CHECK-NEXT: [[TMP5:%.*]] = add nsw i32 [[TMP4]], -1 +; CHECK-NEXT: ret i32 [[TMP5]] +; CHECK: bb6: +; CHECK-NEXT: ret i32 0 +; +bb: + %tmp = icmp eq i8* %arg, null + br i1 %tmp, label %bb6, label %bb3 + +bb3: ; preds = %bb + %tmp4 = tail call i32 @test5a(i8* %arg, i32 %arg1, i32 %arg2) + %tmp5 = add nsw i32 %tmp4, %arg2 + ret i32 %tmp5 + +bb6: ; preds = %bb + ret i32 %arg1 +} + +define void @test5b(i8* %ptr) { +; CHECK-LABEL: @test5b( +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP:%.*]] = tail call i32 @test5a(i8* [[PTR:%.*]], i32 0, i32 -1) +; CHECK-NEXT: ret void +; +bb: + %tmp = tail call i32 @test5a(i8* %ptr, i32 0, i32 -1) + ret void +} + +%struct = type { i32, i32 } + +define internal %struct @test6a(i8* %arg, i32 %arg1, i32 %arg2) { +; CHECK-LABEL: @test6a( +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP:%.*]] = icmp eq i8* [[ARG:%.*]], null +; CHECK-NEXT: br i1 [[TMP]], label [[BB6:%.*]], label [[BB3:%.*]] +; CHECK: bb3: +; CHECK-NEXT: [[S1:%.*]] = tail call [[STRUCT:%.*]] @test6a(i8* [[ARG]], i32 0, i32 -1) +; CHECK-NEXT: [[TMP4:%.*]] = extractvalue [[STRUCT]] %s1, 0 +; CHECK-NEXT: [[TMP5:%.*]] = add nsw i32 [[TMP4]], -1 +; CHECK-NEXT: [[S2:%.*]] = insertvalue [[STRUCT]] %s1, i32 [[TMP5]], 0 +; CHECK-NEXT: ret [[STRUCT]] %s2 +; CHECK: bb6: +; CHECK-NEXT: ret [[STRUCT]] { i32 0, i32 undef } +; +bb: + %tmp = icmp eq i8* %arg, null + br i1 %tmp, label %bb6, label %bb3 + +bb3: ; preds = %bb + %s1 = tail call %struct @test6a(i8* %arg, i32 %arg1, i32 %arg2) + %tmp4 = extractvalue %struct %s1, 0 + %tmp5 = add nsw i32 %tmp4, %arg2 + %s2 = insertvalue %struct %s1, i32 %tmp5, 0 + ret %struct %s2 + +bb6: ; preds = %bb + %s3 = insertvalue %struct undef, i32 %arg1, 0 + ret %struct %s3 +} + +define void @test6b(i8* %ptr) { +; CHECK-LABEL: @test6b( +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP:%.*]] = tail call [[STRUCT:%.*]] @test6a(i8* [[PTR:%.*]], i32 0, i32 -1) +; CHECK-NEXT: ret void +; +bb: + %tmp = tail call %struct @test6a(i8* %ptr, i32 0, i32 -1) + ret void +} diff --git a/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll b/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll --- a/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll +++ b/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll @@ -386,10 +386,7 @@ ; CHECK-NEXT: store i32 [[MUL]], i32* @pcount, align 4 ; CHECK-NEXT: ret void ; CHECK: if.end24: -; CHECK-NEXT: [[CMP25474:%.*]] = icmp sgt i32 [[TMP2]], 0 -; CHECK-NEXT: br i1 [[CMP25474]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] -; CHECK: for.body: -; CHECK-NEXT: ret void +; CHECK-NEXT: br label [[FOR_END:%.*]] ; CHECK: for.end: ; CHECK-NEXT: ret void ; diff --git a/llvm/test/Transforms/SCCP/widening.ll b/llvm/test/Transforms/SCCP/widening.ll --- a/llvm/test/Transforms/SCCP/widening.ll +++ b/llvm/test/Transforms/SCCP/widening.ll @@ -17,10 +17,8 @@ ; SCCP: exit: ; SCCP-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 1, [[BB1]] ] ; SCCP-NEXT: [[A:%.*]] = add i32 [[P]], 1 -; SCCP-NEXT: [[T_1:%.*]] = icmp ult i32 [[A]], 20 -; SCCP-NEXT: call void @use(i1 [[T_1]]) -; SCCP-NEXT: [[F_1:%.*]] = icmp ugt i32 [[A]], 10 -; SCCP-NEXT: call void @use(i1 [[F_1]]) +; SCCP-NEXT: call void @use(i1 true) +; SCCP-NEXT: call void @use(i1 false) ; SCCP-NEXT: ret void ; ; IPSCCP-LABEL: @test_2_incoming_constants( @@ -32,10 +30,8 @@ ; IPSCCP: exit: ; IPSCCP-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 1, [[BB1]] ] ; IPSCCP-NEXT: [[A:%.*]] = add i32 [[P]], 1 -; IPSCCP-NEXT: [[T_1:%.*]] = icmp ult i32 [[A]], 20 -; IPSCCP-NEXT: call void @use(i1 [[T_1]]) -; IPSCCP-NEXT: [[F_1:%.*]] = icmp ugt i32 [[A]], 10 -; IPSCCP-NEXT: call void @use(i1 [[F_1]]) +; IPSCCP-NEXT: call void @use(i1 true) +; IPSCCP-NEXT: call void @use(i1 false) ; IPSCCP-NEXT: ret void ; entry: @@ -68,10 +64,8 @@ ; SCCP: exit: ; SCCP-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 1, [[BB1]] ], [ 2, [[BB2]] ] ; SCCP-NEXT: [[A:%.*]] = add i32 [[P]], 1 -; SCCP-NEXT: [[T_1:%.*]] = icmp ult i32 [[A]], 20 -; SCCP-NEXT: call void @use(i1 [[T_1]]) -; SCCP-NEXT: [[F_1:%.*]] = icmp ugt i32 [[A]], 10 -; SCCP-NEXT: call void @use(i1 [[F_1]]) +; SCCP-NEXT: call void @use(i1 true) +; SCCP-NEXT: call void @use(i1 false) ; SCCP-NEXT: ret void ; ; IPSCCP-LABEL: @test_3_incoming_constants( @@ -86,10 +80,8 @@ ; IPSCCP: exit: ; IPSCCP-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 1, [[BB1]] ], [ 2, [[BB2]] ] ; IPSCCP-NEXT: [[A:%.*]] = add i32 [[P]], 1 -; IPSCCP-NEXT: [[T_1:%.*]] = icmp ult i32 [[A]], 20 -; IPSCCP-NEXT: call void @use(i1 [[T_1]]) -; IPSCCP-NEXT: [[F_1:%.*]] = icmp ugt i32 [[A]], 10 -; IPSCCP-NEXT: call void @use(i1 [[F_1]]) +; IPSCCP-NEXT: call void @use(i1 true) +; IPSCCP-NEXT: call void @use(i1 false) ; IPSCCP-NEXT: ret void ; entry: @@ -132,10 +124,8 @@ ; SCCP: exit: ; SCCP-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 1, [[BB1]] ], [ 2, [[BB2]] ], [ 3, [[BB3]] ], [ 4, [[BB4]] ] ; SCCP-NEXT: [[A:%.*]] = add i32 [[P]], 1 -; SCCP-NEXT: [[T_1:%.*]] = icmp ult i32 [[A]], 20 -; SCCP-NEXT: call void @use(i1 [[T_1]]) -; SCCP-NEXT: [[F_1:%.*]] = icmp ugt i32 [[A]], 10 -; SCCP-NEXT: call void @use(i1 [[F_1]]) +; SCCP-NEXT: call void @use(i1 true) +; SCCP-NEXT: call void @use(i1 false) ; SCCP-NEXT: ret void ; ; IPSCCP-LABEL: @test_5_incoming_constants( @@ -156,10 +146,8 @@ ; IPSCCP: exit: ; IPSCCP-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 1, [[BB1]] ], [ 2, [[BB2]] ], [ 3, [[BB3]] ], [ 4, [[BB4]] ] ; IPSCCP-NEXT: [[A:%.*]] = add i32 [[P]], 1 -; IPSCCP-NEXT: [[T_1:%.*]] = icmp ult i32 [[A]], 20 -; IPSCCP-NEXT: call void @use(i1 [[T_1]]) -; IPSCCP-NEXT: [[F_1:%.*]] = icmp ugt i32 [[A]], 10 -; IPSCCP-NEXT: call void @use(i1 [[F_1]]) +; IPSCCP-NEXT: call void @use(i1 true) +; IPSCCP-NEXT: call void @use(i1 false) ; IPSCCP-NEXT: ret void ; entry: @@ -369,8 +357,7 @@ ; IPSCCP-NEXT: [[C_1:%.*]] = icmp slt i32 [[IV]], 2 ; IPSCCP-NEXT: br i1 [[C_1]], label [[LOOP_BODY]], label [[EXIT:%.*]] ; IPSCCP: loop.body: -; IPSCCP-NEXT: [[T_1:%.*]] = icmp slt i32 [[IV]], 2 -; IPSCCP-NEXT: call void @use(i1 [[T_1]]) +; IPSCCP-NEXT: call void @use(i1 true) ; IPSCCP-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1 ; IPSCCP-NEXT: br label [[LOOP_HEADER]] ; IPSCCP: exit: @@ -418,8 +405,7 @@ ; IPSCCP-NEXT: [[C_1:%.*]] = icmp slt i32 [[IV]], 200 ; IPSCCP-NEXT: br i1 [[C_1]], label [[LOOP_BODY]], label [[EXIT:%.*]] ; IPSCCP: loop.body: -; IPSCCP-NEXT: [[T_1:%.*]] = icmp slt i32 [[IV]], 200 -; IPSCCP-NEXT: call void @use(i1 [[T_1]]) +; IPSCCP-NEXT: call void @use(i1 true) ; IPSCCP-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1 ; IPSCCP-NEXT: br label [[LOOP_HEADER]] ; IPSCCP: exit: @@ -877,3 +863,91 @@ %tmp67 = phi i8* [ %tmp36, %bb35 ], [ null, %bb60 ] ret i8* %tmp67 } + + +define i32 @loop_with_multiple_euqal_incomings(i32 %N) { +; SCCP-LABEL: @loop_with_multiple_euqal_incomings( +; SCCP-NEXT: entry: +; SCCP-NEXT: br label [[LOOP:%.*]] +; SCCP: loop: +; SCCP-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[BB3:%.*]] ], [ 0, [[BB4:%.*]] ], [ 0, [[BB5:%.*]] ], [ 0, [[BB6:%.*]] ] +; SCCP-NEXT: [[C_1:%.*]] = call i1 @cond() +; SCCP-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] +; SCCP: bb1: +; SCCP-NEXT: [[C_2:%.*]] = call i1 @cond() +; SCCP-NEXT: br i1 [[C_2]], label [[BB3]], label [[BB4]] +; SCCP: bb2: +; SCCP-NEXT: [[C_4:%.*]] = call i1 @cond() +; SCCP-NEXT: br i1 [[C_4]], label [[BB5]], label [[BB6]] +; SCCP: bb3: +; SCCP-NEXT: [[P_NEXT]] = add i32 [[P]], 1 +; SCCP-NEXT: br label [[LOOP]] +; SCCP: bb4: +; SCCP-NEXT: [[C_3:%.*]] = call i1 @cond() +; SCCP-NEXT: br i1 [[C_3]], label [[LOOP]], label [[END:%.*]] +; SCCP: bb5: +; SCCP-NEXT: br label [[LOOP]] +; SCCP: bb6: +; SCCP-NEXT: br label [[LOOP]] +; SCCP: end: +; SCCP-NEXT: ret i32 [[P]] +; +; IPSCCP-LABEL: @loop_with_multiple_euqal_incomings( +; IPSCCP-NEXT: entry: +; IPSCCP-NEXT: br label [[LOOP:%.*]] +; IPSCCP: loop: +; IPSCCP-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[BB3:%.*]] ], [ 0, [[BB4:%.*]] ], [ 0, [[BB5:%.*]] ], [ 0, [[BB6:%.*]] ] +; IPSCCP-NEXT: [[C_1:%.*]] = call i1 @cond() +; IPSCCP-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] +; IPSCCP: bb1: +; IPSCCP-NEXT: [[C_2:%.*]] = call i1 @cond() +; IPSCCP-NEXT: br i1 [[C_2]], label [[BB3]], label [[BB4]] +; IPSCCP: bb2: +; IPSCCP-NEXT: [[C_4:%.*]] = call i1 @cond() +; IPSCCP-NEXT: br i1 [[C_4]], label [[BB5]], label [[BB6]] +; IPSCCP: bb3: +; IPSCCP-NEXT: [[P_NEXT]] = add i32 [[P]], 1 +; IPSCCP-NEXT: br label [[LOOP]] +; IPSCCP: bb4: +; IPSCCP-NEXT: [[C_3:%.*]] = call i1 @cond() +; IPSCCP-NEXT: br i1 [[C_3]], label [[LOOP]], label [[END:%.*]] +; IPSCCP: bb5: +; IPSCCP-NEXT: br label [[LOOP]] +; IPSCCP: bb6: +; IPSCCP-NEXT: br label [[LOOP]] +; IPSCCP: end: +; IPSCCP-NEXT: ret i32 [[P]] +; +entry: + br label %loop + +loop: + %p = phi i32 [ 0, %entry ], [ %p.next, %bb3 ], [ 0, %bb4 ], [ 0, %bb5], [ 0, %bb6 ] + %c.1 = call i1 @cond() + br i1 %c.1, label %bb1, label %bb2 + +bb1: + %c.2 = call i1 @cond() + br i1 %c.2, label %bb3, label %bb4 + +bb2: + %c.4 = call i1 @cond() + br i1 %c.4, label %bb5, label %bb6 + +bb3: + %p.next = add i32 %p, 1 + br label %loop + +bb4: + %c.3 = call i1 @cond() + br i1 %c.3, label %loop, label %end + +bb5: + br label %loop + +bb6: + br label %loop + +end: + ret i32 %p +}