OSDN Git Service

Add new test - do not warn about (non-)redundant friend declaration.
[pf3gnuchains/gcc-fork.git] / gcc / sibcall.c
index 1a3a310..13f9c1f 100644 (file)
@@ -1,5 +1,5 @@
 /* Generic sibling call optimization support
-   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -33,13 +33,16 @@ Boston, MA 02111-1307, USA.  */
 #include "except.h"
 
 static int identify_call_return_value  PARAMS ((rtx, rtx *, rtx *));
-static rtx skip_copy_to_return_value   PARAMS ((rtx, rtx, rtx));
+static rtx skip_copy_to_return_value   PARAMS ((rtx));
 static rtx skip_use_of_return_value    PARAMS ((rtx, enum rtx_code));
 static rtx skip_stack_adjustment       PARAMS ((rtx));
+static rtx skip_pic_restore            PARAMS ((rtx));
 static rtx skip_jump_insn              PARAMS ((rtx));
+static int call_ends_block_p           PARAMS ((rtx, rtx));
 static int uses_addressof              PARAMS ((rtx));
 static int sequence_uses_addressof     PARAMS ((rtx));
 static void purge_reg_equiv_notes      PARAMS ((void));
+static void purge_mem_unchanging_flag  PARAMS ((rtx));
 
 /* Examine a CALL_PLACEHOLDER pattern and determine where the call's
    return value is located.  P_HARD_RETURN receives the hard register
@@ -53,10 +56,12 @@ identify_call_return_value (cp, p_hard_return, p_soft_return)
 {
   rtx insn, set, hard, soft;
 
-  /* Search forward through the "normal" call sequence to the CALL insn.  */
   insn = XEXP (cp, 0);
-  while (GET_CODE (insn) != CALL_INSN)
+  /* Search backward through the "normal" call sequence to the CALL insn.  */
+  while (NEXT_INSN (insn))
     insn = NEXT_INSN (insn);
+  while (GET_CODE (insn) != CALL_INSN)
+    insn = PREV_INSN (insn);
 
   /* Assume the pattern is (set (dest) (call ...)), or that the first
      member of a parallel is.  This is the hard return register used
@@ -75,6 +80,16 @@ identify_call_return_value (cp, p_hard_return, p_soft_return)
   if (GET_CODE (hard) != REG)
     return 0;
     
+  /* Stack adjustment done after call may appear here.  */
+  insn = skip_stack_adjustment (insn);
+  if (! insn)
+    return 0;
+
+  /* Restore of GP register may appear here.  */
+  insn = skip_pic_restore (insn);
+  if (! insn)
+    return 0;
+
   /* If there's nothing after, there's no soft return value.  */
   insn = NEXT_INSN (insn);
   if (! insn)
@@ -118,11 +133,15 @@ identify_call_return_value (cp, p_hard_return, p_soft_return)
    copy.  Otherwise return ORIG_INSN.  */
 
 static rtx
-skip_copy_to_return_value (orig_insn, hardret, softret)
+skip_copy_to_return_value (orig_insn)
      rtx orig_insn;
-     rtx hardret, softret;
 {
   rtx insn, set = NULL_RTX;
+  rtx hardret, softret;
+
+  /* If there is no return value, we have nothing to do.  */
+  if (! identify_call_return_value (PATTERN (orig_insn), &hardret, &softret))
+    return orig_insn;
 
   insn = next_nonnote_insn (orig_insn);
   if (! insn)
@@ -192,10 +211,6 @@ skip_stack_adjustment (orig_insn)
   if (insn)
     set = single_set (insn);
 
-  /* The source must be the same as the current function's return value to
-     ensure that any return value is put in the same place by the current
-     function and the function we're calling.   The destination register
-     must be a pseudo.  */
   if (insn
       && set
       && GET_CODE (SET_SRC (set)) == PLUS
@@ -204,8 +219,26 @@ skip_stack_adjustment (orig_insn)
       && SET_DEST (set) == stack_pointer_rtx)
     return insn;
 
-  /* It did not look like a copy of the return value, so return the
-     same insn we were passed.  */
+  return orig_insn;
+}
+
+/* If the first real insn after ORIG_INSN sets the pic register,
+   return it.  Otherwise return ORIG_INSN.  */
+
+static rtx
+skip_pic_restore (orig_insn)
+     rtx orig_insn;
+{
+  rtx insn, set = NULL_RTX;
+
+  insn = next_nonnote_insn (orig_insn);
+
+  if (insn)
+    set = single_set (insn);
+
+  if (insn && set && SET_DEST (set) == pic_offset_table_rtx)
+    return insn;
+
   return orig_insn;
 }
 
@@ -222,14 +255,59 @@ skip_jump_insn (orig_insn)
 
   if (insn
       && GET_CODE (insn) == JUMP_INSN
-      && simplejump_p (insn))
+      && any_uncondjump_p (insn))
     return insn;
 
   return orig_insn;
 }
+\f
+/* Using the above functions, see if INSN, skipping any of the above,
+   goes all the way to END, the end of a basic block.  Return 1 if so.  */
+
+static int
+call_ends_block_p (insn, end)
+     rtx insn;
+     rtx end;
+{
+  /* END might be a note, so get the last nonnote insn of the block.  */
+  end = next_nonnote_insn (PREV_INSN (end));
+
+  /* If the call was the end of the block, then we're OK.  */
+  if (insn == end)
+    return 1;
+
+  /* Skip over copying from the call's return value pseudo into
+     this function's hard return register and if that's the end
+     of the block, we're OK.  */
+  insn = skip_copy_to_return_value (insn);
+  if (insn == end)
+    return 1;
+
+  /* Skip any stack adjustment.  */
+  insn = skip_stack_adjustment (insn);
+  if (insn == end)
+    return 1;
+
+  /* Skip over a CLOBBER of the return value as a hard reg.  */
+  insn = skip_use_of_return_value (insn, CLOBBER);
+  if (insn == end)
+    return 1;
+
+  /* Skip over a USE of the return value (as a hard reg).  */
+  insn = skip_use_of_return_value (insn, USE);
+  if (insn == end)
+    return 1;
+
+  /* Skip over a JUMP_INSN at the end of the block.  If that doesn't end the
+     block, the original CALL_INSN didn't.  */
+  insn = skip_jump_insn (insn);
+  return insn == end;
+}
 
-/* Scan the rtx X for an ADDRESSOF expressions.  Return nonzero if an ADDRESSOF
-   expresion is found, else return zero.  */
+/* Scan the rtx X for ADDRESSOF expressions or
+   current_function_internal_arg_pointer registers.
+   Return nonzero if an ADDRESSOF or current_function_internal_arg_pointer
+   is found outside of some MEM expression, else return zero.  */
 
 static int
 uses_addressof (x)
@@ -244,9 +322,12 @@ uses_addressof (x)
 
   code = GET_CODE (x);
 
-  if (code == ADDRESSOF)
+  if (code == ADDRESSOF || x == current_function_internal_arg_pointer)
     return 1;
 
+  if (code == MEM)
+    return 0;
+
   /* Scan all subexpressions. */
   fmt = GET_RTX_FORMAT (code);
   for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
@@ -267,8 +348,10 @@ uses_addressof (x)
 }
 
 /* Scan the sequence of insns in SEQ to see if any have an ADDRESSOF
-   rtl expression.  If an ADDRESSOF expression is found, return nonzero,
-   else return zero.
+   rtl expression or current_function_internal_arg_pointer occurences
+   not enclosed within a MEM.  If an ADDRESSOF expression or
+   current_function_internal_arg_pointer is found, return nonzero, otherwise
+   return zero.
 
    This function handles CALL_PLACEHOLDERs which contain multiple sequences
    of insns.  */
@@ -280,7 +363,7 @@ sequence_uses_addressof (seq)
   rtx insn;
 
   for (insn = seq; insn; insn = NEXT_INSN (insn))
-    if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+    if (INSN_P (insn))
       {
        /* If this is a CALL_PLACEHOLDER, then recursively call ourselves
           with each nonempty sequence attached to the CALL_PLACEHOLDER.  */
@@ -328,6 +411,45 @@ purge_reg_equiv_notes ()
     }
 }
 
+/* Clear RTX_UNCHANGING_P flag of incoming argument MEMs.  */
+
+static void
+purge_mem_unchanging_flag (x)
+     rtx x;
+{
+  RTX_CODE code;
+  int i, j;
+  const char *fmt;
+
+  if (x == NULL_RTX)
+    return;
+
+  code = GET_CODE (x);
+
+  if (code == MEM)
+    {
+      if (RTX_UNCHANGING_P (x)
+         && (XEXP (x, 0) == current_function_internal_arg_pointer
+             || (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)))
+       RTX_UNCHANGING_P (x) = 0;
+      return;
+    }
+
+  /* Scan all subexpressions. */
+  fmt = GET_RTX_FORMAT (code);
+  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
+    {
+      if (*fmt == 'e')
+       purge_mem_unchanging_flag (XEXP (x, i));
+      else if (*fmt == 'E')
+       for (j = 0; j < XVECLEN (x, i); j++)
+         purge_mem_unchanging_flag (XVECEXP (x, i, j));
+    }
+}
+
 /* Replace the CALL_PLACEHOLDER with one of its children.  INSN should be
    the CALL_PLACEHOLDER insn; USE tells which child to use.  */
 
@@ -392,7 +514,7 @@ optimize_sibling_and_tail_recursive_calls ()
   /* We need cfg information to determine which blocks are succeeded
      only by the epilogue.  */
   find_basic_blocks (insns, max_reg_num (), 0);
-  cleanup_cfg (insns);
+  cleanup_cfg ();
 
   /* If there are no basic blocks, then there is nothing to do.  */
   if (n_basic_blocks == 0)
@@ -459,94 +581,41 @@ optimize_sibling_and_tail_recursive_calls ()
        {
          int sibcall = (XEXP (PATTERN (insn), 1) != NULL_RTX);
          int tailrecursion = (XEXP (PATTERN (insn), 2) != NULL_RTX);
-         basic_block succ_block, call_block;
-         rtx temp, hardret, softret;
-
-         /* We must be careful with stack slots which are live at
-            potential optimization sites.
-
-            ?!? This test is overly conservative and will be replaced.  */
-         if (frame_offset)
-           goto failure;
+         basic_block call_block = BLOCK_FOR_INSN (insn);
 
          /* alloca (until we have stack slot life analysis) inhibits
             sibling call optimizations, but not tail recursion.
-
-            Similarly if we have ADDRESSOF expressions.
-
             Similarly if we use varargs or stdarg since they implicitly
             may take the address of an argument.  */
-         if (current_function_calls_alloca || current_function_uses_addressof
+         if (current_function_calls_alloca
              || current_function_varargs || current_function_stdarg)
            sibcall = 0;
 
-         call_block = BLOCK_FOR_INSN (insn);
-
-         /* If the block has more than one successor, then we can not
-            perform sibcall or tail recursion optimizations.  */
-         if (call_block->succ == NULL
-             || call_block->succ->succ_next != NULL)
-           goto failure;
-
-         /* If the single successor is not the exit block, then we can not
-            perform sibcall or tail recursion optimizations. 
-
-            Note that this test combined with the previous is sufficient
-            to prevent tail call optimization in the presense of active
-            exception handlers.  */
-         succ_block = call_block->succ->dest;
-         if (succ_block != EXIT_BLOCK_PTR && succ_block != alternate_exit)
-           goto failure;
-
-         /* If the call was the end of the block, then we're OK.  */
-         temp = insn;
-         if (temp == call_block->end)
-           goto success;
-
-         /* Skip over copying from the call's return value pseudo into
-            this function's hard return register.  */
-         if (identify_call_return_value (PATTERN (insn), &hardret, &softret))
-           {
-             temp = skip_copy_to_return_value (temp, hardret, softret);
-             if (temp == call_block->end)
-               goto success;
-           }
-
-         /* Skip any stack adjustment.  */
-         temp = skip_stack_adjustment (temp);
-         if (temp == call_block->end)
-           goto success;
-
-         /* Skip over a CLOBBER of the return value (as a hard reg).  */
-         temp = skip_use_of_return_value (temp, CLOBBER);
-         if (temp == call_block->end)
-           goto success;
-
-         /* Skip over a USE of the return value (as a hard reg).  */
-         temp = skip_use_of_return_value (temp, USE);
-         if (temp == call_block->end)
-           goto success;
-
-         /* Skip over the JUMP_INSN at the end of the block.  */
-         temp = skip_jump_insn (temp);
-         if (GET_CODE (temp) == NOTE)
-           temp = next_nonnote_insn (temp);
-         if (temp == call_block->end)
-           goto success;
-
-         /* There are operations at the end of the block which we must
-            execute after returning from the function call.  So this call
-            can not be optimized.  */
-failure:
-         sibcall = 0, tailrecursion = 0;
-success:
+         /* See if there are any reasons we can't perform either sibling or
+            tail call optimizations.  We must be careful with stack slots
+            which are live at potential optimization sites.  ?!? The first
+            test is overly conservative and should be replaced.  */
+         if (frame_offset
+             /* Can't take address of local var if used by recursive call.  */
+             || current_function_uses_addressof
+             /* Can't if more than one successor or single successor is not
+                exit block.  These two tests prevent tail call optimization
+                in the presense of active exception handlers.  */
+             || call_block->succ == NULL
+             || call_block->succ->succ_next != NULL
+             || (call_block->succ->dest != EXIT_BLOCK_PTR
+                 && call_block->succ->dest != alternate_exit)
+             /* If this call doesn't end the block, there are operations at
+                the end of the block which we must execute after returning. */
+             || ! call_ends_block_p (insn, call_block->end))
+           sibcall = 0, tailrecursion = 0;
 
          /* Select a set of insns to implement the call and emit them.
             Tail recursion is the most efficient, so select it over
             a tail/sibling call.  */
-  
          if (sibcall)
            successful_sibling_call = 1;
+
          replaced_call_placeholder = 1;
          replace_call_placeholder (insn, 
                                    tailrecursion != 0 
@@ -557,18 +626,39 @@ success:
        }
     }
 
-  /* A sibling call sequence invalidates any REG_EQUIV notes made for
-     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. 
-
-     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.  */
   if (successful_sibling_call)
-    purge_reg_equiv_notes ();
+    {
+      rtx insn;
+
+      /* A sibling call sequence invalidates any REG_EQUIV notes made for
+        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. 
+
+        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.  */
+      purge_reg_equiv_notes ();
+
+      /* A sibling call sequence also may invalidate RTX_UNCHANGING_P
+        flag of some incoming arguments MEM RTLs, because it can write into
+        those slots.  We clear all those bits now.
+        
+        This is (slight) overkill, we could keep track of which arguments
+        we actually write into.  */
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       {
+         if (GET_CODE (insn) == NOTE)
+           {
+             if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
+               break;
+           }
+         else if (INSN_P (insn))
+           purge_mem_unchanging_flag (PATTERN (insn));
+       }
+    }
 
   /* There may have been NOTE_INSN_BLOCK_{BEGIN,END} notes in the 
      CALL_PLACEHOLDER alternatives that we didn't emit.  Rebuild the