/* Expands front end tree to back end RTL for GNU C-Compiler
- Copyright (C) 1987, 88, 89, 91-97, 1998 Free Software Foundation, Inc.
+ Copyright (C) 1987, 88, 89, 91-98, 1999 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "basic-block.h"
#include "obstack.h"
#include "toplev.h"
+#include "hash.h"
#ifndef TRAMPOLINE_ALIGNMENT
#define TRAMPOLINE_ALIGNMENT FUNCTION_BOUNDARY
#endif
+#ifndef LOCAL_ALIGNMENT
+#define LOCAL_ALIGNMENT(TYPE, ALIGNMENT) ALIGNMENT
+#endif
+
/* Some systems use __main in a way incompatible with its use in gcc, in these
cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
give the same symbol without quotes for an alternative entry point. You
int current_function_has_nonlocal_goto;
-/* Nonzero if this function has a computed goto.
-
- It is computed during find_basic_blocks or during stupid life
- analysis. */
-
-int current_function_has_computed_jump;
-
/* Nonzero if function being compiled contains nested functions. */
int current_function_contains_functions;
+/* 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. */
+
+int current_function_is_leaf;
+
/* 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. */
int current_function_sp_is_unchanging;
+/* Nonzero if the function being compiled is a leaf function which only
+ uses leaf registers. This is valid after reload (specifically after
+ sched2) and is useful only if the port defines LEAF_REGISTERS. */
+
+int current_function_uses_only_leaf_regs;
+
+/* Nonzero if the function being compiled issues a computed jump. */
+
+int current_function_has_computed_jump;
+
/* Nonzero if the current function is a thunk (a lightweight function that
just adjusts one of its arguments and forwards to another function), so
we should try to cut corners where we can. */
tree nonlocal_labels;
-/* RTX for stack slot that holds the current handler for nonlocal gotos.
+/* List (chain of EXPR_LIST) of stack slots that hold the current handlers
+ for nonlocal gotos. There is one for every nonlocal label in the function;
+ this list matches the one in nonlocal_labels.
Zero when function does not have nonlocal labels. */
-rtx nonlocal_goto_handler_slot;
+rtx nonlocal_goto_handler_slots;
+
+/* List (chain of EXPR_LIST) of labels heading the current handlers for
+ nonlocal gotos. */
+
+rtx nonlocal_goto_handler_labels;
/* RTX for stack slot that holds the stack pointer value to restore
for a nonlocal goto.
extern int rtx_equal_function_value_matters;
extern tree sequence_rtl_expr;
+
+/* The currently compiled function. */
+struct function *current_function = 0;
+
+/* Global list of all compiled functions. */
+struct function *all_functions = 0;
\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. */
+ 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 value of `sequence_rtl_expr' when this temporary is allocated. */
tree rtl_expr;
/* Non-zero if this temporary is currently in use. */
struct fixup_replacement *next;
};
+struct insns_for_mem_entry {
+ /* The KEY in HE will be a MEM. */
+ struct hash_entry he;
+ /* These are the INSNS which reference the MEM. */
+ rtx insns;
+};
+
/* Forward declarations. */
static rtx assign_outer_stack_local PROTO ((enum machine_mode, HOST_WIDE_INT,
int, struct function *));
+static rtx assign_stack_temp_for_type PROTO ((enum machine_mode, HOST_WIDE_INT,
+ int, tree));
static struct temp_slot *find_temp_slot_from_address PROTO((rtx));
static void put_reg_into_stack PROTO((struct function *, rtx, tree,
enum machine_mode, enum machine_mode,
- int, int, int));
-static void fixup_var_refs PROTO((rtx, enum machine_mode, int));
+ int, int, int,
+ struct hash_table *));
+static void fixup_var_refs PROTO((rtx, enum machine_mode, int,
+ struct hash_table *));
static struct fixup_replacement
*find_fixup_replacement PROTO((struct fixup_replacement **, rtx));
static void fixup_var_refs_insns PROTO((rtx, enum machine_mode, int,
- rtx, int));
+ rtx, int, struct hash_table *));
static void fixup_var_refs_1 PROTO((rtx, enum machine_mode, rtx *, rtx,
struct fixup_replacement **));
static rtx fixup_memory_subreg PROTO((rtx, rtx, int));
static int *record_insns PROTO((rtx));
static int contains PROTO((rtx, int *));
#endif /* HAVE_prologue || HAVE_epilogue */
-static void put_addressof_into_stack PROTO((rtx));
-static void purge_addressof_1 PROTO((rtx *, rtx, int, int));
+static void put_addressof_into_stack PROTO((rtx, struct hash_table *));
+static void purge_addressof_1 PROTO((rtx *, rtx, int, int,
+ struct hash_table *));
+static struct hash_entry *insns_for_mem_newfunc PROTO((struct hash_entry *,
+ struct hash_table *,
+ hash_table_key));
+static unsigned long insns_for_mem_hash PROTO ((hash_table_key));
+static boolean insns_for_mem_comp PROTO ((hash_table_key, hash_table_key));
+static int insns_for_mem_walk PROTO ((rtx *, void *));
+static void compute_insns_for_mem PROTO ((rtx, rtx, struct hash_table *));
+
\f
/* Pointer to chain of `struct function' for containing functions. */
struct function *outer_function_chain;
push_function_context_to (context)
tree context;
{
- struct function *p = (struct function *) xmalloc (sizeof (struct function));
+ struct function *p;
+
+ if (current_function == 0)
+ init_dummy_function_start ();
+ p = current_function;
p->next = outer_function_chain;
outer_function_chain = p;
p->has_nonlocal_label = current_function_has_nonlocal_label;
p->has_nonlocal_goto = current_function_has_nonlocal_goto;
p->contains_functions = current_function_contains_functions;
+ p->has_computed_jump = current_function_has_computed_jump;
p->is_thunk = current_function_is_thunk;
p->args_size = current_function_args_size;
p->pretend_args_size = current_function_pretend_args_size;
p->parm_reg_stack_loc = parm_reg_stack_loc;
p->outgoing_args_size = current_function_outgoing_args_size;
p->return_rtx = current_function_return_rtx;
- p->nonlocal_goto_handler_slot = nonlocal_goto_handler_slot;
+ p->nonlocal_goto_handler_slots = nonlocal_goto_handler_slots;
+ p->nonlocal_goto_handler_labels = nonlocal_goto_handler_labels;
p->nonlocal_goto_stack_level = nonlocal_goto_stack_level;
p->nonlocal_labels = nonlocal_labels;
p->cleanup_label = cleanup_label;
save_storage_status (p);
save_emit_status (p);
save_expr_status (p);
- save_stmt_status (p);
save_varasm_status (p, context);
if (save_machine_status)
(*save_machine_status) (p);
+
+ current_function = 0;
}
void
struct function *p = outer_function_chain;
struct var_refs_queue *queue;
+ current_function = p;
outer_function_chain = p->next;
current_function_contains_functions
= p->contains_functions || p->inline_obstacks
|| context == current_function_decl;
+ current_function_has_computed_jump = p->has_computed_jump;
current_function_name = p->name;
current_function_decl = p->decl;
current_function_pops_args = p->pops_args;
parm_reg_stack_loc = p->parm_reg_stack_loc;
current_function_outgoing_args_size = p->outgoing_args_size;
current_function_return_rtx = p->return_rtx;
- nonlocal_goto_handler_slot = p->nonlocal_goto_handler_slot;
+ nonlocal_goto_handler_slots = p->nonlocal_goto_handler_slots;
+ nonlocal_goto_handler_labels = p->nonlocal_goto_handler_labels;
nonlocal_goto_stack_level = p->nonlocal_goto_stack_level;
nonlocal_labels = p->nonlocal_labels;
cleanup_label = p->cleanup_label;
restore_storage_status (p);
restore_expr_status (p);
restore_emit_status (p);
- restore_stmt_status (p);
restore_varasm_status (p);
if (restore_machine_status)
/* Finish doing put_var_into_stack for any of our variables
which became addressable during the nested function. */
for (queue = p->fixup_var_refs_queue; queue; queue = queue->next)
- fixup_var_refs (queue->modified, queue->promoted_mode, queue->unsignedp);
-
- free (p);
+ fixup_var_refs (queue->modified, queue->promoted_mode,
+ queue->unsignedp, 0);
/* Reset variables that have known state during rtx generation. */
rtx_equal_function_value_matters = 1;
/* Allocate fixed slots in the stack frame of the current function. */
/* Return size needed for stack frame based on slots so far allocated.
- This size counts from zero. It is not rounded to STACK_BOUNDARY;
+ This size counts from zero. It is not rounded to PREFERRED_STACK_BOUNDARY;
the caller may have to do that. */
HOST_WIDE_INT
if (align == 0)
{
- alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ tree type;
+
+ alignment = GET_MODE_ALIGNMENT (mode);
if (mode == BLKmode)
- alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+ alignment = BIGGEST_ALIGNMENT;
+
+ /* Allow the target to (possibly) increase the alignment of this
+ stack slot. */
+ type = type_for_mode (mode, 0);
+ if (type)
+ alignment = LOCAL_ALIGNMENT (type, alignment);
+
+ alignment /= BITS_PER_UNIT;
}
else if (align == -1)
{
else
alignment = align / BITS_PER_UNIT;
+#ifdef FRAME_GROWS_DOWNWARD
+ frame_offset -= size;
+#endif
+
/* Round frame offset to that alignment.
We must be careful here, since FRAME_OFFSET might be negative and
division with a negative dividend isn't as well defined as we might
if (BYTES_BIG_ENDIAN && mode != BLKmode)
bigend_correction = size - GET_MODE_SIZE (mode);
-#ifdef FRAME_GROWS_DOWNWARD
- frame_offset -= size;
-#endif
-
/* If we have already instantiated virtual registers, return the actual
address relative to the frame pointer. */
if (virtuals_instantiated)
if (align == 0)
{
- alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ tree type;
+
+ alignment = GET_MODE_ALIGNMENT (mode);
if (mode == BLKmode)
- alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+ alignment = BIGGEST_ALIGNMENT;
+
+ /* Allow the target to (possibly) increase the alignment of this
+ stack slot. */
+ type = type_for_mode (mode, 0);
+ if (type)
+ alignment = LOCAL_ALIGNMENT (type, alignment);
+
+ alignment /= BITS_PER_UNIT;
}
else if (align == -1)
{
else
alignment = align / BITS_PER_UNIT;
+#ifdef FRAME_GROWS_DOWNWARD
+ function->frame_offset -= size;
+#endif
+
/* Round frame offset to that alignment. */
#ifdef FRAME_GROWS_DOWNWARD
function->frame_offset = FLOOR_ROUND (function->frame_offset, alignment);
if (BYTES_BIG_ENDIAN && mode != BLKmode)
bigend_correction = size - GET_MODE_SIZE (mode);
-#ifdef FRAME_GROWS_DOWNWARD
- function->frame_offset -= size;
-#endif
addr = plus_constant (virtual_stack_vars_rtx,
function->frame_offset + bigend_correction);
#ifndef FRAME_GROWS_DOWNWARD
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).
-rtx
-assign_stack_temp (mode, size, keep)
+ TYPE is the type that will be used for the stack slot. */
+
+static rtx
+assign_stack_temp_for_type (mode, size, keep, type)
enum machine_mode mode;
HOST_WIDE_INT size;
int keep;
+ 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 ();
- /* First try to find an available, already-allocated temporary that is the
- exact size we require. */
+ /* 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;
+
+ if (! type)
+ type = type_for_mode (mode, 0);
+ if (type)
+ align = LOCAL_ALIGNMENT (type, align);
+
+ /* Try to find an available, already-allocated temporary of the proper
+ mode which meets the size and alignment requirements. Choose the
+ smallest one with the closest alignment. */
for (p = temp_slots; p; p = p->next)
- if (p->size == size && GET_MODE (p->slot) == mode && ! p->in_use)
- break;
-
- /* If we didn't find, one, try one that is larger than what we want. We
- find the smallest such. */
- if (p == 0)
- for (p = temp_slots; p; p = p->next)
- if (p->size > size && GET_MODE (p->slot) == mode && ! p->in_use
- && (best_p == 0 || best_p->size > p->size))
+ 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))
+ && (best_p == 0 || best_p->size > p->size
+ || (best_p->size == p->size && best_p->align > p->align)))
+ {
+ if (p->align == align && p->size == size)
+ {
+ best_p = 0;
+ break;
+ }
best_p = p;
+ }
/* Make our best, if any, the one to use. */
if (best_p)
/* 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)
+ 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)
{
- int alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+ 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->slot = gen_rtx_MEM (BLKmode,
plus_constant (XEXP (best_p->slot, 0),
rounded_size));
+ p->align = best_p->align;
p->address = 0;
p->rtl_expr = 0;
p->next = temp_slots;
p = (struct temp_slot *) oballoc (sizeof (struct temp_slot));
- /* If the temp slot mode doesn't indicate the alignment,
- use the largest possible, so no one will be disappointed. */
- p->slot = assign_stack_local (mode, size, mode == BLKmode ? -1 : 0);
+ /* We are passing an explicit alignment request to assign_stack_local.
+ One side effect of that is assign_stack_local will not round SIZE
+ to ensure the frame offset remains suitably aligned.
+
+ So for requests which depended on the rounding of SIZE, we go ahead
+ and round it now. We also make sure ALIGNMENT is at least
+ BIGGEST_ALIGNMENT. */
+ if (mode == BLKmode && align < (BIGGEST_ALIGNMENT / BITS_PER_UNIT))
+ abort();
+ p->slot = assign_stack_local (mode,
+ mode == BLKmode
+ ? CEIL_ROUND (size, align) : 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
set from before. */
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;
return p->slot;
}
+
+/* Allocate a temporary stack slot and record it for possible later
+ reuse. First three arguments are same as in preceding function. */
+
+rtx
+assign_stack_temp (mode, size, keep)
+ enum machine_mode mode;
+ HOST_WIDE_INT size;
+ int keep;
+{
+ return assign_stack_temp_for_type (mode, size, keep, NULL_TREE);
+}
\f
/* Assign a temporary of given TYPE.
KEEP is as for assign_stack_temp.
&& TREE_CODE (TYPE_ARRAY_MAX_SIZE (type)) == INTEGER_CST)
size = TREE_INT_CST_LOW (TYPE_ARRAY_MAX_SIZE (type));
- tmp = assign_stack_temp (mode, size, keep);
- MEM_IN_STRUCT_P (tmp) = AGGREGATE_TYPE_P (type);
+ tmp = assign_stack_temp_for_type (mode, size, keep, type);
+ MEM_SET_IN_STRUCT_P (tmp, AGGREGATE_TYPE_P (type));
return tmp;
}
struct temp_slot *prev_p, *prev_q;
int num_slots;
+ /* We can't combine slots, because the information about which slot
+ is in which alias set will be lost. */
+ if (flag_strict_aliasing)
+ return;
+
/* If there are a lot of temp slots, don't do anything unless
high levels of optimizaton. */
if (! flag_expensive_optimizations)
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);
+ TREE_USED (decl) || DECL_INITIAL (decl) != 0,
+ 0);
}
else if (GET_CODE (reg) == CONCAT)
{
/* 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);
+ 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);
+ TREE_USED (decl) || DECL_INITIAL (decl) != 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);
+ 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);
+ TREE_USED (decl) || DECL_INITIAL (decl) != 0,
+ 0);
#endif
/* Change the CONCAT into a combined MEM for both parts. */
if (current_function_check_memory_usage)
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
- XEXP (reg, 0), ptr_mode,
+ XEXP (reg, 0), Pmode,
GEN_INT (GET_MODE_SIZE (GET_MODE (reg))),
TYPE_MODE (sizetype),
GEN_INT (MEMORY_USE_RW),
static void
put_reg_into_stack (function, reg, type, promoted_mode, decl_mode, volatile_p,
- original_regno, used_p)
+ original_regno, used_p, ht)
struct function *function;
rtx reg;
tree type;
int volatile_p;
int original_regno;
int used_p;
+ struct hash_table *ht;
{
rtx new = 0;
int regno = original_regno;
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_IN_STRUCT_P (reg) = AGGREGATE_TYPE_P (type) | MEM_IN_STRUCT_P (new);
+ MEM_SET_IN_STRUCT_P (reg,
+ AGGREGATE_TYPE_P (type) || MEM_IN_STRUCT_P (new));
MEM_ALIAS_SET (reg) = get_alias_set (type);
/* Now make sure that all refs to the variable, previously made
}
else if (used_p)
/* Variable is local; fix it up now. */
- fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (type));
+ fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (type), ht);
}
\f
static void
-fixup_var_refs (var, promoted_mode, unsignedp)
+fixup_var_refs (var, promoted_mode, unsignedp, ht)
rtx var;
enum machine_mode promoted_mode;
int unsignedp;
+ struct hash_table *ht;
{
tree pending;
rtx first_insn = get_insns ();
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);
+ 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;
/* 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);
+ stack->first, stack->next != 0, 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);
+ fixup_var_refs_insns (var, promoted_mode, unsignedp, seq, 0,
+ 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 ();
}
\f
/* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is
main chain of insns for the current function. */
static void
-fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel)
+fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel, ht)
rtx var;
enum machine_mode promoted_mode;
int unsignedp;
rtx insn;
int toplevel;
+ struct hash_table *ht;
{
rtx call_dest = 0;
+ rtx insn_list = NULL_RTX;
+
+ /* If we already know which INSNs reference VAR there's no need
+ to walk the entire instruction chain. */
+ if (ht)
+ {
+ 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;
+ insn_list = XEXP (insn_list, 1);
+ }
while (insn)
{
XEXP (note, 0)
= walk_fixup_memory_subreg (XEXP (note, 0), insn, 1);
}
- insn = next;
+
+ if (!ht)
+ insn = next;
+ else if (insn_list)
+ {
+ insn = XEXP (insn_list, 0);
+ insn_list = XEXP (insn_list, 1);
+ }
+ else
+ insn = NULL_RTX;
}
}
\f
/* Prevent sharing of rtl that might lose. */
rtx sub = copy_rtx (XEXP (var, 0));
- start_sequence ();
-
if (! validate_change (insn, loc, sub, 0))
{
- rtx y = force_operand (sub, NULL_RTX);
+ rtx y = gen_reg_rtx (GET_MODE (sub));
+ rtx seq, new_insn;
- if (! validate_change (insn, loc, y, 0))
- *loc = copy_to_reg (y);
- }
+ /* We should be able to replace with a register or all is lost.
+ Note that we can't use validate_change to verify this, since
+ we're not caring for replacing all dups simultaneously. */
+ if (! validate_replace_rtx (*loc, y, insn))
+ abort ();
- emit_insn_before (gen_sequence (), insn);
- end_sequence ();
+ /* Careful! First try to recognize a direct move of the
+ value, mimicking how things are done in gen_reload wrt
+ PLUS. Consider what happens when insn is a conditional
+ move instruction and addsi3 clobbers flags. */
+
+ start_sequence ();
+ new_insn = emit_insn (gen_rtx_SET (VOIDmode, y, sub));
+ seq = gen_sequence ();
+ end_sequence ();
+
+ if (recog_memoized (new_insn) < 0)
+ {
+ /* That failed. Fall back on force_operand and hope. */
+
+ start_sequence ();
+ force_operand (sub, y);
+ seq = gen_sequence ();
+ end_sequence ();
+ }
+
+#ifdef HAVE_cc0
+ /* Don't separate setter from user. */
+ if (PREV_INSN (insn) && sets_cc0_p (PREV_INSN (insn)))
+ insn = PREV_INSN (insn);
+#endif
+
+ emit_insn_before (seq, insn);
+ }
}
return;
newmem = gen_rtx_MEM (wanted_mode,
plus_constant (XEXP (tem, 0), offset));
RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem);
- MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (tem);
- MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (tem);
+ MEM_COPY_ATTRIBUTES (newmem, tem);
/* Make the change and see if the insn remains valid. */
INSN_CODE (insn) = -1;
newmem = gen_rtx_MEM (wanted_mode,
plus_constant (XEXP (tem, 0), offset));
RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem);
- MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (tem);
- MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (tem);
+ MEM_COPY_ATTRIBUTES (newmem, tem);
/* Make the change and see if the insn remains valid. */
INSN_CODE (insn) = -1;
while (GET_CODE (dest) == SUBREG
&& SUBREG_WORD (dest) == 0
&& (GET_MODE_CLASS (GET_MODE (dest))
- == GET_MODE_CLASS (GET_MODE (SUBREG_REG (dest)))))
+ == GET_MODE_CLASS (GET_MODE (SUBREG_REG (dest))))
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))
+ <= UNITS_PER_WORD))
dest = SUBREG_REG (dest);
validate_change (insn, &SET_DEST (body), dest, 1);
PUT_CODE (reg, MEM);
PUT_MODE (reg, DECL_MODE (decl));
MEM_VOLATILE_P (reg) = TREE_SIDE_EFFECTS (decl);
- MEM_IN_STRUCT_P (reg) = AGGREGATE_TYPE_P (type);
+ 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));
+ fixup_var_refs (reg, GET_MODE (reg), TREE_UNSIGNED (type), 0);
return reg;
}
&& GET_CODE (DECL_RTL (decl)) == MEM
&& GET_CODE (XEXP (DECL_RTL (decl), 0)) == ADDRESSOF
&& GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == REG)
- put_addressof_into_stack (XEXP (DECL_RTL (decl), 0));
+ put_addressof_into_stack (XEXP (DECL_RTL (decl), 0), 0);
}
/* Force the register pointed to by R, an ADDRESSOF rtx, into the stack. */
static void
-put_addressof_into_stack (r)
+put_addressof_into_stack (r, ht)
rtx r;
+ struct hash_table *ht;
{
tree decl = ADDRESSOF_DECL (r);
rtx reg = XEXP (r, 0);
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);
+ TREE_USED (decl) || DECL_INITIAL (decl) != 0, ht);
}
/* List of replacements made below in purge_addressof_1 when creating
bitfield insertions. */
+static rtx purge_bitfield_addressof_replacements;
+
+/* List of replacements made below in purge_addressof_1 for patterns
+ (MEM (ADDRESSOF (REG ...))). The key of the list entry is the
+ 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. */
static rtx purge_addressof_replacements;
/* Helper function for purge_addressof. See if the rtx expression at *LOC
the stack. */
static void
-purge_addressof_1 (loc, insn, force, store)
+purge_addressof_1 (loc, insn, force, store, ht)
rtx *loc;
rtx insn;
int force, store;
+ struct hash_table *ht;
{
rtx x;
RTX_CODE code;
overwriting a REG rtx which is always shared. */
rtx sub = copy_rtx (XEXP (XEXP (x, 0), 0));
- if (validate_change (insn, loc, sub, 0))
+ if (validate_change (insn, loc, sub, 0)
+ || validate_replace_rtx (x, sub, insn))
return;
-
+
start_sequence ();
- if (! validate_change (insn, loc,
- force_operand (sub, NULL_RTX),
- 0))
+ sub = force_operand (sub, NULL_RTX);
+ if (! validate_change (insn, loc, sub, 0)
+ && ! validate_replace_rtx (x, sub, insn))
abort ();
insns = gen_sequence ();
else if (code == MEM && GET_CODE (XEXP (x, 0)) == ADDRESSOF && ! force)
{
rtx sub = XEXP (XEXP (x, 0), 0);
+ rtx sub2;
if (GET_CODE (sub) == MEM)
- sub = gen_rtx_MEM (GET_MODE (x), copy_rtx (XEXP (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;
+ }
if (GET_CODE (sub) == REG
&& (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode))
{
- put_addressof_into_stack (XEXP (x, 0));
+ put_addressof_into_stack (XEXP (x, 0), ht);
return;
}
else if (GET_CODE (sub) == REG && GET_MODE (x) != GET_MODE (sub))
was replaced by. */
rtx tem;
- for (tem = purge_addressof_replacements; tem != NULL_RTX;
+ for (tem = purge_bitfield_addressof_replacements;
+ tem != NULL_RTX;
tem = XEXP (XEXP (tem, 1), 1))
if (rtx_equal_p (x, XEXP (tem, 0)))
{
return;
}
+ /* See comment for purge_addressof_replacements. */
+ for (tem = purge_addressof_replacements;
+ tem != NULL_RTX;
+ tem = XEXP (XEXP (tem, 1), 1))
+ if (rtx_equal_p (XEXP (x, 0), XEXP (tem, 0)))
+ {
+ rtx z = XEXP (XEXP (tem, 1), 0);
+
+ if (GET_MODE (x) == GET_MODE (z)
+ || (GET_CODE (XEXP (XEXP (tem, 1), 0)) != REG
+ && GET_CODE (XEXP (XEXP (tem, 1), 0)) != SUBREG))
+ abort ();
+
+ /* 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. */
+
+ 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; ...
+ *(long long int *)&f.
+ ??? We could emit a warning here, but
+ without a line number that wouldn't be
+ very helpful. */
+ z = gen_rtx_SUBREG (GET_MODE (x), z, 0);
+ }
+ else
+ z = gen_lowpart (GET_MODE (x), z);
+
+ *loc = z;
+ return;
+ }
+
/* There should always be such a replacement. */
abort ();
}
if (store)
{
+ rtx p = PREV_INSN (insn);
+
start_sequence ();
val = gen_reg_rtx (GET_MODE (x));
if (! validate_change (insn, loc, val, 0))
seq = gen_sequence ();
end_sequence ();
emit_insn_before (seq, insn);
+ 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)));
+ /* Make sure to unshare any shared rtl that store_bit_field
+ might have created. */
+ for (p = get_insns(); p; p = NEXT_INSN (p))
+ {
+ reset_used_flags (PATTERN (p));
+ reset_used_flags (REG_NOTES (p));
+ reset_used_flags (LOG_LINKS (p));
+ }
+ unshare_all_rtl (get_insns ());
+
seq = gen_sequence ();
end_sequence ();
- emit_insn_after (seq, insn);
+ p = emit_insn_after (seq, insn);
+ if (NEXT_INSN (insn))
+ compute_insns_for_mem (NEXT_INSN (insn),
+ p ? NEXT_INSN (p) : NULL_RTX,
+ ht);
}
else
{
+ rtx p = PREV_INSN (insn);
+
start_sequence ();
val = extract_bit_field (sub, size_x, 0, 1, NULL_RTX,
GET_MODE (x), GET_MODE (x),
seq = gen_sequence ();
end_sequence ();
emit_insn_before (seq, insn);
+ compute_insns_for_mem (p ? NEXT_INSN (p) : get_insns (),
+ insn, ht);
}
/* Remember the replacement so that the same one can be done
on the REG_NOTES. */
- purge_addressof_replacements
+ purge_bitfield_addressof_replacements
= gen_rtx_EXPR_LIST (VOIDmode, x,
- gen_rtx_EXPR_LIST (VOIDmode, val,
- purge_addressof_replacements));
+ gen_rtx_EXPR_LIST
+ (VOIDmode, val,
+ purge_bitfield_addressof_replacements));
/* We replaced with a reg -- all done. */
return;
}
}
else if (validate_change (insn, loc, sub, 0))
- goto restart;
+ {
+ /* Remember the replacement so that the same one can be done
+ on the REG_NOTES. */
+ if (GET_CODE (sub) == REG || GET_CODE (sub) == SUBREG)
+ {
+ rtx tem;
+
+ for (tem = purge_addressof_replacements;
+ tem != NULL_RTX;
+ tem = XEXP (XEXP (tem, 1), 1))
+ if (rtx_equal_p (XEXP (x, 0), XEXP (tem, 0)))
+ {
+ XEXP (XEXP (tem, 1), 0) = sub;
+ return;
+ }
+ purge_addressof_replacements
+ = gen_rtx (EXPR_LIST, VOIDmode, XEXP (x, 0),
+ gen_rtx_EXPR_LIST (VOIDmode, sub,
+ purge_addressof_replacements));
+ return;
+ }
+ goto restart;
+ }
give_up:;
/* else give up and put it into the stack */
}
else if (code == ADDRESSOF)
{
- put_addressof_into_stack (x);
+ put_addressof_into_stack (x, ht);
return;
}
else if (code == SET)
{
- purge_addressof_1 (&SET_DEST (x), insn, force, 1);
- purge_addressof_1 (&SET_SRC (x), insn, force, 0);
+ purge_addressof_1 (&SET_DEST (x), insn, force, 1, ht);
+ purge_addressof_1 (&SET_SRC (x), insn, force, 0, ht);
return;
}
for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
{
if (*fmt == 'e')
- purge_addressof_1 (&XEXP (x, i), insn, force, 0);
+ purge_addressof_1 (&XEXP (x, i), insn, force, 0, ht);
else if (*fmt == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
- purge_addressof_1 (&XVECEXP (x, i, j), insn, force, 0);
+ purge_addressof_1 (&XVECEXP (x, i, j), insn, force, 0, ht);
+ }
+}
+
+/* Return a new hash table entry in HT. */
+
+static struct hash_entry *
+insns_for_mem_newfunc (he, ht, k)
+ struct hash_entry *he;
+ struct hash_table *ht;
+ hash_table_key k ATTRIBUTE_UNUSED;
+{
+ struct insns_for_mem_entry *ifmhe;
+ if (he)
+ return he;
+
+ ifmhe = ((struct insns_for_mem_entry *)
+ hash_allocate (ht, sizeof (struct insns_for_mem_entry)));
+ ifmhe->insns = NULL_RTX;
+
+ return &ifmhe->he;
+}
+
+/* Return a hash value for K, a REG. */
+
+static unsigned long
+insns_for_mem_hash (k)
+ hash_table_key k;
+{
+ /* K is really a RTX. Just use the address as the hash value. */
+ return (unsigned long) k;
+}
+
+/* Return non-zero if K1 and K2 (two REGs) are the same. */
+
+static boolean
+insns_for_mem_comp (k1, k2)
+ hash_table_key k1;
+ hash_table_key k2;
+{
+ return k1 == k2;
+}
+
+struct insns_for_mem_walk_info {
+ /* The hash table that we are using to record which INSNs use which
+ MEMs. */
+ struct hash_table *ht;
+
+ /* The INSN we are currently proessing. */
+ rtx insn;
+
+ /* Zero if we are walking to find ADDRESSOFs, one if we are walking
+ to find the insns that use the REGs in the ADDRESSOFs. */
+ int pass;
+};
+
+/* Called from compute_insns_for_mem via for_each_rtx. If R is a REG
+ that might be used in an ADDRESSOF expression, record this INSN in
+ the hash table given by DATA (which is really a pointer to an
+ insns_for_mem_walk_info structure). */
+
+static int
+insns_for_mem_walk (r, data)
+ rtx *r;
+ void *data;
+{
+ struct insns_for_mem_walk_info *ifmwi
+ = (struct insns_for_mem_walk_info *) data;
+
+ if (ifmwi->pass == 0 && *r && GET_CODE (*r) == ADDRESSOF
+ && GET_CODE (XEXP (*r, 0)) == REG)
+ hash_lookup (ifmwi->ht, XEXP (*r, 0), /*create=*/1, /*copy=*/0);
+ 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 *) hash_lookup (ifmwi->ht,
+ *r,
+ /*create=*/0,
+ /*copy=*/0);
+
+ /* If we have not already recorded this INSN, do so now. Since
+ 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 ();
+ }
}
+
+ return 0;
+}
+
+/* Walk the INSNS, until we reach LAST_INSN, recording which INSNs use
+ which REGs in HT. */
+
+static void
+compute_insns_for_mem (insns, last_insn, ht)
+ rtx insns;
+ rtx last_insn;
+ struct hash_table *ht;
+{
+ rtx insn;
+ struct insns_for_mem_walk_info ifmwi;
+ ifmwi.ht = ht;
+
+ 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')
+ {
+ ifmwi.insn = insn;
+ for_each_rtx (&insn, insns_for_mem_walk, &ifmwi);
+ }
}
/* Eliminate all occurrences of ADDRESSOF from INSNS. Elide any remaining
rtx insns;
{
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
+ 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,
+ insns_for_mem_newfunc,
+ insns_for_mem_hash,
+ insns_for_mem_comp);
+ compute_insns_for_mem (insns, NULL_RTX, &ht);
+
for (insn = insns; insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
|| GET_CODE (insn) == CALL_INSN)
{
purge_addressof_1 (&PATTERN (insn), insn,
- asm_noperands (PATTERN (insn)) > 0, 0);
- purge_addressof_1 (®_NOTES (insn), NULL_RTX, 0, 0);
- purge_addressof_replacements = 0;
+ asm_noperands (PATTERN (insn)) > 0, 0, &ht);
+ purge_addressof_1 (®_NOTES (insn), NULL_RTX, 0, 0, &ht);
}
+
+ /* Clean up. */
+ hash_table_free (&ht);
+ purge_bitfield_addressof_replacements = 0;
+ purge_addressof_replacements = 0;
}
\f
/* Pass through the INSNS of function FNDECL and convert virtual register
rtx x;
RTX_CODE code;
rtx new = 0;
- HOST_WIDE_INT offset;
+ HOST_WIDE_INT offset = 0;
rtx temp;
rtx seq;
int i, j;
TREE_CHAIN (last_t) = TREE_CHAIN (t);
}
}
- if (GET_CODE (insn) == INSN
- && ((nonlocal_goto_handler_slot != 0
- && reg_mentioned_p (nonlocal_goto_handler_slot, PATTERN (insn)))
+ if (GET_CODE (insn) == INSN)
+ {
+ int can_delete = 0;
+ rtx t;
+ for (t = nonlocal_goto_handler_slots; t != 0; t = XEXP (t, 1))
+ if (reg_mentioned_p (t, PATTERN (insn)))
+ {
+ can_delete = 1;
+ break;
+ }
+ if (can_delete
|| (nonlocal_goto_stack_level != 0
&& reg_mentioned_p (nonlocal_goto_stack_level,
- PATTERN (insn)))))
- delete_insn (insn);
+ PATTERN (insn))))
+ delete_insn (insn);
+ }
}
}
-
-/* Return a list (chain of EXPR_LIST nodes) for the nonlocal labels
- of the current function. */
-
-rtx
-nonlocal_label_rtx_list ()
-{
- tree t;
- rtx x = 0;
-
- for (t = nonlocal_labels; t; t = TREE_CHAIN (t))
- x = gen_rtx_EXPR_LIST (VOIDmode, label_rtx (TREE_VALUE (t)), x);
-
- return x;
-}
\f
/* Output a USE for any register use in RTL.
This is used with -noreg to mark the extent of lifespan
/* 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
int varargs_setup = 0;
+#endif
rtx conversion_insns = 0;
/* Nonzero if the last arg is named `__builtin_va_alist',
int did_conversion = 0;
tree passed_type = DECL_ARG_TYPE (parm);
tree nominal_type = TREE_TYPE (parm);
+ int pretend_named;
/* Set LAST_NAMED if this is last named arg before some
anonymous args. */
In this case, we call FUNCTION_ARG with NAMED set to 1 instead of
0 as it was the previous time. */
+ pretend_named = named_arg || PRETEND_OUTGOING_VARARGS_NAMED;
locate_and_pad_parm (promoted_mode, passed_type,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
#ifdef FUNCTION_INCOMING_ARG
FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
passed_type,
- (named_arg
- || varargs_setup)) != 0,
+ pretend_named) != 0,
#else
FUNCTION_ARG (args_so_far, promoted_mode,
passed_type,
- named_arg || varargs_setup) != 0,
+ pretend_named) != 0,
#endif
#endif
fndecl, &stack_args_size, &stack_offset, &arg_size);
/* 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_IN_STRUCT_P (stack_parm) = aggregate;
+ 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);
}
to indicate there is no preallocated stack slot for the parm. */
if (entry_parm == stack_parm
+ || (GET_CODE (entry_parm) == PARALLEL
+ && XEXP (XVECEXP (entry_parm, 0, 0), 0) == NULL_RTX)
#if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE)
/* On some machines, even if a parm value arrives in a register
there is still an (uninitialized) stack slot allocated for it.
/* If this is a memory ref that contains aggregate components,
mark it as such for cse and loop optimize. */
- MEM_IN_STRUCT_P (stack_parm) = aggregate;
+ MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
}
#endif /* 0 */
/* If this is a memory ref that contains aggregate
components, mark it as such for cse and loop optimize. */
- MEM_IN_STRUCT_P (stack_parm) = aggregate;
+ MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
}
else if (PARM_BOUNDARY % BITS_PER_WORD != 0)
{
DECL_RTL (parm)
= gen_rtx_MEM (TYPE_MODE (TREE_TYPE (passed_type)), parmreg);
- MEM_IN_STRUCT_P (DECL_RTL (parm)) = aggregate;
+ MEM_SET_IN_STRUCT_P (DECL_RTL (parm), aggregate);
}
else
DECL_RTL (parm) = parmreg;
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
NOMINAL_MODE, if different. However, PARMREG may be in
push_to_sequence (conversion_insns);
tempreg = convert_to_mode (nominal_mode, tempreg, unsignedp);
+ /* TREE_USED gets set erroneously during expand_assignment. */
+ save_tree_used = TREE_USED (parm);
expand_assignment (parm,
make_tree (nominal_type, tempreg), 0, 0);
+ TREE_USED (parm) = save_tree_used;
conversion_insns = get_insns ();
did_conversion = 1;
end_sequence ();
else
copy = assign_stack_temp (TYPE_MODE (type),
int_size_in_bytes (type), 1);
- MEM_IN_STRUCT_P (copy) = AGGREGATE_TYPE_P (type);
+ MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
RTX_UNCHANGING_P (copy) = TREE_READONLY (parm);
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,
- XEXP (copy, 0), ptr_mode,
+ XEXP (copy, 0), Pmode,
GEN_INT (int_size_in_bytes (type)),
TYPE_MODE (sizetype),
GEN_INT (MEMORY_USE_RW),
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_IN_STRUCT_P (stack_parm) = aggregate;
+ MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
}
if (promoted_mode != nominal_mode)
{
push_to_sequence (conversion_insns);
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
- XEXP (stack_parm, 0), ptr_mode,
+ XEXP (stack_parm, 0), Pmode,
GEN_INT (GET_MODE_SIZE (GET_MODE
(entry_parm))),
TYPE_MODE (sizetype),
DECL_RTL (result)
= gen_rtx_MEM (DECL_MODE (result), DECL_RTL (parm));
- MEM_IN_STRUCT_P (DECL_RTL (result)) = AGGREGATE_TYPE_P (restype);
+ MEM_SET_IN_STRUCT_P (DECL_RTL (result),
+ AGGREGATE_TYPE_P (restype));
}
if (TREE_THIS_VOLATILE (parm))
enum machine_mode passed_mode;
tree type;
int in_regs;
- tree fndecl;
+ tree fndecl ATTRIBUTE_UNUSED;
struct args_size *initial_offset_ptr;
struct args_size *offset_ptr;
struct args_size *arg_size_ptr;
return n_blocks;
}
\f
-/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node)
- and initialize static variables for generating RTL for the statements
- of the function. */
-
-void
-init_function_start (subr, filename, line)
- tree subr;
- char *filename;
- int line;
+/* Allocate a function structure and reset its contents to the defaults. */
+static void
+prepare_function_start ()
{
+ current_function = (struct function *) xcalloc (1, sizeof (struct function));
+
init_stmt_for_function ();
cse_not_expected = ! optimize;
/* No stack slots have been made yet. */
stack_slot_list = 0;
+ current_function_has_nonlocal_label = 0;
+ current_function_has_nonlocal_goto = 0;
+
/* There is no stack slot for handling nonlocal gotos. */
- nonlocal_goto_handler_slot = 0;
+ nonlocal_goto_handler_slots = 0;
nonlocal_goto_stack_level = 0;
/* No labels have been declared for nonlocal use. */
nonlocal_labels = 0;
+ nonlocal_goto_handler_labels = 0;
/* No function calls so far in this function. */
function_call_count = 0;
/* 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;
init_const_rtx_hash_table ();
- current_function_name = (*decl_printable_name) (subr, 2);
-
- /* Nonzero if this is a nested function that uses a static chain. */
-
- current_function_needs_context
- = (decl_function_context (current_function_decl) != 0
- && ! DECL_NO_STATIC_CHAIN (current_function_decl));
-
/* Set if a call to setjmp is seen. */
current_function_calls_setjmp = 0;
current_function_calls_longjmp = 0;
current_function_calls_alloca = 0;
- current_function_has_nonlocal_label = 0;
- current_function_has_nonlocal_goto = 0;
current_function_contains_functions = 0;
+ current_function_is_leaf = 0;
current_function_sp_is_unchanging = 0;
+ current_function_uses_only_leaf_regs = 0;
+ current_function_has_computed_jump = 0;
current_function_is_thunk = 0;
current_function_returns_pcc_struct = 0;
tail_recursion_label = 0;
/* We haven't had a need to make a save area for ap yet. */
-
arg_pointer_save_area = 0;
/* No stack slots allocated yet. */
/* Set up to allocate temporaries. */
init_temp_slots ();
- /* Within function body, compute a type's size as soon it is laid out. */
- immediate_size_expand++;
+ /* Indicate that we need to distinguish between the return value of the
+ present function and the return value of a function being called. */
+ rtx_equal_function_value_matters = 1;
+
+ /* Indicate that we have not instantiated virtual registers yet. */
+ virtuals_instantiated = 0;
+
+ /* Indicate we have no need of a frame pointer yet. */
+ frame_pointer_needed = 0;
+
+ /* By default assume not varargs or stdarg. */
+ current_function_varargs = 0;
+ current_function_stdarg = 0;
/* We haven't made any trampolines for this function yet. */
trampoline_list = 0;
inhibit_defer_pop = 0;
current_function_outgoing_args_size = 0;
+}
+
+/* Initialize the rtl expansion mechanism so that we can do simple things
+ like generate sequences. This is used to provide a context during global
+ initialization of some passes. */
+void
+init_dummy_function_start ()
+{
+ prepare_function_start ();
+}
+
+/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node)
+ and initialize static variables for generating RTL for the statements
+ of the function. */
+
+void
+init_function_start (subr, filename, line)
+ tree subr;
+ char *filename;
+ int line;
+{
+ prepare_function_start ();
+
+ /* Remember this function for later. */
+ current_function->next_global = all_functions;
+ all_functions = current_function;
+
+ current_function_name = (*decl_printable_name) (subr, 2);
+
+ /* Nonzero if this is a nested function that uses a static chain. */
+
+ current_function_needs_context
+ = (decl_function_context (current_function_decl) != 0
+ && ! DECL_NO_STATIC_CHAIN (current_function_decl));
+
+ /* Within function body, compute a type's size as soon it is laid out. */
+ immediate_size_expand++;
/* Prevent ever trying to delete the first instruction of a function.
Also tell final how to output a linenum before the function prologue.
current_function_returns_pointer
= POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (subr)));
-
- /* Indicate that we need to distinguish between the return value of the
- present function and the return value of a function being called. */
- rtx_equal_function_value_matters = 1;
-
- /* Indicate that we have not instantiated virtual registers yet. */
- virtuals_instantiated = 0;
-
- /* Indicate we have no need of a frame pointer yet. */
- frame_pointer_needed = 0;
-
- /* By default assume not varargs or stdarg. */
- current_function_varargs = 0;
- current_function_stdarg = 0;
}
/* Indicate that the current function uses extra args
{
DECL_RTL (DECL_RESULT (subr))
= gen_rtx_MEM (DECL_MODE (DECL_RESULT (subr)), value_address);
- MEM_IN_STRUCT_P (DECL_RTL (DECL_RESULT (subr)))
- = AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr)));
+ MEM_SET_IN_STRUCT_P (DECL_RTL (DECL_RESULT (subr)),
+ AGGREGATE_TYPE_P (TREE_TYPE
+ (DECL_RESULT
+ (subr))));
}
}
else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
/* Save the argument pointer if a save area was made for it. */
if (arg_pointer_save_area)
{
- rtx x = gen_move_insn (arg_pointer_save_area, virtual_incoming_args_rtx);
- emit_insn_before (x, tail_recursion_reentry);
+ /* arg_pointer_save_area may not be a valid memory address, so we
+ have to check it and fix it if necessary. */
+ rtx seq;
+ start_sequence ();
+ emit_move_insn (validize_mem (arg_pointer_save_area),
+ virtual_incoming_args_rtx);
+ seq = gen_sequence ();
+ end_sequence ();
+ emit_insn_before (seq, tail_recursion_reentry);
}
/* Initialize any trampolines required by this function. */
}
/* Delete handlers for nonlocal gotos if nothing uses them. */
- if (nonlocal_goto_handler_slot != 0 && !current_function_has_nonlocal_label)
+ if (nonlocal_goto_handler_slots != 0
+ && ! current_function_has_nonlocal_label)
delete_handlers ();
/* End any sequences that failed to be closed due to syntax errors. */
thread_prologue_and_epilogue_insns (f)
rtx f ATTRIBUTE_UNUSED;
{
+ int insertted = 0;
+
+ prologue = 0;
#ifdef HAVE_prologue
if (HAVE_prologue)
{
- rtx head, seq;
-
- /* The first insn (a NOTE_INSN_DELETED) is followed by zero or more
- prologue insns and a NOTE_INSN_PROLOGUE_END. */
- emit_note_after (NOTE_INSN_PROLOGUE_END, f);
- seq = gen_prologue ();
- head = emit_insn_after (seq, f);
+ rtx seq;
- /* Include the new prologue insns in the first block. Ignore them
- if they form a basic block unto themselves. */
- if (basic_block_head && n_basic_blocks
- && GET_CODE (basic_block_head[0]) != CODE_LABEL)
- basic_block_head[0] = NEXT_INSN (f);
+ start_sequence ();
+ seq = gen_prologue();
+ emit_insn (seq);
/* Retain a map of the prologue insns. */
- prologue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : head);
+ if (GET_CODE (seq) != SEQUENCE)
+ seq = get_insns ();
+ prologue = record_insns (seq);
+
+ emit_note (NULL, NOTE_INSN_PROLOGUE_END);
+ seq = gen_sequence ();
+ end_sequence ();
+
+ /* If optimization is off, and perhaps in an empty function,
+ the entry block will have no successors. */
+ if (ENTRY_BLOCK_PTR->succ)
+ {
+ /* Can't deal with multiple successsors of the entry block. */
+ if (ENTRY_BLOCK_PTR->succ->succ_next)
+ abort ();
+
+ insert_insn_on_edge (seq, ENTRY_BLOCK_PTR->succ);
+ insertted = 1;
+ }
+ else
+ emit_insn_after (seq, f);
}
- else
#endif
- prologue = 0;
+ epilogue = 0;
#ifdef HAVE_epilogue
if (HAVE_epilogue)
{
- rtx insn = get_last_insn ();
- rtx prev = prev_nonnote_insn (insn);
+ edge e;
+ basic_block bb = 0;
+ rtx tail = get_last_insn ();
+
+ /* ??? This is gastly. If function returns were not done via uses,
+ but via mark_regs_live_at_end, we could use insert_insn_on_edge
+ and all of this uglyness would go away. */
- /* If we end with a BARRIER, we don't need an epilogue. */
- if (! (prev && GET_CODE (prev) == BARRIER))
+ switch (optimize)
{
- rtx tail, seq, tem;
- rtx first_use = 0;
- rtx last_use = 0;
-
- /* The last basic block ends with a NOTE_INSN_EPILOGUE_BEG, the
- epilogue insns, the USE insns at the end of a function,
- the jump insn that returns, and then a BARRIER. */
-
- /* Move the USE insns at the end of a function onto a list. */
- while (prev
- && GET_CODE (prev) == INSN
- && GET_CODE (PATTERN (prev)) == USE)
- {
- tem = prev;
+ default:
+ /* If the exit block has no non-fake predecessors, we don't
+ need an epilogue. Furthermore, only pay attention to the
+ fallthru predecessors; if (conditional) return insns were
+ generated, by definition we do not need to emit epilogue
+ insns. */
+
+ for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
+ if ((e->flags & EDGE_FAKE) == 0
+ && (e->flags & EDGE_FALLTHRU) != 0)
+ break;
+ if (e == NULL)
+ break;
+
+ /* We can't handle multiple epilogues -- if one is needed,
+ we won't be able to place it multiple times.
+
+ ??? Fix epilogue expanders to not assume they are the
+ last thing done compiling the function. Either that
+ or copy_rtx each insn.
+
+ ??? Blah, it's not a simple expression to assert that
+ we've exactly one fallthru exit edge. */
+
+ bb = e->src;
+ tail = bb->end;
+
+ /* ??? If the last insn of the basic block is a jump, then we
+ are creating a new basic block. Wimp out and leave these
+ insns outside any block. */
+ if (GET_CODE (tail) == JUMP_INSN)
+ bb = 0;
+
+ /* FALLTHRU */
+ case 0:
+ {
+ rtx prev, seq, first_use;
+
+ /* Move the USE insns at the end of a function onto a list. */
+ prev = tail;
+ if (GET_CODE (prev) == BARRIER
+ || GET_CODE (prev) == NOTE)
prev = prev_nonnote_insn (prev);
- NEXT_INSN (PREV_INSN (tem)) = NEXT_INSN (tem);
- PREV_INSN (NEXT_INSN (tem)) = PREV_INSN (tem);
- if (first_use)
- {
- NEXT_INSN (tem) = first_use;
- PREV_INSN (first_use) = tem;
- }
- first_use = tem;
- if (!last_use)
- last_use = tem;
- }
+ first_use = 0;
+ if (prev
+ && GET_CODE (prev) == INSN
+ && GET_CODE (PATTERN (prev)) == USE)
+ {
+ /* If the end of the block is the use, grab hold of something
+ else so that we emit barriers etc in the right place. */
+ if (prev == tail)
+ {
+ do
+ tail = PREV_INSN (tail);
+ while (GET_CODE (tail) == INSN
+ && GET_CODE (PATTERN (tail)) == USE);
+ }
+
+ do
+ {
+ rtx use = prev;
+ prev = prev_nonnote_insn (prev);
+
+ remove_insn (use);
+ if (first_use)
+ {
+ NEXT_INSN (use) = first_use;
+ PREV_INSN (first_use) = use;
+ }
+ else
+ NEXT_INSN (use) = NULL_RTX;
+ first_use = use;
+ }
+ while (prev
+ && GET_CODE (prev) == INSN
+ && GET_CODE (PATTERN (prev)) == USE);
+ }
- emit_barrier_after (insn);
+ /* The last basic block ends with a NOTE_INSN_EPILOGUE_BEG, the
+ epilogue insns, the USE insns at the end of a function,
+ the jump insn that returns, and then a BARRIER. */
- seq = gen_epilogue ();
- tail = emit_jump_insn_after (seq, insn);
+ if (GET_CODE (tail) != BARRIER)
+ {
+ prev = next_nonnote_insn (tail);
+ if (!prev || GET_CODE (prev) != BARRIER)
+ emit_barrier_after (tail);
+ }
- /* Insert the USE insns immediately before the return insn, which
- must be the first instruction before the final barrier. */
- if (first_use)
- {
- tem = prev_nonnote_insn (get_last_insn ());
- NEXT_INSN (PREV_INSN (tem)) = first_use;
- PREV_INSN (first_use) = PREV_INSN (tem);
- PREV_INSN (tem) = last_use;
- NEXT_INSN (last_use) = tem;
- }
+ seq = gen_epilogue ();
+ prev = tail;
+ tail = emit_jump_insn_after (seq, tail);
- emit_note_after (NOTE_INSN_EPILOGUE_BEG, insn);
+ /* Insert the USE insns immediately before the return insn, which
+ must be the last instruction emitted in the sequence. */
+ if (first_use)
+ emit_insns_before (first_use, tail);
+ emit_note_after (NOTE_INSN_EPILOGUE_BEG, prev);
- /* Include the new epilogue insns in the last block. Ignore
- them if they form a basic block unto themselves. */
- if (basic_block_end && n_basic_blocks
- && GET_CODE (basic_block_end[n_basic_blocks - 1]) != JUMP_INSN)
- basic_block_end[n_basic_blocks - 1] = tail;
+ /* Update the tail of the basic block. */
+ if (bb)
+ bb->end = tail;
- /* Retain a map of the epilogue insns. */
- epilogue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : tail);
- return;
+ /* Retain a map of the epilogue insns. */
+ epilogue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : tail);
+ }
}
}
#endif
- epilogue = 0;
+
+ if (insertted)
+ commit_edge_insertions ();
}
/* Reposition the prologue-end and epilogue-begin notes after instruction
/* Reposition the prologue and epilogue notes. */
if (n_basic_blocks)
{
- rtx next, prev;
int len;
if (prologue)
}
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)
}
next = NEXT_INSN (note);
- prev = PREV_INSN (note);
- if (prev)
- NEXT_INSN (prev) = next;
- if (next)
- PREV_INSN (next) = prev;
- /* Whether or not we can depend on basic_block_head,
+ /* Whether or not we can depend on BLOCK_HEAD,
attempt to keep it up-to-date. */
- if (basic_block_head[0] == note)
- basic_block_head[0] = next;
+ if (BLOCK_HEAD (0) == note)
+ BLOCK_HEAD (0) = next;
+ remove_insn (note);
add_insn_after (note, insn);
}
}
&& NOTE_LINE_NUMBER (note) == NOTE_INSN_EPILOGUE_BEG)
break;
}
- next = NEXT_INSN (note);
- prev = PREV_INSN (note);
- if (prev)
- NEXT_INSN (prev) = next;
- if (next)
- PREV_INSN (next) = prev;
- /* Whether or not we can depend on basic_block_head,
+ /* Whether or not we can depend on BLOCK_HEAD,
attempt to keep it up-to-date. */
if (n_basic_blocks
- && basic_block_head[n_basic_blocks-1] == insn)
- basic_block_head[n_basic_blocks-1] = note;
+ && BLOCK_HEAD (n_basic_blocks-1) == insn)
+ BLOCK_HEAD (n_basic_blocks-1) = note;
+ remove_insn (note);
add_insn_before (note, insn);
}
}