]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
LoongArch: Implement scalar isinf, isnormal, and isfinite via fclass
authorXi Ruoyao <xry111@xry111.site>
Wed, 3 Jul 2024 18:49:28 +0000 (02:49 +0800)
committerXi Ruoyao <xry111@xry111.site>
Thu, 15 Aug 2024 11:38:44 +0000 (19:38 +0800)
Doing so can avoid loading FP constants from the memory.  It also
partially fixes PR 66262 as fclass does not signal on sNaN.

gcc/ChangeLog:

* config/loongarch/loongarch.md (extendsidi2): Add ("=r", "f")
alternative and use movfr2gr.s for it.  The spec clearly states
movfr2gr.s sign extends the value to GRLEN.
(fclass_<fmt>): Make the result SImode instead of a floating
mode.  The fclass results are really not FP values.
(FCLASS_MASK): New define_int_iterator.
(fclass_optab): New define_int_attr.
(<FCLASS_MASK:fclass_optab><ANYF:mode>): New define_expand
template.

gcc/testsuite/ChangeLog:

* gcc.target/loongarch/fclass-compile.c: New test.
* gcc.target/loongarch/fclass-run.c: New test.

gcc/config/loongarch/loongarch.md
gcc/testsuite/gcc.target/loongarch/fclass-compile.c [new file with mode: 0644]
gcc/testsuite/gcc.target/loongarch/fclass-run.c [new file with mode: 0644]

index 73cdb38a4061df987492cc49ae5bd3c64500c3dd..f70ca85bfb386ad1a30f3241d94bcfd761e87af6 100644 (file)
 ;;  ....................
 
 (define_insn "extendsidi2"
-  [(set (match_operand:DI 0 "register_operand" "=r,r,r,r")
+  [(set (match_operand:DI 0 "register_operand" "=r,r,r,r,r")
        (sign_extend:DI
-           (match_operand:SI 1 "nonimmediate_operand" "r,ZC,m,k")))]
+           (match_operand:SI 1 "nonimmediate_operand" "r,ZC,m,k,f")))]
   "TARGET_64BIT"
   "@
    slli.w\t%0,%1,0
    ldptr.w\t%0,%1
    ld.w\t%0,%1
-   ldx.w\t%0,%1"
-  [(set_attr "move_type" "sll0,load,load,load")
+   ldx.w\t%0,%1
+   movfr2gr.s\t%0,%1"
+  [(set_attr "move_type" "sll0,load,load,load,mftg")
    (set_attr "mode" "DI")])
 
 (define_insn "extend<SHORT:mode><GPR:mode>2"
   "movgr2fcsr\t$r%0,%1")
 
 (define_insn "fclass_<fmt>"
-  [(set (match_operand:ANYF 0 "register_operand" "=f")
-       (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")]
-                     UNSPEC_FCLASS))]
+  [(set (match_operand:SI 0 "register_operand" "=f")
+       (unspec:SI [(match_operand:ANYF 1 "register_operand" "f")]
+                  UNSPEC_FCLASS))]
   "TARGET_HARD_FLOAT"
   "fclass.<fmt>\t%0,%1"
   [(set_attr "type" "unknown")
    (set_attr "mode" "<MODE>")])
 
+(define_int_iterator FCLASS_MASK [68 136 952])
+(define_int_attr fclass_optab
+  [(68 "isinf")
+   (136        "isnormal")
+   (952        "isfinite")])
+
+(define_expand "<FCLASS_MASK:fclass_optab><ANYF:mode>2"
+  [(match_operand:SI   0 "register_operand" "=r")
+   (match_operand:ANYF 1 "register_operand" " f")
+   (const_int FCLASS_MASK)]
+  "TARGET_HARD_FLOAT"
+  {
+    rtx ft0 = gen_reg_rtx (SImode);
+    rtx t0 = gen_reg_rtx (word_mode);
+    rtx mask = GEN_INT (<FCLASS_MASK>);
+
+    emit_insn (gen_fclass_<ANYF:fmt> (ft0, operands[1]));
+
+    if (TARGET_64BIT)
+      emit_insn (gen_extend_insn (t0, ft0, DImode, SImode, 0));
+    else
+      emit_move_insn (t0, ft0);
+
+    emit_move_insn (t0, gen_rtx_AND (word_mode, t0, mask));
+    emit_move_insn (t0, gen_rtx_NE (word_mode, t0, const0_rtx));
+
+    if (TARGET_64BIT)
+      {
+       t0 = lowpart_subreg (SImode, t0, DImode);
+       SUBREG_PROMOTED_VAR_P (t0) = 1;
+       SUBREG_PROMOTED_SET (t0, SRP_SIGNED);
+      }
+
+    emit_move_insn (operands[0], t0);
+
+    DONE;
+  })
+
 (define_insn "bytepick_w_<bytepick_imm>"
   [(set (match_operand:SI 0 "register_operand" "=r")
        (ior:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
diff --git a/gcc/testsuite/gcc.target/loongarch/fclass-compile.c b/gcc/testsuite/gcc.target/loongarch/fclass-compile.c
new file mode 100644 (file)
index 0000000..9c24d6e
--- /dev/null
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=loongarch64 -mfpu=64 -mabi=lp64d" } */
+/* { dg-final { scan-assembler-times "fclass\\.s" 1 } } */
+/* { dg-final { scan-assembler-times "fclass\\.d" 1 } } */
+
+__attribute__ ((noipa)) int
+test_fclass_f (float f)
+{
+  return __builtin_isinf (f)
+        | __builtin_isnormal (f) << 1
+        | __builtin_isfinite (f) << 2;
+}
+
+__attribute__ ((noipa)) int
+test_fclass_d (double d)
+{
+  return __builtin_isinf (d)
+        | __builtin_isnormal (d) << 1
+        | __builtin_isfinite (d) << 2;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/fclass-run.c b/gcc/testsuite/gcc.target/loongarch/fclass-run.c
new file mode 100644 (file)
index 0000000..e5585f9
--- /dev/null
@@ -0,0 +1,53 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fsignaling-nans -D_GNU_SOURCE -std=c23" } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+#include "fclass-compile.c"
+
+#define ASSERT_EQ(x, y) (void)(x == y || (__builtin_abort (), 1))
+
+int
+main (void)
+{
+  volatile float f_inf = __builtin_inff ();
+  volatile float f_zero = 0;
+  volatile float f_normal = 114.514;
+  volatile float f_subnormal = 1e-40;
+  volatile float f_qnan = __builtin_nanf ("");
+  volatile float f_snan = __builtin_nansf ("");
+  volatile double d_inf = __builtin_inf ();
+  volatile double d_zero = 0;
+  volatile double d_normal = 1919.810;
+  volatile double d_subnormal = 1e-320;
+  volatile double d_qnan = __builtin_nan ("");
+  volatile double d_snan = __builtin_nans ("");
+
+#if __loongarch_frlen >= 64
+  /* With fclass.{s/d} we shouldn't signal, even if the input is sNaN.
+     PR 66462.  */
+  feenableexcept (FE_INVALID);
+#endif
+
+  ASSERT_EQ (test_fclass_f (f_inf), 0b001);
+  ASSERT_EQ (test_fclass_f (-f_inf), 0b001);
+  ASSERT_EQ (test_fclass_f (f_zero), 0b100);
+  ASSERT_EQ (test_fclass_f (-f_zero), 0b100);
+  ASSERT_EQ (test_fclass_f (f_normal), 0b110);
+  ASSERT_EQ (test_fclass_f (-f_normal), 0b110);
+  ASSERT_EQ (test_fclass_f (f_subnormal), 0b100);
+  ASSERT_EQ (test_fclass_f (-f_subnormal), 0b100);
+  ASSERT_EQ (test_fclass_f (f_qnan), 0);
+  ASSERT_EQ (test_fclass_f (f_snan), 0);
+
+  ASSERT_EQ (test_fclass_d (d_inf), 0b001);
+  ASSERT_EQ (test_fclass_d (-d_inf), 0b001);
+  ASSERT_EQ (test_fclass_d (d_zero), 0b100);
+  ASSERT_EQ (test_fclass_d (-d_zero), 0b100);
+  ASSERT_EQ (test_fclass_d (d_normal), 0b110);
+  ASSERT_EQ (test_fclass_d (-d_normal), 0b110);
+  ASSERT_EQ (test_fclass_d (d_subnormal), 0b100);
+  ASSERT_EQ (test_fclass_d (-d_subnormal), 0b100);
+  ASSERT_EQ (test_fclass_d (d_qnan), 0);
+  ASSERT_EQ (test_fclass_d (d_snan), 0);
+}