[llvm] [RISCV][GISel] Use a single FEQ for fcmp ord/uno x, x (PR #192022)

Jim Lin via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 14 01:24:27 PDT 2026


https://github.com/tclin914 created https://github.com/llvm/llvm-project/pull/192022

When both operands of an ORD/UNO compare are the same register,
the double-FEQ + AND sequence is redundant: a single FEQ x, x
gives the same result. Addresses the FIXME in selectFCmp.

>From 8bce6d4ad70d1aa2d805c0b38f22e8eb1bdf031f Mon Sep 17 00:00:00 2001
From: Jim Lin <jim at andestech.com>
Date: Tue, 14 Apr 2026 02:11:07 +0000
Subject: [PATCH 1/2] [RISCV][GISel] Add pre-commit tests for fcmp ord/uno x, x

Shows the currently-emitted redundant sequence (two FEQs plus AND)
when both operands of an ORD/UNO compare are the same register.
---
 .../CodeGen/RISCV/GlobalISel/double-fcmp.ll   | 73 +++++++++++++++++++
 .../CodeGen/RISCV/GlobalISel/float-fcmp.ll    | 71 ++++++++++++++++++
 2 files changed, 144 insertions(+)

diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/double-fcmp.ll b/llvm/test/CodeGen/RISCV/GlobalISel/double-fcmp.ll
index 9ec8c32e989b0..ac679cd434d94 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/double-fcmp.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/double-fcmp.ll
@@ -587,3 +587,76 @@ define i32 @fcmp_true(double %a, double %b) nounwind {
   %2 = zext i1 %1 to i32
   ret i32 %2
 }
+
+define i32 @fcmp_ord_same(double %a) nounwind {
+; CHECKIFD-LABEL: fcmp_ord_same:
+; CHECKIFD:       # %bb.0:
+; CHECKIFD-NEXT:    feq.d a0, fa0, fa0
+; CHECKIFD-NEXT:    feq.d a1, fa0, fa0
+; CHECKIFD-NEXT:    and a0, a0, a1
+; CHECKIFD-NEXT:    ret
+;
+; RV32I-LABEL: fcmp_ord_same:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    mv a2, a0
+; RV32I-NEXT:    mv a3, a1
+; RV32I-NEXT:    call __unorddf2
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: fcmp_ord_same:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    mv a1, a0
+; RV64I-NEXT:    call __unorddf2
+; RV64I-NEXT:    sext.w a0, a0
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %1 = fcmp ord double %a, %a
+  %2 = zext i1 %1 to i32
+  ret i32 %2
+}
+
+define i32 @fcmp_uno_same(double %a) nounwind {
+; CHECKIFD-LABEL: fcmp_uno_same:
+; CHECKIFD:       # %bb.0:
+; CHECKIFD-NEXT:    feq.d a0, fa0, fa0
+; CHECKIFD-NEXT:    feq.d a1, fa0, fa0
+; CHECKIFD-NEXT:    and a0, a0, a1
+; CHECKIFD-NEXT:    xori a0, a0, 1
+; CHECKIFD-NEXT:    ret
+;
+; RV32I-LABEL: fcmp_uno_same:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    mv a2, a0
+; RV32I-NEXT:    mv a3, a1
+; RV32I-NEXT:    call __unorddf2
+; RV32I-NEXT:    snez a0, a0
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: fcmp_uno_same:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    mv a1, a0
+; RV64I-NEXT:    call __unorddf2
+; RV64I-NEXT:    sext.w a0, a0
+; RV64I-NEXT:    snez a0, a0
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %1 = fcmp uno double %a, %a
+  %2 = zext i1 %1 to i32
+  ret i32 %2
+}
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/float-fcmp.ll b/llvm/test/CodeGen/RISCV/GlobalISel/float-fcmp.ll
index 380751c907c0d..24e8b0148a39f 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/float-fcmp.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/float-fcmp.ll
@@ -572,3 +572,74 @@ define i32 @fcmp_true(float %a, float %b) nounwind {
   %2 = zext i1 %1 to i32
   ret i32 %2
 }
+
+define i32 @fcmp_ord_same(float %a) nounwind {
+; CHECKIF-LABEL: fcmp_ord_same:
+; CHECKIF:       # %bb.0:
+; CHECKIF-NEXT:    feq.s a0, fa0, fa0
+; CHECKIF-NEXT:    feq.s a1, fa0, fa0
+; CHECKIF-NEXT:    and a0, a0, a1
+; CHECKIF-NEXT:    ret
+;
+; RV32I-LABEL: fcmp_ord_same:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    mv a1, a0
+; RV32I-NEXT:    call __unordsf2
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: fcmp_ord_same:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    mv a1, a0
+; RV64I-NEXT:    call __unordsf2
+; RV64I-NEXT:    sext.w a0, a0
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %1 = fcmp ord float %a, %a
+  %2 = zext i1 %1 to i32
+  ret i32 %2
+}
+
+define i32 @fcmp_uno_same(float %a) nounwind {
+; CHECKIF-LABEL: fcmp_uno_same:
+; CHECKIF:       # %bb.0:
+; CHECKIF-NEXT:    feq.s a0, fa0, fa0
+; CHECKIF-NEXT:    feq.s a1, fa0, fa0
+; CHECKIF-NEXT:    and a0, a0, a1
+; CHECKIF-NEXT:    xori a0, a0, 1
+; CHECKIF-NEXT:    ret
+;
+; RV32I-LABEL: fcmp_uno_same:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    mv a1, a0
+; RV32I-NEXT:    call __unordsf2
+; RV32I-NEXT:    snez a0, a0
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: fcmp_uno_same:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    mv a1, a0
+; RV64I-NEXT:    call __unordsf2
+; RV64I-NEXT:    sext.w a0, a0
+; RV64I-NEXT:    snez a0, a0
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+  %1 = fcmp uno float %a, %a
+  %2 = zext i1 %1 to i32
+  ret i32 %2
+}

>From bcac44d071e0351d9b72851bfd60f88a9e55c2ea Mon Sep 17 00:00:00 2001
From: Jim Lin <jim at andestech.com>
Date: Tue, 14 Apr 2026 02:12:26 +0000
Subject: [PATCH 2/2] [RISCV][GISel] Use a single FEQ for fcmp ord/uno x, x

When both operands of an ORD/UNO compare are the same register,
the double-FEQ + AND sequence is redundant: a single FEQ x, x
gives the same result. Addresses the FIXME in selectFCmp.
---
 .../RISCV/GISel/RISCVInstructionSelector.cpp  | 49 +++++++++++--------
 .../CodeGen/RISCV/GlobalISel/double-fcmp.ll   |  4 --
 .../CodeGen/RISCV/GlobalISel/float-fcmp.ll    |  4 --
 3 files changed, 29 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index 60e65f9db3fc6..cf810d867d9ce 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -1922,29 +1922,38 @@ bool RISCVInstructionSelector::selectFPCompare(MachineInstr &MI) const {
     constrainSelectedInstRegOperands(*Or, TII, TRI, RBI);
   } else if (Pred == CmpInst::FCMP_ORD || Pred == CmpInst::FCMP_UNO) {
     // fcmp ord LHS, RHS => (AND (FEQ LHS, LHS), (FEQ RHS, RHS))
-    // FIXME: If LHS and RHS are the same we can use a single FEQ.
+    // If LHS and RHS are the same, a single FEQ suffices.
     NeedInvert = Pred == CmpInst::FCMP_UNO;
-    Register Cmp1Reg = MRI->createVirtualRegister(&RISCV::GPRRegClass);
-    MachineInstr *Cmp1 =
-        BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
-                TII.get(getFCmpOpcode(CmpInst::FCMP_OEQ, Size)), Cmp1Reg)
-            .addReg(LHS)
-            .addReg(LHS);
-    constrainSelectedInstRegOperands(*Cmp1, TII, TRI, RBI);
-    Register Cmp2Reg = MRI->createVirtualRegister(&RISCV::GPRRegClass);
-    MachineInstr *Cmp2 =
-        BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
-                TII.get(getFCmpOpcode(CmpInst::FCMP_OEQ, Size)), Cmp2Reg)
-            .addReg(RHS)
-            .addReg(RHS);
-    constrainSelectedInstRegOperands(*Cmp2, TII, TRI, RBI);
     if (NeedInvert)
       TmpReg = MRI->createVirtualRegister(&RISCV::GPRRegClass);
-    MachineInstr *And = BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
-                                TII.get(RISCV::AND), TmpReg)
-                            .addReg(Cmp1Reg)
-                            .addReg(Cmp2Reg);
-    constrainSelectedInstRegOperands(*And, TII, TRI, RBI);
+    if (LHS == RHS) {
+      MachineInstr *Cmp =
+          BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
+                  TII.get(getFCmpOpcode(CmpInst::FCMP_OEQ, Size)), TmpReg)
+              .addReg(LHS)
+              .addReg(LHS);
+      constrainSelectedInstRegOperands(*Cmp, TII, TRI, RBI);
+    } else {
+      Register Cmp1Reg = MRI->createVirtualRegister(&RISCV::GPRRegClass);
+      MachineInstr *Cmp1 =
+          BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
+                  TII.get(getFCmpOpcode(CmpInst::FCMP_OEQ, Size)), Cmp1Reg)
+              .addReg(LHS)
+              .addReg(LHS);
+      constrainSelectedInstRegOperands(*Cmp1, TII, TRI, RBI);
+      Register Cmp2Reg = MRI->createVirtualRegister(&RISCV::GPRRegClass);
+      MachineInstr *Cmp2 =
+          BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
+                  TII.get(getFCmpOpcode(CmpInst::FCMP_OEQ, Size)), Cmp2Reg)
+              .addReg(RHS)
+              .addReg(RHS);
+      constrainSelectedInstRegOperands(*Cmp2, TII, TRI, RBI);
+      MachineInstr *And = BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
+                                  TII.get(RISCV::AND), TmpReg)
+                              .addReg(Cmp1Reg)
+                              .addReg(Cmp2Reg);
+      constrainSelectedInstRegOperands(*And, TII, TRI, RBI);
+    }
   } else
     llvm_unreachable("Unhandled predicate");
 
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/double-fcmp.ll b/llvm/test/CodeGen/RISCV/GlobalISel/double-fcmp.ll
index ac679cd434d94..fc264d293578b 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/double-fcmp.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/double-fcmp.ll
@@ -592,8 +592,6 @@ define i32 @fcmp_ord_same(double %a) nounwind {
 ; CHECKIFD-LABEL: fcmp_ord_same:
 ; CHECKIFD:       # %bb.0:
 ; CHECKIFD-NEXT:    feq.d a0, fa0, fa0
-; CHECKIFD-NEXT:    feq.d a1, fa0, fa0
-; CHECKIFD-NEXT:    and a0, a0, a1
 ; CHECKIFD-NEXT:    ret
 ;
 ; RV32I-LABEL: fcmp_ord_same:
@@ -628,8 +626,6 @@ define i32 @fcmp_uno_same(double %a) nounwind {
 ; CHECKIFD-LABEL: fcmp_uno_same:
 ; CHECKIFD:       # %bb.0:
 ; CHECKIFD-NEXT:    feq.d a0, fa0, fa0
-; CHECKIFD-NEXT:    feq.d a1, fa0, fa0
-; CHECKIFD-NEXT:    and a0, a0, a1
 ; CHECKIFD-NEXT:    xori a0, a0, 1
 ; CHECKIFD-NEXT:    ret
 ;
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/float-fcmp.ll b/llvm/test/CodeGen/RISCV/GlobalISel/float-fcmp.ll
index 24e8b0148a39f..825e4134d9e9f 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/float-fcmp.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/float-fcmp.ll
@@ -577,8 +577,6 @@ define i32 @fcmp_ord_same(float %a) nounwind {
 ; CHECKIF-LABEL: fcmp_ord_same:
 ; CHECKIF:       # %bb.0:
 ; CHECKIF-NEXT:    feq.s a0, fa0, fa0
-; CHECKIF-NEXT:    feq.s a1, fa0, fa0
-; CHECKIF-NEXT:    and a0, a0, a1
 ; CHECKIF-NEXT:    ret
 ;
 ; RV32I-LABEL: fcmp_ord_same:
@@ -612,8 +610,6 @@ define i32 @fcmp_uno_same(float %a) nounwind {
 ; CHECKIF-LABEL: fcmp_uno_same:
 ; CHECKIF:       # %bb.0:
 ; CHECKIF-NEXT:    feq.s a0, fa0, fa0
-; CHECKIF-NEXT:    feq.s a1, fa0, fa0
-; CHECKIF-NEXT:    and a0, a0, a1
 ; CHECKIF-NEXT:    xori a0, a0, 1
 ; CHECKIF-NEXT:    ret
 ;



More information about the llvm-commits mailing list