OSDN Git Service

* config/mips/mips.c (save_restore_insns): Don't mark any register
[pf3gnuchains/gcc-fork.git] / gcc / integrate.c
index 1955c05..232f5fe 100644 (file)
@@ -1,24 +1,24 @@
-/* Procedure integration for GNU CC.
+/* Procedure integration for GCC.
    Copyright (C) 1988, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
    1999, 2000, 2001 Free Software Foundation, Inc.
    Contributed by Michael Tiemann (tiemann@cygnus.com)
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
 
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+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.  */
 
 #include "config.h"
 #include "system.h"
@@ -28,6 +28,7 @@ Boston, MA 02111-1307, USA.  */
 #include "tm_p.h"
 #include "regs.h"
 #include "flags.h"
+#include "debug.h"
 #include "insn-config.h"
 #include "expr.h"
 #include "output.h"
@@ -40,6 +41,7 @@ Boston, MA 02111-1307, USA.  */
 #include "intl.h"
 #include "loop.h"
 #include "params.h"
+#include "ggc.h"
 
 #include "obstack.h"
 #define        obstack_chunk_alloc     xmalloc
@@ -68,6 +70,20 @@ extern struct obstack *function_maybepermanent_obstack;
 #define FUNCTION_ATTRIBUTE_INLINABLE_P(FNDECL) 0
 #endif
 \f
+
+/* Private type used by {get/has}_func_hard_reg_initial_val.  */
+typedef struct initial_value_pair {
+  rtx hard_reg;
+  rtx pseudo;
+} initial_value_pair;
+typedef struct initial_value_struct {
+  int num_entries;
+  int max_entries;
+  initial_value_pair *entries;
+} initial_value_struct;
+
+static void setup_initial_hard_reg_value_integration PARAMS ((struct function *, struct inline_remap *));
+
 static rtvec initialize_for_inline     PARAMS ((tree));
 static void note_modified_parmregs     PARAMS ((rtx, rtx, void *));
 static void integrate_parm_decls       PARAMS ((tree, struct inline_remap *,
@@ -411,6 +427,13 @@ save_for_inline (fndecl)
 
   argvec = initialize_for_inline (fndecl);
 
+  /* Delete basic block notes created by early run of find_basic_block.
+     The notes would be later used by find_basic_blocks to reuse the memory
+     for basic_block structures on already freed obstack.  */
+  for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
+    if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_BASIC_BLOCK)
+      delete_insn (insn);
+
   /* If there are insns that copy parms from the stack into pseudo registers,
      those insns are not copied.  `expand_inline_function' must
      emit the correct code to handle such things.  */
@@ -650,7 +673,8 @@ expand_inline_function (fndecl, parms, target, ignore, type,
       arg = TREE_VALUE (actual);
       mode = TYPE_MODE (DECL_ARG_TYPE (formal));
 
-      if (mode != TYPE_MODE (TREE_TYPE (arg))
+      if (arg == error_mark_node
+         || mode != TYPE_MODE (TREE_TYPE (arg))
          /* If they are block mode, the types should match exactly.
             They don't match exactly if TREE_TYPE (FORMAL) == ERROR_MARK_NODE,
             which could happen if the parameter has incomplete type.  */
@@ -757,7 +781,7 @@ expand_inline_function (fndecl, parms, target, ignore, type,
 
   /* Allocate the structures we use to remap things.  */
 
-  map = (struct inline_remap *) xmalloc (sizeof (struct inline_remap));
+  map = (struct inline_remap *) xcalloc (1, sizeof (struct inline_remap));
   map->fndecl = fndecl;
 
   VARRAY_TREE_INIT (map->block_map, 10, "block_map");
@@ -806,7 +830,7 @@ expand_inline_function (fndecl, parms, target, ignore, type,
      insn that can be used as an insertion point.  */
   map->insns_at_start = get_last_insn ();
   if (map->insns_at_start == 0)
-    map->insns_at_start = emit_note (NULL_PTR, NOTE_INSN_DELETED);
+    map->insns_at_start = emit_note (NULL, NOTE_INSN_DELETED);
 
   map->regno_pointer_align = inl_f->emit->regno_pointer_align;
   map->x_regno_reg_rtx = inl_f->emit->x_regno_reg_rtx;
@@ -1082,9 +1106,39 @@ expand_inline_function (fndecl, parms, target, ignore, type,
       else
        map->reg_map[REGNO (loc)] = reg_to_map;
     }
+  else if (GET_CODE (loc) == CONCAT)
+    {
+      enum machine_mode departing_mode = TYPE_MODE (type);
+      enum machine_mode arriving_mode
+       = GET_MODE (DECL_RTL (DECL_RESULT (fndecl)));
+
+      if (departing_mode != arriving_mode)
+       abort ();
+      if (GET_CODE (XEXP (loc, 0)) != REG
+         || GET_CODE (XEXP (loc, 1)) != REG)
+       abort ();
+
+      /* Don't use MEMs as direct targets because on some machines
+        substituting a MEM for a REG makes invalid insns.
+        Let the combiner substitute the MEM if that is valid.  */
+      if (target == 0 || GET_CODE (target) != REG
+         || GET_MODE (target) != departing_mode)
+       target = gen_reg_rtx (departing_mode);
+
+      if (GET_CODE (target) != CONCAT)
+       abort ();
+
+      map->reg_map[REGNO (XEXP (loc, 0))] = XEXP (target, 0);
+      map->reg_map[REGNO (XEXP (loc, 1))] = XEXP (target, 1);
+    }
   else
     abort ();
 
+  /* Remap the exception handler data pointer from one to the other.  */
+  temp = get_exception_pointer (inl_f);
+  if (temp)
+    map->reg_map[REGNO (temp)] = get_exception_pointer (cfun);
+
   /* Initialize label_map.  get_label_from_map will actually make
      the labels.  */
   memset ((char *) &map->label_map[min_labelno], 0,
@@ -1128,6 +1182,9 @@ expand_inline_function (fndecl, parms, target, ignore, type,
   if (inl_f->calls_alloca)
     emit_stack_save (SAVE_BLOCK, &stack_save, NULL_RTX);
 
+  /* Map pseudos used for initial hard reg values.  */
+  setup_initial_hard_reg_value_integration (inl_f, map);
+
   /* Now copy the insns one by one.  */
   copy_insn_list (insns, map, static_chain_value);
 
@@ -1406,7 +1463,7 @@ copy_insn_list (insns, map, static_chain_value)
            {
 #ifdef HAVE_cc0
              /* If the previous insn set cc0 for us, delete it.  */
-             if (sets_cc0_p (PREV_INSN (copy)))
+             if (only_sets_cc0_p (PREV_INSN (copy)))
                delete_insn (PREV_INSN (copy));
 #endif
 
@@ -1465,7 +1522,7 @@ copy_insn_list (insns, map, static_chain_value)
          copy = emit_call_insn (pattern);
 
          SIBLING_CALL_P (copy) = SIBLING_CALL_P (insn);
-         CONST_CALL_P (copy) = CONST_CALL_P (insn);
+         CONST_OR_PURE_CALL_P (copy) = CONST_OR_PURE_CALL_P (insn);
 
          /* Because the USAGE information potentially contains objects other
             than hard registers, we need to copy it.  */
@@ -1498,21 +1555,23 @@ copy_insn_list (insns, map, static_chain_value)
          break;
 
        case NOTE:
+         if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL)
+           {
+             copy = emit_label (get_label_from_map (map,
+                                                   CODE_LABEL_NUMBER (insn)));
+             map->const_age++;
+             break;
+           }
+
          /* NOTE_INSN_FUNCTION_END and NOTE_INSN_FUNCTION_BEG are
             discarded because it is important to have only one of
             each in the current function.
 
-            NOTE_INSN_DELETED notes aren't useful.
-
-            NOTE_INSN_BASIC_BLOCK is discarded because the saved bb
-            pointer (which will soon be dangling) confuses flow's
-            attempts to preserve bb structures during the compilation
-            of a function.  */
+            NOTE_INSN_DELETED notes aren't useful.  */
 
          if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END
              && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG
-             && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED
-             && NOTE_LINE_NUMBER (insn) != NOTE_INSN_BASIC_BLOCK)
+             && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED)
            {
              copy = emit_note (NOTE_SOURCE_FILE (insn),
                                NOTE_LINE_NUMBER (insn));
@@ -1535,6 +1594,11 @@ copy_insn_list (insns, map, static_chain_value)
                  else
                    NOTE_BLOCK (copy) = *mapped_block_p;
                }
+             else if (copy
+                      && NOTE_LINE_NUMBER (copy) == NOTE_INSN_EXPECTED_VALUE)
+               NOTE_EXPECTED_VALUE (copy)
+                 = copy_rtx_and_substitute (NOTE_EXPECTED_VALUE (insn),
+                                            map, 0);
            }
          else
            copy = 0;
@@ -1752,15 +1816,7 @@ copy_rtx_and_substitute (orig, map, for_lhs)
        {
          /* Some hard registers are also mapped,
             but others are not translated.  */
-         if (map->reg_map[regno] != 0
-             /* We shouldn't usually have reg_map set for return
-                register, but it may happen if we have leaf-register
-                remapping and the return register is used in one of
-                the calling sequences of a call_placeholer.  In this
-                case, we'll end up with a reg_map set for this
-                register, but we don't want to use for registers
-                marked as return values.  */
-             && ! REG_FUNCTION_VALUE_P (orig))
+         if (map->reg_map[regno] != 0)
            return map->reg_map[regno];
 
          /* If this is the virtual frame pointer, make space in current
@@ -1878,9 +1934,9 @@ copy_rtx_and_substitute (orig, map, for_lhs)
          if (map->integrating && regno < FIRST_PSEUDO_REGISTER
              && LEAF_REGISTERS[regno] && LEAF_REG_REMAP (regno) != regno)
            {
-             temp = gen_rtx_REG (mode, regno);
-             map->reg_map[regno] = temp;
-             return temp;
+             if (!map->leaf_reg_map[regno][mode])
+               map->leaf_reg_map[regno][mode] = gen_rtx_REG (mode, regno);
+             return map->leaf_reg_map[regno][mode]; 
            }
 #endif
          else
@@ -1904,35 +1960,9 @@ copy_rtx_and_substitute (orig, map, for_lhs)
 
     case SUBREG:
       copy = copy_rtx_and_substitute (SUBREG_REG (orig), map, for_lhs);
-      /* SUBREG is ordinary, but don't make nested SUBREGs.  */
-      if (GET_CODE (copy) == SUBREG)
-       {
-         int final_offset = SUBREG_BYTE (orig) + SUBREG_BYTE (copy);
-
-         /* When working with SUBREGs the rule is that the byte
-            offset must be a multiple of the SUBREG's mode.  */
-         final_offset = (final_offset / GET_MODE_SIZE (GET_MODE (orig)));
-         final_offset = (final_offset * GET_MODE_SIZE (GET_MODE (orig)));
-         return gen_rtx_SUBREG (GET_MODE (orig), SUBREG_REG (copy),
-                                final_offset);
-       }
-      else if (GET_CODE (copy) == CONCAT)
-       {
-         rtx retval = subreg_realpart_p (orig) ? XEXP (copy, 0) : XEXP (copy, 1);
-         int final_offset;
-
-         if (GET_MODE (retval) == GET_MODE (orig))
-           return retval;
-         
-         final_offset = SUBREG_BYTE (orig) %
-                        GET_MODE_UNIT_SIZE (GET_MODE (SUBREG_REG (orig)));
-         final_offset = (final_offset / GET_MODE_SIZE (GET_MODE (orig)));
-         final_offset = (final_offset * GET_MODE_SIZE (GET_MODE (orig)));
-         return gen_rtx_SUBREG (GET_MODE (orig), retval, final_offset);
-       }
-      else
-       return gen_rtx_SUBREG (GET_MODE (orig), copy,
-                              SUBREG_BYTE (orig));
+      return simplify_gen_subreg (GET_MODE (orig), copy,
+                                 GET_MODE (SUBREG_REG (orig)),
+                                 SUBREG_BYTE (orig));
 
     case ADDRESSOF:
       copy = gen_rtx_ADDRESSOF (mode,
@@ -1970,18 +2000,18 @@ copy_rtx_and_substitute (orig, map, for_lhs)
        copy = SUBREG_REG (copy);
       return gen_rtx_fmt_e (code, VOIDmode, copy);
 
+    /* We need to handle "deleted" labels that appear in the DECL_RTL
+       of a LABEL_DECL.  */
+    case NOTE:
+      if (NOTE_LINE_NUMBER (orig) != NOTE_INSN_DELETED_LABEL)
+       break;
+
+      /* ... FALLTHRU ...  */
     case CODE_LABEL:
       LABEL_PRESERVE_P (get_label_from_map (map, CODE_LABEL_NUMBER (orig)))
        = LABEL_PRESERVE_P (orig);
       return get_label_from_map (map, CODE_LABEL_NUMBER (orig));
 
-    /* We need to handle "deleted" labels that appear in the DECL_RTL
-       of a LABEL_DECL.  */
-    case NOTE:
-      if (NOTE_LINE_NUMBER (orig) == NOTE_INSN_DELETED_LABEL)
-       return map->insn_map[INSN_UID (orig)];
-      break;
-
     case LABEL_REF:
       copy
        = gen_rtx_LABEL_REF
@@ -2414,19 +2444,14 @@ subst_constants (loc, insn, map, memonly)
             valid.  We handle two cases: extracting a full word in an
             integral mode and extracting the low part.  */
          subst_constants (&inner, NULL_RTX, map, 0);
-
-         if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
-             && GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD
-             && GET_MODE (SUBREG_REG (x)) != VOIDmode)
-           new = operand_subword (inner, SUBREG_BYTE (x) / UNITS_PER_WORD,
-                                  0, GET_MODE (SUBREG_REG (x)));
-
-         cancel_changes (num_changes);
-         if (new == 0 && subreg_lowpart_p (x))
-           new = gen_lowpart_common (GET_MODE (x), inner);
+         new = simplify_gen_subreg (GET_MODE (x), inner,
+                                    GET_MODE (SUBREG_REG (x)),
+                                    SUBREG_BYTE (x));
 
          if (new)
            validate_change (insn, loc, new, 1);
+         else
+           cancel_changes (num_changes);
 
          return;
        }
@@ -2851,6 +2876,7 @@ output_inline_function (fndecl)
 {
   struct function *old_cfun = cfun;
   enum debug_info_type old_write_symbols = write_symbols;
+  struct gcc_debug_hooks *old_debug_hooks = debug_hooks;
   struct function *f = DECL_SAVED_INSNS (fndecl);
 
   cfun = f;
@@ -2864,20 +2890,151 @@ output_inline_function (fndecl)
 
   /* If requested, suppress debugging information.  */
   if (f->no_debugging_symbols)
-    write_symbols = NO_DEBUG;
+    {
+      write_symbols = NO_DEBUG;
+      debug_hooks = &do_nothing_debug_hooks;
+    }
 
   /* Do any preparation, such as emitting abstract debug info for the inline
      before it gets mangled by optimization.  */
-  note_outlining_of_inline_function (fndecl);
+  (*debug_hooks->outlining_inline_function) (fndecl);
 
   /* Compile this function all the way down to assembly code.  */
   rest_of_compilation (fndecl);
 
-  /* We can't inline this anymore.  */
-  f->inlinable = 0;
+  /* We can't inline this anymore; rest_of_compilation destroyed the
+     data structures describing the function.  */
   DECL_INLINE (fndecl) = 0;
+  DECL_SAVED_INSNS (fndecl) = 0;
 
   cfun = old_cfun;
   current_function_decl = old_cfun ? old_cfun->decl : 0;
   write_symbols = old_write_symbols;
+  debug_hooks = old_debug_hooks;
+}
+
+\f
+/* Functions to keep track of the values hard regs had at the start of
+   the function.  */
+
+rtx
+has_func_hard_reg_initial_val (fun, reg)
+     struct function *fun;
+     rtx reg;
+{
+  struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
+  int i;
+
+  if (ivs == 0)
+    return NULL_RTX;
+
+  for (i = 0; i < ivs->num_entries; i++)
+    if (rtx_equal_p (ivs->entries[i].hard_reg, reg))
+      return ivs->entries[i].pseudo;
+
+  return NULL_RTX;
+}
+
+rtx
+get_func_hard_reg_initial_val (fun, reg)
+     struct function *fun;
+     rtx reg;
+{
+  struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
+  rtx rv = has_func_hard_reg_initial_val (fun, reg);
+
+  if (rv)
+    return rv;
+
+  if (ivs == 0)
+    {
+      fun->hard_reg_initial_vals = (void *) xmalloc (sizeof (initial_value_struct));
+      ivs = fun->hard_reg_initial_vals;
+      ivs->num_entries = 0;
+      ivs->max_entries = 5;
+      ivs->entries = (initial_value_pair *) xmalloc (5 * sizeof (initial_value_pair));
+    }
+
+  if (ivs->num_entries >= ivs->max_entries)
+    {
+      ivs->max_entries += 5;
+      ivs->entries = 
+       (initial_value_pair *) xrealloc (ivs->entries,
+                                        ivs->max_entries
+                                        * sizeof (initial_value_pair));
+    }
+
+  ivs->entries[ivs->num_entries].hard_reg = reg;
+  ivs->entries[ivs->num_entries].pseudo = gen_reg_rtx (GET_MODE (reg));
+
+  return ivs->entries[ivs->num_entries++].pseudo;
+}
+
+rtx
+get_hard_reg_initial_val (mode, regno)
+     enum machine_mode mode;
+     int regno;
+{
+  return get_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno));
+}
+
+rtx
+has_hard_reg_initial_val (mode, regno)
+     enum machine_mode mode;
+     int regno;
+{
+  return has_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno));
+}
+
+void
+mark_hard_reg_initial_vals (fun)
+     struct function *fun;
+{
+  struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
+  int i;
+
+  if (ivs == 0)
+    return;
+
+  for (i = 0; i < ivs->num_entries; i ++)
+    {
+      ggc_mark_rtx (ivs->entries[i].hard_reg);
+      ggc_mark_rtx (ivs->entries[i].pseudo);
+    }
+}
+
+static void
+setup_initial_hard_reg_value_integration (inl_f, remap)
+     struct function *inl_f;
+     struct inline_remap *remap;
+{
+  struct initial_value_struct *ivs = inl_f->hard_reg_initial_vals;
+  int i;
+
+  if (ivs == 0)
+    return;
+
+  for (i = 0; i < ivs->num_entries; i ++)
+    remap->reg_map[REGNO (ivs->entries[i].pseudo)]
+      = get_func_hard_reg_initial_val (cfun, ivs->entries[i].hard_reg);
+}
+
+
+void
+emit_initial_value_sets ()
+{
+  struct initial_value_struct *ivs = cfun->hard_reg_initial_vals;
+  int i;
+  rtx seq;
+
+  if (ivs == 0)
+    return;
+
+  start_sequence ();
+  for (i = 0; i < ivs->num_entries; i++)
+    emit_move_insn (ivs->entries[i].pseudo, ivs->entries[i].hard_reg);
+  seq = get_insns ();
+  end_sequence ();
+
+  emit_insns_after (seq, get_insns ());
 }