[InstCombine] Combine binary operator of two phi node
authorluxufan <luxufan@iscas.ac.cn>
Fri, 3 Mar 2023 08:34:17 +0000 (16:34 +0800)
committerluxufan <luxufan@iscas.ac.cn>
Tue, 21 Mar 2023 07:24:17 +0000 (15:24 +0800)
Combine binary operator of two phi node if there is at least one
specific constant value in phi0 and phi1's incoming values for each
same incoming block and this specific constant value can be used
to do optimization for specific binary operator.
For example:
```
%phi0 = phi i32 [0, %bb0], [%i, %bb1]
%phi1 = phi i32 [%j, %bb0], [0, %bb1]
%add = add i32 %phi0, %phi1
==>
%add = phi i32 [%j, %bb0], [%i, %bb1]
```

Fixes: https://github.com/llvm/llvm-project/issues/61137

Differential Revision: https://reviews.llvm.org/D145223

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
llvm/test/Transforms/InstCombine/phi.ll

index c3ba452..8dd752b 100644 (file)
@@ -1294,7 +1294,7 @@ Instruction *InstCombinerImpl::foldBinopWithPhiOperands(BinaryOperator &BO) {
   auto *Phi0 = dyn_cast<PHINode>(BO.getOperand(0));
   auto *Phi1 = dyn_cast<PHINode>(BO.getOperand(1));
   if (!Phi0 || !Phi1 || !Phi0->hasOneUse() || !Phi1->hasOneUse() ||
-      Phi0->getNumOperands() != 2 || Phi1->getNumOperands() != 2)
+      Phi0->getNumOperands() != Phi1->getNumOperands())
     return nullptr;
 
   // TODO: Remove the restriction for binop being in the same block as the phis.
@@ -1302,6 +1302,51 @@ Instruction *InstCombinerImpl::foldBinopWithPhiOperands(BinaryOperator &BO) {
       BO.getParent() != Phi1->getParent())
     return nullptr;
 
+  // Fold if there is at least one specific constant value in phi0 or phi1's
+  // incoming values that comes from the same block and this specific constant
+  // value can be used to do optimization for specific binary operator.
+  // For example:
+  // %phi0 = phi i32 [0, %bb0], [%i, %bb1]
+  // %phi1 = phi i32 [%j, %bb0], [0, %bb1]
+  // %add = add i32 %phi0, %phi1
+  // ==>
+  // %add = phi i32 [%j, %bb0], [%i, %bb1]
+  Constant *C = ConstantExpr::getBinOpIdentity(BO.getOpcode(), BO.getType(),
+                                               /*AllowRHSConstant*/ false);
+  if (C) {
+    SmallVector<Value *, 4> NewIncomingValues;
+    auto CanFoldIncomingValuePair = [&](std::tuple<Use &, Use &> T) {
+      auto &Phi0Use = std::get<0>(T);
+      auto &Phi1Use = std::get<1>(T);
+      if (Phi0->getIncomingBlock(Phi0Use) != Phi1->getIncomingBlock(Phi1Use))
+        return false;
+      Value *Phi0UseV = Phi0Use.get();
+      Value *Phi1UseV = Phi1Use.get();
+      if (Phi0UseV == C)
+        NewIncomingValues.push_back(Phi1UseV);
+      else if (Phi1UseV == C)
+        NewIncomingValues.push_back(Phi0UseV);
+      else
+        return false;
+      return true;
+    };
+
+    if (all_of(zip(Phi0->operands(), Phi1->operands()),
+               CanFoldIncomingValuePair)) {
+      PHINode *NewPhi =
+          PHINode::Create(Phi0->getType(), Phi0->getNumOperands());
+      assert(NewIncomingValues.size() == Phi0->getNumOperands() &&
+             "The number of collected incoming values should equal the number "
+             "of the original PHINode operands!");
+      for (unsigned I = 0; I < Phi0->getNumOperands(); I++)
+        NewPhi->addIncoming(NewIncomingValues[I], Phi0->getIncomingBlock(I));
+      return NewPhi;
+    }
+  }
+
+  if (Phi0->getNumOperands() != 2 || Phi1->getNumOperands() != 2)
+    return nullptr;
+
   // Match a pair of incoming constants for one of the predecessor blocks.
   BasicBlock *ConstBB, *OtherBB;
   Constant *C0, *C1;
index 75eece9..52a5c0b 100644 (file)
@@ -1508,9 +1508,7 @@ define i32 @add_two_phi_node_can_fold(i1 %c, i32 %i, i32 %j)  {
 ; CHECK:       if.then:
 ; CHECK-NEXT:    br label [[IF_END]]
 ; CHECK:       if.end:
-; CHECK-NEXT:    [[X:%.*]] = phi i32 [ 0, [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
-; CHECK-NEXT:    [[Y:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ 0, [[ENTRY]] ]
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[ADD:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
 ; CHECK-NEXT:    ret i32 [[ADD]]
 ;
 entry:
@@ -1558,9 +1556,7 @@ define i32 @or_two_phi_node_can_fold(i1 %c, i32 %i, i32 %j)  {
 ; CHECK:       if.then:
 ; CHECK-NEXT:    br label [[IF_END]]
 ; CHECK:       if.end:
-; CHECK-NEXT:    [[X:%.*]] = phi i32 [ 0, [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
-; CHECK-NEXT:    [[Y:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ 0, [[ENTRY]] ]
-; CHECK-NEXT:    [[ADD:%.*]] = or i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[ADD:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
 ; CHECK-NEXT:    ret i32 [[ADD]]
 ;
 entry:
@@ -1583,9 +1579,7 @@ define i32 @and_two_phi_node_can_fold(i1 %c, i32 %i, i32 %j)  {
 ; CHECK:       if.then:
 ; CHECK-NEXT:    br label [[IF_END]]
 ; CHECK:       if.end:
-; CHECK-NEXT:    [[X:%.*]] = phi i32 [ -1, [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
-; CHECK-NEXT:    [[Y:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ -1, [[ENTRY]] ]
-; CHECK-NEXT:    [[ADD:%.*]] = and i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[ADD:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
 ; CHECK-NEXT:    ret i32 [[ADD]]
 ;
 entry:
@@ -1608,9 +1602,7 @@ define i32 @mul_two_phi_node_can_fold(i1 %c, i32 %i, i32 %j)  {
 ; CHECK:       if.then:
 ; CHECK-NEXT:    br label [[IF_END]]
 ; CHECK:       if.end:
-; CHECK-NEXT:    [[X:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
-; CHECK-NEXT:    [[Y:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ 1, [[ENTRY]] ]
-; CHECK-NEXT:    [[ADD:%.*]] = mul i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[ADD:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
 ; CHECK-NEXT:    ret i32 [[ADD]]
 ;
 entry:
@@ -1633,9 +1625,32 @@ define i32 @xor_two_phi_node_can_fold(i1 %c, i32 %i, i32 %j)  {
 ; CHECK:       if.then:
 ; CHECK-NEXT:    br label [[IF_END]]
 ; CHECK:       if.end:
+; CHECK-NEXT:    [[ADD:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+entry:
+  br i1 %c, label %if.then, label %if.end
+
+if.then:
+  br label %if.end
+
+if.end:
+  %x = phi i32 [ 0, %if.then ], [ %j, %entry ]
+  %y = phi i32 [ %i, %if.then ], [ 0, %entry ]
+  %add = xor i32 %y, %x
+  ret i32 %add
+}
+
+define i32 @sub_two_phi_node_cant_fold(i1 %c, i32 %i, i32 %j)  {
+; CHECK-LABEL: @sub_two_phi_node_cant_fold(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    br label [[IF_END]]
+; CHECK:       if.end:
 ; CHECK-NEXT:    [[X:%.*]] = phi i32 [ 0, [[IF_THEN]] ], [ [[J:%.*]], [[ENTRY:%.*]] ]
 ; CHECK-NEXT:    [[Y:%.*]] = phi i32 [ [[I:%.*]], [[IF_THEN]] ], [ 0, [[ENTRY]] ]
-; CHECK-NEXT:    [[ADD:%.*]] = xor i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[Y]], [[X]]
 ; CHECK-NEXT:    ret i32 [[ADD]]
 ;
 entry:
@@ -1647,6 +1662,6 @@ if.then:
 if.end:
   %x = phi i32 [ 0, %if.then ], [ %j, %entry ]
   %y = phi i32 [ %i, %if.then ], [ 0, %entry ]
-  %add = xor i32 %y, %x
+  %add = sub i32 %y, %x
   ret i32 %add
 }