-/* 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 "obstack.h"
#define obstack_chunk_alloc xmalloc
#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 *,
argvec = initialize_for_inline (fndecl);
+ /* Delete basic block notes created by early run of find_basic_block.
+ The notes would be later used by find_basic_blocks to reuse the memory
+ for basic_block structures on already freed obstack. */
+ for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_BASIC_BLOCK)
+ delete_insn (insn);
+
/* If there are insns that copy parms from the stack into pseudo registers,
those insns are not copied. `expand_inline_function' must
emit the correct code to handle such things. */
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. */
/* 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;
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);
{
#ifdef HAVE_cc0
/* If the previous insn set cc0 for us, delete it. */
- if (sets_cc0_p (PREV_INSN (copy)))
+ if (only_sets_cc0_p (PREV_INSN (copy)))
delete_insn (PREV_INSN (copy));
#endif
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)));
+ 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));
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;
{
/* 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)
- {
- int final_offset = SUBREG_BYTE (orig) + SUBREG_BYTE (copy);
-
- /* When working with SUBREGs the rule is that the byte
- offset must be a multiple of the SUBREG's mode. */
- final_offset = (final_offset / GET_MODE_SIZE (GET_MODE (orig)));
- final_offset = (final_offset * GET_MODE_SIZE (GET_MODE (orig)));
- return gen_rtx_SUBREG (GET_MODE (orig), SUBREG_REG (copy),
- final_offset);
- }
- else if (GET_CODE (copy) == CONCAT)
- {
- rtx retval = subreg_realpart_p (orig) ? XEXP (copy, 0) : XEXP (copy, 1);
- int final_offset;
-
- if (GET_MODE (retval) == GET_MODE (orig))
- return retval;
-
- final_offset = SUBREG_BYTE (orig) %
- GET_MODE_UNIT_SIZE (GET_MODE (SUBREG_REG (orig)));
- final_offset = (final_offset / GET_MODE_SIZE (GET_MODE (orig)));
- final_offset = (final_offset * GET_MODE_SIZE (GET_MODE (orig)));
- return gen_rtx_SUBREG (GET_MODE (orig), retval, final_offset);
- }
- else
- return gen_rtx_SUBREG (GET_MODE (orig), copy,
- SUBREG_BYTE (orig));
+ return simplify_gen_subreg (GET_MODE (orig), copy,
+ GET_MODE (SUBREG_REG (orig)),
+ SUBREG_BYTE (orig));
case ADDRESSOF:
copy = gen_rtx_ADDRESSOF (mode,
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
valid. We handle two cases: extracting a full word in an
integral mode and extracting the low part. */
subst_constants (&inner, NULL_RTX, map, 0);
-
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
- && GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD
- && GET_MODE (SUBREG_REG (x)) != VOIDmode)
- new = operand_subword (inner, SUBREG_BYTE (x) / UNITS_PER_WORD,
- 0, GET_MODE (SUBREG_REG (x)));
-
- cancel_changes (num_changes);
- if (new == 0 && subreg_lowpart_p (x))
- new = gen_lowpart_common (GET_MODE (x), inner);
+ new = simplify_gen_subreg (GET_MODE (x), inner,
+ GET_MODE (SUBREG_REG (x)),
+ SUBREG_BYTE (x));
if (new)
validate_change (insn, loc, new, 1);
+ else
+ cancel_changes (num_changes);
return;
}
{
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. */
rest_of_compilation (fndecl);
- /* We can't inline this anymore. */
- f->inlinable = 0;
+ /* We can't inline this anymore; rest_of_compilation destroyed the
+ data structures describing the function. */
DECL_INLINE (fndecl) = 0;
+ DECL_SAVED_INSNS (fndecl) = 0;
cfun = old_cfun;
current_function_decl = old_cfun ? old_cfun->decl : 0;
write_symbols = old_write_symbols;
+ debug_hooks = old_debug_hooks;
+}
+
+\f
+/* Functions to keep track of the values hard regs had at the start of
+ the function. */
+
+rtx
+has_func_hard_reg_initial_val (fun, reg)
+ struct function *fun;
+ rtx reg;
+{
+ struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
+ int i;
+
+ if (ivs == 0)
+ return NULL_RTX;
+
+ for (i = 0; i < ivs->num_entries; i++)
+ if (rtx_equal_p (ivs->entries[i].hard_reg, reg))
+ return ivs->entries[i].pseudo;
+
+ return NULL_RTX;
+}
+
+rtx
+get_func_hard_reg_initial_val (fun, reg)
+ struct function *fun;
+ rtx reg;
+{
+ struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
+ rtx rv = has_func_hard_reg_initial_val (fun, reg);
+
+ if (rv)
+ return rv;
+
+ if (ivs == 0)
+ {
+ fun->hard_reg_initial_vals = (void *) xmalloc (sizeof (initial_value_struct));
+ ivs = fun->hard_reg_initial_vals;
+ ivs->num_entries = 0;
+ ivs->max_entries = 5;
+ ivs->entries = (initial_value_pair *) xmalloc (5 * sizeof (initial_value_pair));
+ }
+
+ if (ivs->num_entries >= ivs->max_entries)
+ {
+ ivs->max_entries += 5;
+ ivs->entries =
+ (initial_value_pair *) xrealloc (ivs->entries,
+ ivs->max_entries
+ * sizeof (initial_value_pair));
+ }
+
+ ivs->entries[ivs->num_entries].hard_reg = reg;
+ ivs->entries[ivs->num_entries].pseudo = gen_reg_rtx (GET_MODE (reg));
+
+ return ivs->entries[ivs->num_entries++].pseudo;
+}
+
+rtx
+get_hard_reg_initial_val (mode, regno)
+ enum machine_mode mode;
+ int regno;
+{
+ return get_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno));
+}
+
+rtx
+has_hard_reg_initial_val (mode, regno)
+ enum machine_mode mode;
+ int regno;
+{
+ return has_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno));
+}
+
+void
+mark_hard_reg_initial_vals (fun)
+ struct function *fun;
+{
+ struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
+ int i;
+
+ if (ivs == 0)
+ return;
+
+ for (i = 0; i < ivs->num_entries; i ++)
+ {
+ ggc_mark_rtx (ivs->entries[i].hard_reg);
+ ggc_mark_rtx (ivs->entries[i].pseudo);
+ }
+}
+
+static void
+setup_initial_hard_reg_value_integration (inl_f, remap)
+ struct function *inl_f;
+ struct inline_remap *remap;
+{
+ struct initial_value_struct *ivs = inl_f->hard_reg_initial_vals;
+ int i;
+
+ if (ivs == 0)
+ return;
+
+ for (i = 0; i < ivs->num_entries; i ++)
+ remap->reg_map[REGNO (ivs->entries[i].pseudo)]
+ = get_func_hard_reg_initial_val (cfun, ivs->entries[i].hard_reg);
+}
+
+
+void
+emit_initial_value_sets ()
+{
+ struct initial_value_struct *ivs = cfun->hard_reg_initial_vals;
+ int i;
+ rtx seq;
+
+ if (ivs == 0)
+ return;
+
+ start_sequence ();
+ for (i = 0; i < ivs->num_entries; i++)
+ emit_move_insn (ivs->entries[i].pseudo, ivs->entries[i].hard_reg);
+ seq = get_insns ();
+ end_sequence ();
+
+ emit_insns_after (seq, get_insns ());
}