[llvm] [VectorCombine] Check call targets when folding deinterleave/interleave pairs (PR #219116)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 26 22:33:05 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Oscar Priego (Opriego)

<details>
<summary>Changes</summary>

`foldDeinterleaveInterleavePair` uses `Instruction::isSameOperationAs` to
determine whether operations on deinterleaved chains can be widened into a
single operation.

For call instructions, `isSameOperationAs` compares the instruction opcode,
types, operand counts/types, and special state, but does not compare the call
target. As a result, same-signature intrinsics such as `llvm.smax` and
`llvm.smin` could be considered equivalent and incorrectly folded into a
single wide intrinsic.

Require call instructions participating in this fold to have the same called
operand.

This preserves the existing behavior for non-call instructions and identical
intrinsics while preventing folds across different called operations.

Regression coverage includes:
- `smax` + `smin`, which must not fold
- `smax` + `smax`, which must continue folding

Fixes #<!-- -->218162.

Tests:
- `ninja -C ~/LinuxDev/upstream/llvm-build opt`
- `llvm-lit -v llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll`
- `git diff --check`

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


2 Files Affected:

- (modified) llvm/lib/Transforms/Vectorize/VectorCombine.cpp (+9-2) 
- (modified) llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll (+35) 


``````````diff
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index d19894d57c058..7da3121a35998 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -6153,8 +6153,15 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
     unsigned ChainOperand = CurrentUses.front()->getOperandNo();
     if (any_of(CurrentUses, [&](Use *U) {
           auto *Inst = cast<Instruction>(U->getUser());
-          return Inst != FirstInst && (U->getOperandNo() != ChainOperand ||
-                                       !FirstInst->isSameOperationAs(Inst));
+          if (Inst == FirstInst)
+            return false;
+          if (U->getOperandNo() != ChainOperand ||
+              !FirstInst->isSameOperationAs(Inst))
+            return true;
+          // isSameOperationAs does not compare call targets.
+          auto *FirstCB = dyn_cast<CallBase>(FirstInst);
+          return FirstCB && FirstCB->getCalledOperand() !=
+                                cast<CallBase>(Inst)->getCalledOperand();
         }))
       return false;
 
diff --git a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
index e3c542e900fe0..75388509185f6 100644
--- a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
+++ b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
@@ -712,6 +712,41 @@ else:
   ret <vscale x 16 x i16> zeroinitializer
 }
 
+define <4 x i16> @deinterleave2_same_intrinsic_interleave2(<4 x i16> %v) {
+; CHECK-LABEL: define <4 x i16> @deinterleave2_same_intrinsic_interleave2(
+; CHECK-SAME: <4 x i16> [[V:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = call <4 x i16> @llvm.smax.v4i16(<4 x i16> [[V]], <4 x i16> splat (i16 3))
+; CHECK-NEXT:    ret <4 x i16> [[R]]
+;
+  %d = call { <2 x i16>, <2 x i16> } @llvm.vector.deinterleave2.v4i16(<4 x i16> %v)
+  %f0 = extractvalue { <2 x i16>, <2 x i16> } %d, 0
+  %f1 = extractvalue { <2 x i16>, <2 x i16> } %d, 1
+  %u0 = call <2 x i16> @llvm.smax.v2i16(<2 x i16> %f0, <2 x i16> splat (i16 3))
+  %u1 = call <2 x i16> @llvm.smax.v2i16(<2 x i16> %f1, <2 x i16> splat (i16 3))
+  %r = call <4 x i16> @llvm.vector.interleave2.v4i16(<2 x i16> %u0, <2 x i16> %u1)
+  ret <4 x i16> %r
+}
+
+define <4 x i16> @negative_deinterleave2_different_intrinsics_interleave2(<4 x i16> %v) {
+; CHECK-LABEL: define <4 x i16> @negative_deinterleave2_different_intrinsics_interleave2(
+; CHECK-SAME: <4 x i16> [[V:%.*]]) {
+; CHECK-NEXT:    [[D:%.*]] = call { <2 x i16>, <2 x i16> } @llvm.vector.deinterleave2.v4i16(<4 x i16> [[V]])
+; CHECK-NEXT:    [[F0:%.*]] = extractvalue { <2 x i16>, <2 x i16> } [[D]], 0
+; CHECK-NEXT:    [[F1:%.*]] = extractvalue { <2 x i16>, <2 x i16> } [[D]], 1
+; CHECK-NEXT:    [[U0:%.*]] = call <2 x i16> @llvm.smax.v2i16(<2 x i16> [[F0]], <2 x i16> splat (i16 3))
+; CHECK-NEXT:    [[U1:%.*]] = call <2 x i16> @llvm.smin.v2i16(<2 x i16> [[F1]], <2 x i16> splat (i16 3))
+; CHECK-NEXT:    [[R:%.*]] = call <4 x i16> @llvm.vector.interleave2.v4i16(<2 x i16> [[U0]], <2 x i16> [[U1]])
+; CHECK-NEXT:    ret <4 x i16> [[R]]
+;
+  %d = call { <2 x i16>, <2 x i16> } @llvm.vector.deinterleave2.v4i16(<4 x i16> %v)
+  %f0 = extractvalue { <2 x i16>, <2 x i16> } %d, 0
+  %f1 = extractvalue { <2 x i16>, <2 x i16> } %d, 1
+  %u0 = call <2 x i16> @llvm.smax.v2i16(<2 x i16> %f0, <2 x i16> splat (i16 3))
+  %u1 = call <2 x i16> @llvm.smin.v2i16(<2 x i16> %f1, <2 x i16> splat (i16 3))
+  %r = call <4 x i16> @llvm.vector.interleave2.v4i16(<2 x i16> %u0, <2 x i16> %u1)
+  ret <4 x i16> %r
+}
+
 !0 = !{!"function_entry_count", i64 1000}
 !1 = !{!"branch_weights", i32 2, i32 3}
 

``````````

</details>


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


More information about the llvm-commits mailing list