diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -894,7 +894,75 @@ } // Perform PHI construction. - return SSAUpdate.GetValueInMiddleOfBlock(Load->getParent()); + auto *V = SSAUpdate.GetValueInMiddleOfBlock(Load->getParent()); + + auto *NewPN = dyn_cast(V); + if (!NewPN) + return V; + + // If the constructed value is a PHINode and there are equivalent PHIs in its + // parent block, we try to replace the inserted one with an equivalent. + + auto PhisAreEquivalent = [&](const PHINode *LoadPN, + const PHINode *SecondPN) { + const BasicBlock *BB = LoadPN->getParent(); + for (const BasicBlock *Pred : predecessors(BB)) { + auto *V1 = LoadPN->getIncomingValueForBlock(Pred); + auto *V2 = SecondPN->getIncomingValueForBlock(Pred); + + if (V1 == V2) + continue; + + // If both incoming values are binary operators check that their operands + // match. + Value *FirstLHS, *FirstRHS, *SecondLHS, *SecondRHS; + if (match(V1, m_BinOp(m_Value(FirstLHS), m_Value(FirstRHS))) && + match(V2, m_BinOp(m_Value(SecondLHS), m_Value(SecondRHS)))) { + auto *FirstBO = cast(V1); + auto *SecondBO = cast(V2); + + if (FirstBO->getOpcode() != SecondBO->getOpcode()) + return false; + + // Check that overflow flags match. + if (auto *FirstOBO = dyn_cast(FirstBO)) { + auto *SecondOBO = cast(SecondBO); + if (FirstOBO->hasNoSignedWrap() != SecondOBO->hasNoSignedWrap() || + FirstOBO->hasNoUnsignedWrap() != SecondOBO->hasNoUnsignedWrap()) + return false; + } + + // We are asked whether LoadPN is equal to SecondPN. + // We replaced the Load with LoadPN. If the Load is an argument of + // a binary operator which is an incoming value for LoadPN, and + // SecondPN is a corresponding argument of a corresponding binop that is + // incoming for SecondPN, we assume that LoadPN == SecondPN. + // If in addition remaining operands match, we say that current incoming + // values are equal. + if ((FirstLHS == Load && SecondLHS == SecondPN && + FirstRHS == SecondRHS) || + (FirstRHS == Load && SecondRHS == SecondPN && + FirstLHS == SecondLHS)) + continue; + } + + // Neither of the above checks passed, so assume PHIs are not equal. + return false; + } + return true; + }; + + for (PHINode &PN : NewPN->getParent()->phis()) { + if (NewPN == &PN) + continue; + if (PhisAreEquivalent(NewPN, &PN)) { + NewPN->replaceAllUsesWith(&PN); + NewPN->eraseFromParent(); + return &PN; + } + } + + return V; } Value *AvailableValue::MaterializeAdjustedValue(LoadInst *Load, diff --git a/llvm/test/Transforms/GVN/duplicate-phis.ll b/llvm/test/Transforms/GVN/duplicate-phis.ll --- a/llvm/test/Transforms/GVN/duplicate-phis.ll +++ b/llvm/test/Transforms/GVN/duplicate-phis.ll @@ -12,11 +12,9 @@ ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[VAL:%.*]] = phi i32 [ [[VAL_INC:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ] -; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ] ; CHECK-NEXT: [[VAL_INC]] = add i32 [[VAL]], 1 ; CHECK-NEXT: store i32 [[VAL_INC]], i32* [[PTR]], align 4 -; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp eq i32 [[IV]], 1000 +; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp eq i32 [[VAL]], 1000 ; CHECK-NEXT: br i1 [[LOOP_COND]], label [[EXIT:%.*]], label [[LOOP]] ; CHECK: exit: ; CHECK-NEXT: ret void @@ -45,13 +43,11 @@ ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[VAL:%.*]] = phi i32 [ [[VAL_INC:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ] -; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ] ; CHECK-NEXT: [[VAL_INC]] = add i32 [[VAL]], 1 ; CHECK-NEXT: store i32 [[VAL_INC]], i32* [[PTR]], align 4 -; CHECK-NEXT: [[IV_WIDE:%.*]] = zext i32 [[IV]] to i64 +; CHECK-NEXT: [[IV_WIDE:%.*]] = zext i32 [[VAL]] to i64 ; CHECK-NEXT: call void @foo(i64 [[IV_WIDE]]) -; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp eq i32 [[IV]], 1000 +; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp eq i32 [[VAL]], 1000 ; CHECK-NEXT: br i1 [[LOOP_COND]], label [[EXIT:%.*]], label [[LOOP]] ; CHECK: exit: ; CHECK-NEXT: ret void @@ -83,17 +79,13 @@ ; CHECK-NEXT: store i32 0, i32* [[PTR2]], align 4 ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: -; CHECK-NEXT: [[VAL2:%.*]] = phi i32 [ [[VAL2_INC:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ] -; CHECK-NEXT: [[VAL1:%.*]] = phi i32 [ [[VAL1_INC:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ] -; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ] -; CHECK-NEXT: [[VAL1_INC]] = add i32 [[VAL1]], 1 +; CHECK-NEXT: [[VAL2:%.*]] = phi i32 [ [[VAL1_INC:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[VAL1_INC]] = add i32 [[VAL2]], 1 ; CHECK-NEXT: store i32 [[VAL1_INC]], i32* [[PTR1]], align 4 -; CHECK-NEXT: [[VAL2_INC]] = add i32 [[VAL2]], 1 -; CHECK-NEXT: store i32 [[VAL2_INC]], i32* [[PTR2]], align 4 -; CHECK-NEXT: [[IV_WIDE:%.*]] = zext i32 [[IV]] to i64 +; CHECK-NEXT: store i32 [[VAL1_INC]], i32* [[PTR2]], align 4 +; CHECK-NEXT: [[IV_WIDE:%.*]] = zext i32 [[VAL2]] to i64 ; CHECK-NEXT: call void @foo(i64 [[IV_WIDE]]) -; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp eq i32 [[IV]], 1000 +; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp eq i32 [[VAL2]], 1000 ; CHECK-NEXT: br i1 [[LOOP_COND]], label [[EXIT:%.*]], label [[LOOP]] ; CHECK: exit: ; CHECK-NEXT: ret void