diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp --- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp +++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp @@ -2271,6 +2271,24 @@ ThreadEdge(BB, PredsToFactor, SuccBB); } +namespace { + +/// IsUsedBy - Returns 'true' if the instruction is used based on the predicate. +template +bool IsUsedBy(const Instruction *I, UnaryPredicate Predicate) { + for (const Use &U : I->uses()) { + const Instruction *User = cast(U.getUser()); + if (Predicate(User)) + return true; + if (isa(User)) + return IsUsedBy(User, Predicate); + } + + return false; +} + +} + /// TryThreadEdge - Thread an edge if it's safe and profitable to do so. bool JumpThreadingPass::TryThreadEdge( BasicBlock *BB, const SmallVectorImpl &PredBBs, @@ -2304,6 +2322,46 @@ return false; } + // If the PHI node has a constant value and the PHI is used by an is.constant + // intrinsic, then don't thread the edge. Otherwise, we could end up in a + // situation where the intrinsic will resolve to 'true' when it should resolve + // to 'false'. For example, %z should be 'false' in this: + // + // A: + // %v = phi i32 [ 3, %X ], [ %v, %Y ] + // %z = call i1 @llvm.is.constant(i32 %v) + // + // but when jump threading happens we get two different values: + // + // A.thread: + // %v.thread = phi i32 [ 3, %X ] + // %z.thread = call i1 @llvm.is.constant(i32 %v.thread) + // br ... + // + // A: + // %v = phi i32 [ %v, %Y ] + // %z = call i1 @llvm.is.constant(i32 %v) + // br ... + // + // One consequence, if %z and %z.thread are used in a PHI, we cannot perform + // DCE on any dead paths. This is an issue if the programmer relies on DCE + // occurring. + for (PHINode &PN : BB->phis()) { + if (llvm::all_of(PN.incoming_values(), [](const Value *V){ + return !isa(V); + })) + // We are only concerned about PHI nodes with a constant incoming value. + continue; + + for (Use &U : PN.uses()) + if (IsUsedBy(cast(U.getUser()), + [](const Instruction *I){ + using namespace PatternMatch; + return match(I, m_Intrinsic()); + })) + return false; + } + ThreadEdge(BB, PredBBs, SuccBB); return true; } diff --git a/llvm/test/Transforms/JumpThreading/is_constant.ll b/llvm/test/Transforms/JumpThreading/is_constant.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/JumpThreading/is_constant.ll @@ -0,0 +1,35 @@ +; RUN: opt -jump-threading -S -verify < %s | FileCheck %s + +; CHECK-LABEL: define void @test1( +; CHECK-LABEL: if.end18: +; CHECK-NOT: call i1 @llvm.is.constant.i64 +define void @test1() { +entry: + %conv = trunc i64 undef to i32 + %conv8 = ashr exact i64 undef, 32 + %cmp9 = icmp ugt i64 %conv8, 24 + br i1 %cmp9, label %if.then11, label %if.end18 + +if.then11: ; preds = %entry + br label %if.end18 + +if.end18: ; preds = %if.then11, %entry + %len.0 = phi i32 [ 24, %if.then11 ], [ %conv, %entry ] + %cmp3.i.i70 = icmp ugt i32 %len.0, 24 + %0 = call i1 @llvm.is.constant.i64(i64 undef) + br i1 %cmp3.i.i70, label %if.then.i.i71, label %if.end12.i.i74 + +if.then.i.i71: ; preds = %if.end18 + unreachable + +if.end12.i.i74: ; preds = %if.end18 + br i1 %0, label %if.else.i.i, label %if.then7.i.i + +if.then7.i.i: ; preds = %if.end12.i.i74 + ret void + +if.else.i.i: ; preds = %if.end12.i.i74 + unreachable +} + +declare i1 @llvm.is.constant.i64(i64)