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 @@ -1226,6 +1226,17 @@ return false; Value *Cond = BI->getCondition(); + // Assuming that predecessor's branch was taken, if pred's branch condition + // V implies Cond, Cond is either true, undef, or poison. In this case, + // freeze(Cond) is either true or nondeterministic value. + // If freeze(Cond) has only one use, we can freely fold freeze(Cond) to true + // without affecting other instructions. + auto *FICond = dyn_cast(Cond); + if (FICond && FICond->hasOneUse()) + Cond = FICond->getOperand(0); + else + FICond = nullptr; + BasicBlock *CurrentBB = BB; BasicBlock *CurrentPred = BB->getSinglePredecessor(); unsigned Iter = 0; @@ -1242,6 +1253,15 @@ bool CondIsTrue = PBI->getSuccessor(0) == CurrentBB; Optional Implication = isImpliedCondition(PBI->getCondition(), Cond, DL, CondIsTrue); + + // If the branch conditions of BB and CurrentPred is the same freeze + // instruction, predecessor's branch condition implies FICond. + if (!Implication && FICond && isa(PBI->getCondition())) { + if (cast(PBI->getCondition())->getOperand(0) == + FICond->getOperand(0)) + Implication = CondIsTrue; + } + if (Implication) { BasicBlock *KeepSucc = BI->getSuccessor(*Implication ? 0 : 1); BasicBlock *RemoveSucc = BI->getSuccessor(*Implication ? 1 : 0); @@ -1249,6 +1269,9 @@ BranchInst *UncondBI = BranchInst::Create(KeepSucc, BI); UncondBI->setDebugLoc(BI->getDebugLoc()); BI->eraseFromParent(); + if (FICond) + FICond->eraseFromParent(); + DTU->applyUpdatesPermissive({{DominatorTree::Delete, BB, RemoveSucc}}); return true; } diff --git a/llvm/test/Transforms/JumpThreading/freeze-impliescond.ll b/llvm/test/Transforms/JumpThreading/freeze-impliescond.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/JumpThreading/freeze-impliescond.ll @@ -0,0 +1,169 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -jump-threading -S < %s | FileCheck %s + +declare void @f() + +define void @test1(i1 %cond, i1 %dummycond) { +; CHECK-LABEL: @test1( +; CHECK-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]] +; CHECK: A: +; CHECK-NEXT: br i1 [[DUMMYCOND:%.*]], label [[REACHABLE:%.*]], label [[DUMMY:%.*]] +; CHECK: B: +; CHECK-NEXT: br i1 [[DUMMYCOND]], label [[REACHABLE]], label [[DUMMY]] +; CHECK: REACHABLE: +; CHECK-NEXT: call void @f() +; CHECK-NEXT: ret void +; CHECK: DUMMY: +; CHECK-NEXT: ret void +; + br i1 %cond, label %A, label %B +A: + br i1 %dummycond, label %A2, label %DUMMY +A2: + %cond.fr = freeze i1 %cond + br i1 %cond.fr, label %REACHABLE, label %UNREACHABLE +B: + br i1 %dummycond, label %B2, label %DUMMY +B2: + %cond.fr2 = freeze i1 %cond + br i1 %cond.fr2, label %UNREACHABLE, label %REACHABLE + +REACHABLE: + call void @f() + ret void +UNREACHABLE: + ret void +DUMMY: + ret void +} + +define void @test2(i1 %cond, i1 %dummycond) { +; CHECK-LABEL: @test2( +; CHECK-NEXT: [[COND_FR0:%.*]] = freeze i1 [[COND:%.*]] +; CHECK-NEXT: br i1 [[COND_FR0]], label [[A:%.*]], label [[B:%.*]] +; CHECK: A: +; CHECK-NEXT: br i1 [[DUMMYCOND:%.*]], label [[REACHABLE:%.*]], label [[DUMMY:%.*]] +; CHECK: B: +; CHECK-NEXT: br i1 [[DUMMYCOND]], label [[REACHABLE]], label [[DUMMY]] +; CHECK: REACHABLE: +; CHECK-NEXT: call void @f() +; CHECK-NEXT: ret void +; CHECK: DUMMY: +; CHECK-NEXT: ret void +; + %cond.fr0 = freeze i1 %cond + br i1 %cond.fr0, label %A, label %B +A: + br i1 %dummycond, label %A2, label %DUMMY +A2: + %cond.fr = freeze i1 %cond + br i1 %cond.fr, label %REACHABLE, label %UNREACHABLE +B: + br i1 %dummycond, label %B2, label %DUMMY +B2: + %cond.fr2 = freeze i1 %cond + br i1 %cond.fr2, label %UNREACHABLE, label %REACHABLE + +REACHABLE: + call void @f() + ret void +UNREACHABLE: + ret void +DUMMY: + ret void +} + +define void @and_noopt(i32 %x, i1 %cond2, i1 %dummycond) { +; CHECK-LABEL: @and_noopt( +; CHECK-NEXT: [[COND1:%.*]] = icmp slt i32 0, [[X:%.*]] +; CHECK-NEXT: [[COND:%.*]] = and i1 [[COND1]], [[COND2:%.*]] +; CHECK-NEXT: [[COND_FR0:%.*]] = freeze i1 [[COND]] +; CHECK-NEXT: br i1 [[COND_FR0]], label [[A:%.*]], label [[B:%.*]] +; CHECK: A: +; CHECK-NEXT: br i1 [[DUMMYCOND:%.*]], label [[A2:%.*]], label [[DUMMY:%.*]] +; CHECK: A2: +; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[COND1]] +; CHECK-NEXT: br i1 [[COND_FR]], label [[REACHABLE:%.*]], label [[UNREACHABLE:%.*]] +; CHECK: B: +; CHECK-NEXT: br i1 [[DUMMYCOND]], label [[B2:%.*]], label [[DUMMY]] +; CHECK: B2: +; CHECK-NEXT: [[COND_FR2:%.*]] = freeze i1 [[COND1]] +; CHECK-NEXT: br i1 [[COND_FR2]], label [[UNREACHABLE]], label [[REACHABLE]] +; CHECK: REACHABLE: +; CHECK-NEXT: call void @f() +; CHECK-NEXT: ret void +; CHECK: UNREACHABLE: +; CHECK-NEXT: ret void +; CHECK: DUMMY: +; CHECK-NEXT: ret void +; + %cond1 = icmp slt i32 0, %x + %cond = and i1 %cond1, %cond2 + %cond.fr0 = freeze i1 %cond + br i1 %cond.fr0, label %A, label %B +A: + br i1 %dummycond, label %A2, label %DUMMY +A2: + %cond.fr = freeze i1 %cond1 + br i1 %cond.fr, label %REACHABLE, label %UNREACHABLE +B: + br i1 %dummycond, label %B2, label %DUMMY +B2: + %cond.fr2 = freeze i1 %cond1 + br i1 %cond.fr2, label %UNREACHABLE, label %REACHABLE + +REACHABLE: + call void @f() + ret void +UNREACHABLE: + ret void +DUMMY: + ret void +} + +define void @and(i32 %x, i1 %cond2, i1 %dummycond) { +; CHECK-LABEL: @and( +; CHECK-NEXT: [[COND1:%.*]] = icmp slt i32 0, [[X:%.*]] +; CHECK-NEXT: [[COND:%.*]] = and i1 [[COND1]], [[COND2:%.*]] +; CHECK-NEXT: br i1 [[COND]], label [[A:%.*]], label [[B:%.*]] +; CHECK: A: +; CHECK-NEXT: br i1 [[DUMMYCOND:%.*]], label [[REACHABLE:%.*]], label [[DUMMY:%.*]] +; CHECK: B: +; CHECK-NEXT: br i1 [[DUMMYCOND]], label [[B2:%.*]], label [[DUMMY]] +; CHECK: B2: +; CHECK-NEXT: [[COND_FR2:%.*]] = freeze i1 [[COND1]] +; CHECK-NEXT: br i1 [[COND_FR2]], label [[REACHABLE]], label [[REACHABLE2:%.*]] +; CHECK: REACHABLE: +; CHECK-NEXT: call void @f() +; CHECK-NEXT: ret void +; CHECK: REACHABLE2: +; CHECK-NEXT: call void @f() +; CHECK-NEXT: ret void +; CHECK: DUMMY: +; CHECK-NEXT: ret void +; + %cond1 = icmp slt i32 0, %x + %cond = and i1 %cond1, %cond2 + br i1 %cond, label %A, label %B +A: + br i1 %dummycond, label %A2, label %DUMMY +A2: + %cond.fr = freeze i1 %cond1 + br i1 %cond.fr, label %REACHABLE, label %UNREACHABLE +B: + br i1 %dummycond, label %B2, label %DUMMY +B2: + %cond.fr2 = freeze i1 %cond1 + br i1 %cond.fr2, label %REACHABLE, label %REACHABLE2 + +REACHABLE: + call void @f() + ret void +REACHABLE2: + call void @f() + ret void +UNREACHABLE: + ret void +DUMMY: + ret void +} diff --git a/llvm/test/Transforms/JumpThreading/freeze-lvi-edgevaluelocal.ll b/llvm/test/Transforms/JumpThreading/freeze-lvi-edgevaluelocal.ll --- a/llvm/test/Transforms/JumpThreading/freeze-lvi-edgevaluelocal.ll +++ b/llvm/test/Transforms/JumpThreading/freeze-lvi-edgevaluelocal.ll @@ -9,10 +9,7 @@ define i32 @simple(i1 %cond) { ; CHECK-LABEL: @simple( ; CHECK-NEXT: ENTRY: -; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[COND:%.*]] -; CHECK-NEXT: br i1 [[COND]], label [[A:%.*]], label [[EXIT:%.*]] -; CHECK: A: -; CHECK-NEXT: br i1 [[COND_FR]], label [[B:%.*]], label [[EXIT]] +; CHECK-NEXT: br i1 [[COND:%.*]], label [[B:%.*]], label [[EXIT:%.*]] ; CHECK: B: ; CHECK-NEXT: call void @f() ; CHECK-NEXT: ret i32 1