OSDN Git Service

PR target/23832
[pf3gnuchains/gcc-fork.git] / gcc / calls.c
index b26e873..7dbc21f 100644 (file)
@@ -17,8 +17,8 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -570,17 +570,8 @@ flags_from_decl_or_type (tree exp)
 
   if (DECL_P (exp))
     {
-      struct cgraph_rtl_info *i = cgraph_rtl_info (exp);
       type = TREE_TYPE (exp);
 
-      if (i)
-       {
-         if (i->pure_function)
-           flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
-         if (i->const_function)
-           flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
-       }
-
       /* The function exp may have the `malloc' attribute.  */
       if (DECL_IS_MALLOC (exp))
        flags |= ECF_MALLOC;
@@ -591,7 +582,7 @@ flags_from_decl_or_type (tree exp)
 
       /* The function exp may have the `pure' attribute.  */
       if (DECL_IS_PURE (exp))
-       flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
+       flags |= ECF_PURE;
 
       if (DECL_IS_NOVOPS (exp))
        flags |= ECF_NOVOPS;
@@ -600,7 +591,7 @@ flags_from_decl_or_type (tree exp)
        flags |= ECF_NOTHROW;
 
       if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
-       flags |= ECF_LIBCALL_BLOCK | ECF_CONST;
+       flags |= ECF_CONST;
 
       flags = special_function_p (exp, flags);
     }
@@ -615,7 +606,7 @@ flags_from_decl_or_type (tree exp)
   if (TREE_CODE (type) == FUNCTION_TYPE && TYPE_RETURNS_STACK_DEPRESSED (type))
     {
       flags |= ECF_SP_DEPRESSED;
-      flags &= ~(ECF_PURE | ECF_CONST | ECF_LIBCALL_BLOCK);
+      flags &= ~(ECF_PURE | ECF_CONST);
     }
 
   return flags;
@@ -1446,6 +1437,42 @@ rtx_for_function_call (tree fndecl, tree addr)
   return funexp;
 }
 
+/* Return true if and only if SIZE storage units (usually bytes)
+   starting from address ADDR overlap with already clobbered argument
+   area.  This function is used to determine if we should give up a
+   sibcall.  */
+
+static bool
+mem_overlaps_already_clobbered_arg_p (rtx addr, unsigned HOST_WIDE_INT size)
+{
+  HOST_WIDE_INT i;
+
+  if (addr == current_function_internal_arg_pointer)
+    i = 0;
+  else if (GET_CODE (addr) == PLUS
+          && (XEXP (addr, 0)
+              == current_function_internal_arg_pointer)
+          && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+    i = INTVAL (XEXP (addr, 1));
+  else
+    return false;
+
+#ifdef ARGS_GROW_DOWNWARD
+  i = -i - size;
+#endif
+  if (size > 0)
+    {
+      unsigned HOST_WIDE_INT k;
+
+      for (k = 0; k < size; k++)
+       if (i + k < stored_args_map->n_bits
+           && TEST_BIT (stored_args_map, i + k))
+         return true;
+    }
+
+  return false;
+}
+
 /* Do the register loads required for any wholly-register parms or any
    parms which are passed both on the stack and in a register.  Their
    expressions were already evaluated.
@@ -1453,7 +1480,7 @@ rtx_for_function_call (tree fndecl, tree addr)
    Mark all register-parms as living through the call, putting these USE
    insns in the CALL_INSN_FUNCTION_USAGE field.
 
-   When IS_SIBCALL, perform the check_sibcall_overlap_argument_overlap
+   When IS_SIBCALL, perform the check_sibcall_argument_overlap
    checking, setting *SIBCALL_FAILURE if appropriate.  */
 
 static void
@@ -1543,6 +1570,12 @@ load_register_parameters (struct arg_data *args, int num_actuals,
            {
              rtx mem = validize_mem (args[i].value);
 
+             /* Check for overlap with already clobbered argument area.  */
+             if (is_sibcall
+                 && mem_overlaps_already_clobbered_arg_p (XEXP (args[i].value, 0),
+                                                          size))
+               *sibcall_failure = 1;
+
              /* Handle a BLKmode that needs shifting.  */
              if (nregs == 1 && size < UNITS_PER_WORD
 #ifdef BLOCK_REG_PADDING
@@ -1656,7 +1689,6 @@ check_sibcall_argument_overlap_1 (rtx x)
 {
   RTX_CODE code;
   int i, j;
-  unsigned int k;
   const char *fmt;
 
   if (x == NULL_RTX)
@@ -1665,28 +1697,8 @@ check_sibcall_argument_overlap_1 (rtx x)
   code = GET_CODE (x);
 
   if (code == MEM)
-    {
-      if (XEXP (x, 0) == current_function_internal_arg_pointer)
-       i = 0;
-      else if (GET_CODE (XEXP (x, 0)) == PLUS
-              && XEXP (XEXP (x, 0), 0) ==
-                 current_function_internal_arg_pointer
-              && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
-       i = INTVAL (XEXP (XEXP (x, 0), 1));
-      else
-       return 0;
-
-#ifdef ARGS_GROW_DOWNWARD
-      i = -i - GET_MODE_SIZE (GET_MODE (x));
-#endif
-
-      for (k = 0; k < GET_MODE_SIZE (GET_MODE (x)); k++)
-       if (i + k < stored_args_map->n_bits
-           && TEST_BIT (stored_args_map, i + k))
-         return 1;
-
-      return 0;
-    }
+    return mem_overlaps_already_clobbered_arg_p (XEXP (x, 0),
+                                                GET_MODE_SIZE (GET_MODE (x)));
 
   /* Scan all subexpressions.  */
   fmt = GET_RTX_FORMAT (code);
@@ -1766,30 +1778,6 @@ shift_return_value (enum machine_mode mode, bool left_p, rtx value)
   return true;
 }
 
-/* Remove all REG_EQUIV notes found in the insn chain.  */
-
-static void
-purge_reg_equiv_notes (void)
-{
-  rtx insn;
-
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    {
-      while (1)
-       {
-         rtx note = find_reg_note (insn, REG_EQUIV, 0);
-         if (note)
-           {
-             /* Remove the note and keep looking at the notes for
-                this insn.  */
-             remove_note (insn, note);
-             continue;
-           }
-         break;
-       }
-    }
-}
-
 /* Generate all the code for a function call
    and return an rtx for its value.
    Store the value in TARGET (specified as an rtx) if convenient.
@@ -1888,6 +1876,7 @@ expand_call (tree exp, rtx target, int ignore)
 
   int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
   char *initial_stack_usage_map = stack_usage_map;
+  char *stack_usage_map_buf = NULL;
 
   int old_stack_allocated;
 
@@ -1988,17 +1977,7 @@ expand_call (tree exp, rtx target, int ignore)
       {
        struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
 
-       if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (exp))
-         {
-           /* The structure value address arg is already in actparms.
-              Pull it out.  It might be nice to just leave it there, but
-              we need to set structure_value_addr.  */
-           tree return_arg = TREE_VALUE (actparms);
-           actparms = TREE_CHAIN (actparms);
-           structure_value_addr = expand_expr (return_arg, NULL_RTX,
-                                               VOIDmode, EXPAND_NORMAL);
-         }
-       else if (target && MEM_P (target))
+       if (target && MEM_P (target) && CALL_EXPR_RETURN_SLOT_OPT (exp))
          structure_value_addr = XEXP (target, 0);
        else
          {
@@ -2289,6 +2268,9 @@ expand_call (tree exp, rtx target, int ignore)
       if (pass && (flags & (ECF_LIBCALL_BLOCK | ECF_MALLOC)))
        start_sequence ();
 
+      if (pass == 0 && cfun->stack_protect_guard)
+       stack_protect_epilogue ();
+
       adjusted_args_size = args_size;
       /* Compute the actual size of the argument block required.  The variable
         and constant sizes must be combined, the size may have to be rounded,
@@ -2384,7 +2366,10 @@ expand_call (tree exp, rtx target, int ignore)
                  highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
                                                     needed);
 #endif
-                 stack_usage_map = alloca (highest_outgoing_arg_in_use);
+                 if (stack_usage_map_buf)
+                   free (stack_usage_map_buf);
+                 stack_usage_map_buf = xmalloc (highest_outgoing_arg_in_use);
+                 stack_usage_map = stack_usage_map_buf;
 
                  if (initial_highest_arg_in_use)
                    memcpy (stack_usage_map, initial_stack_usage_map,
@@ -2489,7 +2474,10 @@ expand_call (tree exp, rtx target, int ignore)
                    = stack_arg_under_construction;
                  stack_arg_under_construction = 0;
                  /* Make a new map for the new argument list.  */
-                 stack_usage_map = alloca (highest_outgoing_arg_in_use);
+                 if (stack_usage_map_buf)
+                   free (stack_usage_map_buf);
+                 stack_usage_map_buf = xmalloc (highest_outgoing_arg_in_use);
+                 stack_usage_map = stack_usage_map_buf;
                  memset (stack_usage_map, 0, highest_outgoing_arg_in_use);
                  highest_outgoing_arg_in_use = 0;
                }
@@ -2544,9 +2532,10 @@ expand_call (tree exp, rtx target, int ignore)
        {
          if (pcc_struct_value)
            valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
-                                         fndecl, (pass == 0));
+                                         fndecl, NULL, (pass == 0));
          else
-           valreg = hard_function_value (TREE_TYPE (exp), fndecl, (pass == 0));
+           valreg = hard_function_value (TREE_TYPE (exp), fndecl, fntype,
+                                         (pass == 0));
        }
 
       /* Precompute all register parameters.  It isn't safe to compute anything
@@ -3043,6 +3032,9 @@ expand_call (tree exp, rtx target, int ignore)
       emit_move_insn (virtual_stack_dynamic_rtx, stack_pointer_rtx);
     }
 
+  if (stack_usage_map_buf)
+    free (stack_usage_map_buf);
+
   return target;
 }
 
@@ -3050,16 +3042,40 @@ expand_call (tree exp, rtx target, int ignore)
    this function's incoming arguments.
 
    At the start of RTL generation we know the only REG_EQUIV notes
-   in the rtl chain are those for incoming arguments, so we can safely
-   flush any REG_EQUIV note.
+   in the rtl chain are those for incoming arguments, so we can look
+   for REG_EQUIV notes between the start of the function and the
+   NOTE_INSN_FUNCTION_BEG.
 
    This is (slight) overkill.  We could keep track of the highest
    argument we clobber and be more selective in removing notes, but it
    does not seem to be worth the effort.  */
+
 void
 fixup_tail_calls (void)
 {
-  purge_reg_equiv_notes ();
+  rtx insn;
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      /* There are never REG_EQUIV notes for the incoming arguments
+        after the NOTE_INSN_FUNCTION_BEG note, so stop if we see it.  */
+      if (NOTE_P (insn)
+         && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
+       break;
+
+      while (1)
+       {
+         rtx note = find_reg_note (insn, REG_EQUIV, 0);
+         if (note)
+           {
+             /* Remove the note and keep looking at the notes for
+                this insn.  */
+             remove_note (insn, note);
+             continue;
+           }
+         break;
+       }
+    }
 }
 
 /* Traverse an argument list in VALUES and expand all complex
@@ -3213,6 +3229,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
   /* Size of the stack reserved for parameter registers.  */
   int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
   char *initial_stack_usage_map = stack_usage_map;
+  char *stack_usage_map_buf = NULL;
 
   rtx struct_value = targetm.calls.struct_value_rtx (0, 0);
 
@@ -3265,7 +3282,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
        {
 #ifdef PCC_STATIC_STRUCT_RETURN
          rtx pointer_reg
-           = hard_function_value (build_pointer_type (tfom), 0, 0);
+           = hard_function_value (build_pointer_type (tfom), 0, 0, 0);
          mem_value = gen_rtx_MEM (outmode, pointer_reg);
          pcc_struct_value = 1;
          if (value == 0)
@@ -3491,7 +3508,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
                                         needed);
 #endif
-      stack_usage_map = alloca (highest_outgoing_arg_in_use);
+      stack_usage_map_buf = xmalloc (highest_outgoing_arg_in_use);
+      stack_usage_map = stack_usage_map_buf;
 
       if (initial_highest_arg_in_use)
        memcpy (stack_usage_map, initial_stack_usage_map,
@@ -3629,6 +3647,28 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
              stack_usage_map[i] = 1;
 
          NO_DEFER_POP;
+
+         if (flags & ECF_CONST)
+           {
+             rtx use;
+
+             /* Indicate argument access so that alias.c knows that these
+                values are live.  */
+             if (argblock)
+               use = plus_constant (argblock,
+                                    argvec[argnum].locate.offset.constant);
+             else
+               /* When arguments are pushed, trying to tell alias.c where
+                  exactly this argument is won't work, because the
+                  auto-increment causes confusion.  So we merely indicate
+                  that we access something with a known mode somewhere on
+                  the stack.  */
+               use = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
+                                   gen_rtx_SCRATCH (Pmode));
+             use = gen_rtx_MEM (argvec[argnum].mode, use);
+             use = gen_rtx_USE (VOIDmode, use);
+             call_fusage = gen_rtx_EXPR_LIST (VOIDmode, use, call_fusage);
+           }
        }
     }
 
@@ -3845,6 +3885,9 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       stack_usage_map = initial_stack_usage_map;
     }
 
+  if (stack_usage_map_buf)
+    free (stack_usage_map_buf);
+
   return value;
 
 }
@@ -4057,6 +4100,13 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
        stack_arg_under_construction--;
     }
 
+  /* Check for overlap with already clobbered argument area.  */
+  if ((flags & ECF_SIBCALL)
+      && MEM_P (arg->value)
+      && mem_overlaps_already_clobbered_arg_p (XEXP (arg->value, 0),
+                                              arg->locate.size.constant))
+    sibcall_failure = 1;
+
   /* Don't allow anything left on stack from computation
      of argument to alloca.  */
   if (flags & ECF_MAY_BE_ALLOCA)