This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[committed] PR target/20621: out of range MIPS gp save slots


I've committed the patch below to fix PR 20621, a 3.4-only MIPS regression.
The problem was that the gp save slot was out of range of a single load.

This was fixed in 4.0 and mainline by the two 2004-08-01 patches
referenced in the changelog.  Those patches still apply cleanly --
modulo the introduction of the TARGET_OLDABI macro -- and since
the patches have been in use for a while now, I thought it would
be safer to stick to them rather than generate a theoretically
less invasive 3.4 version.

[ For example, a theoretically less invasive version could duplicate
  the "add big offset" logic in mips_restore_gp, rather than change
  mips_add_offset and its callers.  But it's easy to get that logic
  wrong, and since it isn't used often, I thought it would be better
  to go with the tried-and-tested version. ]

Bootstrapped & regression tested on mips-linux-gnu.  Applied to 3.4.
I also applied the testcase to mainline and 4.0.

Richard


	PR target/20621
	Backport from mainline:

	2004-08-01  Richard Sandiford  <rsandifo@redhat.com>
	* config/mips/mips.md (cprestore): Provide two alternatives, one for
	an in-range offset and one for an out-of-range offset.  Wrap the latter
	in .set macro/.set nomacro if it's inside a .set nomacro block.

	2004-08-01  Richard Sandiford  <rsandifo@redhat.com>
	* config/mips/mips-protos.h (mips_gp_save_slot): Remove.
	(mips_restore_gp): Declare.
	* config/mips/mips.c (mips_add_offset): Add a scratch register
	argument.  Reimplement in rtl only, reusing MIPS16 logic from
	mips_output_mi_thunk.
	(mips_legitimize_address, mips_legitimize_const_move): Adjust calls
	to mips_add_offset.
	(mips_gp_save_slot): Delete.
	(mips_restore_gp): New function.
	(mips_set_return_address, mips_output_mi_thunk): Use mips_add_offset.
	* config/mips/mips.md (exception_receiver): Turn into a
	define_insn_and_split.  Use mips_restore_gp to do the split.
	(call_internal, call_value_internal, call_value_multiple_internal): Use
	mips_restore_gp to restore $gp.

testsuite/
	PR target/20621
	* gcc.c-torture/execute/pr20621-1.c: New test.

Index: config/mips/mips-protos.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips-protos.h,v
retrieving revision 1.59.4.4
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.59.4.4 mips-protos.h
*** config/mips/mips-protos.h 7 Jul 2004 19:21:10 -0000 1.59.4.4
--- config/mips/mips-protos.h 31 Jul 2005 08:24:21 -0000
*************** extern rtx mips_subword (rtx, int);
*** 62,68 ****
  extern bool mips_split_64bit_move_p (rtx, rtx);
  extern void mips_split_64bit_move (rtx, rtx);
  extern const char *mips_output_move (rtx, rtx);
! extern rtx mips_gp_save_slot (void);
  #ifdef RTX_CODE
  extern rtx gen_int_relational (enum rtx_code, rtx, rtx, rtx, int *);
  extern void gen_conditional_branch (rtx *, enum rtx_code);
--- 62,68 ----
  extern bool mips_split_64bit_move_p (rtx, rtx);
  extern void mips_split_64bit_move (rtx, rtx);
  extern const char *mips_output_move (rtx, rtx);
! extern void mips_restore_gp (void);
  #ifdef RTX_CODE
  extern rtx gen_int_relational (enum rtx_code, rtx, rtx, rtx, int *);
  extern void gen_conditional_branch (rtx *, enum rtx_code);
Index: config/mips/mips.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.c,v
retrieving revision 1.362.4.17
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.362.4.17 mips.c
*** config/mips/mips.c 8 May 2005 12:06:23 -0000 1.362.4.17
--- config/mips/mips.c 31 Jul 2005 08:24:21 -0000
*************** static rtx mips_split_symbol (rtx, rtx);
*** 206,212 ****
  static rtx mips_unspec_address (rtx, enum mips_symbol_type);
  static rtx mips_unspec_offset_high (rtx, rtx, rtx, enum mips_symbol_type);
  static rtx mips_load_got (rtx, rtx, enum mips_symbol_type);
! static rtx mips_add_offset (rtx, HOST_WIDE_INT);
  static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT);
  static unsigned int mips_build_lower (struct mips_integer_op *,
  				      unsigned HOST_WIDE_INT);
--- 206,212 ----
  static rtx mips_unspec_address (rtx, enum mips_symbol_type);
  static rtx mips_unspec_offset_high (rtx, rtx, rtx, enum mips_symbol_type);
  static rtx mips_load_got (rtx, rtx, enum mips_symbol_type);
! static rtx mips_add_offset (rtx, rtx, HOST_WIDE_INT);
  static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT);
  static unsigned int mips_build_lower (struct mips_integer_op *,
  				      unsigned HOST_WIDE_INT);
*************** mips_load_got_global (rtx base, rtx addr
*** 1787,1804 ****
  }
  
  
! /* Return a legitimate address for REG + OFFSET.  This function will
!    create a temporary register if OFFSET is not a SMALL_OPERAND.  */
  
  static rtx
! mips_add_offset (rtx reg, HOST_WIDE_INT offset)
  {
    if (!SMALL_OPERAND (offset))
!     reg = expand_simple_binop (GET_MODE (reg), PLUS,
! 			       GEN_INT (CONST_HIGH_PART (offset)),
! 			       reg, NULL, 0, OPTAB_WIDEN);
! 
!   return plus_constant (reg, CONST_LOW_PART (offset));
  }
  
  
--- 1787,1819 ----
  }
  
  
! /* Return a legitimate address for REG + OFFSET.  TEMP is as for
!    mips_force_temporary; it is only needed when OFFSET is not a
!    SMALL_OPERAND.  */
  
  static rtx
! mips_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
  {
    if (!SMALL_OPERAND (offset))
!     {
!       rtx high;
!       if (TARGET_MIPS16)
! 	{
! 	  /* Load the full offset into a register so that we can use
! 	     an unextended instruction for the address itself.  */
! 	  high = GEN_INT (offset);
! 	  offset = 0;
! 	}
!       else
! 	{
! 	  /* Leave OFFSET as a 16-bit offset and put the excess in HIGH.  */
! 	  high = GEN_INT (CONST_HIGH_PART (offset));
! 	  offset = CONST_LOW_PART (offset);
! 	}
!       high = mips_force_temporary (temp, high);
!       reg = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, high, reg));
!     }
!   return plus_constant (reg, offset);
  }
  
  
*************** mips_legitimize_address (rtx *xloc, enum
*** 1829,1835 ****
        reg = XEXP (*xloc, 0);
        if (!mips_valid_base_register_p (reg, mode, 0))
  	reg = copy_to_mode_reg (Pmode, reg);
!       *xloc = mips_add_offset (reg, INTVAL (XEXP (*xloc, 1)));
        return true;
      }
  
--- 1844,1850 ----
        reg = XEXP (*xloc, 0);
        if (!mips_valid_base_register_p (reg, mode, 0))
  	reg = copy_to_mode_reg (Pmode, reg);
!       *xloc = mips_add_offset (0, reg, INTVAL (XEXP (*xloc, 1)));
        return true;
      }
  
*************** mips_legitimize_const_move (enum machine
*** 2007,2013 ****
        && (!no_new_pseudos || SMALL_OPERAND (offset)))
      {
        base = mips_force_temporary (dest, base);
!       emit_move_insn (dest, mips_add_offset (base, offset));
        return;
      }
  
--- 2022,2028 ----
        && (!no_new_pseudos || SMALL_OPERAND (offset)))
      {
        base = mips_force_temporary (dest, base);
!       emit_move_insn (dest, mips_add_offset (0, base, offset));
        return;
      }
  
*************** mips_output_move (rtx dest, rtx src)
*** 2772,2796 ****
    abort ();
  }
  
! /* Return an rtx for the gp save slot.  Valid only when using o32 or
     o64 abicalls.  */
  
! rtx
! mips_gp_save_slot (void)
  {
!   rtx loc;
  
    if (!TARGET_ABICALLS || TARGET_NEWABI)
      abort ();
  
!   if (frame_pointer_needed)
!     loc = hard_frame_pointer_rtx;
!   else
!     loc = stack_pointer_rtx;
!   loc = plus_constant (loc, current_function_outgoing_args_size);
!   loc = gen_rtx_MEM (Pmode, loc);
!   RTX_UNCHANGING_P (loc) = 1;
!   return loc;
  }
  
  /* Make normal rtx_code into something we can index from an array */
--- 2787,2814 ----
    abort ();
  }
  
! /* Restore $gp from its save slot.  Valid only when using o32 or
     o64 abicalls.  */
  
! void
! mips_restore_gp (void)
  {
!   rtx address, slot;
  
    if (!TARGET_ABICALLS || TARGET_NEWABI)
      abort ();
  
!   address = mips_add_offset (pic_offset_table_rtx,
! 			     frame_pointer_needed
! 			     ? hard_frame_pointer_rtx
! 			     : stack_pointer_rtx,
! 			     current_function_outgoing_args_size);
!   slot = gen_rtx_MEM (Pmode, address);
!   RTX_UNCHANGING_P (slot) = 1;
! 
!   emit_move_insn (pic_offset_table_rtx, slot);
!   if (!TARGET_EXPLICIT_RELOCS)
!     emit_insn (gen_blockage ());
  }
  
  /* Make normal rtx_code into something we can index from an array */
*************** mips_emit_fcc_reload (rtx dest, rtx src,
*** 3403,3428 ****
  void
  mips_set_return_address (rtx address, rtx scratch)
  {
!   HOST_WIDE_INT gp_offset;
  
    compute_frame_size (get_frame_size ());
    if (((cfun->machine->frame.mask >> 31) & 1) == 0)
      abort ();
!   gp_offset = cfun->machine->frame.gp_sp_offset;
! 
!   /* Reduce SP + GP_OFSET to a legitimate address and put it in SCRATCH.  */
!   if (gp_offset < 32768)
!     scratch = plus_constant (stack_pointer_rtx, gp_offset);
!   else
!     {
!       emit_move_insn (scratch, GEN_INT (gp_offset));
!       if (Pmode == DImode)
! 	emit_insn (gen_adddi3 (scratch, scratch, stack_pointer_rtx));
!       else
! 	emit_insn (gen_addsi3 (scratch, scratch, stack_pointer_rtx));
!     }
  
!   emit_move_insn (gen_rtx_MEM (GET_MODE (address), scratch), address);
  }
  
  /* Emit straight-line code to move LENGTH bytes from SRC to DEST.
--- 3421,3435 ----
  void
  mips_set_return_address (rtx address, rtx scratch)
  {
!   rtx slot_address;
  
    compute_frame_size (get_frame_size ());
    if (((cfun->machine->frame.mask >> 31) & 1) == 0)
      abort ();
!   slot_address = mips_add_offset (scratch, stack_pointer_rtx,
! 				  cfun->machine->frame.gp_sp_offset);
  
!   emit_move_insn (gen_rtx_MEM (GET_MODE (address), slot_address), address);
  }
  
  /* Emit straight-line code to move LENGTH bytes from SRC to DEST.
*************** mips_output_mi_thunk (FILE *file, tree t
*** 7190,7214 ****
        emit_move_insn (temp1, gen_rtx_MEM (Pmode, this));
  
        /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET.  */
!       if (SMALL_OPERAND (vcall_offset))
! 	addr = gen_rtx_PLUS (Pmode, temp1, GEN_INT (vcall_offset));
!       else if (TARGET_MIPS16)
! 	{
! 	  /* Load the full offset into a register so that we can use
! 	     an unextended instruction for the load itself.  */
! 	  emit_move_insn (temp2, GEN_INT (vcall_offset));
! 	  emit_insn (gen_add3_insn (temp1, temp1, temp2));
! 	  addr = temp1;
! 	}
!       else
! 	{
! 	  /* Load the high part of the offset into a register and
! 	     leave the low part for the address.  */
! 	  emit_move_insn (temp2, GEN_INT (CONST_HIGH_PART (vcall_offset)));
! 	  emit_insn (gen_add3_insn (temp1, temp1, temp2));
! 	  addr = gen_rtx_PLUS (Pmode, temp1,
! 			       GEN_INT (CONST_LOW_PART (vcall_offset)));
! 	}
  
        /* Load the offset and add it to THIS.  */
        emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr));
--- 7197,7203 ----
        emit_move_insn (temp1, gen_rtx_MEM (Pmode, this));
  
        /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET.  */
!       addr = mips_add_offset (temp2, temp1, vcall_offset);
  
        /* Load the offset and add it to THIS.  */
        emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr));
Index: config/mips/mips.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.md,v
retrieving revision 1.211.4.11
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.211.4.11 mips.md
*** config/mips/mips.md 8 May 2005 12:06:24 -0000 1.211.4.11
--- config/mips/mips.md 31 Jul 2005 08:24:21 -0000
*************** (define_insn "loadgp_blockage"
*** 5160,5175 ****
     (set_attr "mode"	"none")
     (set_attr "length"	"0")])
  
! ;; Emit a .cprestore directive, which expands to a single store instruction.
! ;; Note that we continue to use .cprestore for explicit reloc code so that
! ;; jals inside inlines asms will work correctly.
  (define_insn "cprestore"
!   [(unspec_volatile [(match_operand 0 "const_int_operand" "")]
  		    UNSPEC_CPRESTORE)]
    ""
!   ".cprestore\t%0"
    [(set_attr "type" "store")
!    (set_attr "length" "4")])
  
  ;; Block moves, see mips.c for more details.
  ;; Argument 0 is the destination
--- 5160,5180 ----
     (set_attr "mode"	"none")
     (set_attr "length"	"0")])
  
! ;; Emit a .cprestore directive, which normally expands to a single store
! ;; instruction.  Note that we continue to use .cprestore for explicit reloc
! ;; code so that jals inside inline asms will work correctly.
  (define_insn "cprestore"
!   [(unspec_volatile [(match_operand 0 "const_int_operand" "I,i")]
  		    UNSPEC_CPRESTORE)]
    ""
! {
!   if (set_nomacro && which_alternative == 1)
!     return ".set\tmacro\;.cprestore\t%0\;.set\tnomacro";
!   else
!     return ".cprestore\t%0";
! }
    [(set_attr "type" "store")
!    (set_attr "length" "4,12")])
  
  ;; Block moves, see mips.c for more details.
  ;; Argument 0 is the destination
*************** (define_split
*** 8367,8383 ****
    DONE;
  })
  
! (define_insn "exception_receiver"
    [(set (reg:SI 28)
  	(unspec_volatile:SI [(const_int 0)] UNSPEC_EH_RECEIVER))]
    "TARGET_ABICALLS && (mips_abi == ABI_32 || mips_abi == ABI_O64)"
  {
!   operands[0] = pic_offset_table_rtx;
!   operands[1] = mips_gp_save_slot ();
!   return mips_output_move (operands[0], operands[1]);
  }
    [(set_attr "type"   "load")
!    (set_attr "length" "8")])
  
  ;;
  ;;  ....................
--- 8372,8390 ----
    DONE;
  })
  
! (define_insn_and_split "exception_receiver"
    [(set (reg:SI 28)
  	(unspec_volatile:SI [(const_int 0)] UNSPEC_EH_RECEIVER))]
    "TARGET_ABICALLS && (mips_abi == ABI_32 || mips_abi == ABI_O64)"
+   "#"
+   "&& reload_completed"
+   [(const_int 0)]
  {
!   mips_restore_gp ();
!   DONE;
  }
    [(set_attr "type"   "load")
!    (set_attr "length" "12")])
  
  ;;
  ;;  ....................
*************** (define_insn_and_split "call_internal"
*** 8548,8554 ****
  {
    emit_call_insn (gen_call_split (operands[0], operands[1]));
    if (!find_reg_note (operands[2], REG_NORETURN, 0))
!     emit_move_insn (pic_offset_table_rtx, mips_gp_save_slot ());
    DONE;
  }
    [(set_attr "jal" "indirect,direct")
--- 8555,8561 ----
  {
    emit_call_insn (gen_call_split (operands[0], operands[1]));
    if (!find_reg_note (operands[2], REG_NORETURN, 0))
!     mips_restore_gp ();
    DONE;
  }
    [(set_attr "jal" "indirect,direct")
*************** (define_insn_and_split "call_value_inter
*** 8589,8595 ****
    emit_call_insn (gen_call_value_split (operands[0], operands[1],
  					operands[2]));
    if (!find_reg_note (operands[3], REG_NORETURN, 0))
!     emit_move_insn (pic_offset_table_rtx, mips_gp_save_slot ());
    DONE;
  }
    [(set_attr "jal" "indirect,direct")
--- 8596,8602 ----
    emit_call_insn (gen_call_value_split (operands[0], operands[1],
  					operands[2]));
    if (!find_reg_note (operands[3], REG_NORETURN, 0))
!     mips_restore_gp ();
    DONE;
  }
    [(set_attr "jal" "indirect,direct")
*************** (define_insn_and_split "call_value_multi
*** 8622,8628 ****
    emit_call_insn (gen_call_value_multiple_split (operands[0], operands[1],
  						 operands[2], operands[3]));
    if (!find_reg_note (operands[4], REG_NORETURN, 0))
!     emit_move_insn (pic_offset_table_rtx, mips_gp_save_slot ());
    DONE;
  }
    [(set_attr "jal" "indirect,direct")
--- 8629,8635 ----
    emit_call_insn (gen_call_value_multiple_split (operands[0], operands[1],
  						 operands[2], operands[3]));
    if (!find_reg_note (operands[4], REG_NORETURN, 0))
!     mips_restore_gp ();
    DONE;
  }
    [(set_attr "jal" "indirect,direct")
diff -c /dev/null testsuite/gcc.c-torture/execute/pr20621-1.c
*** /dev/null	Thu Aug 21 05:59:20 2003
--- testsuite/gcc.c-torture/execute/pr20621-1.c	Sun Jul 31 08:17:11 2005
***************
*** 0 ****
--- 1,6 ----
+ /* When generating o32 MIPS PIC, main's $gp save slot was out of range
+    of a single load instruction.  */
+ struct big { int i[sizeof (int) >= 4 && sizeof (void *) >= 4 ? 0x4000 : 4]; };
+ struct big gb;
+ int foo (struct big b, int x) { return b.i[x]; }
+ int main (void) { return foo (gb, 0) + foo (gb, 1); }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]