[llvm] [SelectionDAG] Fold constant PARTIAL_REDUCE_SMLA/UMLA/SUMLA nodes (PR #210351)
Craig Topper via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 4 22:01:16 PDT 2026
================
@@ -7965,6 +7931,87 @@ SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, const SDLoc &DL,
}
}
+ // Constant fold integer partial reductions with constant BUILD_VECTOR
+ // operands. The reduction order is deliberately unspecified. Use the same
+ // subvector layout as TargetLowering::expandPartialReduceMLA(), where input
+ // lane I contributes to accumulator lane I % NumAccElts.
+ if (Opcode == ISD::PARTIAL_REDUCE_SMLA ||
+ Opcode == ISD::PARTIAL_REDUCE_UMLA ||
+ Opcode == ISD::PARTIAL_REDUCE_SUMLA) {
+ // These nodes have no scalar form, so unsupported cases must not fall
+ // through to generic per-lane vector folding.
+ if (!llvm::all_of(Ops, [](SDValue Op) {
+ return ISD::isBuildVectorOfConstantSDNodes(Op.getNode());
+ }))
+ return SDValue();
+
+ unsigned AccEltBits = VT.getScalarSizeInBits();
+ unsigned InputEltBits = Ops[1].getScalarValueSizeInBits();
+ unsigned NumAccElts = VT.getVectorNumElements();
+ unsigned NumInputElts = Ops[1].getValueType().getVectorNumElements();
+ SmallVector<APInt, 8> Results(NumAccElts, APInt::getZero(AccEltBits));
+ BitVector PoisonElts(NumAccElts);
+
+ for (unsigned I = 0; I != NumAccElts; ++I) {
+ SDValue Elt = Ops[0].getOperand(I);
+ if (Elt.getOpcode() == ISD::POISON) {
+ PoisonElts.set(I);
+ continue;
+ }
+ auto *C = dyn_cast<ConstantSDNode>(Elt);
+ if (!C || C->isOpaque())
+ return SDValue();
+ Results[I] = C->getAPIntValue().trunc(AccEltBits);
+ }
+
+ bool IsLHSSigned = Opcode != ISD::PARTIAL_REDUCE_UMLA;
+ bool IsRHSSigned = Opcode == ISD::PARTIAL_REDUCE_SMLA;
+ for (unsigned I = 0; I != NumInputElts; ++I) {
+ const unsigned AccIdx = I % NumAccElts;
+ SDValue LHSElt = Ops[1].getOperand(I);
+ SDValue RHSElt = Ops[2].getOperand(I);
+ if (LHSElt.getOpcode() == ISD::POISON ||
+ RHSElt.getOpcode() == ISD::POISON) {
+ PoisonElts.set(AccIdx);
+ continue;
+ }
+
+ auto *LHS = dyn_cast<ConstantSDNode>(LHSElt);
+ auto *RHS = dyn_cast<ConstantSDNode>(RHSElt);
+ if (!LHS || !RHS || LHS->isOpaque() || RHS->isOpaque())
+ return SDValue();
+
+ APInt LHSVal = LHS->getAPIntValue().trunc(InputEltBits);
+ APInt RHSVal = RHS->getAPIntValue().trunc(InputEltBits);
+ LHSVal = IsLHSSigned ? LHSVal.sextOrTrunc(AccEltBits)
+ : LHSVal.zextOrTrunc(AccEltBits);
+ RHSVal = IsRHSSigned ? RHSVal.sextOrTrunc(AccEltBits)
+ : RHSVal.zextOrTrunc(AccEltBits);
+ Results[AccIdx] += LHSVal * RHSVal;
+ }
+
+ // After type legalization the vector element type may not be a legal
+ // scalar type (e.g. i16 on AArch64). Create the folded constants in the
+ // promoted legal scalar type instead, matching the generic per-lane path
+ // below. Bail out if legalization would narrow the type, since the lane
+ // value would not fit.
+ EVT AccEltVT = VT.getVectorElementType();
+ EVT LegalSVT = AccEltVT;
+ if (NewNodesMustHaveLegalTypes && LegalSVT.isInteger()) {
+ LegalSVT = TLI->getTypeToTransformTo(*getContext(), LegalSVT);
+ if (LegalSVT.bitsLT(AccEltVT))
+ return SDValue();
+ }
+
+ SmallVector<SDValue, 8> ResultOps;
+ for (unsigned I = 0; I != NumAccElts; ++I)
+ ResultOps.push_back(
+ PoisonElts[I] ? getPOISON(LegalSVT)
+ : getConstant(Results[I].sext(LegalSVT.getSizeInBits()),
+ DL, LegalSVT));
+ return getBuildVector(VT, DL, ResultOps);
+ }
+
// This is for vector folding only from here on.
----------------
topperc wrote:
Should we move the new code this code below this check for vector types?
https://github.com/llvm/llvm-project/pull/210351
More information about the llvm-commits
mailing list