[llvm] [GlobalISel] Translate `fcmp {true, false}` to constant values (PR #218780)

Afonso Bordado via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 25 13:46:06 PDT 2026


https://github.com/afonso360 created https://github.com/llvm/llvm-project/pull/218780

👋 Hey,

RISC-V does not have floating point instructions as part of its base ISA. Instead it relies on expanding these instructions into libcalls during instruction selection.

It looks like there are no such libcalls for `fcmp {true,false}`, so instead of implementing it, or expanding these instructions, we can avoid such IR translations in the first place by replacing `fcmp {true,false}` with an appropriate constant.

There are two places in IRTranslation that insert `fcmp`'s and one of them already performs this "optimization", so this commit mostly ensures that both places go through a single unifying path.

This avoids a crash in the included testcase for RISC-V when compiled with O0 ([Godbolt](https://godbolt.org/z/xa18Wd3Gn)). The testcase was found by running the isel fuzzer, and then minimized.

---

I don't know if this way of fixing the issue is preferable to just letting the backend expand these operations, please let me know, and I'm happy to open a different PR.

>From c1bb76dacf3138a6db57a7823e73319a853b6d22 Mon Sep 17 00:00:00 2001
From: Afonso Bordado <afonsobordado at az8.co>
Date: Tue, 25 Aug 2026 19:31:32 +0100
Subject: [PATCH] [GlobalISel] Translate `fcmp {true,false}` to constant values

RISC-V does not have floating point instructions as part of its base ISA.
Instead it relies on expanding these instructions into libcalls during
instruction selection.

It looks like there are no such libcalls for `fcmp {true,false}`, so instead
of implementing it, or expanding these instructions, we can avoid such IR
translations in the first place by replacing `fcmp {true,false}` with
an appropriate constant.

There are two places in IRTranslation that insert `fcmp`'s and one of them
already performs this "optimization", so this commit mostly ensures that both
places go through a single unifying path.

This avoids a crash in the included testcase for RISC-V when compiled with O0.
---
 llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp  |  34 ++++--
 .../RISCV/GlobalISel/float-condbr-fcmp.ll     | 107 ++++++++++++++++++
 2 files changed, 133 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/float-condbr-fcmp.ll

diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index baac98561f25b..dfdec383105a4 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -474,6 +474,11 @@ class IRTranslatorImpl {
                            MachineBasicBlock *DefaultMBB,
                            MachineIRBuilder &MIB);
 
+  MachineInstrBuilder
+  buildFCmpOrConstant(MachineIRBuilder &MIB, CmpInst::Predicate Pred,
+                      const DstOp &Res, const SrcOp &Op0, const SrcOp &Op1,
+                      std::optional<unsigned> Flags = std::nullopt);
+
   bool translateSwitch(const User &U, MachineIRBuilder &MIRBuilder);
   // End switch lowering section.
 
@@ -1111,6 +1116,24 @@ bool IRTranslatorImpl::translateFNeg(const User &U,
   return translateUnaryOp(TargetOpcode::G_FNEG, U, MIRBuilder);
 }
 
+MachineInstrBuilder IRTranslatorImpl::buildFCmpOrConstant(
+    MachineIRBuilder &MIB, CmpInst::Predicate Pred, const DstOp &Res,
+    const SrcOp &Op0, const SrcOp &Op1, std::optional<unsigned> Flags) {
+
+  if (Pred != CmpInst::FCMP_FALSE && Pred != CmpInst::FCMP_TRUE)
+    return MIB.buildFCmp(Pred, Res, Op0, Op1, Flags);
+
+  // For FCMP_FALSE and FCMP_TRUE, lower it to a constant value.
+  LLT ResTy = Res.getLLTTy(*MIB.getMRI());
+  Type *ResIRTy = getTypeForLLT(ResTy, MIB.getContext());
+
+  Constant *C = Pred == CmpInst::FCMP_FALSE
+                    ? Constant::getNullValue(ResIRTy)
+                    : Constant::getAllOnesValue(ResIRTy);
+
+  return MIB.buildCopy(Res, getOrCreateVReg(*C));
+}
+
 bool IRTranslatorImpl::translateCompare(const User &U,
                                         MachineIRBuilder &MIRBuilder) {
   if (!mayTranslateUserTypes(U))
@@ -1124,14 +1147,8 @@ bool IRTranslatorImpl::translateCompare(const User &U,
   uint32_t Flags = MachineInstr::copyFlagsFromInstruction(*CI);
   if (CmpInst::isIntPredicate(Pred))
     MIRBuilder.buildICmp(Pred, Res, Op0, Op1, Flags);
-  else if (Pred == CmpInst::FCMP_FALSE)
-    MIRBuilder.buildCopy(
-        Res, getOrCreateVReg(*Constant::getNullValue(U.getType())));
-  else if (Pred == CmpInst::FCMP_TRUE)
-    MIRBuilder.buildCopy(
-        Res, getOrCreateVReg(*Constant::getAllOnesValue(U.getType())));
   else
-    MIRBuilder.buildFCmp(Pred, Res, Op0, Op1, Flags);
+    buildFCmpOrConstant(MIRBuilder, Pred, Res, Op0, Op1, Flags);
 
   return true;
 }
@@ -1713,7 +1730,8 @@ void IRTranslatorImpl::emitSwitchCase(SwitchCG::CaseBlock &CB,
       Register CondRHS = getOrCreateVReg(*CB.CmpRHS);
       if (CmpInst::isFPPredicate(CB.PredInfo.Pred))
         Cond =
-            MIB.buildFCmp(CB.PredInfo.Pred, i1Ty, CondLHS, CondRHS).getReg(0);
+            buildFCmpOrConstant(MIB, CB.PredInfo.Pred, i1Ty, CondLHS, CondRHS)
+                .getReg(0);
       else
         Cond =
             MIB.buildICmp(CB.PredInfo.Pred, i1Ty, CondLHS, CondRHS).getReg(0);
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/float-condbr-fcmp.ll b/llvm/test/CodeGen/RISCV/GlobalISel/float-condbr-fcmp.ll
new file mode 100644
index 0000000000000..d8bffc4708541
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/float-condbr-fcmp.ll
@@ -0,0 +1,107 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -global-isel -global-isel-abort=1 -O0 < %s \
+; RUN:   | FileCheck -check-prefix=RV32I %s
+; RUN: llc -mtriple=riscv64 -global-isel -global-isel-abort=1 -O0 < %s \
+; RUN:   | FileCheck -check-prefix=RV64I %s
+
+
+define i64 @and_fcmp_true() {
+; RV32I-LABEL: and_fcmp_true:
+; RV32I:       # %bb.0: # %BB
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    .cfi_def_cfa_offset 16
+; RV32I-NEXT:    li a0, 1
+; RV32I-NEXT:    sw a0, 8(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    li a0, 0
+; RV32I-NEXT:    li a1, 0
+; RV32I-NEXT:    sw a1, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    bnez a0, .LBB0_1
+; RV32I-NEXT:    j .LBB0_2
+; RV32I-NEXT:  .LBB0_1: # %BB
+; RV32I-NEXT:    lw a0, 8(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    bnez a0, .LBB0_2
+; RV32I-NEXT:    j .LBB0_2
+; RV32I-NEXT:  .LBB0_2: # %BB1
+; RV32I-NEXT:    lw a1, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    mv a0, a1
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    .cfi_def_cfa_offset 0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: and_fcmp_true:
+; RV64I:       # %bb.0: # %BB
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    .cfi_def_cfa_offset 16
+; RV64I-NEXT:    li a0, 1
+; RV64I-NEXT:    sd a0, 0(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    li a0, 0
+; RV64I-NEXT:    li a1, 0
+; RV64I-NEXT:    sd a1, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    bnez a0, .LBB0_1
+; RV64I-NEXT:    j .LBB0_2
+; RV64I-NEXT:  .LBB0_1: # %BB
+; RV64I-NEXT:    ld a0, 0(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    bnez a0, .LBB0_2
+; RV64I-NEXT:    j .LBB0_2
+; RV64I-NEXT:  .LBB0_2: # %BB1
+; RV64I-NEXT:    ld a0, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    .cfi_def_cfa_offset 0
+; RV64I-NEXT:    ret
+BB:
+  %C1 = fcmp true float 0.000000e+00, 0.000000e+00
+  %B1 = and i1 false, %C1
+  br i1 %B1, label %BB1, label %BB1
+BB1:
+  ret i64 0
+}
+
+
+define i64 @and_fcmp_false() {
+; RV32I-LABEL: and_fcmp_false:
+; RV32I:       # %bb.0: # %BB
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    .cfi_def_cfa_offset 16
+; RV32I-NEXT:    li a0, 0
+; RV32I-NEXT:    sw a0, 8(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    li a1, 0
+; RV32I-NEXT:    sw a1, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    bnez a0, .LBB1_1
+; RV32I-NEXT:    j .LBB1_2
+; RV32I-NEXT:  .LBB1_1: # %BB
+; RV32I-NEXT:    lw a0, 8(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    bnez a0, .LBB1_2
+; RV32I-NEXT:    j .LBB1_2
+; RV32I-NEXT:  .LBB1_2: # %BB1
+; RV32I-NEXT:    lw a1, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    mv a0, a1
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    .cfi_def_cfa_offset 0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: and_fcmp_false:
+; RV64I:       # %bb.0: # %BB
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    .cfi_def_cfa_offset 16
+; RV64I-NEXT:    li a0, 0
+; RV64I-NEXT:    sd a0, 0(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    li a1, 0
+; RV64I-NEXT:    sd a1, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    bnez a0, .LBB1_1
+; RV64I-NEXT:    j .LBB1_2
+; RV64I-NEXT:  .LBB1_1: # %BB
+; RV64I-NEXT:    ld a0, 0(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    bnez a0, .LBB1_2
+; RV64I-NEXT:    j .LBB1_2
+; RV64I-NEXT:  .LBB1_2: # %BB1
+; RV64I-NEXT:    ld a0, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    .cfi_def_cfa_offset 0
+; RV64I-NEXT:    ret
+BB:
+  %C1 = fcmp false float 0.000000e+00, 0.000000e+00
+  %B1 = and i1 false, %C1
+  br i1 %B1, label %BB1, label %BB1
+BB1:
+  ret i64 0
+}



More information about the llvm-commits mailing list