/* 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, 2004, 2005, 2006
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
/* This file handles the generation of rtl code from tree structure
at the level of the function as a whole.
#include "tree-gimple.h"
#include "tree-pass.h"
#include "predict.h"
+#include "df.h"
+#include "timevar.h"
#include "vecprim.h"
#ifndef LOCAL_ALIGNMENT
/* 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. */
+ pass_stack_ptr_mod has run. */
int current_function_sp_is_unchanging;
/* Nonzero if the function being compiled is a leaf function which only
/* 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, VEC(int,heap) **) ATTRIBUTE_UNUSED;
-static int contains (rtx, VEC(int,heap) **);
+static int contains (const_rtx, VEC(int,heap) **);
#ifdef HAVE_return
static void emit_return_into_block (basic_block);
#endif
#if defined(HAVE_epilogue) && defined(INCOMING_RETURN_ADDR_RTX)
static rtx keep_stack_depressed (rtx);
#endif
-static void prepare_function_start (tree);
+static void prepare_function_start (void);
static void do_clobber_return_reg (rtx, void *);
static void do_use_return_reg (rtx, void *);
static void set_insn_locators (rtx, int) ATTRIBUTE_UNUSED;
struct function *p;
if (cfun == 0)
- init_dummy_function_start ();
+ allocate_struct_function (NULL);
p = cfun;
p->outer = outer_function_chain;
lang_hooks.function.enter_nested (p);
- cfun = 0;
+ set_cfun (NULL);
}
void
{
struct function *p = outer_function_chain;
- cfun = p;
+ set_cfun (p);
outer_function_chain = p->outer;
current_function_decl = p->decl;
temp_slots_at_level (int level)
{
if (level >= (int) VEC_length (temp_slot_p, used_temp_slots))
- {
- size_t old_length = VEC_length (temp_slot_p, used_temp_slots);
- temp_slot_p *p;
-
- VEC_safe_grow (temp_slot_p, gc, used_temp_slots, level + 1);
- p = VEC_address (temp_slot_p, used_temp_slots);
- memset (&p[old_length], 0,
- sizeof (temp_slot_p) * (level + 1 - old_length));
- }
+ VEC_safe_grow_cleared (temp_slot_p, gc, used_temp_slots, level + 1);
return &(VEC_address (temp_slot_p, used_temp_slots)[level]);
}
if (type != 0)
{
MEM_VOLATILE_P (slot) = TYPE_VOLATILE (type);
- MEM_SET_IN_STRUCT_P (slot, AGGREGATE_TYPE_P (type));
+ MEM_SET_IN_STRUCT_P (slot, (AGGREGATE_TYPE_P (type)
+ || TREE_CODE (type) == COMPLEX_TYPE));
}
MEM_NOTRAP_P (slot) = 1;
`current_function_outgoing_args_size'. Nevertheless, we must allow
for it when allocating stack dynamic objects. */
-#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+#if defined(REG_PARM_STACK_SPACE)
#define STACK_DYNAMIC_OFFSET(FNDECL) \
((ACCUMULATE_OUTGOING_ARGS \
- ? (current_function_outgoing_args_size + REG_PARM_STACK_SPACE (FNDECL)) : 0)\
- + (STACK_POINTER_OFFSET)) \
-
+ ? (current_function_outgoing_args_size \
+ + (OUTGOING_REG_PARM_STACK_SPACE ? 0 : REG_PARM_STACK_SPACE (FNDECL))) \
+ : 0) + (STACK_POINTER_OFFSET))
#else
#define STACK_DYNAMIC_OFFSET(FNDECL) \
((ACCUMULATE_OUTGOING_ARGS ? current_function_outgoing_args_size : 0) \
instantiate_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
{
tree t = *tp;
- if (! EXPR_P (t))
+ if (! EXPR_P (t) && ! GIMPLE_STMT_P (t))
{
*walk_subtrees = 0;
if (DECL_P (t) && DECL_RTL_SET_P (t))
EXP may be a type node or an expression (whose type is tested). */
int
-aggregate_value_p (tree exp, tree fntype)
+aggregate_value_p (const_tree exp, const_tree fntype)
{
int i, regno, nregs;
rtx reg;
- tree type = (TYPE_P (exp)) ? exp : TREE_TYPE (exp);
+ const_tree type = (TYPE_P (exp)) ? exp : TREE_TYPE (exp);
/* DECL node associated with FNTYPE when relevant, which we might need to
check for by-invisible-reference returns, typically for CALL_EXPR input
EXPressions. */
- tree fndecl = NULL_TREE;
+ const_tree fndecl = NULL_TREE;
if (fntype)
switch (TREE_CODE (fntype))
should live on the local stack. */
bool
-use_register_for_decl (tree decl)
+use_register_for_decl (const_tree decl)
{
/* Honor volatile. */
if (TREE_SIDE_EFFECTS (decl))
struct args_size stack_args_size;
tree function_result_decl;
tree orig_fnargs;
- rtx conversion_insns;
+ rtx first_conversion_insn;
+ rtx last_conversion_insn;
HOST_WIDE_INT pretend_args_size;
HOST_WIDE_INT extra_pretend_bytes;
int reg_parm_stack_space;
{
rtx parmreg = gen_reg_rtx (data->nominal_mode);
- push_to_sequence (all->conversion_insns);
+ push_to_sequence2 (all->first_conversion_insn,
+ all->last_conversion_insn);
/* For values returned in multiple registers, handle possible
incompatible calls to emit_group_store.
emit_group_store (parmreg, entry_parm, data->nominal_type,
int_size_in_bytes (data->nominal_type));
- all->conversion_insns = get_insns ();
+ all->first_conversion_insn = get_insns ();
+ all->last_conversion_insn = get_last_insn ();
end_sequence ();
SET_DECL_RTL (parm, parmreg);
/* Handle values in multiple non-contiguous locations. */
if (GET_CODE (entry_parm) == PARALLEL)
{
- push_to_sequence (all->conversion_insns);
+ push_to_sequence2 (all->first_conversion_insn,
+ all->last_conversion_insn);
emit_group_store (mem, entry_parm, data->passed_type, size);
- all->conversion_insns = get_insns ();
+ all->first_conversion_insn = get_insns ();
+ all->last_conversion_insn = get_last_insn ();
end_sequence ();
}
#endif
)
{
- rtx reg = gen_rtx_REG (mode, REGNO (entry_parm));
+ rtx reg;
+
+ /* We are really truncating a word_mode value containing
+ SIZE bytes into a value of mode MODE. If such an
+ operation requires no actual instructions, we can refer
+ to the value directly in mode MODE, otherwise we must
+ start with the register in word_mode and explicitly
+ convert it. */
+ if (TRULY_NOOP_TRUNCATION (size * BITS_PER_UNIT, BITS_PER_WORD))
+ reg = gen_rtx_REG (mode, REGNO (entry_parm));
+ else
+ {
+ reg = gen_rtx_REG (word_mode, REGNO (entry_parm));
+ reg = convert_to_mode (mode, copy_to_reg (reg), 1);
+ }
emit_move_insn (change_address (mem, mode, 0), reg);
}
}
else if (data->stack_parm == 0)
{
- push_to_sequence (all->conversion_insns);
+ push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
emit_block_move (stack_parm, data->entry_parm, GEN_INT (size),
BLOCK_OP_NORMAL);
- all->conversion_insns = get_insns ();
+ all->first_conversion_insn = get_insns ();
+ all->last_conversion_insn = get_last_insn ();
end_sequence ();
}
emit_move_insn (tempreg, validize_mem (data->entry_parm));
- push_to_sequence (all->conversion_insns);
+ push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
tempreg = convert_to_mode (data->nominal_mode, tempreg, unsignedp);
if (GET_CODE (tempreg) == SUBREG
/* TREE_USED gets set erroneously during expand_assignment. */
save_tree_used = TREE_USED (parm);
- expand_assignment (parm, make_tree (data->nominal_type, tempreg));
+ expand_assignment (parm, make_tree (data->nominal_type, tempreg), false);
TREE_USED (parm) = save_tree_used;
- all->conversion_insns = get_insns ();
+ all->first_conversion_insn = get_insns ();
+ all->last_conversion_insn = get_last_insn ();
end_sequence ();
did_conversion = true;
rtx tempreg = gen_reg_rtx (GET_MODE (DECL_RTL (parm)));
int unsigned_p = TYPE_UNSIGNED (TREE_TYPE (parm));
- push_to_sequence (all->conversion_insns);
+ push_to_sequence2 (all->first_conversion_insn,
+ all->last_conversion_insn);
emit_move_insn (tempreg, DECL_RTL (parm));
tempreg = convert_to_mode (GET_MODE (parmreg), tempreg, unsigned_p);
emit_move_insn (parmreg, tempreg);
- all->conversion_insns = get_insns ();
+ all->first_conversion_insn = get_insns ();
+ all->last_conversion_insn = get_last_insn ();
end_sequence ();
did_conversion = true;
continue;
if (SET_DEST (set) == regno_reg_rtx [regnoi])
- REG_NOTES (sinsn)
- = gen_rtx_EXPR_LIST (REG_EQUIV, stacki,
- REG_NOTES (sinsn));
+ set_unique_reg_note (sinsn, REG_EQUIV, stacki);
else if (SET_DEST (set) == regno_reg_rtx [regnor])
- REG_NOTES (sinsn)
- = gen_rtx_EXPR_LIST (REG_EQUIV, stackr,
- REG_NOTES (sinsn));
+ set_unique_reg_note (sinsn, REG_EQUIV, stackr);
}
}
else if ((set = single_set (linsn)) != 0
&& SET_DEST (set) == parmreg)
- REG_NOTES (linsn)
- = gen_rtx_EXPR_LIST (REG_EQUIV,
- data->stack_parm, REG_NOTES (linsn));
+ set_unique_reg_note (linsn, REG_EQUIV, data->stack_parm);
}
/* For pointer data type, suggest pointer register. */
emit_move_insn (tempreg, validize_mem (data->entry_parm));
- push_to_sequence (all->conversion_insns);
+ push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
to_conversion = true;
data->entry_parm = convert_to_mode (data->nominal_mode, tempreg,
{
/* Use a block move to handle potentially misaligned entry_parm. */
if (!to_conversion)
- push_to_sequence (all->conversion_insns);
+ push_to_sequence2 (all->first_conversion_insn,
+ all->last_conversion_insn);
to_conversion = true;
emit_block_move (dest, src,
if (to_conversion)
{
- all->conversion_insns = get_insns ();
+ all->first_conversion_insn = get_insns ();
+ all->last_conversion_insn = get_last_insn ();
end_sequence ();
}
set_mem_attributes (tmp, parm, 1);
rmem = adjust_address_nv (tmp, inner, 0);
imem = adjust_address_nv (tmp, inner, GET_MODE_SIZE (inner));
- push_to_sequence (all->conversion_insns);
+ push_to_sequence2 (all->first_conversion_insn,
+ all->last_conversion_insn);
emit_move_insn (rmem, real);
emit_move_insn (imem, imag);
- all->conversion_insns = get_insns ();
+ all->first_conversion_insn = get_insns ();
+ all->last_conversion_insn = get_last_insn ();
end_sequence ();
}
else
/* Output all parameter conversion instructions (possibly including calls)
now that all parameters have been copied out of hard registers. */
- emit_insn (all.conversion_insns);
+ emit_insn (all.first_conversion_insn);
/* If we are receiving a struct value address as the first argument, set up
the RTL for the function result. As this might require code to convert
}
else
{
- tree ptr_type, addr, args;
+ tree ptr_type, addr;
ptr_type = build_pointer_type (type);
addr = create_tmp_var (ptr_type, get_name (parm));
DECL_IGNORED_P (addr) = 0;
local = build_fold_indirect_ref (addr);
- args = tree_cons (NULL, DECL_SIZE_UNIT (parm), NULL);
t = built_in_decls[BUILT_IN_ALLOCA];
- t = build_function_call_expr (t, args);
+ t = build_call_expr (t, 1, DECL_SIZE_UNIT (parm));
t = fold_convert (ptr_type, t);
- t = build2 (MODIFY_EXPR, void_type_node, addr, t);
+ t = build_gimple_modify_stmt (addr, t);
gimplify_and_add (t, &stmts);
}
- t = build2 (MODIFY_EXPR, void_type_node, local, parm);
+ t = build_gimple_modify_stmt (local, parm);
gimplify_and_add (t, &stmts);
SET_DECL_VALUE_EXPR (parm, local);
return stmts;
}
\f
-/* Indicate whether REGNO is an incoming argument to the current function
- that was promoted to a wider mode. If so, return the RTX for the
- register (to get its mode). PMODE and PUNSIGNEDP are set to the mode
- that REGNO is promoted from and whether the promotion was signed or
- unsigned. */
-
-rtx
-promoted_input_arg (unsigned int regno, enum machine_mode *pmode, int *punsignedp)
-{
- tree arg;
-
- for (arg = DECL_ARGUMENTS (current_function_decl); arg;
- arg = TREE_CHAIN (arg))
- if (REG_P (DECL_INCOMING_RTL (arg))
- && REGNO (DECL_INCOMING_RTL (arg)) == regno
- && TYPE_MODE (DECL_ARG_TYPE (arg)) == TYPE_MODE (TREE_TYPE (arg)))
- {
- enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg));
- int unsignedp = TYPE_UNSIGNED (TREE_TYPE (arg));
-
- mode = promote_mode (TREE_TYPE (arg), mode, &unsignedp, 1);
- if (mode == GET_MODE (DECL_INCOMING_RTL (arg))
- && mode != DECL_MODE (arg))
- {
- *pmode = DECL_MODE (arg);
- *punsignedp = unsignedp;
- return DECL_INCOMING_RTL (arg);
- }
- }
-
- return 0;
-}
-
-\f
/* Compute the size and offset from the start of the stacked arguments for a
parm passed in mode PASSED_MODE and with type TYPE.
}
}
\f
-/* Walk the tree of blocks describing the binding levels within a function
- and warn about variables the might be killed by setjmp or vfork.
- This is done after calling flow_analysis and before global_alloc
- clobbers the pseudo-regs to hard regs. */
-void
-setjmp_vars_warning (tree block)
+/* True if register REGNO was alive at a place where `setjmp' was
+ called and was set more than once or is an argument. Such regs may
+ be clobbered by `longjmp'. */
+
+static bool
+regno_clobbered_at_setjmp (bitmap setjmp_crosses, int regno)
+{
+ /* There appear to be cases where some local vars never reach the
+ backend but have bogus regnos. */
+ if (regno >= max_reg_num ())
+ return false;
+
+ return ((REG_N_SETS (regno) > 1
+ || REGNO_REG_SET_P (df_get_live_out (ENTRY_BLOCK_PTR), regno))
+ && REGNO_REG_SET_P (setjmp_crosses, regno));
+}
+
+/* Walk the tree of blocks describing the binding levels within a
+ function and warn about variables the might be killed by setjmp or
+ vfork. This is done after calling flow_analysis before register
+ allocation since that will clobber the pseudo-regs to hard
+ regs. */
+
+static void
+setjmp_vars_warning (bitmap setjmp_crosses, tree block)
{
tree decl, sub;
if (TREE_CODE (decl) == VAR_DECL
&& DECL_RTL_SET_P (decl)
&& REG_P (DECL_RTL (decl))
- && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
- warning (0, "variable %q+D might be clobbered by %<longjmp%>"
- " or %<vfork%>",
- decl);
+ && regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL (decl))))
+ warning (OPT_Wclobbered, "variable %q+D might be clobbered by"
+ " %<longjmp%> or %<vfork%>", decl);
}
for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
- setjmp_vars_warning (sub);
+ setjmp_vars_warning (setjmp_crosses, sub);
}
/* Do the appropriate part of setjmp_vars_warning
but for arguments instead of local variables. */
-void
-setjmp_args_warning (void)
+static void
+setjmp_args_warning (bitmap setjmp_crosses)
{
tree decl;
for (decl = DECL_ARGUMENTS (current_function_decl);
decl; decl = TREE_CHAIN (decl))
if (DECL_RTL (decl) != 0
&& REG_P (DECL_RTL (decl))
- && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
- warning (0, "argument %q+D might be clobbered by %<longjmp%> or %<vfork%>",
+ && regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL (decl))))
+ warning (OPT_Wclobbered,
+ "argument %q+D might be clobbered by %<longjmp%> or %<vfork%>",
decl);
}
+/* Generate warning messages for variables live across setjmp. */
+
+void
+generate_setjmp_warnings (void)
+{
+ bitmap setjmp_crosses = regstat_get_setjmp_crosses ();
+
+ if (n_basic_blocks == NUM_FIXED_BLOCKS
+ || bitmap_empty_p (setjmp_crosses))
+ return;
+
+ setjmp_vars_warning (setjmp_crosses, DECL_INITIAL (current_function_decl));
+ setjmp_args_warning (setjmp_crosses);
+}
+
\f
/* Identify BLOCKs referenced by more than one NOTE_INSN_BLOCK_{BEG,END},
and create duplicate blocks. */
{
if (NOTE_P (insn))
{
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
+ if (NOTE_KIND (insn) == NOTE_INSN_BLOCK_BEG)
{
tree block = NOTE_BLOCK (insn);
tree origin;
}
VEC_safe_push (tree, heap, *p_block_stack, block);
}
- else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ else if (NOTE_KIND (insn) == NOTE_INSN_BLOCK_END)
{
NOTE_BLOCK (insn) = VEC_pop (tree, *p_block_stack);
BLOCK_SUBBLOCKS (current_block)
return NULL_TREE;
}
\f
+/* Keep track of whether we're in a dummy function context. If we are,
+ we don't want to invoke the set_current_function hook, because we'll
+ get into trouble if the hook calls target_reinit () recursively or
+ when the initial initialization is not yet complete. */
+
+static bool in_dummy_function;
+
+/* Invoke the target hook when setting cfun. */
+
+static void
+invoke_set_current_function_hook (tree fndecl)
+{
+ if (!in_dummy_function)
+ targetm.set_current_function (fndecl);
+}
+
+/* cfun should never be set directly; use this function. */
+
+void
+set_cfun (struct function *new_cfun)
+{
+ if (cfun != new_cfun)
+ {
+ cfun = new_cfun;
+ invoke_set_current_function_hook (new_cfun ? new_cfun->decl : NULL_TREE);
+ }
+}
+
+/* Keep track of the cfun stack. */
+
+typedef struct function *function_p;
+
+DEF_VEC_P(function_p);
+DEF_VEC_ALLOC_P(function_p,heap);
+
+/* Initialized with NOGC, making this poisonous to the garbage collector. */
+
+static VEC(function_p,heap) *cfun_stack;
+
+/* Push the current cfun onto the stack, and set cfun to new_cfun. */
+
+void
+push_cfun (struct function *new_cfun)
+{
+ VEC_safe_push (function_p, heap, cfun_stack, cfun);
+ set_cfun (new_cfun);
+}
+
+/* Pop cfun from the stack. */
+
+void
+pop_cfun (void)
+{
+ set_cfun (VEC_pop (function_p, cfun_stack));
+}
+
+/* Return value of funcdef and increase it. */
+int
+get_next_funcdef_no (void)
+{
+ return funcdef_no++;
+}
+
/* Allocate a function structure for FNDECL and set its contents
- to the defaults. */
+ to the defaults. Set cfun to the newly-allocated object.
+ Some of the helper functions invoked during initialization assume
+ that cfun has already been set. Therefore, assign the new object
+ directly into cfun and invoke the back end hook explicitly at the
+ very end, rather than initializing a temporary and calling set_cfun
+ on it.
+*/
void
allocate_struct_function (tree fndecl)
cfun->stack_alignment_needed = STACK_BOUNDARY;
cfun->preferred_stack_boundary = STACK_BOUNDARY;
- current_function_funcdef_no = funcdef_no++;
+ current_function_funcdef_no = get_next_funcdef_no ();
cfun->function_frequency = FUNCTION_FREQUENCY_NORMAL;
if (init_machine_status)
cfun->machine = (*init_machine_status) ();
- if (fndecl == NULL)
- return;
-
- DECL_STRUCT_FUNCTION (fndecl) = cfun;
- cfun->decl = fndecl;
-
- result = DECL_RESULT (fndecl);
- if (aggregate_value_p (result, fndecl))
+ if (fndecl != NULL)
{
+ DECL_STRUCT_FUNCTION (fndecl) = cfun;
+ cfun->decl = fndecl;
+
+ result = DECL_RESULT (fndecl);
+ if (aggregate_value_p (result, fndecl))
+ {
#ifdef PCC_STATIC_STRUCT_RETURN
- current_function_returns_pcc_struct = 1;
+ current_function_returns_pcc_struct = 1;
#endif
- current_function_returns_struct = 1;
+ current_function_returns_struct = 1;
+ }
+
+ current_function_stdarg
+ = (fntype
+ && TYPE_ARG_TYPES (fntype) != 0
+ && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
+ != void_type_node));
+
+ /* Assume all registers in stdarg functions need to be saved. */
+ cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
+ cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
}
- current_function_returns_pointer = POINTER_TYPE_P (TREE_TYPE (result));
+ invoke_set_current_function_hook (fndecl);
+}
- current_function_stdarg
- = (fntype
- && TYPE_ARG_TYPES (fntype) != 0
- && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
- != void_type_node));
+/* This is like allocate_struct_function, but pushes a new cfun for FNDECL
+ instead of just setting it. */
- /* Assume all registers in stdarg functions need to be saved. */
- cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
- cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
+void
+push_struct_function (tree fndecl)
+{
+ VEC_safe_push (function_p, heap, cfun_stack, cfun);
+ allocate_struct_function (fndecl);
}
/* Reset cfun, and other non-struct-function variables to defaults as
appropriate for emitting rtl at the start of a function. */
static void
-prepare_function_start (tree fndecl)
+prepare_function_start (void)
{
- if (fndecl && DECL_STRUCT_FUNCTION (fndecl))
- cfun = DECL_STRUCT_FUNCTION (fndecl);
- else
- allocate_struct_function (fndecl);
init_emit ();
init_varasm_status (cfun);
init_expr ();
/* Initialize the rtl expansion mechanism so that we can do simple things
like generate sequences. This is used to provide a context during global
- initialization of some passes. */
+ initialization of some passes. You must call expand_dummy_function_end
+ to exit this context. */
+
void
init_dummy_function_start (void)
{
- prepare_function_start (NULL);
+ gcc_assert (!in_dummy_function);
+ in_dummy_function = true;
+ push_struct_function (NULL_TREE);
+ prepare_function_start ();
}
/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node)
void
init_function_start (tree subr)
{
- prepare_function_start (subr);
-
- /* Prevent ever trying to delete the first instruction of a
- function. Also tell final how to output a linenum before the
- function prologue. Note linenums could be missing, e.g. when
- compiling a Java .class file. */
- if (! DECL_IS_BUILTIN (subr))
- emit_line_note (DECL_SOURCE_LOCATION (subr));
-
- /* Make sure first insn is a note even if we don't want linenums.
- This makes sure the first insn will never be deleted.
- Also, final expects a note to appear there. */
- emit_note (NOTE_INSN_DELETED);
+ if (subr && DECL_STRUCT_FUNCTION (subr))
+ set_cfun (DECL_STRUCT_FUNCTION (subr));
+ else
+ allocate_struct_function (subr);
+ prepare_function_start ();
/* Warn if this value is an aggregate type,
regardless of which calling convention we are using for it. */
void
expand_dummy_function_end (void)
{
+ gcc_assert (in_dummy_function);
+
/* End any sequences that failed to be closed due to syntax errors. */
while (in_sequence_p ())
end_sequence ();
free_after_parsing (cfun);
free_after_compilation (cfun);
- cfun = 0;
+ pop_cfun ();
+ in_dummy_function = false;
}
/* Call DOIT for each hard register used as a return value from
for (decl = DECL_ARGUMENTS (fn);
decl; decl = TREE_CHAIN (decl))
if (!TREE_USED (decl) && TREE_CODE (decl) == PARM_DECL
- && DECL_NAME (decl) && !DECL_ARTIFICIAL (decl))
+ && DECL_NAME (decl) && !DECL_ARTIFICIAL (decl)
+ && !TREE_NO_WARNING (decl))
warning (OPT_Wunused_parameter, "unused parameter %q+D", decl);
}
}
}
- /* Possibly warn about unused parameters.
- When frontend does unit-at-a-time, the warning is already
- issued at finalization time. */
- if (warn_unused_parameter
- && !lang_hooks.callgraph.expand_function)
- do_warn_unused_parameter (current_function_decl);
-
/* End any sequences that failed to be closed due to syntax errors. */
while (in_sequence_p ())
end_sequence ();
/* Output a linenumber for the end of the function.
SDB depends on this. */
force_next_line_note ();
- emit_line_note (input_location);
+ set_curr_insn_source_location (input_location);
/* Before the return label (if any), clobber the return
registers so that they are not propagated live to the rest of
}
else
{
- /* @@@ 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. */
+ /* 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. */
if (flag_non_call_exceptions)
- emit_insn (gen_rtx_ASM_INPUT (VOIDmode, ""));
+ emit_insn (gen_blockage ());
}
/* If this is an implementation of throw, do what's necessary to
/* Output the label for the naked return from the function. */
emit_label (naked_return_label);
+ /* @@@ 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. */
+ if (! USING_SJLJ_EXCEPTIONS && flag_non_call_exceptions)
+ emit_insn (gen_blockage ());
+
/* If stack protection is enabled for this function, check the guard. */
if (cfun->stack_protect_guard)
stack_protect_epilogue ();
be running after reorg, SEQUENCE rtl is possible. */
static int
-contains (rtx insn, VEC(int,heap) **vec)
+contains (const_rtx insn, VEC(int,heap) **vec)
{
int i, j;
}
int
-prologue_epilogue_contains (rtx insn)
+prologue_epilogue_contains (const_rtx insn)
{
if (contains (insn, &prologue))
return 1;
}
int
-sibcall_epilogue_contains (rtx insn)
+sibcall_epilogue_contains (const_rtx insn)
{
if (sibcall_epilogue)
return contains (insn, &sibcall_epilogue);
};
static void handle_epilogue_set (rtx, struct epi_info *);
-static void update_epilogue_consts (rtx, rtx, void *);
+static void update_epilogue_consts (rtx, const_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
&& !fixed_regs[regno]
&& TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)
&& !REGNO_REG_SET_P
- (EXIT_BLOCK_PTR->il.rtl->global_live_at_start, regno)
+ (DF_LR_IN (EXIT_BLOCK_PTR), regno)
&& !refers_to_regno_p (regno,
- regno + hard_regno_nregs[regno]
- [Pmode],
+ end_hard_regno (Pmode, regno),
info.equiv_reg_src, NULL)
&& info.const_equiv[regno] == 0)
break;
/* Update the tracking information for registers set to constants. */
static void
-update_epilogue_consts (rtx dest, rtx x, void *data)
+update_epilogue_consts (rtx dest, const_rtx x, void *data)
{
struct epi_info *p = (struct epi_info *) data;
rtx new;
this into place with notes indicating where the prologue ends and where
the epilogue begins. Update the basic block information when possible. */
-void
-thread_prologue_and_epilogue_insns (rtx f ATTRIBUTE_UNUSED)
+static void
+thread_prologue_and_epilogue_insns (void)
{
int inserted = 0;
edge e;
#if defined (HAVE_sibcall_epilogue) || defined (HAVE_epilogue) || defined (HAVE_return) || defined (HAVE_prologue)
rtx seq;
#endif
-#ifdef HAVE_prologue
- rtx prologue_end = NULL_RTX;
-#endif
#if defined (HAVE_epilogue) || defined(HAVE_return)
rtx epilogue_end = NULL_RTX;
#endif
seq = gen_prologue ();
emit_insn (seq);
+ /* Insert an explicit USE for the frame pointer
+ if the profiling is on and the frame pointer is required. */
+ if (current_function_profile && frame_pointer_needed)
+ emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
+
/* Retain a map of the prologue insns. */
record_insns (seq, &prologue);
- prologue_end = emit_note (NOTE_INSN_PROLOGUE_END);
+ emit_note (NOTE_INSN_PROLOGUE_END);
+
+#ifndef PROFILE_BEFORE_PROLOGUE
+ /* Ensure that instructions are not moved into the prologue when
+ profiling is on. The call to the profiling routine can be
+ emitted within the live range of a call-clobbered register. */
+ if (current_function_profile)
+ emit_insn (gen_blockage ());
+#endif
seq = get_insns ();
end_sequence ();
epilogue_done:
if (inserted)
- commit_edge_insertions ();
+ {
+ commit_edge_insertions ();
+
+ /* The epilogue insns we inserted may cause the exit edge to no longer
+ be fallthru. */
+ FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
+ {
+ if (((e->flags & EDGE_FALLTHRU) != 0)
+ && returnjump_p (BB_END (e->src)))
+ e->flags &= ~EDGE_FALLTHRU;
+ }
+ }
#ifdef HAVE_sibcall_epilogue
/* Emit sibling epilogues before any sibling call sites. */
{
next = NEXT_INSN (insn);
if (NOTE_P (insn)
- && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG))
+ && (NOTE_KIND (insn) == NOTE_INSN_FUNCTION_BEG))
reorder_insns (insn, insn, PREV_INSN (epilogue_end));
}
}
#endif
+
+ /* Threading the prologue and epilogue changes the artificial refs
+ in the entry and exit blocks. */
+ epilogue_completed = 1;
+ df_update_entry_exit_and_calls ();
}
/* Reposition the prologue-end and epilogue-begin notes after instruction
scheduling and delayed branch scheduling. */
void
-reposition_prologue_and_epilogue_notes (rtx f ATTRIBUTE_UNUSED)
+reposition_prologue_and_epilogue_notes (void)
{
#if defined (HAVE_prologue) || defined (HAVE_epilogue)
rtx insn, last, note;
/* Scan from the beginning until we reach the last prologue insn.
We apparently can't depend on basic_block_{head,end} after
reorg has run. */
- for (insn = f; insn; insn = NEXT_INSN (insn))
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (NOTE_P (insn))
{
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END)
+ if (NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END)
note = insn;
}
else if (contains (insn, &prologue))
{
for (note = last; (note = NEXT_INSN (note));)
if (NOTE_P (note)
- && NOTE_LINE_NUMBER (note) == NOTE_INSN_PROLOGUE_END)
+ && NOTE_KIND (note) == NOTE_INSN_PROLOGUE_END)
break;
}
{
if (NOTE_P (insn))
{
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG)
+ if (NOTE_KIND (insn) == NOTE_INSN_EPILOGUE_BEG)
note = insn;
}
else if (contains (insn, &epilogue))
{
for (note = insn; (note = PREV_INSN (note));)
if (NOTE_P (note)
- && NOTE_LINE_NUMBER (note) == NOTE_INSN_EPILOGUE_BEG)
+ && NOTE_KIND (note) == NOTE_INSN_EPILOGUE_BEG)
break;
}
#endif /* HAVE_prologue or HAVE_epilogue */
}
-/* Resets insn_block_boundaries array. */
-
-void
-reset_block_changes (void)
-{
- cfun->ib_boundaries_block = VEC_alloc (tree, gc, 100);
- VEC_quick_push (tree, cfun->ib_boundaries_block, NULL_TREE);
-}
-
-/* Record the boundary for BLOCK. */
-void
-record_block_change (tree block)
-{
- int i, n;
- tree last_block;
-
- if (!block)
- return;
-
- if(!cfun->ib_boundaries_block)
- return;
-
- last_block = VEC_pop (tree, cfun->ib_boundaries_block);
- n = get_max_uid ();
- for (i = VEC_length (tree, cfun->ib_boundaries_block); i < n; i++)
- VEC_safe_push (tree, gc, cfun->ib_boundaries_block, last_block);
-
- VEC_safe_push (tree, gc, cfun->ib_boundaries_block, block);
-}
-
-/* Finishes record of boundaries. */
-void
-finalize_block_changes (void)
-{
- record_block_change (DECL_INITIAL (current_function_decl));
-}
-
-/* For INSN return the BLOCK it belongs to. */
-void
-check_block_change (rtx insn, tree *block)
-{
- unsigned uid = INSN_UID (insn);
-
- if (uid >= VEC_length (tree, cfun->ib_boundaries_block))
- return;
-
- *block = VEC_index (tree, cfun->ib_boundaries_block, uid);
-}
-
-/* Releases the ib_boundaries_block records. */
-void
-free_block_changes (void)
-{
- VEC_free (tree, gc, cfun->ib_boundaries_block);
-}
-
/* Returns the name of the current function. */
const char *
current_function_name (void)
{
return lang_hooks.decl_printable_name (cfun->decl, 2);
}
+
+/* Returns the raw (mangled) name of the current function. */
+const char *
+current_function_assembler_name (void)
+{
+ return IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (cfun->decl));
+}
\f
static unsigned int
0 /* letter */
};
+static unsigned int
+rest_of_handle_thread_prologue_and_epilogue (void)
+{
+ if (optimize)
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+ /* On some machines, the prologue and epilogue code, or parts thereof,
+ can be represented as RTL. Doing so lets us schedule insns between
+ it and the rest of the code and also allows delayed branch
+ scheduling to operate in the epilogue. */
+
+ thread_prologue_and_epilogue_insns ();
+ return 0;
+}
+
+struct tree_opt_pass pass_thread_prologue_and_epilogue =
+{
+ "pro_and_epilogue", /* name */
+ NULL, /* gate */
+ rest_of_handle_thread_prologue_and_epilogue, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_THREAD_PROLOGUE_AND_EPILOGUE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ TODO_verify_flow, /* todo_flags_start */
+ TODO_dump_func |
+ TODO_df_verify |
+ TODO_df_finish | TODO_verify_rtl_sharing |
+ TODO_ggc_collect, /* todo_flags_finish */
+ 'w' /* letter */
+};
+\f
+
+/* This mini-pass fixes fall-out from SSA in asm statements that have
+ in-out constraints. Say you start with
+
+ orig = inout;
+ asm ("": "+mr" (inout));
+ use (orig);
+
+ which is transformed very early to use explicit output and match operands:
+
+ orig = inout;
+ asm ("": "=mr" (inout) : "0" (inout));
+ use (orig);
+
+ Or, after SSA and copyprop,
+
+ asm ("": "=mr" (inout_2) : "0" (inout_1));
+ use (inout_1);
+
+ Clearly inout_2 and inout_1 can't be coalesced easily anymore, as
+ they represent two separate values, so they will get different pseudo
+ registers during expansion. Then, since the two operands need to match
+ per the constraints, but use different pseudo registers, reload can
+ only register a reload for these operands. But reloads can only be
+ satisfied by hardregs, not by memory, so we need a register for this
+ reload, just because we are presented with non-matching operands.
+ So, even though we allow memory for this operand, no memory can be
+ used for it, just because the two operands don't match. This can
+ cause reload failures on register-starved targets.
+
+ So it's a symptom of reload not being able to use memory for reloads
+ or, alternatively it's also a symptom of both operands not coming into
+ reload as matching (in which case the pseudo could go to memory just
+ fine, as the alternative allows it, and no reload would be necessary).
+ We fix the latter problem here, by transforming
+
+ asm ("": "=mr" (inout_2) : "0" (inout_1));
+
+ back to
+
+ inout_2 = inout_1;
+ asm ("": "=mr" (inout_2) : "0" (inout_2)); */
+
+static void
+match_asm_constraints_1 (rtx insn, rtx *p_sets, int noutputs)
+{
+ int i;
+ bool changed = false;
+ rtx op = SET_SRC (p_sets[0]);
+ int ninputs = ASM_OPERANDS_INPUT_LENGTH (op);
+ rtvec inputs = ASM_OPERANDS_INPUT_VEC (op);
+
+ for (i = 0; i < ninputs; i++)
+ {
+ rtx input, output, insns;
+ const char *constraint = ASM_OPERANDS_INPUT_CONSTRAINT (op, i);
+ char *end;
+ int match, j;
+
+ match = strtoul (constraint, &end, 10);
+ if (end == constraint)
+ continue;
+
+ gcc_assert (match < noutputs);
+ output = SET_DEST (p_sets[match]);
+ input = RTVEC_ELT (inputs, i);
+ /* Only do the transformation for pseudos. */
+ if (! REG_P (output)
+ || rtx_equal_p (output, input)
+ || (GET_MODE (input) != VOIDmode
+ && GET_MODE (input) != GET_MODE (output)))
+ continue;
+
+ /* We can't do anything if the output is also used as input,
+ as we're going to overwrite it. */
+ for (j = 0; j < ninputs; j++)
+ if (reg_overlap_mentioned_p (output, RTVEC_ELT (inputs, j)))
+ break;
+ if (j != ninputs)
+ continue;
+
+ start_sequence ();
+ emit_move_insn (output, input);
+ insns = get_insns ();
+ end_sequence ();
+ emit_insn_before (insns, insn);
+
+ /* Now replace all mentions of the input with output. We can't
+ just replace the occurence in inputs[i], as the register might
+ also be used in some other input (or even in an address of an
+ output), which would mean possibly increasing the number of
+ inputs by one (namely 'output' in addition), which might pose
+ a too complicated problem for reload to solve. E.g. this situation:
+
+ asm ("" : "=r" (output), "=m" (input) : "0" (input))
+
+ Here 'input' is used in two occurrences as input (once for the
+ input operand, once for the address in the second output operand).
+ If we would replace only the occurence of the input operand (to
+ make the matching) we would be left with this:
+
+ output = input
+ asm ("" : "=r" (output), "=m" (input) : "0" (output))
+
+ Now we suddenly have two different input values (containing the same
+ value, but different pseudos) where we formerly had only one.
+ With more complicated asms this might lead to reload failures
+ which wouldn't have happen without this pass. So, iterate over
+ all operands and replace all occurrences of the register used. */
+ for (j = 0; j < noutputs; j++)
+ if (!rtx_equal_p (SET_DEST (p_sets[j]), input)
+ && reg_overlap_mentioned_p (input, SET_DEST (p_sets[j])))
+ SET_DEST (p_sets[j]) = replace_rtx (SET_DEST (p_sets[j]),
+ input, output);
+ for (j = 0; j < ninputs; j++)
+ if (reg_overlap_mentioned_p (input, RTVEC_ELT (inputs, j)))
+ RTVEC_ELT (inputs, j) = replace_rtx (RTVEC_ELT (inputs, j),
+ input, output);
+
+ changed = true;
+ }
+
+ if (changed)
+ df_insn_rescan (insn);
+}
+
+static unsigned
+rest_of_match_asm_constraints (void)
+{
+ basic_block bb;
+ rtx insn, pat, *p_sets;
+ int noutputs;
+
+ if (!cfun->has_asm_statement)
+ return 0;
+
+ df_set_flags (DF_DEFER_INSN_RESCAN);
+ FOR_EACH_BB (bb)
+ {
+ FOR_BB_INSNS (bb, insn)
+ {
+ if (!INSN_P (insn))
+ continue;
+
+ pat = PATTERN (insn);
+ if (GET_CODE (pat) == PARALLEL)
+ p_sets = &XVECEXP (pat, 0, 0), noutputs = XVECLEN (pat, 0);
+ else if (GET_CODE (pat) == SET)
+ p_sets = &PATTERN (insn), noutputs = 1;
+ else
+ continue;
+
+ if (GET_CODE (*p_sets) == SET
+ && GET_CODE (SET_SRC (*p_sets)) == ASM_OPERANDS)
+ match_asm_constraints_1 (insn, p_sets, noutputs);
+ }
+ }
+
+ return TODO_df_finish;
+}
+
+struct tree_opt_pass pass_match_asm_constraints =
+{
+ "asmcons", /* name */
+ NULL, /* gate */
+ rest_of_match_asm_constraints, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func, /* todo_flags_finish */
+ 0 /* letter */
+};
+
#include "gt-function.h"