Index: lib/Transforms/Scalar/SCCP.cpp =================================================================== --- lib/Transforms/Scalar/SCCP.cpp +++ lib/Transforms/Scalar/SCCP.cpp @@ -1999,9 +1999,9 @@ BlocksToErase.clear(); } - // If we inferred constant or undef return values for a function, we replaced - // all call uses with the inferred value. This means we don't need to bother - // actually returning anything from the function. Replace all return + // If we inferred constant or undef return values for a function and replace + // all call uses with the inferred value, it means we don't need to bother + // actually returning anything from the function. Replace all return // instructions with return undef. // // Do this in two stages: first identify the functions we should process, then @@ -2016,6 +2016,14 @@ Function *F = I.first; if (I.second.isOverdefined() || F->getReturnType()->isVoidTy()) continue; + + if (any_of(F->users(), [&Solver](Value *U) { + assert(Solver.isBlockExecutable(cast(U)->getParent()) && + "All unreachable calls should have been removed already."); + return CallSite(U) && Solver.getLatticeValueFor(U).isOverdefined(); + })) + continue; + findReturnsToZap(*F, ReturnsToZap, Solver); } Index: test/Transforms/IPConstantProp/return-zapped.ll =================================================================== --- /dev/null +++ test/Transforms/IPConstantProp/return-zapped.ll @@ -0,0 +1,78 @@ +; RUN: opt < %s -S -ipsccp | FileCheck %s + +; After the firs round of Solver.Solve(), the return value of @testf still +; undefined as we hit a branch on undef. Therefore the conditional branch on +; @testf's return value in @bar is unknown. In ResolvedUndefsIn, we force the +; condition of the branch to false. We later discover that @testf actually +; returns true and merge that value with the value at the call site, which +; becomes overdefined. We cannot change the return value of @testf to undef, +; because we failed to replace the result of the call with true. +define void @test1() { +; CHECK-LABEL: @test1( +; CHECK-LABEL: if.then: +; CHECK: [[CALL:%.+]] = call i1 @testf() +; CHECK-NEXT: br i1 [[CALL]], label %if.end, label %if.then +; +entry: + br label %if.then +if.then: ; preds = %entry, %if.then + %foo = phi i32 [ 0, %entry], [ %next, %if.then] + %next = add i32 %foo, 1 + %call = call i1 @testf() + br i1 %call, label %if.end, label %if.then + +if.end: ; preds = %if.then, %entry + ret void +} + +define internal i1 @testf() { +; CHECK-LABEL: define internal i1 @testf( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[IF_END3:%.*]] +; CHECK: if.end3: +; CHECK-NEXT: ret i1 true +; +entry: + br i1 undef, label %if.then1, label %if.end3 + +if.then1: ; preds = %if.end + br label %if.end3 + +if.end3: ; preds = %if.then1, %entry + ret i1 true +} + + +; Call sites in unreachable blocks should not be a problem. +; CHECK-LABEL: define i1 @test2() { +; CHECK-NEXT: entry: +; CHECK-NEXT: br label %if.end +; CHECK-LABEL: if.end: ; preds = %entry +; CHECK-NEXT: %call2 = call i1 @testf() +; CHECK-NEXT: ret i1 true +define i1 @test2() { +entry: + br label %if.end + +if.then: ; preds = %entry, %if.then + %call = call i1 @testf() + br i1 %call, label %if.end, label %if.then + +if.end: ; preds = %if.then, %entry + %call2 = call i1 @testf() + ret i1 %call2 +} + +; Non-callsite uses of a function are ignored. +; CHECK-LABEL: @test3(i1 ()** %p) { +; CHECK: store i1 ()* @testf, i1 ()** %p +; CHECK: ret i1 true +define i1 @test3(i1 ()** %p) { +entry: + store i1 ()* @testf, i1 ()** %p + br label %if.end + +if.end: ; preds = %if.then, %entry + %call2 = call i1 @testf() + ret i1 %call2 +}