Skip to content

[SLP][NFC] Avoid the getRdxOperand hack #148672

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Conversation

pfusik
Copy link
Contributor

@pfusik pfusik commented Jul 14, 2025

We visit two operands of TreeN:

  • 1, 2 if isCmpSelMinMax(TreeN)
  • 0, 2 if TreeN is (select X, true, Y)
  • 0, 1 otherwise

in the reverse order.

Replace the loop in CheckOperands and a substitution of the second index
in getRdxOperand with simply calling the code for the two determined indexes.

pfusik added 2 commits July 14, 2025 18:44
We visit two operands of TreeN:
- 1, 2 if isCmpSelMinMax(TreeN)
- 0, 2 if TreeN is (select X, true, Y)
- 0, 1 otherwise
in the reverse order.

Replace the loop in CheckOperands and a substitution of the second index
in getRdxOperand with simply calling the code for the two determined indexes.
@llvmbot
Copy link
Member

llvmbot commented Jul 14, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-vectorizers

Author: Piotr Fusik (pfusik)

Changes

We visit two operands of TreeN:

  • 1, 2 if isCmpSelMinMax(TreeN)
  • 0, 2 if TreeN is (select X, true, Y)
  • 0, 1 otherwise

in the reverse order.

Replace the loop in CheckOperands and a substitution of the second index
in getRdxOperand with simply calling the code for the two determined indexes.


Full diff: https://github.com/llvm/llvm-project/pull/148672.diff

1 Files Affected:

  • (modified) llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp (+32-47)
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index c61e1135524b6..1b78e79b6b419 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -21791,16 +21791,6 @@ class HorizontalReduction {
     return I->isAssociative();
   }
 
-  static Value *getRdxOperand(Instruction *I, unsigned Index) {
-    // Poison-safe 'or' takes the form: select X, true, Y
-    // To make that work with the normal operand processing, we skip the
-    // true value operand.
-    // TODO: Change the code and data structures to handle this without a hack.
-    if (getRdxKind(I) == RecurKind::Or && isa<SelectInst>(I) && Index == 1)
-      return I->getOperand(2);
-    return I->getOperand(Index);
-  }
-
   /// Creates reduction operation with the current opcode.
   static Value *createOp(IRBuilderBase &Builder, RecurKind Kind, Value *LHS,
                          Value *RHS, const Twine &Name, bool UseSelect) {
@@ -21992,11 +21982,6 @@ class HorizontalReduction {
   }
 
 private:
-  /// Total number of operands in the reduction operation.
-  static unsigned getNumberOfOperands(Instruction *I) {
-    return isCmpSelMinMax(I) ? 3 : 2;
-  }
-
   /// Checks if the instruction is in basic block \p BB.
   /// For a cmp+sel min/max reduction check that both ops are in \p BB.
   static bool hasSameParent(Instruction *I, BasicBlock *BB) {
@@ -22102,31 +22087,27 @@ class HorizontalReduction {
     // Checks if the operands of the \p TreeN instruction are also reduction
     // operations or should be treated as reduced values or an extra argument,
     // which is not part of the reduction.
-    auto CheckOperands = [&](Instruction *TreeN,
-                             SmallVectorImpl<Value *> &PossibleReducedVals,
-                             SmallVectorImpl<Instruction *> &ReductionOps,
-                             unsigned Level) {
-      for (int I : reverse(seq<int>(getFirstOperandIndex(TreeN),
-                                    getNumberOfOperands(TreeN)))) {
-        Value *EdgeVal = getRdxOperand(TreeN, I);
-        ReducedValsToOps[EdgeVal].push_back(TreeN);
-        auto *EdgeInst = dyn_cast<Instruction>(EdgeVal);
-        // If the edge is not an instruction, or it is different from the main
-        // reduction opcode or has too many uses - possible reduced value.
-        // Also, do not try to reduce const values, if the operation is not
-        // foldable.
-        if (!EdgeInst || Level > RecursionMaxDepth ||
-            getRdxKind(EdgeInst) != RdxKind ||
-            IsCmpSelMinMax != isCmpSelMinMax(EdgeInst) ||
-            !hasRequiredNumberOfUses(IsCmpSelMinMax, EdgeInst) ||
-            !isVectorizable(RdxKind, EdgeInst) ||
-            (R.isAnalyzedReductionRoot(EdgeInst) &&
-             all_of(EdgeInst->operands(), IsaPred<Constant>))) {
-          PossibleReducedVals.push_back(EdgeVal);
-          continue;
-        }
+    auto CheckOperand = [&](Instruction *TreeN, int OperandIndex,
+                            SmallVectorImpl<Value *> &PossibleReducedVals,
+                            SmallVectorImpl<Instruction *> &ReductionOps,
+                            unsigned Level) {
+      Value *EdgeVal = TreeN->getOperand(OperandIndex);
+      ReducedValsToOps[EdgeVal].push_back(TreeN);
+      auto *EdgeInst = dyn_cast<Instruction>(EdgeVal);
+      // If the edge is not an instruction, or it is different from the main
+      // reduction opcode or has too many uses - possible reduced value.
+      // Also, do not try to reduce const values, if the operation is not
+      // foldable.
+      if (!EdgeInst || Level > RecursionMaxDepth ||
+          getRdxKind(EdgeInst) != RdxKind ||
+          IsCmpSelMinMax != isCmpSelMinMax(EdgeInst) ||
+          !hasRequiredNumberOfUses(IsCmpSelMinMax, EdgeInst) ||
+          !isVectorizable(RdxKind, EdgeInst) ||
+          (R.isAnalyzedReductionRoot(EdgeInst) &&
+           all_of(EdgeInst->operands(), IsaPred<Constant>)))
+        PossibleReducedVals.push_back(EdgeVal);
+      else
         ReductionOps.push_back(EdgeInst);
-      }
     };
     // Try to regroup reduced values so that it gets more profitable to try to
     // reduce them. Values are grouped by their value ids, instructions - by
@@ -22177,7 +22158,12 @@ class HorizontalReduction {
       auto [TreeN, Level] = Worklist.pop_back_val();
       SmallVector<Value *> PossibleRedVals;
       SmallVector<Instruction *> PossibleReductionOps;
-      CheckOperands(TreeN, PossibleRedVals, PossibleReductionOps, Level);
+      int I1 = getFirstOperandIndex(TreeN);
+      int I2 = isa<SelectInst>(TreeN) && match(TreeN, m_LogicalOr())
+                   ? 2 // Skip "true" in "select X, true, Y"
+                   : I1 + 1;
+      CheckOperand(TreeN, I2, PossibleRedVals, PossibleReductionOps, Level);
+      CheckOperand(TreeN, I1, PossibleRedVals, PossibleReductionOps, Level);
       addReductionOps(TreeN);
       // Add reduction values. The values are sorted for better vectorization
       // results.
@@ -22295,15 +22281,14 @@ class HorizontalReduction {
               isGuaranteedNotToBePoison(VectorizedTree, AC) ||
               (It != ReducedValsToOps.end() &&
                any_of(It->getSecond(), [&](Instruction *I) {
-                 return isBoolLogicOp(I) &&
-                        getRdxOperand(I, 0) == VectorizedTree;
+                 return isBoolLogicOp(I) && I->getOperand(0) == VectorizedTree;
                }))) {
             ;
           } else if (isGuaranteedNotToBePoison(Res, AC) ||
                      (It1 != ReducedValsToOps.end() &&
-                     any_of(It1->getSecond(), [&](Instruction *I) {
-                       return isBoolLogicOp(I) && getRdxOperand(I, 0) == Res;
-                     }))) {
+                      any_of(It1->getSecond(), [&](Instruction *I) {
+                        return isBoolLogicOp(I) && I->getOperand(0) == Res;
+                      }))) {
             std::swap(VectorizedTree, Res);
           } else {
             VectorizedTree = Builder.CreateFreeze(VectorizedTree);
@@ -22786,11 +22771,11 @@ class HorizontalReduction {
         if (!AnyBoolLogicOp)
           return;
         if (isBoolLogicOp(RedOp1) && ((!InitStep && LHS == VectorizedTree) ||
-                                      getRdxOperand(RedOp1, 0) == LHS ||
+                                      RedOp1->getOperand(0) == LHS ||
                                       isGuaranteedNotToBePoison(LHS, AC)))
           return;
         if (isBoolLogicOp(RedOp2) && ((!InitStep && RHS == VectorizedTree) ||
-                                      getRdxOperand(RedOp2, 0) == RHS ||
+                                      RedOp2->getOperand(0) == RHS ||
                                       isGuaranteedNotToBePoison(RHS, AC))) {
           std::swap(LHS, RHS);
           return;

Copy link

github-actions bot commented Jul 14, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@alexey-bataev
Copy link
Member

I would keep the original functions. Maybe, do some early checks for index 0 and later checks for index 1, but still keep the existing abstractions.

@pfusik
Copy link
Contributor Author

pfusik commented Jul 14, 2025

I would keep the original functions. Maybe, do some early checks for index 0 and later checks for index 1, but still keep the existing abstractions.

The comment says "TODO: Change the code and data structures to handle this without a hack."
I'm doing exactly this. How to keep these functions in place and why?

@alexey-bataev
Copy link
Member

I would keep the original functions. Maybe, do some early checks for index 0 and later checks for index 1, but still keep the existing abstractions.

The comment says "TODO: Change the code and data structures to handle this without a hack." I'm doing exactly this. How to keep these functions in place and why?

Yes, but you did not fix it, just moved to another place. Instead, would be good just to build the list of operands (actual operands, excluding true value) and just return the corresponding operand without adjusting the index

@pfusik
Copy link
Contributor Author

pfusik commented Jul 16, 2025

Yes, but you did not fix it, just moved to another place. Instead, would be good just to build the list of operands (actual operands, excluding true value) and just return the corresponding operand without adjusting the index

The list of operands is now I1 and I2. I find it much cleaner than a reversed sequence and substituting an index.
I don't know how to improve it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants