[clang] [llvm] [AMDGPU] Add dot product patterns with saturating add (clamp) (PR #187945)
via cfe-commits
cfe-commits at lists.llvm.org
Tue May 5 04:57:34 PDT 2026
================
@@ -2405,6 +2412,148 @@ bool AMDGPUCodeGenPrepareImpl::visitMbcntHi(IntrinsicInst &I) const {
return tryReplaceWithWorkitemId(I, Wave);
}
+/// Check if type is <4 x i8>.
+static bool isV4I8(Type *Ty) {
+ FixedVectorType *VTy = dyn_cast<FixedVectorType>(Ty);
+ return VTy && VTy->getNumElements() == 4 &&
+ VTy->getElementType()->isIntegerTy(8);
+}
+
+/// Helper to match the dot4 pattern: mul(zext/sext <4 x i8>, zext/sext <4 x
+/// i8>) Returns true if pattern matches and signedness matches IsSigned.
+/// Sets A, B to the <4 x i8> sources.
+static bool matchDot4Pattern(Value *MulOp, Value *&A, Value *&B,
+ bool IsSigned) {
+ Value *Src0, *Src1;
+ if (!match(MulOp, m_Mul(m_Value(Src0), m_Value(Src1))))
+ return false;
+
+ // Check that result type is <4 x i32>
+ FixedVectorType *MulTy = dyn_cast<FixedVectorType>(MulOp->getType());
+ if (!MulTy || MulTy->getNumElements() != 4 ||
+ !MulTy->getElementType()->isIntegerTy(32))
+ return false;
+
+ // Match zext or sext based on IsSigned
+ Value *ExtSrc0, *ExtSrc1;
+ if (IsSigned) {
+ if (!match(Src0, m_SExt(m_Value(ExtSrc0))) || !isV4I8(ExtSrc0->getType()))
+ return false;
+ if (!match(Src1, m_SExt(m_Value(ExtSrc1))) || !isV4I8(ExtSrc1->getType()))
+ return false;
+ } else {
+ if (!match(Src0, m_ZExt(m_Value(ExtSrc0))) || !isV4I8(ExtSrc0->getType()))
+ return false;
+ if (!match(Src1, m_ZExt(m_Value(ExtSrc1))) || !isV4I8(ExtSrc1->getType()))
+ return false;
+ }
+
+ A = ExtSrc0;
+ B = ExtSrc1;
+ return true;
+}
+
+/// Try to convert vector.reduce.add(mul(zext/sext <4 x i8>, zext/sext <4 x
+/// i8>)) to a dot4 intrinsic call (non-saturating case only).
+bool AMDGPUCodeGenPrepareImpl::visitVectorReduceAdd(IntrinsicInst &I) {
+ // Check if we have dot4 instructions available
+ if (!ST.hasDot7Insts() || (!ST.hasDot1Insts() && !ST.hasDot8Insts()))
+ return false;
+
+ Value *A = nullptr, *B = nullptr;
+
+ // Try unsigned first, then signed
+ bool IsSigned = false;
+ if (!matchDot4Pattern(I.getArgOperand(0), A, B, /*IsSigned=*/false)) {
+ if (!matchDot4Pattern(I.getArgOperand(0), A, B, /*IsSigned=*/true))
+ return false;
+ IsSigned = true;
+ }
+
+ LLVMContext &Ctx = I.getContext();
+ Type *I32Ty = Type::getInt32Ty(Ctx);
+ IRBuilder<> Builder(&I);
+
+ // Bitcast <4 x i8> to i32
+ Value *ASrc = Builder.CreateBitCast(A, I32Ty);
+ Value *BSrc = Builder.CreateBitCast(B, I32Ty);
+
+ // Non-saturating case: accumulator is 0, clamp is false
+ Value *Acc = ConstantInt::get(I32Ty, 0);
+ Value *Clamp = ConstantInt::getFalse(Ctx);
----------------
addmisol wrote:
I tested this ir on latest main:
```
define i32 @test(i32 %a, i32 %b, i32 %acc) {
%dot = call i32 @llvm.amdgcn.udot4(i32 %a, i32 %b, i32 0, i1 false)
%result = call i32 @llvm.uadd.sat.i32(i32 %dot, i32 %acc)
ret i32 %result
}
```
It generates 2 instructions:
```
v_dot4_u32_u8 v0, v0, v1, 0
v_add_u32_e64 v0, v0, v2 clamp
```
https://github.com/llvm/llvm-project/pull/187945
More information about the cfe-commits
mailing list