/* Expands front end tree to back end RTL for GCC.
Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
- 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GCC.
static int all_blocks (tree, tree *);
static tree *get_block_vector (tree, int *);
extern tree debug_find_var_in_block_tree (tree, tree);
-/* We always define `record_insns' even if its not used so that we
+/* We always define `record_insns' even if it's not used so that we
can always export `prologue_epilogue_contains'. */
static void record_insns (rtx, varray_type *) ATTRIBUTE_UNUSED;
static int contains (rtx, varray_type);
static void set_insn_locators (rtx, int) ATTRIBUTE_UNUSED;
\f
/* Pointer to chain of `struct function' for containing functions. */
-static GTY(()) struct function *outer_function_chain;
+struct function *outer_function_chain;
/* List of insns that were postponed by purge_addressof_1. */
static rtx postponed_insns;
outer_function_chain = p;
p->fixup_var_refs_queue = 0;
- (*lang_hooks.function.enter_nested) (p);
+ lang_hooks.function.enter_nested (p);
cfun = 0;
}
restore_emit_status (p);
- (*lang_hooks.function.leave_nested) (p);
+ lang_hooks.function.leave_nested (p);
/* Finish doing put_var_into_stack for any of our variables which became
addressable during the nested function. If only one entry has to be
/* f->varasm is used by code generation. */
/* f->eh->eh_return_stub_label is used by code generation. */
- (*lang_hooks.function.final) (f);
+ lang_hooks.function.final (f);
f->stmt = NULL;
}
f->x_nonlocal_goto_stack_level = NULL;
f->x_cleanup_label = NULL;
f->x_return_label = NULL;
+ f->x_naked_return_label = NULL;
f->computed_goto_common_label = NULL;
f->computed_goto_common_reg = NULL;
f->x_save_expr_regs = NULL;
/* Allow the target to (possibly) increase the alignment of this
stack slot. */
- type = (*lang_hooks.types.type_for_mode) (mode, 0);
+ type = lang_hooks.types.type_for_mode (mode, 0);
if (type)
alignment = LOCAL_ALIGNMENT (type, alignment);
align = GET_MODE_ALIGNMENT (mode);
if (! type)
- type = (*lang_hooks.types.type_for_mode) (mode, 0);
+ type = lang_hooks.types.type_for_mode (mode, 0);
if (type)
align = LOCAL_ALIGNMENT (type, align);
if (function->decl == context)
break;
- /* 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 this is a variable-sized object or a structure passed by invisible
+ reference, with a pseudo to address it, put that pseudo into the stack
+ if the var is non-local. */
if (TREE_CODE (decl) != SAVE_EXPR && DECL_NONLOCAL (decl)
&& GET_CODE (reg) == MEM
&& GET_CODE (XEXP (reg, 0)) == REG
decl_mode = promoted_mode = GET_MODE (reg);
}
+ /* If this variable lives in the current function and we don't need to put it
+ in the stack for the sake of setjmp or the non-locality, try to keep it in
+ a register until we know we actually need the address. */
can_use_addressof
= (function == 0
+ && ! (TREE_CODE (decl) != SAVE_EXPR && DECL_NONLOCAL (decl))
&& optimize > 0
/* FIXME make it work for promoted modes too */
&& decl_mode == promoted_mode
if (GET_CODE (reg) == REG)
{
- /* If this variable lives in the current function and we don't need
- to put things in the stack for the sake of setjmp, try to keep it
- in a register until we know we actually need the address. */
if (can_use_addressof)
gen_mem_addressof (reg, decl, rescan);
else
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 = (*lang_hooks.types.type_for_mode) (part_mode, 0);
+ tree part_type = lang_hooks.types.type_for_mode (part_mode, 0);
rtx lopart = XEXP (reg, 0);
rtx hipart = XEXP (reg, 1);
#ifdef FRAME_GROWS_DOWNWARD
static void
put_reg_into_stack (struct function *function, rtx reg, tree type,
- enum machine_mode promoted_mode, enum machine_mode decl_mode,
- int volatile_p, unsigned int original_regno, int used_p, htab_t ht)
+ enum machine_mode promoted_mode,
+ enum machine_mode decl_mode, int volatile_p,
+ unsigned int original_regno, int used_p, htab_t ht)
{
struct function *func = function ? function : cfun;
rtx new = 0;
regno = REGNO (reg);
if (regno < func->x_max_parm_reg)
- new = func->x_parm_reg_stack_loc[regno];
+ {
+ if (!func->x_parm_reg_stack_loc)
+ abort ();
+ 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);
rtx first_insn = get_insns ();
struct sequence_stack *stack = seq_stack;
tree rtl_exps = rtl_expr_chain;
+ int save_volatile_ok = volatile_ok;
/* If there's a hash table, it must record all uses of VAR. */
if (ht)
return;
}
+ /* Volatile is valid in MEMs because all we're doing in changing the
+ address inside. */
+ volatile_ok = 1;
fixup_var_refs_insns (first_insn, var, promoted_mode, unsignedp,
stack == 0, may_share);
end_sequence ();
}
}
+
+ volatile_ok = save_volatile_ok;
}
\f
/* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is
replacement = find_fixup_replacement (replacements, x);
if (replacement->new)
{
+ enum machine_mode mode = GET_MODE (x);
*loc = replacement->new;
+
+ /* Careful! We may have just replaced a SUBREG by a MEM, which
+ means that the insn may have become invalid again. We can't
+ in this case make a new replacement since we already have one
+ and we must deal with MATCH_DUPs. */
+ if (GET_CODE (replacement->new) == MEM)
+ {
+ INSN_CODE (insn) = -1;
+ if (recog_memoized (insn) >= 0)
+ return;
+
+ fixup_var_refs_1 (replacement->new, mode, &PATTERN (insn),
+ insn, replacements, no_share);
+ }
+
return;
}
fixup_var_refs (reg, GET_MODE (reg), TREE_UNSIGNED (type), reg, 0);
}
else if (rescan)
- fixup_var_refs (reg, GET_MODE (reg), 0, reg, 0);
+ {
+ /* This can only happen during reload. Clear the same flag bits as
+ reload. */
+ MEM_VOLATILE_P (reg) = 0;
+ RTX_UNCHANGING_P (reg) = 0;
+ MEM_IN_STRUCT_P (reg) = 0;
+ MEM_SCALAR_P (reg) = 0;
+ MEM_ATTRS (reg) = 0;
+
+ fixup_var_refs (reg, GET_MODE (reg), 0, reg, 0);
+ }
return reg;
}
int i, j;
const char *fmt;
bool result = true;
+ bool libcall = false;
/* Re-start here to avoid recursion in common cases. */
restart:
if (x == 0)
return true;
+ /* Is this a libcall? */
+ if (!insn)
+ libcall = REG_NOTE_KIND (*loc) == REG_RETVAL;
+
code = GET_CODE (x);
/* If we don't return in any of the cases below, we will recurse inside
which can be succinctly described with a simple SUBREG.
Note that removing the REG_EQUAL note is not an option
on the last insn of a libcall, so we must do a replacement. */
- if (! purge_addressof_replacements
- && ! purge_bitfield_addressof_replacements)
+
+ /* In compile/990107-1.c:7 compiled at -O1 -m1 for sh-elf,
+ we got
+ (mem:DI (addressof:SI (reg/v:DF 160) 159 0x401c8510)
+ [0 S8 A32]), which can be expressed with a simple
+ same-size subreg */
+ if ((GET_MODE_SIZE (GET_MODE (x))
+ <= GET_MODE_SIZE (GET_MODE (sub)))
+ /* Again, invalid pointer casts (as in
+ compile/990203-1.c) can require paradoxical
+ subregs. */
+ || (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD
+ && (GET_MODE_SIZE (GET_MODE (x))
+ > GET_MODE_SIZE (GET_MODE (sub)))
+ && libcall))
{
- /* In compile/990107-1.c:7 compiled at -O1 -m1 for sh-elf,
- we got
- (mem:DI (addressof:SI (reg/v:DF 160) 159 0x401c8510)
- [0 S8 A32]), which can be expressed with a simple
- same-size subreg */
- if ((GET_MODE_SIZE (GET_MODE (x))
- == GET_MODE_SIZE (GET_MODE (sub)))
- /* Again, invalid pointer casts (as in
- compile/990203-1.c) can require paradoxical
- subregs. */
- || (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD
- && (GET_MODE_SIZE (GET_MODE (x))
- > GET_MODE_SIZE (GET_MODE (sub)))))
- {
- *loc = gen_rtx_SUBREG (GET_MODE (x), sub, 0);
- return true;
- }
- /* ??? Are there other cases we should handle? */
+ *loc = gen_rtx_SUBREG (GET_MODE (x), sub, 0);
+ return true;
}
+ /* ??? Are there other cases we should handle? */
+
/* Sometimes we may not be able to find the replacement. For
example when the original insn was a MEM in a wider mode,
and the note is part of a sign extension of a narrowed
return true;
}
purge_addressof_replacements
- = gen_rtx (EXPR_LIST, VOIDmode, XEXP (x, 0),
- gen_rtx_EXPR_LIST (VOIDmode, sub,
- purge_addressof_replacements));
+ = gen_rtx_EXPR_LIST (VOIDmode, XEXP (x, 0),
+ gen_rtx_EXPR_LIST (VOIDmode, sub,
+ purge_addressof_replacements));
return true;
}
goto restart;
return 0;
regno = REGNO (reg);
- nregs = HARD_REGNO_NREGS (regno, TYPE_MODE (type));
+ nregs = hard_regno_nregs[regno][TYPE_MODE (type)];
for (i = 0; i < nregs; i++)
if (! call_used_regs[regno + i])
return 1;
/* Total space needed so far for args on the stack,
given as a constant and a tree-expression. */
struct args_size stack_args_size;
+ HOST_WIDE_INT extra_pretend_bytes = 0;
tree fntype = TREE_TYPE (fndecl);
tree fnargs = DECL_ARGUMENTS (fndecl), orig_fnargs;
/* This is used for the arg pointer when referring to stack args. */
the function returns a structure. */
tree function_result_decl = 0;
int varargs_setup = 0;
- int reg_parm_stack_space = 0;
+ int reg_parm_stack_space ATTRIBUTE_UNUSED = 0;
rtx conversion_insns = 0;
/* Nonzero if function takes extra anonymous args.
max_parm_reg = LAST_VIRTUAL_REGISTER + 1;
parm_reg_stack_loc = ggc_alloc_cleared (max_parm_reg * sizeof (rtx));
- if (SPLIT_COMPLEX_ARGS)
+ /* If the target wants to split complex arguments into scalars, do so. */
+ if (targetm.calls.split_complex_arg)
fnargs = split_complex_args (fnargs);
#ifdef REG_PARM_STACK_SPACE
-#ifdef MAYBE_REG_PARM_STACK_SPACE
- reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
-#else
reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
#endif
-#endif
#ifdef INIT_CUMULATIVE_INCOMING_ARGS
INIT_CUMULATIVE_INCOMING_ARGS (args_so_far, fntype, NULL_RTX);
#else
- INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, fndecl);
+ INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, fndecl, -1);
#endif
/* We haven't yet found an argument that we must push and pretend the
int in_regs;
int partial = 0;
int pretend_bytes = 0;
+ int loaded_in_reg = 0;
/* Set LAST_NAMED if this is last named arg before last
anonymous args. */
/* Set NAMED_ARG if this arg should be treated as a named arg. For
most machines, if this is a varargs/stdarg function, then we treat
the last named arg as if it were anonymous too. */
- named_arg = targetm.calls.strict_argument_naming (&args_so_far) ? 1 : ! last_named;
+ named_arg = (targetm.calls.strict_argument_naming (&args_so_far)
+ ? 1 : !last_named);
if (TREE_TYPE (parm) == error_mark_node
/* This can happen after weird syntax errors
partial = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, promoted_mode,
passed_type, named_arg);
if (partial
-#ifndef MAYBE_REG_PARM_STACK_SPACE
/* The caller might already have allocated stack space
for the register parameters. */
- && reg_parm_stack_space == 0
-#endif
- )
+ && reg_parm_stack_space == 0)
{
/* Part of this argument is passed in registers and part
is passed on the stack. Ask the prologue code to extend
Internally, gcc assumes that the argument pointer is
aligned to STACK_BOUNDARY bits. This is used both for
- alignment optimisations (see init_emit) and to locate
+ alignment optimizations (see init_emit) and to locate
arguments that are aligned to more than PARM_BOUNDARY
bits. We must preserve this invariant by rounding
CURRENT_FUNCTION_PRETEND_ARGS_SIZE up to a stack
boundary. */
+
+ /* We assume at most one partial arg, and it must be the first
+ argument on the stack. */
+ if (extra_pretend_bytes || current_function_pretend_args_size)
+ abort ();
+
pretend_bytes = partial * UNITS_PER_WORD;
current_function_pretend_args_size
= CEIL_ROUND (pretend_bytes, STACK_BYTES);
- /* If PRETEND_BYTES != CURRENT_FUNCTION_PRETEND_ARGS_SIZE,
- insert the padding before the start of the first pretend
- argument. */
- stack_args_size.constant
- = (current_function_pretend_args_size - pretend_bytes);
+ /* We want to align relative to the actual stack pointer, so
+ don't include this in the stack size until later. */
+ extra_pretend_bytes = current_function_pretend_args_size;
}
}
#endif
locate_and_pad_parm (promoted_mode, passed_type, in_regs,
entry_parm ? partial : 0, fndecl,
&stack_args_size, &locate);
+ /* Adjust offsets to include the pretend args. */
+ locate.slot_offset.constant += extra_pretend_bytes - pretend_bytes;
+ locate.offset.constant += extra_pretend_bytes - pretend_bytes;
{
rtx offset_rtx;
entry_parm = stack_parm;
/* Record permanently how this parm was passed. */
- DECL_INCOMING_RTL (parm) = entry_parm;
+ set_decl_incoming_rtl (parm, entry_parm);
/* If there is actually space on the stack for this parm,
count it in stack_args_size; otherwise set stack_parm to 0
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)
+#if defined (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.
-
- ??? When MAYBE_REG_PARM_STACK_SPACE is defined, we can't tell
- whether this parameter already has a stack slot allocated,
- because an arg block exists only if current_function_args_size
- is larger than some threshold, and we haven't calculated that
- yet. So, for now, we just assume that stack slots never exist
- in this case. */
+ there is still an (uninitialized) stack slot allocated
+ for it. */
|| REG_PARM_STACK_SPACE (fndecl) > 0
#endif
)
{
- stack_args_size.constant += pretend_bytes + locate.size.constant;
+ stack_args_size.constant += locate.size.constant;
if (locate.size.var)
ADD_PARM_SIZE (stack_args_size, locate.size.var);
}
&& INTVAL (XEXP (XVECEXP (entry_parm, 0, i), 1)) == 0)
{
entry_parm = XEXP (XVECEXP (entry_parm, 0, i), 0);
- DECL_INCOMING_RTL (parm) = entry_parm;
+ set_decl_incoming_rtl (parm, entry_parm);
break;
}
}
Set DECL_RTL to that place. */
+ if (GET_CODE (entry_parm) == PARALLEL && nominal_mode != BLKmode
+ && XVECLEN (entry_parm, 0) > 1)
+ {
+ /* Reconstitute objects the size of a register or larger using
+ register operations instead of the stack. */
+ rtx parmreg = gen_reg_rtx (nominal_mode);
+
+ if (REG_P (parmreg))
+ {
+ unsigned int regno = REGNO (parmreg);
+
+ emit_group_store (parmreg, entry_parm, TREE_TYPE (parm),
+ int_size_in_bytes (TREE_TYPE (parm)));
+ SET_DECL_RTL (parm, parmreg);
+ loaded_in_reg = 1;
+
+ if (regno >= max_parm_reg)
+ {
+ rtx *new;
+ int old_max_parm_reg = max_parm_reg;
+
+ /* It's slow to expand this one register at a time,
+ but it's also rare and we need max_parm_reg to be
+ precisely correct. */
+ max_parm_reg = regno + 1;
+ new = ggc_realloc (parm_reg_stack_loc,
+ max_parm_reg * sizeof (rtx));
+ memset (new + old_max_parm_reg, 0,
+ (max_parm_reg - old_max_parm_reg) * sizeof (rtx));
+ parm_reg_stack_loc = new;
+ parm_reg_stack_loc[regno] = stack_parm;
+ }
+ }
+ }
+
if (nominal_mode == BLKmode
#ifdef BLOCK_REG_PADDING
|| (locate.where_pad == (BYTES_BIG_ENDIAN ? upward : downward)
Handle calls that pass values in multiple non-contiguous
locations. The Irix 6 ABI has examples of this. */
if (GET_CODE (entry_parm) == REG
- || GET_CODE (entry_parm) == PARALLEL)
+ || (GET_CODE (entry_parm) == PARALLEL
+ && (!loaded_in_reg || !optimize)))
{
int size = int_size_in_bytes (TREE_TYPE (parm));
int size_stored = CEIL_ROUND (size, UNITS_PER_WORD);
assign_stack_local if space was not allocated in the argument
list. If it was, this will not work if PARM_BOUNDARY is not
a multiple of BITS_PER_WORD. It isn't clear how to fix this
- if it becomes a problem. */
+ if it becomes a problem. Exception is when BLKmode arrives
+ with arguments not conforming to word_mode. */
if (stack_parm == 0)
{
PUT_MODE (stack_parm, GET_MODE (entry_parm));
set_mem_attributes (stack_parm, parm, 1);
}
-
+ else if (GET_CODE (entry_parm) == PARALLEL
+ && GET_MODE(entry_parm) == BLKmode)
+ ;
else if (PARM_BOUNDARY % BITS_PER_WORD != 0)
abort ();
move_block_from_reg (REGNO (entry_parm), mem,
size_stored / UNITS_PER_WORD);
}
- SET_DECL_RTL (parm, stack_parm);
+ /* If parm is already bound to register pair, don't change
+ this binding. */
+ if (! DECL_RTL_SET_P (parm))
+ SET_DECL_RTL (parm, stack_parm);
}
else if (! ((! optimize
&& ! DECL_REGISTER (parm))
else if (passed_pointer
&& FUNCTION_ARG_CALLEE_COPIES (args_so_far,
- TYPE_MODE (DECL_ARG_TYPE (parm)),
- DECL_ARG_TYPE (parm),
+ TYPE_MODE (TREE_TYPE (passed_type)),
+ TREE_TYPE (passed_type),
named_arg)
- && ! TREE_ADDRESSABLE (DECL_ARG_TYPE (parm)))
+ && ! TREE_ADDRESSABLE (TREE_TYPE (passed_type)))
{
rtx copy;
- tree type = DECL_ARG_TYPE (parm);
+ tree type = TREE_TYPE (passed_type);
/* This sequence may involve a library call perhaps clobbering
registers that haven't been copied to pseudos yet. */
}
}
- if (SPLIT_COMPLEX_ARGS && fnargs != orig_fnargs)
+ if (targetm.calls.split_complex_arg && fnargs != orig_fnargs)
{
for (parm = orig_fnargs; parm; parm = TREE_CHAIN (parm))
{
- if (TREE_CODE (TREE_TYPE (parm)) == COMPLEX_TYPE)
+ if (TREE_CODE (TREE_TYPE (parm)) == COMPLEX_TYPE
+ && targetm.calls.split_complex_arg (TREE_TYPE (parm)))
{
- SET_DECL_RTL (parm,
- gen_rtx_CONCAT (DECL_MODE (parm),
- DECL_RTL (fnargs),
- DECL_RTL (TREE_CHAIN (fnargs))));
- DECL_INCOMING_RTL (parm)
- = gen_rtx_CONCAT (DECL_MODE (parm),
- DECL_INCOMING_RTL (fnargs),
- DECL_INCOMING_RTL (TREE_CHAIN (fnargs)));
+ rtx tmp, real, imag;
+ enum machine_mode inner = GET_MODE_INNER (DECL_MODE (parm));
+
+ real = DECL_RTL (fnargs);
+ imag = DECL_RTL (TREE_CHAIN (fnargs));
+ if (inner != GET_MODE (real))
+ {
+ real = gen_lowpart_SUBREG (inner, real);
+ imag = gen_lowpart_SUBREG (inner, imag);
+ }
+ tmp = gen_rtx_CONCAT (DECL_MODE (parm), real, imag);
+ SET_DECL_RTL (parm, tmp);
+
+ real = DECL_INCOMING_RTL (fnargs);
+ imag = DECL_INCOMING_RTL (TREE_CHAIN (fnargs));
+ if (inner != GET_MODE (real))
+ {
+ real = gen_lowpart_SUBREG (inner, real);
+ imag = gen_lowpart_SUBREG (inner, imag);
+ }
+ tmp = gen_rtx_CONCAT (DECL_MODE (parm), real, imag);
+ set_decl_incoming_rtl (parm, tmp);
fnargs = TREE_CHAIN (fnargs);
}
else
{
SET_DECL_RTL (parm, DECL_RTL (fnargs));
- DECL_INCOMING_RTL (parm) = DECL_INCOMING_RTL (fnargs);
+ set_decl_incoming_rtl (parm, DECL_INCOMING_RTL (fnargs));
}
fnargs = TREE_CHAIN (fnargs);
}
last_parm_insn = get_last_insn ();
+ /* We have aligned all the args, so add space for the pretend args. */
+ stack_args_size.constant += extra_pretend_bytes;
current_function_args_size = stack_args_size.constant;
/* Adjust function incoming argument size for alignment and
minimum length. */
#ifdef REG_PARM_STACK_SPACE
-#ifndef MAYBE_REG_PARM_STACK_SPACE
current_function_args_size = MAX (current_function_args_size,
REG_PARM_STACK_SPACE (fndecl));
#endif
-#endif
current_function_args_size
= ((current_function_args_size + STACK_BYTES - 1)
/* Before allocating memory, check for the common case of no complex. */
for (p = args; p; p = TREE_CHAIN (p))
- if (TREE_CODE (TREE_TYPE (p)) == COMPLEX_TYPE)
- goto found;
+ {
+ tree type = TREE_TYPE (p);
+ if (TREE_CODE (type) == COMPLEX_TYPE
+ && targetm.calls.split_complex_arg (type))
+ goto found;
+ }
return args;
found:
for (p = args; p; p = TREE_CHAIN (p))
{
tree type = TREE_TYPE (p);
- if (TREE_CODE (type) == COMPLEX_TYPE)
+ if (TREE_CODE (type) == COMPLEX_TYPE
+ && targetm.calls.split_complex_arg (type))
{
tree decl;
tree subtype = TREE_TYPE (type);
int part_size_in_regs;
#ifdef REG_PARM_STACK_SPACE
-#ifdef MAYBE_REG_PARM_STACK_SPACE
- reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
-#else
reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
-#endif
/* If we have found a stack parm before we reach the end of the
area reserved for registers, skip that area. */
{
tree save_var = NULL_TREE;
HOST_WIDE_INT save_constant = 0;
-
int boundary_in_bytes = boundary / BITS_PER_UNIT;
+ HOST_WIDE_INT sp_offset = STACK_POINTER_OFFSET;
+
+#ifdef SPARC_STACK_BOUNDARY_HACK
+ /* The sparc port has a bug. It sometimes claims a STACK_BOUNDARY
+ higher than the real alignment of %sp. However, when it does this,
+ the alignment of %sp+STACK_POINTER_OFFSET will be STACK_BOUNDARY.
+ This is a temporary hack while the sparc port is fixed. */
+ if (SPARC_STACK_BOUNDARY_HACK)
+ sp_offset = 0;
+#endif
if (boundary > PARM_BOUNDARY && boundary > STACK_BOUNDARY)
{
{
if (offset_ptr->var)
{
- offset_ptr->var =
+ tree sp_offset_tree = ssize_int (sp_offset);
+ tree offset = size_binop (PLUS_EXPR,
+ ARGS_SIZE_TREE (*offset_ptr),
+ sp_offset_tree);
#ifdef ARGS_GROW_DOWNWARD
- round_down
+ tree rounded = round_down (offset, boundary / BITS_PER_UNIT);
#else
- round_up
+ tree rounded = round_up (offset, boundary / BITS_PER_UNIT);
#endif
- (ARGS_SIZE_TREE (*offset_ptr),
- boundary / BITS_PER_UNIT);
+
+ offset_ptr->var = size_binop (MINUS_EXPR, rounded, sp_offset_tree);
/* ARGS_SIZE_TREE includes constant term. */
offset_ptr->constant = 0;
if (boundary > PARM_BOUNDARY && boundary > STACK_BOUNDARY)
}
else
{
- offset_ptr->constant =
+ offset_ptr->constant = -sp_offset +
#ifdef ARGS_GROW_DOWNWARD
- FLOOR_ROUND (offset_ptr->constant, boundary_in_bytes);
+ FLOOR_ROUND (offset_ptr->constant + sp_offset, boundary_in_bytes);
#else
- CEIL_ROUND (offset_ptr->constant, boundary_in_bytes);
+ CEIL_ROUND (offset_ptr->constant + sp_offset, boundary_in_bytes);
#endif
if (boundary > PARM_BOUNDARY && boundary > STACK_BOUNDARY)
alignment_pad->constant = offset_ptr->constant - save_constant;
flow.c that the entire aggregate was initialized.
Unions are troublesome because members may be shorter. */
&& ! AGGREGATE_TYPE_P (TREE_TYPE (decl))
- && DECL_RTL (decl) != 0
+ && DECL_RTL_SET_P (decl)
&& GET_CODE (DECL_RTL (decl)) == REG
/* Global optimizations can make it difficult to determine if a
particular variable has been initialized. However, a VAR_DECL
decl, decl);
if (extra_warnings
&& TREE_CODE (decl) == VAR_DECL
- && DECL_RTL (decl) != 0
+ && DECL_RTL_SET_P (decl)
&& GET_CODE (DECL_RTL (decl)) == REG
&& regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
warning ("%Jvariable '%D' might be clobbered by `longjmp' or `vfork'",
init_stmt_for_function ();
init_eh_for_function ();
- init_emit ();
- init_expr ();
- init_varasm_status (cfun);
- (*lang_hooks.function.init) (cfun);
+ lang_hooks.function.init (cfun);
if (init_machine_status)
cfun->machine = (*init_machine_status) ();
if (fndecl == NULL)
return;
- DECL_SAVED_INSNS (fndecl) = cfun;
+ DECL_STRUCT_FUNCTION (fndecl) = cfun;
cfun->decl = fndecl;
- current_function_name = (*lang_hooks.decl_printable_name) (fndecl, 2);
-
result = DECL_RESULT (fndecl);
if (aggregate_value_p (result, fndecl))
{
}
/* Reset cfun, and other non-struct-function variables to defaults as
- appropriate for emiiting rtl at the start of a function. */
+ appropriate for emitting rtl at the start of a function. */
static void
prepare_function_start (tree fndecl)
{
- if (fndecl && DECL_SAVED_INSNS (fndecl))
- cfun = DECL_SAVED_INSNS (fndecl);
+ if (fndecl && DECL_STRUCT_FUNCTION (fndecl))
+ cfun = DECL_STRUCT_FUNCTION (fndecl);
else
allocate_struct_function (fndecl);
+ init_emit ();
+ init_varasm_status (cfun);
+ init_expr ();
cse_not_expected = ! optimize;
tem = decl_function_context (tem);
if (tem == 0)
break;
- /* Chain thru stack frames, assuming pointer to next lexical frame
+ /* Chain through 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,
clear_pending_stack_adjust ();
do_pending_stack_adjust ();
+ /* @@@ This is a kludge. We want to ensure that instructions that
+ may trap are not moved into the epilogue by scheduling, because
+ we don't always emit unwind information for the epilogue.
+ However, not all machine descriptions define a blockage insn, so
+ emit an ASM_INPUT to act as one. */
+ if (flag_non_call_exceptions)
+ emit_insn (gen_rtx_ASM_INPUT (VOIDmode, ""));
+
/* Mark the end of the function body.
If control reaches this insn, the function can drop through
without returning a value. */
/* If we had calls to alloca, and this machine needs
an accurate stack pointer to exit the function,
insert some code to save and restore the stack pointer. */
-#ifdef EXIT_IGNORE_STACK
- if (! EXIT_IGNORE_STACK)
-#endif
- if (current_function_calls_alloca)
- {
- rtx tem = 0;
+ if (! EXIT_IGNORE_STACK
+ && current_function_calls_alloca)
+ {
+ rtx tem = 0;
- emit_stack_save (SAVE_FUNCTION, &tem, parm_birth_insn);
- emit_stack_restore (SAVE_FUNCTION, tem, NULL_RTX);
- }
+ emit_stack_save (SAVE_FUNCTION, &tem, parm_birth_insn);
+ emit_stack_restore (SAVE_FUNCTION, tem, NULL_RTX);
+ }
/* 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
cfun->x_clobber_return_insn = after;
}
+ /* Output the label for the naked return from the function, if one is
+ expected. This is currently used only by __builtin_return. */
+ if (naked_return_label)
+ emit_label (naked_return_label);
+
/* ??? This should no longer be necessary since stupid is no longer with
us, but there are some parts of the compiler (eg reload_combine, and
sh mach_dep_reorg) that still try and compute their own lifetime info
}
}
-/* Set the specified locator to the insn chain. */
+/* Set the locator of the insn chain starting at INSN to LOC. */
static void
set_insn_locators (rtx insn, int loc)
{
static void
emit_return_into_block (basic_block bb, rtx line_note)
{
- emit_jump_insn_after (gen_return (), bb->end);
+ emit_jump_insn_after (gen_return (), BB_END (bb));
if (line_note)
- emit_note_copy_after (line_note, PREV_INSN (bb->end));
+ emit_note_copy_after (line_note, PREV_INSN (BB_END (bb)));
}
#endif /* HAVE_return */
rtx equiv_reg_src; /* If nonzero, the value that SP_EQUIV_REG
should be set to once we no longer need
its value. */
+ rtx const_equiv[FIRST_PSEUDO_REGISTER]; /* Any known constant equivalences
+ for registers. */
};
static void handle_epilogue_set (rtx, struct epi_info *);
+static void update_epilogue_consts (rtx, rtx, void *);
static void emit_equiv_load (struct epi_info *);
/* Modify INSN, a list of one or more insns that is part of the epilogue, to
struct epi_info info;
rtx insn, next;
- /* If the epilogue is just a single instruction, it ust be OK as is. */
-
+ /* If the epilogue is just a single instruction, it must be OK as is. */
if (NEXT_INSN (insns) == NULL_RTX)
return insns;
info.sp_offset = 0;
info.equiv_reg_src = 0;
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ info.const_equiv[j] = 0;
+
insn = insns;
next = NULL_RTX;
while (insn != NULL_RTX)
&& !REGNO_REG_SET_P (EXIT_BLOCK_PTR->global_live_at_start,
regno)
&& !refers_to_regno_p (regno,
- regno + HARD_REGNO_NREGS (regno,
- Pmode),
- info.equiv_reg_src, NULL))
+ regno + hard_regno_nregs[regno]
+ [Pmode],
+ info.equiv_reg_src, NULL)
+ && info.const_equiv[regno] == 0)
break;
if (regno == FIRST_PSEUDO_REGISTER)
info.sp_equiv_reg = info.new_sp_equiv_reg;
info.sp_offset = info.new_sp_offset;
+ /* Now update any constants this insn sets. */
+ note_stores (PATTERN (insn), update_epilogue_consts, &info);
insn = next;
}
if (SET_DEST (set) != stack_pointer_rtx)
abort ();
- if (GET_CODE (SET_SRC (set)) == PLUS
- && GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT)
+ if (GET_CODE (SET_SRC (set)) == PLUS)
{
p->new_sp_equiv_reg = XEXP (SET_SRC (set), 0);
- p->new_sp_offset = INTVAL (XEXP (SET_SRC (set), 1));
+ if (GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT)
+ p->new_sp_offset = INTVAL (XEXP (SET_SRC (set), 1));
+ else if (GET_CODE (XEXP (SET_SRC (set), 1)) == REG
+ && REGNO (XEXP (SET_SRC (set), 1)) < FIRST_PSEUDO_REGISTER
+ && p->const_equiv[REGNO (XEXP (SET_SRC (set), 1))] != 0)
+ p->new_sp_offset
+ = INTVAL (p->const_equiv[REGNO (XEXP (SET_SRC (set), 1))]);
+ else
+ abort ();
}
else
p->new_sp_equiv_reg = SET_SRC (set), p->new_sp_offset = 0;
there seems little point in handling that case. Note that we have
to allow for the case where we are setting the register set in
the previous part of a PARALLEL inside a single insn. But use the
- old offset for any updates within this insn. */
+ old offset for any updates within this insn. We must allow for the case
+ where the register is being set in a different (usually wider) mode than
+ Pmode). */
else if (p->new_sp_equiv_reg != 0 && reg_set_p (p->new_sp_equiv_reg, set))
{
- if (!rtx_equal_p (p->new_sp_equiv_reg, SET_DEST (set))
- || p->equiv_reg_src != 0)
+ if (p->equiv_reg_src != 0
+ || GET_CODE (p->new_sp_equiv_reg) != REG
+ || GET_CODE (SET_DEST (set)) != REG
+ || GET_MODE_BITSIZE (GET_MODE (SET_DEST (set))) > BITS_PER_WORD
+ || REGNO (p->new_sp_equiv_reg) != REGNO (SET_DEST (set)))
abort ();
else
p->equiv_reg_src
}
}
+/* Update the tracking information for registers set to constants. */
+
+static void
+update_epilogue_consts (rtx dest, rtx x, void *data)
+{
+ struct epi_info *p = (struct epi_info *) data;
+ rtx new;
+
+ if (GET_CODE (dest) != REG || REGNO (dest) >= FIRST_PSEUDO_REGISTER)
+ return;
+
+ /* If we are either clobbering a register or doing a partial set,
+ show we don't know the value. */
+ else if (GET_CODE (x) == CLOBBER || ! rtx_equal_p (dest, SET_DEST (x)))
+ p->const_equiv[REGNO (dest)] = 0;
+
+ /* If we are setting it to a constant, record that constant. */
+ else if (GET_CODE (SET_SRC (x)) == CONST_INT)
+ p->const_equiv[REGNO (dest)] = SET_SRC (x);
+
+ /* If this is a binary operation between a register we have been tracking
+ and a constant, see if we can compute a new constant value. */
+ else if (ARITHMETIC_P (SET_SRC (x))
+ && GET_CODE (XEXP (SET_SRC (x), 0)) == REG
+ && REGNO (XEXP (SET_SRC (x), 0)) < FIRST_PSEUDO_REGISTER
+ && p->const_equiv[REGNO (XEXP (SET_SRC (x), 0))] != 0
+ && GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
+ && 0 != (new = simplify_binary_operation
+ (GET_CODE (SET_SRC (x)), GET_MODE (dest),
+ p->const_equiv[REGNO (XEXP (SET_SRC (x), 0))],
+ XEXP (SET_SRC (x), 1)))
+ && GET_CODE (new) == CONST_INT)
+ p->const_equiv[REGNO (dest)] = new;
+
+ /* Otherwise, we can't do anything with this value. */
+ else
+ p->const_equiv[REGNO (dest)] = 0;
+}
+
/* Emit an insn to do the load shown in p->equiv_reg_src, if needed. */
static void
emit_equiv_load (struct epi_info *p)
{
if (p->equiv_reg_src != 0)
- emit_move_insn (p->sp_equiv_reg, p->equiv_reg_src);
+ {
+ rtx dest = p->sp_equiv_reg;
+
+ if (GET_MODE (p->equiv_reg_src) != GET_MODE (dest))
+ dest = gen_rtx_REG (GET_MODE (p->equiv_reg_src),
+ REGNO (p->sp_equiv_reg));
- p->equiv_reg_src = 0;
+ emit_move_insn (dest, p->equiv_reg_src);
+ p->equiv_reg_src = 0;
+ }
}
#endif
last = e->src;
/* Verify that there are no active instructions in the last block. */
- label = last->end;
+ label = BB_END (last);
while (label && GET_CODE (label) != CODE_LABEL)
{
if (active_insn_p (label))
label = PREV_INSN (label);
}
- if (last->head == label && GET_CODE (label) == CODE_LABEL)
+ if (BB_HEAD (last) == label && GET_CODE (label) == CODE_LABEL)
{
rtx epilogue_line_note = NULL_RTX;
if (bb == ENTRY_BLOCK_PTR)
continue;
- jump = bb->end;
+ jump = BB_END (bb);
if ((GET_CODE (jump) != JUMP_INSN) || JUMP_LABEL (jump) != label)
continue;
/* Emit a return insn for the exit fallthru block. Whether
this is still reachable will be determined later. */
- emit_barrier_after (last->end);
+ emit_barrier_after (BB_END (last));
emit_return_into_block (last, epilogue_line_note);
- epilogue_end = last->end;
+ epilogue_end = BB_END (last);
last->succ->flags &= ~EDGE_FALLTHRU;
goto epilogue_done;
}
for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
{
basic_block bb = e->src;
- rtx insn = bb->end;
+ rtx insn = BB_END (bb);
rtx i;
rtx newinsn;
#endif
#ifdef HAVE_prologue
+ /* This is probably all useless now that we use locators. */
if (prologue_end)
{
rtx insn, prev;
}
/* Find the last line number note in the first block. */
- for (insn = ENTRY_BLOCK_PTR->next_bb->end;
+ for (insn = BB_END (ENTRY_BLOCK_PTR->next_bb);
insn != prologue_end && insn;
insn = PREV_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
/* Similarly, move any line notes that appear after the epilogue.
There is no need, however, to be quite so anal about the existence
- of such a note. */
+ of such a note. Also move the NOTE_INSN_FUNCTION_END and (possibly)
+ NOTE_INSN_FUNCTION_BEG notes, as those can be relevant for debug
+ info generation. */
for (insn = epilogue_end; insn; insn = next)
{
next = NEXT_INSN (insn);
- if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ if (GET_CODE (insn) == NOTE
+ && (NOTE_LINE_NUMBER (insn) > 0
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END))
reorder_insns (insn, insn, PREV_INSN (epilogue_end));
}
}
VARRAY_INT_INIT (sibcall_epilogue, 0, "sibcall_epilogue");
}
+/* Returns the name of the current function. */
+const char *
+current_function_name (void)
+{
+ return lang_hooks.decl_printable_name (cfun->decl, 2);
+}
+
#include "gt-function.h"