[llvm] 8730fb7 - [llvm][RISCV] Optimize fneg for fixed vectors (#194555)

via llvm-commits llvm-commits at lists.llvm.org
Sun May 10 08:58:23 PDT 2026


Author: Brandon Wu
Date: 2026-05-10T23:58:19+08:00
New Revision: 8730fb782b815bfec78f2731798258e105024cf7

URL: https://github.com/llvm/llvm-project/commit/8730fb782b815bfec78f2731798258e105024cf7
DIFF: https://github.com/llvm/llvm-project/commit/8730fb782b815bfec78f2731798258e105024cf7.diff

LOG: [llvm][RISCV] Optimize fneg for fixed vectors (#194555)

vfneg is not available on zvfhmin or zvfbfmin, it's expected to expand
to integer operations instead of unrolling to scalar operations.
General expandFNEG already handles that in most of cases except for
fixed vector types that are not promotable, we need to find a better
heuristic to gate this.

Added: 
    

Modified: 
    llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
    llvm/test/CodeGen/NVPTX/f16-instructions.ll
    llvm/test/CodeGen/RISCV/rvv/fixed-vectors-vfneg-sdnode.ll
    llvm/test/CodeGen/Thumb2/mve-fp-negabs.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
index 0a9c3dda7f330..21f9bce565efe 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
@@ -2066,10 +2066,26 @@ SDValue VectorLegalizer::ExpandFNEG(SDNode *Node) {
   if (!TLI.isOperationLegalOrCustom(ISD::XOR, IntVT))
     return SDValue();
 
-  // FIXME: The FSUB check is here to force unrolling v1f64 vectors on AArch64.
-  if (!TLI.isOperationLegalOrCustomOrPromote(ISD::FSUB, VT) &&
-      !VT.isScalableVector())
-    return SDValue();
+  // Heuristic check to determine whether vector should be expanded to integer
+  // operations or unrolled to scalar operations.
+  // 1. Scalable vector is never unrolled.
+  // 2. Fixed vector is unrolled if one of followings is true:
+  //      a. Vector only has 1 element and target knows how to handle scalar
+  //         FNEG (either legal or custom expand or promote).
+  //      b. Vector has more than 1 element and target supports scalar
+  //         FNEG natively and vector length <= 2(1 XOR + 1 CONST).
+  // FIXME: Scalar construction instruction count varies in every architecture,
+  // here we assume 1 instruction for now.
+  if (VT.isFixedLengthVector()) {
+    EVT EltVT = VT.getVectorElementType();
+    unsigned NumElts = VT.getVectorNumElements();
+    if ((NumElts == 1 &&
+         TLI.isOperationLegalOrCustomOrPromote(ISD::FNEG, EltVT)) ||
+        (NumElts < 3 && TLI.isOperationLegal(ISD::FNEG, EltVT) &&
+         TLI.isExtractVecEltCheap(VT, 0) &&
+         (NumElts == 1 || TLI.isExtractVecEltCheap(VT, 1))))
+      return SDValue();
+  }
 
   SDLoc DL(Node);
   SDValue Cast = DAG.getNode(ISD::BITCAST, DL, IntVT, Node->getOperand(0));

diff  --git a/llvm/test/CodeGen/NVPTX/f16-instructions.ll b/llvm/test/CodeGen/NVPTX/f16-instructions.ll
index 53288b35d55a4..38c5b567b1378 100644
--- a/llvm/test/CodeGen/NVPTX/f16-instructions.ll
+++ b/llvm/test/CodeGen/NVPTX/f16-instructions.ll
@@ -1176,8 +1176,7 @@ define half @test_neg_f16(half noundef %arg) #0 {
 ; CHECK-LABEL: test_neg_f16x2(
 ; CHECK-F16-NOFTZ: neg.f16x2
 ; CHECK-F16-FTZ: neg.ftz.f16x2
-; CHECK-NOF16: xor.b16  	%rs{{.*}}, %rs{{.*}}, -32768
-; CHECK-NOF16: xor.b16  	%rs{{.*}}, %rs{{.*}}, -32768
+; CHECK-NOF16: xor.b32 %r{{.*}}, %r{{.*}}, -2147450880
 define <2 x half> @test_neg_f16x2(<2 x half> noundef %arg) #0 {
   %res = fneg <2 x half> %arg
   ret <2 x half> %res

diff  --git a/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-vfneg-sdnode.ll b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-vfneg-sdnode.ll
index b3b9a62600f46..cf16fde1354b3 100644
--- a/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-vfneg-sdnode.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-vfneg-sdnode.ll
@@ -1,66 +1,193 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=riscv32 -mattr=+experimental-zvfbfa,+v \
-; RUN:     -target-abi=ilp32d -verify-machineinstrs < %s | FileCheck %s
-; RUN: llc -mtriple=riscv64 -mattr=+experimental-zvfbfa,+v \
-; RUN:     -target-abi=lp64d -verify-machineinstrs < %s | FileCheck %s
+; RUN: llc -mtriple=riscv32 -mattr=+v,+zvfhmin,+experimental-zvfbfa \
+; RUN:     -target-abi=ilp32d -verify-machineinstrs < %s \
+; RUN:     | FileCheck %s --check-prefixes=CHECK,ZVFBFA
+; RUN: llc -mtriple=riscv64 -mattr=+v,+zvfhmin,+experimental-zvfbfa \
+; RUN:     -target-abi=lp64d -verify-machineinstrs < %s \
+; RUN:     | FileCheck %s --check-prefixes=CHECK,ZVFBFA
+; RUN: llc -mtriple=riscv32 -mattr=+v,+zvfhmin,+zvfbfmin -target-abi=ilp32d \
+; RUN:     -verify-machineinstrs < %s \
+; RUN:     | FileCheck %s --check-prefixes=CHECK,ZVFBFMIN
+; RUN: llc -mtriple=riscv64 -mattr=+v,+zvfhmin,+zvfbfmin -target-abi=lp64d \
+; RUN:     -verify-machineinstrs < %s \
+; RUN:     | FileCheck %s --check-prefixes=CHECK,ZVFBFMIN
 
 define <1 x bfloat> @v1bf16(<1 x bfloat> %va) {
-; CHECK-LABEL: v1bf16:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    vsetivli zero, 1, e16alt, mf4, ta, ma
-; CHECK-NEXT:    vfneg.v v8, v8
-; CHECK-NEXT:    ret
+; ZVFBFA-LABEL: v1bf16:
+; ZVFBFA:       # %bb.0:
+; ZVFBFA-NEXT:    vsetivli zero, 1, e16alt, mf4, ta, ma
+; ZVFBFA-NEXT:    vfneg.v v8, v8
+; ZVFBFA-NEXT:    ret
+;
+; ZVFBFMIN-LABEL: v1bf16:
+; ZVFBFMIN:       # %bb.0:
+; ZVFBFMIN-NEXT:    lui a0, 8
+; ZVFBFMIN-NEXT:    vsetivli zero, 1, e16, mf4, ta, ma
+; ZVFBFMIN-NEXT:    vxor.vx v8, v8, a0
+; ZVFBFMIN-NEXT:    ret
   %vb = fneg <1 x bfloat> %va
   ret <1 x bfloat> %vb
 }
 
 define <2 x bfloat> @v2bf16(<2 x bfloat> %va) {
-; CHECK-LABEL: v2bf16:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    vsetivli zero, 2, e16alt, mf4, ta, ma
-; CHECK-NEXT:    vfneg.v v8, v8
-; CHECK-NEXT:    ret
+; ZVFBFA-LABEL: v2bf16:
+; ZVFBFA:       # %bb.0:
+; ZVFBFA-NEXT:    vsetivli zero, 2, e16alt, mf4, ta, ma
+; ZVFBFA-NEXT:    vfneg.v v8, v8
+; ZVFBFA-NEXT:    ret
+;
+; ZVFBFMIN-LABEL: v2bf16:
+; ZVFBFMIN:       # %bb.0:
+; ZVFBFMIN-NEXT:    lui a0, 8
+; ZVFBFMIN-NEXT:    vsetivli zero, 2, e16, mf4, ta, ma
+; ZVFBFMIN-NEXT:    vxor.vx v8, v8, a0
+; ZVFBFMIN-NEXT:    ret
   %vb = fneg <2 x bfloat> %va
   ret <2 x bfloat> %vb
 }
 
 define <4 x bfloat> @v4bf16(<4 x bfloat> %va) {
-; CHECK-LABEL: v4bf16:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    vsetivli zero, 4, e16alt, mf2, ta, ma
-; CHECK-NEXT:    vfneg.v v8, v8
-; CHECK-NEXT:    ret
+; ZVFBFA-LABEL: v4bf16:
+; ZVFBFA:       # %bb.0:
+; ZVFBFA-NEXT:    vsetivli zero, 4, e16alt, mf2, ta, ma
+; ZVFBFA-NEXT:    vfneg.v v8, v8
+; ZVFBFA-NEXT:    ret
+;
+; ZVFBFMIN-LABEL: v4bf16:
+; ZVFBFMIN:       # %bb.0:
+; ZVFBFMIN-NEXT:    lui a0, 8
+; ZVFBFMIN-NEXT:    vsetivli zero, 4, e16, mf2, ta, ma
+; ZVFBFMIN-NEXT:    vxor.vx v8, v8, a0
+; ZVFBFMIN-NEXT:    ret
   %vb = fneg <4 x bfloat> %va
   ret <4 x bfloat> %vb
 }
 
 define <8 x bfloat> @v8bf16(<8 x bfloat> %va) {
-; CHECK-LABEL: v8bf16:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    vsetivli zero, 8, e16alt, m1, ta, ma
-; CHECK-NEXT:    vfneg.v v8, v8
-; CHECK-NEXT:    ret
+; ZVFBFA-LABEL: v8bf16:
+; ZVFBFA:       # %bb.0:
+; ZVFBFA-NEXT:    vsetivli zero, 8, e16alt, m1, ta, ma
+; ZVFBFA-NEXT:    vfneg.v v8, v8
+; ZVFBFA-NEXT:    ret
+;
+; ZVFBFMIN-LABEL: v8bf16:
+; ZVFBFMIN:       # %bb.0:
+; ZVFBFMIN-NEXT:    lui a0, 8
+; ZVFBFMIN-NEXT:    vsetivli zero, 8, e16, m1, ta, ma
+; ZVFBFMIN-NEXT:    vxor.vx v8, v8, a0
+; ZVFBFMIN-NEXT:    ret
   %vb = fneg <8 x bfloat> %va
   ret <8 x bfloat> %vb
 }
 
 define <16 x bfloat> @v16bf16(<16 x bfloat> %va) {
-; CHECK-LABEL: v16bf16:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    vsetivli zero, 16, e16alt, m2, ta, ma
-; CHECK-NEXT:    vfneg.v v8, v8
-; CHECK-NEXT:    ret
+; ZVFBFA-LABEL: v16bf16:
+; ZVFBFA:       # %bb.0:
+; ZVFBFA-NEXT:    vsetivli zero, 16, e16alt, m2, ta, ma
+; ZVFBFA-NEXT:    vfneg.v v8, v8
+; ZVFBFA-NEXT:    ret
+;
+; ZVFBFMIN-LABEL: v16bf16:
+; ZVFBFMIN:       # %bb.0:
+; ZVFBFMIN-NEXT:    lui a0, 8
+; ZVFBFMIN-NEXT:    vsetivli zero, 16, e16, m2, ta, ma
+; ZVFBFMIN-NEXT:    vxor.vx v8, v8, a0
+; ZVFBFMIN-NEXT:    ret
   %vb = fneg <16 x bfloat> %va
   ret <16 x bfloat> %vb
 }
 
 define <32 x bfloat> @v32bf16(<32 x bfloat> %va) {
-; CHECK-LABEL: v32bf16:
+; ZVFBFA-LABEL: v32bf16:
+; ZVFBFA:       # %bb.0:
+; ZVFBFA-NEXT:    li a0, 32
+; ZVFBFA-NEXT:    vsetvli zero, a0, e16alt, m4, ta, ma
+; ZVFBFA-NEXT:    vfneg.v v8, v8
+; ZVFBFA-NEXT:    ret
+;
+; ZVFBFMIN-LABEL: v32bf16:
+; ZVFBFMIN:       # %bb.0:
+; ZVFBFMIN-NEXT:    li a0, 32
+; ZVFBFMIN-NEXT:    lui a1, 8
+; ZVFBFMIN-NEXT:    vsetvli zero, a0, e16, m4, ta, ma
+; ZVFBFMIN-NEXT:    vxor.vx v8, v8, a1
+; ZVFBFMIN-NEXT:    ret
+  %vb = fneg <32 x bfloat> %va
+  ret <32 x bfloat> %vb
+}
+
+define <64 x bfloat> @v64bf16(<64 x bfloat> %va) {
+; ZVFBFA-LABEL: v64bf16:
+; ZVFBFA:       # %bb.0:
+; ZVFBFA-NEXT:    li a0, 64
+; ZVFBFA-NEXT:    vsetvli zero, a0, e16alt, m8, ta, ma
+; ZVFBFA-NEXT:    vfneg.v v8, v8
+; ZVFBFA-NEXT:    ret
+;
+; ZVFBFMIN-LABEL: v64bf16:
+; ZVFBFMIN:       # %bb.0:
+; ZVFBFMIN-NEXT:    li a0, 64
+; ZVFBFMIN-NEXT:    lui a1, 8
+; ZVFBFMIN-NEXT:    vsetvli zero, a0, e16, m8, ta, ma
+; ZVFBFMIN-NEXT:    vxor.vx v8, v8, a1
+; ZVFBFMIN-NEXT:    ret
+  %vb = fneg <64 x bfloat> %va
+  ret <64 x bfloat> %vb
+}
+
+define <1 x half> @v1f16(<1 x half> %va) {
+; CHECK-LABEL: v1f16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a0, 8
+; CHECK-NEXT:    vsetivli zero, 1, e16, mf4, ta, ma
+; CHECK-NEXT:    vxor.vx v8, v8, a0
+; CHECK-NEXT:    ret
+  %vb = fneg <1 x half> %va
+  ret <1 x half> %vb
+}
+
+define <2 x half> @v2f16(<2 x half> %va) {
+; CHECK-LABEL: v2f16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a0, 8
+; CHECK-NEXT:    vsetivli zero, 2, e16, mf4, ta, ma
+; CHECK-NEXT:    vxor.vx v8, v8, a0
+; CHECK-NEXT:    ret
+  %vb = fneg <2 x half> %va
+  ret <2 x half> %vb
+}
+
+define <8 x half> @v8f16(<8 x half> %va) {
+; CHECK-LABEL: v8f16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a0, 8
+; CHECK-NEXT:    vsetivli zero, 8, e16, m1, ta, ma
+; CHECK-NEXT:    vxor.vx v8, v8, a0
+; CHECK-NEXT:    ret
+  %vb = fneg <8 x half> %va
+  ret <8 x half> %vb
+}
+
+define <32 x half> @v32f16(<32 x half> %va) {
+; CHECK-LABEL: v32f16:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    li a0, 32
-; CHECK-NEXT:    vsetvli zero, a0, e16alt, m4, ta, ma
-; CHECK-NEXT:    vfneg.v v8, v8
+; CHECK-NEXT:    lui a1, 8
+; CHECK-NEXT:    vsetvli zero, a0, e16, m4, ta, ma
+; CHECK-NEXT:    vxor.vx v8, v8, a1
 ; CHECK-NEXT:    ret
-  %vb = fneg <32 x bfloat> %va
-  ret <32 x bfloat> %vb
+  %vb = fneg <32 x half> %va
+  ret <32 x half> %vb
+}
+
+define <64 x half> @v64f16(<64 x half> %va) {
+; CHECK-LABEL: v64f16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a0, 64
+; CHECK-NEXT:    lui a1, 8
+; CHECK-NEXT:    vsetvli zero, a0, e16, m8, ta, ma
+; CHECK-NEXT:    vxor.vx v8, v8, a1
+; CHECK-NEXT:    ret
+  %vb = fneg <64 x half> %va
+  ret <64 x half> %vb
 }

diff  --git a/llvm/test/CodeGen/Thumb2/mve-fp-negabs.ll b/llvm/test/CodeGen/Thumb2/mve-fp-negabs.ll
index 0e993f35ce85d..8b8b897a32eda 100644
--- a/llvm/test/CodeGen/Thumb2/mve-fp-negabs.ll
+++ b/llvm/test/CodeGen/Thumb2/mve-fp-negabs.ll
@@ -5,22 +5,8 @@
 define arm_aapcs_vfpcc <8 x half> @fneg_float16_t(<8 x half> %src) {
 ; CHECK-MVE-LABEL: fneg_float16_t:
 ; CHECK-MVE:       @ %bb.0: @ %entry
-; CHECK-MVE-NEXT:    vmovx.f16 s4, s0
-; CHECK-MVE-NEXT:    vneg.f16 s0, s0
-; CHECK-MVE-NEXT:    vneg.f16 s4, s4
-; CHECK-MVE-NEXT:    vins.f16 s0, s4
-; CHECK-MVE-NEXT:    vmovx.f16 s4, s1
-; CHECK-MVE-NEXT:    vneg.f16 s4, s4
-; CHECK-MVE-NEXT:    vneg.f16 s1, s1
-; CHECK-MVE-NEXT:    vins.f16 s1, s4
-; CHECK-MVE-NEXT:    vmovx.f16 s4, s2
-; CHECK-MVE-NEXT:    vneg.f16 s4, s4
-; CHECK-MVE-NEXT:    vneg.f16 s2, s2
-; CHECK-MVE-NEXT:    vins.f16 s2, s4
-; CHECK-MVE-NEXT:    vmovx.f16 s4, s3
-; CHECK-MVE-NEXT:    vneg.f16 s4, s4
-; CHECK-MVE-NEXT:    vneg.f16 s3, s3
-; CHECK-MVE-NEXT:    vins.f16 s3, s4
+; CHECK-MVE-NEXT:    vmov.i16 q1, #0x8000
+; CHECK-MVE-NEXT:    veor q0, q0, q1
 ; CHECK-MVE-NEXT:    bx lr
 ;
 ; CHECK-MVEFP-LABEL: fneg_float16_t:
@@ -35,10 +21,8 @@ entry:
 define arm_aapcs_vfpcc <4 x float> @fneg_float32_t(<4 x float> %src) {
 ; CHECK-MVE-LABEL: fneg_float32_t:
 ; CHECK-MVE:       @ %bb.0: @ %entry
-; CHECK-MVE-NEXT:    vneg.f32 s3, s3
-; CHECK-MVE-NEXT:    vneg.f32 s2, s2
-; CHECK-MVE-NEXT:    vneg.f32 s1, s1
-; CHECK-MVE-NEXT:    vneg.f32 s0, s0
+; CHECK-MVE-NEXT:    vmov.i32 q1, #0x80000000
+; CHECK-MVE-NEXT:    veor q0, q0, q1
 ; CHECK-MVE-NEXT:    bx lr
 ;
 ; CHECK-MVEFP-LABEL: fneg_float32_t:
@@ -53,20 +37,17 @@ entry:
 define arm_aapcs_vfpcc <2 x double> @fneg_float64_t(<2 x double> %src) {
 ; CHECK-LABEL: fneg_float64_t:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    .pad #16
-; CHECK-NEXT:    sub sp, #16
-; CHECK-NEXT:    vstr d1, [sp]
-; CHECK-NEXT:    ldrb.w r0, [sp, #7]
-; CHECK-NEXT:    vstr d0, [sp, #8]
-; CHECK-NEXT:    ldrb.w r1, [sp, #15]
-; CHECK-NEXT:    eor r0, r0, #128
-; CHECK-NEXT:    strb.w r0, [sp, #7]
-; CHECK-NEXT:    vldr d1, [sp]
-; CHECK-NEXT:    eor r0, r1, #128
-; CHECK-NEXT:    strb.w r0, [sp, #15]
-; CHECK-NEXT:    vldr d0, [sp, #8]
-; CHECK-NEXT:    add sp, #16
+; CHECK-NEXT:    adr r0, .LCPI2_0
+; CHECK-NEXT:    vldrw.u32 q1, [r0]
+; CHECK-NEXT:    veor q0, q0, q1
 ; CHECK-NEXT:    bx lr
+; CHECK-NEXT:    .p2align 4
+; CHECK-NEXT:  @ %bb.1:
+; CHECK-NEXT:  .LCPI2_0:
+; CHECK-NEXT:    .long 0 @ 0x0
+; CHECK-NEXT:    .long 2147483648 @ 0x80000000
+; CHECK-NEXT:    .long 0 @ 0x0
+; CHECK-NEXT:    .long 2147483648 @ 0x80000000
 entry:
   %0 = fsub nnan ninf nsz <2 x double> <double 0.0e0, double 0.0e0>, %src
   ret <2 x double> %0


        


More information about the llvm-commits mailing list