/* Expands front end tree to back end RTL for GNU C-Compiler
- Copyright (C) 1987, 88, 89, 91-99, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
+ 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
This file is part of GNU CC.
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
-
/* This file handles the generation of rtl code from tree structure
at the level of the function as a whole.
It creates the rtl expressions for parameters and auto variables
#include "ggc.h"
#include "tm_p.h"
+#ifndef ACCUMULATE_OUTGOING_ARGS
+#define ACCUMULATE_OUTGOING_ARGS 0
+#endif
+
#ifndef TRAMPOLINE_ALIGNMENT
#define TRAMPOLINE_ALIGNMENT FUNCTION_BOUNDARY
#endif
/* Nonzero if function being compiled doesn't contain any calls
(ignoring the prologue and epilogue). This is set prior to
local register allocation and is valid for the remaining
- compiler passes. */
+ compiler passes. */
int current_function_is_leaf;
+/* Nonzero if function being compiled doesn't contain any instructions
+ that can throw an exception. This is set prior to final. */
+
+int current_function_nothrow;
+
/* Nonzero if function being compiled doesn't modify the stack pointer
(ignoring the prologue and epilogue). This is only valid after
- life_analysis has run. */
+ life_analysis has run. */
int current_function_sp_is_unchanging;
/* Nonzero if the function being compiled is a leaf function which only
assign_stack_local uses frame_pointer_rtx when this is nonzero. */
static int virtuals_instantiated;
-/* These variables hold pointers to functions to
- save and restore machine-specific data,
- in push_function_context and pop_function_context. */
+/* These variables hold pointers to functions to create and destroy
+ target specific, per-function data structures. */
void (*init_machine_status) PARAMS ((struct function *));
-void (*save_machine_status) PARAMS ((struct function *));
-void (*restore_machine_status) PARAMS ((struct function *));
-void (*mark_machine_status) PARAMS ((struct function *));
void (*free_machine_status) PARAMS ((struct function *));
+/* This variable holds a pointer to a function to register any
+ data items in the target specific, per-function data structure
+ that will need garbage collection. */
+void (*mark_machine_status) PARAMS ((struct function *));
/* Likewise, but for language-specific data. */
void (*init_lang_status) PARAMS ((struct function *));
struct function *all_functions = 0;
/* These arrays record the INSN_UIDs of the prologue and epilogue insns. */
-static int *prologue;
-static int *epilogue;
+static varray_type prologue;
+static varray_type epilogue;
+
+/* Array of INSN_UIDs to hold the INSN_UIDs for each sibcall epilogue
+ in this function. */
+static varray_type sibcall_epilogue;
\f
/* In order to evaluate some expressions, such as function calls returning
structures in memory, we need to temporarily allocate stack locations.
/* The rtx used to represent the address if not the address of the
slot above. May be an EXPR_LIST if multiple addresses exist. */
rtx address;
- /* The alignment (in bits) of the slot. */
+ /* The alignment (in bits) of the slot. */
int align;
/* The size, in units, of the slot. */
HOST_WIDE_INT size;
- /* The alias set for the slot. If the alias set is zero, we don't
- know anything about the alias set of the slot. We must only
- reuse a slot if it is assigned an object of the same alias set.
- Otherwise, the rest of the compiler may assume that the new use
- of the slot cannot alias the old use of the slot, which is
- false. If the slot has alias set zero, then we can't reuse the
- slot at all, since we have no idea what alias set may have been
- imposed on the memory. For example, if the stack slot is the
- call frame for an inline functioned, we have no idea what alias
- sets will be assigned to various pieces of the call frame. */
- int alias_set;
+ /* The type of the object in the slot, or zero if it doesn't correspond
+ to a type. We use this to determine whether a slot can be reused.
+ It can be reused if objects of the type of the new slot will always
+ conflict with objects of the type of the old slot. */
+ tree type;
/* The value of `sequence_rtl_expr' when this temporary is allocated. */
tree rtl_expr;
/* Non-zero if this temporary is currently in use. */
rtx new;
struct fixup_replacement *next;
};
-
+
struct insns_for_mem_entry {
/* The KEY in HE will be a MEM. */
struct hash_entry he;
static struct temp_slot *find_temp_slot_from_address PARAMS ((rtx));
static void put_reg_into_stack PARAMS ((struct function *, rtx, tree,
enum machine_mode, enum machine_mode,
- int, int, int, struct hash_table *));
-static void fixup_var_refs PARAMS ((rtx, enum machine_mode, int,
+ int, unsigned int, int,
+ struct hash_table *));
+static void schedule_fixup_var_refs PARAMS ((struct function *, rtx, tree,
+ enum machine_mode,
+ struct hash_table *));
+static void fixup_var_refs PARAMS ((rtx, enum machine_mode, int,
struct hash_table *));
static struct fixup_replacement
*find_fixup_replacement PARAMS ((struct fixup_replacement **, rtx));
-static void fixup_var_refs_insns PARAMS ((rtx, enum machine_mode, int,
- rtx, int, struct hash_table *));
+static void fixup_var_refs_insns PARAMS ((rtx, rtx, enum machine_mode,
+ int, int));
+static void fixup_var_refs_insns_with_hash
+ PARAMS ((struct hash_table *, rtx,
+ enum machine_mode, int));
+static void fixup_var_refs_insn PARAMS ((rtx, rtx, enum machine_mode,
+ int, int));
static void fixup_var_refs_1 PARAMS ((rtx, enum machine_mode, rtx *, rtx,
struct fixup_replacement **));
static rtx fixup_memory_subreg PARAMS ((rtx, rtx, int));
static void optimize_bit_field PARAMS ((rtx, rtx, rtx *));
static void instantiate_decls PARAMS ((tree, int));
static void instantiate_decls_1 PARAMS ((tree, int));
-static void instantiate_decl PARAMS ((rtx, int, int));
+static void instantiate_decl PARAMS ((rtx, HOST_WIDE_INT, int));
+static rtx instantiate_new_reg PARAMS ((rtx, HOST_WIDE_INT *));
static int instantiate_virtual_regs_1 PARAMS ((rtx *, rtx, int));
static void delete_handlers PARAMS ((void));
static void pad_to_arg_alignment PARAMS ((struct args_size *, int,
static void pad_below PARAMS ((struct args_size *, enum machine_mode,
tree));
#endif
-#ifdef ARGS_GROW_DOWNWARD
-static tree round_down PARAMS ((tree, int));
-#endif
static rtx round_trampoline_addr PARAMS ((rtx));
+static rtx adjust_trampoline_addr PARAMS ((rtx));
+static tree *identify_blocks_1 PARAMS ((rtx, tree *, tree *, tree *));
+static void reorder_blocks_0 PARAMS ((rtx));
+static void reorder_blocks_1 PARAMS ((rtx, tree, varray_type *));
static tree blocks_nreverse PARAMS ((tree));
static int all_blocks PARAMS ((tree, tree *));
+static tree *get_block_vector PARAMS ((tree, int *));
/* We always define `record_insns' even if its not used so that we
can always export `prologue_epilogue_contains'. */
-static int *record_insns PARAMS ((rtx)) ATTRIBUTE_UNUSED;
-static int contains PARAMS ((rtx, int *));
+static void record_insns PARAMS ((rtx, varray_type *)) ATTRIBUTE_UNUSED;
+static int contains PARAMS ((rtx, varray_type));
#ifdef HAVE_return
-static void emit_return_into_block PARAMS ((basic_block));
+static void emit_return_into_block PARAMS ((basic_block, rtx));
#endif
static void put_addressof_into_stack PARAMS ((rtx, struct hash_table *));
-static boolean purge_addressof_1 PARAMS ((rtx *, rtx, int, int,
+static bool purge_addressof_1 PARAMS ((rtx *, rtx, int, int,
struct hash_table *));
+static void purge_single_hard_subreg_set PARAMS ((rtx));
+#ifdef HAVE_epilogue
+static void keep_stack_depressed PARAMS ((rtx));
+#endif
static int is_addressof PARAMS ((rtx *, void *));
static struct hash_entry *insns_for_mem_newfunc PARAMS ((struct hash_entry *,
struct hash_table *,
hash_table_key));
static unsigned long insns_for_mem_hash PARAMS ((hash_table_key));
-static boolean insns_for_mem_comp PARAMS ((hash_table_key, hash_table_key));
+static bool insns_for_mem_comp PARAMS ((hash_table_key, hash_table_key));
static int insns_for_mem_walk PARAMS ((rtx *, void *));
static void compute_insns_for_mem PARAMS ((rtx, rtx, struct hash_table *));
static void mark_temp_slot PARAMS ((struct temp_slot *));
outer_function_chain = p;
p->fixup_var_refs_queue = 0;
- save_tree_status (p);
if (save_lang_status)
(*save_lang_status) (p);
- if (save_machine_status)
- (*save_machine_status) (p);
cfun = 0;
}
current_function_decl = p->decl;
reg_renumber = 0;
- restore_tree_status (p);
restore_emit_status (p);
- if (restore_machine_status)
- (*restore_machine_status) (p);
if (restore_lang_status)
(*restore_lang_status) (p);
/* Reset variables that have known state during rtx generation. */
rtx_equal_function_value_matters = 1;
virtuals_instantiated = 0;
+ generating_concat_p = 1;
}
void
free_after_compilation (f)
struct function *f;
{
+ struct temp_slot *ts;
+ struct temp_slot *next;
+
free_eh_status (f);
free_expr_status (f);
free_emit_status (f);
if (f->x_parm_reg_stack_loc)
free (f->x_parm_reg_stack_loc);
+ for (ts = f->x_temp_slots; ts; ts = next)
+ {
+ next = ts->next;
+ free (ts);
+ }
+ f->x_temp_slots = NULL;
+
f->arg_offset_rtx = NULL;
f->return_rtx = NULL;
f->internal_arg_pointer = NULL;
f->x_tail_recursion_label = NULL;
f->x_tail_recursion_reentry = NULL;
f->x_arg_pointer_save_area = NULL;
+ f->x_clobber_return_insn = NULL;
f->x_context_display = NULL;
f->x_trampoline_list = NULL;
f->x_parm_birth_insn = NULL;
f->x_last_parm_insn = NULL;
f->x_parm_reg_stack_loc = NULL;
- f->x_temp_slots = NULL;
f->fixup_var_refs_queue = NULL;
f->original_arg_vector = NULL;
f->original_decl_initial = NULL;
f->inl_last_parm_insn = NULL;
f->epilogue_delay_list = NULL;
}
-
\f
/* Allocate fixed slots in the stack frame of the current function. */
/* Allocate a stack slot of SIZE bytes and return a MEM rtx for it
with machine mode MODE.
-
+
ALIGN controls the amount of alignment for the address of the slot:
0 means according to MODE,
-1 means use BIGGEST_ALIGNMENT and round size to multiple of that,
int bigend_correction = 0;
int alignment;
- /* Allocate in the memory associated with the function in whose frame
- we are assigning. */
- if (function != cfun)
- push_obstacks (function->function_obstack,
- function->function_maybepermanent_obstack);
-
if (align == 0)
{
tree type;
- alignment = GET_MODE_ALIGNMENT (mode);
if (mode == BLKmode)
alignment = BIGGEST_ALIGNMENT;
+ else
+ alignment = GET_MODE_ALIGNMENT (mode);
/* Allow the target to (possibly) increase the alignment of this
stack slot. */
function->x_stack_slot_list
= gen_rtx_EXPR_LIST (VOIDmode, x, function->x_stack_slot_list);
- if (function != cfun)
- pop_obstacks ();
-
return x;
}
/* Wrapper around assign_stack_local_1; assign a local stack slot for the
current function. */
+
rtx
assign_stack_local (mode, size, align)
enum machine_mode mode;
with this flag. KEEP is 2 if we allocate a longer term temporary,
whose lifetime is controlled by CLEANUP_POINT_EXPRs. KEEP is 3
if we are to allocate something at an inner level to be treated as
- a variable in the block (e.g., a SAVE_EXPR).
+ a variable in the block (e.g., a SAVE_EXPR).
TYPE is the type that will be used for the stack slot. */
tree type;
{
int align;
- int alias_set;
struct temp_slot *p, *best_p = 0;
/* If SIZE is -1 it means that somebody tried to allocate a temporary
if (size == -1)
abort ();
- /* If we know the alias set for the memory that will be used, use
- it. If there's no TYPE, then we don't know anything about the
- alias set for the memory. */
- if (type)
- alias_set = get_alias_set (type);
- else
- alias_set = 0;
-
- align = GET_MODE_ALIGNMENT (mode);
if (mode == BLKmode)
align = BIGGEST_ALIGNMENT;
+ else
+ align = GET_MODE_ALIGNMENT (mode);
if (! type)
type = type_for_mode (mode, 0);
+
if (type)
align = LOCAL_ALIGNMENT (type, align);
for (p = temp_slots; p; p = p->next)
if (p->align >= align && p->size >= size && GET_MODE (p->slot) == mode
&& ! p->in_use
- && (!flag_strict_aliasing
- || (alias_set && p->alias_set == alias_set))
+ && objects_must_conflict_p (p->type, type)
&& (best_p == 0 || best_p->size > p->size
|| (best_p->size == p->size && best_p->align > p->align)))
{
/* If there are enough aligned bytes left over, make them into a new
temp_slot so that the extra bytes don't get wasted. Do this only
for BLKmode slots, so that we can be sure of the alignment. */
- if (GET_MODE (best_p->slot) == BLKmode
- /* We can't split slots if -fstrict-aliasing because the
- information about the alias set for the new slot will be
- lost. */
- && !flag_strict_aliasing)
+ if (GET_MODE (best_p->slot) == BLKmode)
{
int alignment = best_p->align / BITS_PER_UNIT;
HOST_WIDE_INT rounded_size = CEIL_ROUND (size, alignment);
if (best_p->size - rounded_size >= alignment)
{
- p = (struct temp_slot *) oballoc (sizeof (struct temp_slot));
+ p = (struct temp_slot *) xmalloc (sizeof (struct temp_slot));
p->in_use = p->addr_taken = 0;
p->size = best_p->size - rounded_size;
p->base_offset = best_p->base_offset + rounded_size;
p->align = best_p->align;
p->address = 0;
p->rtl_expr = 0;
+ p->type = best_p->type;
p->next = temp_slots;
temp_slots = p;
p = best_p;
}
-
+
/* If we still didn't find one, make a new temporary. */
if (p == 0)
{
HOST_WIDE_INT frame_offset_old = frame_offset;
- p = (struct temp_slot *) oballoc (sizeof (struct temp_slot));
+ p = (struct temp_slot *) xmalloc (sizeof (struct temp_slot));
/* We are passing an explicit alignment request to assign_stack_local.
One side effect of that is assign_stack_local will not round SIZE
align);
p->align = align;
- p->alias_set = alias_set;
/* The following slot size computation is necessary because we don't
know the actual size of the temporary slot until assign_stack_local
p->in_use = 1;
p->addr_taken = 0;
p->rtl_expr = seq_rtl_expr;
+ p->type = type;
if (keep == 2)
{
RTX_UNCHANGING_P (p->slot) = 0;
MEM_IN_STRUCT_P (p->slot) = 0;
MEM_SCALAR_P (p->slot) = 0;
- MEM_ALIAS_SET (p->slot) = 0;
+ MEM_VOLATILE_P (p->slot) = 0;
+
+ /* If we know the alias set for the memory that will be used, use
+ it. If there's no TYPE, then we don't know anything about the
+ alias set for the memory. */
+ if (type)
+ MEM_ALIAS_SET (p->slot) = get_alias_set (type);
+ else
+ MEM_ALIAS_SET (p->slot) = 0;
+
+ /* If a type is specified, set the relevant flags. */
+ if (type != 0)
+ {
+ RTX_UNCHANGING_P (p->slot) = TYPE_READONLY (type);
+ MEM_VOLATILE_P (p->slot) = TYPE_VOLATILE (type);
+ MEM_SET_IN_STRUCT_P (p->slot, AGGREGATE_TYPE_P (type));
+ }
+
return p->slot;
}
instead. This is the case for Chill variable-sized strings. */
if (size == -1 && TREE_CODE (type) == ARRAY_TYPE
&& TYPE_ARRAY_MAX_SIZE (type) != NULL_TREE
- && TREE_CODE (TYPE_ARRAY_MAX_SIZE (type)) == INTEGER_CST)
- size = TREE_INT_CST_LOW (TYPE_ARRAY_MAX_SIZE (type));
+ && host_integerp (TYPE_ARRAY_MAX_SIZE (type), 1))
+ size = tree_low_cst (TYPE_ARRAY_MAX_SIZE (type), 1);
tmp = assign_stack_temp_for_type (mode, size, keep, type);
- MEM_SET_IN_STRUCT_P (tmp, AGGREGATE_TYPE_P (type));
return tmp;
}
if (flag_strict_aliasing)
return;
- /* If there are a lot of temp slots, don't do anything unless
+ /* If there are a lot of temp slots, don't do anything unless
high levels of optimizaton. */
if (! flag_expensive_optimizations)
for (p = temp_slots, num_slots = 0; p; p = p->next, num_slots++)
}
/* Either delete Q or advance past it. */
if (delete_q)
- prev_q->next = q->next;
+ {
+ prev_q->next = q->next;
+ free (q);
+ }
else
prev_q = q;
}
return 0;
}
-
+
/* Indicate that NEW is an alternate way of referring to the temp slot
that previously was known by OLD. */
return;
}
- /* Otherwise add an alias for the temp's address. */
+ /* Otherwise add an alias for the temp's address. */
else if (p->address == 0)
p->address = new;
else
a temporary slot we know it points to. To be consistent with
the code below, we really should preserve all non-kept slots
if we can't find a match, but that seems to be much too costly. */
- if (GET_CODE (x) == REG && REGNO_POINTER_FLAG (REGNO (x)))
+ if (GET_CODE (x) == REG && REG_POINTER (x))
p = find_temp_slot_from_address (x);
/* If X is not in memory or is at a constant address, it cannot be in
for (p = temp_slots; p; p = p->next)
if (p->rtl_expr == t)
- p->in_use = 0;
+ {
+ /* If this slot is below the current TEMP_SLOT_LEVEL, then it
+ needs to be preserved. This can happen if a temporary in
+ the RTL_EXPR was addressed; preserve_temp_slots will move
+ the temporary into a higher level. */
+ if (temp_slot_level <= p->level)
+ p->in_use = 0;
+ else
+ p->rtl_expr = NULL_TREE;
+ }
combine_temp_slots ();
}
struct function *function = 0;
tree context;
int can_use_addressof;
+ int volatilep = TREE_CODE (decl) != SAVE_EXPR && TREE_THIS_VOLATILE (decl);
+ int usedp = (TREE_USED (decl)
+ || (TREE_CODE (decl) != SAVE_EXPR && DECL_INITIAL (decl) != 0));
context = decl_function_context (decl);
/* If this is a variable-size object with a pseudo to address it,
put that pseudo into the stack, if the var is nonlocal. */
- if (DECL_NONLOCAL (decl)
+ if (TREE_CODE (decl) != SAVE_EXPR && DECL_NONLOCAL (decl)
&& GET_CODE (reg) == MEM
&& GET_CODE (XEXP (reg, 0)) == REG
&& REGNO (XEXP (reg, 0)) > LAST_VIRTUAL_REGISTER)
if (can_use_addressof)
gen_mem_addressof (reg, decl);
else
- put_reg_into_stack (function, reg, TREE_TYPE (decl),
- promoted_mode, decl_mode,
- TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl) || DECL_INITIAL (decl) != 0,
- 0);
+ put_reg_into_stack (function, reg, TREE_TYPE (decl), promoted_mode,
+ decl_mode, volatilep, 0, usedp, 0);
}
else if (GET_CODE (reg) == CONCAT)
{
/* A CONCAT contains two pseudos; put them both in the stack.
- We do it so they end up consecutive. */
+ We do it so they end up consecutive.
+ We fixup references to the parts only after we fixup references
+ to the whole CONCAT, lest we do double fixups for the latter
+ references. */
enum machine_mode part_mode = GET_MODE (XEXP (reg, 0));
- tree part_type = TREE_TYPE (TREE_TYPE (decl));
+ tree part_type = type_for_mode (part_mode, 0);
+ rtx lopart = XEXP (reg, 0);
+ rtx hipart = XEXP (reg, 1);
#ifdef FRAME_GROWS_DOWNWARD
/* Since part 0 should have a lower address, do it second. */
- put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl) || DECL_INITIAL (decl) != 0,
- 0);
- put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl) || DECL_INITIAL (decl) != 0,
- 0);
+ put_reg_into_stack (function, hipart, part_type, part_mode,
+ part_mode, volatilep, 0, 0, 0);
+ put_reg_into_stack (function, lopart, part_type, part_mode,
+ part_mode, volatilep, 0, 0, 0);
#else
- put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl) || DECL_INITIAL (decl) != 0,
- 0);
- put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl), 0,
- TREE_USED (decl) || DECL_INITIAL (decl) != 0,
- 0);
+ put_reg_into_stack (function, lopart, part_type, part_mode,
+ part_mode, volatilep, 0, 0, 0);
+ put_reg_into_stack (function, hipart, part_type, part_mode,
+ part_mode, volatilep, 0, 0, 0);
#endif
/* Change the CONCAT into a combined MEM for both parts. */
PUT_CODE (reg, MEM);
- MEM_VOLATILE_P (reg) = MEM_VOLATILE_P (XEXP (reg, 0));
- MEM_ALIAS_SET (reg) = get_alias_set (decl);
+ set_mem_attributes (reg, decl, 1);
/* The two parts are in memory order already.
Use the lower parts address as ours. */
/* Prevent sharing of rtl that might lose. */
if (GET_CODE (XEXP (reg, 0)) == PLUS)
XEXP (reg, 0) = copy_rtx (XEXP (reg, 0));
+ if (usedp)
+ {
+ schedule_fixup_var_refs (function, reg, TREE_TYPE (decl),
+ promoted_mode, 0);
+ schedule_fixup_var_refs (function, lopart, part_type, part_mode, 0);
+ schedule_fixup_var_refs (function, hipart, part_type, part_mode, 0);
+ }
}
else
return;
-
+
if (current_function_check_memory_usage)
- emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
- XEXP (reg, 0), Pmode,
+ emit_library_call (chkr_set_right_libfunc, LCT_CONST_MAKE_BLOCK, VOIDmode,
+ 3, XEXP (reg, 0), Pmode,
GEN_INT (GET_MODE_SIZE (GET_MODE (reg))),
TYPE_MODE (sizetype),
GEN_INT (MEMORY_USE_RW),
tree type;
enum machine_mode promoted_mode, decl_mode;
int volatile_p;
- int original_regno;
+ unsigned int original_regno;
int used_p;
struct hash_table *ht;
{
struct function *func = function ? function : cfun;
rtx new = 0;
- int regno = original_regno;
+ unsigned int regno = original_regno;
if (regno == 0)
regno = REGNO (reg);
if (regno < func->x_max_parm_reg)
new = func->x_parm_reg_stack_loc[regno];
+
if (new == 0)
new = assign_stack_local_1 (decl_mode, GET_MODE_SIZE (decl_mode), 0, func);
previously generated stack slot, then we need to copy the bit in
case it was set for other reasons. For instance, it is set for
__builtin_va_alist. */
- MEM_SET_IN_STRUCT_P (reg,
- AGGREGATE_TYPE_P (type) || MEM_IN_STRUCT_P (new));
- MEM_ALIAS_SET (reg) = get_alias_set (type);
+ if (type)
+ {
+ MEM_SET_IN_STRUCT_P (reg,
+ AGGREGATE_TYPE_P (type) || MEM_IN_STRUCT_P (new));
+ MEM_ALIAS_SET (reg) = get_alias_set (type);
+ }
+ if (used_p)
+ schedule_fixup_var_refs (function, reg, type, promoted_mode, ht);
+}
+
+/* Make sure that all refs to the variable, previously made
+ when it was a register, are fixed up to be valid again.
+ See function above for meaning of arguments. */
- /* Now make sure that all refs to the variable, previously made
- when it was a register, are fixed up to be valid again. */
+static void
+schedule_fixup_var_refs (function, reg, type, promoted_mode, ht)
+ struct function *function;
+ rtx reg;
+ tree type;
+ enum machine_mode promoted_mode;
+ struct hash_table *ht;
+{
+ int unsigned_p = type ? TREE_UNSIGNED (type) : 0;
- if (used_p && function != 0)
+ if (function != 0)
{
struct var_refs_queue *temp;
= (struct var_refs_queue *) xmalloc (sizeof (struct var_refs_queue));
temp->modified = reg;
temp->promoted_mode = promoted_mode;
- temp->unsignedp = TREE_UNSIGNED (type);
+ temp->unsignedp = unsigned_p;
temp->next = function->fixup_var_refs_queue;
function->fixup_var_refs_queue = temp;
}
- else if (used_p)
+ else
/* Variable is local; fix it up now. */
- fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (type), ht);
+ fixup_var_refs (reg, promoted_mode, unsigned_p, ht);
}
\f
static void
struct sequence_stack *stack = seq_stack;
tree rtl_exps = rtl_expr_chain;
- /* Must scan all insns for stack-refs that exceed the limit. */
- fixup_var_refs_insns (var, promoted_mode, unsignedp, first_insn,
- stack == 0, ht);
/* If there's a hash table, it must record all uses of VAR. */
if (ht)
- return;
+ {
+ if (stack != 0)
+ abort ();
+ fixup_var_refs_insns_with_hash (ht, var, promoted_mode, unsignedp);
+ return;
+ }
+
+ fixup_var_refs_insns (first_insn, var, promoted_mode, unsignedp,
+ stack == 0);
/* Scan all pending sequences too. */
for (; stack; stack = stack->next)
{
- push_to_sequence (stack->first);
- fixup_var_refs_insns (var, promoted_mode, unsignedp,
- stack->first, stack->next != 0, 0);
+ push_to_full_sequence (stack->first, stack->last);
+ fixup_var_refs_insns (stack->first, var, promoted_mode, unsignedp,
+ stack->next != 0);
/* Update remembered end of sequence
in case we added an insn at the end. */
stack->last = get_last_insn ();
if (seq != const0_rtx && seq != 0)
{
push_to_sequence (seq);
- fixup_var_refs_insns (var, promoted_mode, unsignedp, seq, 0,
- 0);
+ fixup_var_refs_insns (seq, var, promoted_mode, unsignedp, 0);
end_sequence ();
}
}
/* Scan the catch clauses for exception handling too. */
- push_to_sequence (catch_clauses);
- fixup_var_refs_insns (var, promoted_mode, unsignedp, catch_clauses,
- 0, 0);
- end_sequence ();
+ push_to_full_sequence (catch_clauses, catch_clauses_last);
+ fixup_var_refs_insns (catch_clauses, var, promoted_mode, unsignedp, 0);
+ end_full_sequence (&catch_clauses, &catch_clauses_last);
}
\f
/* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is
if (p == 0)
{
- p = (struct fixup_replacement *) oballoc (sizeof (struct fixup_replacement));
+ p = (struct fixup_replacement *) xmalloc (sizeof (struct fixup_replacement));
p->old = x;
p->new = 0;
p->next = *replacements;
main chain of insns for the current function. */
static void
-fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel, ht)
+fixup_var_refs_insns (insn, var, promoted_mode, unsignedp, toplevel)
+ rtx insn;
rtx var;
enum machine_mode promoted_mode;
int unsignedp;
- rtx insn;
int toplevel;
+{
+ while (insn)
+ {
+ /* fixup_var_refs_insn might modify insn, so save its next
+ pointer now. */
+ rtx next = NEXT_INSN (insn);
+
+ /* CALL_PLACEHOLDERs are special; we have to switch into each of
+ the three sequences they (potentially) contain, and process
+ them recursively. The CALL_INSN itself is not interesting. */
+
+ if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+ {
+ int i;
+
+ /* Look at the Normal call, sibling call and tail recursion
+ sequences attached to the CALL_PLACEHOLDER. */
+ for (i = 0; i < 3; i++)
+ {
+ rtx seq = XEXP (PATTERN (insn), i);
+ if (seq)
+ {
+ push_to_sequence (seq);
+ fixup_var_refs_insns (seq, var, promoted_mode, unsignedp, 0);
+ XEXP (PATTERN (insn), i) = get_insns ();
+ end_sequence ();
+ }
+ }
+ }
+
+ else if (INSN_P (insn))
+ fixup_var_refs_insn (insn, var, promoted_mode, unsignedp, toplevel);
+
+ insn = next;
+ }
+}
+
+/* Look up the insns which reference VAR in HT and fix them up. Other
+ arguments are the same as fixup_var_refs_insns.
+
+ N.B. No need for special processing of CALL_PLACEHOLDERs here,
+ because the hash table will point straight to the interesting insn
+ (inside the CALL_PLACEHOLDER). */
+static void
+fixup_var_refs_insns_with_hash (ht, var, promoted_mode, unsignedp)
struct hash_table *ht;
+ rtx var;
+ enum machine_mode promoted_mode;
+ int unsignedp;
{
- rtx call_dest = 0;
- rtx insn_list = NULL_RTX;
+ struct insns_for_mem_entry *ime = (struct insns_for_mem_entry *)
+ hash_lookup (ht, var, /*create=*/0, /*copy=*/0);
+ rtx insn_list = ime->insns;
- /* If we already know which INSNs reference VAR there's no need
- to walk the entire instruction chain. */
- if (ht)
+ while (insn_list)
{
- insn_list = ((struct insns_for_mem_entry *)
- hash_lookup (ht, var, /*create=*/0, /*copy=*/0))->insns;
- insn = insn_list ? XEXP (insn_list, 0) : NULL_RTX;
+ rtx insn = XEXP (insn_list, 0);
+
+ if (INSN_P (insn))
+ fixup_var_refs_insn (insn, var, promoted_mode, unsignedp, 0);
+
insn_list = XEXP (insn_list, 1);
}
+}
- while (insn)
+
+/* Per-insn processing by fixup_var_refs_insns(_with_hash). INSN is
+ the insn under examination, VAR is the variable to fix up
+ references to, PROMOTED_MODE and UNSIGNEDP describe VAR, and
+ TOPLEVEL is nonzero if this is the main insn chain for this
+ function. */
+static void
+fixup_var_refs_insn (insn, var, promoted_mode, unsignedp, toplevel)
+ rtx insn;
+ rtx var;
+ enum machine_mode promoted_mode;
+ int unsignedp;
+ int toplevel;
+{
+ rtx call_dest = 0;
+ rtx set, prev, prev_set;
+ rtx note;
+
+ /* Remember the notes in case we delete the insn. */
+ note = REG_NOTES (insn);
+
+ /* If this is a CLOBBER of VAR, delete it.
+
+ If it has a REG_LIBCALL note, delete the REG_LIBCALL
+ and REG_RETVAL notes too. */
+ if (GET_CODE (PATTERN (insn)) == CLOBBER
+ && (XEXP (PATTERN (insn), 0) == var
+ || (GET_CODE (XEXP (PATTERN (insn), 0)) == CONCAT
+ && (XEXP (XEXP (PATTERN (insn), 0), 0) == var
+ || XEXP (XEXP (PATTERN (insn), 0), 1) == var))))
{
- rtx next = NEXT_INSN (insn);
- rtx set, prev, prev_set;
- rtx note;
+ if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0)
+ /* The REG_LIBCALL note will go away since we are going to
+ turn INSN into a NOTE, so just delete the
+ corresponding REG_RETVAL note. */
+ remove_note (XEXP (note, 0),
+ find_reg_note (XEXP (note, 0), REG_RETVAL,
+ NULL_RTX));
+
+ /* In unoptimized compilation, we shouldn't call delete_insn
+ except in jump.c doing warnings. */
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+
+ /* The insn to load VAR from a home in the arglist
+ is now a no-op. When we see it, just delete it.
+ Similarly if this is storing VAR from a register from which
+ it was loaded in the previous insn. This will occur
+ when an ADDRESSOF was made for an arglist slot. */
+ else if (toplevel
+ && (set = single_set (insn)) != 0
+ && SET_DEST (set) == var
+ /* If this represents the result of an insn group,
+ don't delete the insn. */
+ && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0
+ && (rtx_equal_p (SET_SRC (set), var)
+ || (GET_CODE (SET_SRC (set)) == REG
+ && (prev = prev_nonnote_insn (insn)) != 0
+ && (prev_set = single_set (prev)) != 0
+ && SET_DEST (prev_set) == SET_SRC (set)
+ && rtx_equal_p (SET_SRC (prev_set), var))))
+ {
+ /* In unoptimized compilation, we shouldn't call delete_insn
+ except in jump.c doing warnings. */
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ else
+ {
+ struct fixup_replacement *replacements = 0;
+ rtx next_insn = NEXT_INSN (insn);
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ if (SMALL_REGISTER_CLASSES)
{
- /* Remember the notes in case we delete the insn. */
- note = REG_NOTES (insn);
-
- /* If this is a CLOBBER of VAR, delete it.
-
- If it has a REG_LIBCALL note, delete the REG_LIBCALL
- and REG_RETVAL notes too. */
- if (GET_CODE (PATTERN (insn)) == CLOBBER
- && (XEXP (PATTERN (insn), 0) == var
- || (GET_CODE (XEXP (PATTERN (insn), 0)) == CONCAT
- && (XEXP (XEXP (PATTERN (insn), 0), 0) == var
- || XEXP (XEXP (PATTERN (insn), 0), 1) == var))))
+ /* If the insn that copies the results of a CALL_INSN
+ into a pseudo now references VAR, we have to use an
+ intermediate pseudo since we want the life of the
+ return value register to be only a single insn.
+
+ If we don't use an intermediate pseudo, such things as
+ address computations to make the address of VAR valid
+ if it is not can be placed between the CALL_INSN and INSN.
+
+ To make sure this doesn't happen, we record the destination
+ of the CALL_INSN and see if the next insn uses both that
+ and VAR. */
+
+ if (call_dest != 0 && GET_CODE (insn) == INSN
+ && reg_mentioned_p (var, PATTERN (insn))
+ && reg_mentioned_p (call_dest, PATTERN (insn)))
{
- if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0)
- /* The REG_LIBCALL note will go away since we are going to
- turn INSN into a NOTE, so just delete the
- corresponding REG_RETVAL note. */
- remove_note (XEXP (note, 0),
- find_reg_note (XEXP (note, 0), REG_RETVAL,
- NULL_RTX));
-
- /* In unoptimized compilation, we shouldn't call delete_insn
- except in jump.c doing warnings. */
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- }
+ rtx temp = gen_reg_rtx (GET_MODE (call_dest));
- /* The insn to load VAR from a home in the arglist
- is now a no-op. When we see it, just delete it.
- Similarly if this is storing VAR from a register from which
- it was loaded in the previous insn. This will occur
- when an ADDRESSOF was made for an arglist slot. */
- else if (toplevel
- && (set = single_set (insn)) != 0
- && SET_DEST (set) == var
- /* If this represents the result of an insn group,
- don't delete the insn. */
- && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0
- && (rtx_equal_p (SET_SRC (set), var)
- || (GET_CODE (SET_SRC (set)) == REG
- && (prev = prev_nonnote_insn (insn)) != 0
- && (prev_set = single_set (prev)) != 0
- && SET_DEST (prev_set) == SET_SRC (set)
- && rtx_equal_p (SET_SRC (prev_set), var))))
- {
- /* In unoptimized compilation, we shouldn't call delete_insn
- except in jump.c doing warnings. */
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- if (insn == last_parm_insn)
- last_parm_insn = PREV_INSN (next);
+ emit_insn_before (gen_move_insn (temp, call_dest), insn);
+
+ PATTERN (insn) = replace_rtx (PATTERN (insn),
+ call_dest, temp);
}
+
+ if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == SET)
+ call_dest = SET_DEST (PATTERN (insn));
+ else if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == PARALLEL
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
+ call_dest = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
else
+ call_dest = 0;
+ }
+
+ /* See if we have to do anything to INSN now that VAR is in
+ memory. If it needs to be loaded into a pseudo, use a single
+ pseudo for the entire insn in case there is a MATCH_DUP
+ between two operands. We pass a pointer to the head of
+ a list of struct fixup_replacements. If fixup_var_refs_1
+ needs to allocate pseudos or replacement MEMs (for SUBREGs),
+ it will record them in this list.
+
+ If it allocated a pseudo for any replacement, we copy into
+ it here. */
+
+ fixup_var_refs_1 (var, promoted_mode, &PATTERN (insn), insn,
+ &replacements);
+
+ /* If this is last_parm_insn, and any instructions were output
+ after it to fix it up, then we must set last_parm_insn to
+ the last such instruction emitted. */
+ if (insn == last_parm_insn)
+ last_parm_insn = PREV_INSN (next_insn);
+
+ while (replacements)
+ {
+ struct fixup_replacement *next;
+
+ if (GET_CODE (replacements->new) == REG)
{
- struct fixup_replacement *replacements = 0;
- rtx next_insn = NEXT_INSN (insn);
+ rtx insert_before;
+ rtx seq;
- if (SMALL_REGISTER_CLASSES)
- {
- /* If the insn that copies the results of a CALL_INSN
- into a pseudo now references VAR, we have to use an
- intermediate pseudo since we want the life of the
- return value register to be only a single insn.
-
- If we don't use an intermediate pseudo, such things as
- address computations to make the address of VAR valid
- if it is not can be placed between the CALL_INSN and INSN.
-
- To make sure this doesn't happen, we record the destination
- of the CALL_INSN and see if the next insn uses both that
- and VAR. */
-
- if (call_dest != 0 && GET_CODE (insn) == INSN
- && reg_mentioned_p (var, PATTERN (insn))
- && reg_mentioned_p (call_dest, PATTERN (insn)))
- {
- rtx temp = gen_reg_rtx (GET_MODE (call_dest));
+ /* OLD might be a (subreg (mem)). */
+ if (GET_CODE (replacements->old) == SUBREG)
+ replacements->old
+ = fixup_memory_subreg (replacements->old, insn, 0);
+ else
+ replacements->old
+ = fixup_stack_1 (replacements->old, insn);
- emit_insn_before (gen_move_insn (temp, call_dest), insn);
+ insert_before = insn;
- PATTERN (insn) = replace_rtx (PATTERN (insn),
- call_dest, temp);
- }
-
- if (GET_CODE (insn) == CALL_INSN
- && GET_CODE (PATTERN (insn)) == SET)
- call_dest = SET_DEST (PATTERN (insn));
- else if (GET_CODE (insn) == CALL_INSN
- && GET_CODE (PATTERN (insn)) == PARALLEL
- && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
- call_dest = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
- else
- call_dest = 0;
- }
+ /* If we are changing the mode, do a conversion.
+ This might be wasteful, but combine.c will
+ eliminate much of the waste. */
- /* See if we have to do anything to INSN now that VAR is in
- memory. If it needs to be loaded into a pseudo, use a single
- pseudo for the entire insn in case there is a MATCH_DUP
- between two operands. We pass a pointer to the head of
- a list of struct fixup_replacements. If fixup_var_refs_1
- needs to allocate pseudos or replacement MEMs (for SUBREGs),
- it will record them in this list.
-
- If it allocated a pseudo for any replacement, we copy into
- it here. */
-
- fixup_var_refs_1 (var, promoted_mode, &PATTERN (insn), insn,
- &replacements);
-
- /* If this is last_parm_insn, and any instructions were output
- after it to fix it up, then we must set last_parm_insn to
- the last such instruction emitted. */
- if (insn == last_parm_insn)
- last_parm_insn = PREV_INSN (next_insn);
-
- while (replacements)
+ if (GET_MODE (replacements->new)
+ != GET_MODE (replacements->old))
{
- if (GET_CODE (replacements->new) == REG)
- {
- rtx insert_before;
- rtx seq;
-
- /* OLD might be a (subreg (mem)). */
- if (GET_CODE (replacements->old) == SUBREG)
- replacements->old
- = fixup_memory_subreg (replacements->old, insn, 0);
- else
- replacements->old
- = fixup_stack_1 (replacements->old, insn);
-
- insert_before = insn;
-
- /* If we are changing the mode, do a conversion.
- This might be wasteful, but combine.c will
- eliminate much of the waste. */
-
- if (GET_MODE (replacements->new)
- != GET_MODE (replacements->old))
- {
- start_sequence ();
- convert_move (replacements->new,
- replacements->old, unsignedp);
- seq = gen_sequence ();
- end_sequence ();
- }
- else
- seq = gen_move_insn (replacements->new,
- replacements->old);
-
- emit_insn_before (seq, insert_before);
- }
-
- replacements = replacements->next;
+ start_sequence ();
+ convert_move (replacements->new,
+ replacements->old, unsignedp);
+ seq = gen_sequence ();
+ end_sequence ();
}
- }
+ else
+ seq = gen_move_insn (replacements->new,
+ replacements->old);
- /* Also fix up any invalid exprs in the REG_NOTES of this insn.
- But don't touch other insns referred to by reg-notes;
- we will get them elsewhere. */
- while (note)
- {
- if (GET_CODE (note) != INSN_LIST)
- XEXP (note, 0)
- = walk_fixup_memory_subreg (XEXP (note, 0), insn, 1);
- note = XEXP (note, 1);
+ emit_insn_before (seq, insert_before);
}
- }
- if (!ht)
- insn = next;
- else if (insn_list)
- {
- insn = XEXP (insn_list, 0);
- insn_list = XEXP (insn_list, 1);
+ next = replacements->next;
+ free (replacements);
+ replacements = next;
}
- else
- insn = NULL_RTX;
+ }
+
+ /* Also fix up any invalid exprs in the REG_NOTES of this insn.
+ But don't touch other insns referred to by reg-notes;
+ we will get them elsewhere. */
+ while (note)
+ {
+ if (GET_CODE (note) != INSN_LIST)
+ XEXP (note, 0)
+ = walk_fixup_memory_subreg (XEXP (note, 0), insn, 1);
+ note = XEXP (note, 1);
}
}
\f
/* VAR is a MEM that used to be a pseudo register with mode PROMOTED_MODE.
- See if the rtx expression at *LOC in INSN needs to be changed.
+ See if the rtx expression at *LOC in INSN needs to be changed.
REPLACEMENTS is a pointer to a list head that starts out zero, but may
contain a list of original rtx's and replacements. If we find that we need
to modify this insn by replacing a memory reference with a pseudo or by
making a new MEM to implement a SUBREG, we consult that list to see if
we have already chosen a replacement. If none has already been allocated,
- we allocate it and update the list. fixup_var_refs_insns will copy VAR
+ we allocate it and update the list. fixup_var_refs_insn will copy VAR
or the SUBREG, as appropriate, to the pseudo. */
static void
/* That failed. Fall back on force_operand and hope. */
start_sequence ();
- force_operand (sub, y);
+ sub = force_operand (sub, y);
+ if (sub != y)
+ emit_insn (gen_move_insn (y, sub));
seq = gen_sequence ();
end_sequence ();
}
case MEM:
if (var == x)
{
- /* If we already have a replacement, use it. Otherwise,
+ /* If we already have a replacement, use it. Otherwise,
try to fix up this address in case it is invalid. */
replacement = find_fixup_replacement (replacements, var);
/* Unless we are forcing memory to register or we changed the mode,
we can leave things the way they are if the insn is valid. */
-
+
INSN_CODE (insn) = -1;
if (! flag_force_mem && GET_MODE (x) == promoted_mode
&& recog_memoized (insn) >= 0)
replacement->new = copy_most_rtx (x, var);
*loc = x = replacement->new;
+ code = GET_CODE (x);
}
break;
if (replacement->new == 0)
replacement->new = gen_reg_rtx (GET_MODE (var));
SUBREG_REG (tem) = replacement->new;
+
+ /* The following code works only if we have a MEM, so we
+ need to handle the subreg here. We directly substitute
+ it assuming that a subreg must be OK here. We already
+ scheduled a replacement to copy the mem into the
+ subreg. */
+ XEXP (x, 0) = tem;
+ return;
}
else
tem = fixup_memory_subreg (tem, insn, 0);
newmem = gen_rtx_MEM (wanted_mode,
plus_constant (XEXP (tem, 0), offset));
- RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem);
MEM_COPY_ATTRIBUTES (newmem, tem);
/* Make the change and see if the insn remains valid. */
return;
}
break;
-
+
case SUBREG:
if (SUBREG_REG (x) == var)
{
}
/* If this SUBREG makes VAR wider, it has become a paradoxical
- SUBREG with VAR in memory, but these aren't allowed at this
+ SUBREG with VAR in memory, but these aren't allowed at this
stage of the compilation. So load VAR into a pseudo and take
a SUBREG of that pseudo. */
if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (var)))
*loc = replacement->new;
return;
}
-
+
replacement->new = *loc = fixup_memory_subreg (x, insn, 0);
INSN_CODE (insn) = -1;
insn);
break;
}
-
+
{
rtx dest = SET_DEST (x);
rtx src = SET_SRC (x);
/* If VAR does not appear at the top level of the SET
just scan the lower levels of the tree. */
- if (src != var && dest != var)
+ if (src != var && dest != var)
break;
/* We will need to rerecognize this insn. */
newmem = gen_rtx_MEM (wanted_mode,
plus_constant (XEXP (tem, 0),
offset));
- RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem);
MEM_COPY_ATTRIBUTES (newmem, tem);
/* Make the change and see if the insn remains valid. */
INSN_CODE (insn) = -1;
XEXP (outerdest, 0) = newmem;
XEXP (outerdest, 2) = GEN_INT (pos);
-
+
if (recog_memoized (insn) >= 0)
return;
-
+
/* Otherwise, restore old position. XEXP (x, 0) will be
restored later. */
XEXP (outerdest, 2) = old_pos;
&& GET_MODE (var) == promoted_mode
&& x == single_set (insn))
{
- rtx pat;
+ rtx pat, last;
replacement = find_fixup_replacement (replacements, SET_SRC (x));
if (replacement->new)
copy SET_SRC (x) to SET_DEST (x) in some way. So
we generate the move and see whether it requires more
than one insn. If it does, we emit those insns and
- delete INSN. Otherwise, we an just replace the pattern
+ delete INSN. Otherwise, we an just replace the pattern
of INSN; we have already verified above that INSN has
no other function that to do X. */
pat = gen_move_insn (SET_DEST (x), SET_SRC (x));
if (GET_CODE (pat) == SEQUENCE)
{
- emit_insn_after (pat, insn);
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
+ last = emit_insn_before (pat, insn);
+
+ /* INSN might have REG_RETVAL or other important notes, so
+ we need to store the pattern of the last insn in the
+ sequence into INSN similarly to the normal case. LAST
+ should not have REG_NOTES, but we allow them if INSN has
+ no REG_NOTES. */
+ if (REG_NOTES (last) && REG_NOTES (insn))
+ abort ();
+ if (REG_NOTES (last))
+ REG_NOTES (insn) = REG_NOTES (last);
+ PATTERN (insn) = PATTERN (last);
+
+ PUT_CODE (last, NOTE);
+ NOTE_LINE_NUMBER (last) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (last) = 0;
}
else
PATTERN (insn) = pat;
&& GET_MODE (var) == promoted_mode
&& x == single_set (insn))
{
- rtx pat;
+ rtx pat, last;
if (GET_CODE (SET_DEST (x)) == SUBREG)
SET_DEST (x) = fixup_memory_subreg (SET_DEST (x), insn, 0);
pat = gen_move_insn (SET_DEST (x), SET_SRC (x));
if (GET_CODE (pat) == SEQUENCE)
{
- emit_insn_after (pat, insn);
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
+ last = emit_insn_before (pat, insn);
+
+ /* INSN might have REG_RETVAL or other important notes, so
+ we need to store the pattern of the last insn in the
+ sequence into INSN similarly to the normal case. LAST
+ should not have REG_NOTES, but we allow them if INSN has
+ no REG_NOTES. */
+ if (REG_NOTES (last) && REG_NOTES (insn))
+ abort ();
+ if (REG_NOTES (last))
+ REG_NOTES (insn) = REG_NOTES (last);
+ PATTERN (insn) = PATTERN (last);
+
+ PUT_CODE (last, NOTE);
+ NOTE_LINE_NUMBER (last) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (last) = 0;
}
else
PATTERN (insn) = pat;
If X itself is a (SUBREG (MEM ...) ...), return the replacement expression.
Otherwise return X, with its contents possibly altered.
- If any insns must be emitted to compute NEWADDR, put them before INSN.
+ If any insns must be emitted to compute NEWADDR, put them before INSN.
UNCRITICAL is as in fixup_memory_subreg. */
#ifndef STACK_DYNAMIC_OFFSET
-#ifdef ACCUMULATE_OUTGOING_ARGS
/* The bottom of the stack points to the actual arguments. If
REG_PARM_STACK_SPACE is defined, this includes the space for the register
parameters. However, if OUTGOING_REG_PARM_STACK space is not defined,
- stack space for register parameters is not pushed by the caller, but
+ stack space for register parameters is not pushed by the caller, but
rather part of the fixed stack areas and hence not included in
`current_function_outgoing_args_size'. Nevertheless, we must allow
for it when allocating stack dynamic objects. */
#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
#define STACK_DYNAMIC_OFFSET(FNDECL) \
-(current_function_outgoing_args_size \
- + REG_PARM_STACK_SPACE (FNDECL) + (STACK_POINTER_OFFSET))
+((ACCUMULATE_OUTGOING_ARGS \
+ ? (current_function_outgoing_args_size + REG_PARM_STACK_SPACE (FNDECL)) : 0)\
+ + (STACK_POINTER_OFFSET)) \
#else
#define STACK_DYNAMIC_OFFSET(FNDECL) \
-(current_function_outgoing_args_size + (STACK_POINTER_OFFSET))
-#endif
-
-#else
-#define STACK_DYNAMIC_OFFSET(FNDECL) STACK_POINTER_OFFSET
+((ACCUMULATE_OUTGOING_ARGS ? current_function_outgoing_args_size : 0) \
+ + (STACK_POINTER_OFFSET))
#endif
#endif
-/* On a few machines, the CFA coincides with the arg pointer. */
+/* On most machines, the CFA coincides with the first incoming parm. */
#ifndef ARG_POINTER_CFA_OFFSET
-#define ARG_POINTER_CFA_OFFSET 0
+#define ARG_POINTER_CFA_OFFSET(FNDECL) FIRST_PARM_OFFSET (FNDECL)
#endif
-
/* Build up a (MEM (ADDRESSOF (REG))) rtx for a register REG that just had
its address taken. DECL is the decl for the object stored in the
register, for later use if we do need to force REG into the stack.
rtx reg;
tree decl;
{
- tree type = TREE_TYPE (decl);
rtx r = gen_rtx_ADDRESSOF (Pmode, gen_reg_rtx (GET_MODE (reg)),
REGNO (reg), decl);
RTX_UNCHANGING_P (XEXP (r, 0)) = RTX_UNCHANGING_P (reg);
PUT_CODE (reg, MEM);
- PUT_MODE (reg, DECL_MODE (decl));
XEXP (reg, 0) = r;
- MEM_VOLATILE_P (reg) = TREE_SIDE_EFFECTS (decl);
- MEM_SET_IN_STRUCT_P (reg, AGGREGATE_TYPE_P (type));
- MEM_ALIAS_SET (reg) = get_alias_set (decl);
+ if (decl)
+ {
+ tree type = TREE_TYPE (decl);
+
+ PUT_MODE (reg, DECL_MODE (decl));
+ MEM_VOLATILE_P (reg) = TREE_SIDE_EFFECTS (decl);
+ MEM_SET_IN_STRUCT_P (reg, AGGREGATE_TYPE_P (type));
+ MEM_ALIAS_SET (reg) = get_alias_set (decl);
- if (TREE_USED (decl) || DECL_INITIAL (decl) != 0)
- fixup_var_refs (reg, GET_MODE (reg), TREE_UNSIGNED (type), 0);
+ if (TREE_USED (decl) || DECL_INITIAL (decl) != 0)
+ fixup_var_refs (reg, GET_MODE (reg), TREE_UNSIGNED (type), 0);
+ }
+ else
+ {
+ /* We have no alias information about this newly created MEM. */
+ MEM_ALIAS_SET (reg) = 0;
+
+ fixup_var_refs (reg, GET_MODE (reg), 0, 0);
+ }
return reg;
}
/* If DECL has an RTL that is an ADDRESSOF rtx, put it into the stack. */
-#if 0
void
flush_addressof (decl)
tree decl;
&& GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == REG)
put_addressof_into_stack (XEXP (DECL_RTL (decl), 0), 0);
}
-#endif
/* Force the register pointed to by R, an ADDRESSOF rtx, into the stack. */
rtx r;
struct hash_table *ht;
{
- tree decl = ADDRESSOF_DECL (r);
+ tree decl, type;
+ int volatile_p, used_p;
+
rtx reg = XEXP (r, 0);
if (GET_CODE (reg) != REG)
abort ();
- put_reg_into_stack (0, reg, TREE_TYPE (decl), GET_MODE (reg),
- DECL_MODE (decl), TREE_SIDE_EFFECTS (decl),
- ADDRESSOF_REGNO (r),
- TREE_USED (decl) || DECL_INITIAL (decl) != 0, ht);
+ decl = ADDRESSOF_DECL (r);
+ if (decl)
+ {
+ type = TREE_TYPE (decl);
+ volatile_p = (TREE_CODE (decl) != SAVE_EXPR
+ && TREE_THIS_VOLATILE (decl));
+ used_p = (TREE_USED (decl)
+ || (TREE_CODE (decl) != SAVE_EXPR
+ && DECL_INITIAL (decl) != 0));
+ }
+ else
+ {
+ type = NULL_TREE;
+ volatile_p = 0;
+ used_p = 1;
+ }
+
+ put_reg_into_stack (0, reg, type, GET_MODE (reg), GET_MODE (reg),
+ volatile_p, ADDRESSOF_REGNO (r), used_p, ht);
}
/* List of replacements made below in purge_addressof_1 when creating
corresponding (ADDRESSOF (REG ...)) and value is a substitution for
the all pattern. List PURGE_BITFIELD_ADDRESSOF_REPLACEMENTS is not
enough in complex cases, e.g. when some field values can be
- extracted by usage MEM with narrower mode. */
+ extracted by usage MEM with narrower mode. */
static rtx purge_addressof_replacements;
/* Helper function for purge_addressof. See if the rtx expression at *LOC
the stack. If the function returns FALSE then the replacement could not
be made. */
-static boolean
+static bool
purge_addressof_1 (loc, insn, force, store, ht)
rtx *loc;
rtx insn;
RTX_CODE code;
int i, j;
const char *fmt;
- boolean result = true;
+ bool result = true;
/* Re-start here to avoid recursion in common cases. */
restart:
if (validate_change (insn, loc, sub, 0)
|| validate_replace_rtx (x, sub, insn))
return true;
-
+
start_sequence ();
sub = force_operand (sub, NULL_RTX);
if (! validate_change (insn, loc, sub, 0)
{
sub2 = gen_rtx_MEM (GET_MODE (x), copy_rtx (XEXP (sub, 0)));
MEM_COPY_ATTRIBUTES (sub2, sub);
- RTX_UNCHANGING_P (sub2) = RTX_UNCHANGING_P (sub);
sub = sub2;
}
else if (GET_CODE (sub) == REG
return true;
}
- /* See comment for purge_addressof_replacements. */
+ /* See comment for purge_addressof_replacements. */
for (tem = purge_addressof_replacements;
tem != NULL_RTX;
tem = XEXP (XEXP (tem, 1), 1))
/* It can happen that the note may speak of things
in a wider (or just different) mode than the
code did. This is especially true of
- REG_RETVAL. */
+ REG_RETVAL. */
if (GET_CODE (z) == SUBREG && SUBREG_WORD (z) == 0)
z = SUBREG_REG (z);
-
+
if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD
&& (GET_MODE_SIZE (GET_MODE (x))
> GET_MODE_SIZE (GET_MODE (z))))
{
/* This can occur as a result in invalid
- pointer casts, e.g. float f; ...
+ pointer casts, e.g. float f; ...
*(long long int *)&f.
??? We could emit a warning here, but
without a line number that wouldn't be
seq = gen_sequence ();
end_sequence ();
emit_insn_before (seq, insn);
- compute_insns_for_mem (p ? NEXT_INSN (p) : get_insns (),
+ compute_insns_for_mem (p ? NEXT_INSN (p) : get_insns (),
insn, ht);
-
+
start_sequence ();
store_bit_field (sub, size_x, 0, GET_MODE (x),
val, GET_MODE_SIZE (GET_MODE (sub)),
- GET_MODE_SIZE (GET_MODE (sub)));
+ GET_MODE_ALIGNMENT (GET_MODE (sub)));
/* Make sure to unshare any shared rtl that store_bit_field
might have created. */
end_sequence ();
p = emit_insn_after (seq, insn);
if (NEXT_INSN (insn))
- compute_insns_for_mem (NEXT_INSN (insn),
+ compute_insns_for_mem (NEXT_INSN (insn),
p ? NEXT_INSN (p) : NULL_RTX,
ht);
}
return result;
}
- /* Scan all subexpressions. */
+ /* Scan all subexpressions. */
fmt = GET_RTX_FORMAT (code);
for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
{
/* Return non-zero if K1 and K2 (two REGs) are the same. */
-static boolean
+static bool
insns_for_mem_comp (k1, k2)
hash_table_key k1;
hash_table_key k2;
rtx *r;
void *data;
{
- struct insns_for_mem_walk_info *ifmwi
+ struct insns_for_mem_walk_info *ifmwi
= (struct insns_for_mem_walk_info *) data;
if (ifmwi->pass == 0 && *r && GET_CODE (*r) == ADDRESSOF
else if (ifmwi->pass == 1 && *r && GET_CODE (*r) == REG)
{
/* Lookup this MEM in the hashtable, creating it if necessary. */
- struct insns_for_mem_entry *ifme
+ struct insns_for_mem_entry *ifme
= (struct insns_for_mem_entry *) hash_lookup (ifmwi->ht,
*r,
/*create=*/0,
we process the INSNs in order, we know that if we have
recorded it it must be at the front of the list. */
if (ifme && (!ifme->insns || XEXP (ifme->insns, 0) != ifmwi->insn))
- {
- /* We do the allocation on the same obstack as is used for
- the hash table since this memory will not be used once
- the hash table is deallocated. */
- push_obstacks (&ifmwi->ht->memory, &ifmwi->ht->memory);
- ifme->insns = gen_rtx_EXPR_LIST (VOIDmode, ifmwi->insn,
- ifme->insns);
- pop_obstacks ();
- }
+ ifme->insns = gen_rtx_EXPR_LIST (VOIDmode, ifmwi->insn,
+ ifme->insns);
}
return 0;
for (ifmwi.pass = 0; ifmwi.pass < 2; ++ifmwi.pass)
for (insn = insns; insn != last_insn; insn = NEXT_INSN (insn))
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ if (INSN_P (insn))
{
ifmwi.insn = insn;
for_each_rtx (&insn, insns_for_mem_walk, &ifmwi);
Returns true iff the rtl is an ADDRESSOF. */
static int
is_addressof (rtl, data)
- rtx * rtl;
- void * data ATTRIBUTE_UNUSED;
+ rtx *rtl;
+ void *data ATTRIBUTE_UNUSED;
{
- return GET_CODE (* rtl) == ADDRESSOF;
+ return GET_CODE (*rtl) == ADDRESSOF;
}
/* Eliminate all occurrences of ADDRESSOF from INSNS. Elide any remaining
{
rtx insn;
struct hash_table ht;
-
+
/* When we actually purge ADDRESSOFs, we turn REGs into MEMs. That
requires a fixup pass over the instruction stream to correct
INSNs that depended on the REG being a REG, and not a MEM. But,
- these fixup passes are slow. Furthermore, more MEMs are not
+ these fixup passes are slow. Furthermore, most MEMs are not
mentioned in very many instructions. So, we speed up the process
by pre-calculating which REGs occur in which INSNs; that allows
us to perform the fixup passes much more quickly. */
- hash_table_init (&ht,
+ hash_table_init (&ht,
insns_for_mem_newfunc,
insns_for_mem_hash,
insns_for_mem_comp);
/* If we could not replace the ADDRESSOFs in the insn,
something is wrong. */
abort ();
-
+
if (! purge_addressof_1 (®_NOTES (insn), NULL_RTX, 0, 0, &ht))
{
/* If we could not replace the ADDRESSOFs in the insn's notes,
safe to delete the notes here, and instead we abort. */
if (REG_NOTE_KIND (note) == REG_RETVAL)
abort ();
- if (for_each_rtx (& note, is_addressof, NULL))
+ if (for_each_rtx (¬e, is_addressof, NULL))
remove_note (insn, note);
}
}
hash_table_free (&ht);
purge_bitfield_addressof_replacements = 0;
purge_addressof_replacements = 0;
+
+ /* REGs are shared. purge_addressof will destructively replace a REG
+ with a MEM, which creates shared MEMs.
+
+ Unfortunately, the children of put_reg_into_stack assume that MEMs
+ referring to the same stack slot are shared (fixup_var_refs and
+ the associated hash table code).
+
+ So, we have to do another unsharing pass after we have flushed any
+ REGs that had their address taken into the stack.
+
+ It may be worth tracking whether or not we converted any REGs into
+ MEMs to avoid this overhead when it is not needed. */
+ unshare_all_rtl_again (get_insns ());
+}
+\f
+/* Convert a SET of a hard subreg to a set of the appropriet hard
+ register. A subroutine of purge_hard_subreg_sets. */
+
+static void
+purge_single_hard_subreg_set (pattern)
+ rtx pattern;
+{
+ rtx reg = SET_DEST (pattern);
+ enum machine_mode mode = GET_MODE (SET_DEST (pattern));
+ int word = 0;
+
+ while (GET_CODE (reg) == SUBREG)
+ {
+ word += SUBREG_WORD (reg);
+ reg = SUBREG_REG (reg);
+ }
+
+ if (REGNO (reg) < FIRST_PSEUDO_REGISTER)
+ {
+ reg = gen_rtx_REG (mode, REGNO (reg) + word);
+ SET_DEST (pattern) = reg;
+ }
+}
+
+/* Eliminate all occurrences of SETs of hard subregs from INSNS. The
+ only such SETs that we expect to see are those left in because
+ integrate can't handle sets of parts of a return value register.
+
+ We don't use alter_subreg because we only want to eliminate subregs
+ of hard registers. */
+
+void
+purge_hard_subreg_sets (insn)
+ rtx insn;
+{
+ for (; insn; insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn))
+ {
+ rtx pattern = PATTERN (insn);
+ switch (GET_CODE (pattern))
+ {
+ case SET:
+ if (GET_CODE (SET_DEST (pattern)) == SUBREG)
+ purge_single_hard_subreg_set (pattern);
+ break;
+ case PARALLEL:
+ {
+ int j;
+ for (j = XVECLEN (pattern, 0) - 1; j >= 0; j--)
+ {
+ rtx inner_pattern = XVECEXP (pattern, 0, j);
+ if (GET_CODE (inner_pattern) == SET
+ && GET_CODE (SET_DEST (inner_pattern)) == SUBREG)
+ purge_single_hard_subreg_set (inner_pattern);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
}
\f
/* Pass through the INSNS of function FNDECL and convert virtual register
rtx insns;
{
rtx insn;
- int i;
+ unsigned int i;
/* Compute the offsets to use for this function. */
in_arg_offset = FIRST_PARM_OFFSET (fndecl);
var_offset = STARTING_FRAME_OFFSET;
dynamic_offset = STACK_DYNAMIC_OFFSET (fndecl);
out_arg_offset = STACK_POINTER_OFFSET;
- cfa_offset = ARG_POINTER_CFA_OFFSET;
+ cfa_offset = ARG_POINTER_CFA_OFFSET (fndecl);
/* Scan all variables and parameters of this function. For each that is
in memory, instantiate all virtual registers if the result is a valid
{
instantiate_virtual_regs_1 (&PATTERN (insn), insn, 1);
instantiate_virtual_regs_1 (®_NOTES (insn), NULL_RTX, 0);
+ /* Instantiate any virtual registers in CALL_INSN_FUNCTION_USAGE. */
+ if (GET_CODE (insn) == CALL_INSN)
+ instantiate_virtual_regs_1 (&CALL_INSN_FUNCTION_USAGE (insn),
+ NULL_RTX, 0);
}
/* Instantiate the stack slots for the parm registers, for later use in
{
tree decl;
- if (DECL_SAVED_INSNS (fndecl))
- /* When compiling an inline function, the obstack used for
- rtl allocation is the maybepermanent_obstack. Calling
- `resume_temporary_allocation' switches us back to that
- obstack while we process this function's parameters. */
- resume_temporary_allocation ();
-
/* Process all parameters of the function. */
for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
{
HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl));
- instantiate_decl (DECL_RTL (decl), size, valid_only);
+ instantiate_decl (DECL_RTL (decl), size, valid_only);
/* If the parameter was promoted, then the incoming RTL mode may be
larger than the declared type size. We must use the larger of
/* Now process all variables defined in the function or its subblocks. */
instantiate_decls_1 (DECL_INITIAL (fndecl), valid_only);
-
- if (DECL_INLINE (fndecl) || DECL_DEFER_OUTPUT (fndecl))
- {
- /* Save all rtl allocated for this function by raising the
- high-water mark on the maybepermanent_obstack. */
- preserve_data ();
- /* All further rtl allocation is now done in the current_obstack. */
- rtl_in_current_obstack ();
- }
}
/* Subroutine of instantiate_decls: Process all decls in the given
static void
instantiate_decl (x, size, valid_only)
rtx x;
- int size;
+ HOST_WIDE_INT size;
int valid_only;
{
enum machine_mode mode;
instantiate_virtual_regs_1 (&addr, NULL_RTX, 0);
- if (valid_only)
+ if (valid_only && size >= 0)
{
+ unsigned HOST_WIDE_INT decl_size = size;
+
/* Now verify that the resulting address is valid for every integer or
floating-point mode up to and including SIZE bytes long. We do this
since the object might be accessed in any mode and frame addresses
are shared. */
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
- mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
+ mode != VOIDmode && GET_MODE_SIZE (mode) <= decl_size;
mode = GET_MODE_WIDER_MODE (mode))
if (! memory_address_p (mode, addr))
return;
for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
- mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
+ mode != VOIDmode && GET_MODE_SIZE (mode) <= decl_size;
mode = GET_MODE_WIDER_MODE (mode))
if (! memory_address_p (mode, addr))
return;
XEXP (x, 0) = addr;
}
\f
-/* Given a pointer to a piece of rtx and an optional pointer to the
- containing object, instantiate any virtual registers present in it.
+/* Given a piece of RTX and a pointer to a HOST_WIDE_INT, if the RTX
+ is a virtual register, return the requivalent hard register and set the
+ offset indirectly through the pointer. Otherwise, return 0. */
- If EXTRA_INSNS, we always do the replacement and generate
- any extra insns before OBJECT. If it zero, we do nothing if replacement
- is not valid.
+static rtx
+instantiate_new_reg (x, poffset)
+ rtx x;
+ HOST_WIDE_INT *poffset;
+{
+ rtx new;
+ HOST_WIDE_INT offset;
+
+ if (x == virtual_incoming_args_rtx)
+ new = arg_pointer_rtx, offset = in_arg_offset;
+ else if (x == virtual_stack_vars_rtx)
+ new = frame_pointer_rtx, offset = var_offset;
+ else if (x == virtual_stack_dynamic_rtx)
+ new = stack_pointer_rtx, offset = dynamic_offset;
+ else if (x == virtual_outgoing_args_rtx)
+ new = stack_pointer_rtx, offset = out_arg_offset;
+ else if (x == virtual_cfa_rtx)
+ new = arg_pointer_rtx, offset = cfa_offset;
+ else
+ return 0;
+
+ *poffset = offset;
+ return new;
+}
+\f
+/* Given a pointer to a piece of rtx and an optional pointer to the
+ containing object, instantiate any virtual registers present in it.
+
+ If EXTRA_INSNS, we always do the replacement and generate
+ any extra insns before OBJECT. If it zero, we do nothing if replacement
+ is not valid.
Return 1 if we either had nothing to do or if we were able to do the
- needed replacement. Return 0 otherwise; we only return zero if
+ needed replacement. Return 0 otherwise; we only return zero if
EXTRA_INSNS is zero.
We first try some simple transformations to avoid the creation of extra
the actual register should receive the source minus the
appropriate offset. This is used, for example, in the handling
of non-local gotos. */
- if (SET_DEST (x) == virtual_incoming_args_rtx)
- new = arg_pointer_rtx, offset = - in_arg_offset;
- else if (SET_DEST (x) == virtual_stack_vars_rtx)
- new = frame_pointer_rtx, offset = - var_offset;
- else if (SET_DEST (x) == virtual_stack_dynamic_rtx)
- new = stack_pointer_rtx, offset = - dynamic_offset;
- else if (SET_DEST (x) == virtual_outgoing_args_rtx)
- new = stack_pointer_rtx, offset = - out_arg_offset;
- else if (SET_DEST (x) == virtual_cfa_rtx)
- new = arg_pointer_rtx, offset = - cfa_offset;
-
- if (new)
+ if ((new = instantiate_new_reg (SET_DEST (x), &offset)) != 0)
{
rtx src = SET_SRC (x);
+ /* We are setting the register, not using it, so the relevant
+ offset is the negative of the offset to use were we using
+ the register. */
+ offset = - offset;
instantiate_virtual_regs_1 (&src, NULL_RTX, 0);
/* The only valid sources here are PLUS or REG. Just do
/* Check for (plus (plus VIRT foo) (const_int)) first. */
if (GET_CODE (XEXP (x, 0)) == PLUS)
{
- rtx inner = XEXP (XEXP (x, 0), 0);
-
- if (inner == virtual_incoming_args_rtx)
- new = arg_pointer_rtx, offset = in_arg_offset;
- else if (inner == virtual_stack_vars_rtx)
- new = frame_pointer_rtx, offset = var_offset;
- else if (inner == virtual_stack_dynamic_rtx)
- new = stack_pointer_rtx, offset = dynamic_offset;
- else if (inner == virtual_outgoing_args_rtx)
- new = stack_pointer_rtx, offset = out_arg_offset;
- else if (inner == virtual_cfa_rtx)
- new = arg_pointer_rtx, offset = cfa_offset;
+ if ((new = instantiate_new_reg (XEXP (XEXP (x, 0), 0), &offset)))
+ {
+ instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 1), object,
+ extra_insns);
+ new = gen_rtx_PLUS (Pmode, new, XEXP (XEXP (x, 0), 1));
+ }
else
{
loc = &XEXP (x, 0);
goto restart;
}
-
- instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 1), object,
- extra_insns);
- new = gen_rtx_PLUS (Pmode, new, XEXP (XEXP (x, 0), 1));
}
- else if (XEXP (x, 0) == virtual_incoming_args_rtx)
- new = arg_pointer_rtx, offset = in_arg_offset;
- else if (XEXP (x, 0) == virtual_stack_vars_rtx)
- new = frame_pointer_rtx, offset = var_offset;
- else if (XEXP (x, 0) == virtual_stack_dynamic_rtx)
- new = stack_pointer_rtx, offset = dynamic_offset;
- else if (XEXP (x, 0) == virtual_outgoing_args_rtx)
- new = stack_pointer_rtx, offset = out_arg_offset;
- else if (XEXP (x, 0) == virtual_cfa_rtx)
- new = arg_pointer_rtx, offset = cfa_offset;
- else
+#ifdef POINTERS_EXTEND_UNSIGNED
+ /* If we have (plus (subreg (virtual-reg)) (const_int)), we know
+ we can commute the PLUS and SUBREG because pointers into the
+ frame are well-behaved. */
+ else if (GET_CODE (XEXP (x, 0)) == SUBREG && GET_MODE (x) == ptr_mode
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && 0 != (new
+ = instantiate_new_reg (SUBREG_REG (XEXP (x, 0)),
+ &offset))
+ && validate_change (object, loc,
+ plus_constant (gen_lowpart (ptr_mode,
+ new),
+ offset
+ + INTVAL (XEXP (x, 1))),
+ 0))
+ return 1;
+#endif
+ else if ((new = instantiate_new_reg (XEXP (x, 0), &offset)) == 0)
{
/* We know the second operand is a constant. Unless the
first operand is a REG (which has been already checked),
/* Most cases of MEM that convert to valid addresses have already been
handled by our scan of decls. The only special handling we
need here is to make a copy of the rtx to ensure it isn't being
- shared if we have to change it to a pseudo.
+ shared if we have to change it to a pseudo.
If the rtx is a simple reference to an address via a virtual register,
it can potentially be shared. In such cases, first try to make it
a valid address, which can also be shared. Otherwise, copy it and
- proceed normally.
+ proceed normally.
First check for common cases that need no processing. These are
usually due to instantiation already being done on a previous instance
case CLOBBER:
/* If the operand is a MEM, see if the change is a valid MEM. If not,
go ahead and make the invalid one, but do it to a copy. For a REG,
- just make the recursive call, since there's no chance of a problem. */
+ just make the recursive call, since there's no chance of a problem. */
if ((GET_CODE (XEXP (x, 0)) == MEM
&& instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 0), XEXP (x, 0),
case REG:
/* Try to replace with a PLUS. If that doesn't work, compute the sum
in front of this insn and substitute the temporary. */
- if (x == virtual_incoming_args_rtx)
- new = arg_pointer_rtx, offset = in_arg_offset;
- else if (x == virtual_stack_vars_rtx)
- new = frame_pointer_rtx, offset = var_offset;
- else if (x == virtual_stack_dynamic_rtx)
- new = stack_pointer_rtx, offset = dynamic_offset;
- else if (x == virtual_outgoing_args_rtx)
- new = stack_pointer_rtx, offset = out_arg_offset;
- else if (x == virtual_cfa_rtx)
- new = arg_pointer_rtx, offset = cfa_offset;
-
- if (new)
+ if ((new = instantiate_new_reg (x, &offset)) != 0)
{
temp = plus_constant (new, offset);
if (!validate_change (object, loc, temp, 0))
return 1;
}
break;
-
+
default:
break;
}
{
int i, regno, nregs;
rtx reg;
- tree type;
- if (TREE_CODE_CLASS (TREE_CODE (exp)) == 't')
- type = exp;
- else
- type = TREE_TYPE (exp);
+ tree type = (TYPE_P (exp)) ? exp : TREE_TYPE (exp);
+
+ if (TREE_CODE (type) == VOID_TYPE)
+ return 0;
if (RETURN_IN_MEMORY (type))
return 1;
/* Types that are TREE_ADDRESSABLE must be constructed in memory,
tree fnargs = DECL_ARGUMENTS (fndecl);
/* This is used for the arg pointer when referring to stack args. */
rtx internal_arg_pointer;
- /* This is a dummy PARM_DECL that we used for the function result if
+ /* This is a dummy PARM_DECL that we used for the function result if
the function returns a structure. */
tree function_result_decl = 0;
#ifdef SETUP_INCOMING_VARARGS
TREE_CHAIN (function_result_decl) = fnargs;
fnargs = function_result_decl;
}
-
+
max_parm_reg = LAST_VIRTUAL_REGISTER + 1;
parm_reg_stack_loc = (rtx *) xcalloc (max_parm_reg, sizeof (rtx));
for (parm = fnargs; parm; parm = TREE_CHAIN (parm))
{
- int aggregate = AGGREGATE_TYPE_P (TREE_TYPE (parm));
struct args_size stack_offset;
struct args_size arg_size;
int passed_pointer = 0;
type of the first field for the tests below. We have already
verified that the modes are the same. */
if (DECL_TRANSPARENT_UNION (parm)
- || TYPE_TRANSPARENT_UNION (passed_type))
+ || (TREE_CODE (passed_type) == UNION_TYPE
+ && TYPE_TRANSPARENT_UNION (passed_type)))
passed_type = TREE_TYPE (TYPE_FIELDS (passed_type));
/* See if this arg was passed by invisible reference. It is if
#endif
#endif
fndecl, &stack_args_size, &stack_offset, &arg_size,
- &alignment_pad);
+ &alignment_pad);
{
rtx offset_rtx = ARGS_SIZE_RTX (stack_offset);
internal_arg_pointer,
offset_rtx));
- /* If this is a memory ref that contains aggregate components,
- mark it as such for cse and loop optimize. Likewise if it
- is readonly. */
- MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
- RTX_UNCHANGING_P (stack_parm) = TREE_READONLY (parm);
- MEM_ALIAS_SET (stack_parm) = get_alias_set (parm);
+ set_mem_attributes (stack_parm, parm, 1);
}
/* If this parameter was passed both in registers and in the stack,
if (GET_CODE (entry_parm) == PARALLEL)
emit_group_store (validize_mem (stack_parm), entry_parm,
int_size_in_bytes (TREE_TYPE (parm)),
- (TYPE_ALIGN (TREE_TYPE (parm))
- / BITS_PER_UNIT));
+ TYPE_ALIGN (TREE_TYPE (parm)));
+
else
move_block_from_reg (REGNO (entry_parm),
validize_mem (stack_parm), nregs,
for its ultimate type, don't use that slot after entry.
We'll make another stack slot, if we need one. */
{
- int thisparm_boundary
+ unsigned int thisparm_boundary
= FUNCTION_ARG_BOUNDARY (promoted_mode, passed_type);
if (GET_MODE_ALIGNMENT (nominal_mode) > thisparm_boundary)
&& nominal_mode != BLKmode && nominal_mode != passed_mode)
stack_parm = 0;
-#if 0
- /* Now adjust STACK_PARM to the mode and precise location
- where this parameter should live during execution,
- if we discover that it must live in the stack during execution.
- To make debuggers happier on big-endian machines, we store
- the value in the last bytes of the space available. */
-
- if (nominal_mode != BLKmode && nominal_mode != passed_mode
- && stack_parm != 0)
+ /* When an argument is passed in multiple locations, we can't
+ make use of this information, but we can save some copying if
+ the whole argument is passed in a single register. */
+ if (GET_CODE (entry_parm) == PARALLEL
+ && nominal_mode != BLKmode && passed_mode != BLKmode)
{
- rtx offset_rtx;
-
- if (BYTES_BIG_ENDIAN
- && GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD)
- stack_offset.constant += (GET_MODE_SIZE (passed_mode)
- - GET_MODE_SIZE (nominal_mode));
-
- offset_rtx = ARGS_SIZE_RTX (stack_offset);
- if (offset_rtx == const0_rtx)
- stack_parm = gen_rtx_MEM (nominal_mode, internal_arg_pointer);
- else
- stack_parm = gen_rtx_MEM (nominal_mode,
- gen_rtx_PLUS (Pmode,
- internal_arg_pointer,
- offset_rtx));
-
- /* If this is a memory ref that contains aggregate components,
- mark it as such for cse and loop optimize. */
- MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
+ int i, len = XVECLEN (entry_parm, 0);
+
+ for (i = 0; i < len; i++)
+ if (XEXP (XVECEXP (entry_parm, 0, i), 0) != NULL_RTX
+ && GET_CODE (XEXP (XVECEXP (entry_parm, 0, i), 0)) == REG
+ && (GET_MODE (XEXP (XVECEXP (entry_parm, 0, i), 0))
+ == passed_mode)
+ && INTVAL (XEXP (XVECEXP (entry_parm, 0, i), 1)) == 0)
+ {
+ entry_parm = XEXP (XVECEXP (entry_parm, 0, i), 0);
+ DECL_INCOMING_RTL (parm) = entry_parm;
+ break;
+ }
}
-#endif /* 0 */
/* ENTRY_PARM is an RTX for the parameter as it arrives,
in the mode in which it arrives.
stack_parm
= assign_stack_local (GET_MODE (entry_parm),
size_stored, 0);
-
- /* If this is a memory ref that contains aggregate
- components, mark it as such for cse and loop optimize. */
- MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
+ set_mem_attributes (stack_parm, parm, 1);
}
else if (PARM_BOUNDARY % BITS_PER_WORD != 0)
abort ();
- if (TREE_READONLY (parm))
- RTX_UNCHANGING_P (stack_parm) = 1;
-
/* Handle calls that pass values in multiple non-contiguous
locations. The Irix 6 ABI has examples of this. */
if (GET_CODE (entry_parm) == PARALLEL)
emit_group_store (validize_mem (stack_parm), entry_parm,
int_size_in_bytes (TREE_TYPE (parm)),
- (TYPE_ALIGN (TREE_TYPE (parm))
- / BITS_PER_UNIT));
+ TYPE_ALIGN (TREE_TYPE (parm)));
else
move_block_from_reg (REGNO (entry_parm),
validize_mem (stack_parm),
else if (! ((! optimize
&& ! DECL_REGISTER (parm)
&& ! DECL_INLINE (fndecl))
- /* layout_decl may set this. */
- || TREE_ADDRESSABLE (parm)
|| TREE_SIDE_EFFECTS (parm)
/* If -ffloat-store specified, don't put explicit
float variables into registers. */
may need to do it in a wider mode. */
register rtx parmreg;
- int regno, regnoi = 0, regnor = 0;
+ unsigned int regno, regnoi = 0, regnor = 0;
unsignedp = TREE_UNSIGNED (TREE_TYPE (parm));
{
DECL_RTL (parm)
= gen_rtx_MEM (TYPE_MODE (TREE_TYPE (passed_type)), parmreg);
- MEM_SET_IN_STRUCT_P (DECL_RTL (parm), aggregate);
+ set_mem_attributes (DECL_RTL (parm), parm, 1);
}
else
- DECL_RTL (parm) = parmreg;
-
+ {
+ DECL_RTL (parm) = parmreg;
+ maybe_set_unchanging (DECL_RTL (parm), parm);
+ }
+
/* Copy the value into the register. */
if (nominal_mode != passed_mode
|| promoted_nominal_mode != promoted_mode)
{
int save_tree_used;
/* ENTRY_PARM has been converted to PROMOTED_MODE, its
- mode, by the caller. We now have to convert it to
+ mode, by the caller. We now have to convert it to
NOMINAL_MODE, if different. However, PARMREG may be in
a different mode than NOMINAL_MODE if it is being stored
promoted.
&& ! ((! optimize
&& ! DECL_REGISTER (parm)
&& ! DECL_INLINE (fndecl))
- /* layout_decl may set this. */
- || TREE_ADDRESSABLE (parm)
|| TREE_SIDE_EFFECTS (parm)
/* If -ffloat-store specified, don't put explicit
float variables into registers. */
Pmode above. We must use the actual mode of the parm. */
parmreg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (parm)));
mark_user_reg (parmreg);
- emit_move_insn (parmreg, DECL_RTL (parm));
+ if (GET_MODE (parmreg) != GET_MODE (DECL_RTL (parm)))
+ {
+ rtx tempreg = gen_reg_rtx (GET_MODE (DECL_RTL (parm)));
+
+ push_to_sequence (conversion_insns);
+ emit_move_insn (tempreg, DECL_RTL (parm));
+ DECL_RTL (parm)
+ = convert_to_mode (GET_MODE (parmreg), tempreg,
+ TREE_UNSIGNED (TREE_TYPE (parm)));
+ emit_move_insn (parmreg, DECL_RTL (parm));
+ conversion_insns = get_insns();
+ did_conversion = 1;
+ end_sequence ();
+ }
+ else
+ emit_move_insn (parmreg, DECL_RTL (parm));
DECL_RTL (parm) = parmreg;
/* STACK_PARM is the pointer, not the parm, and PARMREG is
now the parm. */
push_to_sequence (conversion_insns);
- if (TYPE_SIZE (type) == 0
+ if (!COMPLETE_TYPE_P (type)
|| TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
/* This is a variable sized object. */
copy = gen_rtx_MEM (BLKmode,
else
copy = assign_stack_temp (TYPE_MODE (type),
int_size_in_bytes (type), 1);
- MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
- RTX_UNCHANGING_P (copy) = TREE_READONLY (parm);
+ set_mem_attributes (copy, parm, 1);
store_expr (parm, copy, 0);
emit_move_insn (parmreg, XEXP (copy, 0));
if (current_function_check_memory_usage)
- emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
+ emit_library_call (chkr_set_right_libfunc,
+ LCT_CONST_MAKE_BLOCK, VOIDmode, 3,
XEXP (copy, 0), Pmode,
GEN_INT (int_size_in_bytes (type)),
TYPE_MODE (sizetype),
#endif /* FUNCTION_ARG_CALLEE_COPIES */
/* In any case, record the parm's desired stack location
- in case we later discover it must live in the stack.
+ in case we later discover it must live in the stack.
If it is a COMPLEX value, store the stack location for both
halves. */
max_parm_reg = regno + 1;
new = (rtx *) xrealloc (parm_reg_stack_loc,
max_parm_reg * sizeof (rtx));
- bzero ((char *) (new + old_max_parm_reg),
+ memset ((char *) (new + old_max_parm_reg), 0,
(max_parm_reg - old_max_parm_reg) * sizeof (rtx));
parm_reg_stack_loc = new;
}
}
else if ((set = single_set (linsn)) != 0
&& SET_DEST (set) == parmreg)
- REG_NOTES (linsn)
+ REG_NOTES (linsn)
= gen_rtx_EXPR_LIST (REG_EQUIV,
stack_parm, REG_NOTES (linsn));
}
/* For pointer data type, suggest pointer register. */
if (POINTER_TYPE_P (TREE_TYPE (parm)))
mark_reg_pointer (parmreg,
- (TYPE_ALIGN (TREE_TYPE (TREE_TYPE (parm)))
- / BITS_PER_UNIT));
+ TYPE_ALIGN (TREE_TYPE (TREE_TYPE (parm))));
+
+ /* If something wants our address, try to use ADDRESSOF. */
+ if (TREE_ADDRESSABLE (parm))
+ {
+ /* If we end up putting something into the stack,
+ fixup_var_refs_insns will need to make a pass over
+ all the instructions. It looks throughs the pending
+ sequences -- but it can't see the ones in the
+ CONVERSION_INSNS, if they're not on the sequence
+ stack. So, we go back to that sequence, just so that
+ the fixups will happen. */
+ push_to_sequence (conversion_insns);
+ put_var_into_stack (parm);
+ conversion_insns = get_insns ();
+ end_sequence ();
+ }
}
else
{
stack_parm
= assign_stack_local (GET_MODE (entry_parm),
GET_MODE_SIZE (GET_MODE (entry_parm)), 0);
- /* If this is a memory ref that contains aggregate components,
- mark it as such for cse and loop optimize. */
- MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
+ set_mem_attributes (stack_parm, parm, 1);
}
if (promoted_mode != nominal_mode)
if (current_function_check_memory_usage)
{
push_to_sequence (conversion_insns);
- emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
- XEXP (stack_parm, 0), Pmode,
- GEN_INT (GET_MODE_SIZE (GET_MODE
+ emit_library_call (chkr_set_right_libfunc, LCT_CONST_MAKE_BLOCK,
+ VOIDmode, 3, XEXP (stack_parm, 0), Pmode,
+ GEN_INT (GET_MODE_SIZE (GET_MODE
(entry_parm))),
TYPE_MODE (sizetype),
GEN_INT (MEMORY_USE_RW),
}
DECL_RTL (parm) = stack_parm;
}
-
+
/* If this "parameter" was the place where we are receiving the
function's incoming structure pointer, set up the result. */
if (parm == function_result_decl)
{
tree result = DECL_RESULT (fndecl);
- tree restype = TREE_TYPE (result);
DECL_RTL (result)
= gen_rtx_MEM (DECL_MODE (result), DECL_RTL (parm));
- MEM_SET_IN_STRUCT_P (DECL_RTL (result),
- AGGREGATE_TYPE_P (restype));
+ set_mem_attributes (DECL_RTL (result), result, 1);
}
-
- if (TREE_THIS_VOLATILE (parm))
- MEM_VOLATILE_P (DECL_RTL (parm)) = 1;
- if (TREE_READONLY (parm))
- RTX_UNCHANGING_P (DECL_RTL (parm)) = 1;
}
/* Output all parameter conversion instructions (possibly including calls)
current_function_args_size
= ((current_function_args_size + STACK_BYTES - 1)
/ STACK_BYTES) * STACK_BYTES;
-#endif
+#endif
#ifdef ARGS_GROW_DOWNWARD
current_function_arg_offset_rtx
= (stack_args_size.var == 0 ? GEN_INT (-stack_args_size.constant)
- : expand_expr (size_binop (MINUS_EXPR, stack_args_size.var,
- size_int (-stack_args_size.constant)),
+ : expand_expr (size_diffop (stack_args_size.var,
+ size_int (-stack_args_size.constant)),
NULL_RTX, VOIDmode, EXPAND_MEMORY_USE_BAD));
#else
current_function_arg_offset_rtx = ARGS_SIZE_RTX (stack_args_size);
rtx
promoted_input_arg (regno, pmode, punsignedp)
- int regno;
+ unsigned int regno;
enum machine_mode *pmode;
int *punsignedp;
{
initial offset is not affected by this rounding, while the size always
is and the starting offset may be. */
-/* offset_ptr will be negative for ARGS_GROW_DOWNWARD case;
+/* offset_ptr will be negative for ARGS_GROW_DOWNWARD case;
initial_offset_ptr is positive because locate_and_pad_parm's
callers pass in the total size of args so far as
initial_offset_ptr. arg_size_ptr is always positive.*/
void
locate_and_pad_parm (passed_mode, type, in_regs, fndecl,
initial_offset_ptr, offset_ptr, arg_size_ptr,
- alignment_pad)
+ alignment_pad)
enum machine_mode passed_mode;
tree type;
int in_regs ATTRIBUTE_UNUSED;
{
initial_offset_ptr->var
= size_binop (MAX_EXPR, ARGS_SIZE_TREE (*initial_offset_ptr),
- size_int (reg_parm_stack_space));
+ ssize_int (reg_parm_stack_space));
initial_offset_ptr->constant = 0;
}
else if (initial_offset_ptr->constant < reg_parm_stack_space)
arg_size_ptr->var = 0;
arg_size_ptr->constant = 0;
+ alignment_pad->var = 0;
+ alignment_pad->constant = 0;
#ifdef ARGS_GROW_DOWNWARD
if (initial_offset_ptr->var)
{
offset_ptr->constant = 0;
- offset_ptr->var = size_binop (MINUS_EXPR, integer_zero_node,
+ offset_ptr->var = size_binop (MINUS_EXPR, ssize_int (0),
initial_offset_ptr->var);
}
else
{
- offset_ptr->constant = - initial_offset_ptr->constant;
+ offset_ptr->constant = -initial_offset_ptr->constant;
offset_ptr->var = 0;
}
if (where_pad != none
- && (TREE_CODE (sizetree) != INTEGER_CST
- || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
+ && (!host_integerp (sizetree, 1)
+ || (tree_low_cst (sizetree, 1) * BITS_PER_UNIT) % PARM_BOUNDARY))
sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
SUB_PARM_SIZE (*offset_ptr, sizetree);
if (where_pad != downward)
pad_to_arg_alignment (offset_ptr, boundary, alignment_pad);
if (initial_offset_ptr->var)
- {
- arg_size_ptr->var = size_binop (MINUS_EXPR,
- size_binop (MINUS_EXPR,
- integer_zero_node,
- initial_offset_ptr->var),
- offset_ptr->var);
- }
+ arg_size_ptr->var = size_binop (MINUS_EXPR,
+ size_binop (MINUS_EXPR,
+ ssize_int (0),
+ initial_offset_ptr->var),
+ offset_ptr->var);
+
else
- {
- arg_size_ptr->constant = (- initial_offset_ptr->constant
- - offset_ptr->constant);
- }
+ arg_size_ptr->constant = (-initial_offset_ptr->constant
+ - offset_ptr->constant);
+
#else /* !ARGS_GROW_DOWNWARD */
- pad_to_arg_alignment (initial_offset_ptr, boundary, alignment_pad);
+ if (!in_regs
+#ifdef REG_PARM_STACK_SPACE
+ || REG_PARM_STACK_SPACE (fndecl) > 0
+#endif
+ )
+ pad_to_arg_alignment (initial_offset_ptr, boundary, alignment_pad);
*offset_ptr = *initial_offset_ptr;
#ifdef PUSH_ROUNDING
pad_below (offset_ptr, passed_mode, sizetree);
if (where_pad != none
- && (TREE_CODE (sizetree) != INTEGER_CST
- || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
+ && (!host_integerp (sizetree, 1)
+ || (tree_low_cst (sizetree, 1) * BITS_PER_UNIT) % PARM_BOUNDARY))
sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
ADD_PARM_SIZE (*arg_size_ptr, sizetree);
HOST_WIDE_INT save_constant = 0;
int boundary_in_bytes = boundary / BITS_PER_UNIT;
-
+
if (boundary > PARM_BOUNDARY && boundary > STACK_BOUNDARY)
{
save_var = offset_ptr->var;
{
if (offset_ptr->var)
{
- offset_ptr->var =
+ offset_ptr->var =
#ifdef ARGS_GROW_DOWNWARD
- round_down
+ round_down
#else
round_up
#endif
boundary / BITS_PER_UNIT);
offset_ptr->constant = 0; /*?*/
if (boundary > PARM_BOUNDARY && boundary > STACK_BOUNDARY)
- alignment_pad->var = size_binop (MINUS_EXPR, offset_ptr->var, save_var);
+ alignment_pad->var = size_binop (MINUS_EXPR, offset_ptr->var,
+ save_var);
}
else
- {
+ {
offset_ptr->constant =
#ifdef ARGS_GROW_DOWNWARD
FLOOR_ROUND (offset_ptr->constant, boundary_in_bytes);
#else
CEIL_ROUND (offset_ptr->constant, boundary_in_bytes);
#endif
- if (boundary > PARM_BOUNDARY && boundary > STACK_BOUNDARY)
- alignment_pad->constant = offset_ptr->constant - save_constant;
- }
+ if (boundary > PARM_BOUNDARY && boundary > STACK_BOUNDARY)
+ alignment_pad->constant = offset_ptr->constant - save_constant;
+ }
}
}
}
}
#endif
-
-#ifdef ARGS_GROW_DOWNWARD
-static tree
-round_down (value, divisor)
- tree value;
- int divisor;
-{
- return size_binop (MULT_EXPR,
- size_binop (FLOOR_DIV_EXPR, value, size_int (divisor)),
- size_int (divisor));
-}
-#endif
\f
/* Walk the tree of blocks describing the binding levels within a function
and warn about uninitialized variables.
if (DECL_RTL (decl) != 0
&& GET_CODE (DECL_RTL (decl)) == REG
&& regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
- warning_with_decl (decl, "argument `%s' might be clobbered by `longjmp' or `vfork'");
+ warning_with_decl (decl,
+ "argument `%s' might be clobbered by `longjmp' or `vfork'");
}
/* If this function call setjmp, put all vars into the stack
addr = fix_lexical_addr (XEXP (fp->x_arg_pointer_save_area, 0), var);
addr = memory_address (Pmode, addr);
- base = copy_to_reg (gen_rtx_MEM (Pmode, addr));
+ base = gen_rtx_MEM (Pmode, addr);
+ MEM_ALIAS_SET (base) = get_frame_alias_set ();
+ base = copy_to_reg (base);
#else
displacement += (FIRST_PARM_OFFSET (context) - STARTING_FRAME_OFFSET);
base = lookup_static_chain (var);
for (link = trampoline_list; link; link = TREE_CHAIN (link))
if (TREE_PURPOSE (link) == function)
return
- round_trampoline_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0));
+ adjust_trampoline_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0));
for (fp = outer_function_chain; fp; fp = fp->next)
for (link = fp->x_trampoline_list; link; link = TREE_CHAIN (link))
{
tramp = fix_lexical_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0),
function);
- return round_trampoline_addr (tramp);
+ return adjust_trampoline_addr (tramp);
}
/* None exists; we must make one. */
by expand_function_end. */
if (fp != 0)
{
- push_obstacks (fp->function_maybepermanent_obstack,
- fp->function_maybepermanent_obstack);
rtlexp = make_node (RTL_EXPR);
RTL_EXPR_RTL (rtlexp) = tramp;
fp->x_trampoline_list = tree_cons (function, rtlexp,
fp->x_trampoline_list);
- pop_obstacks ();
}
else
{
/* Make the RTL_EXPR node temporary, not momentary, so that the
trampoline_list doesn't become garbage. */
- int momentary = suspend_momentary ();
rtlexp = make_node (RTL_EXPR);
- resume_momentary (momentary);
RTL_EXPR_RTL (rtlexp) = tramp;
trampoline_list = tree_cons (function, rtlexp, trampoline_list);
}
tramp = fix_lexical_addr (XEXP (tramp, 0), function);
- return round_trampoline_addr (tramp);
+ return adjust_trampoline_addr (tramp);
}
/* Given a trampoline address,
GEN_INT (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1),
temp, 0, OPTAB_LIB_WIDEN);
tramp = expand_binop (Pmode, and_optab, temp,
- GEN_INT (- TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT),
+ GEN_INT (-TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT),
temp, 0, OPTAB_LIB_WIDEN);
#endif
return tramp;
}
-\f
-/* The functions identify_blocks and reorder_blocks provide a way to
- reorder the tree of BLOCK nodes, for optimizers that reshuffle or
- duplicate portions of the RTL code. Call identify_blocks before
- changing the RTL, and call reorder_blocks after. */
+/* Given a trampoline address, round it then apply any
+ platform-specific adjustments so that the result can be used for a
+ function call . */
+
+static rtx
+adjust_trampoline_addr (tramp)
+ rtx tramp;
+{
+ tramp = round_trampoline_addr (tramp);
+#ifdef TRAMPOLINE_ADJUST_ADDRESS
+ TRAMPOLINE_ADJUST_ADDRESS (tramp);
+#endif
+ return tramp;
+}
+\f
/* Put all this function's BLOCK nodes including those that are chained
onto the first block into a vector, and return it.
Also store in each NOTE for the beginning or end of a block
and INSNS, the insn chain of the function. */
void
-identify_blocks (block, insns)
- tree block;
- rtx insns;
+identify_blocks ()
{
int n_blocks;
- tree *block_vector;
+ tree *block_vector, *last_block_vector;
tree *block_stack;
- int depth = 0;
- int current_block_number = 1;
- rtx insn;
+ tree block = DECL_INITIAL (current_function_decl);
if (block == 0)
return;
/* Fill the BLOCK_VECTOR with all of the BLOCKs in this function, in
depth-first order. */
- n_blocks = all_blocks (block, 0);
- block_vector = (tree *) xmalloc (n_blocks * sizeof (tree));
- all_blocks (block, block_vector);
-
+ block_vector = get_block_vector (block, &n_blocks);
block_stack = (tree *) xmalloc (n_blocks * sizeof (tree));
- for (insn = insns; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == NOTE)
- {
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
- {
- tree b;
-
- /* If there are more block notes than BLOCKs, something
- is badly wrong. */
- if (current_block_number == n_blocks)
- abort ();
-
- b = block_vector[current_block_number++];
- NOTE_BLOCK (insn) = b;
- block_stack[depth++] = b;
- }
- else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
- {
- if (depth == 0)
- /* There are more NOTE_INSN_BLOCK_ENDs that
- NOTE_INSN_BLOCK_BEGs. Something is badly wrong. */
- abort ();
-
- NOTE_BLOCK (insn) = block_stack[--depth];
- }
- }
+ last_block_vector = identify_blocks_1 (get_insns (),
+ block_vector + 1,
+ block_vector + n_blocks,
+ block_stack);
- /* In whole-function mode, we might not have seen the whole function
- yet, so we might not use up all the blocks. */
- if (n_blocks != current_block_number
- && !cfun->x_whole_function_mode_p)
+ /* If we didn't use all of the subblocks, we've misplaced block notes. */
+ /* ??? This appears to happen all the time. Latent bugs elsewhere? */
+ if (0 && last_block_vector != block_vector + n_blocks)
abort ();
free (block_vector);
free (block_stack);
}
-/* Given a revised instruction chain, rebuild the tree structure of
- BLOCK nodes to correspond to the new order of RTL. The new block
- tree is inserted below TOP_BLOCK. Returns the current top-level
- block. */
+/* Subroutine of identify_blocks. Do the block substitution on the
+ insn chain beginning with INSNS. Recurse for CALL_PLACEHOLDER chains.
-tree
-reorder_blocks (block, insns)
- tree block;
+ BLOCK_STACK is pushed and popped for each BLOCK_BEGIN/BLOCK_END pair.
+ BLOCK_VECTOR is incremented for each block seen. */
+
+static tree *
+identify_blocks_1 (insns, block_vector, end_block_vector, orig_block_stack)
rtx insns;
+ tree *block_vector;
+ tree *end_block_vector;
+ tree *orig_block_stack;
{
- tree current_block = block;
rtx insn;
+ tree *block_stack = orig_block_stack;
+
+ for (insn = insns; insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
+ {
+ tree b;
+
+ /* If there are more block notes than BLOCKs, something
+ is badly wrong. */
+ if (block_vector == end_block_vector)
+ abort ();
+
+ b = *block_vector++;
+ NOTE_BLOCK (insn) = b;
+ *block_stack++ = b;
+ }
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ {
+ /* If there are more NOTE_INSN_BLOCK_ENDs than
+ NOTE_INSN_BLOCK_BEGs, something is badly wrong. */
+ if (block_stack == orig_block_stack)
+ abort ();
+
+ NOTE_BLOCK (insn) = *--block_stack;
+ }
+ }
+ else if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+ {
+ rtx cp = PATTERN (insn);
+
+ block_vector = identify_blocks_1 (XEXP (cp, 0), block_vector,
+ end_block_vector, block_stack);
+ if (XEXP (cp, 1))
+ block_vector = identify_blocks_1 (XEXP (cp, 1), block_vector,
+ end_block_vector, block_stack);
+ if (XEXP (cp, 2))
+ block_vector = identify_blocks_1 (XEXP (cp, 2), block_vector,
+ end_block_vector, block_stack);
+ }
+ }
+
+ /* If there are more NOTE_INSN_BLOCK_BEGINs than NOTE_INSN_BLOCK_ENDs,
+ something is badly wrong. */
+ if (block_stack != orig_block_stack)
+ abort ();
+
+ return block_vector;
+}
+
+/* Identify BLOCKs referenced by more than one
+ NOTE_INSN_BLOCK_{BEG,END}, and create duplicate blocks. */
+
+void
+reorder_blocks ()
+{
+ tree block = DECL_INITIAL (current_function_decl);
+ varray_type block_stack;
if (block == NULL_TREE)
- return NULL_TREE;
+ return;
+
+ VARRAY_TREE_INIT (block_stack, 10, "block_stack");
+
+ /* Prune the old trees away, so that they don't get in the way. */
+ BLOCK_SUBBLOCKS (block) = NULL_TREE;
+ BLOCK_CHAIN (block) = NULL_TREE;
- /* Prune the old trees away, so that it doesn't get in the way. */
- BLOCK_SUBBLOCKS (current_block) = 0;
- BLOCK_CHAIN (current_block) = 0;
+ reorder_blocks_0 (get_insns ());
+ reorder_blocks_1 (get_insns (), block, &block_stack);
+
+ BLOCK_SUBBLOCKS (block) = blocks_nreverse (BLOCK_SUBBLOCKS (block));
+
+ VARRAY_FREE (block_stack);
+}
+
+/* Helper function for reorder_blocks. Process the insn chain beginning
+ at INSNS. Recurse for CALL_PLACEHOLDER insns. */
+
+static void
+reorder_blocks_0 (insns)
+ rtx insns;
+{
+ rtx insn;
for (insn = insns; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == NOTE)
- {
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
- {
- tree block = NOTE_BLOCK (insn);
- /* If we have seen this block before, copy it. */
- if (TREE_ASM_WRITTEN (block))
- block = copy_node (block);
- BLOCK_SUBBLOCKS (block) = 0;
- TREE_ASM_WRITTEN (block) = 1;
- BLOCK_SUPERCONTEXT (block) = current_block;
- BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (current_block);
- BLOCK_SUBBLOCKS (current_block) = block;
- current_block = block;
- }
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
- {
- BLOCK_SUBBLOCKS (current_block)
- = blocks_nreverse (BLOCK_SUBBLOCKS (current_block));
- current_block = BLOCK_SUPERCONTEXT (current_block);
- }
- }
+ {
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
+ {
+ tree block = NOTE_BLOCK (insn);
+ TREE_ASM_WRITTEN (block) = 0;
+ }
+ }
+ else if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+ {
+ rtx cp = PATTERN (insn);
+ reorder_blocks_0 (XEXP (cp, 0));
+ if (XEXP (cp, 1))
+ reorder_blocks_0 (XEXP (cp, 1));
+ if (XEXP (cp, 2))
+ reorder_blocks_0 (XEXP (cp, 2));
+ }
+ }
+}
- BLOCK_SUBBLOCKS (current_block)
- = blocks_nreverse (BLOCK_SUBBLOCKS (current_block));
- return current_block;
+static void
+reorder_blocks_1 (insns, current_block, p_block_stack)
+ rtx insns;
+ tree current_block;
+ varray_type *p_block_stack;
+{
+ rtx insn;
+
+ for (insn = insns; insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
+ {
+ tree block = NOTE_BLOCK (insn);
+ /* If we have seen this block before, copy it. */
+ if (TREE_ASM_WRITTEN (block))
+ {
+ block = copy_node (block);
+ NOTE_BLOCK (insn) = block;
+ }
+ BLOCK_SUBBLOCKS (block) = 0;
+ TREE_ASM_WRITTEN (block) = 1;
+ BLOCK_SUPERCONTEXT (block) = current_block;
+ BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (current_block);
+ BLOCK_SUBBLOCKS (current_block) = block;
+ current_block = block;
+ VARRAY_PUSH_TREE (*p_block_stack, block);
+ }
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ {
+ NOTE_BLOCK (insn) = VARRAY_TOP_TREE (*p_block_stack);
+ VARRAY_POP (*p_block_stack);
+ BLOCK_SUBBLOCKS (current_block)
+ = blocks_nreverse (BLOCK_SUBBLOCKS (current_block));
+ current_block = BLOCK_SUPERCONTEXT (current_block);
+ }
+ }
+ else if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+ {
+ rtx cp = PATTERN (insn);
+ reorder_blocks_1 (XEXP (cp, 0), current_block, p_block_stack);
+ if (XEXP (cp, 1))
+ reorder_blocks_1 (XEXP (cp, 1), current_block, p_block_stack);
+ if (XEXP (cp, 2))
+ reorder_blocks_1 (XEXP (cp, 2), current_block, p_block_stack);
+ }
+ }
}
/* Reverse the order of elements in the chain T of blocks,
return prev;
}
-/* Count the subblocks of the list starting with BLOCK, and list them
- all into the vector VECTOR. Also clear TREE_ASM_WRITTEN in all
+/* Count the subblocks of the list starting with BLOCK. If VECTOR is
+ non-NULL, list them all into VECTOR, in a depth-first preorder
+ traversal of the block tree. Also clear TREE_ASM_WRITTEN in all
blocks. */
static int
vector[n_blocks] = block;
++n_blocks;
-
+
/* Record the subblocks, and their subblocks... */
n_blocks += all_blocks (BLOCK_SUBBLOCKS (block),
vector ? vector + n_blocks : 0);
return n_blocks;
}
+
+/* Return a vector containing all the blocks rooted at BLOCK. The
+ number of elements in the vector is stored in N_BLOCKS_P. The
+ vector is dynamically allocated; it is the caller's responsibility
+ to call `free' on the pointer returned. */
+
+static tree *
+get_block_vector (block, n_blocks_p)
+ tree block;
+ int *n_blocks_p;
+{
+ tree *block_vector;
+
+ *n_blocks_p = all_blocks (block, NULL);
+ block_vector = (tree *) xmalloc (*n_blocks_p * sizeof (tree));
+ all_blocks (block, block_vector);
+
+ return block_vector;
+}
+
+static int next_block_index = 2;
+
+/* Set BLOCK_NUMBER for all the blocks in FN. */
+
+void
+number_blocks (fn)
+ tree fn;
+{
+ int i;
+ int n_blocks;
+ tree *block_vector;
+
+ /* For SDB and XCOFF debugging output, we start numbering the blocks
+ from 1 within each function, rather than keeping a running
+ count. */
+#if defined (SDB_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+ if (write_symbols == SDB_DEBUG || write_symbols == XCOFF_DEBUG)
+ next_block_index = 1;
+#endif
+
+ block_vector = get_block_vector (DECL_INITIAL (fn), &n_blocks);
+
+ /* The top-level BLOCK isn't numbered at all. */
+ for (i = 1; i < n_blocks; ++i)
+ /* We number the blocks from two. */
+ BLOCK_NUMBER (block_vector[i]) = next_block_index++;
+
+ free (block_vector);
+
+ return;
+}
\f
/* Allocate a function structure and reset its contents to the defaults. */
static void
/* Initialize the queue of pending postincrement and postdecrements,
and some other info in expr.c. */
init_expr ();
-
+
/* We haven't done register allocation yet. */
reg_renumber = 0;
/* Clear out data used for inlining. */
cfun->inlinable = 0;
cfun->original_decl_initial = 0;
- cfun->original_arg_vector = 0;
+ cfun->original_arg_vector = 0;
- cfun->stack_alignment_needed = 0;
#ifdef STACK_BOUNDARY
+ cfun->stack_alignment_needed = STACK_BOUNDARY;
cfun->preferred_stack_boundary = STACK_BOUNDARY;
+#else
+ cfun->stack_alignment_needed = 0;
+ cfun->preferred_stack_boundary = 0;
#endif
/* Set if a call to setjmp is seen. */
current_function_calls_alloca = 0;
current_function_contains_functions = 0;
current_function_is_leaf = 0;
+ current_function_nothrow = 0;
current_function_sp_is_unchanging = 0;
current_function_uses_only_leaf_regs = 0;
current_function_has_computed_jump = 0;
/* Indicate that we have not instantiated virtual registers yet. */
virtuals_instantiated = 0;
+ /* Indicate that we want CONCATs now. */
+ generating_concat_p = 1;
+
/* Indicate we have no need of a frame pointer yet. */
frame_pointer_needed = 0;
void
init_function_start (subr, filename, line)
tree subr;
- char *filename;
+ const char *filename;
int line;
{
prepare_function_start ();
/* Remember this function for later. */
cfun->next_global = all_functions;
all_functions = cfun;
-
+
current_function_name = (*decl_printable_name) (subr, 2);
cfun->decl = subr;
/* Prevent ever trying to delete the first instruction of a function.
Also tell final how to output a linenum before the function prologue.
- Note linenums could be missing, e.g. when compiling a Java .class file. */
+ Note linenums could be missing, e.g. when compiling a Java .class file. */
if (line > 0)
emit_line_note (filename, line);
init_function_for_compilation ()
{
reg_renumber = 0;
+
/* No prologue/epilogue insns yet. */
- prologue = epilogue = 0;
+ VARRAY_GROW (prologue, 0);
+ VARRAY_GROW (epilogue, 0);
+ VARRAY_GROW (sibcall_epilogue, 0);
}
/* Indicate that the current function uses extra args
conflicts with regs used for parameters. */
if (! SMALL_REGISTER_CLASSES
|| GET_CODE (static_chain_incoming_rtx) == REG)
- emit_move_insn (last_ptr, static_chain_incoming_rtx);
+ emit_move_insn (last_ptr, static_chain_incoming_rtx);
}
/* If the parameters of this function need cleaning up, get a label
{
DECL_RTL (DECL_RESULT (subr))
= gen_rtx_MEM (DECL_MODE (DECL_RESULT (subr)), value_address);
- MEM_SET_IN_STRUCT_P (DECL_RTL (DECL_RESULT (subr)),
- AGGREGATE_TYPE_P (TREE_TYPE
- (DECL_RESULT
- (subr))));
+ set_mem_attributes (DECL_RTL (DECL_RESULT (subr)),
+ DECL_RESULT (subr), 1);
}
}
else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
else
/* Scalar, returned in a register. */
{
-#ifdef FUNCTION_OUTGOING_VALUE
- DECL_RTL (DECL_RESULT (subr))
- = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr);
-#else
DECL_RTL (DECL_RESULT (subr))
- = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr);
-#endif
+ = hard_function_value (TREE_TYPE (DECL_RESULT (subr)), subr, 1);
/* Mark this reg as the function's return value. */
if (GET_CODE (DECL_RTL (DECL_RESULT (subr))) == REG)
/* Chain thru stack frames, assuming pointer to next lexical frame
is found at the place we always store it. */
#ifdef FRAME_GROWS_DOWNWARD
- last_ptr = plus_constant (last_ptr, - GET_MODE_SIZE (Pmode));
+ last_ptr = plus_constant (last_ptr,
+ -(HOST_WIDE_INT) GET_MODE_SIZE (Pmode));
#endif
- last_ptr = copy_to_reg (gen_rtx_MEM (Pmode,
- memory_address (Pmode,
- last_ptr)));
+ last_ptr = gen_rtx_MEM (Pmode, memory_address (Pmode, last_ptr));
+ MEM_ALIAS_SET (last_ptr) = get_frame_alias_set ();
+ last_ptr = copy_to_reg (last_ptr);
/* If we are not optimizing, ensure that we know that this
piece of context is live over the entire function. */
Pmode);
}
+#ifdef PROFILE_HOOK
+ if (profile_flag)
+ PROFILE_HOOK (profile_label_no);
+#endif
+
/* After the display initializations is where the tail-recursion label
should go, if we end up needing one. Ensure we have a NOTE here
since some things (like trampolines) get placed before this. */
void *arg;
{
rtx outgoing = current_function_return_rtx;
+ int pcc;
if (! outgoing)
return;
- if (GET_CODE (outgoing) == REG
- && REGNO (outgoing) >= FIRST_PSEUDO_REGISTER)
+ pcc = (current_function_returns_struct
+ || current_function_returns_pcc_struct);
+
+ if ((GET_CODE (outgoing) == REG
+ && REGNO (outgoing) >= FIRST_PSEUDO_REGISTER)
+ || pcc)
{
tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
+
+ /* A PCC-style return returns a pointer to the memory in which
+ the structure is stored. */
+ if (pcc)
+ type = build_pointer_type (type);
+
#ifdef FUNCTION_OUTGOING_VALUE
outgoing = FUNCTION_OUTGOING_VALUE (type, current_function_decl);
#else
/* If this is a BLKmode structure being returned in registers, then use
the mode computed in expand_return. */
if (GET_MODE (outgoing) == BLKmode)
- PUT_MODE (outgoing,
- GET_MODE (DECL_RTL (DECL_RESULT (current_function_decl))));
+ PUT_MODE (outgoing, GET_MODE (current_function_return_rtx));
+ REG_FUNCTION_VALUE_P (outgoing) = 1;
}
if (GET_CODE (outgoing) == REG)
}
/* Generate RTL for the end of the current function.
- FILENAME and LINE are the current position in the source file.
+ FILENAME and LINE are the current position in the source file.
It is up to language-specific callers to do cleanups for parameters--
or else, supply 1 for END_BINDINGS and we will call expand_end_bindings. */
void
expand_function_end (filename, line, end_bindings)
- char *filename;
+ const char *filename;
int line;
int end_bindings;
{
initializing trampolines. */
if (initial_trampoline == 0)
{
- end_temporary_allocation ();
initial_trampoline
= gen_rtx_MEM (BLKmode, assemble_trampoline_template ());
- resume_temporary_allocation ();
ggc_add_rtx_root (&initial_trampoline, 1);
}
blktramp = change_address (initial_trampoline, BLKmode, tramp);
emit_block_move (blktramp, initial_trampoline,
GEN_INT (TRAMPOLINE_SIZE),
- TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT);
+ TRAMPOLINE_ALIGNMENT);
#endif
INITIALIZE_TRAMPOLINE (tramp, XEXP (DECL_RTL (function), 0), context);
seq = get_insns ();
}
/* Warn about unused parms if extra warnings were specified. */
- if (warn_unused && extra_warnings)
+ /* Either ``-W -Wunused'' or ``-Wunused-parameter'' enables this
+ warning. WARN_UNUSED_PARAMETER is negative when set by
+ -Wunused. */
+ if (warn_unused_parameter > 0
+ || (warn_unused_parameter < 0 && extra_warnings))
{
tree decl;
already exists a copy of this note somewhere above. This line number
note is still needed for debugging though, so we can't delete it. */
if (flag_test_coverage)
- emit_note (NULL_PTR, NOTE_REPEATED_LINE_NUMBER);
+ emit_note (NULL_PTR, NOTE_INSN_REPEATED_LINE_NUMBER);
/* Output a linenumber for the end of the function.
SDB depends on this. */
if (return_label)
{
+ rtx before, after;
+
/* Before the return label, clobber the return registers so that
they are not propogated live to the rest of the function. This
can only happen with functions that drop through; if there had
been a return statement, there would have either been a return
rtx, or a jump to the return label. */
+
+ before = get_last_insn ();
clobber_return_register ();
+ after = get_last_insn ();
+
+ if (before != after)
+ cfun->x_clobber_return_insn = after;
emit_label (return_label);
}
/* If there are any catch_clauses remaining, output them now. */
emit_insns (catch_clauses);
- catch_clauses = NULL_RTX;
+ catch_clauses = catch_clauses_last = NULL_RTX;
/* If the above emitted any code, may sure we jump around it. */
if (last != get_last_insn ())
{
emit_stack_restore (SAVE_FUNCTION, tem, NULL_RTX);
}
- /* If scalar return value was computed in a pseudo-reg,
- copy that to the hard return register. */
- if (DECL_RTL (DECL_RESULT (current_function_decl)) != 0
- && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG
- && (REGNO (DECL_RTL (DECL_RESULT (current_function_decl)))
- >= FIRST_PSEUDO_REGISTER))
+ /* If scalar return value was computed in a pseudo-reg, or was a named
+ return value that got dumped to the stack, copy that to the hard
+ return register. */
+ if (DECL_RTL (DECL_RESULT (current_function_decl)) != 0)
{
- rtx real_decl_result;
+ tree decl_result = DECL_RESULT (current_function_decl);
+ rtx decl_rtl = DECL_RTL (decl_result);
+
+ if (REG_P (decl_rtl)
+ ? REGNO (decl_rtl) >= FIRST_PSEUDO_REGISTER
+ : DECL_REGISTER (decl_result))
+ {
+ rtx real_decl_rtl;
#ifdef FUNCTION_OUTGOING_VALUE
- real_decl_result
- = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)),
- current_function_decl);
+ real_decl_rtl = FUNCTION_OUTGOING_VALUE (TREE_TYPE (decl_result),
+ current_function_decl);
#else
- real_decl_result
- = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)),
- current_function_decl);
+ real_decl_rtl = FUNCTION_VALUE (TREE_TYPE (decl_result),
+ current_function_decl);
#endif
- REG_FUNCTION_VALUE_P (real_decl_result) = 1;
- /* If this is a BLKmode structure being returned in registers, then use
- the mode computed in expand_return. */
- if (GET_MODE (real_decl_result) == BLKmode)
- PUT_MODE (real_decl_result,
- GET_MODE (DECL_RTL (DECL_RESULT (current_function_decl))));
- emit_move_insn (real_decl_result,
- DECL_RTL (DECL_RESULT (current_function_decl)));
-
- /* The delay slot scheduler assumes that current_function_return_rtx
- holds the hard register containing the return value, not a temporary
- pseudo. */
- current_function_return_rtx = real_decl_result;
+ REG_FUNCTION_VALUE_P (real_decl_rtl) = 1;
+
+ /* If this is a BLKmode structure being returned in registers,
+ then use the mode computed in expand_return. Note that if
+ decl_rtl is memory, then its mode may have been changed,
+ but that current_function_return_rtx has not. */
+ if (GET_MODE (real_decl_rtl) == BLKmode)
+ PUT_MODE (real_decl_rtl, GET_MODE (current_function_return_rtx));
+
+ /* If a named return value dumped decl_return to memory, then
+ we may need to re-do the PROMOTE_MODE signed/unsigned
+ extension. */
+ if (GET_MODE (real_decl_rtl) != GET_MODE (decl_rtl))
+ {
+ int unsignedp = TREE_UNSIGNED (TREE_TYPE (decl_result));
+
+#ifdef PROMOTE_FUNCTION_RETURN
+ promote_mode (TREE_TYPE (decl_result), GET_MODE (decl_rtl),
+ &unsignedp, 1);
+#endif
+
+ convert_move (real_decl_rtl, decl_rtl, unsignedp);
+ }
+ else
+ emit_move_insn (real_decl_rtl, decl_rtl);
+
+ /* The delay slot scheduler assumes that current_function_return_rtx
+ holds the hard register containing the return value, not a
+ temporary pseudo. */
+ current_function_return_rtx = real_decl_rtl;
+ }
}
/* If returning a structure, arrange to return the address of the value
if (current_function_returns_struct
|| current_function_returns_pcc_struct)
{
- rtx value_address = XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
+ rtx value_address
+ = XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
#ifdef FUNCTION_OUTGOING_VALUE
rtx outgoing
current_function_decl);
#else
rtx outgoing
- = FUNCTION_VALUE (build_pointer_type (type),
- current_function_decl);
+ = FUNCTION_VALUE (build_pointer_type (type), current_function_decl);
#endif
/* Mark this as a function return value so integrate will delete the
assignment and USE below when inlining this function. */
REG_FUNCTION_VALUE_P (outgoing) = 1;
+#ifdef POINTERS_EXTEND_UNSIGNED
+ /* The address may be ptr_mode and OUTGOING may be Pmode. */
+ if (GET_MODE (outgoing) != GET_MODE (value_address))
+ value_address = convert_memory_address (GET_MODE (outgoing),
+ value_address);
+#endif
+
emit_move_insn (outgoing, value_address);
+
+ /* Show return register used to hold result (in this case the address
+ of the result. */
+ current_function_return_rtx = outgoing;
}
/* ??? This should no longer be necessary since stupid is no longer with
instead of using the general framework. */
use_return_register ();
- /* If this is an implementation of __throw, do what's necessary to
+ /* If this is an implementation of __throw, do what's necessary to
communicate between __builtin_eh_return and the epilogue. */
expand_eh_return ();
expand_fixups (get_insns ());
}
\f
-/* Create an array that records the INSN_UIDs of INSNS (either a sequence
- or a single insn). */
+/* Extend a vector that records the INSN_UIDs of INSNS (either a
+ sequence or a single insn). */
-static int *
-record_insns (insns)
+static void
+record_insns (insns, vecp)
rtx insns;
+ varray_type *vecp;
{
- int *vec;
-
if (GET_CODE (insns) == SEQUENCE)
{
int len = XVECLEN (insns, 0);
- vec = (int *) oballoc ((len + 1) * sizeof (int));
- vec[len] = 0;
+ int i = VARRAY_SIZE (*vecp);
+
+ VARRAY_GROW (*vecp, i + len);
while (--len >= 0)
- vec[len] = INSN_UID (XVECEXP (insns, 0, len));
+ {
+ VARRAY_INT (*vecp, i) = INSN_UID (XVECEXP (insns, 0, len));
+ ++i;
+ }
}
else
{
- vec = (int *) oballoc (2 * sizeof (int));
- vec[0] = INSN_UID (insns);
- vec[1] = 0;
+ int i = VARRAY_SIZE (*vecp);
+ VARRAY_GROW (*vecp, i + 1);
+ VARRAY_INT (*vecp, i) = INSN_UID (insns);
}
- return vec;
}
/* Determine how many INSN_UIDs in VEC are part of INSN. */
static int
contains (insn, vec)
rtx insn;
- int *vec;
+ varray_type vec;
{
register int i, j;
{
int count = 0;
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
- for (j = 0; vec[j]; j++)
- if (INSN_UID (XVECEXP (PATTERN (insn), 0, i)) == vec[j])
+ for (j = VARRAY_SIZE (vec) - 1; j >= 0; --j)
+ if (INSN_UID (XVECEXP (PATTERN (insn), 0, i)) == VARRAY_INT (vec, j))
count++;
return count;
}
else
{
- for (j = 0; vec[j]; j++)
- if (INSN_UID (insn) == vec[j])
+ for (j = VARRAY_SIZE (vec) - 1; j >= 0; --j)
+ if (INSN_UID (insn) == VARRAY_INT (vec, j))
return 1;
}
return 0;
prologue_epilogue_contains (insn)
rtx insn;
{
- if (prologue && contains (insn, prologue))
+ if (contains (insn, prologue))
return 1;
- if (epilogue && contains (insn, epilogue))
+ if (contains (insn, epilogue))
return 1;
return 0;
}
+int
+sibcall_epilogue_contains (insn)
+ rtx insn;
+{
+ if (sibcall_epilogue)
+ return contains (insn, sibcall_epilogue);
+ return 0;
+}
+
#ifdef HAVE_return
/* Insert gen_return at the end of block BB. This also means updating
block_for_insn appropriately. */
static void
-emit_return_into_block (bb)
+emit_return_into_block (bb, line_note)
basic_block bb;
+ rtx line_note;
{
rtx p, end;
+ p = NEXT_INSN (bb->end);
end = emit_jump_insn_after (gen_return (), bb->end);
- p = NEXT_INSN (bb->end);
+ if (line_note)
+ emit_line_note_after (NOTE_SOURCE_FILE (line_note),
+ NOTE_LINE_NUMBER (line_note), bb->end);
+
while (1)
{
set_block_for_insn (p, bb);
- if (p == end)
+ if (p == bb->end)
break;
- p = NEXT_INSN (p);
+ p = PREV_INSN (p);
}
bb->end = end;
}
#endif /* HAVE_return */
+#ifdef HAVE_epilogue
+
+/* Modify SEQ, a SEQUENCE that is part of the epilogue, to no modifications
+ to the stack pointer. */
+
+static void
+keep_stack_depressed (seq)
+ rtx seq;
+{
+ int i;
+ rtx sp_from_reg = 0;
+ int sp_modified_unknown = 0;
+
+ /* If the epilogue is just a single instruction, it's OK as is */
+
+ if (GET_CODE (seq) != SEQUENCE)
+ return;
+
+ /* Scan all insns in SEQ looking for ones that modified the stack
+ pointer. Record if it modified the stack pointer by copying it
+ from the frame pointer or if it modified it in some other way.
+ Then modify any subsequent stack pointer references to take that
+ into account. We start by only allowing SP to be copied from a
+ register (presumably FP) and then be subsequently referenced. */
+
+ for (i = 0; i < XVECLEN (seq, 0); i++)
+ {
+ rtx insn = XVECEXP (seq, 0, i);
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ continue;
+
+ if (reg_set_p (stack_pointer_rtx, insn))
+ {
+ rtx set = single_set (insn);
+
+ /* If SP is set as a side-effect, we can't support this. */
+ if (set == 0)
+ abort ();
+
+ if (GET_CODE (SET_SRC (set)) == REG)
+ sp_from_reg = SET_SRC (set);
+ else
+ sp_modified_unknown = 1;
+
+ /* Don't allow the SP modification to happen. */
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ else if (reg_referenced_p (stack_pointer_rtx, PATTERN (insn)))
+ {
+ if (sp_modified_unknown)
+ abort ();
+
+ else if (sp_from_reg != 0)
+ PATTERN (insn)
+ = replace_rtx (PATTERN (insn), stack_pointer_rtx, sp_from_reg);
+ }
+ }
+}
+#endif
+
/* Generate the prologue and epilogue RTL if the machine supports it. Thread
this into place with notes indicating where the prologue ends and where
the epilogue begins. Update the basic block information when possible. */
thread_prologue_and_epilogue_insns (f)
rtx f ATTRIBUTE_UNUSED;
{
- int insertted = 0;
+ int inserted = 0;
edge e;
rtx seq;
+#ifdef HAVE_prologue
+ rtx prologue_end = NULL_RTX;
+#endif
+#if defined (HAVE_epilogue) || defined(HAVE_return)
+ rtx epilogue_end = NULL_RTX;
+#endif
#ifdef HAVE_prologue
if (HAVE_prologue)
{
start_sequence ();
- seq = gen_prologue();
+ seq = gen_prologue ();
emit_insn (seq);
/* Retain a map of the prologue insns. */
if (GET_CODE (seq) != SEQUENCE)
seq = get_insns ();
- prologue = record_insns (seq);
+ record_insns (seq, &prologue);
+ prologue_end = emit_note (NULL, NOTE_INSN_PROLOGUE_END);
- emit_note (NULL, NOTE_INSN_PROLOGUE_END);
seq = gen_sequence ();
end_sequence ();
abort ();
insert_insn_on_edge (seq, ENTRY_BLOCK_PTR->succ);
- insertted = 1;
+ inserted = 1;
}
else
emit_insn_after (seq, f);
/* If the exit block has no non-fake predecessors, we don't need
an epilogue. */
- for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
+ for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
if ((e->flags & EDGE_FAKE) == 0)
break;
if (e == NULL)
{
/* If we're allowed to generate a simple return instruction,
then by definition we don't need a full epilogue. Examine
- the block that falls through to EXIT. If it does not
- contain any code, examine its predecessors and try to
+ the block that falls through to EXIT. If it does not
+ contain any code, examine its predecessors and try to
emit (conditional) return instructions. */
basic_block last;
edge e_next;
rtx label;
- for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
+ for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
if (e->flags & EDGE_FALLTHRU)
break;
if (e == NULL)
if (last->head == label && GET_CODE (label) == CODE_LABEL)
{
- for (e = last->pred; e ; e = e_next)
+ rtx epilogue_line_note = NULL_RTX;
+
+ /* Locate the line number associated with the closing brace,
+ if we can find one. */
+ for (seq = get_last_insn ();
+ seq && ! active_insn_p (seq);
+ seq = PREV_INSN (seq))
+ if (GET_CODE (seq) == NOTE && NOTE_LINE_NUMBER (seq) > 0)
+ {
+ epilogue_line_note = seq;
+ break;
+ }
+
+ for (e = last->pred; e; e = e_next)
{
basic_block bb = e->src;
rtx jump;
with a simple return instruction. */
if (simplejump_p (jump))
{
- emit_return_into_block (bb);
+ emit_return_into_block (bb, epilogue_line_note);
flow_delete_insn (jump);
}
continue;
if (JUMP_LABEL (jump))
LABEL_NUSES (JUMP_LABEL (jump))--;
-
+
/* If this block has only one successor, it both jumps
and falls through to the fallthru block, so we can't
delete the edge. */
- if (bb->succ->succ_next == NULL)
- continue;
+ if (bb->succ->succ_next == NULL)
+ continue;
}
else
continue;
/* Fix up the CFG for the successful change we just made. */
- remove_edge (e);
- make_edge (NULL, bb, EXIT_BLOCK_PTR, 0);
+ redirect_edge_succ (e, EXIT_BLOCK_PTR);
}
- }
- /* Emit a return insn for the exit fallthru block. Whether
- this is still reachable will be determined later. */
+ /* Emit a return insn for the exit fallthru block. Whether
+ this is still reachable will be determined later. */
- emit_barrier_after (last->end);
- emit_return_into_block (last);
- goto epilogue_done;
+ emit_barrier_after (last->end);
+ emit_return_into_block (last, epilogue_line_note);
+ epilogue_end = last->end;
+ goto epilogue_done;
+ }
}
#endif
#ifdef HAVE_epilogue
There really shouldn't be a mixture -- either all should have
been converted or none, however... */
- for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
+ for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
if (e->flags & EDGE_FALLTHRU)
break;
if (e == NULL)
goto epilogue_done;
start_sequence ();
- emit_note (NULL, NOTE_INSN_EPILOGUE_BEG);
+ epilogue_end = emit_note (NULL, NOTE_INSN_EPILOGUE_BEG);
seq = gen_epilogue ();
+
+ /* If this function returns with the stack depressed, massage
+ the epilogue to actually do that. */
+ if (TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE
+ && TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (current_function_decl)))
+ keep_stack_depressed (seq);
+
emit_jump_insn (seq);
/* Retain a map of the epilogue insns. */
if (GET_CODE (seq) != SEQUENCE)
seq = get_insns ();
- epilogue = record_insns (seq);
+ record_insns (seq, &epilogue);
seq = gen_sequence ();
- end_sequence();
+ end_sequence ();
insert_insn_on_edge (seq, e);
- insertted = 1;
+ inserted = 1;
}
#endif
epilogue_done:
- if (insertted)
+ if (inserted)
commit_edge_insertions ();
+
+#ifdef HAVE_sibcall_epilogue
+ /* Emit sibling epilogues before any sibling call sites. */
+ for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
+ {
+ basic_block bb = e->src;
+ rtx insn = bb->end;
+ rtx i;
+ rtx newinsn;
+
+ if (GET_CODE (insn) != CALL_INSN
+ || ! SIBLING_CALL_P (insn))
+ continue;
+
+ start_sequence ();
+ seq = gen_sibcall_epilogue ();
+ end_sequence ();
+
+ i = PREV_INSN (insn);
+ newinsn = emit_insn_before (seq, insn);
+
+ /* Update the UID to basic block map. */
+ for (i = NEXT_INSN (i); i != insn; i = NEXT_INSN (i))
+ set_block_for_insn (i, bb);
+
+ /* Retain a map of the epilogue insns. Used in life analysis to
+ avoid getting rid of sibcall epilogue insns. */
+ record_insns (GET_CODE (seq) == SEQUENCE
+ ? seq : newinsn, &sibcall_epilogue);
+ }
+#endif
+
+#ifdef HAVE_prologue
+ if (prologue_end)
+ {
+ rtx insn, prev;
+
+ /* GDB handles `break f' by setting a breakpoint on the first
+ line note after the prologue. Which means (1) that if
+ there are line number notes before where we inserted the
+ prologue we should move them, and (2) we should generate a
+ note before the end of the first basic block, if there isn't
+ one already there.
+
+ ??? This behaviour is completely broken when dealing with
+ multiple entry functions. We simply place the note always
+ into first basic block and let alternate entry points
+ to be missed.
+ */
+
+ for (insn = prologue_end; insn; insn = prev)
+ {
+ prev = PREV_INSN (insn);
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ {
+ /* Note that we cannot reorder the first insn in the
+ chain, since rest_of_compilation relies on that
+ remaining constant. */
+ if (prev == NULL)
+ break;
+ reorder_insns (insn, insn, prologue_end);
+ }
+ }
+
+ /* Find the last line number note in the first block. */
+ for (insn = BASIC_BLOCK (0)->end;
+ insn != prologue_end && insn;
+ insn = PREV_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ break;
+
+ /* If we didn't find one, make a copy of the first line number
+ we run across. */
+ if (! insn)
+ {
+ for (insn = next_active_insn (prologue_end);
+ insn;
+ insn = PREV_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ {
+ emit_line_note_after (NOTE_SOURCE_FILE (insn),
+ NOTE_LINE_NUMBER (insn),
+ prologue_end);
+ break;
+ }
+ }
+ }
+#endif
+#ifdef HAVE_epilogue
+ if (epilogue_end)
+ {
+ rtx insn, next;
+
+ /* Similarly, move any line notes that appear after the epilogue.
+ There is no need, however, to be quite so anal about the existance
+ of such a note. */
+ for (insn = epilogue_end; insn; insn = next)
+ {
+ next = NEXT_INSN (insn);
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ reorder_insns (insn, insn, PREV_INSN (epilogue_end));
+ }
+ }
+#endif
}
/* Reposition the prologue-end and epilogue-begin notes after instruction
rtx f ATTRIBUTE_UNUSED;
{
#if defined (HAVE_prologue) || defined (HAVE_epilogue)
- /* Reposition the prologue and epilogue notes. */
- if (n_basic_blocks)
+ int len;
+
+ if ((len = VARRAY_SIZE (prologue)) > 0)
{
- int len;
+ register rtx insn, note = 0;
- if (prologue)
+ /* Scan from the beginning until we reach the last prologue insn.
+ We apparently can't depend on basic_block_{head,end} after
+ reorg has run. */
+ for (insn = f; len && insn; insn = NEXT_INSN (insn))
{
- register rtx insn, note = 0;
-
- /* Scan from the beginning until we reach the last prologue insn.
- We apparently can't depend on basic_block_{head,end} after
- reorg has run. */
- for (len = 0; prologue[len]; len++)
- ;
- for (insn = f; len && insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE)
{
- if (GET_CODE (insn) == NOTE)
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END)
+ note = insn;
+ }
+ else if ((len -= contains (insn, prologue)) == 0)
+ {
+ rtx next;
+ /* Find the prologue-end note if we haven't already, and
+ move it to just after the last prologue insn. */
+ if (note == 0)
{
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END)
- note = insn;
+ for (note = insn; (note = NEXT_INSN (note));)
+ if (GET_CODE (note) == NOTE
+ && NOTE_LINE_NUMBER (note) == NOTE_INSN_PROLOGUE_END)
+ break;
}
- else if ((len -= contains (insn, prologue)) == 0)
- {
- rtx next;
- /* Find the prologue-end note if we haven't already, and
- move it to just after the last prologue insn. */
- if (note == 0)
- {
- for (note = insn; (note = NEXT_INSN (note));)
- if (GET_CODE (note) == NOTE
- && NOTE_LINE_NUMBER (note) == NOTE_INSN_PROLOGUE_END)
- break;
- }
- next = NEXT_INSN (note);
+ next = NEXT_INSN (note);
- /* Whether or not we can depend on BLOCK_HEAD,
- attempt to keep it up-to-date. */
- if (BLOCK_HEAD (0) == note)
- BLOCK_HEAD (0) = next;
+ /* Whether or not we can depend on BLOCK_HEAD,
+ attempt to keep it up-to-date. */
+ if (BLOCK_HEAD (0) == note)
+ BLOCK_HEAD (0) = next;
- remove_insn (note);
- add_insn_after (note, insn);
- }
+ remove_insn (note);
+ /* Avoid placing note between CODE_LABEL and BASIC_BLOCK note. */
+ if (GET_CODE (insn) == CODE_LABEL)
+ insn = NEXT_INSN (insn);
+ add_insn_after (note, insn);
}
}
+ }
+
+ if ((len = VARRAY_SIZE (epilogue)) > 0)
+ {
+ register rtx insn, note = 0;
- if (epilogue)
+ /* Scan from the end until we reach the first epilogue insn.
+ We apparently can't depend on basic_block_{head,end} after
+ reorg has run. */
+ for (insn = get_last_insn (); len && insn; insn = PREV_INSN (insn))
{
- register rtx insn, note = 0;
-
- /* Scan from the end until we reach the first epilogue insn.
- We apparently can't depend on basic_block_{head,end} after
- reorg has run. */
- for (len = 0; epilogue[len]; len++)
- ;
- for (insn = get_last_insn (); len && insn; insn = PREV_INSN (insn))
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG)
+ note = insn;
+ }
+ else if ((len -= contains (insn, epilogue)) == 0)
{
- if (GET_CODE (insn) == NOTE)
+ /* Find the epilogue-begin note if we haven't already, and
+ move it to just before the first epilogue insn. */
+ if (note == 0)
{
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG)
- note = insn;
+ for (note = insn; (note = PREV_INSN (note));)
+ if (GET_CODE (note) == NOTE
+ && NOTE_LINE_NUMBER (note) == NOTE_INSN_EPILOGUE_BEG)
+ break;
}
- else if ((len -= contains (insn, epilogue)) == 0)
- {
- /* Find the epilogue-begin note if we haven't already, and
- move it to just before the first epilogue insn. */
- if (note == 0)
- {
- for (note = insn; (note = PREV_INSN (note));)
- if (GET_CODE (note) == NOTE
- && NOTE_LINE_NUMBER (note) == NOTE_INSN_EPILOGUE_BEG)
- break;
- }
- /* Whether or not we can depend on BLOCK_HEAD,
- attempt to keep it up-to-date. */
- if (n_basic_blocks
- && BLOCK_HEAD (n_basic_blocks-1) == insn)
- BLOCK_HEAD (n_basic_blocks-1) = note;
+ /* Whether or not we can depend on BLOCK_HEAD,
+ attempt to keep it up-to-date. */
+ if (n_basic_blocks
+ && BLOCK_HEAD (n_basic_blocks-1) == insn)
+ BLOCK_HEAD (n_basic_blocks-1) = note;
- remove_insn (note);
- add_insn_before (note, insn);
- }
+ remove_insn (note);
+ add_insn_before (note, insn);
}
}
}
static void
mark_temp_slot (t)
- struct temp_slot *t;
+ struct temp_slot *t;
{
while (t)
{
ggc_mark_rtx (t->slot);
ggc_mark_rtx (t->address);
ggc_mark_tree (t->rtl_expr);
+ ggc_mark_tree (t->type);
t = t->next;
}
ggc_mark_tree (p->x_context_display);
ggc_mark_tree (p->x_trampoline_list);
ggc_mark_rtx (p->epilogue_delay_list);
+ ggc_mark_rtx (p->x_clobber_return_insn);
mark_temp_slot (p->x_temp_slots);
{
ggc_add_root (&all_functions, 1, sizeof all_functions,
mark_function_chain);
+
+ VARRAY_INT_INIT (prologue, 0, "prologue");
+ VARRAY_INT_INIT (epilogue, 0, "epilogue");
+ VARRAY_INT_INIT (sibcall_epilogue, 0, "sibcall_epilogue");
}