[llvm] [Hexagon] Add qf16 fused multiply-accumulate patterns (PR #215656)
Chamika Sudusinghe via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 11 13:47:44 PDT 2026
https://github.com/chamikasudusinghe created https://github.com/llvm/llvm-project/pull/215656
On HVX with the qfloat feature an f16 multiply produces a qf16 result (`V6_vmpy_qf16_hf`), and `V6_vadd_qf16_mix` accumulates a qf16 value with an hf value while keeping the result in qf16. Lowering an `fmul` and an `fadd` separately converts the product back to hf in between, so a multiply-accumulate takes four instructions instead of three.
This makes `ISD::FMA` legal for `v64f16` under `UseHVXQFloat` and adds a pattern that keeps the product in qf16 across the accumulate, converting to hf once at the end. DAGCombine contracts `fmul`+`fadd` into the `fma` where the fast-math flags allow it, so the loop-carried accumulator of a vectorized f16 matmul inner loop drops the extra conversion per MAC.
Test: `llvm/test/CodeGen/Hexagon/autohvx/qf16-fused-mac.ll` covers an explicit `llvm.fma.v64f16` and a fast-math loop-carried MAC.
>From 458eef3e49dbbe556f2315514a2170873ac6fe29 Mon Sep 17 00:00:00 2001
From: Chamika Sudusinghe <csudusin at qti.qualcomm.com>
Date: Tue, 11 Aug 2026 15:40:17 -0500
Subject: [PATCH] [Hexagon] Add qf16 fused multiply-accumulate patterns
On HVX with the qfloat feature an f16 multiply produces a qf16 result
(V6_vmpy_qf16_hf), and V6_vadd_qf16_mix accumulates a qf16 value with an hf
value while keeping the result in qf16. Lowering an fmul and an fadd
separately converts the product back to hf in between, so a multiply-add
takes four instructions instead of three.
Make ISD::FMA legal for v64f16 under UseHVXQFloat and add a pattern that
keeps the product in qf16 across the accumulate, converting to hf once at the
end. DAGCombine contracts fmul+fadd into the fma where the fast-math flags
allow it.
---
.../Target/Hexagon/HexagonISelLoweringHVX.cpp | 4 ++
llvm/lib/Target/Hexagon/HexagonPatternsHVX.td | 8 ++++
.../CodeGen/Hexagon/autohvx/qf16-fused-mac.ll | 46 +++++++++++++++++++
3 files changed, 58 insertions(+)
create mode 100644 llvm/test/CodeGen/Hexagon/autohvx/qf16-fused-mac.ll
diff --git a/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp b/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp
index 670fe270f36a1..df853d1c083a5 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp
@@ -249,6 +249,10 @@ HexagonTargetLowering::initializeHVXLowering() {
if (Subtarget.useHVXQFloatOps()) {
setOperationAction(ISD::FP_EXTEND, MVT::v64f32, Custom);
setOperationAction(ISD::FP_ROUND, MVT::v64f16, Legal);
+ // qf16 has a fused multiply-accumulate form (see HexagonPatternsHVX.td).
+ // Declaring it legal lets DAGCombine contract fmul+fadd into fma where
+ // the fast-math flags permit it, instead of emitting both separately.
+ setOperationAction(ISD::FMA, MVT::v64f16, Legal);
} else if (Subtarget.useHVXIEEEFPOps()) {
setOperationAction(ISD::FP_EXTEND, MVT::v64f32, Legal);
setOperationAction(ISD::FP_ROUND, MVT::v64f16, Legal);
diff --git a/llvm/lib/Target/Hexagon/HexagonPatternsHVX.td b/llvm/lib/Target/Hexagon/HexagonPatternsHVX.td
index fa13bd013bc7c..48015af46f045 100644
--- a/llvm/lib/Target/Hexagon/HexagonPatternsHVX.td
+++ b/llvm/lib/Target/Hexagon/HexagonPatternsHVX.td
@@ -565,6 +565,14 @@ let Predicates = [UseHVXV68, UseHVXQFloat] in {
def: OpR_RR_pat_conv_hf<V6_vsub_hf, pf2<fsub>, VecF16, HVF16>;
def: OpR_RR_pat_conv_hf<V6_vadd_hf, pf2<fadd>, VecF16, HVF16>;
def: OpR_RR_pat_conv_hf<V6_vmpy_qf16_hf, pf2<fmul>, VecF16, HVF16>;
+
+ // Fused multiply-add. Keeping the vmpy product in qf16 and accumulating with
+ // vadd_qf16_mix avoids the qf16->hf conversion that lowering the multiply and
+ // the add separately would emit, so a MAC is 3 instructions instead of 4.
+ def: Pat<(VecF16 (fma HVF16:$Vu, HVF16:$Vv, HVF16:$Vacc)),
+ (V6_vconv_hf_qf16 (V6_vadd_qf16_mix
+ (V6_vmpy_qf16_hf HVF16:$Vu, HVF16:$Vv), HVF16:$Vacc))>;
+
def: OpR_RR_pat_conv<V6_vsub_sf, pf2<fsub>, VecF32, HVF32>;
def: OpR_RR_pat_conv<V6_vadd_sf, pf2<fadd>, VecF32, HVF32>;
def: OpR_RR_pat_conv<V6_vmpy_qf32_sf, pf2<fmul>, VecF32, HVF32>;
diff --git a/llvm/test/CodeGen/Hexagon/autohvx/qf16-fused-mac.ll b/llvm/test/CodeGen/Hexagon/autohvx/qf16-fused-mac.ll
new file mode 100644
index 0000000000000..7afa87cd70d97
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/autohvx/qf16-fused-mac.ll
@@ -0,0 +1,46 @@
+; RUN: llc -mtriple=hexagon < %s | FileCheck %s
+
+; Fused multiply-accumulate on qf16: the vmpy product stays in qf16 and is
+; accumulated with vadd_qf16_mix, so a MAC needs one conversion back to hf
+; instead of two.
+
+; The shape that matters: a loop-carried accumulator, which is what a vectorized
+; f16 matmul inner loop looks like. Without the fma pattern the product is
+; converted back to hf before the add (vadd(hf,hf)), costing an extra
+; instruction per MAC.
+define <64 x half> @mac_loop_carried(<64 x half> %init, ptr %pa, ptr %pb, i32 %n) #0 {
+; CHECK-LABEL: mac_loop_carried:
+; CHECK: [[M:v[0-9]+]].qf16 = vmpy({{v[0-9]+}}.hf,{{v[0-9]+}}.hf)
+; CHECK: [[A:v[0-9]+]].qf16 = vadd([[M]].qf16,{{v[0-9]+}}.hf)
+; CHECK-NOT: .hf = [[M]].qf16
+entry:
+ br label %loop
+
+loop:
+ %acc = phi <64 x half> [ %init, %entry ], [ %new, %loop ]
+ %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+ %a = load <64 x half>, ptr %pa
+ %b = load <64 x half>, ptr %pb
+ %mul = fmul fast <64 x half> %a, %b
+ %new = fadd fast <64 x half> %acc, %mul
+ %i.next = add i32 %i, 1
+ %cmp = icmp slt i32 %i.next, %n
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret <64 x half> %new
+}
+
+; An explicit fma is matched directly, with no fast-math flags needed.
+define <64 x half> @fma_v64f16(<64 x half> %a0, <64 x half> %a1, <64 x half> %a2) #0 {
+; CHECK-LABEL: fma_v64f16:
+; CHECK: [[M:v[0-9]+]].qf16 = vmpy(v0.hf,v1.hf)
+; CHECK: [[A:v[0-9]+]].qf16 = vadd([[M]].qf16,v2.hf)
+; CHECK: v0.hf = [[A]].qf16
+ %v0 = call <64 x half> @llvm.fma.v64f16(<64 x half> %a0, <64 x half> %a1, <64 x half> %a2)
+ ret <64 x half> %v0
+}
+
+declare <64 x half> @llvm.fma.v64f16(<64 x half>, <64 x half>, <64 x half>)
+
+attributes #0 = { nounwind "target-cpu"="hexagonv68" "target-features"="+hvxv68,+hvx-length128b,+hvx-qfloat" }
More information about the llvm-commits
mailing list