OSDN Git Service

* config/mips/mips.c: Include cfglayout.h.
authorrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 18 Jan 2004 09:42:11 +0000 (09:42 +0000)
committerrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 18 Jan 2004 09:42:11 +0000 (09:42 +0000)
(TARGET_ASM_OUTPUT_MI_THUNK, TARGET_ASM_CAN_OUTPUT_MI_THUNK): Define.
(mips_unspec_offset_high): Add temporary register argument.
(mips_load_call_address): New function, split out from...
(mips_expand_call): ...here.
(mips_output_cplocal): New function.
(mips_output_function_prologue, mips_output_function_epilogue): Use it.
(mips_emit_loadgp): New function, split out from...
(mips_expand_prologue): ...here.
(mips_output_mi_thunk): New function.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@76088 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/mips/mips.c

index 1bdd7ea..04568ec 100644 (file)
@@ -1,3 +1,16 @@
+2004-01-18  Richard Sandiford  <rsandifo@redhat.com>
+
+       * config/mips/mips.c: Include cfglayout.h.
+       (TARGET_ASM_OUTPUT_MI_THUNK, TARGET_ASM_CAN_OUTPUT_MI_THUNK): Define.
+       (mips_unspec_offset_high): Add temporary register argument.
+       (mips_load_call_address): New function, split out from...
+       (mips_expand_call): ...here.
+       (mips_output_cplocal): New function.
+       (mips_output_function_prologue, mips_output_function_epilogue): Use it.
+       (mips_emit_loadgp): New function, split out from...
+       (mips_expand_prologue): ...here.
+       (mips_output_mi_thunk): New function.
+
 2004-01-17  Bernardo Innocenti  <bernie@develer.com>
 
        * longlong.h (mc68020, __mc68030__, mc68030, __mc68040__, mc68040,
index 477f989..131c559 100644 (file)
@@ -53,6 +53,7 @@ Boston, MA 02111-1307, USA.  */
 #include "target-def.h"
 #include "integrate.h"
 #include "langhooks.h"
+#include "cfglayout.h"
 
 /* Enumeration for all of the relational tests, so that we can build
    arrays indexed by the test type, and not worry about the order
@@ -200,7 +201,7 @@ static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx);
 static rtx mips_force_temporary (rtx, rtx);
 static rtx mips_split_symbol (rtx, rtx);
 static rtx mips_unspec_address (rtx, enum mips_symbol_type);
-static rtx mips_unspec_offset_high (rtx, 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);
@@ -216,6 +217,7 @@ static int mips_address_cost (rtx);
 static enum internal_test map_test_to_internal_test (enum rtx_code);
 static void get_float_compare_codes (enum rtx_code, enum rtx_code *,
                                     enum rtx_code *);
+static void mips_load_call_address (rtx, rtx, int);
 static bool mips_function_ok_for_sibcall (tree, tree);
 static void mips_block_move_straight (rtx, rtx, HOST_WIDE_INT);
 static void mips_adjust_block_mem (rtx, HOST_WIDE_INT, rtx *, rtx *);
@@ -239,12 +241,16 @@ static bool mips_save_reg_p (unsigned int);
 static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT,
                                   mips_save_restore_fn);
 static void mips_for_each_saved_reg (HOST_WIDE_INT, mips_save_restore_fn);
+static void mips_output_cplocal (void);
+static void mips_emit_loadgp (void);
 static void mips_output_function_prologue (FILE *, HOST_WIDE_INT);
 static void mips_set_frame_expr (rtx);
 static rtx mips_frame_set (rtx, rtx);
 static void mips_save_reg (rtx, rtx);
 static void mips_output_function_epilogue (FILE *, HOST_WIDE_INT);
 static void mips_restore_reg (rtx, rtx);
+static void mips_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
+                                 HOST_WIDE_INT, tree);
 static int symbolic_expression_p (rtx);
 static void mips_select_rtx_section (enum machine_mode, rtx,
                                     unsigned HOST_WIDE_INT);
@@ -792,6 +798,11 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
 #undef TARGET_RETURN_IN_MSB
 #define TARGET_RETURN_IN_MSB mips_return_in_msb
 
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK mips_output_mi_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF.  */
@@ -1648,17 +1659,19 @@ mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
 
 /* If mips_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the
    high part to BASE and return the result.  Just return BASE otherwise.
+   TEMP is available as a temporary register if needed.
 
    The returned expression can be used as the first operand to a LO_SUM.  */
 
 static rtx
-mips_unspec_offset_high (rtx base, rtx addr, enum mips_symbol_type symbol_type)
+mips_unspec_offset_high (rtx temp, rtx base, rtx addr,
+                        enum mips_symbol_type symbol_type)
 {
   if (mips_split_p[symbol_type])
     {
       addr = gen_rtx_HIGH (Pmode, mips_unspec_address (addr, symbol_type));
-      base = force_reg (Pmode, expand_simple_binop (Pmode, PLUS, base, addr,
-                                                   NULL, 0, OPTAB_WIDEN));
+      addr = mips_force_temporary (temp, addr);
+      return mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base));
     }
   return base;
 }
@@ -3192,6 +3205,35 @@ mips_gen_conditional_trap (rtx *operands)
                              operands[1]));
 }
 \f
+/* Load function address ADDR into register DEST.  SIBCALL_P is true
+   if the address is needed for a sibling call.  */
+
+static void
+mips_load_call_address (rtx dest, rtx addr, int sibcall_p)
+{
+  /* If we're generating PIC, and this call is to a global function,
+     try to allow its address to be resolved lazily.  This isn't
+     possible for NewABI sibcalls since the value of $gp on entry
+     to the stub would be our caller's gp, not ours.  */
+  if (TARGET_EXPLICIT_RELOCS
+      && !(sibcall_p && TARGET_NEWABI)
+      && global_got_operand (addr, VOIDmode))
+    {
+      rtx high, lo_sum_symbol;
+
+      high = mips_unspec_offset_high (dest, pic_offset_table_rtx,
+                                     addr, SYMBOL_GOTOFF_CALL);
+      lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL);
+      if (Pmode == SImode)
+       emit_insn (gen_load_callsi (dest, high, lo_sum_symbol));
+      else
+       emit_insn (gen_load_calldi (dest, high, lo_sum_symbol));
+    }
+  else
+    emit_move_insn (dest, addr);
+}
+
+
 /* Expand a call or call_value instruction.  RESULT is where the
    result will go (null for calls), ADDR is the address of the
    function, ARGS_SIZE is the size of the arguments and AUX is
@@ -3204,27 +3246,9 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
 {
   if (!call_insn_operand (addr, VOIDmode))
     {
-      /* If we're generating PIC, and this call is to a global function,
-        try to allow its address to be resolved lazily.  This isn't
-        possible for NewABI sibcalls since the value of $gp on entry
-        to the stub would be our caller's gp, not ours.  */
-      if (TARGET_EXPLICIT_RELOCS
-         && !(sibcall_p && TARGET_NEWABI)
-         && global_got_operand (addr, VOIDmode))
-       {
-         rtx high, lo_sum_symbol;
-
-         high = mips_unspec_offset_high (pic_offset_table_rtx,
-                                         addr, SYMBOL_GOTOFF_CALL);
-         lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL);
-         addr = gen_reg_rtx (Pmode);
-         if (Pmode == SImode)
-           emit_insn (gen_load_callsi (addr, high, lo_sum_symbol));
-         else
-           emit_insn (gen_load_calldi (addr, high, lo_sum_symbol));
-       }
-      else
-       addr = force_reg (Pmode, addr);
+      rtx dest = gen_reg_rtx (Pmode);
+      mips_load_call_address (dest, addr, sibcall_p);
+      addr = dest;
     }
 
   if (TARGET_MIPS16
@@ -6466,6 +6490,38 @@ mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
 #undef BITSET_P
 }
 \f
+/* If we're generating n32 or n64 abicalls, and the current function
+   does not use $28 as its global pointer, emit a cplocal directive.
+   Use pic_offset_table_rtx as the argument to the directive.  */
+
+static void
+mips_output_cplocal (void)
+{
+  if (!TARGET_EXPLICIT_RELOCS
+      && cfun->machine->global_pointer > 0
+      && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
+    output_asm_insn (".cplocal %+", 0);
+}
+
+/* If we're generating n32 or n64 abicalls, emit instructions
+   to set up the global pointer.  */
+
+static void
+mips_emit_loadgp (void)
+{
+  if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0)
+    {
+      rtx addr, offset, incoming_address;
+
+      addr = XEXP (DECL_RTL (current_function_decl), 0);
+      offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
+      incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+      emit_insn (gen_loadgp (offset, incoming_address));
+      if (!TARGET_EXPLICIT_RELOCS)
+       emit_insn (gen_loadgp_blockage ());
+    }
+}
+
 /* Set up the stack and frame (if desired) for the function.  */
 
 static void
@@ -6554,6 +6610,11 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
     }
   else if (cfun->machine->all_noreorder_p)
     output_asm_insn ("%(%<", 0);
+
+  /* Tell the assembler which register we're using as the global
+     pointer.  This is needed for thunks, since they can use either
+     explicit relocs or assembler macros.  */
+  mips_output_cplocal ();
 }
 \f
 /* Make the last instruction frame related and note that it performs
@@ -6709,18 +6770,7 @@ mips_expand_prologue (void)
   if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf)
     emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size)));
 
-  /* If generating n32/n64 abicalls, emit the instructions to load $gp.  */
-  if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0)
-    {
-      rtx addr, offset, incoming_address;
-
-      addr = XEXP (DECL_RTL (current_function_decl), 0);
-      offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
-      incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
-      emit_insn (gen_loadgp (offset, incoming_address));
-      if (!TARGET_EXPLICIT_RELOCS)
-       emit_insn (gen_loadgp_blockage ());
-    }
+  mips_emit_loadgp ();
 
   /* If we are profiling, make sure no instructions are scheduled before
      the call to mcount.  */
@@ -6741,6 +6791,10 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
 {
   rtx string;
 
+  /* Reinstate the normal $gp.  */
+  REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM;
+  mips_output_cplocal ();
+
   if (cfun->machine->all_noreorder_p)
     {
       /* Avoid using %>%) since it adds excess whitespace.  */
@@ -6781,9 +6835,6 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
   for (string = mips16_strings; string != 0; string = XEXP (string, 1))
     SYMBOL_REF_FLAG (XEXP (string, 0)) = 0;
   free_EXPR_LIST_list (&mips16_strings);
-
-  /* Reinstate the normal $gp.  */
-  REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM;
 }
 \f
 /* Emit instructions to restore register REG from slot MEM.  */
@@ -6954,6 +7005,132 @@ mips_can_use_return_insn (void)
   return compute_frame_size (get_frame_size ()) == 0;
 }
 \f
+/* Implement TARGET_ASM_OUTPUT_MI_THUNK.  Generate rtl rather than asm text
+   in order to avoid duplicating too much logic from elsewhere.  */
+
+static void
+mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+                     HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+                     tree function)
+{
+  rtx this, temp1, temp2, insn, fnaddr;
+
+  /* Pretend to be a post-reload pass while generating rtl.  */
+  no_new_pseudos = 1;
+  reload_completed = 1;
+
+  /* Pick a global pointer for -mabicalls.  Use $15 rather than $28
+     for TARGET_NEWABI since the latter is a call-saved register.  */
+  if (TARGET_ABICALLS)
+    cfun->machine->global_pointer
+      = REGNO (pic_offset_table_rtx)
+      = TARGET_NEWABI ? 15 : GLOBAL_POINTER_REGNUM;
+
+  /* Set up the global pointer for n32 or n64 abicalls.  */
+  mips_emit_loadgp ();
+
+  /* We need two temporary registers in some cases.  */
+  temp1 = gen_rtx_REG (Pmode, 2);
+  temp2 = gen_rtx_REG (Pmode, 3);
+
+  /* Find out which register contains the "this" pointer.  */
+  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+    this = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
+  else
+    this = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+
+  /* Add DELTA to THIS.  */
+  if (delta != 0)
+    {
+      rtx offset = GEN_INT (delta);
+      if (!SMALL_OPERAND (delta))
+       {
+         emit_move_insn (temp1, offset);
+         offset = temp1;
+       }
+      emit_insn (gen_add3_insn (this, this, offset));
+    }
+
+  /* If needed, add *(*THIS + VCALL_OFFSET) to THIS.  */
+  if (vcall_offset != 0)
+    {
+      rtx addr;
+
+      /* Set TEMP1 to *THIS.  */
+      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));
+      emit_insn (gen_add3_insn (this, this, temp1));
+    }
+
+  /* Jump to the target function.  Use a sibcall if direct jumps are
+     allowed, otherwise load the address into a register first.  */
+  fnaddr = XEXP (DECL_RTL (function), 0);
+  if (TARGET_MIPS16 || TARGET_ABICALLS || TARGET_LONG_CALLS)
+    {
+      /* This is messy.  gas treats "la $25,foo" as part of a call
+        sequence and may allow a global "foo" to be lazily bound.
+        The general move patterns therefore reject this combination.
+
+        In this context, lazy binding would actually be OK for o32 and o64,
+        but it's still wrong for n32 and n64; see mips_load_call_address.
+        We must therefore load the address via a temporary register if
+        mips_dangerous_for_la25_p.
+
+        If we jump to the temporary register rather than $25, the assembler
+        can use the move insn to fill the jump's delay slot.  */
+      if (TARGET_ABICALLS && !mips_dangerous_for_la25_p (fnaddr))
+       temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+      mips_load_call_address (temp1, fnaddr, true);
+
+      if (TARGET_ABICALLS && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
+       emit_move_insn (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
+      emit_jump_insn (gen_indirect_jump (temp1));
+    }
+  else
+    {
+      insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
+      SIBLING_CALL_P (insn) = 1;
+    }
+
+  /* Run just enough of rest_of_compilation.  This sequence was
+     "borrowed" from alpha.c.  */
+  insn = get_insns ();
+  insn_locators_initialize ();
+  split_all_insns_noflow ();
+  shorten_branches (insn);
+  final_start_function (insn, file, 1);
+  final (insn, file, 1, 0);
+  final_end_function ();
+
+  /* Clean up the vars set above.  Note that final_end_function resets
+     the global pointer for us.  */
+  reload_completed = 0;
+  no_new_pseudos = 0;
+}
+\f
 /* Returns nonzero if X contains a SYMBOL_REF.  */
 
 static int