[llvm] [SLP] Merge redundant input operands of binary-operator nodes (PR #208670)

Balakrishna Bandlapalli via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 14 07:48:37 PDT 2026


================
@@ -13145,9 +13256,163 @@ BoUpSLP::getScalarsVectorizationLegality(ArrayRef<Value *> VL, unsigned Depth,
   return ScalarsVectorizationLegality(S, /*IsLegal=*/true);
 }
 
+void BoUpSLP::updateReuseShuffleForMergedOps(
+    const EdgeInfo &UserTreeIdx, SmallVectorImpl<int> &ReuseShuffleIndices,
+    SmallDenseMap<Value *, unsigned, 16> &UniquePositions,
+    ArrayRef<Value *> UniqueValues, bool MergedOp) const {
+  if (!MergedOp)
+    return;
+  // The operand node is built from the merged scalar list, but the reuse
+  // shuffle mask must map the user node's operand lanes onto the (unique)
+  // merged scalars so that codegen reads the correct lane.
+  ArrayRef<Value *> InVL = UserTreeIdx.UserTE->getOperand(UserTreeIdx.EdgeIdx);
+  ReuseShuffleIndices.clear();
+  for (Value *V : InVL) {
+    auto Res = UniquePositions.try_emplace(V, UniqueValues.size());
+    ReuseShuffleIndices.emplace_back(Res.first->second);
+    assert(!Res.second && "Merged operand value not found in unique values.");
+  }
+}
+
+bool BoUpSLP::isInputVectorizable(ArrayRef<Value *> ParentVL,
+                                  ArrayRef<Value *> InVL,
+                                  SmallVectorImpl<Value *> &MergedVL) {
+  SmallVector<Value *> VL(InVL.begin(), InVL.end());
+  assert((allConstant(VL) || allSameType(VL)) && "Invalid types!");
+
+  SmallVector<int> ReuseShuffleIndices;
+  SmallVector<Value *> UniqueValues;
+  auto TryToFindDuplicates = [&]() {
+    SmallDenseMap<Value *, unsigned, 16> UniquePositions(VL.size());
+    for (Value *V : VL) {
+      if (isConstant(V)) {
+        ReuseShuffleIndices.emplace_back(
+            isa<UndefValue>(V) ? PoisonMaskElem : UniqueValues.size());
+        UniqueValues.emplace_back(V);
+        continue;
+      }
+      auto Res = UniquePositions.try_emplace(V, UniqueValues.size());
+      ReuseShuffleIndices.emplace_back(Res.first->second);
+      if (Res.second)
+        UniqueValues.emplace_back(V);
+    }
+    size_t NumUniqueScalarValues = UniqueValues.size();
+    if (NumUniqueScalarValues == VL.size()) {
+      ReuseShuffleIndices.clear();
+    } else {
+      if (NumUniqueScalarValues <= 1 ||
+          (UniquePositions.size() == 1 &&
+           all_of(UniqueValues,
+                  [](Value *V) {
+                    return isa<UndefValue>(V) || !isConstant(V);
+                  })) ||
+          !has_single_bit(NumUniqueScalarValues))
+        return false;
+      VL = UniqueValues;
+    }
+    return true;
+  };
+  InstructionsState S = getSameOpcode(VL, *TLI);
+  if (!S)
+    return false;
+  if (!isa<BinaryOperator>(S.getMainOp()))
+    return false;
+  // Check that every instruction appears once in this bundle.
+  if (!TryToFindDuplicates())
+    return false;
+
+  // Bail out if the unique operations in the input operand vector are not
+  // (at least) half of the parent VL - otherwise merging is not profitable.
+  if (PowerOf2Ceil(ParentVL.size()) / 2 < PowerOf2Ceil(VL.size()))
+    return false;
+  // The merged operands must be vectorizable on their own.
+  OrdersType CurrentOrder;
+  SmallVector<Value *> PointerOps;
+  StridedPtrInfo SPtrInfo;
+  SmallVector<int> ExpandShuffleMask;
+  TreeEntry::EntryState State = getScalarsVectorizationState(
+      S, VL, /*IsScatterVectorizeUserTE=*/false, CurrentOrder, PointerOps,
+      SPtrInfo, ExpandShuffleMask);
+  if (State != TreeEntry::Vectorize)
+    return false;
+
+  MergedVL.append(VL.begin(), VL.end());
+  return true;
+}
+
+bool BoUpSLP::canMergeInputOperands(ArrayRef<Value *> InVL,
+                                    ArrayRef<Value *> InLeft,
+                                    ArrayRef<Value *> InRight,
+                                    SmallVectorImpl<Value *> &MergedVL) {
+  // We expect the minimum size of the merged VL to be at least 4; smaller
+  // sizes are better vectorized with splats if not merged.
+  if (PowerOf2Ceil(InVL.size()) < 4)
+    return false;
+  if (!isInputVectorizable(InVL, InLeft, MergedVL) ||
+      !isInputVectorizable(InVL, InRight, MergedVL))
+    return false;
+  if (MergedVL.size() != InLeft.size() || MergedVL.size() != InRight.size())
+    return false;
+  // Both operand nodes are built from the single MergedVL bundle and are
+  // distinguished only by their reuse-shuffle masks selecting lanes out of one
+  // merged vector. For codegen to reconstruct each operand correctly:
+  //  - every merged scalar must occupy a distinct lane (otherwise the two
+  //    operand nodes can no longer be told apart by their reuse masks and they
+  //    collapse to the same vector), and
+  //  - the merged bundle must vectorize as a single uniform node. A
+  //    mixed-opcode bundle would become an alt-shuffle node whose vectorized
+  //    value is a shuffle of two different source vectors; the merged-operand
+  //    codegen only unwraps a single source operand (getShuffleInput) and
+  //    would silently drop the other half.
+  SmallPtrSet<Value *, 8> SeenVals;
+  for (Value *V : MergedVL)
+    if (!SeenVals.insert(V).second)
+      return false;
+  InstructionsState MergedS = getSameOpcode(MergedVL, *TLI);
+  if (!MergedS || MergedS.isAltShuffle())
+    return false;
+  OrdersType MergedOrder;
+  SmallVector<Value *> MergedPointerOps;
+  StridedPtrInfo MergedSPtrInfo;
+  SmallVector<int> MergedExpandShuffleMask;
+  if (getScalarsVectorizationState(MergedS, MergedVL,
+                                   /*IsScatterVectorizeUserTE=*/false,
+                                   MergedOrder, MergedPointerOps,
+                                   MergedSPtrInfo,
+                                   MergedExpandShuffleMask) != TreeEntry::Vectorize)
+    return false;
+  return true;
+}
+
+bool BoUpSLP::handleMergedOperands(TreeEntry *TE, ArrayRef<Value *> VL,
+                                   ArrayRef<Value *> Left,
+                                   ArrayRef<Value *> Right, unsigned Depth) {
+  if (!SLPMergeInputOps)
+    return false;
+  SmallVector<Value *> MergedVL;
+  if (!canMergeInputOperands(VL, Left, Right, MergedVL))
+    return false;
+  LLVM_DEBUG(dbgs() << "SLP: Merging input operands in " << F->getName()
+                    << "\n");
+
+  TE->setOperand(0, Left);
+  TE->setOperand(1, Right);
----------------
amd-bbandlap wrote:

Left and Right are preserved so that ReuseShuffleIndicies of the nodes are updated correctly as MergedVL is passed as main VL to buildTreeRec  

https://github.com/llvm/llvm-project/pull/208670


More information about the llvm-commits mailing list