Bring back 64-bit move splitting for loongarch32. The code was removed
in commit 16fc26d (`LoongArch: Support split symbol.`) for unknown
reason.

gcc/ChangeLog:

        * config/loongarch/loongarch-protos.h (loongarch_split_doubleword_move):
        Delete.
        * config/loongarch/loongarch.cc (loongarch_split_move):
        (loongarch_split_doubleword_move): Delete.
        * config/loongarch/loongarch.md (move_doubleword_2_<mode>): New.
        (load_low<mode>): New.
        (load_high<mode>): New.
        (store_word<mode>): New.
        (movgr2frh<mode>): New.
        (movfrh2gr<mode>): New.
---
 gcc/config/loongarch/loongarch.cc |  31 +++++++-
 gcc/config/loongarch/loongarch.md | 123 +++++++++++++++++++++++++++++-
 2 files changed, 151 insertions(+), 3 deletions(-)

diff --git a/gcc/config/loongarch/loongarch.cc 
b/gcc/config/loongarch/loongarch.cc
index 929085eb9c7..50c8b297e88 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -4704,12 +4704,41 @@ loongarch_split_move_p (rtx dest, rtx src)
 void
 loongarch_split_move (rtx dest, rtx src)
 {
+  rtx low_dest;
+
   gcc_checking_assert (loongarch_split_move_p (dest, src));
   if (LSX_SUPPORTED_MODE_P (GET_MODE (dest))
       || LASX_SUPPORTED_MODE_P (GET_MODE (dest)))
     loongarch_split_vector_move (dest, src);
+  else if (FP_REG_RTX_P (dest) || FP_REG_RTX_P (src))
+    {
+      if (TARGET_32BIT && GET_MODE (dest) == DImode)
+       emit_insn (gen_move_doubleword_2_di (dest, src));
+      else if (TARGET_32BIT && GET_MODE (dest) == DFmode)
+       emit_insn (gen_move_doubleword_2_df (dest, src));
+      else if (TARGET_64BIT && GET_MODE (dest) == TFmode)
+       emit_insn (gen_move_doubleword_2_tf (dest, src));
+      else
+       gcc_unreachable ();
+    }
   else
-    gcc_unreachable ();
+    {
+      /* The operation can be split into two normal moves.  Decide in
+        which order to do them.  */
+      low_dest = loongarch_subword (dest, false);
+      if (REG_P (low_dest) && reg_overlap_mentioned_p (low_dest, src))
+       {
+         loongarch_emit_move (loongarch_subword (dest, true),
+                              loongarch_subword (src, true));
+         loongarch_emit_move (low_dest, loongarch_subword (src, false));
+       }
+      else
+       {
+         loongarch_emit_move (low_dest, loongarch_subword (src, false));
+         loongarch_emit_move (loongarch_subword (dest, true),
+                              loongarch_subword (src, true));
+       }
+    }
 }
 
 /* Check if adding an integer constant value for a specific mode can be
diff --git a/gcc/config/loongarch/loongarch.md 
b/gcc/config/loongarch/loongarch.md
index 40821cff35a..0e0a2ea4586 100644
--- a/gcc/config/loongarch/loongarch.md
+++ b/gcc/config/loongarch/loongarch.md
@@ -409,6 +409,12 @@ (define_mode_iterator ANYF [(SF "TARGET_HARD_FLOAT")
 (define_mode_iterator ANYFI [(SI "TARGET_HARD_FLOAT")
                             (DI "TARGET_DOUBLE_FLOAT")])
 
+;; A mode for which moves involving FPRs may need to be split.
+(define_mode_iterator SPLITF
+  [(DF "!TARGET_64BIT && TARGET_DOUBLE_FLOAT")
+   (DI "!TARGET_64BIT && TARGET_DOUBLE_FLOAT")
+   (TF "TARGET_64BIT && TARGET_DOUBLE_FLOAT")])
+
 ;; A mode for anything with 32 bits or more, and able to be loaded with
 ;; the same addressing mode as ld.w.
 (define_mode_iterator LD_AT_LEAST_32_BIT [GPR ANYF])
@@ -2254,6 +2260,19 @@ (define_insn_and_split "*movdi_32bit"
   [(set_attr "move_type" "move,const,load,store,mgtf,fpload,mftg,fpstore")
    (set_attr "mode" "DI")])
 
+;; Split 64-bit move in LoongArch32
+
+(define_split
+  [(set (match_operand:MOVE64 0 "nonimmediate_operand")
+       (match_operand:MOVE64 1 "move_operand"))]
+  "TARGET_32BIT && reload_completed
+   && loongarch_split_move_p (operands[0], operands[1])"
+  [(const_int 0)]
+{
+  loongarch_split_move (operands[0], operands[1]);
+  DONE;
+})
+
 (define_insn_and_split "*movdi_64bit"
   [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,w,*f,*f,*r,*m")
        (match_operand:DI 1 "move_operand" "r,Yd,w,rJ,*r*J,*m,*f,*f"))]
@@ -2263,8 +2282,8 @@ (define_insn_and_split "*movdi_64bit"
   {
     return loongarch_output_move (operands);
   }
-  "CONST_INT_P (operands[1]) && REG_P (operands[0]) && GP_REG_P (REGNO
-  (operands[0]))"
+  "CONST_INT_P (operands[1]) && REG_P (operands[0])
+   && GP_REG_P (REGNO (operands[0]))"
   [(const_int 0)]
   "
 {
@@ -2484,6 +2503,41 @@ (define_insn "*movdf_softfloat"
   [(set_attr "move_type" "move,load,store")
    (set_attr "mode" "DF")])
 
+;; Emit a doubleword move in which exactly one of the operands is
+;; a floating-point register.  We can't just emit two normal moves
+;; because of the constraints imposed by the FPU register model;
+;; see loongarch_can_change_mode_class for details.  Instead, we keep
+;; the FPR whole and use special patterns to refer to each word of
+;; the other operand.
+
+(define_expand "move_doubleword_2_<mode>"
+  [(set (match_operand:SPLITF 0)
+       (match_operand:SPLITF 1))]
+  ""
+{
+  if (FP_REG_RTX_P (operands[0]))
+    {
+      rtx low = loongarch_subword (operands[1], 0);
+      rtx high = loongarch_subword (operands[1], 1);
+      emit_insn (gen_load_low<mode> (operands[0], low));
+      if (!TARGET_64BIT)
+       emit_insn (gen_movgr2frh<mode> (operands[0], high, operands[0]));
+      else
+       emit_insn (gen_load_high<mode> (operands[0], high, operands[0]));
+    }
+  else
+    {
+      rtx low = loongarch_subword (operands[0], 0);
+      rtx high = loongarch_subword (operands[0], 1);
+      emit_insn (gen_store_word<mode> (low, operands[1], const0_rtx));
+      if (!TARGET_64BIT)
+       emit_insn (gen_movfrh2gr<mode> (high, operands[1]));
+      else
+       emit_insn (gen_store_word<mode> (high, operands[1], const1_rtx));
+    }
+  DONE;
+})
+
 ;; Clear one FCC register
 
 (define_expand "movfcc"
@@ -2808,6 +2862,71 @@ (define_insn "<lrint_pattern><ANYF:mode><ANYFI:mode>2"
   [(set_attr "type" "fcvt")
    (set_attr "mode" "<ANYF:MODE>")])
 
+;; Load the low word of operand 0 with operand 1.
+(define_insn "load_low<mode>"
+  [(set (match_operand:SPLITF 0 "register_operand" "=f,f")
+       (unspec:SPLITF [(match_operand:<HALFMODE> 1 "general_operand" "rJ,m")]
+                      UNSPEC_LOAD_LOW))]
+  "TARGET_HARD_FLOAT"
+{
+  operands[0] = loongarch_subword (operands[0], 0);
+  return loongarch_output_move (operands);
+}
+  [(set_attr "move_type" "mgtf,fpload")
+   (set_attr "mode" "<HALFMODE>")])
+
+;; Load the high word of operand 0 from operand 1, preserving the value
+;; in the low word.
+(define_insn "load_high<mode>"
+  [(set (match_operand:SPLITF 0 "register_operand" "=f,f")
+       (unspec:SPLITF [(match_operand:<HALFMODE> 1 "general_operand" "rJ,m")
+                       (match_operand:SPLITF 2 "register_operand" "0,0")]
+                      UNSPEC_LOAD_HIGH))]
+  "TARGET_HARD_FLOAT"
+{
+  operands[0] = loongarch_subword (operands[0], 1);
+  return loongarch_output_move (operands);
+}
+  [(set_attr "move_type" "mgtf,fpload")
+   (set_attr "mode" "<HALFMODE>")])
+
+;; Store one word of operand 1 in operand 0.  Operand 2 is 1 to store the
+;; high word and 0 to store the low word.
+(define_insn "store_word<mode>"
+  [(set (match_operand:<HALFMODE> 0 "nonimmediate_operand" "=r,m")
+       (unspec:<HALFMODE> [(match_operand:SPLITF 1 "register_operand" "f,f")
+                           (match_operand 2 "const_int_operand")]
+                          UNSPEC_STORE_WORD))]
+  "TARGET_HARD_FLOAT"
+{
+  operands[1] = loongarch_subword (operands[1], INTVAL (operands[2]));
+  return loongarch_output_move (operands);
+}
+  [(set_attr "move_type" "mftg,fpstore")
+   (set_attr "mode" "<HALFMODE>")])
+
+;; Move operand 1 to the high word of operand 0 using movgr2frh.w, preserving 
the
+;; value in the low word.
+(define_insn "movgr2frh<mode>"
+  [(set (match_operand:SPLITF 0 "register_operand" "=f")
+       (unspec:SPLITF [(match_operand:<HALFMODE> 1 "reg_or_0_operand" "rJ")
+                       (match_operand:SPLITF 2 "register_operand" "0")]
+                       UNSPEC_MOVGR2FRH))]
+  "TARGET_DOUBLE_FLOAT"
+  "movgr2frh.w\t%0,%z1"
+  [(set_attr "move_type" "mgtf")
+   (set_attr "mode" "<HALFMODE>")])
+
+;; Move high word of operand 1 to operand 0 using movfrh2gr.s.
+(define_insn "movfrh2gr<mode>"
+  [(set (match_operand:<HALFMODE> 0 "register_operand" "=r")
+       (unspec:<HALFMODE> [(match_operand:SPLITF 1 "register_operand" "f")]
+                           UNSPEC_MOVFRH2GR))]
+  "TARGET_DOUBLE_FLOAT"
+  "movfrh2gr.s\t%0,%1"
+  [(set_attr "move_type" "mftg")
+   (set_attr "mode" "<HALFMODE>")])
+
 ;; Thread-Local Storage
 
 (define_insn "@got_load_tls_desc<mode>"
-- 
2.34.1

Reply via email to