-/* 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.
+ 1999, 2000, 2001, 2002 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"
#include "tm_p.h"
#include "regs.h"
#include "flags.h"
+#include "debug.h"
#include "insn-config.h"
#include "expr.h"
#include "output.h"
#include "intl.h"
#include "loop.h"
#include "params.h"
-
-#include "obstack.h"
-#define obstack_chunk_alloc xmalloc
-#define obstack_chunk_free free
-
-extern struct obstack *function_maybepermanent_obstack;
+#include "ggc.h"
+#include "target.h"
+#include "langhooks.h"
/* Similar, but round to the next highest integer that meets the
alignment. */
? (1 + (3 * list_length (DECL_ARGUMENTS (DECL))) / 2) \
: (8 * (8 + list_length (DECL_ARGUMENTS (DECL)))))
#endif
-
-/* Decide whether a function with a target specific attribute
- attached can be inlined. By default we disallow this. */
-#ifndef FUNCTION_ATTRIBUTE_INLINABLE_P
-#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 GTY(()) {
+ rtx hard_reg;
+ rtx pseudo;
+} initial_value_pair;
+typedef struct initial_value_struct GTY(()) {
+ int num_entries;
+ int max_entries;
+ initial_value_pair * GTY ((length ("%h.num_entries"))) 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 *,
static void process_reg_param PARAMS ((struct inline_remap *, rtx,
rtx));
void set_decl_abstract_flags PARAMS ((tree, int));
-static rtx expand_inline_function_eh_labelmap PARAMS ((rtx));
static void mark_stores PARAMS ((rtx, rtx, void *));
static void save_parm_insns PARAMS ((rtx, rtx));
static void copy_insn_list PARAMS ((rtx, struct inline_remap *,
rtx));
-static void copy_insn_notes PARAMS ((rtx, struct inline_remap *));
+static void copy_insn_notes PARAMS ((rtx, struct inline_remap *,
+ int));
static int compare_blocks PARAMS ((const PTR, const PTR));
static int find_block PARAMS ((const PTR, const PTR));
return x;
}
+/* Return false if the function FNDECL cannot be inlined on account of its
+ attributes, true otherwise. */
+bool
+function_attribute_inlinable_p (fndecl)
+ tree fndecl;
+{
+ if (targetm.attribute_table)
+ {
+ tree a;
+
+ for (a = DECL_ATTRIBUTES (fndecl); a; a = TREE_CHAIN (a))
+ {
+ tree name = TREE_PURPOSE (a);
+ int i;
+
+ for (i = 0; targetm.attribute_table[i].name != NULL; i++)
+ if (is_attribute_p (targetm.attribute_table[i].name, name))
+ return (*targetm.function_attribute_inlinable_p) (fndecl);
+ }
+ }
+
+ return true;
+}
+
/* Zero if the current function (whose FUNCTION_DECL is FNDECL)
is safe and reasonable to integrate into other functions.
Nonzero means value is a warning msgid with a single %s
const char *
function_cannot_inline_p (fndecl)
- register tree fndecl;
+ tree fndecl;
{
- register rtx insn;
+ rtx insn;
tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
/* For functions marked as inline increase the maximum size to
+ 8 * list_length (DECL_ARGUMENTS (fndecl)))
: INTEGRATE_THRESHOLD (fndecl);
- register int ninsns = 0;
- register tree parms;
+ int ninsns = 0;
+ tree parms;
if (DECL_UNINLINABLE (fndecl))
return N_("function cannot be inline");
/* No inlines with varargs. */
- if ((last && TREE_VALUE (last) != void_type_node)
- || current_function_varargs)
+ if (last && TREE_VALUE (last) != void_type_node)
return N_("varargs function cannot be inline");
if (current_function_calls_alloca)
if (current_function_calls_setjmp)
return N_("function using setjmp cannot be inline");
+ if (current_function_calls_eh_return)
+ return N_("function uses __builtin_eh_return");
+
if (current_function_contains_functions)
return N_("function with nested functions cannot be inline");
/* We will not inline a function which uses computed goto. The addresses of
its local labels, which may be tucked into global storage, are of course
- not constant across instantiations, which causes unexpected behaviour. */
+ not constant across instantiations, which causes unexpected behavior. */
if (current_function_has_computed_jump)
return N_("function with computed jump cannot inline");
if (current_function_has_nonlocal_goto)
return N_("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 N_("function with complex parameters cannot be inline");
- }
-
/* We can't inline functions that return a PARALLEL rtx. */
if (DECL_RTL_SET_P (DECL_RESULT (fndecl)))
{
/* If the function has a target specific attribute attached to it,
then we assume that we should not inline it. This can be overriden
- by the target if it defines FUNCTION_ATTRIBUTE_INLINABLE_P. */
- if (DECL_MACHINE_ATTRIBUTES (fndecl)
- && ! FUNCTION_ATTRIBUTE_INLINABLE_P (fndecl))
+ by the target if it defines TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P. */
+ if (!function_attribute_inlinable_p (fndecl))
return N_("function with target specific attribute(s) cannot be inlined");
return NULL;
else
{
copy = copy_node (decl);
- if (DECL_LANG_SPECIFIC (copy))
- copy_lang_decl (copy);
+ (*lang_hooks.dup_lang_specific_decl) (copy);
/* TREE_ADDRESSABLE isn't used to indicate that a label's
address has been taken; it's for internal bookkeeping in
Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values
for the parms, prior to elimination of virtual registers.
These values are needed for substituting parms properly. */
-
- parmdecl_map = (tree *) xmalloc (max_parm_reg * sizeof (tree));
+ if (! flag_no_inline)
+ parmdecl_map = (tree *) xmalloc (max_parm_reg * sizeof (tree));
/* Make and emit a return-label if we have not already done so. */
emit_label (return_label);
}
- argvec = initialize_for_inline (fndecl);
+ if (! flag_no_inline)
+ argvec = initialize_for_inline (fndecl);
+ else
+ argvec = NULL;
+
+ /* 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_related_insns (insn);
/* If there are insns that copy parms from the stack into pseudo registers,
those insns are not copied. `expand_inline_function' must
if (GET_CODE (insn) != NOTE)
abort ();
- /* Get the insn which signals the end of parameter setup code. */
- first_nonparm_insn = get_first_nonparm_insn ();
-
- /* Now just scan the chain of insns to see what happens to our
- PARM_DECLs. If a PARM_DECL is used but never modified, we
- can substitute its rtl directly when expanding inline (and
- perform constant folding when its incoming value is constant).
- Otherwise, we have to copy its value into a new register and track
- the new register's life. */
- in_nonparm_insns = 0;
- save_parm_insns (insn, first_nonparm_insn);
-
- cfun->inl_max_label_num = max_label_num ();
- cfun->inl_last_parm_insn = cfun->x_last_parm_insn;
- cfun->original_arg_vector = argvec;
+ if (! flag_no_inline)
+ {
+ /* Get the insn which signals the end of parameter setup code. */
+ first_nonparm_insn = get_first_nonparm_insn ();
+
+ /* Now just scan the chain of insns to see what happens to our
+ PARM_DECLs. If a PARM_DECL is used but never modified, we
+ can substitute its rtl directly when expanding inline (and
+ perform constant folding when its incoming value is
+ constant). Otherwise, we have to copy its value into a new
+ register and track the new register's life. */
+ in_nonparm_insns = 0;
+ save_parm_insns (insn, first_nonparm_insn);
+
+ cfun->inl_max_label_num = max_label_num ();
+ cfun->inl_last_parm_insn = cfun->x_last_parm_insn;
+ cfun->original_arg_vector = argvec;
+ }
cfun->original_decl_initial = DECL_INITIAL (fndecl);
cfun->no_debugging_symbols = (write_symbols == NO_DEBUG);
DECL_SAVED_INSNS (fndecl) = cfun;
/* Clean up. */
- free (parmdecl_map);
+ if (! flag_no_inline)
+ free (parmdecl_map);
}
/* Scan the chain of insns to see what happens to our PARM_DECLs. If a
map->reg_map[REGNO (loc)] = copy;
}
-/* Used by duplicate_eh_handlers to map labels for the exception table */
-static struct inline_remap *eif_eh_map;
-
-static rtx
-expand_inline_function_eh_labelmap (label)
- rtx label;
-{
- int index = CODE_LABEL_NUMBER (label);
- return get_label_from_map (eif_eh_map, index);
-}
-
/* Compare two BLOCKs for qsort. The key we sort on is the
- BLOCK_ABSTRACT_ORIGIN of the blocks. */
+ BLOCK_ABSTRACT_ORIGIN of the blocks. We cannot just subtract the
+ two pointers, because it may overflow sizeof(int). */
static int
compare_blocks (v1, v2)
{
tree b1 = *((const tree *) v1);
tree b2 = *((const tree *) v2);
+ char *p1 = (char *) BLOCK_ABSTRACT_ORIGIN (b1);
+ char *p2 = (char *) BLOCK_ABSTRACT_ORIGIN (b2);
- return ((char *) BLOCK_ABSTRACT_ORIGIN (b1)
- - (char *) BLOCK_ABSTRACT_ORIGIN (b2));
+ if (p1 == p2)
+ return 0;
+ return p1 < p2 ? -1 : 1;
}
/* Compare two BLOCKs for bsearch. The first pointer corresponds to
{
const union tree_node *b1 = (const union tree_node *) v1;
tree b2 = *((const tree *) v2);
+ char *p1 = (char *) b1;
+ char *p2 = (char *) BLOCK_ABSTRACT_ORIGIN (b2);
- return ((const char *) b1 - (char *) BLOCK_ABSTRACT_ORIGIN (b2));
+ if (p1 == p2)
+ return 0;
+ return p1 < p2 ? -1 : 1;
}
/* Integrate the procedure defined by FNDECL. Note that this function
tree *arg_trees;
rtx *arg_vals;
int max_regno;
- register int i;
+ int i;
int min_labelno = inl_f->emit->x_first_label_num;
int max_labelno = inl_f->inl_max_label_num;
int nargs;
rtx stack_save = 0;
rtx temp;
struct inline_remap *map = 0;
-#ifdef HAVE_cc0
- rtx cc0_insn = 0;
-#endif
- rtvec arg_vector = (rtvec) inl_f->original_arg_vector;
+ rtvec arg_vector = inl_f->original_arg_vector;
rtx static_chain_value = 0;
int inl_max_uid;
+ int eh_region_offset;
/* The pointer used to track the true location of the memory used
for MAP->LABEL_MAP. */
enum machine_mode mode;
if (actual == 0)
- return (rtx) (HOST_WIDE_INT) -1;
+ return (rtx) (size_t) -1;
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. */
|| (mode == BLKmode
&& (TYPE_MAIN_VARIANT (TREE_TYPE (arg))
!= TYPE_MAIN_VARIANT (TREE_TYPE (formal)))))
- return (rtx) (HOST_WIDE_INT) -1;
+ return (rtx) (size_t) -1;
}
/* Extra arguments are valid, but will be ignored below, so we must
else if (GET_CODE (loc) != MEM)
{
if (GET_MODE (loc) != TYPE_MODE (TREE_TYPE (arg)))
- /* The mode if LOC and ARG can differ if LOC was a variable
- that had its mode promoted via PROMOTED_MODE. */
- arg_vals[i] = convert_modes (GET_MODE (loc),
- TYPE_MODE (TREE_TYPE (arg)),
- expand_expr (arg, NULL_RTX, mode,
- EXPAND_SUM),
- TREE_UNSIGNED (TREE_TYPE (formal)));
+ {
+ int unsignedp = TREE_UNSIGNED (TREE_TYPE (formal));
+ enum machine_mode pmode = TYPE_MODE (TREE_TYPE (formal));
+
+ pmode = promote_mode (TREE_TYPE (formal), pmode,
+ &unsignedp, 0);
+
+ if (GET_MODE (loc) != pmode)
+ abort ();
+
+ /* The mode if LOC and ARG can differ if LOC was a variable
+ that had its mode promoted via PROMOTED_MODE. */
+ arg_vals[i] = convert_modes (pmode,
+ TYPE_MODE (TREE_TYPE (arg)),
+ expand_expr (arg, NULL_RTX, mode,
+ EXPAND_SUM),
+ unsignedp);
+ }
else
arg_vals[i] = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM);
}
/* 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");
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;
incoming arg rtx values are expanded now so that we can be
sure we have enough slots in the const equiv map since the
store_expr call can easily blow the size estimate. */
- if (DECL_FRAME_SIZE (fndecl) != 0)
- copy_rtx_and_substitute (virtual_stack_vars_rtx, map, 0);
-
if (DECL_SAVED_INSNS (fndecl)->args_size != 0)
copy_rtx_and_substitute (virtual_incoming_args_rtx, map, 0);
}
/* The function returns an object in a register and we use the return
value. Set up our target for remapping. */
- /* Machine mode function was declared to return. */
+ /* Machine mode function was declared to return. */
enum machine_mode departing_mode = TYPE_MODE (type);
/* (Possibly wider) machine mode it actually computes
(for the sake of callers that fail to declare it right).
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,
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);
+ /* Duplicate the EH regions. This will create an offset from the
+ region numbers in the function we're inlining to the region
+ numbers in the calling function. This must wait until after
+ copy_insn_list, as we need the insn map to be complete. */
+ eh_region_offset = duplicate_eh_regions (inl_f, map);
+
/* Now copy the REG_NOTES for those insns. */
- copy_insn_notes (insns, map);
+ copy_insn_notes (insns, map, eh_region_offset);
/* If the insn sequence required one, emit the return label. */
if (map->local_return_label)
this block to the list of blocks at this binding level. We
can't do it the way it's done for function-at-a-time mode the
superblocks have not been created yet. */
- insert_block (block);
+ (*lang_hooks.decls.insert_block) (block);
else
{
BLOCK_CHAIN (block)
free (real_label_map);
VARRAY_FREE (map->const_equiv_varray);
free (map->reg_map);
- VARRAY_FREE (map->block_map);
free (map->insn_map);
free (map);
free (arg_vals);
Copying is done in two passes, first the insns and then their REG_NOTES.
- If static_chain_value is non-zero, it represents the context-pointer
+ If static_chain_value is nonzero, it represents the context-pointer
register for the function. */
static void
struct inline_remap *map;
rtx static_chain_value;
{
- register int i;
+ int i;
rtx insn;
rtx temp;
#ifdef HAVE_cc0
rtx cc0_insn = 0;
#endif
+ rtx static_chain_mem = 0;
/* Copy the insns one by one. Do this in two passes, first the insns and
then their REG_NOTES. */
inline_target. */
break;
- /* If the inline fn needs eh context, make sure that
- the current fn has one. */
- if (GET_CODE (pattern) == USE
- && find_reg_note (insn, REG_EH_CONTEXT, 0) != 0)
- get_eh_context ();
-
/* Ignore setting a function value that we don't want to use. */
if (map->inline_target == 0
&& set != 0
&& REG_FUNCTION_VALUE_P (XEXP (pattern, 0)))
break;
- /* If this is setting the static chain rtx, omit it. */
+ /* Look for the address of the static chain slot. The
+ rtx_equal_p comparisons against the
+ static_chain_incoming_rtx below may fail if the static
+ chain is in memory and the address specified is not
+ "legitimate". This happens on Xtensa where the static
+ chain is at a negative offset from argp and where only
+ positive offsets are legitimate. When the RTL is
+ generated, the address is "legitimized" by copying it
+ into a register, causing the rtx_equal_p comparisons to
+ fail. This workaround looks for code that sets a
+ register to the address of the static chain. Subsequent
+ memory references via that register can then be
+ identified as static chain references. We assume that
+ the register is only assigned once, and that the static
+ chain address is only live in one register at a time. */
+
else if (static_chain_value != 0
&& set != 0
+ && GET_CODE (static_chain_incoming_rtx) == MEM
&& GET_CODE (SET_DEST (set)) == REG
- && rtx_equal_p (SET_DEST (set),
- static_chain_incoming_rtx))
+ && rtx_equal_p (SET_SRC (set),
+ XEXP (static_chain_incoming_rtx, 0)))
+ {
+ static_chain_mem =
+ gen_rtx_MEM (GET_MODE (static_chain_incoming_rtx),
+ SET_DEST (set));
+
+ /* emit the instruction in case it is used for something
+ other than setting the static chain; if it's not used,
+ it can always be removed as dead code */
+ copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0));
+ }
+
+ /* If this is setting the static chain rtx, omit it. */
+ else if (static_chain_value != 0
+ && set != 0
+ && (rtx_equal_p (SET_DEST (set),
+ static_chain_incoming_rtx)
+ || (static_chain_mem
+ && rtx_equal_p (SET_DEST (set), static_chain_mem))))
break;
/* If this is setting the static chain pseudo, set it from
the value we want to give it instead. */
else if (static_chain_value != 0
&& set != 0
- && rtx_equal_p (SET_SRC (set),
- static_chain_incoming_rtx))
+ && (rtx_equal_p (SET_SRC (set),
+ static_chain_incoming_rtx)
+ || (static_chain_mem
+ && rtx_equal_p (SET_SRC (set), static_chain_mem))))
{
rtx newdest = copy_rtx_and_substitute (SET_DEST (set), map, 1);
copy = emit_move_insn (newdest, static_chain_value);
- static_chain_value = 0;
+ if (GET_CODE (static_chain_incoming_rtx) != MEM)
+ static_chain_value = 0;
}
/* If this is setting the virtual stack vars register, this must
#else
try_constants (copy, map);
#endif
+ INSN_SCOPE (copy) = INSN_SCOPE (insn);
break;
case JUMP_INSN:
- if (GET_CODE (PATTERN (insn)) == RETURN
- || (GET_CODE (PATTERN (insn)) == PARALLEL
- && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == RETURN))
+ if (map->integrating && returnjump_p (insn))
{
if (map->local_return_label == 0)
map->local_return_label = gen_label_rtx ();
cc0_insn = 0;
#endif
try_constants (copy, map);
+ INSN_SCOPE (copy) = INSN_SCOPE (insn);
/* If this used to be a conditional jump insn but whose branch
direction is now know, we must do something special. */
{
#ifdef HAVE_cc0
/* If the previous insn set cc0 for us, delete it. */
- if (sets_cc0_p (PREV_INSN (copy)))
- delete_insn (PREV_INSN (copy));
+ if (only_sets_cc0_p (PREV_INSN (copy)))
+ delete_related_insns (PREV_INSN (copy));
#endif
/* If this is now a no-op, delete it. */
if (map->last_pc_value == pc_rtx)
{
- delete_insn (copy);
+ delete_related_insns (copy);
copy = 0;
}
else
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);
+ INSN_SCOPE (copy) = INSN_SCOPE (insn);
/* Because the USAGE information potentially contains objects other
than hard registers, we need to copy it. */
break;
case NOTE:
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL)
+ {
+ copy = emit_label (get_label_from_map (map,
+ CODE_LABEL_NUMBER (insn)));
+ LABEL_NAME (copy) = NOTE_SOURCE_FILE (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));
if (copy
- && (NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_BEG
- || NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_END))
- {
- rtx label
- = get_label_from_map (map, NOTE_EH_HANDLER (copy));
-
- /* We have to duplicate the handlers for the original. */
- if (NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_BEG)
- {
- /* We need to duplicate the handlers for the EH region
- and we need to indicate where the label map is */
- eif_eh_map = map;
- duplicate_eh_handlers (NOTE_EH_HANDLER (copy),
- CODE_LABEL_NUMBER (label),
- expand_inline_function_eh_labelmap);
- }
-
- /* We have to forward these both to match the new exception
- region. */
- NOTE_EH_HANDLER (copy) = CODE_LABEL_NUMBER (label);
- }
- else if (copy
- && (NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_BEG
- || NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_END)
- && NOTE_BLOCK (insn))
+ && (NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_BEG
+ || NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_END)
+ && NOTE_BLOCK (insn))
{
tree *mapped_block_p;
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;
that are valid across the entire function. */
static void
-copy_insn_notes (insns, map)
+copy_insn_notes (insns, map, eh_region_offset)
rtx insns;
struct inline_remap *map;
+ int eh_region_offset;
{
- rtx insn;
+ rtx insn, new_insn;
map->const_age++;
for (insn = insns; insn; insn = NEXT_INSN (insn))
- if (INSN_P (insn)
- && map->insn_map[INSN_UID (insn)]
- && REG_NOTES (insn))
- {
- rtx next, note = copy_rtx_and_substitute (REG_NOTES (insn), map, 0);
+ {
+ if (! INSN_P (insn))
+ continue;
- /* We must also do subst_constants, in case one of our parameters
- has const type and constant value. */
- subst_constants (¬e, NULL_RTX, map, 0);
- apply_change_group ();
- REG_NOTES (map->insn_map[INSN_UID (insn)]) = note;
+ new_insn = map->insn_map[INSN_UID (insn)];
+ if (! new_insn)
+ continue;
- /* Finally, delete any REG_LABEL notes from the chain. */
- for (; note; note = next)
- {
- next = XEXP (note, 1);
- if (REG_NOTE_KIND (note) == REG_LABEL)
- remove_note (map->insn_map[INSN_UID (insn)], note);
- }
- }
+ if (REG_NOTES (insn))
+ {
+ rtx next, note = copy_rtx_and_substitute (REG_NOTES (insn), map, 0);
+
+ /* We must also do subst_constants, in case one of our parameters
+ has const type and constant value. */
+ subst_constants (¬e, NULL_RTX, map, 0);
+ apply_change_group ();
+ REG_NOTES (new_insn) = note;
+
+ /* Delete any REG_LABEL notes from the chain. Remap any
+ REG_EH_REGION notes. */
+ for (; note; note = next)
+ {
+ next = XEXP (note, 1);
+ if (REG_NOTE_KIND (note) == REG_LABEL)
+ remove_note (new_insn, note);
+ else if (REG_NOTE_KIND (note) == REG_EH_REGION
+ && INTVAL (XEXP (note, 0)) > 0)
+ XEXP (note, 0) = GEN_INT (INTVAL (XEXP (note, 0))
+ + eh_region_offset);
+ }
+ }
+
+ if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+ {
+ int i;
+ for (i = 0; i < 3; i++)
+ copy_insn_notes (XEXP (PATTERN (insn), i), map, eh_region_offset);
+ }
+
+ if (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) == RESX)
+ XINT (PATTERN (new_insn), 0) += eh_region_offset;
+ }
}
\f
/* Given a chain of PARM_DECLs, ARGS, copy each decl into a VAR_DECL,
struct inline_remap *map;
rtvec arg_vector;
{
- register tree tail;
- register int i;
+ tree tail;
+ int i;
for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++)
{
r = DECL_RTL (d);
subst_constants (&r, NULL_RTX, map, 1);
SET_DECL_RTL (d, r);
+
+ if (GET_CODE (r) == REG)
+ REGNO_DECL (REGNO (r)) = d;
+ else if (GET_CODE (r) == CONCAT)
+ {
+ REGNO_DECL (REGNO (XEXP (r, 0))) = d;
+ REGNO_DECL (REGNO (XEXP (r, 1))) = d;
+ }
+
apply_change_group ();
}
rtx
copy_rtx_and_substitute (orig, map, for_lhs)
- register rtx orig;
+ rtx orig;
struct inline_remap *map;
int for_lhs;
{
- register rtx copy, temp;
- register int i, j;
- register RTX_CODE code;
- register enum machine_mode mode;
- register const char *format_ptr;
+ rtx copy, temp;
+ int i, j;
+ RTX_CODE code;
+ enum machine_mode mode;
+ const char *format_ptr;
int regno;
if (orig == 0)
{
/* 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
SET_CONST_EQUIV_DATA (map, temp, loc, CONST_AGE_PARM);
- seq = gen_sequence ();
+ seq = get_insns ();
end_sequence ();
emit_insn_after (seq, map->insns_at_start);
return temp;
SET_CONST_EQUIV_DATA (map, temp, loc, CONST_AGE_PARM);
- seq = gen_sequence ();
+ seq = get_insns ();
end_sequence ();
emit_insn_after (seq, map->insns_at_start);
return temp;
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
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)
- return gen_rtx_SUBREG (GET_MODE (orig), SUBREG_REG (copy),
- SUBREG_WORD (orig) + SUBREG_WORD (copy));
- else if (GET_CODE (copy) == CONCAT)
- {
- rtx retval = subreg_realpart_p (orig) ? XEXP (copy, 0) : XEXP (copy, 1);
-
- if (GET_MODE (retval) == GET_MODE (orig))
- return retval;
- else
- return gen_rtx_SUBREG (GET_MODE (orig), retval,
- (SUBREG_WORD (orig) %
- (GET_MODE_UNIT_SIZE (GET_MODE (SUBREG_REG (orig)))
- / (unsigned) UNITS_PER_WORD)));
- }
- else
- return gen_rtx_SUBREG (GET_MODE (orig), copy,
- SUBREG_WORD (orig));
+ return simplify_gen_subreg (GET_MODE (orig), copy,
+ GET_MODE (SUBREG_REG (orig)),
+ SUBREG_BYTE (orig));
case ADDRESSOF:
copy = gen_rtx_ADDRESSOF (mode,
RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (temp);
/* A reg with REG_FUNCTION_VALUE_P true will never reach here. */
- if (REG_POINTER (map->x_regno_reg_rtx[regno]))
+ /* Objects may initially be represented as registers, but
+ but turned into a MEM if their address is taken by
+ put_var_into_stack. Therefore, the register table may have
+ entries which are MEMs.
+
+ We briefly tried to clear such entries, but that ended up
+ cascading into many changes due to the optimizers not being
+ prepared for empty entries in the register table. So we've
+ decided to allow the MEMs in the register table for now. */
+ if (REG_P (map->x_regno_reg_rtx[regno])
+ && REG_POINTER (map->x_regno_reg_rtx[regno]))
mark_reg_pointer (map->reg_map[regno],
map->regno_pointer_align[regno]);
regno = REGNO (map->reg_map[regno]);
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
case PC:
case CC0:
case CONST_INT:
+ case CONST_VECTOR:
return orig;
case SYMBOL_REF:
copy_rtx_and_substitute (constant, map, for_lhs)),
0);
}
- else if (SYMBOL_REF_NEED_ADJUST (orig))
- {
- eif_eh_map = map;
- return rethrow_symbol_map (orig,
- expand_inline_function_eh_labelmap);
- }
return orig;
if (map->orig_asm_operands_vector == ASM_OPERANDS_INPUT_VEC (orig))
{
copy = rtx_alloc (ASM_OPERANDS);
- copy->volatil = orig->volatil;
+ RTX_FLAG (copy, volatil) = RTX_FLAG (orig, volatil);
PUT_MODE (copy, GET_MODE (orig));
ASM_OPERANDS_TEMPLATE (copy) = ASM_OPERANDS_TEMPLATE (orig);
ASM_OPERANDS_OUTPUT_CONSTRAINT (copy)
#ifndef NO_FUNCTION_CSE
if (! (optimize && ! flag_no_function_cse))
#endif
- return
- gen_rtx_CALL
- (GET_MODE (orig),
- gen_rtx_MEM (GET_MODE (XEXP (orig, 0)),
- copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0),
- map, 0)),
- copy_rtx_and_substitute (XEXP (orig, 1), map, 0));
+ {
+ rtx copy
+ = gen_rtx_MEM (GET_MODE (XEXP (orig, 0)),
+ copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0),
+ map, 0));
+
+ MEM_COPY_ATTRIBUTES (copy, XEXP (orig, 0));
+
+ return
+ gen_rtx_CALL (GET_MODE (orig), copy,
+ copy_rtx_and_substitute (XEXP (orig, 1), map, 0));
+ }
break;
#if 0
return validize_mem (force_const_mem (const_mode, constant));
}
- copy = rtx_alloc (MEM);
- PUT_MODE (copy, mode);
- XEXP (copy, 0) = copy_rtx_and_substitute (XEXP (orig, 0), map, 0);
+ copy = gen_rtx_MEM (mode, copy_rtx_and_substitute (XEXP (orig, 0),
+ map, 0));
MEM_COPY_ATTRIBUTES (copy, orig);
+
+ /* If inlining and this is not for the LHS, turn off RTX_UNCHANGING_P
+ since this may be an indirect reference to a parameter and the
+ actual may not be readonly. */
+ if (inlining && !for_lhs)
+ RTX_UNCHANGING_P (copy) = 0;
+
+ /* If inlining, squish aliasing data that references the subroutine's
+ parameter list, since that's no longer applicable. */
+ if (inlining && MEM_EXPR (copy)
+ && TREE_CODE (MEM_EXPR (copy)) == INDIRECT_REF
+ && TREE_CODE (TREE_OPERAND (MEM_EXPR (copy), 0)) == PARM_DECL)
+ set_mem_expr (copy, NULL_TREE);
+
return copy;
default:
copy = rtx_alloc (code);
PUT_MODE (copy, mode);
- copy->in_struct = orig->in_struct;
- copy->volatil = orig->volatil;
- copy->unchanging = orig->unchanging;
+ RTX_FLAG (copy, in_struct) = RTX_FLAG (orig, in_struct);
+ RTX_FLAG (copy, volatil) = RTX_FLAG (orig, volatil);
+ RTX_FLAG (copy, unchanging) = RTX_FLAG (orig, unchanging);
format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
int memonly;
{
rtx x = *loc;
- register int i, j;
- register enum rtx_code code;
- register const char *format_ptr;
+ int i, j;
+ enum rtx_code code;
+ const char *format_ptr;
int num_changes = num_validated_changes ();
rtx new = 0;
enum machine_mode op0_mode = MAX_MACHINE_MODE;
case PC:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case SYMBOL_REF:
case CONST:
case LABEL_REF:
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_WORD (x), 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;
}
case 'w':
case 'n':
case 't':
+ case 'B':
break;
case 'E':
new = CONST0_RTX (mode);
else
{
- REAL_VALUE_TYPE val = FLOAT_STORE_FLAG_VALUE (mode);
+ REAL_VALUE_TYPE val;
+
+ /* Avoid automatic aggregate initialization. */
+ val = FLOAT_STORE_FLAG_VALUE (mode);
new = CONST_DOUBLE_FROM_REAL_VALUE (val, mode);
}
}
regno = REGNO (dest), mode = GET_MODE (dest);
else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG)
{
- regno = REGNO (SUBREG_REG (dest)) + SUBREG_WORD (dest);
+ regno = REGNO (SUBREG_REG (dest));
+ if (regno < FIRST_PSEUDO_REGISTER)
+ regno += subreg_regno_offset (REGNO (SUBREG_REG (dest)),
+ GET_MODE (SUBREG_REG (dest)),
+ SUBREG_BYTE (dest),
+ GET_MODE (dest));
mode = GET_MODE (SUBREG_REG (dest));
}
static void
set_block_origin_self (stmt)
- register tree stmt;
+ tree stmt;
{
if (BLOCK_ABSTRACT_ORIGIN (stmt) == NULL_TREE)
{
BLOCK_ABSTRACT_ORIGIN (stmt) = stmt;
{
- register tree local_decl;
+ tree local_decl;
for (local_decl = BLOCK_VARS (stmt);
local_decl != NULL_TREE;
}
{
- register tree subblock;
+ tree subblock;
for (subblock = BLOCK_SUBBLOCKS (stmt);
subblock != NULL_TREE;
void
set_decl_origin_self (decl)
- register tree decl;
+ tree decl;
{
if (DECL_ABSTRACT_ORIGIN (decl) == NULL_TREE)
{
DECL_ABSTRACT_ORIGIN (decl) = decl;
if (TREE_CODE (decl) == FUNCTION_DECL)
{
- register tree arg;
+ tree arg;
for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
DECL_ABSTRACT_ORIGIN (arg) = arg;
static void
set_block_abstract_flags (stmt, setting)
- register tree stmt;
- register int setting;
+ tree stmt;
+ int setting;
{
- register tree local_decl;
- register tree subblock;
+ tree local_decl;
+ tree subblock;
BLOCK_ABSTRACT (stmt) = setting;
void
set_decl_abstract_flags (decl, setting)
- register tree decl;
- register int setting;
+ tree decl;
+ int setting;
{
DECL_ABSTRACT (decl) = setting;
if (TREE_CODE (decl) == FUNCTION_DECL)
{
- register tree arg;
+ tree arg;
for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
DECL_ABSTRACT (arg) = setting;
{
struct function *old_cfun = cfun;
enum debug_info_type old_write_symbols = write_symbols;
+ const struct gcc_debug_hooks *const old_debug_hooks = debug_hooks;
struct function *f = DECL_SAVED_INSNS (fndecl);
cfun = f;
current_function_decl = fndecl;
- clear_emit_caches ();
set_new_last_label_num (f->inl_max_label_num);
/* If requested, suppress debugging information. */
if (f->no_debugging_symbols)
- write_symbols = NO_DEBUG;
-
- /* Do any preparation, such as emitting abstract debug info for the inline
- before it gets mangled by optimization. */
- note_outlining_of_inline_function (fndecl);
+ {
+ write_symbols = NO_DEBUG;
+ debug_hooks = &do_nothing_debug_hooks;
+ }
- /* Compile this function all the way down to assembly code. */
+ /* Compile this function all the way down to assembly code. As a
+ side effect this destroys the saved RTL representation, but
+ that's okay, because we don't need to inline this anymore. */
rest_of_compilation (fndecl);
-
- /* We can't inline this anymore. */
- f->inlinable = 0;
DECL_INLINE (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
+get_hard_reg_initial_reg (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].pseudo, reg))
+ return ivs->entries[i].hard_reg;
+
+ return NULL_RTX;
+}
+
+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 *) ggc_alloc (sizeof (initial_value_struct));
+ ivs = fun->hard_reg_initial_vals;
+ ivs->num_entries = 0;
+ ivs->max_entries = 5;
+ ivs->entries = (initial_value_pair *) ggc_alloc (5 * sizeof (initial_value_pair));
+ }
+
+ if (ivs->num_entries >= ivs->max_entries)
+ {
+ ivs->max_entries += 5;
+ ivs->entries =
+ (initial_value_pair *) ggc_realloc (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));
+}
+
+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_insn_after (seq, get_insns ());
}
+
+/* If the backend knows where to allocate pseudos for hard
+ register initial values, register these allocations now. */
+void
+allocate_initial_values (reg_equiv_memory_loc)
+ rtx *reg_equiv_memory_loc ATTRIBUTE_UNUSED;
+{
+#ifdef ALLOCATE_INITIAL_VALUE
+ struct initial_value_struct *ivs = cfun->hard_reg_initial_vals;
+ int i;
+
+ if (ivs == 0)
+ return;
+
+ for (i = 0; i < ivs->num_entries; i++)
+ {
+ int regno = REGNO (ivs->entries[i].pseudo);
+ rtx x = ALLOCATE_INITIAL_VALUE (ivs->entries[i].hard_reg);
+
+ if (x == NULL_RTX || REG_N_SETS (REGNO (ivs->entries[i].pseudo)) > 1)
+ ; /* Do nothing. */
+ else if (GET_CODE (x) == MEM)
+ reg_equiv_memory_loc[regno] = x;
+ else if (GET_CODE (x) == REG)
+ {
+ reg_renumber[regno] = REGNO (x);
+ /* Poke the regno right into regno_reg_rtx
+ so that even fixed regs are accepted. */
+ REGNO (ivs->entries[i].pseudo) = REGNO (x);
+ }
+ else abort ();
+ }
+#endif
+}
+
+#include "gt-integrate.h"