/* Expands front end tree to back end RTL for GNU C-Compiler
- Copyright (C) 1987, 88, 89, 91-96, 1997 Free Software Foundation, Inc.
+ Copyright (C) 1987, 88, 89, 91-98, 1999 Free Software Foundation, Inc.
This file is part of GNU CC.
then scans all the RTL instructions so far generated to correct them. */
#include "config.h"
-#include <stdio.h>
+#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "output.h"
#include "basic-block.h"
#include "obstack.h"
-#include "bytecode.h"
-#include "bc-emit.h"
+#include "toplev.h"
+
+#if !defined PREFERRED_STACK_BOUNDARY && defined STACK_BOUNDARY
+#define PREFERRED_STACK_BOUNDARY STACK_BOUNDARY
+#endif
#ifndef TRAMPOLINE_ALIGNMENT
#define TRAMPOLINE_ALIGNMENT FUNCTION_BOUNDARY
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 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 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. */
+int current_function_is_thunk;
+
/* Nonzero if function being compiled can call alloca,
either as a subroutine or builtin. */
char *current_function_name;
-/* If non-zero, an RTL expression for that location at which the current
- function returns its result. Always equal to
- DECL_RTL (DECL_RESULT (current_function_decl)), but provided
- independently of the tree structures. */
+/* If non-zero, an RTL expression for the location at which the current
+ function returns its result. If the current function returns its
+ result in a register, current_function_return_rtx will always be
+ the hard register containing the result. */
rtx current_function_return_rtx;
/* The arg pointer hard register, or the pseudo into which it was copied. */
rtx current_function_internal_arg_pointer;
+/* Language-specific reason why the current function cannot be made inline. */
+char *current_function_cannot_inline;
+
+/* Nonzero if instrumentation calls for function entry and exit should be
+ generated. */
+int current_function_instrument_entry_exit;
+
+/* Nonzero if memory access checking be enabled in the current function. */
+int current_function_check_memory_usage;
+
/* The FUNCTION_DECL for an inline function currently being expanded. */
tree inline_function_decl;
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;
/* RTX for stack slot that holds the stack pointer value to restore
for a nonlocal goto.
/* Last insn of those whose job was to put parms into their nominal homes. */
static rtx last_parm_insn;
-/* 1 + last pseudo register number used for loading a copy
- of a parameter of this function. */
-static int max_parm_reg;
+/* 1 + last pseudo register number possibly used for loading a copy
+ of a parameter of this function. */
+int max_parm_reg;
/* Vector indexed by REGNO, containing location on stack in which
to put the parm which is nominally in pseudo register REGNO,
- if we discover that that parm must go in the stack. */
-static rtx *parm_reg_stack_loc;
+ if we discover that that parm must go in the stack. The highest
+ element in this vector is one less than MAX_PARM_REG, above. */
+rtx *parm_reg_stack_loc;
/* Nonzero once virtual register instantiation has been done.
assign_stack_local uses frame_pointer_rtx when this is nonzero. */
slot above. May be an EXPR_LIST if multiple addresses exist. */
rtx address;
/* The size, in units, of the slot. */
- int size;
+ HOST_WIDE_INT size;
/* The value of `sequence_rtl_expr' when this temporary is allocated. */
tree rtl_expr;
/* Non-zero if this temporary is currently in use. */
int keep;
/* The offset of the slot from the frame_pointer, including extra space
for alignment. This info is for combine_temp_slots. */
- int base_offset;
+ HOST_WIDE_INT base_offset;
/* The size of the slot, including extra space for alignment. This
info is for combine_temp_slots. */
- int full_size;
+ HOST_WIDE_INT full_size;
};
/* List of all temporaries allocated, both available and in use. */
/* Current nesting level for temporaries. */
int temp_slot_level;
-\f
-/* The FUNCTION_DECL node for the current function. */
-static tree this_function_decl;
-
-/* Callinfo pointer for the current function. */
-static rtx this_function_callinfo;
-
-/* The label in the bytecode file of this function's actual bytecode.
- Not an rtx. */
-static char *this_function_bytecode;
-
-/* The call description vector for the current function. */
-static rtx this_function_calldesc;
-
-/* Size of the local variables allocated for the current function. */
-int local_vars_size;
-
-/* Current depth of the bytecode evaluation stack. */
-int stack_depth;
-/* Maximum depth of the evaluation stack in this function. */
-int max_stack_depth;
+/* Current nesting level for variables in a block. */
-/* Current depth in statement expressions. */
-static int stmt_expr_depth;
+int var_temp_slot_level;
+/* When temporaries are created by TARGET_EXPRs, they are created at
+ this level of temp_slot_level, so that they can remain allocated
+ until no longer needed. CLEANUP_POINT_EXPRs define the lifetime
+ of TARGET_EXPRs. */
+int target_temp_slot_level;
+\f
/* This structure is used to record MEMs or pseudos used to replace VAR, any
SUBREGs of VAR, and any MEMs containing VAR as an address. We need to
maintain this list in case two operands of an insn were required to match;
/* Forward declarations. */
+static rtx assign_outer_stack_local PROTO ((enum machine_mode, HOST_WIDE_INT,
+ int, struct function *));
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, int));
static void fixup_var_refs PROTO((rtx, enum machine_mode, int));
static struct fixup_replacement
*find_fixup_replacement PROTO((struct fixup_replacement **, rtx));
static int instantiate_virtual_regs_1 PROTO((rtx *, rtx, int));
static void delete_handlers PROTO((void));
static void pad_to_arg_alignment PROTO((struct args_size *, int));
+#ifndef ARGS_GROW_DOWNWARD
static void pad_below PROTO((struct args_size *, enum machine_mode,
tree));
+#endif
+#ifdef ARGS_GROW_DOWNWARD
static tree round_down PROTO((tree, int));
+#endif
static rtx round_trampoline_addr PROTO((rtx));
static tree blocks_nreverse PROTO((tree));
static int all_blocks PROTO((tree, tree *));
+#if defined (HAVE_prologue) || defined (HAVE_epilogue)
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));
\f
/* Pointer to chain of `struct function' for containing functions. */
struct function *outer_function_chain;
tree decl;
{
struct function *p;
+
for (p = outer_function_chain; p; p = p->next)
if (p->decl == decl)
return p;
+
abort ();
}
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->arg_offset_rtx = current_function_arg_offset_rtx;
p->uses_const_pool = current_function_uses_const_pool;
p->uses_pic_offset_table = current_function_uses_pic_offset_table;
p->internal_arg_pointer = current_function_internal_arg_pointer;
+ p->cannot_inline = current_function_cannot_inline;
p->max_parm_reg = max_parm_reg;
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_stack_level = nonlocal_goto_stack_level;
p->nonlocal_labels = nonlocal_labels;
p->cleanup_label = cleanup_label;
p->function_call_count = function_call_count;
p->temp_slots = temp_slots;
p->temp_slot_level = temp_slot_level;
+ p->target_temp_slot_level = target_temp_slot_level;
+ p->var_temp_slot_level = var_temp_slot_level;
p->fixup_var_refs_queue = 0;
p->epilogue_delay_list = current_function_epilogue_delay_list;
p->args_info = current_function_args_info;
+ p->check_memory_usage = current_function_check_memory_usage;
+ p->instrument_entry_exit = current_function_instrument_entry_exit;
save_tree_status (p, context);
save_storage_status (p);
save_emit_status (p);
- init_emit ();
save_expr_status (p);
save_stmt_status (p);
- save_varasm_status (p);
-
+ save_varasm_status (p, context);
if (save_machine_status)
(*save_machine_status) (p);
}
tree context;
{
struct function *p = outer_function_chain;
+ struct var_refs_queue *queue;
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;
current_function_calls_alloca = p->calls_alloca;
current_function_has_nonlocal_label = p->has_nonlocal_label;
current_function_has_nonlocal_goto = p->has_nonlocal_goto;
+ current_function_is_thunk = p->is_thunk;
current_function_args_size = p->args_size;
current_function_pretend_args_size = p->pretend_args_size;
current_function_arg_offset_rtx = p->arg_offset_rtx;
current_function_uses_const_pool = p->uses_const_pool;
current_function_uses_pic_offset_table = p->uses_pic_offset_table;
current_function_internal_arg_pointer = p->internal_arg_pointer;
+ current_function_cannot_inline = p->cannot_inline;
max_parm_reg = p->max_parm_reg;
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_stack_level = p->nonlocal_goto_stack_level;
nonlocal_labels = p->nonlocal_labels;
cleanup_label = p->cleanup_label;
function_call_count = p->function_call_count;
temp_slots = p->temp_slots;
temp_slot_level = p->temp_slot_level;
+ target_temp_slot_level = p->target_temp_slot_level;
+ var_temp_slot_level = p->var_temp_slot_level;
current_function_epilogue_delay_list = p->epilogue_delay_list;
reg_renumber = 0;
current_function_args_info = p->args_info;
+ current_function_check_memory_usage = p->check_memory_usage;
+ current_function_instrument_entry_exit = p->instrument_entry_exit;
- restore_tree_status (p);
+ restore_tree_status (p, context);
restore_storage_status (p);
restore_expr_status (p);
restore_emit_status (p);
/* Finish doing put_var_into_stack for any of our variables
which became addressable during the nested function. */
- {
- struct var_refs_queue *queue = p->fixup_var_refs_queue;
- for (; queue; queue = queue->next)
- fixup_var_refs (queue->modified, queue->promoted_mode, queue->unsignedp);
- }
+ for (queue = p->fixup_var_refs_queue; queue; queue = queue->next)
+ fixup_var_refs (queue->modified, queue->promoted_mode, queue->unsignedp);
free (p);
/* 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
rtx
assign_stack_local (mode, size, align)
enum machine_mode mode;
- int size;
+ HOST_WIDE_INT size;
int align;
{
register rtx x, addr;
frame_offset += size;
#endif
- x = gen_rtx (MEM, mode, addr);
+ x = gen_rtx_MEM (mode, addr);
- stack_slot_list = gen_rtx (EXPR_LIST, VOIDmode, x, stack_slot_list);
+ stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, x, stack_slot_list);
return x;
}
First three arguments are same as in preceding function.
The last argument specifies the function to allocate in. */
-rtx
+static rtx
assign_outer_stack_local (mode, size, align, function)
enum machine_mode mode;
- int size;
+ HOST_WIDE_INT size;
int align;
struct function *function;
{
function->frame_offset += size;
#endif
- x = gen_rtx (MEM, mode, addr);
+ x = gen_rtx_MEM (mode, addr);
function->stack_slot_list
- = gen_rtx (EXPR_LIST, VOIDmode, x, function->stack_slot_list);
+ = gen_rtx_EXPR_LIST (VOIDmode, x, function->stack_slot_list);
pop_obstacks ();
KEEP is 1 if this slot is to be retained after a call to
free_temp_slots. Automatic variables for a block are allocated
- with this flag. KEEP is 2, if we allocate a longer term temporary,
- whose lifetime is controlled by CLEANUP_POINT_EXPRs. */
+ 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). */
rtx
assign_stack_temp (mode, size, keep)
enum machine_mode mode;
- int size;
+ HOST_WIDE_INT size;
int keep;
{
struct temp_slot *p, *best_p = 0;
if (GET_MODE (best_p->slot) == BLKmode)
{
int alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
- int rounded_size = CEIL_ROUND (size, alignment);
+ HOST_WIDE_INT rounded_size = CEIL_ROUND (size, alignment);
if (best_p->size - rounded_size >= alignment)
{
p->size = best_p->size - rounded_size;
p->base_offset = best_p->base_offset + rounded_size;
p->full_size = best_p->full_size - rounded_size;
- p->slot = gen_rtx (MEM, BLKmode,
- plus_constant (XEXP (best_p->slot, 0),
- rounded_size));
+ p->slot = gen_rtx_MEM (BLKmode,
+ plus_constant (XEXP (best_p->slot, 0),
+ rounded_size));
p->address = 0;
p->rtl_expr = 0;
p->next = temp_slots;
temp_slots = p;
- stack_slot_list = gen_rtx (EXPR_LIST, VOIDmode, p->slot,
- stack_slot_list);
+ stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, p->slot,
+ stack_slot_list);
best_p->size = rounded_size;
best_p->full_size = rounded_size;
/* If we still didn't find one, make a new temporary. */
if (p == 0)
{
- int frame_offset_old = frame_offset;
+ HOST_WIDE_INT frame_offset_old = frame_offset;
+
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);
+
/* The following slot size computation is necessary because we don't
know the actual size of the temporary slot until assign_stack_local
has performed all the frame alignment and size rounding for the
#else
p->size = size;
#endif
+
/* Now define the fields used by combine_temp_slots. */
#ifdef FRAME_GROWS_DOWNWARD
p->base_offset = frame_offset;
p->level = target_temp_slot_level;
p->keep = 0;
}
+ else if (keep == 3)
+ {
+ p->level = var_temp_slot_level;
+ p->keep = 0;
+ }
else
{
p->level = temp_slot_level;
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;
}
\f
if (mode == BLKmode || memory_required)
{
- int size = int_size_in_bytes (type);
+ HOST_WIDE_INT size = int_size_in_bytes (type);
rtx tmp;
/* Unfortunately, we don't yet know how to allocate variable-sized
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);
+ MEM_SET_IN_STRUCT_P (tmp, AGGREGATE_TYPE_P (type));
return tmp;
}
{
struct temp_slot *p, *q;
struct temp_slot *prev_p, *prev_q;
- /* Determine where to free back to after this function. */
- rtx free_pointer = rtx_alloc (CONST_INT);
+ int num_slots;
+
+ /* 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++)
+ if (num_slots > 100 || (num_slots > 10 && optimize == 0))
+ return;
for (p = temp_slots, prev_p = 0; p; p = prev_p ? prev_p->next : temp_slots)
{
int delete_p = 0;
+
if (! p->in_use && GET_MODE (p->slot) == BLKmode)
for (q = p->next, prev_q = p; q; q = prev_q->next)
{
else
prev_p = p;
}
-
- /* Free all the RTL made by plus_constant. */
- rtx_free (free_pointer);
}
\f
/* Find the temp slot corresponding to the object at address X. */
{
if (! p->in_use)
continue;
+
else if (XEXP (p->slot, 0) == x
|| p->address == x
|| (GET_CODE (x) == PLUS
}
/* Indicate that NEW is an alternate way of referring to the temp slot
- that previous was known by OLD. */
+ that previously was known by OLD. */
void
update_temp_slot_address (old, new)
else
{
if (GET_CODE (p->address) != EXPR_LIST)
- p->address = gen_rtx (EXPR_LIST, VOIDmode, p->address, NULL_RTX);
+ p->address = gen_rtx_EXPR_LIST (VOIDmode, p->address, NULL_RTX);
- p->address = gen_rtx (EXPR_LIST, VOIDmode, new, p->address);
+ p->address = gen_rtx_EXPR_LIST (VOIDmode, new, p->address);
}
}
combine_temp_slots ();
}
-/* Mark all temporaries ever allocated in this functon as not suitable
+/* Mark all temporaries ever allocated in this function as not suitable
for reuse until the current level is exited. */
void
temp_slot_level++;
}
+/* Likewise, but save the new level as the place to allocate variables
+ for blocks. */
+
+void
+push_temp_slots_for_block ()
+{
+ push_temp_slots ();
+
+ var_temp_slot_level = temp_slot_level;
+}
+
+/* Likewise, but save the new level as the place to allocate temporaries
+ for TARGET_EXPRs. */
+
+void
+push_temp_slots_for_target ()
+{
+ push_temp_slots ();
+
+ target_temp_slot_level = temp_slot_level;
+}
+
+/* Set and get the value of target_temp_slot_level. The only
+ permitted use of these functions is to save and restore this value. */
+
+int
+get_target_temp_slot_level ()
+{
+ return target_temp_slot_level;
+}
+
+void
+set_target_temp_slot_level (level)
+ int level;
+{
+ target_temp_slot_level = level;
+}
+
/* Pop a temporary nesting level. All slots in use in the current level
are freed. */
/* We have not allocated any temporaries yet. */
temp_slots = 0;
temp_slot_level = 0;
+ var_temp_slot_level = 0;
target_temp_slot_level = 0;
}
\f
enum machine_mode promoted_mode, decl_mode;
struct function *function = 0;
tree context;
+ int can_use_addressof;
- if (output_bytecode)
- return;
-
context = decl_function_context (decl);
- /* Get the current rtl used for this object and it's original mode. */
+ /* Get the current rtl used for this object and its original mode. */
reg = TREE_CODE (decl) == SAVE_EXPR ? SAVE_EXPR_RTL (decl) : DECL_RTL (decl);
/* No need to do anything if decl has no rtx yet
/* If this variable comes from an outer function,
find that function's saved context. */
- if (context != current_function_decl)
+ if (context != current_function_decl && context != inline_function_decl)
for (function = outer_function_chain; function; function = function->next)
if (function->decl == context)
break;
decl_mode = promoted_mode = GET_MODE (reg);
}
+ can_use_addressof
+ = (function == 0
+ && optimize > 0
+ /* FIXME make it work for promoted modes too */
+ && decl_mode == promoted_mode
+#ifdef NON_SAVING_SETJMP
+ && ! (NON_SAVING_SETJMP && current_function_calls_setjmp)
+#endif
+ );
+
+ /* If we can't use ADDRESSOF, make sure we see through one we already
+ generated. */
+ if (! can_use_addressof && GET_CODE (reg) == MEM
+ && GET_CODE (XEXP (reg, 0)) == ADDRESSOF)
+ reg = XEXP (XEXP (reg, 0), 0);
+
/* Now we should have a value that resides in one or more pseudo regs. */
if (GET_CODE (reg) == REG)
- put_reg_into_stack (function, reg, TREE_TYPE (decl),
- promoted_mode, decl_mode, TREE_SIDE_EFFECTS (decl));
+ {
+ /* 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);
+ 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);
+ }
else if (GET_CODE (reg) == CONCAT)
{
/* A CONCAT contains two pseudos; put them both in the stack.
#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));
+ part_mode, TREE_SIDE_EFFECTS (decl), 0,
+ TREE_USED (decl) || DECL_INITIAL (decl) != 0);
put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl));
+ part_mode, TREE_SIDE_EFFECTS (decl), 0,
+ TREE_USED (decl) || DECL_INITIAL (decl) != 0);
#else
put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl));
+ part_mode, TREE_SIDE_EFFECTS (decl), 0,
+ TREE_USED (decl) || DECL_INITIAL (decl) != 0);
put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode,
- part_mode, TREE_SIDE_EFFECTS (decl));
+ part_mode, TREE_SIDE_EFFECTS (decl), 0,
+ TREE_USED (decl) || DECL_INITIAL (decl) != 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);
/* The two parts are in memory order already.
Use the lower parts address as ours. */
if (GET_CODE (XEXP (reg, 0)) == PLUS)
XEXP (reg, 0) = copy_rtx (XEXP (reg, 0));
}
+ else
+ return;
+
+ if (current_function_check_memory_usage)
+ emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
+ XEXP (reg, 0), ptr_mode,
+ GEN_INT (GET_MODE_SIZE (GET_MODE (reg))),
+ TYPE_MODE (sizetype),
+ GEN_INT (MEMORY_USE_RW),
+ TYPE_MODE (integer_type_node));
}
/* Subroutine of put_var_into_stack. This puts a single pseudo reg REG
into the stack frame of FUNCTION (0 means the current function).
DECL_MODE is the machine mode of the user-level data type.
PROMOTED_MODE is the machine mode of the register.
- VOLATILE_P is nonzero if this is for a "volatile" decl. */
+ VOLATILE_P is nonzero if this is for a "volatile" decl.
+ USED_P is nonzero if this reg might have already been used in an insn. */
static void
-put_reg_into_stack (function, reg, type, promoted_mode, decl_mode, volatile_p)
+put_reg_into_stack (function, reg, type, promoted_mode, decl_mode, volatile_p,
+ original_regno, used_p)
struct function *function;
rtx reg;
tree type;
enum machine_mode promoted_mode, decl_mode;
int volatile_p;
+ int original_regno;
+ int used_p;
{
rtx new = 0;
+ int regno = original_regno;
+
+ if (regno == 0)
+ regno = REGNO (reg);
if (function)
{
- if (REGNO (reg) < function->max_parm_reg)
- new = function->parm_reg_stack_loc[REGNO (reg)];
+ if (regno < function->max_parm_reg)
+ new = function->parm_reg_stack_loc[regno];
if (new == 0)
new = assign_outer_stack_local (decl_mode, GET_MODE_SIZE (decl_mode),
0, function);
}
else
{
- if (REGNO (reg) < max_parm_reg)
- new = parm_reg_stack_loc[REGNO (reg)];
+ if (regno < max_parm_reg)
+ new = parm_reg_stack_loc[regno];
if (new == 0)
new = assign_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), 0);
}
PUT_CODE (reg, MEM);
/* If this is a memory ref that contains aggregate components,
- mark it as such for cse and loop optimize. */
- MEM_IN_STRUCT_P (reg) = AGGREGATE_TYPE_P (type);
+ mark it as such for cse and loop optimize. If we are reusing a
+ 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);
/* Now make sure that all refs to the variable, previously made
when it was a register, are fixed up to be valid again. */
- if (function)
+
+ if (used_p && function != 0)
{
struct var_refs_queue *temp;
function->fixup_var_refs_queue = temp;
pop_obstacks ();
}
- else
+ else if (used_p)
/* Variable is local; fix it up now. */
fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (type));
}
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);
+ end_sequence ();
}
\f
/* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is
while (insn)
{
rtx next = NEXT_INSN (insn);
+ rtx set, prev, prev_set;
rtx note;
+
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
/* 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)
+ && (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 ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0)
/* The REG_LIBCALL note will go away since we are going to
}
/* The insn to load VAR from a home in the arglist
- is now a no-op. When we see it, just delete it. */
+ 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
- && GET_CODE (PATTERN (insn)) == SET
- && SET_DEST (PATTERN (insn)) == var
+ && (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 (PATTERN (insn)), var))
+ && (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. */
struct fixup_replacement *replacements = 0;
rtx next_insn = NEXT_INSN (insn);
-#ifdef 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 (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.
+ 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. */
+ 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 (SMALL_REGISTER_CLASSES)
- {
if (call_dest != 0 && GET_CODE (insn) == INSN
&& reg_mentioned_p (var, PATTERN (insn))
&& reg_mentioned_p (call_dest, PATTERN (insn)))
else
call_dest = 0;
}
-#endif
/* 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
switch (code)
{
+ case ADDRESSOF:
+ if (XEXP (x, 0) == var)
+ {
+ /* 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);
+
+ if (! validate_change (insn, loc, y, 0))
+ *loc = copy_to_reg (y);
+ }
+
+ emit_insn_before (gen_sequence (), insn);
+ end_sequence ();
+ }
+ return;
+
case MEM:
if (var == x)
{
{
enum machine_mode wanted_mode = VOIDmode;
enum machine_mode is_mode = GET_MODE (tem);
- int width = INTVAL (XEXP (x, 1));
- int pos = INTVAL (XEXP (x, 2));
+ HOST_WIDE_INT pos = INTVAL (XEXP (x, 2));
#ifdef HAVE_extzv
if (GET_CODE (x) == ZERO_EXTRACT)
- wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+ {
+ wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+ if (wanted_mode == VOIDmode)
+ wanted_mode = word_mode;
+ }
#endif
#ifdef HAVE_extv
if (GET_CODE (x) == SIGN_EXTRACT)
- wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+ {
+ wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+ if (wanted_mode == VOIDmode)
+ wanted_mode = word_mode;
+ }
#endif
/* If we have a narrower mode, we can do something. */
if (wanted_mode != VOIDmode
&& GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode))
{
- int offset = pos / BITS_PER_UNIT;
+ HOST_WIDE_INT offset = pos / BITS_PER_UNIT;
rtx old_pos = XEXP (x, 2);
rtx newmem;
pos %= GET_MODE_BITSIZE (wanted_mode);
- newmem = gen_rtx (MEM, wanted_mode,
- plus_constant (XEXP (tem, 0), offset));
+ 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;
{
rtx dest = SET_DEST (x);
rtx src = SET_SRC (x);
+#ifdef HAVE_insv
rtx outerdest = dest;
+#endif
while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == SIGN_EXTRACT
&& ! mode_dependent_address_p (XEXP (tem, 0))
&& ! MEM_VOLATILE_P (tem))
{
- enum machine_mode wanted_mode
- = insn_operand_mode[(int) CODE_FOR_insv][0];
+ enum machine_mode wanted_mode;
enum machine_mode is_mode = GET_MODE (tem);
- int width = INTVAL (XEXP (outerdest, 1));
- int pos = INTVAL (XEXP (outerdest, 2));
+ HOST_WIDE_INT pos = INTVAL (XEXP (outerdest, 2));
+
+ wanted_mode = insn_operand_mode[(int) CODE_FOR_insv][0];
+ if (wanted_mode == VOIDmode)
+ wanted_mode = word_mode;
/* If we have a narrower mode, we can do something. */
if (GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode))
{
- int offset = pos / BITS_PER_UNIT;
+ HOST_WIDE_INT offset = pos / BITS_PER_UNIT;
rtx old_pos = XEXP (outerdest, 2);
rtx newmem;
pos %= GET_MODE_BITSIZE (wanted_mode);
- newmem = gen_rtx (MEM, wanted_mode,
- plus_constant (XEXP (tem, 0), offset));
+ 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;
SET_DEST (x) = temp;
}
}
+
+ default:
+ break;
}
/* Nothing special about this RTX; fix its operands. */
int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
rtx addr = XEXP (SUBREG_REG (x), 0);
enum machine_mode mode = GET_MODE (x);
- rtx saved, result;
+ rtx result;
/* Paradoxical SUBREGs are usually invalid during RTL generation. */
if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
&& GET_CODE (XEXP (ad, 0)) == REG
&& ((REGNO (XEXP (ad, 0)) >= FIRST_VIRTUAL_REGISTER
&& REGNO (XEXP (ad, 0)) <= LAST_VIRTUAL_REGISTER)
+ || REGNO (XEXP (ad, 0)) == FRAME_POINTER_REGNUM
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ || REGNO (XEXP (ad, 0)) == HARD_FRAME_POINTER_REGNUM
+#endif
+ || REGNO (XEXP (ad, 0)) == STACK_POINTER_REGNUM
+ || REGNO (XEXP (ad, 0)) == ARG_POINTER_REGNUM
|| XEXP (ad, 0) == current_function_internal_arg_pointer)
&& GET_CODE (XEXP (ad, 1)) == CONST_INT)
{
that we are now getting rid of,
and then for which byte of the word is wanted. */
- register int offset = INTVAL (XEXP (bitfield, 2));
+ HOST_WIDE_INT offset = INTVAL (XEXP (bitfield, 2));
rtx insns;
/* Adjust OFFSET to count bits from low-address byte. */
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);
static int var_offset;
static int dynamic_offset;
static int out_arg_offset;
+static int cfa_offset;
/* In most machines, the stack pointer register is equivalent to the bottom
of the stack. */
#endif
#endif
+/* On a few machines, the CFA coincides with the arg pointer. */
+
+#ifndef ARG_POINTER_CFA_OFFSET
+#define ARG_POINTER_CFA_OFFSET 0
+#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.
+ REG is overwritten by the MEM like in put_reg_into_stack. */
+
+rtx
+gen_mem_addressof (reg, decl)
+ rtx reg;
+ tree decl;
+{
+ tree type = TREE_TYPE (decl);
+ rtx r = gen_rtx_ADDRESSOF (Pmode, gen_reg_rtx (GET_MODE (reg)), REGNO (reg));
+ SET_ADDRESSOF_DECL (r, decl);
+ /* If the original REG was a user-variable, then so is the REG whose
+ address is being taken. */
+ REG_USERVAR_P (XEXP (r, 0)) = REG_USERVAR_P (reg);
+
+ XEXP (reg, 0) = r;
+ PUT_CODE (reg, MEM);
+ 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));
+
+ return reg;
+}
+
+/* If DECL has an RTL that is an ADDRESSOF rtx, put it into the stack. */
+
+void
+flush_addressof (decl)
+ tree decl;
+{
+ if ((TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == VAR_DECL)
+ && DECL_RTL (decl) != 0
+ && 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));
+}
+
+/* Force the register pointed to by R, an ADDRESSOF rtx, into the stack. */
+
+static void
+put_addressof_into_stack (r)
+ rtx r;
+{
+ tree decl = ADDRESSOF_DECL (r);
+ 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);
+}
+
+/* List of replacements made below in purge_addressof_1 when creating
+ bitfield insertions. */
+static rtx purge_addressof_replacements;
+
+/* Helper function for purge_addressof. See if the rtx expression at *LOC
+ in INSN needs to be changed. If FORCE, always put any ADDRESSOFs into
+ the stack. */
+
+static void
+purge_addressof_1 (loc, insn, force, store)
+ rtx *loc;
+ rtx insn;
+ int force, store;
+{
+ rtx x;
+ RTX_CODE code;
+ int i, j;
+ char *fmt;
+
+ /* Re-start here to avoid recursion in common cases. */
+ restart:
+
+ x = *loc;
+ if (x == 0)
+ return;
+
+ code = GET_CODE (x);
+
+ if (code == ADDRESSOF && GET_CODE (XEXP (x, 0)) == MEM)
+ {
+ rtx insns;
+ /* We must create a copy of the rtx because it was created by
+ overwriting a REG rtx which is always shared. */
+ rtx sub = copy_rtx (XEXP (XEXP (x, 0), 0));
+
+ if (validate_change (insn, loc, sub, 0)
+ || validate_replace_rtx (x, sub, insn))
+ return;
+
+ start_sequence ();
+ sub = force_operand (sub, NULL_RTX);
+ if (! validate_change (insn, loc, sub, 0)
+ && ! validate_replace_rtx (x, sub, insn))
+ abort ();
+
+ insns = gen_sequence ();
+ end_sequence ();
+ emit_insn_before (insns, insn);
+ return;
+ }
+ 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)
+ {
+ 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));
+ return;
+ }
+ else if (GET_CODE (sub) == REG && GET_MODE (x) != GET_MODE (sub))
+ {
+ int size_x, size_sub;
+
+ if (!insn)
+ {
+ /* When processing REG_NOTES look at the list of
+ replacements done on the insn to find the register that X
+ was replaced by. */
+ rtx tem;
+
+ for (tem = purge_addressof_replacements; tem != NULL_RTX;
+ tem = XEXP (XEXP (tem, 1), 1))
+ {
+ rtx y = XEXP (tem, 0);
+ if (GET_CODE (y) == MEM
+ && rtx_equal_p (XEXP (x, 0), XEXP (y, 0)))
+ {
+ /* 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. */
+
+ rtx z = XEXP (XEXP (tem, 1), 0);
+ if (GET_MODE (x) != GET_MODE (y))
+ {
+ if (GET_CODE (z) == SUBREG && SUBREG_WORD (z) == 0)
+ z = SUBREG_REG (z);
+
+ /* ??? If we'd gotten into any of the really complex
+ cases below, I'm not sure we can do a proper
+ replacement. Might we be able to delete the
+ note in some cases? */
+ if (GET_MODE_SIZE (GET_MODE (x))
+ < GET_MODE_SIZE (GET_MODE (y)))
+ abort ();
+
+ 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 ();
+ }
+
+ size_x = GET_MODE_BITSIZE (GET_MODE (x));
+ size_sub = GET_MODE_BITSIZE (GET_MODE (sub));
+
+ /* Don't even consider working with paradoxical subregs,
+ or the moral equivalent seen here. */
+ if (size_x <= size_sub
+ && int_mode_for_mode (GET_MODE (sub)) != BLKmode)
+ {
+ /* Do a bitfield insertion to mirror what would happen
+ in memory. */
+
+ rtx val, seq;
+
+ if (store)
+ {
+ rtx p;
+
+ start_sequence ();
+ val = gen_reg_rtx (GET_MODE (x));
+ if (! validate_change (insn, loc, val, 0))
+ {
+ /* Discard the current sequence and put the
+ ADDRESSOF on stack. */
+ end_sequence ();
+ goto give_up;
+ }
+ seq = gen_sequence ();
+ end_sequence ();
+ emit_insn_before (seq, insn);
+
+ 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);
+ }
+ else
+ {
+ start_sequence ();
+ val = extract_bit_field (sub, size_x, 0, 1, NULL_RTX,
+ GET_MODE (x), GET_MODE (x),
+ GET_MODE_SIZE (GET_MODE (sub)),
+ GET_MODE_SIZE (GET_MODE (sub)));
+
+ if (! validate_change (insn, loc, val, 0))
+ {
+ /* Discard the current sequence and put the
+ ADDRESSOF on stack. */
+ end_sequence ();
+ goto give_up;
+ }
+
+ seq = gen_sequence ();
+ end_sequence ();
+ emit_insn_before (seq, insn);
+ }
+
+ /* Remember the replacement so that the same one can be done
+ on the REG_NOTES. */
+ purge_addressof_replacements
+ = gen_rtx_EXPR_LIST (VOIDmode, x,
+ gen_rtx_EXPR_LIST (VOIDmode, val,
+ purge_addressof_replacements));
+
+ /* We replaced with a reg -- all done. */
+ return;
+ }
+ }
+ else if (validate_change (insn, loc, sub, 0))
+ {
+ /* Remember the replacement so that the same one can be done
+ on the REG_NOTES. */
+ purge_addressof_replacements
+ = gen_rtx_EXPR_LIST (VOIDmode, x,
+ gen_rtx_EXPR_LIST (VOIDmode, sub,
+ purge_addressof_replacements));
+ goto restart;
+ }
+ give_up:;
+ /* else give up and put it into the stack */
+ }
+ else if (code == ADDRESSOF)
+ {
+ put_addressof_into_stack (x);
+ return;
+ }
+ else if (code == SET)
+ {
+ purge_addressof_1 (&SET_DEST (x), insn, force, 1);
+ purge_addressof_1 (&SET_SRC (x), insn, force, 0);
+ return;
+ }
+
+ /* Scan all subexpressions. */
+ fmt = GET_RTX_FORMAT (code);
+ for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
+ {
+ if (*fmt == 'e')
+ purge_addressof_1 (&XEXP (x, i), insn, force, 0);
+ else if (*fmt == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ purge_addressof_1 (&XVECEXP (x, i, j), insn, force, 0);
+ }
+}
+
+/* Eliminate all occurrences of ADDRESSOF from INSNS. Elide any remaining
+ (MEM (ADDRESSOF)) patterns, and force any needed registers into the
+ stack. */
+
+void
+purge_addressof (insns)
+ rtx insns;
+{
+ rtx insn;
+ 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;
+}
+\f
/* Pass through the INSNS of function FNDECL and convert virtual register
references to hard register references. */
rtx insns;
{
rtx insn;
+ 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;
/* 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 (®_NOTES (insn), NULL_RTX, 0);
}
+ /* Instantiate the stack slots for the parm registers, for later use in
+ addressof elimination. */
+ for (i = 0; i < max_parm_reg; ++i)
+ if (parm_reg_stack_loc[i])
+ instantiate_virtual_regs_1 (&parm_reg_stack_loc[i], NULL_RTX, 0);
+
/* Now instantiate the remaining register equivalences for debugging info.
These will not be valid addresses. */
instantiate_decls (fndecl, 0);
/* Process all parameters of the function. */
for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
{
- instantiate_decl (DECL_RTL (decl), int_size_in_bytes (TREE_TYPE (decl)),
- valid_only);
- instantiate_decl (DECL_INCOMING_RTL (decl),
- int_size_in_bytes (TREE_TYPE (decl)), valid_only);
+ HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl));
+
+ 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
+ the two sizes. */
+ size = MAX (GET_MODE_SIZE (GET_MODE (DECL_INCOMING_RTL (decl))), size);
+ instantiate_decl (DECL_INCOMING_RTL (decl), size, valid_only);
}
/* Now process all variables defined in the function or its subblocks. */
addr = XEXP (x, 0);
if (CONSTANT_P (addr)
+ || (GET_CODE (addr) == ADDRESSOF && GET_CODE (XEXP (addr, 0)) == REG)
|| (GET_CODE (addr) == REG
&& (REGNO (addr) < FIRST_VIRTUAL_REGISTER
|| REGNO (addr) > LAST_VIRTUAL_REGISTER)))
rtx x;
RTX_CODE code;
rtx new = 0;
- int offset;
+ HOST_WIDE_INT offset;
rtx temp;
rtx seq;
int i, j;
case SET:
/* We are allowed to set the virtual registers. This means that
- that the actual register should receive the source minus the
+ 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 = 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)
{
emit_insns_before (seq, object);
SET_DEST (x) = new;
- if (!validate_change (object, &SET_SRC (x), temp, 0)
+ if (! validate_change (object, &SET_SRC (x), temp, 0)
|| ! extra_insns)
abort ();
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;
else
{
loc = &XEXP (x, 0);
instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 1), object,
extra_insns);
- new = gen_rtx (PLUS, Pmode, new, XEXP (XEXP (x, 0), 1));
+ new = gen_rtx_PLUS (Pmode, new, XEXP (XEXP (x, 0), 1));
}
else if (XEXP (x, 0) == virtual_incoming_args_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
{
/* We know the second operand is a constant. Unless the
register containing the sum. */
XEXP (x, 0) = old;
- new = gen_rtx (PLUS, Pmode, new, new_offset);
+ new = gen_rtx_PLUS (Pmode, new, new_offset);
start_sequence ();
temp = force_operand (new, NULL_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)
{
}
return 1;
+
+ case ADDRESSOF:
+ if (GET_CODE (XEXP (x, 0)) == REG)
+ return 1;
+
+ else if (GET_CODE (XEXP (x, 0)) == MEM)
+ {
+ /* If we have a (addressof (mem ..)), do any instantiation inside
+ since we know we'll be making the inside valid when we finally
+ remove the ADDRESSOF. */
+ instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 0), NULL_RTX, 0);
+ return 1;
+ }
+ break;
+
+ default:
+ break;
}
/* Scan all subexpressions. */
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);
+ }
}
}
rtx x = 0;
for (t = nonlocal_labels; t; t = TREE_CHAIN (t))
- x = gen_rtx (EXPR_LIST, VOIDmode, label_rtx (TREE_VALUE (t)), x);
+ x = gen_rtx_EXPR_LIST (VOIDmode, label_rtx (TREE_VALUE (t)), x);
return x;
}
{
if (GET_CODE (rtl) == REG)
/* This is a register variable. */
- emit_insn (gen_rtx (USE, VOIDmode, rtl));
+ emit_insn (gen_rtx_USE (VOIDmode, rtl));
else if (GET_CODE (rtl) == MEM
&& GET_CODE (XEXP (rtl, 0)) == REG
&& (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER
|| REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER)
&& XEXP (rtl, 0) != current_function_internal_arg_pointer)
/* This is a variable-sized structure. */
- emit_insn (gen_rtx (USE, VOIDmode, XEXP (rtl, 0)));
+ emit_insn (gen_rtx_USE (VOIDmode, XEXP (rtl, 0)));
}
/* Like use_variable except that it outputs the USEs after INSN
{
if (GET_CODE (rtl) == REG)
/* This is a register variable. */
- emit_insn_after (gen_rtx (USE, VOIDmode, rtl), insn);
+ emit_insn_after (gen_rtx_USE (VOIDmode, rtl), insn);
else if (GET_CODE (rtl) == MEM
&& GET_CODE (XEXP (rtl, 0)) == REG
&& (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER
|| REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER)
&& XEXP (rtl, 0) != current_function_internal_arg_pointer)
/* This is a variable-sized structure. */
- emit_insn_after (gen_rtx (USE, VOIDmode, XEXP (rtl, 0)), insn);
+ emit_insn_after (gen_rtx_USE (VOIDmode, XEXP (rtl, 0)), insn);
}
\f
int
if (RETURN_IN_MEMORY (type))
return 1;
- /* Types that are TREE_ADDRESSABLE must be contructed in memory,
+ /* Types that are TREE_ADDRESSABLE must be constructed in memory,
and thus can't be returned in registers. */
if (TREE_ADDRESSABLE (type))
return 1;
/* This is a dummy PARM_DECL that we used for the function result if
the function returns a structure. */
tree function_result_decl = 0;
- int nparmregs = list_length (fnargs) + LAST_VIRTUAL_REGISTER + 1;
int varargs_setup = 0;
rtx conversion_insns = 0;
fnargs = function_result_decl;
}
- parm_reg_stack_loc = (rtx *) oballoc (nparmregs * sizeof (rtx));
- bzero ((char *) parm_reg_stack_loc, nparmregs * sizeof (rtx));
+ max_parm_reg = LAST_VIRTUAL_REGISTER + 1;
+ parm_reg_stack_loc = (rtx *) savealloc (max_parm_reg * sizeof (rtx));
+ bzero ((char *) parm_reg_stack_loc, max_parm_reg * sizeof (rtx));
#ifdef INIT_CUMULATIVE_INCOMING_ARGS
INIT_CUMULATIVE_INCOMING_ARGS (args_so_far, fntype, NULL_RTX);
tree nominal_type = TREE_TYPE (parm);
/* Set LAST_NAMED if this is last named arg before some
- anonymous args. We treat it as if it were anonymous too. */
+ anonymous args. */
int last_named = ((TREE_CHAIN (parm) == 0
|| DECL_NAME (TREE_CHAIN (parm)) == 0)
&& (stdarg || current_function_varargs));
+ /* 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. */
+ int named_arg = STRICT_ARGUMENT_NAMING ? 1 : ! last_named;
if (TREE_TYPE (parm) == error_mark_node
/* This can happen after weird syntax errors
|| TREE_CODE (parm) != PARM_DECL
|| passed_type == NULL)
{
- DECL_INCOMING_RTL (parm) = DECL_RTL (parm) = gen_rtx (MEM, BLKmode,
- const0_rtx);
+ DECL_INCOMING_RTL (parm) = DECL_RTL (parm)
+ = gen_rtx_MEM (BLKmode, const0_rtx);
TREE_USED (parm) = 1;
continue;
}
|| TREE_ADDRESSABLE (passed_type)
#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
|| FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, passed_mode,
- passed_type, ! last_named)
+ passed_type, named_arg)
#endif
)
{
#ifdef PROMOTE_FUNCTION_ARGS
/* Compute the mode in which the arg is actually extended to. */
+ unsignedp = TREE_UNSIGNED (passed_type);
promoted_mode = promote_mode (passed_type, promoted_mode, &unsignedp, 1);
#endif
0 means it arrives on the stack. */
#ifdef FUNCTION_INCOMING_ARG
entry_parm = FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
- passed_type, ! last_named);
+ passed_type, named_arg);
#else
entry_parm = FUNCTION_ARG (args_so_far, promoted_mode,
- passed_type, ! last_named);
+ passed_type, named_arg);
#endif
if (entry_parm == 0)
In this case, we call FUNCTION_ARG with NAMED set to 1 instead of
0 as it was the previous time. */
- locate_and_pad_parm (promoted_mode, passed_type,
+ locate_and_pad_parm (nominal_mode, passed_type,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
#else
#ifdef FUNCTION_INCOMING_ARG
FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
passed_type,
- (! last_named
+ (named_arg
|| varargs_setup)) != 0,
#else
FUNCTION_ARG (args_so_far, promoted_mode,
passed_type,
- ! last_named || varargs_setup) != 0,
+ named_arg || varargs_setup) != 0,
#endif
#endif
fndecl, &stack_args_size, &stack_offset, &arg_size);
rtx offset_rtx = ARGS_SIZE_RTX (stack_offset);
if (offset_rtx == const0_rtx)
- stack_parm = gen_rtx (MEM, promoted_mode, internal_arg_pointer);
+ stack_parm = gen_rtx_MEM (nominal_mode, internal_arg_pointer);
else
- stack_parm = gen_rtx (MEM, promoted_mode,
- gen_rtx (PLUS, Pmode,
- internal_arg_pointer, offset_rtx));
+ 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. 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);
}
/* If this parameter was passed both in registers and in the stack,
if (entry_parm)
{
int nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, promoted_mode,
- passed_type, ! last_named);
+ passed_type, named_arg);
if (nregs > 0)
{
/* 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);
+ emit_group_store (validize_mem (stack_parm), entry_parm,
+ int_size_in_bytes (TREE_TYPE (parm)),
+ (TYPE_ALIGN (TREE_TYPE (parm))
+ / BITS_PER_UNIT));
else
move_block_from_reg (REGNO (entry_parm),
validize_mem (stack_parm), nregs,
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.
/* Update info on where next arg arrives in registers. */
FUNCTION_ARG_ADVANCE (args_so_far, promoted_mode,
- passed_type, ! last_named);
+ passed_type, named_arg);
/* If this is our second time through, we are done with this parm. */
if (second_time)
offset_rtx = ARGS_SIZE_RTX (stack_offset);
if (offset_rtx == const0_rtx)
- stack_parm = gen_rtx (MEM, nominal_mode, internal_arg_pointer);
+ 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));
+ 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_IN_STRUCT_P (stack_parm) = aggregate;
+ MEM_SET_IN_STRUCT_P (stack_parm, aggregate);
}
#endif /* 0 */
have been optimised away. */
if (GET_CODE (entry_parm) == REG && !(hide_last_arg && last_named))
- emit_insn (gen_rtx (USE, GET_MODE (entry_parm), entry_parm));
+ emit_insn (gen_rtx_USE (GET_MODE (entry_parm), entry_parm));
#endif
/* ENTRY_PARM is an RTX for the parameter as it arrives,
/* 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)
/* 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);
+ emit_group_store (validize_mem (stack_parm), entry_parm,
+ int_size_in_bytes (TREE_TYPE (parm)),
+ (TYPE_ALIGN (TREE_TYPE (parm))
+ / BITS_PER_UNIT));
else
move_block_from_reg (REGNO (entry_parm),
validize_mem (stack_parm),
may need to do it in a wider mode. */
register rtx parmreg;
- int regno, regnoi, regnor;
+ int regno, regnoi = 0, regnor = 0;
unsignedp = TREE_UNSIGNED (TREE_TYPE (parm));
if (passed_pointer)
{
DECL_RTL (parm)
- = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (passed_type)), parmreg);
- MEM_IN_STRUCT_P (DECL_RTL (parm)) = aggregate;
+ = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (passed_type)), parmreg);
+ 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
- a diffent mode than NOMINAL_MODE if it is being stored
+ a different mode than NOMINAL_MODE if it is being stored
promoted.
If ENTRY_PARM is a hard register, it might be in a register
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 ();
&& FUNCTION_ARG_CALLEE_COPIES (args_so_far,
TYPE_MODE (DECL_ARG_TYPE (parm)),
DECL_ARG_TYPE (parm),
- ! last_named)
+ named_arg)
&& ! TREE_ADDRESSABLE (DECL_ARG_TYPE (parm)))
{
rtx copy;
if (TYPE_SIZE (type) == 0
|| TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
/* This is a variable sized object. */
- copy = gen_rtx (MEM, BLKmode,
- allocate_dynamic_stack_space
- (expr_size (parm), NULL_RTX,
- TYPE_ALIGN (type)));
+ copy = gen_rtx_MEM (BLKmode,
+ allocate_dynamic_stack_space
+ (expr_size (parm), NULL_RTX,
+ TYPE_ALIGN (type)));
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,
+ GEN_INT (int_size_in_bytes (type)),
+ TYPE_MODE (sizetype),
+ GEN_INT (MEMORY_USE_RW),
+ TYPE_MODE (integer_type_node));
conversion_insns = get_insns ();
did_conversion = 1;
end_sequence ();
else
regno = REGNO (parmreg);
- if (regno >= nparmregs)
+ if (regno >= max_parm_reg)
{
rtx *new;
- int old_nparmregs = nparmregs;
+ int old_max_parm_reg = max_parm_reg;
- nparmregs = regno + 5;
- new = (rtx *) oballoc (nparmregs * sizeof (rtx));
+ /* 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 = (rtx *) savealloc (max_parm_reg * sizeof (rtx));
bcopy ((char *) parm_reg_stack_loc, (char *) new,
- old_nparmregs * sizeof (rtx));
- bzero ((char *) (new + old_nparmregs),
- (nparmregs - old_nparmregs) * sizeof (rtx));
+ old_max_parm_reg * sizeof (rtx));
+ bzero ((char *) (new + old_max_parm_reg),
+ (max_parm_reg - old_max_parm_reg) * sizeof (rtx));
parm_reg_stack_loc = new;
}
as we make here would screw up life analysis for it. */
if (nominal_mode == passed_mode
&& ! did_conversion
- && GET_CODE (entry_parm) == MEM
- && entry_parm == stack_parm
+ && stack_parm != 0
+ && GET_CODE (stack_parm) == MEM
&& stack_offset.var == 0
&& reg_mentioned_p (virtual_incoming_args_rtx,
- XEXP (entry_parm, 0)))
+ XEXP (stack_parm, 0)))
{
rtx linsn = get_last_insn ();
rtx sinsn, set;
if (set != 0
&& SET_DEST (set) == regno_reg_rtx [regnoi])
REG_NOTES (sinsn)
- = gen_rtx (EXPR_LIST, REG_EQUIV,
- parm_reg_stack_loc[regnoi],
- REG_NOTES (sinsn));
+ = gen_rtx_EXPR_LIST (REG_EQUIV,
+ parm_reg_stack_loc[regnoi],
+ REG_NOTES (sinsn));
else if (set != 0
&& SET_DEST (set) == regno_reg_rtx [regnor])
REG_NOTES (sinsn)
- = gen_rtx (EXPR_LIST, REG_EQUIV,
- parm_reg_stack_loc[regnor],
- REG_NOTES (sinsn));
+ = gen_rtx_EXPR_LIST (REG_EQUIV,
+ parm_reg_stack_loc[regnor],
+ REG_NOTES (sinsn));
}
else if ((set = single_set (linsn)) != 0
&& SET_DEST (set) == parmreg)
REG_NOTES (linsn)
- = gen_rtx (EXPR_LIST, REG_EQUIV,
- entry_parm, REG_NOTES (linsn));
+ = gen_rtx_EXPR_LIST (REG_EQUIV,
+ stack_parm, REG_NOTES (linsn));
}
/* For pointer data type, suggest pointer register. */
- if (TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE)
+ if (POINTER_TYPE_P (TREE_TYPE (parm)))
mark_reg_pointer (parmreg,
(TYPE_ALIGN (TREE_TYPE (TREE_TYPE (parm)))
/ BITS_PER_UNIT));
push_to_sequence (conversion_insns);
entry_parm = convert_to_mode (nominal_mode, tempreg,
TREE_UNSIGNED (TREE_TYPE (parm)));
+ if (stack_parm)
+ {
+ /* ??? This may need a big-endian conversion on sparc64. */
+ stack_parm = change_address (stack_parm, nominal_mode,
+ NULL_RTX);
+ }
conversion_insns = get_insns ();
did_conversion = 1;
end_sequence ();
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)
emit_move_insn (validize_mem (stack_parm),
validize_mem (entry_parm));
}
+ 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), ptr_mode,
+ GEN_INT (GET_MODE_SIZE (GET_MODE
+ (entry_parm))),
+ TYPE_MODE (sizetype),
+ GEN_INT (MEMORY_USE_RW),
+ TYPE_MODE (integer_type_node));
+ conversion_insns = get_insns ();
+ end_sequence ();
+ }
DECL_RTL (parm) = stack_parm;
}
tree restype = TREE_TYPE (result);
DECL_RTL (result)
- = gen_rtx (MEM, DECL_MODE (result), DECL_RTL (parm));
+ = 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))
now that all parameters have been copied out of hard registers. */
emit_insns (conversion_insns);
- max_parm_reg = max_reg_num ();
last_parm_insn = get_last_insn ();
current_function_args_size = stack_args_size.constant;
#endif
#endif
-#ifdef STACK_BOUNDARY
-#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
+#ifdef PREFERRED_STACK_BOUNDARY
+#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
current_function_args_size
= ((current_function_args_size + STACK_BYTES - 1)
= (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)),
- NULL_RTX, VOIDmode, 0));
+ NULL_RTX, VOIDmode, EXPAND_MEMORY_USE_BAD));
#else
current_function_arg_offset_rtx = ARGS_SIZE_RTX (stack_args_size);
#endif
= type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
enum direction where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
int boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type);
- int boundary_in_bytes = boundary / BITS_PER_UNIT;
- int reg_parm_stack_space = 0;
#ifdef REG_PARM_STACK_SPACE
/* If we have found a stack parm before we reach the end of the
area reserved for registers, skip that area. */
if (! in_regs)
{
+ int reg_parm_stack_space = 0;
+
#ifdef MAYBE_REG_PARM_STACK_SPACE
reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
#else
}
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);
}
}
+#ifndef ARGS_GROW_DOWNWARD
static void
pad_below (offset_ptr, passed_mode, sizetree)
struct args_size *offset_ptr;
}
}
}
+#endif
+#ifdef ARGS_GROW_DOWNWARD
static tree
round_down (value, divisor)
tree value;
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.
&& ! AGGREGATE_TYPE_P (TREE_TYPE (decl))
&& DECL_RTL (decl) != 0
&& 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
+ with a nonzero DECL_INITIAL had an initializer, so do not
+ claim it is potentially uninitialized.
+
+ We do not care about the actual value in DECL_INITIAL, so we do
+ not worry that it may be a dangling pointer. */
+ && DECL_INITIAL (decl) == NULL_TREE
&& regno_uninitialized (REGNO (DECL_RTL (decl))))
warning_with_decl (decl,
"`%s' might be used uninitialized in this function");
if ((TREE_CODE (decl) == VAR_DECL
|| TREE_CODE (decl) == PARM_DECL)
&& DECL_RTL (decl) != 0
- && GET_CODE (DECL_RTL (decl)) == REG
+ && (GET_CODE (DECL_RTL (decl)) == REG
+ || (GET_CODE (DECL_RTL (decl)) == MEM
+ && GET_CODE (XEXP (DECL_RTL (decl), 0)) == ADDRESSOF))
/* If this variable came from an inline function, it must be
- that it's life doesn't overlap the setjmp. If there was a
+ that its life doesn't overlap the setjmp. If there was a
setjmp in the function, it would already be in memory. We
must exclude such variable because their DECL_RTL might be
set to strange things such as virtual_stack_vars_rtx. */
void
setjmp_protect_args ()
{
- register tree decl, sub;
+ register tree decl;
for (decl = DECL_ARGUMENTS (current_function_decl);
decl; decl = TREE_CHAIN (decl))
if ((TREE_CODE (decl) == VAR_DECL
|| TREE_CODE (decl) == PARM_DECL)
&& DECL_RTL (decl) != 0
- && GET_CODE (DECL_RTL (decl)) == REG
+ && (GET_CODE (DECL_RTL (decl)) == REG
+ || (GET_CODE (DECL_RTL (decl)) == MEM
+ && GET_CODE (XEXP (DECL_RTL (decl), 0)) == ADDRESSOF))
&& (
/* If longjmp doesn't restore the registers,
don't put anything in them. */
tree var;
{
rtx basereg;
- int displacement;
+ HOST_WIDE_INT displacement;
tree context = decl_function_context (var);
struct function *fp;
rtx base = 0;
if (fp == 0)
abort ();
+ if (GET_CODE (addr) == ADDRESSOF && GET_CODE (XEXP (addr, 0)) == MEM)
+ addr = XEXP (XEXP (addr, 0), 0);
+
/* Decode given address as base reg plus displacement. */
if (GET_CODE (addr) == REG)
basereg = addr, displacement = 0;
addr = fix_lexical_addr (XEXP (fp->arg_pointer_save_area, 0), var);
addr = memory_address (Pmode, addr);
- base = copy_to_reg (gen_rtx (MEM, Pmode, addr));
+ base = copy_to_reg (gen_rtx_MEM (Pmode, addr));
#else
displacement += (FIRST_PARM_OFFSET (context) - STARTING_FRAME_OFFSET);
base = lookup_static_chain (var);
/* Find the `struct function' for the function containing FUNCTION. */
fp = 0;
fn_context = decl_function_context (function);
- if (fn_context != current_function_decl)
+ if (fn_context != current_function_decl
+ && fn_context != inline_function_decl)
for (fp = outer_function_chain; fp; fp = fp->next)
if (fp->decl == fn_context)
break;
}
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
{
- current_block_number = block_stack[--depth];
NOTE_BLOCK_NUMBER (insn) = current_block_number;
+ current_block_number = block_stack[--depth];
}
}
return n_blocks;
}
\f
-/* Build bytecode call descriptor for function SUBR. */
-
-rtx
-bc_build_calldesc (subr)
- tree subr;
-{
- tree calldesc = 0, arg;
- int nargs = 0;
-
- /* Build the argument description vector in reverse order. */
- DECL_ARGUMENTS (subr) = nreverse (DECL_ARGUMENTS (subr));
- nargs = 0;
-
- for (arg = DECL_ARGUMENTS (subr); arg; arg = TREE_CHAIN (arg))
- {
- ++nargs;
-
- calldesc = tree_cons ((tree) 0, size_in_bytes (TREE_TYPE (arg)), calldesc);
- calldesc = tree_cons ((tree) 0, bc_runtime_type_code (TREE_TYPE (arg)), calldesc);
- }
-
- DECL_ARGUMENTS (subr) = nreverse (DECL_ARGUMENTS (subr));
-
- /* Prepend the function's return type. */
- calldesc = tree_cons ((tree) 0,
- size_in_bytes (TREE_TYPE (TREE_TYPE (subr))),
- calldesc);
-
- calldesc = tree_cons ((tree) 0,
- bc_runtime_type_code (TREE_TYPE (TREE_TYPE (subr))),
- calldesc);
-
- /* Prepend the arg count. */
- calldesc = tree_cons ((tree) 0, build_int_2 (nargs, 0), calldesc);
-
- /* Output the call description vector and get its address. */
- calldesc = build_nt (CONSTRUCTOR, (tree) 0, calldesc);
- TREE_TYPE (calldesc) = build_array_type (integer_type_node,
- build_index_type (build_int_2 (nargs * 2, 0)));
-
- return output_constant_def (calldesc);
-}
-
-
/* 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. */
char *filename;
int line;
{
- if (output_bytecode)
- {
- this_function_decl = subr;
- this_function_calldesc = bc_build_calldesc (subr);
- local_vars_size = 0;
- stack_depth = 0;
- max_stack_depth = 0;
- stmt_expr_depth = 0;
- return;
- }
-
init_stmt_for_function ();
cse_not_expected = ! optimize;
stack_slot_list = 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. */
current_function_has_nonlocal_label = 0;
current_function_has_nonlocal_goto = 0;
current_function_contains_functions = 0;
+ current_function_sp_is_unchanging = 0;
+ current_function_has_computed_jump = 0;
+ current_function_is_thunk = 0;
current_function_returns_pcc_struct = 0;
current_function_returns_struct = 0;
current_function_epilogue_delay_list = 0;
current_function_uses_const_pool = 0;
current_function_uses_pic_offset_table = 0;
+ current_function_cannot_inline = 0;
/* We have not yet needed to make a label to jump to for tail-recursion. */
tail_recursion_label = 0;
current_function_outgoing_args_size = 0;
/* Prevent ever trying to delete the first instruction of a function.
- Also tell final how to output a linenum before the function prologue. */
- emit_line_note (filename, line);
+ 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. */
+ if (line > 0)
+ emit_line_note (filename, line);
/* Make sure first insn is a note even if we don't want linenums.
This makes sure the first insn will never be deleted.
void
expand_main_function ()
{
- if (!output_bytecode)
- {
- /* The zero below avoids a possible parse error */
- 0;
#if !defined (HAS_INIT_SECTION)
- emit_library_call (gen_rtx (SYMBOL_REF, Pmode, NAME__MAIN), 0,
- VOIDmode, 0);
+ emit_library_call (gen_rtx_SYMBOL_REF (Pmode, NAME__MAIN), 0,
+ VOIDmode, 0);
#endif /* not HAS_INIT_SECTION */
- }
}
\f
extern struct obstack permanent_obstack;
-/* Expand start of bytecode function. See comment at
- expand_function_start below for details. */
-
-void
-bc_expand_function_start (subr, parms_have_cleanups)
- tree subr;
- int parms_have_cleanups;
-{
- char label[20], *name;
- static int nlab;
- tree thisarg;
- int argsz;
-
- if (TREE_PUBLIC (subr))
- bc_globalize_label (IDENTIFIER_POINTER (DECL_NAME (subr)));
-
-#ifdef DEBUG_PRINT_CODE
- fprintf (stderr, "\n<func %s>\n", IDENTIFIER_POINTER (DECL_NAME (subr)));
-#endif
-
- for (argsz = 0, thisarg = DECL_ARGUMENTS (subr); thisarg; thisarg = TREE_CHAIN (thisarg))
- {
- if (DECL_RTL (thisarg))
- abort (); /* Should be NULL here I think. */
- else if (TREE_CONSTANT (DECL_SIZE (thisarg)))
- {
- DECL_RTL (thisarg) = bc_gen_rtx ((char *) 0, argsz, (struct bc_label *) 0);
- argsz += TREE_INT_CST_LOW (DECL_SIZE (thisarg));
- }
- else
- {
- /* Variable-sized objects are pointers to their storage. */
- DECL_RTL (thisarg) = bc_gen_rtx ((char *) 0, argsz, (struct bc_label *) 0);
- argsz += POINTER_SIZE;
- }
- }
-
- bc_begin_function (xstrdup (IDENTIFIER_POINTER (DECL_NAME (subr))));
-
- ASM_GENERATE_INTERNAL_LABEL (label, "LX", nlab);
-
- ++nlab;
- name = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label));
- this_function_callinfo = bc_gen_rtx (name, 0, (struct bc_label *) 0);
- this_function_bytecode =
- bc_emit_trampoline (BYTECODE_LABEL (this_function_callinfo));
-}
-
-
-/* Expand end of bytecode function. See details the comment of
- expand_function_end(), below. */
-
-void
-bc_expand_function_end ()
-{
- char *ptrconsts;
-
- expand_null_return ();
-
- /* Emit any fixup code. This must be done before the call to
- to BC_END_FUNCTION (), since that will cause the bytecode
- segment to be finished off and closed. */
-
- expand_fixups (NULL_RTX);
-
- ptrconsts = bc_end_function ();
-
- bc_align_const (2 /* INT_ALIGN */);
-
- /* If this changes also make sure to change bc-interp.h! */
-
- bc_emit_const_labeldef (BYTECODE_LABEL (this_function_callinfo));
- bc_emit_const ((char *) &max_stack_depth, sizeof max_stack_depth);
- bc_emit_const ((char *) &local_vars_size, sizeof local_vars_size);
- bc_emit_const_labelref (this_function_bytecode, 0);
- bc_emit_const_labelref (ptrconsts, 0);
- bc_emit_const_labelref (BYTECODE_LABEL (this_function_calldesc), 0);
-}
-
-
/* Start the RTL for a new function, and set variables used for
emitting RTL.
SUBR is the FUNCTION_DECL node.
{
register int i;
tree tem;
- rtx last_ptr;
-
- if (output_bytecode)
- {
- bc_expand_function_start (subr, parms_have_cleanups);
- return;
- }
+ rtx last_ptr = NULL_RTX;
/* Make sure volatile mem refs aren't considered
valid operands of arithmetic insns. */
init_recog_no_volatile ();
+ /* Set this before generating any memory accesses. */
+ current_function_check_memory_usage
+ = (flag_check_memory_usage
+ && ! DECL_NO_CHECK_MEMORY_USAGE (current_function_decl));
+
+ current_function_instrument_entry_exit
+ = (flag_instrument_function_entry_exit
+ && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr));
+
/* If function gets a static chain arg, store it in the stack frame.
Do this first, so it gets the first stack slot offset. */
if (current_function_needs_context)
{
last_ptr = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
-#ifdef SMALL_REGISTER_CLASSES
/* Delay copying static chain if it is not a register to avoid
conflicts with regs used for parameters. */
if (! SMALL_REGISTER_CLASSES
|| GET_CODE (static_chain_incoming_rtx) == REG)
-#endif
emit_move_insn (last_ptr, static_chain_incoming_rtx);
}
or if it returns a structure, or if it has parm cleanups. */
#ifdef HAVE_return
if (cleanup_label == 0 && HAVE_return
+ && ! current_function_instrument_entry_exit
&& ! current_function_returns_pcc_struct
&& ! (current_function_returns_struct && ! optimize))
return_label = 0;
if (value_address)
{
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)));
+ = 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))));
}
}
else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
/* If return mode is void, this decl rtl should not be used. */
DECL_RTL (DECL_RESULT (subr)) = 0;
- else if (parms_have_cleanups)
+ else if (parms_have_cleanups || current_function_instrument_entry_exit)
{
/* If function will end with cleanup code for parms,
compute the return values into a pseudo reg,
assign_parms (subr, 0);
-#ifdef SMALL_REGISTER_CLASSES
/* Copy the static chain now if it wasn't a register. The delay is to
avoid conflicts with the parameter passing registers. */
if (SMALL_REGISTER_CLASSES && current_function_needs_context)
if (GET_CODE (static_chain_incoming_rtx) != REG)
emit_move_insn (last_ptr, static_chain_incoming_rtx);
-#endif
/* The following was moved from init_function_start.
The move is supposed to make sdb output more accurate. */
generated above. */
if (tem && ! obey_regdecls)
{
-#ifdef SMALL_REGISTER_CLASSES
/* If the static chain originally came in a register, put it back
there, then move it out in the next insn. The reason for
this peculiar code is to satisfy function integration. */
if (SMALL_REGISTER_CLASSES
&& GET_CODE (static_chain_incoming_rtx) == REG)
emit_move_insn (static_chain_incoming_rtx, last_ptr);
-#endif
-
last_ptr = copy_to_reg (static_chain_incoming_rtx);
}
#ifdef FRAME_GROWS_DOWNWARD
last_ptr = plus_constant (last_ptr, - GET_MODE_SIZE (Pmode));
#endif
- last_ptr = copy_to_reg (gen_rtx (MEM, Pmode,
- memory_address (Pmode, last_ptr)));
+ last_ptr = copy_to_reg (gen_rtx_MEM (Pmode,
+ memory_address (Pmode, last_ptr)));
/* If we are not optimizing, ensure that we know that this
piece of context is live over the entire function. */
if (! optimize)
- save_expr_regs = gen_rtx (EXPR_LIST, VOIDmode, last_ptr,
- save_expr_regs);
+ save_expr_regs = gen_rtx_EXPR_LIST (VOIDmode, last_ptr,
+ save_expr_regs);
}
}
+ if (current_function_instrument_entry_exit)
+ {
+ rtx fun = DECL_RTL (current_function_decl);
+ if (GET_CODE (fun) == MEM)
+ fun = XEXP (fun, 0);
+ else
+ abort ();
+ emit_library_call (profile_function_entry_libfunc, 0, VOIDmode, 2,
+ fun, Pmode,
+ expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
+ 0,
+ hard_frame_pointer_rtx),
+ Pmode);
+ }
+
/* 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. */
/* Evaluate now the sizes of any types declared among the arguments. */
for (tem = nreverse (get_pending_sizes ()); tem; tem = TREE_CHAIN (tem))
- expand_expr (TREE_VALUE (tem), const0_rtx, VOIDmode, 0);
+ {
+ expand_expr (TREE_VALUE (tem), const0_rtx, VOIDmode,
+ EXPAND_MEMORY_USE_BAD);
+ /* Flush the queue in case this parameter declaration has
+ side-effects. */
+ emit_queue ();
+ }
/* Make sure there is a line number after the function entry setup code. */
force_next_line_note ();
static rtx initial_trampoline;
#endif
- if (output_bytecode)
- {
- bc_expand_function_end ();
- return;
- }
-
#ifdef NON_SAVING_SETJMP
/* Don't put any variables in registers if we call setjmp
on a machine that fails to restore the registers. */
/* 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. */
tree function = TREE_PURPOSE (link);
rtx context = lookup_static_chain (function);
rtx tramp = RTL_EXPR_RTL (TREE_VALUE (link));
+#ifdef TRAMPOLINE_TEMPLATE
rtx blktramp;
+#endif
rtx seq;
#ifdef TRAMPOLINE_TEMPLATE
{
end_temporary_allocation ();
initial_trampoline
- = gen_rtx (MEM, BLKmode, assemble_trampoline_template ());
+ = gen_rtx_MEM (BLKmode, assemble_trampoline_template ());
resume_temporary_allocation ();
}
#endif
emit_insns_before (seq, tail_recursion_reentry);
}
+ /* If we are doing stack checking and this function makes calls,
+ do a stack probe at the start of the function to ensure we have enough
+ space for another stack frame. */
+ if (flag_stack_check && ! STACK_CHECK_BUILTIN)
+ {
+ rtx insn, seq;
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ start_sequence ();
+ probe_stack_range (STACK_CHECK_PROTECT,
+ GEN_INT (STACK_CHECK_MAX_FRAME_SIZE));
+ seq = get_insns ();
+ end_sequence ();
+ emit_insns_before (seq, tail_recursion_reentry);
+ break;
+ }
+ }
+
/* Warn about unused parms if extra warnings were specified. */
if (warn_unused && extra_warnings)
{
}
/* 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. */
without returning a value. */
emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END);
+ /* Must mark the last line number note in the function, so that the test
+ coverage code can avoid counting the last line twice. This just tells
+ the code to ignore the immediately following line note, since there
+ 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);
+
/* Output a linenumber for the end of the function.
SDB depends on this. */
emit_line_note_force (filename, line);
if (end_bindings)
expand_end_bindings (0, 0, 0);
+ /* Now handle any leftover exception regions that may have been
+ created for the parameters. */
+ {
+ rtx last = get_last_insn ();
+ rtx label;
+
+ expand_leftover_cleanups ();
+
+ /* If the above emitted any code, may sure we jump around it. */
+ if (last != get_last_insn ())
+ {
+ label = gen_label_rtx ();
+ last = emit_jump_insn_after (gen_jump (label), last);
+ last = emit_barrier_after (last);
+ emit_label (label);
+ }
+ }
+
+ if (current_function_instrument_entry_exit)
+ {
+ rtx fun = DECL_RTL (current_function_decl);
+ if (GET_CODE (fun) == MEM)
+ fun = XEXP (fun, 0);
+ else
+ abort ();
+ emit_library_call (profile_function_exit_libfunc, 0, VOIDmode, 2,
+ fun, Pmode,
+ expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
+ 0,
+ hard_frame_pointer_rtx),
+ Pmode);
+ }
+
/* 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. */
GET_MODE (DECL_RTL (DECL_RESULT (current_function_decl))));
emit_move_insn (real_decl_result,
DECL_RTL (DECL_RESULT (current_function_decl)));
- emit_insn (gen_rtx (USE, VOIDmode, real_decl_result));
+ emit_insn (gen_rtx_USE (VOIDmode, real_decl_result));
+
+ /* 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;
}
/* If returning a structure, arrange to return the address of the value
use_variable (outgoing);
}
+ /* If this is an implementation of __throw, do what's necessary to
+ communicate between __builtin_eh_return and the epilogue. */
+ expand_eh_return ();
+
/* Output a return insn if we are using one.
Otherwise, let the rtl chain end here, to drop through
into the epilogue. */
/* Create an array that records the INSN_UIDs of INSNS (either a sequence
or a single insn). */
+#if defined (HAVE_prologue) || defined (HAVE_epilogue)
static int *
record_insns (insns)
rtx insns;
}
return 0;
}
+#endif /* HAVE_prologue || HAVE_epilogue */
/* Generate the prologue and epilogue RTL if the machine supports it. Thread
this into place with notes indicating where the prologue ends and where
void
thread_prologue_and_epilogue_insns (f)
- rtx f;
+ rtx f ATTRIBUTE_UNUSED;
{
#ifdef HAVE_prologue
if (HAVE_prologue)
{
- rtx head, seq, insn;
+ rtx head, seq;
/* The first insn (a NOTE_INSN_DELETED) is followed by zero or more
prologue insns and a NOTE_INSN_PROLOGUE_END. */
/* 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);
+ if (x_basic_block_head && n_basic_blocks
+ && GET_CODE (BLOCK_HEAD (0)) != CODE_LABEL)
+ BLOCK_HEAD (0) = NEXT_INSN (f);
/* Retain a map of the prologue insns. */
prologue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : head);
/* 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;
+ if (x_basic_block_end && n_basic_blocks
+ && GET_CODE (BLOCK_END (n_basic_blocks - 1)) != JUMP_INSN)
+ BLOCK_END (n_basic_blocks - 1) = tail;
/* Retain a map of the epilogue insns. */
epilogue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : tail);
void
reposition_prologue_and_epilogue_notes (f)
- rtx f;
+ rtx f ATTRIBUTE_UNUSED;
{
#if defined (HAVE_prologue) || defined (HAVE_epilogue)
/* 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)
{
- for (note = insn; note = NEXT_INSN (note);)
+ 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);
- prev = PREV_INSN (note);
- if (prev)
- NEXT_INSN (prev) = next;
- if (next)
- PREV_INSN (next) = prev;
+
+ /* 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);
}
}
move it to just before the first epilogue insn. */
if (note == 0)
{
- for (note = insn; note = PREV_INSN (note);)
+ for (note = insn; (note = PREV_INSN (note));)
if (GET_CODE (note) == NOTE
&& 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;
- add_insn_after (note, PREV_INSN (insn));
+
+ /* 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);
}
}
}