OSDN Git Service

2008-04-03 Jan Hubicka <jh@suse.cz>
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index e7eddf7..861a308 100644 (file)
@@ -2135,7 +2135,44 @@ mips_force_temporary (rtx dest, rtx value)
       return dest;
     }
 }
+
+/* Emit a call sequence with call pattern PATTERN and return the call
+   instruction itself (which is not necessarily the last instruction
+   emitted).  LAZY_P is true if the call address is lazily-bound.  */
+
+static rtx
+mips_emit_call_insn (rtx pattern, bool lazy_p)
+{
+  rtx insn;
+
+  insn = emit_call_insn (pattern);
+
+  /* Lazy-binding stubs require $gp to be valid on entry.  */
+  if (lazy_p)
+    use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
+
+  if (TARGET_USE_GOT)
+    {
+      /* See the comment above load_call<mode> for details.  */
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
+              gen_rtx_REG (Pmode, GOT_VERSION_REGNUM));
+      emit_insn (gen_update_got_version ());
+    }
+  return insn;
+}
 \f
+/* Return an instruction that copies $gp into register REG.  We want
+   GCC to treat the register's value as constant, so that its value
+   can be rematerialized on demand.  */
+
+static rtx
+gen_load_const_gp (rtx reg)
+{
+  return (Pmode == SImode
+         ? gen_load_const_gp_si (reg)
+         : gen_load_const_gp_di (reg));
+}
+
 /* Return a pseudo register that contains the value of $gp throughout
    the current function.  Such registers are needed by MIPS16 functions,
    for which $gp itself is not a valid base register or addition operand.  */
@@ -2154,8 +2191,6 @@ mips16_gp_pseudo_reg (void)
     {
       rtx insn, scan, after;
 
-      /* We want GCC to treat the register's value as constant, so that
-        it can be rematerialized on demand.  */
       insn = gen_load_const_gp (cfun->machine->mips16_gp_pseudo_rtx);
 
       push_topmost_sequence ();
@@ -2309,7 +2344,7 @@ static GTY(()) rtx mips_tls_symbol;
 static rtx
 mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
 {
-  rtx insn, loc, tga, a0;
+  rtx insn, loc, a0;
 
   a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST);
 
@@ -2322,8 +2357,7 @@ mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
 
   emit_insn (gen_rtx_SET (Pmode, a0,
                          gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc)));
-  tga = gen_const_mem (Pmode, mips_tls_symbol);
-  insn = emit_call_insn (gen_call_value (v0, tga, const0_rtx, const0_rtx));
+  insn = mips_expand_call (v0, mips_tls_symbol, const0_rtx, const0_rtx, false);
   CONST_OR_PURE_CALL_P (insn) = 1;
   use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
   insn = get_insns ();
@@ -4451,6 +4485,31 @@ mips_return_mode_in_fpr_p (enum machine_mode mode)
          && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_HWFPVALUE);
 }
 
+/* Return the representation of an FPR return register when the
+   value being returned in FP_RETURN has mode VALUE_MODE and the
+   return type itself has mode TYPE_MODE.  On NewABI targets,
+   the two modes may be different for structures like:
+
+       struct __attribute__((packed)) foo { float f; }
+
+   where we return the SFmode value of "f" in FP_RETURN, but where
+   the structure itself has mode BLKmode.  */
+
+static rtx
+mips_return_fpr_single (enum machine_mode type_mode,
+                       enum machine_mode value_mode)
+{
+  rtx x;
+
+  x = gen_rtx_REG (value_mode, FP_RETURN);
+  if (type_mode != value_mode)
+    {
+      x = gen_rtx_EXPR_LIST (VOIDmode, x, const0_rtx);
+      x = gen_rtx_PARALLEL (type_mode, gen_rtvec (1, x));
+    }
+  return x;
+}
+
 /* Return a composite value in a pair of floating-point registers.
    MODE1 and OFFSET1 are the mode and byte offset for the first value,
    likewise MODE2 and OFFSET2 for the second.  MODE is the mode of the
@@ -4502,7 +4561,8 @@ mips_function_value (const_tree valtype, enum machine_mode mode)
       switch (mips_fpr_return_fields (valtype, fields))
        {
        case 1:
-         return gen_rtx_REG (mode, FP_RETURN);
+         return mips_return_fpr_single (mode,
+                                        TYPE_MODE (TREE_TYPE (fields[0])));
 
        case 2:
          return mips_return_fpr_pair (mode,
@@ -4709,9 +4769,9 @@ mips_build_builtin_va_list (void)
     return ptr_type_node;
 }
 
-/* Implement EXPAND_BUILTIN_VA_START.  */
+/* Implement TARGET_EXPAND_BUILTIN_VA_START.  */
 
-void
+static void
 mips_va_start (tree valist, rtx nextarg)
 {
   if (EABI_FLOAT_VARARGS_P)
@@ -5172,7 +5232,7 @@ mips16_copy_fpr_return_value (void)
   fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
   arg = gen_rtx_REG (return_mode, GP_RETURN);
   call = gen_call_value_internal (arg, fn, const0_rtx);
-  insn = emit_call_insn (call);
+  insn = mips_emit_call_insn (call, false);
   use_reg (&CALL_INSN_FUNCTION_USAGE (insn), arg);
 }
 
@@ -5182,7 +5242,8 @@ mips16_copy_fpr_return_value (void)
    arguments and FP_CODE is the code built by mips_function_arg;
    see the comment above CUMULATIVE_ARGS for details.
 
-   Return true if a stub was needed, and emit the call if so.
+   If a stub was needed, emit the call and return the call insn itself.
+   Return null otherwise.
 
    A stub is needed for calls to functions that, in normal mode,
    receive arguments in FPRs or return values in FPRs.  The stub
@@ -5195,7 +5256,7 @@ mips16_copy_fpr_return_value (void)
    to be to a non-MIPS16 function, the linker automatically redirects
    the JAL to the stub, otherwise the JAL continues to call FN directly.  */
 
-static bool
+static rtx
 mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
 {
   const char *fnname;
@@ -5206,7 +5267,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
   /* We don't need to do anything if we aren't in MIPS16 mode, or if
      we were invoked with the -msoft-float option.  */
   if (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI)
-    return false;
+    return NULL_RTX;
 
   /* Figure out whether the value might come back in a floating-point
      register.  */
@@ -5216,13 +5277,13 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
      arguments and the value will not be returned in a floating-point
      register.  */
   if (fp_code == 0 && !fp_ret_p)
-    return false;
+    return NULL_RTX;
 
   /* We don't need to do anything if this is a call to a special
      MIPS16 support function.  */
   if (GET_CODE (fn) == SYMBOL_REF
       && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0)
-    return false;
+    return NULL_RTX;
 
   /* This code will only work for o32 and o64 abis.  The other ABI's
      require more sophisticated support.  */
@@ -5255,7 +5316,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
        insn = gen_call_internal (stub_fn, args_size);
       else
        insn = gen_call_value_internal (retval, stub_fn, args_size);
-      insn = emit_call_insn (insn);
+      insn = mips_emit_call_insn (insn, false);
 
       /* Tell GCC that this call does indeed use the value of $2.  */
       CALL_INSN_FUNCTION_USAGE (insn) =
@@ -5275,7 +5336,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
                                          gen_rtx_REG (word_mode, 18)),
                             CALL_INSN_FUNCTION_USAGE (insn));
 
-      return true;
+      return insn;
     }
 
   /* We know the function we are going to call.  If we have already
@@ -5442,7 +5503,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
     insn = gen_call_internal_direct (fn, args_size);
   else
     insn = gen_call_value_internal_direct (retval, fn, args_size);
-  insn = emit_call_insn (insn);
+  insn = mips_emit_call_insn (insn, false);
 
   /* If we are calling a stub which handles a floating-point return
      value, we need to arrange to save $18 in the prologue.  We do this
@@ -5454,7 +5515,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
                         gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)),
                         CALL_INSN_FUNCTION_USAGE (insn));
 
-  return true;
+  return insn;
 }
 \f
 /* Return true if calls to X can use R_MIPS_CALL* relocations.  */
@@ -5505,9 +5566,11 @@ mips_load_call_address (rtx dest, rtx addr, bool sibcall_p)
    ADDR is the address of the function, ARGS_SIZE is the size of the
    arguments and AUX is the value passed to us by mips_function_arg.
    SIBCALL_P is true if we are expanding a sibling call, false if we're
-   expanding a normal call.  */
+   expanding a normal call.
 
-void
+   Return the call itself.  */
+
+rtx
 mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p)
 {
   rtx orig_addr, pattern, insn;
@@ -5521,13 +5584,12 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p)
       lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p);
     }
 
-  if (TARGET_MIPS16
-      && TARGET_HARD_FLOAT_ABI
-      && mips16_build_call_stub (result, addr, args_size,
-                                aux == 0 ? 0 : (int) GET_MODE (aux)))
+  insn = mips16_build_call_stub (result, addr, args_size,
+                                aux == 0 ? 0 : (int) GET_MODE (aux));
+  if (insn)
     {
-      gcc_assert (!sibcall_p);
-      return;
+      gcc_assert (!sibcall_p && !lazy_p);
+      return insn;
     }
 
   if (result == 0)
@@ -5536,6 +5598,7 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p)
               : gen_call_internal (addr, args_size));
   else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
     {
+      /* Handle return values created by mips_return_fpr_pair.  */
       rtx reg1, reg2;
 
       reg1 = XEXP (XVECEXP (result, 0, 0), 0);
@@ -5546,21 +5609,16 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p)
         : gen_call_value_multiple_internal (reg1, addr, args_size, reg2));
     }
   else
-    pattern = (sibcall_p
-              ? gen_sibcall_value_internal (result, addr, args_size)
-              : gen_call_value_internal (result, addr, args_size));
-
-  insn = emit_call_insn (pattern);
-
-  /* Lazy-binding stubs require $gp to be valid on entry.  We also pretend
-     that they use FAKE_CALL_REGNO; see the load_call<mode> patterns for
-     details.  */
-  if (lazy_p)
     {
-      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
-      use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
-              gen_rtx_REG (Pmode, FAKE_CALL_REGNO));
+      /* Handle return values created by mips_return_fpr_single.  */
+      if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 1)
+       result = XEXP (XVECEXP (result, 0, 0), 0);
+      pattern = (sibcall_p
+                ? gen_sibcall_value_internal (result, addr, args_size)
+                : gen_call_value_internal (result, addr, args_size));
     }
+
+  return mips_emit_call_insn (pattern, lazy_p);
 }
 
 /* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
@@ -5579,12 +5637,12 @@ mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
       && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
     return false;
 
-  /* When -minterlink-mips16 is in effect, assume that external
-     functions could be MIPS16 ones unless an attribute explicitly
-     tells us otherwise.  */
+  /* When -minterlink-mips16 is in effect, assume that non-locally-binding
+     functions could be MIPS16 ones unless an attribute explicitly tells
+     us otherwise.  */
   if (TARGET_INTERLINK_MIPS16
       && decl
-      && DECL_EXTERNAL (decl)
+      && (DECL_EXTERNAL (decl) || !targetm.binds_local_p (decl))
       && !mips_nomips16_decl_p (decl)
       && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
     return false;
@@ -6645,8 +6703,10 @@ mips_in_small_data_p (const_tree decl)
        return false;
     }
 
+  /* We have traditionally not treated zero-sized objects as small data,
+     so this is now effectively part of the ABI.  */
   size = int_size_in_bytes (TREE_TYPE (decl));
-  return size <= mips_small_data_threshold;
+  return size > 0 && size <= mips_small_data_threshold;
 }
 
 /* Implement TARGET_USE_ANCHORS_FOR_SYMBOL_P.  We don't want to use
@@ -7054,7 +7114,9 @@ mips_file_start (void)
 
 #ifdef HAVE_AS_GNU_ATTRIBUTE
       fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n",
-              TARGET_HARD_FLOAT_ABI ? (TARGET_DOUBLE_FLOAT ? 1 : 2) : 3);
+              (TARGET_HARD_FLOAT_ABI
+               ? (TARGET_DOUBLE_FLOAT
+                  ? ((!TARGET_64BIT && TARGET_FLOAT64) ? 4 : 1) : 2) : 3));
 #endif
     }
 
@@ -7941,14 +8003,21 @@ mips_initial_elimination_offset (int from, int to)
   return offset;
 }
 \f
-/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  Some code models use the incoming
-   value of PIC_FUNCTION_ADDR_REGNUM to set up the global pointer.  */
+/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  */
 
 static void
 mips_extra_live_on_entry (bitmap regs)
 {
-  if (TARGET_USE_GOT && !TARGET_ABSOLUTE_ABICALLS)
-    bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
+  if (TARGET_USE_GOT)
+    {
+      /* PIC_FUNCTION_ADDR_REGNUM is live if we need it to set up
+        the global pointer.   */
+      if (!TARGET_ABSOLUTE_ABICALLS)
+       bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
+
+      /* See the comment above load_call<mode> for details.  */
+      bitmap_set_bit (regs, GOT_VERSION_REGNUM);
+    }
 }
 
 /* Implement RETURN_ADDR_RTX.  We do not support moving back to a
@@ -8247,8 +8316,9 @@ static GTY(()) rtx mips_gnu_local_gp;
 static void
 mips_emit_loadgp (void)
 {
-  rtx addr, offset, incoming_address, base, index;
+  rtx addr, offset, incoming_address, base, index, pic_reg;
 
+  pic_reg = pic_offset_table_rtx;
   switch (mips_current_loadgp_style ())
     {
     case LOADGP_ABSOLUTE:
@@ -8257,14 +8327,18 @@ mips_emit_loadgp (void)
          mips_gnu_local_gp = gen_rtx_SYMBOL_REF (Pmode, "__gnu_local_gp");
          SYMBOL_REF_FLAGS (mips_gnu_local_gp) |= SYMBOL_FLAG_LOCAL;
        }
-      emit_insn (gen_loadgp_absolute (mips_gnu_local_gp));
+      emit_insn (Pmode == SImode
+                ? gen_loadgp_absolute_si (pic_reg, mips_gnu_local_gp)
+                : gen_loadgp_absolute_di (pic_reg, mips_gnu_local_gp));
       break;
 
     case LOADGP_NEWABI:
       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_newabi (offset, incoming_address));
+      emit_insn (Pmode == SImode
+                ? gen_loadgp_newabi_si (pic_reg, offset, incoming_address)
+                : gen_loadgp_newabi_di (pic_reg, offset, incoming_address));
       if (!TARGET_EXPLICIT_RELOCS)
        emit_insn (gen_loadgp_blockage ());
       break;
@@ -8272,7 +8346,9 @@ mips_emit_loadgp (void)
     case LOADGP_RTP:
       base = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_BASE));
       index = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_INDEX));
-      emit_insn (gen_loadgp_rtp (base, index));
+      emit_insn (Pmode == SImode
+                ? gen_loadgp_rtp_si (pic_reg, base, index)
+                : gen_loadgp_rtp_di (pic_reg, base, index));
       if (!TARGET_EXPLICIT_RELOCS)
        emit_insn (gen_loadgp_blockage ());
       break;
@@ -8677,6 +8753,9 @@ mips_hard_regno_mode_ok_p (unsigned int regno, enum machine_mode mode)
   if (ALL_COP_REG_P (regno))
     return class == MODE_INT && size <= UNITS_PER_WORD;
 
+  if (regno == GOT_VERSION_REGNUM)
+    return mode == SImode;
+
   return false;
 }
 
@@ -11317,7 +11396,7 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
                   rtx *delayed_reg, rtx lo_reg)
 {
   rtx pattern, set;
-  int nops, ninsns, hazard_set;
+  int nops, ninsns;
 
   pattern = PATTERN (insn);
 
@@ -11363,15 +11442,8 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
        break;
 
       case HAZARD_DELAY:
-       hazard_set = (int) get_attr_hazard_set (insn);
-       if (hazard_set == 0)
-         set = single_set (insn);
-       else
-         {
-           gcc_assert (GET_CODE (PATTERN (insn)) == PARALLEL);
-           set = XVECEXP (PATTERN (insn), 0, hazard_set - 1);
-         }
-       gcc_assert (set && GET_CODE (set) == SET);
+       set = single_set (insn);
+       gcc_assert (set);
        *delayed_reg = SET_DEST (set);
        break;
       }
@@ -11613,6 +11685,7 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   final_start_function (insn, file, 1);
   final (insn, file, 1);
   final_end_function ();
+  free_after_compilation (cfun);
 
   /* Clean up the vars set above.  Note that final_end_function resets
      the global pointer for us.  */
@@ -12411,6 +12484,8 @@ mips_order_regs_for_local_alloc (void)
 
 #undef TARGET_BUILD_BUILTIN_VA_LIST
 #define TARGET_BUILD_BUILTIN_VA_LIST mips_build_builtin_va_list
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START mips_va_start
 #undef TARGET_GIMPLIFY_VA_ARG_EXPR
 #define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr