[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
Tue Oct 6 04:46:32 PDT 2026
================
@@ -34168,6 +34168,99 @@ class HorizontalReduction {
cast<VectorType>(getWidenedType(RType, ReduxWidth)), FMF,
CostKind);
}
+ // reduce.add(mul(ext(A), ext(B))) lowers to a single dot-product
+ // reduction (e.g. UDOT/SDOT) where the target supports it. Prefer
+ // that fused cost when it is cheaper. The extends and the multiply
+ // are already counted in the tree cost, so subtract them here to
+ // avoid double counting (mirrors the FMA handling below).
+ 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()->getScalarType();
+ 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;
+ if (!SharedExt)
+ SameOperands = false;
+ if (!SrcElemTy) {
+ SrcElemTy = ThisSrcTy;
+ IsZExt = ThisZExt;
+ return true;
+ }
+ return SrcElemTy == ThisSrcTy && IsZExt == ThisZExt;
+ });
+ // Only fuse when the multiply factors are vectorized as the
+ // root non-gather tree entry; otherwise the dot-product does not
+ // form and the fused cost would not apply.
+ ArrayRef MulTEs = R.getTreeEntries(ReducedVals.front());
+ auto MulTEIt = find_if(MulTEs, [](const auto *TE) {
+ return TE->Idx == 0 && !TE->isGather();
+ });
+ if (IsMulAcc && SrcElemTy && MulTEIt != MulTEs.end()) {
+ const auto *MulTE = *MulTEIt;
+ auto *SrcVecTy =
+ cast<VectorType>(getWidenedType(SrcElemTy, ReduxWidth));
+ InstructionCost RedCost = TTI->getMulAccReductionCost(
+ IsZExt, RdxOpcode, RedTy, SrcVecTy, CostKind);
+ if (RedCost.isValid()) {
+ auto *Mul = cast<Instruction>(ReducedVals.front());
+ auto *Ext0 = cast<Instruction>(Mul->getOperand(0));
+ auto *Ext1 = cast<Instruction>(Mul->getOperand(1));
+ // Cast context of an extend comes from its operand node when
+ // the extend is itself a single vectorized entry.
+ auto GetExtCCH = [&](Instruction *Ext) {
+ if (ArrayRef ExtTEs = R.getTreeEntries(Ext);
+ ExtTEs.size() == 1)
+ return R.getCastContextHint(
+ *R.getOperandEntry(ExtTEs.front(), 0));
+ return TTI::getCastContextHint(Ext);
+ };
+ // Operand info from the multiply's tree-entry operands so it
+ // reflects all lanes, not a single scalar.
+ InstructionCost MulCost = TTI->getArithmeticInstrCost(
+ Instruction::Mul, VectorTy, CostKind,
+ TTI::getOperandInfo(MulTE->getOperand(0)),
+ TTI::getOperandInfo(MulTE->getOperand(1)));
+ InstructionCost TreeExtCost = TTI->getCastInstrCost(
+ Ext0->getOpcode(), VectorTy, SrcVecTy, GetExtCCH(Ext0),
+ CostKind);
+ if (!SameOperands)
+ TreeExtCost += TTI->getCastInstrCost(
+ Ext1->getOpcode(), VectorTy, SrcVecTy, GetExtCCH(Ext1),
+ CostKind);
+ InstructionCost MulAccCost = RedCost - MulCost - TreeExtCost;
+ if (MulAccCost < VectorCost)
+ VectorCost = MulAccCost;
+ }
+ }
+ }
----------------
alexey-bataev wrote:
Most of this stuff should be in TTI, which shall query SLP for the required info. This is target-specific stuff, SLP should not be aware of it
https://github.com/llvm/llvm-project/pull/224066
More information about the llvm-commits
mailing list