[llvm] [AMDGPU][X86][DAG] Avoid duplicate BinOp result from narrowing insert-extract sub-vector (PR #201056)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 30 01:01:37 PDT 2026
================
@@ -27269,36 +27301,113 @@ static SDValue narrowInsertExtractVectorBinOp(EVT SubVT, SDValue BinOp,
EVT VecVT = BinOp.getValueType();
SDValue Bop0 = BinOp.getOperand(0), Bop1 = BinOp.getOperand(1);
- if (VecVT != Bop0.getValueType() || VecVT != Bop1.getValueType())
+ if (VecVT != Bop0.getValueType() || VecVT != Bop1.getValueType() ||
+ VecVT.isScalableVT())
return SDValue();
+
+ // This fold only pays off when the wide binop disappears completely, so every
+ // user must be an extract_subvector. Require them all to extract N's type so
+ // a single chain scan serves every extract.
+ EVT SubVT = N->getValueType(0);
+ if (VecVT.getSizeInBits() <= SubVT.getSizeInBits())
+ return SDValue();
+
if (!TLI.isOperationLegalOrCustom(BinOpcode, SubVT, LegalOperations))
return SDValue();
- SDValue Sub0 = getSubVectorSrc(Bop0, Index, SubVT);
- SDValue Sub1 = getSubVectorSrc(Bop1, Index, SubVT);
+ // The wide binop splits into a small, dense set of SubVT-sized slots, so a
+ // flat table indexed by subvector position is all we need.
+ unsigned NumSubElts = SubVT.getVectorMinNumElements();
+ unsigned NumParts = VecVT.getVectorMinNumElements() / NumSubElts;
+ SmallVector<std::tuple<SDNode *, SDValue, SDValue>, 4> Slots(NumParts);
+ // Scan each wide operand's chain once, filling each slot's source.
+ collectSubVectorSrcs(Bop0, SubVT, /*OpNo=*/0, Slots);
+ collectSubVectorSrcs(Bop1, SubVT, /*OpNo=*/1, Slots);
+
+ bool HasNonZeroExt = false;
+ bool HasNonExtUser = false;
+ bool AllExtractsCheap = true;
+ for (SDNode *User : BinOp->users()) {
+ if (User->getOpcode() != ISD::EXTRACT_SUBVECTOR) {
+ HasNonExtUser = true;
+ continue;
+ }
+ if (User->getValueType(0) != SubVT ||
+ (User->getCombinerWorklistIndex() < 0 && User != N))
+ return SDValue();
+ unsigned Idx = User->getConstantOperandVal(1);
+ if (Idx % NumSubElts != 0 || Idx / NumSubElts >= NumParts)
+ return SDValue();
+ auto &Slot = Slots[Idx / NumSubElts];
+ if (!std::get<1>(Slot) || !std::get<2>(Slot))
+ return SDValue();
+ SDNode *&ExtSubVec = std::get<0>(Slot);
+ if (!ExtSubVec) {
+ ExtSubVec = User;
+ AllExtractsCheap &= TLI.isExtractSubvectorCheap(SubVT, VecVT, Idx);
+ if (Idx != 0)
+ HasNonZeroExt = true;
+ } else {
+ llvm_unreachable("Duplicate extract subvector");
+ }
+ }
- // TODO: We could handle the case where only 1 operand is being inserted by
- // creating an extract of the other operand, but that requires checking
- // number of uses and/or costs.
- if (!Sub0 || !Sub1)
+ if (TLI.isTypeLegal(VecVT) && AllExtractsCheap &&
+ !TLI.isNarrowingProfitable(BinOp.getNode(), VecVT, SubVT))
return SDValue();
- // We are inserting both operands of the wide binop only to extract back
- // to the narrow vector size. Eliminate all of the insert/extract:
- // ext (binop (ins ?, X, Index), (ins ?, Y, Index)), Index --> binop X, Y
- return DAG.getNode(BinOpcode, DL, SubVT, Sub0, Sub1, BinOp->getFlags());
+ // Narrow for [SubVT, 0/undef,...,0/undef]: when the wide binop also has a
+ // non-extract user it survives, so narrowing only pays off if it folds for
+ // free to concat(narrow binop, 0/undef, ...). That holds when the sole
+ // extract is lane 0 (HasNonZeroExt rejects multi-real-lane cases such as
+ // [a,b,0,0,c,d]) and every other lane is 0/undef in both operands; otherwise
+ // a lane carries real data the wide binop must still compute. A slot with
+ // both sources null was never sourced (i.e. undef) and is skipped; a slot
+ // sourced on only one operand has a null source there which is not provably
+ // 0/undef, so `V &&` makes IsZeroOrUndef reject it.
+ auto IsZeroOrUndef = [](SDValue V) {
+ return V && (V.isUndef() || isNullOrNullSplat(V, /*AllowUndefs=*/true));
+ };
+ if (HasNonExtUser) {
+ if (HasNonZeroExt)
+ return SDValue();
+ for (unsigned Part = 1; Part < NumParts; ++Part) {
+ auto &Slot = Slots[Part];
----------------
arsenm wrote:
Structure binding
https://github.com/llvm/llvm-project/pull/201056
More information about the llvm-commits
mailing list