From: Robert Suchanek <robert.sucha...@imgtec.com>

gcc/ChangeLog:

        * config/mips/mips-protos.h (mips_load_store_bonding_p): New
        prototype.
        * config/mips/mips.cc (mips_load_store_bond_insns): New static
        function.
        (mips_block_move_straight): Bond insns where possible.
        (mips_for_each_saved_gpr_and_fpr): Save/restore registers with
        increasing offsets if load store pairs optimisation is enabled.
        (mips_expand_prologue): Bond insns in the prologue.
        (mips_expand_epilogue): Bond insns in the epilogue.
        (mips_multipass_dfa_lookahead): Fix sched_fusion with compiler
        checking enabled.
        (mips_sched_fusion_priority): New static function.
        (mips_avoid_hazard): Check if instruction is not in forbidden
        slot.
        (mips_reorg_process_insns): Likewise.
        (mips_option_override): Disable schedule_fusion for MSA.
        (mips_load_store_p): New function.
        (mips_load_store_insn_p): Likewise.
        (mips_load_store_bond_insns_in_range): Likewise.
        (mips_load_store_bonding_p): Remove load_p argument.
        (mips_load_store_bonding_insn_p): Add more rules for bonding.
        (TARGET_SCHED_FUSION_PRIORITY): Define macro.
        * config/mips/mips.md (can_forbidden): New attribute.
        (JOIN_MODE): Add DI mode to the mode iterator.
        (join2_load_store<JOIN_MODE:mode>): Change this to named
        pattern.  Add 0 operand to constraints.  Add `can_forbidden'
        attribute.
        (*join2_loadhi): Add `can_forbidden' attribute.
        * config/mips/predicates.md (nonimmediate_or_0_operand): New
        predicate.

Cherry-picked 65c0efe581901a706fbe2d4a9d96337090ac220a
and 4a2432906766a48b7f3f9aaad8a1358604ce2f88
from https://github.com/MIPS/gcc

Signed-off-by: Robert Suchanek <robert.sucha...@mips.com>
Signed-off-by: Faraz Shahbazker <fshahbaz...@wavecomp.com>
Signed-off-by: Aleksandar Rakic <aleksandar.ra...@htecgroup.com>
---
 gcc/config/mips/mips-protos.h |   2 +-
 gcc/config/mips/mips.cc       | 399 ++++++++++++++++++++++++++++++++--
 gcc/config/mips/mips.md       |  28 ++-
 gcc/config/mips/predicates.md |   5 +
 4 files changed, 404 insertions(+), 30 deletions(-)

diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h
index 6b8f2370752..1ec6f386f5f 100644
--- a/gcc/config/mips/mips-protos.h
+++ b/gcc/config/mips/mips-protos.h
@@ -379,7 +379,7 @@ extern bool mips_epilogue_uses (unsigned int);
 extern void mips_final_prescan_insn (rtx_insn *, rtx *, int);
 extern int mips_trampoline_code_size (void);
 extern void mips_function_profiler (FILE *);
-extern bool mips_load_store_bonding_p (rtx *, machine_mode, bool);
+extern bool mips_load_store_bonding_p (rtx *, machine_mode);
 
 typedef rtx (*mulsidi3_gen_fn) (rtx, rtx, rtx);
 #ifdef RTX_CODE
diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
index 508435cc9eb..36ce297085b 100644
--- a/gcc/config/mips/mips.cc
+++ b/gcc/config/mips/mips.cc
@@ -1790,6 +1790,9 @@ static int mips_register_move_cost (machine_mode, 
reg_class_t,
                                    reg_class_t);
 static unsigned int mips_function_arg_boundary (machine_mode, const_tree);
 static rtx mips_gen_const_int_vector_shuffle (machine_mode, int);
+static bool mips_load_store_insn_p (rtx_insn *, rtx *,
+                                   HOST_WIDE_INT *, bool *);
+static void mips_load_store_bond_insns ();
 
 /* This hash table keeps track of implicit "mips16" and "nomips16" attributes
    for -mflip_mips16.  It maps decl names onto a boolean mode setting.  */
@@ -9398,6 +9401,9 @@ mips_block_move_straight (rtx dest, rtx src, 
HOST_WIDE_INT length,
       move_by_pieces (dest, src, length - offset,
                      MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), RETURN_BEGIN);
     }
+
+  if (ENABLE_LD_ST_PAIRS)
+    mips_load_store_bond_insns ();
 }
 
 /* Helper function for doing a loop-based block operation on memory
@@ -13279,8 +13285,9 @@ mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT 
sp_offset,
   machine_mode fpr_mode;
   int regno;
   const struct mips_frame_info *frame = &cfun->machine->frame;
-  HOST_WIDE_INT offset;
+  HOST_WIDE_INT offset, offset_dec;
   unsigned int mask;
+  bool increasing_order_p = false;
 
   /* Save registers starting from high to low.  The debuggers prefer at least
      the return register be stored at func+4, and also it allows us not to
@@ -13292,20 +13299,53 @@ mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT 
sp_offset,
   if (TARGET_MICROMIPS)
     umips_build_save_restore (fn, &mask, &offset);
 
-  for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+  if (ENABLE_LD_ST_PAIRS)
+    increasing_order_p = true;
+
+  if (BITSET_P (mask, (regno = GP_REG_LAST - GP_REG_FIRST)))
+    {
+      /* Record the ra offset for use by mips_function_profiler.  */
+      if (regno == RETURN_ADDR_REGNUM)
+       cfun->machine->frame.ra_fp_offset = offset + sp_offset;
+      mips_save_restore_reg (word_mode, regno, offset, fn);
+      offset -= UNITS_PER_WORD;
+    }
+
+  if (increasing_order_p)
+    {
+      offset_dec = 0;
+      for (regno = GP_REG_LAST - 1; regno >= GP_REG_FIRST; regno--)
+       if (BITSET_P (mask, regno - GP_REG_FIRST))
+         offset_dec += UNITS_PER_WORD;
+      offset -= (offset_dec - UNITS_PER_WORD);
+    }
+
+  for (regno = GP_REG_LAST - 1; regno >= GP_REG_FIRST; regno--)
     if (BITSET_P (mask, regno - GP_REG_FIRST))
       {
-       /* Record the ra offset for use by mips_function_profiler.  */
-       if (regno == RETURN_ADDR_REGNUM)
-         cfun->machine->frame.ra_fp_offset = offset + sp_offset;
        mips_save_restore_reg (word_mode, regno, offset, fn);
-       offset -= UNITS_PER_WORD;
+       if (increasing_order_p)
+         offset += UNITS_PER_WORD;
+       else
+         offset -= UNITS_PER_WORD;
       }
 
   /* This loop must iterate over the same space as its companion in
      mips_compute_frame_info.  */
   offset = cfun->machine->frame.fp_sp_offset - sp_offset;
   fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode);
+
+  if (increasing_order_p)
+    {
+      offset_dec = 0;
+      for (regno = FP_REG_LAST - MAX_FPRS_PER_FMT + 1;
+          regno >= FP_REG_FIRST;
+          regno -= MAX_FPRS_PER_FMT)
+       if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
+         offset_dec += GET_MODE_SIZE (fpr_mode);
+      offset -= (offset_dec - GET_MODE_SIZE (fpr_mode));
+    }
+
   for (regno = FP_REG_LAST - MAX_FPRS_PER_FMT + 1;
        regno >= FP_REG_FIRST;
        regno -= MAX_FPRS_PER_FMT)
@@ -13321,7 +13361,10 @@ mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT 
sp_offset,
          }
        else
          mips_save_restore_reg (fpr_mode, regno, offset, fn);
-       offset -= GET_MODE_SIZE (fpr_mode);
+       if (increasing_order_p)
+         offset += GET_MODE_SIZE (fpr_mode);
+       else
+         offset -= GET_MODE_SIZE (fpr_mode);
       }
 }
 
@@ -14078,6 +14121,9 @@ mips_expand_prologue (void)
      the call to mcount.  */
   if (crtl->profile)
     emit_insn (gen_blockage ());
+
+  if (ENABLE_LD_ST_PAIRS)
+    mips_load_store_bond_insns ();
 }
 
 /* Attach all pending register saves to the previous instruction.
@@ -14430,6 +14476,9 @@ mips_expand_epilogue (bool sibcall_p)
       emit_insn_before (gen_mips_di (), insn);
       emit_insn_before (gen_mips_ehb (), insn);
     }
+
+  if (ENABLE_LD_ST_PAIRS)
+    mips_load_store_bond_insns ();
 }
 
 /* Return nonzero if this function is known to have a null epilogue.
@@ -16486,6 +16535,9 @@ mips_dfa_post_advance_cycle (void)
 static int
 mips_multipass_dfa_lookahead (void)
 {
+  if (sched_fusion)
+    return 0;
+
   /* Can schedule up to 4 of the 6 function units in any one cycle.  */
   if (TUNE_SB1)
     return 4;
@@ -16876,6 +16928,48 @@ mips_set_sched_flags (spec_info_t spec_info 
ATTRIBUTE_UNUSED)
   *flags |= DONT_BREAK_DEPENDENCIES;
 }
 
+/* Implement the TARGET_SCHED_FUSION_PRIORITY hook.  */
+
+static void
+mips_sched_fusion_priority (rtx_insn *insn, int max_pri,
+                          int *fusion_pri, int *pri)
+{
+  int tmp;
+  bool is_load;
+  rtx base;
+  HOST_WIDE_INT offset;
+
+  gcc_assert (INSN_P (insn));
+
+  tmp = max_pri - 1;
+  if (!mips_load_store_insn_p (insn, &base, &offset, &is_load))
+    {
+      *pri = tmp;
+      *fusion_pri = tmp;
+      return;
+    }
+
+  /* Load goes first.  */
+  if (is_load)
+    *fusion_pri = tmp - 1;
+  else
+    *fusion_pri = tmp - 2;
+
+  tmp /= 2;
+
+  /* INSN with smaller base register goes first.  */
+  tmp -= ((REGNO (base) & 0xff) << 20);
+
+  /* INSN with smaller offset goes first.  */
+  if (offset >= 0)
+    tmp -= (offset & 0xfffff);
+  else
+    tmp += ((-offset) & 0xfffff);
+
+  *pri = tmp;
+  return;
+}
+
 static void
 mips_weight_finish_global ()
 {
@@ -20791,7 +20885,7 @@ mips_avoid_hazard (rtx_insn *after, rtx_insn *insn, int 
*hilo_delay,
      imply it is not actually a compact branch anyway) and the current
      insn is not an inline asm, and can't go in a delay slot.  */
   else if (TARGET_FORBIDDEN_SLOTS && *fs_delay
-          && get_attr_can_delay (insn) == CAN_DELAY_NO
+          && get_attr_can_forbidden (insn) == CAN_FORBIDDEN_NO
           && GET_CODE (PATTERN (after)) != SEQUENCE
           && GET_CODE (pattern) != ASM_INPUT
           && asm_noperands (pattern) < 0)
@@ -21007,7 +21101,8 @@ mips_reorg_process_insns (void)
                  && ((next_active
                       && INSN_P (next_active)
                       && GET_CODE (PATTERN (next_active)) != SEQUENCE
-                      && get_attr_can_delay (next_active) == CAN_DELAY_YES)
+                      && (get_attr_can_forbidden (next_active)
+                          == CAN_FORBIDDEN_YES))
                      || !optimize_size))
                {
                  /* To hide a potential pipeline bubble, if we scan backwards
@@ -22615,6 +22710,11 @@ mips_option_override (void)
   if (ISA_HAS_MSA && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT_ABI))
     error ("%<-mmsa%> must be used with %<-mfp64%> and %<-mhard-float%>");
 
+  /* Disable fusion for MSA as it can significantly interfere and schedule
+     loads too close to their use.  */
+  if (ISA_HAS_MSA)
+    flag_schedule_fusion = 0;
+
   /* Make sure that -mpaired-single is only used on ISAs that support it.
      We must disable it otherwise since it relies on other ISA properties
      like ISA_HAS_8CC having their normal values.  */
@@ -23258,12 +23358,96 @@ umips_load_store_pair_p_1 (bool load_p, bool swap_p,
   return true;
 }
 
+/* Return TRUE if OPERANDS represents a load or store of address in
+   the form of [BASE+OFFSET] that can be later bonded.  LOAD_P is set to TRUE
+   if it's a load.  Return FALSE otherwise.  */
+
+static bool
+mips_load_store_p (rtx *operands, rtx *base, HOST_WIDE_INT *offset,
+                  bool *load_p)
+{
+  rtx mem;
+  rtx dest = operands[0];
+  rtx src = operands[1];
+
+  if ((GET_CODE (src) == REG || src == const0_rtx)
+      && GET_CODE ((mem = dest)) == MEM)
+    *load_p = false;
+  else if (GET_CODE ((mem = src)) == MEM && GET_CODE (dest) == REG)
+    *load_p = true;
+  else
+    return false;
+
+  mips_split_plus (XEXP (mem, 0), base, offset);
+
+  if (GET_CODE (*base) != REG)
+    return false;
+
+  if (*load_p && MEM_VOLATILE_P (mem))
+    return false;
+
+  return true;
+}
+
+/* Return TRUE if INSN represents a load or store of address in the form of
+   [BASE+OFFSET] that can be later bonded.  LOAD_P is set to TRUE
+   if it's a load.  Return FALSE otherwise.  */
+
+static bool
+mips_load_store_insn_p (rtx_insn *insn, rtx *base, HOST_WIDE_INT *offset,
+                       bool *load_p)
+{
+  rtx op[2], x;
+
+  gcc_assert (INSN_P (insn));
+
+  x = PATTERN (insn);
+  if (GET_CODE (x) != SET)
+    return false;
+
+  op[0] = SET_DEST (x);
+  op[1] = SET_SRC (x);
+  return mips_load_store_p (op, base, offset, load_p);
+}
+
+/* Return TRUE if operands OPERANDS represent two consecutive instructions
+   than can be bonded as load-load/store-store pair in mode MODE.
+   Return FALSE otherwise.  */
+
 bool
-mips_load_store_bonding_p (rtx *operands, machine_mode mode, bool load_p)
+mips_load_store_bonding_p (rtx *operands, machine_mode mode)
 {
   rtx reg1, reg2, mem1, mem2, base1, base2;
   enum reg_class rc1, rc2;
   HOST_WIDE_INT offset1, offset2;
+  bool load_p, load_p2;
+
+  /* Check the supported modes.  */
+  switch (mode)
+    {
+    case E_HImode:
+    case E_SImode:
+      break;
+    case E_DImode:
+      if (!TARGET_64BIT)
+       return false;
+      break;
+    case E_SFmode:
+      if (!TARGET_HARD_FLOAT)
+       return false;
+      break;
+    case E_DFmode:
+      if (!(TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT))
+       return false;
+      break;
+    default:
+      return false;
+    }
+
+  if (!mips_load_store_p (&operands[0], &base1, &offset1, &load_p)
+      || !mips_load_store_p (&operands[2], &base2, &offset2, &load_p2)
+      || load_p != load_p2)
+    return false;
 
   if (load_p)
     {
@@ -23280,12 +23464,17 @@ mips_load_store_bonding_p (rtx *operands, 
machine_mode mode, bool load_p)
       mem2 = operands[2];
     }
 
+  if (MEM_ALIGN (mem1) < GET_MODE_BITSIZE (GET_MODE (mem1))
+      || MEM_ALIGN (mem2) < GET_MODE_BITSIZE (GET_MODE (mem2)))
+    return false;
+
   if (mips_address_insns (XEXP (mem1, 0), mode, false) == 0
       || mips_address_insns (XEXP (mem2, 0), mode, false) == 0)
     return false;
 
-  mips_split_plus (XEXP (mem1, 0), &base1, &offset1);
-  mips_split_plus (XEXP (mem2, 0), &base2, &offset2);
+  if ((!REG_P (reg1) && reg1 != const0_rtx)
+      || (!REG_P (reg2) && reg2 != const0_rtx))
+    return false;
 
   /* Base regs do not match.  */
   if (!REG_P (base1) || !rtx_equal_p (base1, base2))
@@ -23295,29 +23484,196 @@ mips_load_store_bonding_p (rtx *operands, 
machine_mode mode, bool load_p)
      loads if second load clobbers base register.  However, hardware does not
      support such bonding.  */
   if (load_p
-      && (REGNO (reg1) == REGNO (base1)
-         || (REGNO (reg2) == REGNO (base1))))
+      && ((REG_P (reg1) && REGNO (reg1) == REGNO (base1))
+         || (REG_P (reg2) && REGNO (reg2) == REGNO (base1))))
     return false;
 
   /* Loading in same registers.  */
   if (load_p
+      && REG_P (reg1) && REG_P (reg2)
       && REGNO (reg1) == REGNO (reg2))
     return false;
 
-  /* The loads/stores are not of same type.  */
-  rc1 = REGNO_REG_CLASS (REGNO (reg1));
-  rc2 = REGNO_REG_CLASS (REGNO (reg2));
-  if (rc1 != rc2
-      && !reg_class_subset_p (rc1, rc2)
-      && !reg_class_subset_p (rc2, rc1))
+  /* Check if the loads/stores are of the same mode.  */
+  if (GET_MODE (mem1) != GET_MODE (mem2))
     return false;
 
+  /* The loads/stores are not of same type.  */
+  if (reload_completed
+      && reg1 != const0_rtx
+      && reg2 != const0_rtx)
+    {
+      rc1 = REGNO_REG_CLASS (REGNO (reg1));
+      rc2 = REGNO_REG_CLASS (REGNO (reg2));
+      if (rc1 != rc2
+         && !reg_class_subset_p (rc1, rc2)
+         && !reg_class_subset_p (rc2, rc1))
+       return false;
+    }
+
   if (abs (offset1 - offset2) != GET_MODE_SIZE (mode))
     return false;
 
   return true;
 }
 
+/* Return TRUE if INSN1 and INSN2 can be bonded, FALSE otherwise.  */
+
+bool
+mips_load_store_bonding_insn_p (rtx insn1, rtx insn2)
+{
+  rtx operands[4];
+  rtx pat1, pat2;
+
+  gcc_assert (INSN_P (insn1) && INSN_P (insn2));
+
+  pat1 = PATTERN (insn1);
+  pat2 = PATTERN (insn2);
+
+  if (GET_CODE (pat1) == SET && GET_CODE (pat2) == SET)
+    {
+      machine_mode mode;
+
+      operands[0] = SET_DEST (pat1);
+      operands[1] = SET_SRC (pat1);
+      operands[2] = SET_DEST (pat2);
+      operands[3] = SET_SRC (pat2);
+
+      /* We take the mode from either SET_DESTs and the remaining operands
+        and modes will be checked later.  */
+      mode = GET_MODE (operands[0]);
+
+      return mips_load_store_bonding_p (operands, mode);
+    }
+
+  return false;
+}
+
+/* Find and bond load/store pairs in range FROM to TO.  */
+
+static void
+mips_load_store_bond_insns_in_range (rtx_insn *from, rtx_insn *to)
+{
+  rtx_insn *cur, *next;
+
+  if (!ENABLE_LD_ST_PAIRS)
+    return;
+
+  if (from == NULL || to == NULL || from == to)
+    return;
+
+  for (cur = from, next = NEXT_INSN (from);
+       next;
+       cur = next, next = NEXT_INSN (next))
+    {
+      if (INSN_P (cur) && INSN_P (next)
+         && mips_load_store_bonding_insn_p (cur, next))
+       {
+         rtx_insn *bonded;
+         int code;
+         rtx base1, base2;
+         HOST_WIDE_INT offset1, offset2;
+         rtx par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2));
+
+         XVECEXP (par, 0, 0) = PATTERN (cur);
+         XVECEXP (par, 0, 1) = PATTERN (next);
+
+         bonded = emit_insn_before (par, cur);
+         code = recog_memoized (bonded);
+
+         if (code < 0)
+           {
+             delete_insn (bonded);
+             continue;
+           }
+
+         base1 = base2 = NULL_RTX;
+
+         if (GET_CODE (SET_SRC (single_set (cur))) == REG
+             && GET_CODE (SET_DEST (single_set (cur))) == MEM)
+           {
+             mips_split_plus (XEXP (SET_DEST (single_set (cur)), 0),
+                                    &base1, &offset1);
+             mips_split_plus (XEXP (SET_DEST (single_set (next)), 0),
+                                    &base2, &offset2);
+           }
+
+         if (base1 != NULL_RTX
+             && GET_CODE (base1) == REG
+             && REGNO (base1) == STACK_POINTER_REGNUM)
+           {
+             rtx dwarf, dwarf1 = NULL_RTX, dwarf2 = NULL_RTX;
+             rtx note1, note2;
+             int len = 0;
+             int dwarf_index = 0;
+
+             gcc_assert (base2 != NULL_RTX && GET_CODE (base2) == REG
+                         && REGNO (base2) == STACK_POINTER_REGNUM);
+
+             if ((note1 = find_reg_note (cur, REG_FRAME_RELATED_EXPR, 0)))
+               {
+                 dwarf1 = XEXP (note1, 0);
+                 if (GET_CODE (dwarf1) == PARALLEL)
+                   len += XVECLEN (dwarf1, 0);
+                 else
+                   len += 1;
+               }
+
+             if ((note2 = find_reg_note (next, REG_FRAME_RELATED_EXPR, 0)))
+               {
+                 dwarf2 = XEXP (note2, 0);
+                 if (GET_CODE (dwarf2) == PARALLEL)
+                   len += XVECLEN (dwarf2, 0);
+                 else
+                   len += 1;
+               }
+
+             dwarf = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (len));
+
+             if (dwarf1 && GET_CODE (dwarf1) == PARALLEL)
+               {
+                 int i;
+                 for (i = 0 ; i < XVECLEN (dwarf1, 0) ; i++)
+                   {
+                     XVECEXP (dwarf, 0, dwarf_index++) = XVECEXP (dwarf1,
+                                                                  0, i);
+                   }
+               }
+             else if (dwarf1)
+               XVECEXP (dwarf, 0, dwarf_index++) = dwarf1;
+
+             if (dwarf2 && GET_CODE (dwarf2) == PARALLEL)
+               {
+                 int i;
+                 for (i = 0 ; i < XVECLEN (dwarf2, 0) ; i++)
+                   {
+                     XVECEXP (dwarf, 0, dwarf_index++) = XVECEXP (dwarf2,
+                                                                  0, i);
+                   }
+               }
+             else if (dwarf2)
+               XVECEXP (dwarf, 0, dwarf_index++) = dwarf2;
+
+             RTX_FRAME_RELATED_P (bonded) = 1;
+             add_reg_note (bonded, REG_FRAME_RELATED_EXPR, dwarf);
+           }
+
+         remove_insn (cur);
+         remove_insn (next);
+         cur = bonded;
+         next = bonded;
+       }
+    }
+}
+
+/* Find and bond load/store pairs for the entire sequence.  */
+
+static void
+mips_load_store_bond_insns ()
+{
+  mips_load_store_bond_insns_in_range (get_insns (), get_last_insn ());
+}
+
 /* OPERANDS describes the operands to a pair of SETs, in the order
    dest1, src1, dest2, src2.  Return true if the operands can be used
    in an LWP or SWP instruction; LOAD_P says which.  */
@@ -25491,6 +25847,9 @@ mips_bit_clear_p (enum machine_mode mode, unsigned 
HOST_WIDE_INT m)
 #undef TARGET_SCHED_SET_SCHED_FLAGS
 #define TARGET_SCHED_SET_SCHED_FLAGS mips_set_sched_flags
 
+#undef TARGET_SCHED_FUSION_PRIORITY
+#define TARGET_SCHED_FUSION_PRIORITY mips_sched_fusion_priority
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-mips.h"
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 21f31a5595a..814692aecf1 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -786,6 +786,12 @@
                (const_string "yes")
                (const_string "no")))
 
+;; Can the instruction be put into a forbidden slot?
+(define_attr "can_forbidden" "no,yes"
+  (if_then_else (eq_attr "can_delay" "yes")
+               (const_string "yes")
+               (const_string "no")))
+
 ;; Attribute defining whether or not we can use the branch-likely
 ;; instructions.
 (define_attr "branch_likely" "no,yes"
@@ -817,6 +823,7 @@
 (define_mode_iterator MOVEP2 [SI SF])
 (define_mode_iterator JOIN_MODE [HI
                                 SI
+                                (DI "TARGET_64BIT")
                                 (SF "TARGET_HARD_FLOAT")
                                 (DF "TARGET_HARD_FLOAT
                                      && TARGET_DOUBLE_FLOAT")])
@@ -8015,12 +8022,13 @@
    (set_attr "insn_count" "3")])
 
 ;; Match paired HI/SI/SF/DFmode load/stores.
-(define_insn "*join2_load_store<JOIN_MODE:mode>"
+(define_insn "join2_load_store<JOIN_MODE:mode>"
   [(set (match_operand:JOIN_MODE 0 "nonimmediate_operand" "=d,f,m,m")
-       (match_operand:JOIN_MODE 1 "nonimmediate_operand" "m,m,d,f"))
+       (match_operand:JOIN_MODE 1 "nonimmediate_or_0_operand" "m,m,dJ,f"))
    (set (match_operand:JOIN_MODE 2 "nonimmediate_operand" "=d,f,m,m")
-       (match_operand:JOIN_MODE 3 "nonimmediate_operand" "m,m,d,f"))]
-  "ENABLE_LD_ST_PAIRS && reload_completed"
+       (match_operand:JOIN_MODE 3 "nonimmediate_or_0_operand" "m,m,dJ,f"))]
+  "ENABLE_LD_ST_PAIRS
+   && mips_load_store_bonding_p (operands, <JOIN_MODE:MODE>mode)"
   {
     bool load_p = (which_alternative == 0 || which_alternative == 1);
     /* Reg-renaming pass reuses base register if it is dead after bonded loads.
@@ -8043,6 +8051,7 @@
     return "";
   }
   [(set_attr "move_type" "load,fpload,store,fpstore")
+   (set_attr "can_forbidden" "yes")
    (set_attr "insn_count" "2,2,2,2")])
 
 ;; 2 HI/SI/SF/DF loads are joined.
@@ -8055,7 +8064,7 @@
    (set (match_operand:JOIN_MODE 2 "register_operand")
        (match_operand:JOIN_MODE 3 "non_volatile_mem_operand"))]
   "ENABLE_LD_ST_PAIRS
-   && mips_load_store_bonding_p (operands, <JOIN_MODE:MODE>mode, true)"
+   && mips_load_store_bonding_p (operands, <JOIN_MODE:MODE>mode)"
   [(parallel [(set (match_dup 0)
                   (match_dup 1))
              (set (match_dup 2)
@@ -8066,11 +8075,11 @@
 ;; P5600 does not support bonding of two SBs, hence QI mode is not included.
 (define_peephole2
   [(set (match_operand:JOIN_MODE 0 "memory_operand")
-       (match_operand:JOIN_MODE 1 "register_operand"))
+       (match_operand:JOIN_MODE 1 "reg_or_0_operand"))
    (set (match_operand:JOIN_MODE 2 "memory_operand")
-       (match_operand:JOIN_MODE 3 "register_operand"))]
+       (match_operand:JOIN_MODE 3 "reg_or_0_operand"))]
   "ENABLE_LD_ST_PAIRS
-   && mips_load_store_bonding_p (operands, <JOIN_MODE:MODE>mode, false)"
+   && mips_load_store_bonding_p (operands, <JOIN_MODE:MODE>mode)"
   [(parallel [(set (match_dup 0)
                   (match_dup 1))
              (set (match_dup 2)
@@ -8102,6 +8111,7 @@
     return "";
   }
   [(set_attr "move_type" "load")
+   (set_attr "can_forbidden" "yes")
    (set_attr "insn_count" "2")])
 
 ;;
@@ -8153,7 +8163,7 @@
    (set (match_operand:SI 2 "register_operand")
        (any_extend:SI (match_operand:HI 3 "non_volatile_mem_operand")))]
   "ENABLE_LD_ST_PAIRS
-   && mips_load_store_bonding_p (operands, HImode, true)"
+   && mips_load_store_bonding_p (operands, HImode)"
   [(parallel [(set (match_dup 0)
                   (any_extend:SI (match_dup 1)))
              (set (match_dup 2)
diff --git a/gcc/config/mips/predicates.md b/gcc/config/mips/predicates.md
index 31cc57af435..604b1676f2b 100644
--- a/gcc/config/mips/predicates.md
+++ b/gcc/config/mips/predicates.md
@@ -120,6 +120,11 @@
                                  (match_test "ISA_HAS_MIPS16E2")))
        (match_operand 0 "register_operand")))
 
+(define_predicate "nonimmediate_or_0_operand"
+  (ior (and (match_operand 0 "const_0_operand")
+           (not (match_test "TARGET_MIPS16")))
+       (match_operand 0 "nonimmediate_operand")))
+
 (define_predicate "const_1_operand"
   (and (match_code "const_int,const_double,const_vector")
        (match_test "op == CONST1_RTX (GET_MODE (op))")))
-- 
2.34.1

Reply via email to