-/* 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"
#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 "ggc.h"
+#include "target.h"
#include "obstack.h"
#define obstack_chunk_alloc xmalloc
? (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 {
+ 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 *,
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;
+{
+ bool has_machine_attr = false;
+ 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))
+ {
+ has_machine_attr = true;
+ break;
+ }
+ }
+ if (has_machine_attr)
+ break;
+ }
+
+ if (has_machine_attr)
+ return (*targetm.function_attribute_inlinable_p) (fndecl);
+ else
+ 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");
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");
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;
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. */
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;
rtvec arg_vector = (rtvec) 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. */
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. */
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)
struct inline_remap *map;
rtx static_chain_value;
{
- register int i;
+ int i;
rtx insn;
rtx temp;
#ifdef HAVE_cc0
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
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 ();
{
#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);
/* 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)
+ 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++)
{
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
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,
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
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;
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;
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;
}
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;
+ struct gcc_debug_hooks *old_debug_hooks = debug_hooks;
struct function *f = DECL_SAVED_INSNS (fndecl);
cfun = f;
/* 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. */
+ /* 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
+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 ());
+}
+
+/* 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
}