Skip to content

Conversation

@sunshaoce
Copy link
Contributor

@sunshaoce sunshaoce commented Aug 16, 2025

Resolve the TODO: on RV32, when constructing the double-precision constant +0.0 for s64, BuildPairF64Pseudo can be optimized to use the fcvt.d.w instruction to generate the result directly.

@llvmbot
Copy link
Member

llvmbot commented Aug 16, 2025

@llvm/pr-subscribers-llvm-globalisel

@llvm/pr-subscribers-backend-risc-v

Author: Shaoce SUN (sunshaoce)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/153978.diff

3 Files Affected:

  • (modified) llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp (+14-1)
  • (modified) llvm/test/CodeGen/RISCV/GlobalISel/double-arith.ll (+8-40)
  • (modified) llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/fp-constant.mir (+2-4)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index f83c2b6da8923..51ea3fc5f6774 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -736,7 +736,6 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
   }
   case TargetOpcode::G_FCONSTANT: {
     // TODO: Use constant pool for complex constants.
-    // TODO: Optimize +0.0 to use fcvt.d.w for s64 on rv32.
     Register DstReg = MI.getOperand(0).getReg();
     const APFloat &FPimm = MI.getOperand(1).getFPImm()->getValueAPF();
     APInt Imm = FPimm.bitcastToAPInt();
@@ -753,8 +752,22 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
       if (!FMV.constrainAllUses(TII, TRI, RBI))
         return false;
     } else {
+      // s64 on rv32
       assert(Size == 64 && !Subtarget->is64Bit() &&
              "Unexpected size or subtarget");
+
+      if (Imm.isNonNegative() && Imm.isZero()) {
+        // Optimize +0.0 to use fcvt.d.w
+        MachineInstrBuilder FCVT =
+            MIB.buildInstr(RISCV::FCVT_D_W, {DstReg}, {Register(RISCV::X0)})
+                .addImm(RISCVFPRndMode::RNE);
+        if (!FCVT.constrainAllUses(TII, TRI, RBI))
+          return false;
+
+        MI.eraseFromParent();
+        return true;
+      }
+
       // Split into two pieces and build through the stack.
       Register GPRRegHigh = MRI->createVirtualRegister(&RISCV::GPRRegClass);
       Register GPRRegLow = MRI->createVirtualRegister(&RISCV::GPRRegClass);
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/double-arith.ll b/llvm/test/CodeGen/RISCV/GlobalISel/double-arith.ll
index cb2037f5fb027..4eb7646d13a39 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/double-arith.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/double-arith.ll
@@ -395,13 +395,9 @@ define double @fmadd_d(double %a, double %b, double %c) nounwind {
 define double @fmsub_d(double %a, double %b, double %c) nounwind {
 ; RV32IFD-LABEL: fmsub_d:
 ; RV32IFD:       # %bb.0:
-; RV32IFD-NEXT:    addi sp, sp, -16
-; RV32IFD-NEXT:    sw zero, 8(sp)
-; RV32IFD-NEXT:    sw zero, 12(sp)
-; RV32IFD-NEXT:    fld fa5, 8(sp)
+; RV32IFD-NEXT:    fcvt.d.w fa5, zero
 ; RV32IFD-NEXT:    fadd.d fa5, fa2, fa5
 ; RV32IFD-NEXT:    fmsub.d fa0, fa0, fa1, fa5
-; RV32IFD-NEXT:    addi sp, sp, 16
 ; RV32IFD-NEXT:    ret
 ;
 ; RV64IFD-LABEL: fmsub_d:
@@ -478,14 +474,10 @@ define double @fmsub_d(double %a, double %b, double %c) nounwind {
 define double @fnmadd_d(double %a, double %b, double %c) nounwind {
 ; RV32IFD-LABEL: fnmadd_d:
 ; RV32IFD:       # %bb.0:
-; RV32IFD-NEXT:    addi sp, sp, -16
-; RV32IFD-NEXT:    sw zero, 8(sp)
-; RV32IFD-NEXT:    sw zero, 12(sp)
-; RV32IFD-NEXT:    fld fa5, 8(sp)
+; RV32IFD-NEXT:    fcvt.d.w fa5, zero
 ; RV32IFD-NEXT:    fadd.d fa4, fa0, fa5
 ; RV32IFD-NEXT:    fadd.d fa5, fa2, fa5
 ; RV32IFD-NEXT:    fnmadd.d fa0, fa4, fa1, fa5
-; RV32IFD-NEXT:    addi sp, sp, 16
 ; RV32IFD-NEXT:    ret
 ;
 ; RV64IFD-LABEL: fnmadd_d:
@@ -590,14 +582,10 @@ define double @fnmadd_d(double %a, double %b, double %c) nounwind {
 define double @fnmadd_d_2(double %a, double %b, double %c) nounwind {
 ; RV32IFD-LABEL: fnmadd_d_2:
 ; RV32IFD:       # %bb.0:
-; RV32IFD-NEXT:    addi sp, sp, -16
-; RV32IFD-NEXT:    sw zero, 8(sp)
-; RV32IFD-NEXT:    sw zero, 12(sp)
-; RV32IFD-NEXT:    fld fa5, 8(sp)
+; RV32IFD-NEXT:    fcvt.d.w fa5, zero
 ; RV32IFD-NEXT:    fadd.d fa4, fa1, fa5
 ; RV32IFD-NEXT:    fadd.d fa5, fa2, fa5
 ; RV32IFD-NEXT:    fnmadd.d fa0, fa4, fa0, fa5
-; RV32IFD-NEXT:    addi sp, sp, 16
 ; RV32IFD-NEXT:    ret
 ;
 ; RV64IFD-LABEL: fnmadd_d_2:
@@ -772,13 +760,9 @@ define double @fnmadd_nsz(double %a, double %b, double %c) nounwind {
 define double @fnmsub_d(double %a, double %b, double %c) nounwind {
 ; RV32IFD-LABEL: fnmsub_d:
 ; RV32IFD:       # %bb.0:
-; RV32IFD-NEXT:    addi sp, sp, -16
-; RV32IFD-NEXT:    sw zero, 8(sp)
-; RV32IFD-NEXT:    sw zero, 12(sp)
-; RV32IFD-NEXT:    fld fa5, 8(sp)
+; RV32IFD-NEXT:    fcvt.d.w fa5, zero
 ; RV32IFD-NEXT:    fadd.d fa5, fa0, fa5
 ; RV32IFD-NEXT:    fnmsub.d fa0, fa5, fa1, fa2
-; RV32IFD-NEXT:    addi sp, sp, 16
 ; RV32IFD-NEXT:    ret
 ;
 ; RV64IFD-LABEL: fnmsub_d:
@@ -851,13 +835,9 @@ define double @fnmsub_d(double %a, double %b, double %c) nounwind {
 define double @fnmsub_d_2(double %a, double %b, double %c) nounwind {
 ; RV32IFD-LABEL: fnmsub_d_2:
 ; RV32IFD:       # %bb.0:
-; RV32IFD-NEXT:    addi sp, sp, -16
-; RV32IFD-NEXT:    sw zero, 8(sp)
-; RV32IFD-NEXT:    sw zero, 12(sp)
-; RV32IFD-NEXT:    fld fa5, 8(sp)
+; RV32IFD-NEXT:    fcvt.d.w fa5, zero
 ; RV32IFD-NEXT:    fadd.d fa5, fa1, fa5
 ; RV32IFD-NEXT:    fnmsub.d fa0, fa5, fa0, fa2
-; RV32IFD-NEXT:    addi sp, sp, 16
 ; RV32IFD-NEXT:    ret
 ;
 ; RV64IFD-LABEL: fnmsub_d_2:
@@ -976,14 +956,10 @@ define double @fmadd_d_contract(double %a, double %b, double %c) nounwind {
 define double @fmsub_d_contract(double %a, double %b, double %c) nounwind {
 ; RV32IFD-LABEL: fmsub_d_contract:
 ; RV32IFD:       # %bb.0:
-; RV32IFD-NEXT:    addi sp, sp, -16
-; RV32IFD-NEXT:    sw zero, 8(sp)
-; RV32IFD-NEXT:    sw zero, 12(sp)
-; RV32IFD-NEXT:    fld fa5, 8(sp)
+; RV32IFD-NEXT:    fcvt.d.w fa5, zero
 ; RV32IFD-NEXT:    fadd.d fa5, fa2, fa5
 ; RV32IFD-NEXT:    fmul.d fa4, fa0, fa1
 ; RV32IFD-NEXT:    fsub.d fa0, fa4, fa5
-; RV32IFD-NEXT:    addi sp, sp, 16
 ; RV32IFD-NEXT:    ret
 ;
 ; RV64IFD-LABEL: fmsub_d_contract:
@@ -1069,17 +1045,13 @@ define double @fmsub_d_contract(double %a, double %b, double %c) nounwind {
 define double @fnmadd_d_contract(double %a, double %b, double %c) nounwind {
 ; RV32IFD-LABEL: fnmadd_d_contract:
 ; RV32IFD:       # %bb.0:
-; RV32IFD-NEXT:    addi sp, sp, -16
-; RV32IFD-NEXT:    sw zero, 8(sp)
-; RV32IFD-NEXT:    sw zero, 12(sp)
-; RV32IFD-NEXT:    fld fa5, 8(sp)
+; RV32IFD-NEXT:    fcvt.d.w fa5, zero
 ; RV32IFD-NEXT:    fadd.d fa4, fa0, fa5
 ; RV32IFD-NEXT:    fadd.d fa3, fa1, fa5
 ; RV32IFD-NEXT:    fadd.d fa5, fa2, fa5
 ; RV32IFD-NEXT:    fmul.d fa4, fa4, fa3
 ; RV32IFD-NEXT:    fneg.d fa4, fa4
 ; RV32IFD-NEXT:    fsub.d fa0, fa4, fa5
-; RV32IFD-NEXT:    addi sp, sp, 16
 ; RV32IFD-NEXT:    ret
 ;
 ; RV64IFD-LABEL: fnmadd_d_contract:
@@ -1204,14 +1176,10 @@ define double @fnmadd_d_contract(double %a, double %b, double %c) nounwind {
 define double @fnmsub_d_contract(double %a, double %b, double %c) nounwind {
 ; RV32IFD-LABEL: fnmsub_d_contract:
 ; RV32IFD:       # %bb.0:
-; RV32IFD-NEXT:    addi sp, sp, -16
-; RV32IFD-NEXT:    sw zero, 8(sp)
-; RV32IFD-NEXT:    sw zero, 12(sp)
-; RV32IFD-NEXT:    fld fa5, 8(sp)
+; RV32IFD-NEXT:    fcvt.d.w fa5, zero
 ; RV32IFD-NEXT:    fadd.d fa4, fa0, fa5
 ; RV32IFD-NEXT:    fadd.d fa5, fa1, fa5
 ; RV32IFD-NEXT:    fnmsub.d fa0, fa4, fa5, fa2
-; RV32IFD-NEXT:    addi sp, sp, 16
 ; RV32IFD-NEXT:    ret
 ;
 ; RV64IFD-LABEL: fnmsub_d_contract:
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/fp-constant.mir b/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/fp-constant.mir
index e82d4bcec48b1..4db80c6c1141f 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/fp-constant.mir
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/fp-constant.mir
@@ -164,10 +164,8 @@ body:             |
     ; RV32-LABEL: name: double_positive_zero
     ; RV32: liveins: $x10
     ; RV32-NEXT: {{  $}}
-    ; RV32-NEXT: [[COPY:%[0-9]+]]:gpr = COPY $x0
-    ; RV32-NEXT: [[COPY1:%[0-9]+]]:gpr = COPY $x0
-    ; RV32-NEXT: [[BuildPairF64Pseudo:%[0-9]+]]:fpr64 = BuildPairF64Pseudo [[COPY1]], [[COPY]]
-    ; RV32-NEXT: $f10_d = COPY [[BuildPairF64Pseudo]]
+    ; RV32-NEXT: [[FCVT_D_W:%[0-9]+]]:fpr64 = FCVT_D_W $x0, 0
+    ; RV32-NEXT: $f10_d = COPY [[FCVT_D_W]]
     ; RV32-NEXT: PseudoRET implicit $f10_d
     ;
     ; RV64-LABEL: name: double_positive_zero

Copy link
Collaborator

@topperc topperc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@sunshaoce sunshaoce enabled auto-merge (squash) August 18, 2025 17:25
@sunshaoce sunshaoce merged commit 7e8ff2a into llvm:main Aug 18, 2025
9 checks passed
@sunshaoce sunshaoce deleted the pr-pos-zero branch August 18, 2025 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants