[llvm] [SLP] Cost reduce.add(mul(ext, ext)) as a dot-product reduction (PR #224066)
Alexey Bataev via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 7 03:38:53 PDT 2026
================
@@ -34168,6 +34168,100 @@ class HorizontalReduction {
cast<VectorType>(getWidenedType(RType, ReduxWidth)), FMF,
CostKind);
}
+ // reduce.add(mul(ext(A), ext(B))) may lower to a single
+ // multiply-accumulate reduction (e.g. a dot product). The multiply
+ // and the extends are already counted in the tree cost, so
+ // subtract them to avoid double counting.
+ if (RdxKind == RecurKind::Add && !ReducedVals.empty()) {
+ Type *SrcElemTy = nullptr;
+ bool IsZExt = true;
+ bool SameOperands = true;
+ // Match one reduced lane as mul(ext(a), ext(b)) where both
+ // factors use the same widening extend. Reports the extend
+ // signedness, the pre-extension scalar type, and whether both
+ // factors are the same extend value (a single extend column).
+ auto MatchMulAccLane = [](Value *V, bool &ZExt, Type *&SrcTy,
+ bool &SharedExt) {
+ Value *E0, *E1, *A, *B;
+ if (match(V,
+ m_Mul(m_CombineAnd(m_Value(E0), m_ZExt(m_Value(A))),
+ m_CombineAnd(m_Value(E1), m_ZExt(m_Value(B))))))
+ ZExt = true;
+ else if (match(V, m_Mul(m_CombineAnd(m_Value(E0),
+ m_SExt(m_Value(A))),
+ m_CombineAnd(m_Value(E1),
+ m_SExt(m_Value(B))))))
+ ZExt = false;
+ else
+ return false;
+ if (A->getType() != B->getType())
+ return false;
+ SrcTy = A->getType();
+ SharedExt = E0 == E1;
+ return true;
+ };
+ bool IsMulAcc = all_of(ReducedVals, [&](Value *RdxVal) {
+ bool ThisZExt;
+ Type *ThisSrcTy;
+ bool SharedExt;
+ if (!MatchMulAccLane(RdxVal, ThisZExt, ThisSrcTy, SharedExt))
+ return false;
+ SameOperands &= SharedExt;
+ if (!SrcElemTy) {
+ SrcElemTy = ThisSrcTy;
+ IsZExt = ThisZExt;
+ return true;
+ }
+ return SrcElemTy == ThisSrcTy && IsZExt == ThisZExt;
+ });
+ // The single vectorized node producing V. Narrowed, combined or
+ // alternate nodes are costed differently in the tree, so they
+ // are not folded.
+ using TreeEntry = BoUpSLP::TreeEntry;
+ auto GetVectorizedTE = [&](Value *V) -> const TreeEntry * {
+ ArrayRef<TreeEntry *> TEs = R.getTreeEntries(V);
+ if (TEs.size() != 1)
+ return nullptr;
+ const TreeEntry *TE = TEs.front();
+ if (TE->isGather() || TE->State != TreeEntry::Vectorize ||
+ TE->isAltShuffle() ||
+ TE->CombinedOp != TreeEntry::NotCombinedOp ||
+ R.MinBWs.contains(TE))
+ return nullptr;
+ return TE;
+ };
+ auto *Mul =
+ IsMulAcc ? cast<Instruction>(ReducedVals.front()) : nullptr;
+ const TreeEntry *MulTE = Mul ? GetVectorizedTE(Mul) : nullptr;
+ const TreeEntry *Ext0TE =
+ MulTE ? GetVectorizedTE(Mul->getOperand(0)) : nullptr;
+ const TreeEntry *Ext1TE =
+ SameOperands || !Ext0TE ? Ext0TE
+ : GetVectorizedTE(Mul->getOperand(1));
+ if (Ext1TE) {
+ // The unfused sequence costs both extends with one hint.
+ TTI::CastContextHint CCH =
+ R.getCastContextHint(*R.getOperandEntry(Ext0TE, 0));
+ if (Ext1TE == Ext0TE ||
+ CCH ==
+ R.getCastContextHint(*R.getOperandEntry(Ext1TE, 0))) {
+ auto *SrcVecTy =
+ cast<VectorType>(getWidenedType(SrcElemTy, ReduxWidth));
+ InstructionCost MulAccCost = TTI->getMulAccReductionCost(
+ IsZExt, RdxOpcode, RedTy, SrcVecTy, CostKind, CCH);
+ MulAccCost -= TTI->getArithmeticInstrCost(
+ Instruction::Mul, VectorTy, CostKind,
+ TTI::getOperandInfo(MulTE->getOperand(0)),
+ TTI::getOperandInfo(MulTE->getOperand(1)));
+ InstructionCost ExtCost = TTI->getCastInstrCost(
+ IsZExt ? Instruction::ZExt : Instruction::SExt, VectorTy,
+ SrcVecTy, CCH, CostKind);
+ MulAccCost -= SameOperands ? ExtCost : 2 * ExtCost;
+ if (MulAccCost.isValid() && MulAccCost < VectorCost)
+ VectorCost = MulAccCost;
+ }
----------------
alexey-bataev wrote:
I don't like this logic, we should do any kind of compensations here, it also must be a part of TTI logic
https://github.com/llvm/llvm-project/pull/224066
More information about the llvm-commits
mailing list