[llvm] [GISel] Extend ConstantFoldFPBinOp with fminimumnum, fmaximumnum (PR #190561)

Tim Gymnich via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 5 16:53:16 PDT 2026


https://github.com/tgymnich updated https://github.com/llvm/llvm-project/pull/190561

>From bfedb6fbf8d1d642d30f6e347875de1f4725fe23 Mon Sep 17 00:00:00 2001
From: Tim Gymnich <tim at gymni.ch>
Date: Mon, 6 Apr 2026 01:22:32 +0200
Subject: [PATCH] [GISel] Fix and extend ConstantFoldFPBinOp

- Distinguish G_FREM (IEEE remainder) from G_FMODF (modf), using
  APFloat::remainder() for G_FREM. Remove the bogus G_FMODF case from
  ConstantFoldFPBinOp: G_FMODF is a unary op (one input, two outputs)
  so it can never match the two-operand lookup in this function.
- Add constant folding for G_FMINIMUMNUM and G_FMAXIMUMNUM using the
  minimumnum/maximumnum APFloat helpers.
- Extend the constant_fold_fp_binop combiner rule to cover all opcodes
  that ConstantFoldFPBinOp actually folds: G_FREM, G_FCOPYSIGN,
  G_FMINNUM, G_FMAXNUM, G_FMINIMUM, G_FMAXIMUM, G_FMINIMUMNUM,
  G_FMAXIMUMNUM. Previously only G_FADD/FSUB/FMUL/FDIV were wired up.
- Add MIR tests for all newly folded opcodes in
  postlegalizer-combiner-constant-fold.mir, and update
  AArch64/select_const.ll for the changed frem codegen.
---
 .../include/llvm/Target/GlobalISel/Combine.td |   5 +-
 llvm/lib/CodeGen/GlobalISel/Utils.cpp         |   6 +-
 .../postlegalizer-combiner-constant-fold.mir  | 181 ++++++++++++++++++
 llvm/test/CodeGen/AArch64/select_const.ll     |   9 +-
 4 files changed, 195 insertions(+), 6 deletions(-)

diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 5e99049c32734..ef8c5385c719c 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -1365,7 +1365,10 @@ def constant_fold_binop : GICombineRule<
 
 def constant_fold_fp_binop : GICombineRule<
   (defs root:$d, constantfp_matchinfo:$matchinfo),
-  (match (wip_match_opcode G_FADD, G_FSUB, G_FMUL, G_FDIV):$d,
+  (match (wip_match_opcode G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FREM,
+                           G_FCOPYSIGN, G_FMINNUM, G_FMAXNUM,
+                           G_FMINIMUM, G_FMAXIMUM,
+                           G_FMINIMUMNUM, G_FMAXIMUMNUM):$d,
    [{ return Helper.matchConstantFoldFPBinOp(*${d}, ${matchinfo}); }]),
   (apply [{ Helper.replaceInstWithFConstant(*${d}, ${matchinfo}); }])>;
 
diff --git a/llvm/lib/CodeGen/GlobalISel/Utils.cpp b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
index d019633369163..621b554a525f4 100644
--- a/llvm/lib/CodeGen/GlobalISel/Utils.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
@@ -763,7 +763,7 @@ llvm::ConstantFoldFPBinOp(unsigned Opcode, const Register Op1,
     C1.divide(C2, APFloat::rmNearestTiesToEven);
     return C1;
   case TargetOpcode::G_FREM:
-    C1.mod(C2);
+    C1.remainder(C2);
     return C1;
   case TargetOpcode::G_FCOPYSIGN:
     C1.copySign(C2);
@@ -776,6 +776,10 @@ llvm::ConstantFoldFPBinOp(unsigned Opcode, const Register Op1,
     return minimum(C1, C2);
   case TargetOpcode::G_FMAXIMUM:
     return maximum(C1, C2);
+  case TargetOpcode::G_FMINIMUMNUM:
+    return minimumnum(C1, C2);
+  case TargetOpcode::G_FMAXIMUMNUM:
+    return maximumnum(C1, C2);
   case TargetOpcode::G_FMINNUM_IEEE:
   case TargetOpcode::G_FMAXNUM_IEEE:
     // FIXME: These operations were unfortunately named. fminnum/fmaxnum do not
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/postlegalizer-combiner-constant-fold.mir b/llvm/test/CodeGen/AArch64/GlobalISel/postlegalizer-combiner-constant-fold.mir
index d600bff8e08a9..eef42b9241373 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/postlegalizer-combiner-constant-fold.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/postlegalizer-combiner-constant-fold.mir
@@ -226,6 +226,187 @@ body:             |
     $d0 = COPY %res(s64)
     RET_ReallyLR implicit $d0
 
+...
+---
+name:            frem
+legalized:       true
+liveins:
+  - { reg: '$d0' }
+body:             |
+  bb.1.entry:
+    liveins: $d0
+
+    ; CHECK-LABEL: name: frem
+    ; CHECK: liveins: $d0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %res:_(s64) = G_FCONSTANT double -1.000000e+00
+    ; CHECK-NEXT: $d0 = COPY %res(s64)
+    ; CHECK-NEXT: RET_ReallyLR implicit $d0
+    %a:_(s64) = G_FCONSTANT double 7.0
+    %b:_(s64) = G_FCONSTANT double 2.0
+    %res:_(s64) = G_FREM %a, %b
+    $d0 = COPY %res(s64)
+    RET_ReallyLR implicit $d0
+
+...
+---
+name:            fminimumnum
+legalized:       true
+liveins:
+  - { reg: '$d0' }
+body:             |
+  bb.1.entry:
+    liveins: $d0
+
+    ; CHECK-LABEL: name: fminimumnum
+    ; CHECK: liveins: $d0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %a:_(s64) = G_FCONSTANT double 2.000000e+00
+    ; CHECK-NEXT: $d0 = COPY %a(s64)
+    ; CHECK-NEXT: RET_ReallyLR implicit $d0
+    %a:_(s64) = G_FCONSTANT double 2.0
+    %b:_(s64) = G_FCONSTANT double 5.0
+    %res:_(s64) = G_FMINIMUMNUM %a, %b
+    $d0 = COPY %res(s64)
+    RET_ReallyLR implicit $d0
+
+...
+---
+name:            fmaximumnum
+legalized:       true
+liveins:
+  - { reg: '$d0' }
+body:             |
+  bb.1.entry:
+    liveins: $d0
+
+    ; CHECK-LABEL: name: fmaximumnum
+    ; CHECK: liveins: $d0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %b:_(s64) = G_FCONSTANT double 5.000000e+00
+    ; CHECK-NEXT: $d0 = COPY %b(s64)
+    ; CHECK-NEXT: RET_ReallyLR implicit $d0
+    %a:_(s64) = G_FCONSTANT double 2.0
+    %b:_(s64) = G_FCONSTANT double 5.0
+    %res:_(s64) = G_FMAXIMUMNUM %a, %b
+    $d0 = COPY %res(s64)
+    RET_ReallyLR implicit $d0
+
+...
+---
+name:            fcopysign
+legalized:       true
+liveins:
+  - { reg: '$d0' }
+body:             |
+  bb.1.entry:
+    liveins: $d0
+
+    ; fcopysign(3.0, -1.0) = -3.0
+    ; CHECK-LABEL: name: fcopysign
+    ; CHECK: liveins: $d0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %res:_(s64) = G_FCONSTANT double -3.000000e+00
+    ; CHECK-NEXT: $d0 = COPY %res(s64)
+    ; CHECK-NEXT: RET_ReallyLR implicit $d0
+    %a:_(s64) = G_FCONSTANT double 3.0
+    %b:_(s64) = G_FCONSTANT double -1.0
+    %res:_(s64) = G_FCOPYSIGN %a, %b
+    $d0 = COPY %res(s64)
+    RET_ReallyLR implicit $d0
+
+...
+---
+name:            fminnum
+legalized:       true
+liveins:
+  - { reg: '$d0' }
+body:             |
+  bb.1.entry:
+    liveins: $d0
+
+    ; fminnum(2.0, 5.0) = 2.0
+    ; CHECK-LABEL: name: fminnum
+    ; CHECK: liveins: $d0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %a:_(s64) = G_FCONSTANT double 2.000000e+00
+    ; CHECK-NEXT: $d0 = COPY %a(s64)
+    ; CHECK-NEXT: RET_ReallyLR implicit $d0
+    %a:_(s64) = G_FCONSTANT double 2.0
+    %b:_(s64) = G_FCONSTANT double 5.0
+    %res:_(s64) = G_FMINNUM %a, %b
+    $d0 = COPY %res(s64)
+    RET_ReallyLR implicit $d0
+
+...
+---
+name:            fmaxnum
+legalized:       true
+liveins:
+  - { reg: '$d0' }
+body:             |
+  bb.1.entry:
+    liveins: $d0
+
+    ; fmaxnum(2.0, 5.0) = 5.0
+    ; CHECK-LABEL: name: fmaxnum
+    ; CHECK: liveins: $d0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %b:_(s64) = G_FCONSTANT double 5.000000e+00
+    ; CHECK-NEXT: $d0 = COPY %b(s64)
+    ; CHECK-NEXT: RET_ReallyLR implicit $d0
+    %a:_(s64) = G_FCONSTANT double 2.0
+    %b:_(s64) = G_FCONSTANT double 5.0
+    %res:_(s64) = G_FMAXNUM %a, %b
+    $d0 = COPY %res(s64)
+    RET_ReallyLR implicit $d0
+
+...
+---
+name:            fminimum
+legalized:       true
+liveins:
+  - { reg: '$d0' }
+body:             |
+  bb.1.entry:
+    liveins: $d0
+
+    ; fminimum(2.0, 5.0) = 2.0
+    ; CHECK-LABEL: name: fminimum
+    ; CHECK: liveins: $d0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %a:_(s64) = G_FCONSTANT double 2.000000e+00
+    ; CHECK-NEXT: $d0 = COPY %a(s64)
+    ; CHECK-NEXT: RET_ReallyLR implicit $d0
+    %a:_(s64) = G_FCONSTANT double 2.0
+    %b:_(s64) = G_FCONSTANT double 5.0
+    %res:_(s64) = G_FMINIMUM %a, %b
+    $d0 = COPY %res(s64)
+    RET_ReallyLR implicit $d0
+
+...
+---
+name:            fmaximum
+legalized:       true
+liveins:
+  - { reg: '$d0' }
+body:             |
+  bb.1.entry:
+    liveins: $d0
+
+    ; fmaximum(2.0, 5.0) = 5.0
+    ; CHECK-LABEL: name: fmaximum
+    ; CHECK: liveins: $d0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %b:_(s64) = G_FCONSTANT double 5.000000e+00
+    ; CHECK-NEXT: $d0 = COPY %b(s64)
+    ; CHECK-NEXT: RET_ReallyLR implicit $d0
+    %a:_(s64) = G_FCONSTANT double 2.0
+    %b:_(s64) = G_FCONSTANT double 5.0
+    %res:_(s64) = G_FMAXIMUM %a, %b
+    $d0 = COPY %res(s64)
+    RET_ReallyLR implicit $d0
+
 ...
 ---
 name:            fadd32
diff --git a/llvm/test/CodeGen/AArch64/select_const.ll b/llvm/test/CodeGen/AArch64/select_const.ll
index 0a73aed803415..48616d6af96d2 100644
--- a/llvm/test/CodeGen/AArch64/select_const.ll
+++ b/llvm/test/CodeGen/AArch64/select_const.ll
@@ -933,12 +933,13 @@ define double @sel_constants_frem_constant(i1 %cond) {
 ;
 ; CHECK-GI-LABEL: sel_constants_frem_constant:
 ; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    adrp x8, .LCPI48_0
-; CHECK-GI-NEXT:    fmov d0, #-4.00000000
-; CHECK-GI-NEXT:    ldr d1, [x8, :lo12:.LCPI48_0]
+; CHECK-GI-NEXT:    adrp x8, .LCPI48_1
+; CHECK-GI-NEXT:    adrp x9, .LCPI48_0
+; CHECK-GI-NEXT:    ldr d0, [x8, :lo12:.LCPI48_1]
+; CHECK-GI-NEXT:    ldr d1, [x9, :lo12:.LCPI48_0]
 ; CHECK-GI-NEXT:    and w8, w0, #0x1
 ; CHECK-GI-NEXT:    tst w8, #0x1
-; CHECK-GI-NEXT:    fcsel d0, d0, d1, ne
+; CHECK-GI-NEXT:    fcsel d0, d1, d0, ne
 ; CHECK-GI-NEXT:    ret
   %sel = select i1 %cond, double -4.0, double 23.3
   %bo = frem double %sel, 5.1



More information about the llvm-commits mailing list