#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
`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) \
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 ();
}
}
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;
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
}
}
\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))))
+ && 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))))
+ && 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)
{
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);
-
/* Warn if this value is an aggregate type,
regardless of which calling convention we are using for it. */
if (AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr))))
/* 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 ();
&& !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;
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 ();
{
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_finish |
+ 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;
+
+ match = strtoul (constraint, &end, 10);
+ if (end == constraint)
+ continue;
+
+ gcc_assert (match < noutputs);
+ output = SET_DEST (p_sets[match]);
+ input = RTVEC_ELT (inputs, i);
+ if (rtx_equal_p (output, input)
+ || (GET_MODE (input) != VOIDmode
+ && GET_MODE (input) != GET_MODE (output)))
+ continue;
+
+ start_sequence ();
+ emit_move_insn (copy_rtx (output), input);
+ RTVEC_ELT (inputs, i) = copy_rtx (output);
+ insns = get_insns ();
+ end_sequence ();
+
+ emit_insn_before (insns, insn);
+ 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"