OSDN Git Service

* expr.c (move_block_from_reg): Try using an integral mov operation first.
[pf3gnuchains/gcc-fork.git] / gcc / integrate.c
index ff29746..693304a 100644 (file)
@@ -1,5 +1,5 @@
 /* Procedure integration for GNU CC.
-   Copyright (C) 1988, 1991, 1993, 1994, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1988, 91, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
    Contributed by Michael Tiemann (tiemann@cygnus.com)
 
 This file is part of GNU CC.
@@ -16,7 +16,8 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include <stdio.h>
@@ -24,6 +25,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "config.h"
 #include "rtl.h"
 #include "tree.h"
+#include "regs.h"
 #include "flags.h"
 #include "insn-config.h"
 #include "insn-flags.h"
@@ -31,6 +33,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "output.h"
 #include "integrate.h"
 #include "real.h"
+#include "except.h"
 #include "function.h"
 #include "bytecode.h"
 
@@ -65,6 +68,7 @@ static void note_modified_parmregs PROTO((rtx, rtx));
 static rtx copy_for_inline     PROTO((rtx));
 static void integrate_parm_decls PROTO((tree, struct inline_remap *, rtvec));
 static void integrate_decl_tree        PROTO((tree, int, struct inline_remap *));
+static void save_constants_in_decl_trees PROTO ((tree));
 static void subst_constants    PROTO((rtx *, rtx, struct inline_remap *));
 static void restore_constants  PROTO((rtx *));
 static void set_block_origin_self PROTO((tree));
@@ -87,6 +91,7 @@ function_cannot_inline_p (fndecl)
   int max_insns = INTEGRATE_THRESHOLD (fndecl);
   register int ninsns = 0;
   register tree parms;
+  rtx result;
 
   /* No inlines with varargs.  `grokdeclarator' gives a warning
      message about that if `inline' is specified.  This code
@@ -106,14 +111,6 @@ function_cannot_inline_p (fndecl)
     return "function too large to be inline";
 
 #if 0
-  /* Large stacks are OK now that inlined functions can share them.  */
-  /* Don't inline functions with large stack usage,
-     since they can make other recursive functions burn up stack.  */
-  if (!DECL_INLINE (fndecl) && get_frame_size () > 100)
-    return "function stack frame for inlining";
-#endif
-
-#if 0
   /* Don't inline functions which do not specify a function prototype and
      have BLKmode argument or take the address of a parameter.  */
   for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
@@ -151,12 +148,11 @@ function_cannot_inline_p (fndecl)
 
   if (!DECL_INLINE (fndecl) && get_max_uid () > max_insns)
     {
-      for (ninsns = 0, insn = get_first_nonparm_insn (); insn && ninsns < max_insns;
+      for (ninsns = 0, insn = get_first_nonparm_insn ();
+          insn && ninsns < max_insns;
           insn = NEXT_INSN (insn))
-       {
-         if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
-           ninsns++;
-       }
+       if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+         ninsns++;
 
       if (ninsns >= max_insns)
        return "function too large to be inline";
@@ -176,6 +172,24 @@ function_cannot_inline_p (fndecl)
   if (current_function_has_nonlocal_goto)
     return "function with nonlocal goto cannot be inline";
 
+  /* This is a hack, until the inliner is taught about eh regions at
+     the start of the function.  */
+  for (insn = get_insns ();
+       insn &&
+         ! (GET_CODE (insn) == NOTE
+           && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG);
+       insn = NEXT_INSN (insn))
+    {
+      if (insn && GET_CODE (insn) == NOTE
+         && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
+       return "function with complex parameters cannot be inline";
+    }
+
+  /* We can't inline functions that return a PARALLEL rtx.  */
+  result = DECL_RTL (DECL_RESULT (fndecl));
+  if (result && GET_CODE (result) == PARALLEL)
+    return "inline functions not supported for this return value type";
+
   return 0;
 }
 \f
@@ -315,10 +329,12 @@ initialize_for_inline (fndecl, min_labelno, max_labelno, max_reg, copy)
      the size of the incoming stack area for parameters,
      the number of bytes popped on return,
      the stack slot list,
+     the labels that are forced to exist,
      some flags that are used to restore compiler globals,
      the value of current_function_outgoing_args_size,
      the original argument vector,
-     and the original DECL_INITIAL.  */
+     the original DECL_INITIAL,
+     and pointers to the table of psuedo regs, pointer flags, and alignment. */
 
   return gen_inline_header_rtx (NULL_RTX, NULL_RTX, min_labelno, max_labelno,
                                max_parm_reg, max_reg,
@@ -326,7 +342,9 @@ initialize_for_inline (fndecl, min_labelno, max_labelno, max_reg, copy)
                                current_function_pops_args,
                                stack_slot_list, forced_labels, function_flags,
                                current_function_outgoing_args_size,
-                               arg_vector, (rtx) DECL_INITIAL (fndecl));
+                               arg_vector, (rtx) DECL_INITIAL (fndecl),
+                               (rtvec) regno_reg_rtx, regno_pointer_flag,
+                               regno_pointer_align);
 }
 
 /* Subroutine for `save_for_inline{copying,nocopy}'.  Finishes up the
@@ -338,7 +356,7 @@ finish_inline (fndecl, head)
      tree fndecl;
      rtx head;
 {
-  NEXT_INSN (head) = get_first_nonparm_insn ();
+  FIRST_FUNCTION_INSN (head) = get_first_nonparm_insn ();
   FIRST_PARM_INSN (head) = get_insns ();
   DECL_SAVED_INSNS (fndecl) = head;
   DECL_FRAME_SIZE (fndecl) = get_frame_size ();
@@ -394,9 +412,10 @@ save_for_inline_copying (fndecl)
   int max_reg;
   int max_uid;
   rtx first_nonparm_insn;
+  char *new, *new1;
 
   /* Make and emit a return-label if we have not already done so. 
-     Do this before recording the bounds on label numbers. */
+     Do this before recording the bounds on label numbers.  */
 
   if (return_label == 0)
     {
@@ -433,6 +452,10 @@ save_for_inline_copying (fndecl)
              save_constants (&REG_NOTES (insn));
          }
 
+      /* Also scan all decls, and replace any constant pool references with the
+        actual constant.  */
+      save_constants_in_decl_trees (DECL_INITIAL (fndecl));
+
       /* Clear out the constant pool so that we can recreate it with the
         copied constants below.  */
       init_const_rtx_hash_table ();
@@ -472,16 +495,20 @@ save_for_inline_copying (fndecl)
      Make these new rtx's now, and install them in regno_reg_rtx, so they
      will be the official pseudo-reg rtx's for the rest of compilation.  */
 
-  reg_map = (rtx *) alloca ((max_reg + 1) * sizeof (rtx));
+  reg_map = (rtx *) savealloc (regno_pointer_flag_length * sizeof (rtx));
 
   len = sizeof (struct rtx_def) + (GET_RTX_LENGTH (REG) - 1) * sizeof (rtunion);
   for (i = max_reg - 1; i > LAST_VIRTUAL_REGISTER; i--)
     reg_map[i] = (rtx)obstack_copy (function_maybepermanent_obstack,
                                    regno_reg_rtx[i], len);
 
-  bcopy ((char *) (reg_map + LAST_VIRTUAL_REGISTER + 1),
-        (char *) (regno_reg_rtx + LAST_VIRTUAL_REGISTER + 1),
-        (max_reg - (LAST_VIRTUAL_REGISTER + 1)) * sizeof (rtx));
+  regno_reg_rtx = reg_map;
+
+  /* Put copies of all the virtual register rtx into the new regno_reg_rtx.  */
+  regno_reg_rtx[VIRTUAL_INCOMING_ARGS_REGNUM] = virtual_incoming_args_rtx;
+  regno_reg_rtx[VIRTUAL_STACK_VARS_REGNUM] = virtual_stack_vars_rtx;
+  regno_reg_rtx[VIRTUAL_STACK_DYNAMIC_REGNUM] = virtual_stack_dynamic_rtx;
+  regno_reg_rtx[VIRTUAL_OUTGOING_ARGS_REGNUM] = virtual_outgoing_args_rtx;
 
   /* Likewise each label rtx must have a unique rtx as its copy.  */
 
@@ -559,6 +586,16 @@ save_for_inline_copying (fndecl)
              NOTE_SOURCE_FILE (insn) = (char *) copy;
              NOTE_SOURCE_FILE (copy) = 0;
            }
+         if (NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_BEG
+             || NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_END)
+           {
+             /* We have to forward these both to match the new exception
+                region.  */
+             NOTE_BLOCK_NUMBER (copy)
+               = CODE_LABEL_NUMBER (label_map[NOTE_BLOCK_NUMBER (copy)]);
+             
+           }
+         RTX_INTEGRATED_P (copy) = RTX_INTEGRATED_P (insn);
          break;
 
        case INSN:
@@ -608,6 +645,15 @@ save_for_inline_copying (fndecl)
 
   finish_inline (fndecl, head);
 
+  /* Make new versions of the register tables.  */
+  new = (char *) savealloc (regno_pointer_flag_length);
+  bcopy (regno_pointer_flag, new, regno_pointer_flag_length);
+  new1 = (char *) savealloc (regno_pointer_flag_length);
+  bcopy (regno_pointer_align, new1, regno_pointer_flag_length);
+
+  regno_pointer_flag = new;
+  regno_pointer_align = new1;
+
   set_new_first_and_last_insn (first_insn, last_insn);
 }
 
@@ -668,7 +714,7 @@ copy_decl_tree (block)
   BLOCK_VARS (t) = vars;
   BLOCK_SUBBLOCKS (t) = nreverse (subblocks);
   /* If the BLOCK being cloned is already marked as having been instantiated
-     from something else, then leave that `origin' marking alone.  Elsewise,
+     from something else, then leave that `origin' marking alone.  Otherwise,
      mark the clone as having originated from the BLOCK we are cloning.  */
   if (BLOCK_ABSTRACT_ORIGIN (t) == NULL_TREE)
     BLOCK_ABSTRACT_ORIGIN (t) = block;
@@ -706,10 +752,10 @@ copy_decl_rtls (block)
    modified.  In the second case, FNDECL is used for the last time,
    so modifying the rtl is not a problem.
 
-   ??? Actually, we do not verify that FNDECL is not inline expanded
-   by other functions which must also be written down at the end
-   of compilation.  We could set flag_no_inline to nonzero when
-   the time comes to write down such functions.  */
+   We don't have to worry about FNDECL being inline expanded by
+   other functions which are written at the end of compilation
+   because flag_no_inline is turned on when we begin writing
+   functions at the end of compilation.  */
 
 void
 save_for_inline_nocopy (fndecl)
@@ -779,6 +825,10 @@ save_for_inline_nocopy (fndecl)
        }
     }
 
+  /* Also scan all decls, and replace any constant pool references with the
+     actual constant.  */
+  save_constants_in_decl_trees (DECL_INITIAL (fndecl));
+
   /* We have now allocated all that needs to be allocated permanently
      on the rtx obstack.  Set our high-water mark, so that we
      can free the rest of this when the time comes.  */
@@ -792,8 +842,8 @@ save_for_inline_nocopy (fndecl)
    pool.  Replace each with a CONST that has the mode of the original
    constant, contains the constant, and has RTX_INTEGRATED_P set.
    Similarly, constant pool addresses not enclosed in a MEM are replaced
-   with an ADDRESS rtx which also gives the constant, mode, and has
-   RTX_INTEGRATED_P set.  */
+   with an ADDRESS and CONST rtx which also gives the constant, its
+   mode, the mode of the address, and has RTX_INTEGRATED_P set.  */
 
 static void
 save_constants (px)
@@ -832,7 +882,9 @@ save_constants (px)
   else if (GET_CODE (x) == SYMBOL_REF
           && CONSTANT_POOL_ADDRESS_P (x))
     {
-      *px = gen_rtx (ADDRESS, get_pool_mode (x), get_pool_constant (x));
+      *px = gen_rtx (ADDRESS, GET_MODE (x),
+                    gen_rtx (CONST, get_pool_mode (x),
+                             get_pool_constant (x)));
       save_constants (&XEXP (*px, 0));
       RTX_INTEGRATED_P (*px) = 1;
     }
@@ -899,6 +951,7 @@ copy_for_inline (orig)
      rtx orig;
 {
   register rtx x = orig;
+  register rtx new;
   register int i;
   register enum rtx_code code;
   register char *format_ptr;
@@ -944,9 +997,8 @@ copy_for_inline (orig)
       /* Get constant pool entry, but access in different mode.  */
       if (RTX_INTEGRATED_P (x))
        {
-         rtx new
-           = force_const_mem (GET_MODE (SUBREG_REG (x)),
-                              copy_for_inline (XEXP (SUBREG_REG (x), 0)));
+         new = force_const_mem (GET_MODE (SUBREG_REG (x)),
+                                copy_for_inline (XEXP (SUBREG_REG (x), 0)));
 
          PUT_MODE (new, GET_MODE (x));
          return validize_mem (new);
@@ -959,8 +1011,16 @@ copy_for_inline (orig)
       if (! RTX_INTEGRATED_P (x))
        abort ();
 
-      return XEXP (force_const_mem (GET_MODE (x),
-                                   copy_for_inline (XEXP (x, 0))), 0);
+      new = force_const_mem (GET_MODE (XEXP (x, 0)),
+                            copy_for_inline (XEXP (XEXP (x, 0), 0)));
+      new = XEXP (new, 0);
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+      if (GET_MODE (new) != GET_MODE (x))
+       new = convert_memory_address (GET_MODE (x), new);
+#endif
+
+      return new;
 
     case ASM_OPERANDS:
       /* If a single asm insn contains multiple output operands
@@ -1104,7 +1164,7 @@ copy_for_inline (orig)
            {
              register int j;
 
-             XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0));
+             XVEC (x, i) = gen_rtvec_vv (XVECLEN (x, i), XVEC (x, i)->elem);
              for (j = 0; j < XVECLEN (x, i); j++)
                XVECEXP (x, i, j)
                  = copy_for_inline (XVECEXP (x, i, j));
@@ -1150,7 +1210,8 @@ int global_const_equiv_map_size;
    else an rtx for where the value is stored.  */
 
 rtx
-expand_inline_function (fndecl, parms, target, ignore, type, structure_value_addr)
+expand_inline_function (fndecl, parms, target, ignore, type,
+                       structure_value_addr)
      tree fndecl, parms;
      rtx target;
      int ignore;
@@ -1189,11 +1250,9 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
      passed.  Since the appropriate conversions or default promotions have
      already been applied, the machine modes should match exactly.  */
 
-  for (formal = DECL_ARGUMENTS (fndecl),
-       actual = parms;
+  for (formal = DECL_ARGUMENTS (fndecl), actual = parms;
        formal;
-       formal = TREE_CHAIN (formal),
-       actual = TREE_CHAIN (actual))
+       formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual))
     {
       tree arg;
       enum machine_mode mode;
@@ -1202,13 +1261,15 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
        return (rtx) (HOST_WIDE_INT) -1;
 
       arg = TREE_VALUE (actual);
-      mode= TYPE_MODE (DECL_ARG_TYPE (formal));
+      mode = TYPE_MODE (DECL_ARG_TYPE (formal));
 
       if (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.  */
-         || (mode == BLKmode && TREE_TYPE (arg) != TREE_TYPE (formal)))
+         || (mode == BLKmode
+             && (TYPE_MAIN_VARIANT (TREE_TYPE (arg))
+                 != TYPE_MAIN_VARIANT (TREE_TYPE (formal)))))
        return (rtx) (HOST_WIDE_INT) -1;
     }
 
@@ -1302,6 +1363,12 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
                 handle SUBREGs in addresses.  */
              || (GET_CODE (arg_vals[i]) == SUBREG)))
        arg_vals[i] = copy_to_mode_reg (GET_MODE (loc), arg_vals[i]);
+
+      if (arg_vals[i] != 0 && GET_CODE (arg_vals[i]) == REG
+         && TREE_CODE (TREE_TYPE (formal)) == POINTER_TYPE)
+       mark_reg_pointer (arg_vals[i],
+                         (TYPE_ALIGN (TREE_TYPE (TREE_TYPE (formal)))
+                          / BITS_PER_UNIT));
     }
        
   /* Allocate the structures we use to remap things.  */
@@ -1352,6 +1419,9 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
      and argument memory blocks.  */
   map->insns_at_start = get_last_insn ();
 
+  map->regno_pointer_flag = INLINE_REGNO_POINTER_FLAG (header);
+  map->regno_pointer_align = INLINE_REGNO_POINTER_ALIGN (header);
+
   /* Update the outgoing argument size to allow for those in the inlined
      function.  */
   if (OUTGOING_ARGS_SIZE (header) > current_function_outgoing_args_size)
@@ -1436,7 +1506,7 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
             that flag set if it is a register.
 
             Also, don't allow hard registers here; they might not be valid
-            when substituted into insns. */
+            when substituted into insns.  */
 
          if ((GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG)
              || (GET_CODE (copy) == REG && REG_USERVAR_P (loc)
@@ -1467,7 +1537,7 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
             that flag set if it is a register.
 
             Also, don't allow hard registers here; they might not be valid
-            when substituted into insns. */
+            when substituted into insns.  */
          rtx locreal = gen_realpart (GET_MODE (XEXP (loc, 0)), loc);
          rtx locimag = gen_imagpart (GET_MODE (XEXP (loc, 0)), loc);
          rtx copyreal = gen_realpart (GET_MODE (locreal), copy);
@@ -1569,7 +1639,8 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
 
       if (GET_CODE (XEXP (loc, 0)) == REG)
        {
-         temp = force_reg (Pmode, structure_value_addr);
+         temp = force_reg (Pmode,
+                           force_operand (structure_value_addr, NULL_RTX));
          map->reg_map[REGNO (XEXP (loc, 0))] = temp;
          if ((CONSTANT_P (structure_value_addr)
               || (GET_CODE (structure_value_addr) == PLUS
@@ -1618,7 +1689,21 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
         avoid machine mode mismatch when we substitute INLINE_TARGET.
         But TARGET is what we will return to the caller.  */
       if (arriving_mode != departing_mode)
-       reg_to_map = gen_rtx (SUBREG, arriving_mode, target, 0);
+       {
+         /* Avoid creating a paradoxical subreg wider than
+            BITS_PER_WORD, since that is illegal.  */
+         if (GET_MODE_BITSIZE (arriving_mode) > BITS_PER_WORD)
+           {
+             if (!TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (departing_mode),
+                                         GET_MODE_BITSIZE (arriving_mode)))
+               /* Maybe could be handled by using convert_move () ?  */
+               abort ();
+             reg_to_map = gen_reg_rtx (arriving_mode);
+             target = gen_lowpart (departing_mode, reg_to_map);
+           }
+         else
+           reg_to_map = gen_rtx (SUBREG, arriving_mode, target, 0);
+       }
       else
        reg_to_map = target;
 
@@ -1629,6 +1714,8 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
       else
        map->reg_map[REGNO (loc)] = reg_to_map;
     }
+  else
+    abort ();
 
   /* Make new label equivalences for the labels in the called function.  */
   for (i = min_labelno; i < max_labelno; i++)
@@ -1698,6 +1785,11 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
                  SET_DEST (new_set)
                    = gen_reg_rtx (GET_MODE (SET_DEST (new_set)));
                }
+             /* If the source and destination are the same and it
+                has a note on it, keep the insn.  */
+             else if (rtx_equal_p (SET_DEST (set), SET_SRC (set))
+                      && REG_NOTES (insn) != 0)
+               copy = emit_insn (copy_rtx_and_substitute (pattern, map));
              else
                break;
            }
@@ -1755,7 +1847,9 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
          break;
 
        case JUMP_INSN:
-         if (GET_CODE (PATTERN (insn)) == RETURN)
+         if (GET_CODE (PATTERN (insn)) == RETURN
+             || (GET_CODE (PATTERN (insn)) == PARALLEL
+                 && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == RETURN))
            {
              if (local_return_label == 0)
                local_return_label = gen_label_rtx ();
@@ -1836,7 +1930,18 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
          if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END
              && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG
              && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED)
-           copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn));
+           {
+             copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn));
+             if (copy && (NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_BEG
+                          || NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_END))
+               {
+                 rtx label = map->label_map[NOTE_BLOCK_NUMBER (copy)];
+
+                 /* We have to forward these both to match the new exception
+                    region.  */
+                 NOTE_BLOCK_NUMBER (copy) = CODE_LABEL_NUMBER (label);
+               }
+           }
          else
            copy = 0;
          break;
@@ -1894,6 +1999,16 @@ expand_inline_function (fndecl, parms, target, ignore, type, structure_value_add
   BLOCK_ABSTRACT_ORIGIN (block) = (DECL_ABSTRACT_ORIGIN (fndecl) == NULL
                                   ? fndecl : DECL_ABSTRACT_ORIGIN (fndecl));
   poplevel (0, 0, 0);
+
+  /* Must mark the line number note after inlined functions as a repeat, so
+     that the test coverage code can avoid counting the call twice.  This
+     just tells the code to ignore the immediately following line note, since
+     there already exists a copy of this note before the expanded inline call.
+     This line number note is still needed for debugging though, so we can't
+     delete it.  */
+  if (flag_test_coverage)
+    emit_note (0, NOTE_REPEATED_LINE_NUMBER);
+
   emit_line_note (input_filename, lineno);
 
   if (structure_value_addr)
@@ -2007,6 +2122,23 @@ integrate_decl_tree (let, level, map)
        }
     }
 }
+
+/* Given a BLOCK node LET, search for all DECL_RTL fields, and pass them
+   through save_constants.  */
+
+static void
+save_constants_in_decl_trees (let)
+     tree let;
+{
+  tree t;
+
+  for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t))
+    if (DECL_RTL (t) != 0)
+      save_constants (&DECL_RTL (t));
+
+  for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t))
+    save_constants_in_decl_trees (t);
+}
 \f
 /* Create a new copy of an rtx.
    Recursively copies the operands of the rtx,
@@ -2064,22 +2196,31 @@ copy_rtx_and_substitute (orig, map)
            {
              rtx loc, seq;
              int size = DECL_FRAME_SIZE (map->fndecl);
-             int rounded;
 
+#ifdef FRAME_GROWS_DOWNWARD
+             /* In this case, virtual_stack_vars_rtx points to one byte
+                higher than the top of the frame area.  So make sure we
+                allocate a big enough chunk to keep the frame pointer
+                aligned like a real one.  */
+             size = CEIL_ROUND (size, BIGGEST_ALIGNMENT / BITS_PER_UNIT);
+#endif
              start_sequence ();
              loc = assign_stack_temp (BLKmode, size, 1);
              loc = XEXP (loc, 0);
 #ifdef FRAME_GROWS_DOWNWARD
              /* In this case, virtual_stack_vars_rtx points to one byte
                 higher than the top of the frame area.  So compute the offset
-                to one byte higher than our substitute frame.
-                Keep the fake frame pointer aligned like a real one.  */
-             rounded = CEIL_ROUND (size, BIGGEST_ALIGNMENT / BITS_PER_UNIT);
-             loc = plus_constant (loc, rounded);
+                to one byte higher than our substitute frame.  */
+             loc = plus_constant (loc, size);
 #endif
              map->reg_map[regno] = temp
                = force_reg (Pmode, force_operand (loc, NULL_RTX));
 
+#ifdef STACK_BOUNDARY
+             mark_reg_pointer (map->reg_map[regno],
+                               STACK_BOUNDARY / BITS_PER_UNIT);
+#endif
+
              if (REGNO (temp) < map->const_equiv_map_size)
                {
                  map->const_equiv_map[REGNO (temp)] = loc;
@@ -2094,7 +2235,7 @@ copy_rtx_and_substitute (orig, map)
          else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM)
            {
              /* Do the same for a block to contain any arguments referenced
-                in memory. */
+                in memory.  */
              rtx loc, seq;
              int size = FUNCTION_ARGS_SIZE (DECL_SAVED_INSNS (map->fndecl));
 
@@ -2103,13 +2244,18 @@ copy_rtx_and_substitute (orig, map)
              loc = XEXP (loc, 0);
              /* When arguments grow downward, the virtual incoming 
                 args pointer points to the top of the argument block,
-                so the remapped location better do the same. */
+                so the remapped location better do the same.  */
 #ifdef ARGS_GROW_DOWNWARD
              loc = plus_constant (loc, size);
 #endif
              map->reg_map[regno] = temp
                = force_reg (Pmode, force_operand (loc, NULL_RTX));
 
+#ifdef STACK_BOUNDARY
+             mark_reg_pointer (map->reg_map[regno],
+                               STACK_BOUNDARY / BITS_PER_UNIT);
+#endif
+
              if (REGNO (temp) < map->const_equiv_map_size)
                {
                  map->const_equiv_map[REGNO (temp)] = loc;
@@ -2144,6 +2290,10 @@ copy_rtx_and_substitute (orig, map)
          REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (orig);
          RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (orig);
          /* A reg with REG_FUNCTION_VALUE_P true will never reach here.  */
+
+         if (map->regno_pointer_flag[regno])
+           mark_reg_pointer (map->reg_map[regno],
+                             map->regno_pointer_align[regno]);
        }
       return map->reg_map[regno];
 
@@ -2211,7 +2361,7 @@ copy_rtx_and_substitute (orig, map)
        {
          rtx constant = get_pool_constant (orig);
          if (GET_CODE (constant) == LABEL_REF)
-           return XEXP (force_const_mem (Pmode, 
+           return XEXP (force_const_mem (GET_MODE (orig),
                                          copy_rtx_and_substitute (constant,
                                                                   map)),
                         0);
@@ -2258,8 +2408,10 @@ copy_rtx_and_substitute (orig, map)
       if (! RTX_INTEGRATED_P (orig))
        abort ();
 
-      temp = force_const_mem (GET_MODE (orig),
-                             copy_rtx_and_substitute (XEXP (orig, 0), map));
+      temp
+       = force_const_mem (GET_MODE (XEXP (orig, 0)),
+                          copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0),
+                                                   map));
 
 #if 0
       /* Legitimizing the address here is incorrect.
@@ -2278,13 +2430,20 @@ copy_rtx_and_substitute (orig, map)
         will not have valid reg_map entries.  This can cause try_constants()
         to fail because assumes that all registers in the rtx have valid
         reg_map entries, and it may end up replacing one of these new
-        registers with junk. */
+        registers with junk.  */
 
       if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0)))
        temp = change_address (temp, GET_MODE (temp), XEXP (temp, 0));
 #endif
 
-      return XEXP (temp, 0);
+      temp = XEXP (temp, 0);
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+      if (GET_MODE (temp) != GET_MODE (orig))
+       temp = convert_memory_address (GET_MODE (orig), temp);
+#endif
+
+      return temp;
 
     case ASM_OPERANDS:
       /* If a single asm insn contains multiple output operands
@@ -2590,8 +2749,6 @@ subst_constants (loc, insn, map)
        src = SET_SRC (x);
 
        while (GET_CODE (*dest_loc) == ZERO_EXTRACT
-              /* By convention, we always use ZERO_EXTRACT in the dest.  */
-/*            || GET_CODE (*dest_loc) == SIGN_EXTRACT */
               || GET_CODE (*dest_loc) == SUBREG
               || GET_CODE (*dest_loc) == STRICT_LOW_PART)
          {
@@ -2824,8 +2981,16 @@ restore_constants (px)
     }
   else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == ADDRESS)
     {
-      restore_constants (&XEXP (x, 0));
-      *px = XEXP (force_const_mem (GET_MODE (x), XEXP (x, 0)), 0);
+      rtx new = XEXP (force_const_mem (GET_MODE (XEXP (x, 0)),
+                                      XEXP (XEXP (x, 0), 0)),
+                     0);
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+      if (GET_MODE (new) != GET_MODE (x))
+       new = convert_memory_address (GET_MODE (x), new);
+#endif
+
+      *px = new;
     }
   else
     {
@@ -2909,7 +3074,8 @@ set_decl_origin_self (decl)
 
          for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
            DECL_ABSTRACT_ORIGIN (arg) = arg;
-         if (DECL_INITIAL (decl) != NULL_TREE)
+         if (DECL_INITIAL (decl) != NULL_TREE
+             && DECL_INITIAL (decl) != error_mark_node)
            set_block_origin_self (DECL_INITIAL (decl));
        }
     }
@@ -2925,25 +3091,20 @@ set_block_abstract_flags (stmt, setting)
      register tree stmt;
      register int setting;
 {
-  BLOCK_ABSTRACT (stmt) = setting;
-
-  {
-    register tree local_decl;
+  register tree local_decl;
+  register tree subblock;
 
-    for (local_decl = BLOCK_VARS (stmt);
-        local_decl != NULL_TREE;
-        local_decl = TREE_CHAIN (local_decl))
-      set_decl_abstract_flags (local_decl, setting);
-  }
+  BLOCK_ABSTRACT (stmt) = setting;
 
-  {
-    register tree subblock;
+  for (local_decl = BLOCK_VARS (stmt);
+       local_decl != NULL_TREE;
+       local_decl = TREE_CHAIN (local_decl))
+    set_decl_abstract_flags (local_decl, setting);
 
-    for (subblock = BLOCK_SUBBLOCKS (stmt);
-        subblock != NULL_TREE;
-        subblock = BLOCK_CHAIN (subblock))
-      set_block_abstract_flags (subblock, setting);
-  }
+  for (subblock = BLOCK_SUBBLOCKS (stmt);
+       subblock != NULL_TREE;
+       subblock = BLOCK_CHAIN (subblock))
+    set_block_abstract_flags (subblock, setting);
 }
 
 /* Given a pointer to some ..._DECL node, and a boolean value to set the
@@ -2964,7 +3125,8 @@ set_decl_abstract_flags (decl, setting)
 
       for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
        DECL_ABSTRACT (arg) = setting;
-      if (DECL_INITIAL (decl) != NULL_TREE)
+      if (DECL_INITIAL (decl) != NULL_TREE
+         && DECL_INITIAL (decl) != error_mark_node)
        set_block_abstract_flags (DECL_INITIAL (decl), setting);
     }
 }
@@ -2979,6 +3141,7 @@ output_inline_function (fndecl)
 {
   rtx head;
   rtx last;
+  int save_flag_no_inline = flag_no_inline;
 
   if (output_bytecode)
     {
@@ -2986,6 +3149,10 @@ output_inline_function (fndecl)
       return;
     }
 
+  /* Things we allocate from here on are part of this function, not
+     permanent.  */
+  temporary_allocation ();
+
   head = DECL_SAVED_INSNS (fndecl);
   current_function_decl = fndecl;
 
@@ -2999,8 +3166,13 @@ output_inline_function (fndecl)
   /* Set stack frame size.  */
   assign_stack_local (BLKmode, DECL_FRAME_SIZE (fndecl), 0);
 
-  restore_reg_data (FIRST_PARM_INSN (head));
-
+  /* The first is a bit of a lie (the array may be larger), but doesn't
+     matter too much and it isn't worth saving the actual bound.  */
+  reg_rtx_no = regno_pointer_flag_length = MAX_REGNUM (head);
+  regno_reg_rtx = (rtx *) INLINE_REGNO_REG_RTX (head);
+  regno_pointer_flag = INLINE_REGNO_POINTER_FLAG (head);
+  regno_pointer_align = INLINE_REGNO_POINTER_ALIGN (head);
+  
   stack_slot_list = STACK_SLOT_LIST (head);
   forced_labels = FORCED_LABELS (head);
 
@@ -3069,8 +3241,15 @@ output_inline_function (fndecl)
   /* We're not deferring this any longer.  */
   DECL_DEFER_OUTPUT (fndecl) = 0;
 
+  /* Integrating function calls isn't safe anymore, so turn on
+     flag_no_inline.  */
+  flag_no_inline = 1;
+
   /* Compile this function all the way down to assembly code.  */
   rest_of_compilation (fndecl);
 
+  /* Reset flag_no_inline to its original value.  */
+  flag_no_inline = save_flag_no_inline;
+
   current_function_decl = 0;
 }