]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
RX: Fix infinite-loop on LRA [PR113948]
authorYoshinori Sato <yoshinori.sato@nifty.com>
Tue, 12 May 2026 11:39:07 +0000 (12:39 +0100)
committerRichard Sandiford <rdsandiford@googlemail.com>
Tue, 12 May 2026 11:39:07 +0000 (12:39 +0100)
The LRA was confused and looping due to the definition of the register class.
The LRA yielded incorrect results because the manipulation of stack frames
and the movement of double words relied heavily on existing reloads.
These changes ensure that the correct code is generated even when the "-mlra"
option is specified.

gcc/ChangeLog:
PR target/113948

* config/rx/rx-protos.h (rx_split_double_move): New helper prototype.
(rx_relax_double_operands): Likewise.
* config/rx/rx.cc (rx_legitimize_address): Add expand complex case.
(rx_is_legitimate_address): Add double word case.
(rx_gen_move_template): Fix operation size in unsigned extend.
(rx_gen_move_template): Remove DImode and DFmode.
(rx_get_stack_layout): Fix for frame size calculation.
(rx_initial_elimination_offset): The calculation method has been
changed to one that supports LRA.
(rx_hard_regno_nregs): Use CEIL.
(rx_hard_regno_mode_ok): Add ATTRIBUTE_UNUSED.
(rx_get_subword): New. Double word move helper.
(rx_split_double_move): Likewise.
(rx_relax_double_operands): Likewise.
* config/rx/rx.h (reg_class): Add CC for all regsisters.
(CLASS_MAX_NREGS): Remove.
* config/rx/rx.md (mov<register_modes:mode>):
Replace copy_to_mode_reg to force_reg.
(movdi): Limit the arguments to make register allocation easier.
(movdf): Likewise.
(movdi_internal): New.
(movdf_internal): New.
(addsi3_pid): New. Handling UNSPEC_PID_ADDR.
(addsi3_lra): New. alternative addptrsi3.
(ashlsi3_lra): Likewise.

Signed-off-by: Yoshinori Sato <yoshinori.sato@nifty.com>
gcc/config/rx/rx-protos.h
gcc/config/rx/rx.cc
gcc/config/rx/rx.h
gcc/config/rx/rx.md

index 829882b0bc837429ff97b8b8d6ebf92d5443f29c..5a36ef269483b5c2e10b5a9cf136f2fe676389ea 100644 (file)
@@ -70,6 +70,8 @@ extern void rx_copy_reg_dead_or_unused_notes (rtx reg, const rtx_insn* src,
 
 extern bool rx_fuse_in_memory_bitop (rtx* operands, rtx_insn* curr_insn,
                                     rtx (*gen_insn)(rtx, rtx));
+extern void rx_split_double_move (rtx* operands, machine_mode mode);
+extern void rx_relax_double_operands (rtx* operands, machine_mode mode);
 
 /* Result value of rx_find_set_of_reg.  */
 struct set_of_reg
index 902e756a34e4f00e76edbae427969633086d39dd..69f4b214fee63d912a0c2fb1122e57ae56ea97ec 100644 (file)
@@ -154,11 +154,22 @@ rx_legitimize_address (rtx x,
       return rv;
     }
 
-  if (GET_CODE (x) == PLUS
-      && GET_CODE (XEXP (x, 0)) == PLUS
-      && REG_P (XEXP (XEXP (x, 0), 0))
-      && REG_P (XEXP (x, 1)))
-    return force_reg (SImode, x);
+  if (GET_CODE (x) == PLUS)
+    {
+      rtx op0 = XEXP (x, 0);
+      rtx op1 = XEXP (x, 1);
+
+      if (GET_CODE (op0) == PLUS
+         && REG_P (XEXP (op0, 0))
+         && CONST_INT_P (op1))
+       {
+         rtx base = XEXP (op0, 0);
+         rtx index = XEXP (op0, 1);
+
+         rtx new_base = force_reg (Pmode, gen_rtx_PLUS (Pmode, base, op1));
+         return simplify_gen_binary (PLUS, Pmode, new_base, index);
+       }
+    }
 
   return x;
 }
@@ -194,6 +205,27 @@ rx_is_legitimate_address (machine_mode mode, rtx x,
        Post-increment Register Indirect.  */
     return RTX_OK_FOR_BASE (XEXP (x, 0), strict);
 
+  if (GET_MODE_SIZE (mode) == UNITS_PER_WORD * 2)
+    {
+      /* Since double-word memory access is split into multiple parts,
+        only simple indirect addresses are accepted.  */
+      if (GET_CODE (x) == PRE_DEC || GET_CODE (x) == POST_INC)
+       return false;
+
+      if (GET_CODE (x) == PLUS)
+       {
+         rtx base = XEXP (x, 0);
+         rtx index = XEXP (x, 1);
+
+         if (!REG_P (base) || !CONST_INT_P (index))
+           return false;
+
+         if (!IN_RANGE (INTVAL (index), 0, (0x10000 * 4) - 1 - 4)
+             || (INTVAL (index) % 4) != 0)
+           return false;
+       }
+    }
+
   switch (rx_pid_data_operand (x))
     {
     case PID_UNENCODED:
@@ -970,7 +1002,8 @@ rx_gen_move_template (rtx * operands, bool is_movu)
   rtx          src  = operands[1];
 
   /* Decide which extension, if any, should be given to the move instruction.  */
-  switch (CONST_INT_P (src) ? GET_MODE (dest) : GET_MODE (src))
+  /* When zero-extending, always check the size of the source. */
+  switch ((is_movu || MEM_P (src)) ? GET_MODE (src) : GET_MODE (dest))
     {
     case E_QImode:
       /* The .B extension is not valid when
@@ -984,10 +1017,9 @@ rx_gen_move_template (rtx * operands, bool is_movu)
           loading an immediate into a register.  */
        extension = ".W";
       break;
-    case E_DFmode:
-    case E_DImode:
     case E_SFmode:
     case E_SImode:
+      gcc_assert (!is_movu);
       extension = ".L";
       break;
     case E_VOIDmode:
@@ -1025,18 +1057,8 @@ rx_gen_move_template (rtx * operands, bool is_movu)
   else
     dst_template = "%0";
 
-  if (GET_MODE (dest) == DImode || GET_MODE (dest) == DFmode)
-    {
-      gcc_assert (! is_movu);
-
-      if (REG_P (src) && REG_P (dest) && (REGNO (dest) == REGNO (src) + 1))
-       sprintf (out_template, "mov.L\t%%H1, %%H0 ! mov.L\t%%1, %%0");
-      else
-       sprintf (out_template, "mov.L\t%%1, %%0 ! mov.L\t%%H1, %%H0");
-    }
-  else
-    sprintf (out_template, "%s%s\t%s, %s", is_movu ? "movu" : "mov",
-            extension, src_template, dst_template);
+  sprintf (out_template, "%s%s\t%s, %s", is_movu ? "movu" : "mov",
+          extension, src_template, dst_template);
   return out_template;
 }
 \f
@@ -1568,12 +1590,9 @@ rx_get_stack_layout (unsigned int * lowest,
       * register_mask = 0;
     }
 
-  * frame_size = rx_round_up
-    (get_frame_size (), STACK_BOUNDARY / BITS_PER_UNIT);
-
-  if (crtl->args.size > 0)
-    * frame_size += rx_round_up
-      (crtl->args.size, STACK_BOUNDARY / BITS_PER_UNIT);
+  * frame_size = rx_round_up (
+      get_frame_size () + crtl->args.pretend_args_size,
+      STACK_BOUNDARY / BITS_PER_UNIT);
 
   * stack_size = rx_round_up
     (crtl->outgoing_args_size, STACK_BOUNDARY / BITS_PER_UNIT);
@@ -2221,26 +2240,27 @@ rx_initial_elimination_offset (int from, int to)
   unsigned int frame_size;
   unsigned int stack_size;
   unsigned int mask;
+  unsigned int saved_regs_size = 0;
 
   rx_get_stack_layout (& low, & high, & mask, & frame_size, & stack_size);
 
+  if (mask != 0)
+    /* multiple push reg */
+    saved_regs_size = bit_count (mask) * UNITS_PER_WORD;
+  else if (low != 0)
+    /* pushm low - high */
+    saved_regs_size = (high - low + 1) * UNITS_PER_WORD;
+
   if (from == ARG_POINTER_REGNUM)
     {
-      /* Extend the computed size of the stack frame to
-        include the registers pushed in the prologue.  */
-      if (low)
-       frame_size += ((high - low) + 1) * UNITS_PER_WORD;
-      else
-       frame_size += bit_count (mask) * UNITS_PER_WORD;
-
       /* Remember to include the return address.  */
-      frame_size += 1 * UNITS_PER_WORD;
+      frame_size += UNITS_PER_WORD;
 
       if (to == FRAME_POINTER_REGNUM)
-       return frame_size;
+       return frame_size + saved_regs_size;
 
       gcc_assert (to == STACK_POINTER_REGNUM);
-      return frame_size + stack_size;
+      return frame_size + stack_size + saved_regs_size;
     }
 
   gcc_assert (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM);
@@ -3611,13 +3631,13 @@ rx_fuse_in_memory_bitop (rtx* operands, rtx_insn* curr_insn,
 static unsigned int
 rx_hard_regno_nregs (unsigned int, machine_mode mode)
 {
-  return CLASS_MAX_NREGS (0, mode);
+  return CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD);
 }
 
 /* Implement TARGET_HARD_REGNO_MODE_OK.  */
 
 static bool
-rx_hard_regno_mode_ok (unsigned int regno, machine_mode)
+rx_hard_regno_mode_ok (unsigned int regno, machine_mode mode ATTRIBUTE_UNUSED)
 {
   return REGNO_REG_CLASS (regno) == GR_REGS;
 }
@@ -3644,7 +3664,61 @@ rx_c_mode_for_floating_type (enum tree_index ti)
     return TARGET_64BIT_DOUBLES ? DFmode : SFmode;
   return default_mode_for_floating_type (ti);
 }
+
+/* Return one word of doubleword value OP, where OP has mode MODE.
+   A REG_OFFSET of 0 selects the low word and a REG_OFFSET of 1 selects
+   the high word.  */
+static rtx
+rx_get_subword (rtx op, machine_mode mode, int reg_offset)
+{
+  unsigned int mem_offset = reg_offset * 4;
+  if (TARGET_BIG_ENDIAN_DATA)
+    mem_offset = 4 - mem_offset;
+
+  if (MEM_P (op))
+    return adjust_address (op, SImode, mem_offset);
+  else
+    return simplify_gen_subreg (SImode, op, mode, mem_offset);
+}
+
+/* Split a doubleword move with operands OPERANDS.
+   Both operands have mode MODE.  */
+void
+rx_split_double_move (rtx * operands, machine_mode mode)
+{
+  rtx dest = operands[0];
+  rtx src  = operands[1];
+
+  rtx dest_low, dest_high, src_low, src_high;
+
+  src_low  = rx_get_subword (src, mode, 0);
+  src_high = rx_get_subword (src, mode, 1);
+
+  dest_low  = rx_get_subword (dest, mode, 0);
+  dest_high = rx_get_subword (dest, mode, 1);
+
+  if (REG_P (operands[0]) && reg_overlap_mentioned_p (dest_low, operands[1]))
+    {
+      emit_move_insn (dest_high, src_high);
+      emit_move_insn (dest_low, src_low);
+    }
+  else
+    {
+      emit_move_insn (dest_low, src_low);
+      emit_move_insn (dest_high, src_high);
+    }
+}
+
+/* Adjust the operands of a doubleword move. OPERANDS gives the operands and
+   MODE gives the mode of the operands.  */
+void
+rx_relax_double_operands(rtx * operands, machine_mode mode)
+{
+  if (MEM_P (operands[0]) && !REG_P (operands[1]))
+    operands[1] = force_reg (mode, operands[1]);
+}
 \f
+
 #undef  TARGET_NARROW_VOLATILE_BITFIELD
 #define TARGET_NARROW_VOLATILE_BITFIELD                rx_narrow_volatile_bitfield
 
index a363a3caaad5ab361594879ffe95d9dc7f1972d7..f78c103fcb2e2d332fd2b99b329796d13cac8ef1 100644 (file)
@@ -195,13 +195,10 @@ enum reg_class
 {                                                      \
   { 0x00000000 },      /* No registers,  */            \
   { 0x0000ffff },      /* Integer registers.  */       \
-  { 0x0000ffff }       /* All registers.  */           \
+  { 0x0001ffff }       /* All registers. */            \
 }
 
 #define N_REG_CLASSES                  (int) LIM_REG_CLASSES
-#define CLASS_MAX_NREGS(CLASS, MODE)    ((GET_MODE_SIZE (MODE) \
-                                         + UNITS_PER_WORD - 1) \
-                                        / UNITS_PER_WORD)
 
 #define GENERAL_REGS                   GR_REGS
 #define BASE_REG_CLASS                 GR_REGS
index 83aab0f14b2598f81b7a20e777c58c1de252d78a..b3398ab9fc53234485b3ce28e884c72f44d1b9f2 100644 (file)
   ""
   {
     if (MEM_P (operands[0]) && MEM_P (operands[1]))
-      operands[1] = copy_to_mode_reg (<register_modes:MODE>mode, operands[1]);
+      operands[1] = force_reg (<register_modes:MODE>mode, operands[1]);
     operands[0] = rx_maybe_pidify_operand (operands[0], 0);
     operands[1] = rx_maybe_pidify_operand (operands[1], 0);
-    if (GET_CODE (operands[0]) != REG
-       && GET_CODE (operands[1]) == PLUS)
-      operands[1] = copy_to_mode_reg (<register_modes:MODE>mode, operands[1]);
-    if (GET_CODE (operands[1]) == PLUS && GET_MODE (operands[1]) == SImode)
-      {
-        emit_insn (gen_addsi3 (operands[0], XEXP (operands[1], 0), XEXP (operands[1], 1)));
-        DONE;
-      }
+    if (MEM_P (operands[0]) && GET_CODE (operands[1]) == PLUS)
+      operands[1] = force_reg (<register_modes:MODE>mode, operands[1]);
     if (CONST_INT_P (operand1)
         && ! rx_is_legitimate_constant (<register_modes:MODE>mode, operand1))
       FAIL;
    (set_attr "timings" "11,11,11,11,11,12,11,11,11,11,11,11")]
 )
 
+(define_expand "movdi"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "")
+        (match_operand:DI 1 "general_operand" ""))]
+  ""
+  {
+    rx_relax_double_operands(operands, DImode);
+  }
+)
+
+(define_insn_and_split "movdi_internal"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,m")
+        (match_operand:DI 1 "general_operand"  "ri,m,r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+  {
+    rx_split_double_move (operands, DImode);
+    DONE;
+  }
+  [(set_attr "length" "8")]
+)
+
+(define_expand "movdf"
+  [(set (match_operand:DF 0 "nonimmediate_operand" "")
+        (match_operand:DF 1 "general_operand" ""))]
+  ""
+  {
+    rx_relax_double_operands(operands, DFmode);
+  }
+)
+
+(define_insn_and_split "movdf_internal"
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=r,r,m")
+        (match_operand:DF 1 "general_operand"  "rF,m,r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+  {
+    rx_split_double_move (operands, DFmode);
+    DONE;
+  }
+  [(set_attr "length" "8")]
+)
+
 (define_insn "extend<small_int_modes:mode>si2"
   [(set (match_operand:SI 0 "register_operand"    "=r,r")
         (sign_extend:SI (match_operand:small_int_modes
    (set_attr "length"   "3,4,5,6,7,6")]
 )
 
+(define_insn "addsi3_pid"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+        (plus:SI (match_operand:SI 1 "register_operand" "%0")
+                 (const:SI (unspec:SI [(match_operand:SI 2 "immediate_operand" "i")] UNSPEC_PID_ADDR))))
+    (clobber (reg:CC CC_REG))]
+
+  ""
+  "add\t%2, %0"
+  [(set_attr "length" "6")
+   (set_attr "timings" "11")]
+)
+
 ;; Peepholes to match:
 ;;   (set (reg A) (reg B))
 ;;   (set (CC) (compare:CC (reg A/reg B) (const_int 0)))
   [(set_attr "timings" "33")
    (set_attr "length"  "5")] ;; This length is corrected in rx_adjust_insn_length
 )
+
 \f
 ;; Floating Point Instructions
 
   ""
 )
 
-(define_insn "movdi"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=rm")
-        (match_operand:DI 1 "general_operand"      "rmi"))]
-  "TARGET_ENABLE_LRA"
-  { return rx_gen_move_template (operands, false); }
-  [(set_attr "length" "16")
-   (set_attr "timings" "22")]
-)
-
-(define_insn "movdf"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "=rm")
-        (match_operand:DF 1 "general_operand"      "rmi"))]
-  "TARGET_ENABLE_LRA"
-  { return rx_gen_move_template (operands, false); }
-  [(set_attr "length" "16")
-   (set_attr "timings" "22")]
+;; RX does not allow addition without destroying CC.
+;; As an alternative to addptrsi3, we define addsi3, which hides changes to CC.
+(define_insn_and_split "*addsi3_lra"
+  [(set (match_operand:SI 0 "register_operand" "=r,r")
+        (plus:SI (match_operand:SI 1 "register_operand" "0,r")
+                 (match_operand:SI 2 "rx_source_operand" "ri,ri")))]
+  "!post_ra_split_completed"
+  "#"
+  "&& 1"
+  [(parallel [
+     (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))
+     (clobber (reg:CC 16))
+   ])]
+)
+
+(define_insn_and_split "*ashlsi3_lra"
+  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
+        (ashift:SI (match_operand:SI 1 "register_operand" "%0,0,r")
+                   (match_operand:SI 2 "rx_shift_operand" "r,i,i")))]
+  "!post_ra_split_completed"
+  "#"
+  "&& 1"
+  [(parallel [
+     (set (match_dup 0) (ashift:SI (match_dup 1) (match_dup 2)))
+     (clobber (reg:CC CC_REG))
+   ])]
 )