[llvm] [VectorCombine] Fold Deinterleave/Interleave Pairs (PR #211022)

Graham Hunter via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 7 09:50:54 PDT 2026


================
@@ -5882,6 +5883,265 @@ bool VectorCombine::foldInsExtVectorToShuffle(Instruction &I) {
   return true;
 }
 
+namespace {
+
+class InterleavedElementwiseChain {
+  struct ElementwiseStep {
+    SmallVector<Instruction *, 8> Insts;
+    unsigned ChainOperand;
+
+    ElementwiseStep(ArrayRef<Instruction *> Insts, unsigned ChainOperand)
+        : Insts(Insts.begin(), Insts.end()), ChainOperand(ChainOperand) {}
+  };
+
+  IRBuilderBase &Builder;
+  Value *Root;
+  SmallVector<ElementwiseStep, 4> Steps;
+
+  static unsigned getNumDataOperands(Instruction *Inst) {
+    if (auto *II = dyn_cast<IntrinsicInst>(Inst))
+      return II->arg_size();
+    return Inst->getNumOperands();
+  }
+
+  static Value *getSplatOrScalar(Value *V) {
+    return isa<VectorType>(V->getType()) ? getSplatValue(V) : V;
+  }
+
+  static bool isSupportedElementwise(Instruction *Inst) {
+    auto *ResultTy = dyn_cast<VectorType>(Inst->getType());
+    if (!ResultTy)
+      return false;
+
+    if (auto *II = dyn_cast<IntrinsicInst>(Inst)) {
+      if (II->hasOperandBundles() ||
+          !isTriviallyVectorizable(II->getIntrinsicID()))
+        return false;
+    } else if (!isa<BinaryOperator, UnaryOperator, CastInst, CmpInst,
+                    SelectInst, FreezeInst>(Inst)) {
+      return false;
+    }
+
+    // Reject operations that change the element-count.
+    for (unsigned Op = 0, E = getNumDataOperands(Inst); Op != E; ++Op) {
+      auto *OperandTy = dyn_cast<VectorType>(Inst->getOperand(Op)->getType());
+      if (OperandTy &&
+          OperandTy->getElementCount() != ResultTy->getElementCount())
+        return false;
+    }
+
+    return true;
+  }
+
+  Value *createWideInstruction(Instruction *NarrowInst,
+                               ArrayRef<Value *> NewOperands,
+                               VectorType *WideResultTy) {
+    if (isa<BinaryOperator, UnaryOperator>(NarrowInst))
+      return Builder.CreateNAryOp(NarrowInst->getOpcode(), NewOperands);
+    if (auto *Cast = dyn_cast<CastInst>(NarrowInst))
+      return Builder.CreateCast(Cast->getOpcode(), NewOperands[0],
+                                WideResultTy);
+    if (auto *Cmp = dyn_cast<CmpInst>(NarrowInst))
+      return Builder.CreateCmp(Cmp->getPredicate(), NewOperands[0],
+                               NewOperands[1]);
+    if (isa<SelectInst>(NarrowInst))
+      return Builder.CreateSelect(NewOperands[0], NewOperands[1],
+                                  NewOperands[2]);
+    if (isa<FreezeInst>(NarrowInst))
+      return Builder.CreateFreeze(NewOperands[0]);
+    if (auto *II = dyn_cast<IntrinsicInst>(NarrowInst))
+      return Builder.CreateIntrinsic(WideResultTy, II->getIntrinsicID(),
+                                     NewOperands);
+    llvm_unreachable("Unsupported instruction");
+  }
+
+public:
+  InterleavedElementwiseChain(IRBuilderBase &Builder, Value *Root)
+      : Builder(Builder), Root(Root) {}
+
+  /// Visit a list  of instructions and check if they can be rewritten as a
+  /// single wider instruction. If so, the list is appened to \p Steps.
+  bool visitInstLevel(ArrayRef<Instruction *> Insts,
+                      ArrayRef<unsigned> OperandNumbers) {
+    if (Insts.empty() || Insts.size() != OperandNumbers.size())
+      return false;
+
+    Instruction *FirstInst = Insts.front();
+    unsigned ChainOperand = OperandNumbers.front();
+
+    if (!isSupportedElementwise(FirstInst))
+      return false;
+
+    for (unsigned Index = 1; Index != Insts.size(); ++Index)
+      if (OperandNumbers[Index] != ChainOperand ||
+          !FirstInst->isSameOperationAs(Insts[Index]))
+        return false;
----------------
huntergr-arm wrote:

With `Use`, this could just become a `any_of` operation over the arrayref (with a drop_front)

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


More information about the llvm-commits mailing list