[v1] LoongArch: Add floating point conditional move support.

Message ID 20230915033302.21325-1-chenglulu@loongson.cn
State New
Headers
Series [v1] LoongArch: Add floating point conditional move support. |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Testing passed

Commit Message

Lulu Cheng Sept. 15, 2023, 3:33 a.m. UTC
  gcc/ChangeLog:

	* config/loongarch/loongarch-protos.h (loongarch_expand_conditional_move):
	Modify the return value type of a function.
	* config/loongarch/loongarch.cc (loongarch_expand_conditional_move): Added
	floating point conditional
	transfer implementation code.
	* config/loongarch/loongarch.md (%3,%2): Define new code_attr.
	(@movdgr2fr<mode>): New template.
	(@movdfr2gr<mode>): Likewise.
	(@movfr2fcc<mode>): Likewise.
	(@movgr2fcc<mode>): Likewise.

gcc/testsuite/ChangeLog:

	* gcc.target/loongarch/cmov_ff.c: New test.
	* gcc.target/loongarch/cmov_fi.c: New test.
	* gcc.target/loongarch/cmov_if.c: New test.

Signed-off-by: yala <zhaojunchao@loongson.cn>
---
 gcc/config/loongarch/loongarch-protos.h      |   2 +-
 gcc/config/loongarch/loongarch.cc            | 117 ++++++++++++++++++-
 gcc/config/loongarch/loongarch.md            |  60 ++++++++--
 gcc/testsuite/gcc.target/loongarch/cmov_ff.c |  16 +++
 gcc/testsuite/gcc.target/loongarch/cmov_fi.c |  15 +++
 gcc/testsuite/gcc.target/loongarch/cmov_if.c |  15 +++
 6 files changed, 208 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/loongarch/cmov_ff.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/cmov_fi.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/cmov_if.c
  

Patch

diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h
index 251011c5414..5501fb8da97 100644
--- a/gcc/config/loongarch/loongarch-protos.h
+++ b/gcc/config/loongarch/loongarch-protos.h
@@ -100,7 +100,7 @@  extern bool loongarch_cfun_has_cprestore_slot_p (void);
 extern void loongarch_expand_scc (rtx *);
 extern bool loongarch_expand_vec_cmp (rtx *);
 extern void loongarch_expand_conditional_branch (rtx *);
-extern void loongarch_expand_conditional_move (rtx *);
+extern bool loongarch_expand_conditional_move (rtx *);
 extern void loongarch_expand_conditional_trap (rtx);
 #endif
 extern void loongarch_set_return_address (rtx, rtx);
diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
index 845fad5a8e8..5aad5058024 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -5063,7 +5063,42 @@  loongarch_expand_conditional_branch (rtx *operands)
 /* Perform the comparison in OPERANDS[1].  Move OPERANDS[2] into OPERANDS[0]
    if the condition holds, otherwise move OPERANDS[3] into OPERANDS[0].  */
 
-void
+/* iiii: means selecting a fixed point based on fixed point comparison result.
+    cmp_code is eq/ne:
+      xor op0 i i
+      maskeqz
+      masknez
+      or
+    cmp_code is not eq/ne:
+      slt[u] op0 i i
+      maskeqz
+      masknez
+      or
+
+   iiff: means Selecting a floating point base on fixed point comparison result.
+     cmp_code is eq/ne:
+      xor op0 i i
+      slt[u] op1
+      movdgr2fr f, op1
+      movfr2fcc fcc, f0
+      fsel f, f, f, fcc
+     cmp_code is not eq/ne:
+      slt[u] op0
+      movdgr2fr f, op0
+      movfr2fcc fcc, f
+      fsel f,f,f,fcc
+
+   ffii: means Selecting a fixed point base on floating point comparison result.
+      fcmp.cond.{s/d} fcc, f, f
+      movgr2fr f, i
+      movgr2fr f, i
+      fsel f,f,f,fcc
+      movfr2gr i,f
+
+   ffff: means Selecting a floating point base on floating point comparison
+   result.
+      fcmp.cond.{s.d}.  */
+bool
 loongarch_expand_conditional_move (rtx *operands)
 {
   enum rtx_code code = GET_CODE (operands[1]);
@@ -5071,6 +5106,8 @@  loongarch_expand_conditional_move (rtx *operands)
   rtx op1 = XEXP (operands[1], 1);
   rtx op0_extend = op0;
   rtx op1_extend = op1;
+  machine_mode cmp_mode = GET_MODE (op0);
+  machine_mode sel_mode = GET_MODE (operands[2]);
 
   /* Record whether operands[2] and operands[3] modes are promoted to word_mode.  */
   bool promote_p = false;
@@ -5097,6 +5134,12 @@  loongarch_expand_conditional_move (rtx *operands)
       if (code == EQ || code == NE)
 	{
 	  op0 = loongarch_zero_if_equal (op0, op1);
+
+	  /* Be careful iiff.  */
+	  if (FLOAT_MODE_P (sel_mode))
+	    loongarch_emit_int_order_test (LTU, NULL, op0,
+					   force_reg (GET_MODE (op0),
+						      const0_rtx), op0);
 	  op1 = const0_rtx;
 	}
       else
@@ -5115,7 +5158,8 @@  loongarch_expand_conditional_move (rtx *operands)
   rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
   /* There is no direct support for general conditional GP move involving
      two registers using SEL.  */
-  if (INTEGRAL_MODE_P (GET_MODE (operands[2]))
+  if (INTEGRAL_MODE_P (cmp_mode)
+      && (INTEGRAL_MODE_P (sel_mode))
       && register_operand (operands[2], VOIDmode)
       && register_operand (operands[3], VOIDmode))
     {
@@ -5165,11 +5209,72 @@  loongarch_expand_conditional_move (rtx *operands)
 	}
       else
 	emit_insn (gen_rtx_SET (operands[0], gen_rtx_IOR (mode, temp, temp2)));
+
+      return true;
     }
-  else
-    emit_insn (gen_rtx_SET (operands[0],
-			    gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), cond,
-						  operands[2], operands[3])));
+  /* For ffii, iiff due to movgr2fr, movfr2gr overhead is relatively large, so
+     we use some compromise.  */
+  else if (INTEGRAL_MODE_P (cmp_mode)
+	   && (FLOAT_MODE_P (sel_mode))
+	   && register_operand (operands[2], VOIDmode)
+	   && register_operand (operands[3], VOIDmode))
+    {
+      rtx temp = gen_reg_rtx (sel_mode);
+      rtx fcc_reg = loongarch_allocate_fcc (FCCmode);
+      rtx diop0 = convert_to_mode (E_DImode, op0, true);
+
+      /* stl t0 i i-> movgr2fr f0 t0 -> movfr2cf fcc0 f0 -> fsel f f.  */
+      emit_insn (gen_movdgr2fr (sel_mode, temp, diop0));
+      emit_insn (gen_movfr2fcc (sel_mode, fcc_reg, temp));
+
+      cond = gen_rtx_fmt_ee (code, GET_MODE (fcc_reg), fcc_reg, const0_rtx);
+
+      emit_insn (gen_rtx_SET (operands[0],
+			      gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+						    cond, operands[2],
+						    operands[3])));
+      return true;
+    }
+  else if (FLOAT_MODE_P (cmp_mode) && (INTEGRAL_MODE_P (sel_mode)))
+    {
+      /* movgr2fr f0 i -> movgr2fr f1 i -> fcmp fcc0 f f
+	 -> fsel f3 f0 f1 -> movfr2gr t0 f3.  */
+      machine_mode dst_mode = GET_MODE (operands[0]);
+      rtx temp = gen_reg_rtx (E_DFmode);
+      rtx temp2 = gen_reg_rtx (E_DFmode);
+      rtx temp3 = gen_reg_rtx (E_DFmode);
+
+      if (CONST_INT_P (operands[2]))
+	operands[2] = copy_to_mode_reg (dst_mode, operands[2]);
+
+      if (CONST_INT_P (operands[3]))
+	operands[3] = copy_to_mode_reg (dst_mode, operands[3]);
+
+      if (GET_MODE (operands[2]) != E_DImode)
+	operands[2] = convert_to_mode (E_DImode, operands[2], false);
+
+      if (GET_MODE (operands[3]) != E_DImode)
+	operands[3] = convert_to_mode (E_DImode, operands[3], false);
+
+      emit_insn (gen_movdgr2frdf (temp2, operands[2]));
+      emit_insn (gen_movdgr2frdf (temp3, operands[3]));
+      emit_insn (gen_rtx_SET (temp,
+			      gen_rtx_IF_THEN_ELSE (E_DFmode, cond,
+						    temp2, temp3)));
+      emit_insn (gen_movdfr2gr (GET_MODE (operands[0]), operands[0], temp));
+
+      return true;
+    }
+  else if (FLOAT_MODE_P (cmp_mode) && FLOAT_MODE_P (sel_mode))
+    {
+      emit_insn (gen_rtx_SET (operands[0],
+			      gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+						    cond, operands[2],
+						    operands[3])));
+      return true;
+    }
+
+  return false;
 }
 
 /* Implement TARGET_EXPAND_BUILTIN_VA_START.  */
diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md
index 4fcb6d781d5..158480ca2a4 100644
--- a/gcc/config/loongarch/loongarch.md
+++ b/gcc/config/loongarch/loongarch.md
@@ -30,7 +30,12 @@  (define_c_enum "unspec" [
   UNSPEC_LOAD_HIGH
   UNSPEC_STORE_WORD
   UNSPEC_MOVGR2FRH
+  UNSPEC_MOVGR2FR
   UNSPEC_MOVFRH2GR
+  UNSPEC_MOVFR2GR
+  UNSPEC_MOVFCC2GR
+  UNSPEC_MOVGR2FCC
+  UNSPEC_MOVFR2FCC
 
   ;; Floating point unspecs.
   UNSPEC_FRINT
@@ -295,6 +300,7 @@  (define_attr "type"
 ;; D2I	float to integer (DF to SI/DI)
 ;; D2S	double to float single
 ;; S2D	float single to double
+;; C2D  fcc to DI
 
 (define_attr "cnv_mode" "unknown,I2S,I2D,S2I,D2I,D2S,S2D"
   (const_string "unknown"))
@@ -559,6 +565,7 @@  (define_code_attr fcond [(unordered "cun")
 
 ;; The sel mnemonic to use depending on the condition test.
 (define_code_attr sel [(eq "masknez") (ne "maskeqz")])
+(define_code_attr fsel_invert [(eq "%2,%3") (ne "%3,%2")])
 (define_code_attr selinv [(eq "maskeqz") (ne "masknez")])
 
 ;; Iterator and attributes for floating-point to fixed-point conversion
@@ -2178,12 +2185,12 @@  (define_insn "*sel<code><GPR:mode>_using_<GPR2:mode>"
 (define_insn "*sel<mode>"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
 	(if_then_else:ANYF
-	 (ne:FCC (match_operand:FCC 1 "register_operand" "z")
+	 (equality_op:FCC (match_operand:FCC 1 "register_operand" "z")
 		 (const_int 0))
 	 (match_operand:ANYF 2 "reg_or_0_operand" "f")
 	 (match_operand:ANYF 3 "reg_or_0_operand" "f")))]
   ""
-  "fsel\t%0,%3,%2,%1"
+  "fsel\t%0,<fsel_invert>,%1"
   [(set_attr "type" "condmove")
    (set_attr "mode" "<ANYF:MODE>")])
 
@@ -2196,11 +2203,10 @@  (define_expand "mov<mode>cc"
 			  (match_operand:GPR 3 "reg_or_0_operand")])))]
   "TARGET_COND_MOVE_INT"
 {
-  if (!INTEGRAL_MODE_P (GET_MODE (XEXP (operands[1], 0))))
+  if (loongarch_expand_conditional_move (operands))
+    DONE;
+  else
     FAIL;
-
-  loongarch_expand_conditional_move (operands);
-  DONE;
 })
 
 (define_expand "mov<mode>cc"
@@ -2210,11 +2216,11 @@  (define_expand "mov<mode>cc"
 			   (match_operand:ANYF 3 "reg_or_0_operand")])))]
   "TARGET_COND_MOVE_FLOAT"
 {
-  if (!FLOAT_MODE_P (GET_MODE (XEXP (operands[1], 0))))
-    FAIL;
 
-  loongarch_expand_conditional_move (operands);
-  DONE;
+  if (loongarch_expand_conditional_move (operands))
+    DONE;
+  else
+    FAIL;
 })
 
 (define_insn "lu32i_d"
@@ -2447,6 +2453,15 @@  (define_insn "movgr2frh<mode>"
   [(set_attr "move_type" "mgtf")
    (set_attr "mode" "<HALFMODE>")])
 
+(define_insn "@movdgr2fr<mode>"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(unspec:ANYF [(match_operand:DI 1 "register_operand" "r")]
+			UNSPEC_MOVGR2FR))]
+  "TARGET_DOUBLE_FLOAT"
+  "movgr2fr.d\t%0,%1"
+  [(set_attr "move_type" "mgtf")
+   (set_attr "mode" "<MODE>")])
+
 ;; Move high word of operand 1 to operand 0 using movfrh2gr.s.
 (define_insn "movfrh2gr<mode>"
   [(set (match_operand:<HALFMODE> 0 "register_operand" "=r")
@@ -2457,6 +2472,31 @@  (define_insn "movfrh2gr<mode>"
   [(set_attr "move_type" "mftg")
    (set_attr "mode" "<HALFMODE>")])
 
+(define_insn "@movdfr2gr<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=r")
+	(unspec:GPR [(match_operand:DF 1 "register_operand" "f")]
+			    UNSPEC_MOVFR2GR))]
+  "TARGET_DOUBLE_FLOAT"
+  "movfr2gr.d\t%0,%1"
+  [(set_attr "move_type" "mftg")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "@movfr2fcc<mode>"
+  [(set (match_operand:FCC 0 "register_operand" "=z")
+	(unspec:FCC [(match_operand:ANYF 1 "register_operand" "f")]
+			    UNSPEC_MOVFR2FCC))]
+  "TARGET_HARD_FLOAT"
+  "movfr2cf\t%0,%1"
+  [(set_attr "mode" "<MODE>")])
+
+(define_insn "@movgr2fcc<mode>"
+  [(set (match_operand:FCC 0 "register_operand" "=z")
+	(unspec:FCC [(match_operand:GPR 1 "register_operand" "r")]
+			    UNSPEC_MOVGR2FCC))]
+  "TARGET_HARD_FLOAT"
+  "movgr2cf\t%0,%1"
+  [(set_attr "mode" "<MODE>")])
+
 
 ;; Expand in-line code to clear the instruction cache between operand[0] and
 ;; operand[1].
diff --git a/gcc/testsuite/gcc.target/loongarch/cmov_ff.c b/gcc/testsuite/gcc.target/loongarch/cmov_ff.c
new file mode 100644
index 00000000000..c49a20f23a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/cmov_ff.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -mdouble-float" } */
+/* { dg-final { scan-assembler "test:.*fcmp.*fsel.*" } } */
+
+extern void foo_ff (float *, float *, float *, float *);
+
+float
+test (void)
+{
+  float a, b;
+  float c, d, out;
+  foo_ff (&a, &b, &c, &d);
+  out = a > b ? c : d;
+  return out;
+}
+
diff --git a/gcc/testsuite/gcc.target/loongarch/cmov_fi.c b/gcc/testsuite/gcc.target/loongarch/cmov_fi.c
new file mode 100644
index 00000000000..07838dad748
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/cmov_fi.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -mdouble-float" } */
+/* { dg-final { scan-assembler "test:.*movgr2fr.*movgr2fr.*fsel.*movfr2gr.*" } } */
+
+extern void foo_fi (float *, float *, int *, int *);
+
+int
+test (void)
+{
+  float a, b;
+  int c, d, out;
+  foo_fi (&a, &b, &c, &d);
+  out = a > b ? c : d;
+  return out;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/cmov_if.c b/gcc/testsuite/gcc.target/loongarch/cmov_if.c
new file mode 100644
index 00000000000..8da11bc90a6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/cmov_if.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -mdouble-float" } */
+/* { dg-final { scan-assembler "test:.*movfr2cf.*fsel.*" } } */
+
+extern void foo_if (int *, int *, float *, float *);
+
+float
+test (void)
+{
+  int a, b;
+  float c, d, out;
+  foo_if (&a, &b, &c, &d);
+  out = a == b ? c : d;
+  return out;
+}