/* Expands front end tree to back end RTL for GCC.
- Copyright (C) 1987-2015 Free Software Foundation, Inc.
+ Copyright (C) 1987-2016 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "cfghooks.h"
+#include "df.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "stringpool.h"
+#include "expmed.h"
+#include "optabs.h"
+#include "regs.h"
+#include "emit-rtl.h"
+#include "recog.h"
#include "rtl-error.h"
#include "alias.h"
-#include "symtab.h"
-#include "tree.h"
#include "fold-const.h"
#include "stor-layout.h"
#include "varasm.h"
-#include "stringpool.h"
-#include "flags.h"
#include "except.h"
-#include "hard-reg-set.h"
-#include "function.h"
-#include "rtl.h"
-#include "insn-config.h"
-#include "expmed.h"
#include "dojump.h"
#include "explow.h"
#include "calls.h"
-#include "emit-rtl.h"
-#include "stmt.h"
#include "expr.h"
-#include "insn-codes.h"
-#include "optabs.h"
-#include "libfuncs.h"
-#include "regs.h"
-#include "recog.h"
+#include "optabs-tree.h"
#include "output.h"
-#include "tm_p.h"
#include "langhooks.h"
-#include "target.h"
#include "common/common-target.h"
-#include "gimple-expr.h"
#include "gimplify.h"
#include "tree-pass.h"
-#include "predict.h"
-#include "dominance.h"
-#include "cfg.h"
#include "cfgrtl.h"
#include "cfganal.h"
#include "cfgbuild.h"
#include "cfgcleanup.h"
-#include "basic-block.h"
-#include "df.h"
-#include "params.h"
-#include "bb-reorder.h"
+#include "cfgexpand.h"
#include "shrink-wrap.h"
#include "toplev.h"
#include "rtl-iter.h"
#include "tree-chkp.h"
#include "rtl-chkp.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
/* So we can assign to cfun in this file. */
#undef cfun
static void prepare_function_start (void);
static void do_clobber_return_reg (rtx, void *);
static void do_use_return_reg (rtx, void *);
+
\f
/* Stack of nested functions. */
/* Keep track of the cfun stack. */
-typedef struct function *function_p;
-
-static vec<function_p> function_context_stack;
+static vec<function *> function_context_stack;
/* Save the current context for compilation of a nested function.
This is called from language-specific code. */
f->eh = NULL;
f->machine = NULL;
f->cfg = NULL;
+ f->curr_properties &= ~PROP_cfg;
regno_reg_rtx = NULL;
}
{
unsigned HOST_WIDE_INT size = FRAME_GROWS_DOWNWARD ? -offset : offset;
- if (size > ((unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (Pmode) - 1))
+ if (size > (HOST_WIDE_INT_1U << (GET_MODE_BITSIZE (Pmode) - 1))
/* Leave room for the fixed part of the frame. */
- 64 * UNITS_PER_WORD)
{
set_mem_align (x, alignment_in_bits);
MEM_NOTRAP_P (x) = 1;
- stack_slot_list
- = gen_rtx_EXPR_LIST (VOIDmode, x, stack_slot_list);
+ vec_safe_push (stack_slot_list, x);
if (frame_offset_overflow (frame_offset, current_function_decl))
frame_offset = 0;
p->type = best_p->type;
insert_slot_to_list (p, &avail_temp_slots);
- stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, p->slot,
- stack_slot_list);
+ vec_safe_push (stack_slot_list, p->slot);
best_p->size = rounded_size;
best_p->full_size = rounded_size;
/* Create a new MEM rtx to avoid clobbering MEM flags of old slots. */
slot = gen_rtx_MEM (mode, XEXP (p->slot, 0));
- stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, slot, stack_slot_list);
+ vec_safe_push (stack_slot_list, slot);
/* If we know the alias set for the memory that will be used, use
it. If there's no TYPE, then we don't know anything about the
unsignedp = TYPE_UNSIGNED (type);
#endif
+ /* Allocating temporaries of TREE_ADDRESSABLE type must be done in the front
+ end. See also create_tmp_var for the gimplification-time check. */
+ gcc_assert (!TREE_ADDRESSABLE (type) && COMPLETE_TYPE_P (type));
+
if (mode == BLKmode || memory_required)
{
HOST_WIDE_INT size = int_size_in_bytes (type);
/* Private type used by get_hard_reg_initial_reg, get_hard_reg_initial_val,
and has_hard_reg_initial_val.. */
-typedef struct GTY(()) initial_value_pair {
+struct GTY(()) initial_value_pair {
rtx hard_reg;
rtx pseudo;
-} initial_value_pair;
+};
/* ??? This could be a VEC but there is currently no way to define an
opaque VEC type. This could be worked around by defining struct
initial_value_pair in function.h. */
-typedef struct GTY(()) initial_value_struct {
+struct GTY(()) initial_value_struct {
int num_entries;
int max_entries;
initial_value_pair * GTY ((length ("%h.num_entries"))) entries;
-} initial_value_struct;
+};
/* If a pseudo represents an initial hard reg (or expression), return
it, else return NULL_RTX. */
if (TREE_CODE (t) == PARM_DECL && DECL_NAMELESS (t)
&& DECL_INCOMING_RTL (t))
instantiate_decl_rtl (DECL_INCOMING_RTL (t));
- if ((TREE_CODE (t) == VAR_DECL
- || TREE_CODE (t) == RESULT_DECL)
+ if ((VAR_P (t) || TREE_CODE (t) == RESULT_DECL)
&& DECL_HAS_VALUE_EXPR_P (t))
{
tree v = DECL_VALUE_EXPR (t);
{
if (DECL_RTL_SET_P (t))
instantiate_decl_rtl (DECL_RTL (t));
- if (TREE_CODE (t) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (t))
+ if (VAR_P (t) && DECL_HAS_VALUE_EXPR_P (t))
{
tree v = DECL_VALUE_EXPR (t);
walk_tree (&v, instantiate_expr, NULL, NULL);
bool
use_register_for_decl (const_tree decl)
{
- if (!targetm.calls.allocate_stack_slots_for_args ())
- return true;
+ if (TREE_CODE (decl) == SSA_NAME)
+ {
+ /* We often try to use the SSA_NAME, instead of its underlying
+ decl, to get type information and guide decisions, to avoid
+ differences of behavior between anonymous and named
+ variables, but in this one case we have to go for the actual
+ variable if there is one. The main reason is that, at least
+ at -O0, we want to place user variables on the stack, but we
+ don't mind using pseudos for anonymous or ignored temps.
+ Should we take the SSA_NAME, we'd conclude all SSA_NAMEs
+ should go in pseudos, whereas their corresponding variables
+ might have to go on the stack. So, disregarding the decl
+ here would negatively impact debug info at -O0, enable
+ coalescing between SSA_NAMEs that ought to get different
+ stack/pseudo assignments, and get the incoming argument
+ processing thoroughly confused by PARM_DECLs expected to live
+ in stack slots but assigned to pseudos. */
+ if (!SSA_NAME_VAR (decl))
+ return TYPE_MODE (TREE_TYPE (decl)) != BLKmode
+ && !(flag_float_store && FLOAT_TYPE_P (TREE_TYPE (decl)));
+
+ decl = SSA_NAME_VAR (decl);
+ }
/* Honor volatile. */
if (TREE_SIDE_EFFECTS (decl))
if (TREE_ADDRESSABLE (decl))
return false;
+ /* RESULT_DECLs are a bit special in that they're assigned without
+ regard to use_register_for_decl, but we generally only store in
+ them. If we coalesce their SSA NAMEs, we'd better return a
+ result that matches the assignment in expand_function_start. */
+ if (TREE_CODE (decl) == RESULT_DECL)
+ {
+ /* If it's not an aggregate, we're going to use a REG or a
+ PARALLEL containing a REG. */
+ if (!aggregate_value_p (decl, current_function_decl))
+ return true;
+
+ /* If expand_function_start determines the return value, we'll
+ use MEM if it's not by reference. */
+ if (cfun->returns_pcc_struct
+ || (targetm.calls.struct_value_rtx
+ (TREE_TYPE (current_function_decl), 1)))
+ return DECL_BY_REFERENCE (decl);
+
+ /* Otherwise, we're taking an extra all.function_result_decl
+ argument. It's set up in assign_parms_augmented_arg_list,
+ under the (negated) conditions above, and then it's used to
+ set up the RESULT_DECL rtl in assign_params, after looping
+ over all parameters. Now, if the RESULT_DECL is not by
+ reference, we'll use a MEM either way. */
+ if (!DECL_BY_REFERENCE (decl))
+ return false;
+
+ /* Otherwise, if RESULT_DECL is DECL_BY_REFERENCE, it will take
+ the function_result_decl's assignment. Since it's a pointer,
+ we can short-circuit a number of the tests below, and we must
+ duplicat e them because we don't have the
+ function_result_decl to test. */
+ if (!targetm.calls.allocate_stack_slots_for_args ())
+ return true;
+ /* We don't set DECL_IGNORED_P for the function_result_decl. */
+ if (optimize)
+ return true;
+ /* We don't set DECL_REGISTER for the function_result_decl. */
+ return false;
+ }
+
/* Decl is implicitly addressible by bound stores and loads
if it is an aggregate holding bounds. */
if (chkp_function_instrumented_p (current_function_decl)
if (flag_float_store && FLOAT_TYPE_P (TREE_TYPE (decl)))
return false;
+ if (!targetm.calls.allocate_stack_slots_for_args ())
+ return true;
+
/* If we're not interested in tracking debugging information for
this decl, then we can certainly put it in a register. */
if (DECL_IGNORED_P (decl))
DECL_ARTIFICIAL (decl) = 1;
DECL_NAMELESS (decl) = 1;
TREE_CONSTANT (decl) = 1;
+ /* We don't set DECL_IGNORED_P or DECL_REGISTER here. If this
+ changes, the end of the RESULT_DECL handling block in
+ use_register_for_decl must be adjusted to match. */
DECL_CHAIN (decl) = all->orig_fnargs;
all->orig_fnargs = decl;
else if (CONST_INT_P (offset_rtx))
{
align = INTVAL (offset_rtx) * BITS_PER_UNIT | boundary;
- align = align & -align;
+ align = least_bit_hwi (align);
}
set_mem_align (stack_parm, align);
{
rtx entry_parm = data->entry_parm;
rtx stack_parm = data->stack_parm;
+ rtx target_reg = NULL_RTX;
+ bool in_conversion_seq = false;
HOST_WIDE_INT size;
HOST_WIDE_INT size_stored;
if (GET_CODE (entry_parm) == PARALLEL)
entry_parm = emit_group_move_into_temps (entry_parm);
+ /* If we want the parameter in a pseudo, don't use a stack slot. */
+ if (is_gimple_reg (parm) && use_register_for_decl (parm))
+ {
+ tree def = ssa_default_def (cfun, parm);
+ gcc_assert (def);
+ machine_mode mode = promote_ssa_mode (def, NULL);
+ rtx reg = gen_reg_rtx (mode);
+ if (GET_CODE (reg) != CONCAT)
+ stack_parm = reg;
+ else
+ {
+ target_reg = reg;
+ /* Avoid allocating a stack slot, if there isn't one
+ preallocated by the ABI. It might seem like we should
+ always prefer a pseudo, but converting between
+ floating-point and integer modes goes through the stack
+ on various machines, so it's better to use the reserved
+ stack slot than to risk wasting it and allocating more
+ for the conversion. */
+ if (stack_parm == NULL_RTX)
+ {
+ int save = generating_concat_p;
+ generating_concat_p = 0;
+ stack_parm = gen_reg_rtx (mode);
+ generating_concat_p = save;
+ }
+ }
+ data->stack_parm = NULL;
+ }
+
size = int_size_in_bytes (data->passed_type);
size_stored = CEIL_ROUND (size, UNITS_PER_WORD);
if (stack_parm == 0)
{
- DECL_ALIGN (parm) = MAX (DECL_ALIGN (parm), BITS_PER_WORD);
+ SET_DECL_ALIGN (parm, MAX (DECL_ALIGN (parm), BITS_PER_WORD));
stack_parm = assign_stack_local (BLKmode, size_stored,
DECL_ALIGN (parm));
if (GET_MODE_SIZE (GET_MODE (entry_parm)) == size)
mem = validize_mem (copy_rtx (stack_parm));
/* Handle values in multiple non-contiguous locations. */
- if (GET_CODE (entry_parm) == PARALLEL)
+ if (GET_CODE (entry_parm) == PARALLEL && !MEM_P (mem))
+ emit_group_store (mem, entry_parm, data->passed_type, size);
+ else if (GET_CODE (entry_parm) == PARALLEL)
{
push_to_sequence2 (all->first_conversion_insn,
all->last_conversion_insn);
all->first_conversion_insn = get_insns ();
all->last_conversion_insn = get_last_insn ();
end_sequence ();
+ in_conversion_seq = true;
}
else if (size == 0)
emit_move_insn (change_address (mem, mode, 0), reg);
}
+#ifdef BLOCK_REG_PADDING
+ /* Storing the register in memory as a full word, as
+ move_block_from_reg below would do, and then using the
+ MEM in a smaller mode, has the effect of shifting right
+ if BYTES_BIG_ENDIAN. If we're bypassing memory, the
+ shifting must be explicit. */
+ else if (!MEM_P (mem))
+ {
+ rtx x;
+
+ /* If the assert below fails, we should have taken the
+ mode != BLKmode path above, unless we have downward
+ padding of smaller-than-word arguments on a machine
+ with little-endian bytes, which would likely require
+ additional changes to work correctly. */
+ gcc_checking_assert (BYTES_BIG_ENDIAN
+ && (BLOCK_REG_PADDING (mode,
+ data->passed_type, 1)
+ == upward));
+
+ int by = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
+
+ x = gen_rtx_REG (word_mode, REGNO (entry_parm));
+ x = expand_shift (RSHIFT_EXPR, word_mode, x, by,
+ NULL_RTX, 1);
+ x = force_reg (word_mode, x);
+ x = gen_lowpart_SUBREG (GET_MODE (mem), x);
+
+ emit_move_insn (mem, x);
+ }
+#endif
+
/* Blocks smaller than a word on a BYTES_BIG_ENDIAN
machine must be aligned to the left before storing
to memory. Note that the previous test doesn't
move_block_from_reg (REGNO (entry_parm), mem,
size_stored / UNITS_PER_WORD);
}
+ else if (!MEM_P (mem))
+ {
+ gcc_checking_assert (size > UNITS_PER_WORD);
+#ifdef BLOCK_REG_PADDING
+ gcc_checking_assert (BLOCK_REG_PADDING (GET_MODE (mem),
+ data->passed_type, 0)
+ == upward);
+#endif
+ emit_move_insn (mem, entry_parm);
+ }
else
move_block_from_reg (REGNO (entry_parm), mem,
size_stored / UNITS_PER_WORD);
all->first_conversion_insn = get_insns ();
all->last_conversion_insn = get_last_insn ();
end_sequence ();
+ in_conversion_seq = true;
+ }
+
+ if (target_reg)
+ {
+ if (!in_conversion_seq)
+ emit_move_insn (target_reg, stack_parm);
+ else
+ {
+ push_to_sequence2 (all->first_conversion_insn,
+ all->last_conversion_insn);
+ emit_move_insn (target_reg, stack_parm);
+ all->first_conversion_insn = get_insns ();
+ all->last_conversion_insn = get_last_insn ();
+ end_sequence ();
+ }
+ stack_parm = target_reg;
}
data->stack_parm = stack_parm;
- SET_DECL_RTL (parm, stack_parm);
+ set_parm_rtl (parm, stack_parm);
}
/* A subroutine of assign_parms. Allocate a pseudo to hold the current
int unsignedp = TYPE_UNSIGNED (TREE_TYPE (parm));
bool did_conversion = false;
bool need_conversion, moved;
+ rtx rtl;
/* Store the parm in a pseudoregister during the function, but we may
need to do it in a wider mode. Using 2 here makes the result
TREE_TYPE (current_function_decl), 2);
parmreg = gen_reg_rtx (promoted_nominal_mode);
-
if (!DECL_ARTIFICIAL (parm))
mark_user_reg (parmreg);
/* If this was an item that we received a pointer to,
- set DECL_RTL appropriately. */
+ set rtl appropriately. */
if (data->passed_pointer)
{
- rtx x = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data->passed_type)), parmreg);
- set_mem_attributes (x, parm, 1);
- SET_DECL_RTL (parm, x);
+ rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data->passed_type)), parmreg);
+ set_mem_attributes (rtl, parm, 1);
}
else
- SET_DECL_RTL (parm, parmreg);
+ rtl = parmreg;
assign_parm_remove_parallels (data);
/* TREE_USED gets set erroneously during expand_assignment. */
save_tree_used = TREE_USED (parm);
+ SET_DECL_RTL (parm, rtl);
expand_assignment (parm, make_tree (data->nominal_type, tempreg), false);
+ SET_DECL_RTL (parm, NULL_RTX);
TREE_USED (parm) = save_tree_used;
all->first_conversion_insn = get_insns ();
all->last_conversion_insn = get_last_insn ();
set_mem_attributes (parmreg, parm, 1);
}
- if (GET_MODE (parmreg) != GET_MODE (DECL_RTL (parm)))
+ /* We need to preserve an address based on VIRTUAL_STACK_VARS_REGNUM for
+ the debug info in case it is not legitimate. */
+ if (GET_MODE (parmreg) != GET_MODE (rtl))
{
- rtx tempreg = gen_reg_rtx (GET_MODE (DECL_RTL (parm)));
+ rtx tempreg = gen_reg_rtx (GET_MODE (rtl));
int unsigned_p = TYPE_UNSIGNED (TREE_TYPE (parm));
push_to_sequence2 (all->first_conversion_insn,
all->last_conversion_insn);
- emit_move_insn (tempreg, DECL_RTL (parm));
+ emit_move_insn (tempreg, rtl);
tempreg = convert_to_mode (GET_MODE (parmreg), tempreg, unsigned_p);
- emit_move_insn (parmreg, tempreg);
+ emit_move_insn (MEM_P (parmreg) ? copy_rtx (parmreg) : parmreg,
+ tempreg);
all->first_conversion_insn = get_insns ();
all->last_conversion_insn = get_last_insn ();
end_sequence ();
did_conversion = true;
}
else
- emit_move_insn (parmreg, DECL_RTL (parm));
+ emit_move_insn (MEM_P (parmreg) ? copy_rtx (parmreg) : parmreg, rtl);
- SET_DECL_RTL (parm, parmreg);
+ rtl = parmreg;
/* STACK_PARM is the pointer, not the parm, and PARMREG is
now the parm. */
data->stack_parm = NULL;
}
+ set_parm_rtl (parm, rtl);
+
/* Mark the register as eliminable if we did no conversion and it was
copied from memory at a fixed offset, and the arg pointer was not
copied to a pseudo-reg. If the arg pointer is a pseudo reg or the
set_unique_reg_note (sinsn, REG_EQUIV, stackr);
}
}
- else
+ else
set_dst_reg_note (linsn, REG_EQUIV, equiv_stack_parm, parmreg);
}
BLOCK_OP_NORMAL);
}
else
- emit_move_insn (dest, src);
+ {
+ if (!REG_P (src))
+ src = force_reg (GET_MODE (src), src);
+ emit_move_insn (dest, src);
+ }
}
if (to_conversion)
end_sequence ();
}
- SET_DECL_RTL (parm, data->stack_parm);
+ set_parm_rtl (parm, data->stack_parm);
}
/* A subroutine of assign_parms. If the ABI splits complex arguments, then
}
else
tmp = gen_rtx_CONCAT (DECL_MODE (parm), real, imag);
- SET_DECL_RTL (parm, tmp);
+ set_parm_rtl (parm, tmp);
real = DECL_INCOMING_RTL (fnargs[i]);
imag = DECL_INCOMING_RTL (fnargs[i + 1]);
else
set_decl_incoming_rtl (parm, data.entry_parm, false);
- /* Boudns should be loaded in the particular order to
+ assign_parm_adjust_stack_rtl (&data);
+
+ /* Bounds should be loaded in the particular order to
have registers allocated correctly. Collect info about
input bounds and load them later. */
if (POINTER_BOUNDS_TYPE_P (data.passed_type))
}
else
{
- assign_parm_adjust_stack_rtl (&data);
-
if (assign_parm_setup_block_p (&data))
assign_parm_setup_block (&all, parm, &data);
else if (data.passed_pointer || use_register_for_decl (parm))
DECL_HAS_VALUE_EXPR_P (result) = 1;
- SET_DECL_RTL (result, x);
+ set_parm_rtl (result, x);
}
/* We have aligned all the args, so add space for the pretend args. */
locate->slot_offset.var = size_binop (MINUS_EXPR, ssize_int (0),
initial_offset_ptr->var);
- {
- tree s2 = sizetree;
- if (where_pad != none
- && (!tree_fits_uhwi_p (sizetree)
- || (tree_to_uhwi (sizetree) * BITS_PER_UNIT) % round_boundary))
- s2 = round_up (s2, round_boundary / BITS_PER_UNIT);
- SUB_PARM_SIZE (locate->slot_offset, s2);
- }
+ {
+ tree s2 = sizetree;
+ if (where_pad != none
+ && (!tree_fits_uhwi_p (sizetree)
+ || (tree_to_uhwi (sizetree) * BITS_PER_UNIT) % round_boundary))
+ s2 = round_up (s2, round_boundary / BITS_PER_UNIT);
+ SUB_PARM_SIZE (locate->slot_offset, s2);
+ }
locate->slot_offset.constant += part_size_in_regs;
for (decl = BLOCK_VARS (block); decl; decl = DECL_CHAIN (decl))
{
- if (TREE_CODE (decl) == VAR_DECL
+ if (VAR_P (decl)
&& DECL_RTL_SET_P (decl)
&& REG_P (DECL_RTL (decl))
&& regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL (decl))))
/* For SDB and XCOFF debugging output, we start numbering the blocks
from 1 within each function, rather than keeping a running
count. */
-#if defined (SDB_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+#if SDB_DEBUGGING_INFO || defined (XCOFF_DEBUGGING_INFO)
if (write_symbols == SDB_DEBUG || write_symbols == XCOFF_DEBUG)
next_block_index = 1;
#endif
{
cfun = new_cfun;
invoke_set_current_function_hook (new_cfun ? new_cfun->decl : NULL_TREE);
+ redirect_edge_var_map_empty ();
}
}
/* Initialized with NOGC, making this poisonous to the garbage collector. */
-static vec<function_p> cfun_stack;
+static vec<function *> cfun_stack;
/* Push the current cfun onto the stack, and set cfun to new_cfun. Also set
current_function_decl accordingly. */
if (fndecl != NULL_TREE)
{
tree result = DECL_RESULT (fndecl);
+
+ if (!abstract_p)
+ {
+ /* Now that we have activated any function-specific attributes
+ that might affect layout, particularly vector modes, relayout
+ each of the parameters and the result. */
+ relayout_decl (result);
+ for (tree parm = DECL_ARGUMENTS (fndecl); parm;
+ parm = DECL_CHAIN (parm))
+ relayout_decl (parm);
+
+ /* Similarly relayout the function decl. */
+ targetm.target_option.relayout_function (fndecl);
+ }
+
if (!abstract_p && aggregate_value_p (result, fndecl))
{
#ifdef PCC_STATIC_STRUCT_RETURN
void
init_function_start (tree subr)
{
- if (subr && DECL_STRUCT_FUNCTION (subr))
- set_cfun (DECL_STRUCT_FUNCTION (subr));
- else
- allocate_struct_function (subr, false);
-
/* Initialize backend, if needed. */
initialize_rtl ();
/* Expand code to verify the stack_protect_guard. This is invoked at
the end of a function to be protected. */
-#ifndef HAVE_stack_protect_test
-# define HAVE_stack_protect_test 0
-# define gen_stack_protect_test(x, y, z) (gcc_unreachable (), NULL_RTX)
-#endif
-
void
stack_protect_epilogue (void)
{
tree guard_decl = targetm.stack_protect_guard ();
rtx_code_label *label = gen_label_rtx ();
- rtx x, y, tmp;
+ rtx x, y;
+ rtx_insn *seq;
x = expand_normal (crtl->stack_protect_guard);
- y = expand_normal (guard_decl);
+ if (guard_decl)
+ y = expand_normal (guard_decl);
+ else
+ y = const0_rtx;
/* Allow the target to compare Y with X without leaking either into
a register. */
- switch ((int) (HAVE_stack_protect_test != 0))
- {
- case 1:
- tmp = gen_stack_protect_test (x, y, label);
- if (tmp)
- {
- emit_insn (tmp);
- break;
- }
- /* FALLTHRU */
-
- default:
- emit_cmp_and_jump_insns (x, y, EQ, NULL_RTX, ptr_mode, 1, label);
- break;
- }
+ if (targetm.have_stack_protect_test ()
+ && ((seq = targetm.gen_stack_protect_test (x, y, label)) != NULL_RTX))
+ emit_insn (seq);
+ else
+ emit_cmp_and_jump_insns (x, y, EQ, NULL_RTX, ptr_mode, 1, label);
/* The noreturn predictor has been moved to the tree level. The rtl-level
predictors estimate this branch about 20%, which isn't enough to get
things moved out of line. Since this is the only extant case of adding
a noreturn function at the rtl level, it doesn't seem worth doing ought
except adding the prediction by hand. */
- tmp = get_last_insn ();
+ rtx_insn *tmp = get_last_insn ();
if (JUMP_P (tmp))
- predict_insn_def (as_a <rtx_insn *> (tmp), PRED_NORETURN, TAKEN);
+ predict_insn_def (tmp, PRED_NORETURN, TAKEN);
expand_call (targetm.stack_protect_fail (), NULL_RTX, /*ignore=*/true);
free_temp_slots ();
before any library calls that assign parms might generate. */
/* Decide whether to return the value in memory or in a register. */
- if (aggregate_value_p (DECL_RESULT (subr), subr))
+ tree res = DECL_RESULT (subr);
+ if (aggregate_value_p (res, subr))
{
/* Returning something that won't go in a register. */
rtx value_address = 0;
#ifdef PCC_STATIC_STRUCT_RETURN
if (cfun->returns_pcc_struct)
{
- int size = int_size_in_bytes (TREE_TYPE (DECL_RESULT (subr)));
+ int size = int_size_in_bytes (TREE_TYPE (res));
value_address = assemble_static_space (size);
}
else
if (value_address)
{
rtx x = value_address;
- if (!DECL_BY_REFERENCE (DECL_RESULT (subr)))
+ if (!DECL_BY_REFERENCE (res))
{
- x = gen_rtx_MEM (DECL_MODE (DECL_RESULT (subr)), x);
- set_mem_attributes (x, DECL_RESULT (subr), 1);
+ x = gen_rtx_MEM (DECL_MODE (res), x);
+ set_mem_attributes (x, res, 1);
}
- SET_DECL_RTL (DECL_RESULT (subr), x);
+ set_parm_rtl (res, x);
}
}
- else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
+ else if (DECL_MODE (res) == VOIDmode)
/* If return mode is void, this decl rtl should not be used. */
- SET_DECL_RTL (DECL_RESULT (subr), NULL_RTX);
- else
+ set_parm_rtl (res, NULL_RTX);
+ else
{
/* Compute the return values into a pseudo reg, which we will copy
into the true return register after the cleanups are done. */
- tree return_type = TREE_TYPE (DECL_RESULT (subr));
- if (TYPE_MODE (return_type) != BLKmode
- && targetm.calls.return_in_msb (return_type))
+ tree return_type = TREE_TYPE (res);
+
+ /* If we may coalesce this result, make sure it has the expected mode
+ in case it was promoted. But we need not bother about BLKmode. */
+ machine_mode promoted_mode
+ = flag_tree_coalesce_vars && is_gimple_reg (res)
+ ? promote_ssa_mode (ssa_default_def (cfun, res), NULL)
+ : BLKmode;
+
+ if (promoted_mode != BLKmode)
+ set_parm_rtl (res, gen_reg_rtx (promoted_mode));
+ else if (TYPE_MODE (return_type) != BLKmode
+ && targetm.calls.return_in_msb (return_type))
/* expand_function_end will insert the appropriate padding in
this case. Use the return value's natural (unpadded) mode
within the function proper. */
- SET_DECL_RTL (DECL_RESULT (subr),
- gen_reg_rtx (TYPE_MODE (return_type)));
+ set_parm_rtl (res, gen_reg_rtx (TYPE_MODE (return_type)));
else
{
/* In order to figure out what mode to use for the pseudo, we
/* Structures that are returned in registers are not
aggregate_value_p, so we may see a PARALLEL or a REG. */
if (REG_P (hard_reg))
- SET_DECL_RTL (DECL_RESULT (subr),
- gen_reg_rtx (GET_MODE (hard_reg)));
+ set_parm_rtl (res, gen_reg_rtx (GET_MODE (hard_reg)));
else
{
gcc_assert (GET_CODE (hard_reg) == PARALLEL);
- SET_DECL_RTL (DECL_RESULT (subr), gen_group_rtx (hard_reg));
+ set_parm_rtl (res, gen_group_rtx (hard_reg));
}
}
/* Set DECL_REGISTER flag so that expand_function_end will copy the
result to the real return register(s). */
- DECL_REGISTER (DECL_RESULT (subr)) = 1;
+ DECL_REGISTER (res) = 1;
if (chkp_function_instrumented_p (current_function_decl))
{
- tree return_type = TREE_TYPE (DECL_RESULT (subr));
+ tree return_type = TREE_TYPE (res);
rtx bounds = targetm.calls.chkp_function_value_bounds (return_type,
subr, 1);
- SET_DECL_BOUNDS_RTL (DECL_RESULT (subr), bounds);
+ SET_DECL_BOUNDS_RTL (res, bounds);
}
}
{
tree parm = cfun->static_chain_decl;
rtx local, chain;
- rtx_insn *insn;
+ rtx_insn *insn;
+ int unsignedp;
- local = gen_reg_rtx (Pmode);
+ local = gen_reg_rtx (promote_decl_mode (parm, &unsignedp));
chain = targetm.calls.static_chain (current_function_decl, true);
set_decl_incoming_rtl (parm, chain, false);
- SET_DECL_RTL (parm, local);
+ set_parm_rtl (parm, local);
mark_reg_pointer (local, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (parm))));
- insn = emit_move_insn (local, chain);
+ if (GET_MODE (local) != GET_MODE (chain))
+ {
+ convert_move (local, chain, unsignedp);
+ insn = get_last_insn ();
+ }
+ else
+ insn = emit_move_insn (local, chain);
/* Mark the register as eliminable, similar to parameters. */
if (MEM_P (chain)
diddle_return_value (do_use_return_reg, NULL);
}
-/* Possibly warn about unused parameters. */
-void
-do_warn_unused_parameter (tree fn)
-{
- tree decl;
-
- for (decl = DECL_ARGUMENTS (fn);
- decl; decl = DECL_CHAIN (decl))
- if (!TREE_USED (decl) && TREE_CODE (decl) == PARM_DECL
- && DECL_NAME (decl) && !DECL_ARTIFICIAL (decl)
- && !TREE_NO_WARNING (decl))
- warning (OPT_Wunused_parameter, "unused parameter %q+D", decl);
-}
-
/* Set the location of the insn chain starting at INSN to LOC. */
static void
decl_rtl);
shift_return_value (GET_MODE (decl_rtl), true, real_decl_rtl);
}
- /* If a named return value dumped decl_return to memory, then
- we may need to re-do the PROMOTE_MODE signed/unsigned
- extension. */
- else if (GET_MODE (real_decl_rtl) != GET_MODE (decl_rtl))
- {
- int unsignedp = TYPE_UNSIGNED (TREE_TYPE (decl_result));
- promote_function_mode (TREE_TYPE (decl_result),
- GET_MODE (decl_rtl), &unsignedp,
- TREE_TYPE (current_function_decl), 1);
-
- convert_move (real_decl_rtl, decl_rtl, unsignedp);
- }
else if (GET_CODE (real_decl_rtl) == PARALLEL)
{
/* If expand_function_start has created a PARALLEL for decl_rtl,
emit_move_insn (tmp, decl_rtl);
emit_move_insn (real_decl_rtl, tmp);
}
+ /* If a named return value dumped decl_return to memory, then
+ we may need to re-do the PROMOTE_MODE signed/unsigned
+ extension. */
+ else if (GET_MODE (real_decl_rtl) != GET_MODE (decl_rtl))
+ {
+ int unsignedp = TYPE_UNSIGNED (TREE_TYPE (decl_result));
+ promote_function_mode (TREE_TYPE (decl_result),
+ GET_MODE (decl_rtl), &unsignedp,
+ TREE_TYPE (current_function_decl), 1);
+
+ convert_move (real_decl_rtl, decl_rtl, unsignedp);
+ }
else
emit_move_insn (real_decl_rtl, decl_rtl);
}
return hash->find (const_cast<rtx> (insn)) != NULL;
}
+int
+prologue_contains (const_rtx insn)
+{
+ return contains (insn, prologue_insn_hash);
+}
+
+int
+epilogue_contains (const_rtx insn)
+{
+ return contains (insn, epilogue_insn_hash);
+}
+
int
prologue_epilogue_contains (const_rtx insn)
{
return 0;
}
-/* Insert use of return register before the end of BB. */
-
-static void
-emit_use_return_register_into_block (basic_block bb)
-{
- start_sequence ();
- use_return_register ();
- rtx_insn *seq = get_insns ();
- end_sequence ();
- rtx_insn *insn = BB_END (bb);
- if (HAVE_cc0 && reg_mentioned_p (cc0_rtx, PATTERN (insn)))
- insn = prev_cc0_setter (insn);
-
- emit_insn_before (seq, insn);
-}
-
-
-/* Create a return pattern, either simple_return or return, depending on
- simple_p. */
-
-static rtx_insn *
-gen_return_pattern (bool simple_p)
+void
+record_prologue_seq (rtx_insn *seq)
{
- return (simple_p
- ? targetm.gen_simple_return ()
- : targetm.gen_return ());
+ record_insns (seq, NULL, &prologue_insn_hash);
}
-/* Insert an appropriate return pattern at the end of block BB. This
- also means updating block_for_insn appropriately. SIMPLE_P is
- the same as in gen_return_pattern and passed to it. */
-
void
-emit_return_into_block (bool simple_p, basic_block bb)
+record_epilogue_seq (rtx_insn *seq)
{
- rtx_jump_insn *jump = emit_jump_insn_after (gen_return_pattern (simple_p),
- BB_END (bb));
- rtx pat = PATTERN (jump);
- if (GET_CODE (pat) == PARALLEL)
- pat = XVECEXP (pat, 0, 0);
- gcc_assert (ANY_RETURN_P (pat));
- JUMP_LABEL (jump) = pat;
+ record_insns (seq, NULL, &epilogue_insn_hash);
}
/* Set JUMP_LABEL for a return insn. */
JUMP_LABEL (returnjump) = ret_rtx;
}
-/* Return true if there are any active insns between HEAD and TAIL. */
-bool
-active_insn_between (rtx_insn *head, rtx_insn *tail)
-{
- while (tail)
- {
- if (active_insn_p (tail))
- return true;
- if (tail == head)
- return false;
- tail = PREV_INSN (tail);
- }
- return false;
-}
+/* Return a sequence to be used as the split prologue for the current
+ function, or NULL. */
-/* LAST_BB is a block that exits, and empty of active instructions.
- Examine its predecessors for jumps that can be converted to
- (conditional) returns. */
-vec<edge>
-convert_jumps_to_returns (basic_block last_bb, bool simple_p,
- vec<edge> unconverted ATTRIBUTE_UNUSED)
+static rtx_insn *
+make_split_prologue_seq (void)
{
- int i;
- basic_block bb;
- edge_iterator ei;
- edge e;
- auto_vec<basic_block> src_bbs (EDGE_COUNT (last_bb->preds));
+ if (!flag_split_stack
+ || lookup_attribute ("no_split_stack", DECL_ATTRIBUTES (cfun->decl)))
+ return NULL;
- FOR_EACH_EDGE (e, ei, last_bb->preds)
- if (e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun))
- src_bbs.quick_push (e->src);
+ start_sequence ();
+ emit_insn (targetm.gen_split_stack_prologue ());
+ rtx_insn *seq = get_insns ();
+ end_sequence ();
- rtx_insn *label = BB_HEAD (last_bb);
+ record_insns (seq, NULL, &prologue_insn_hash);
+ set_insn_locations (seq, prologue_location);
- FOR_EACH_VEC_ELT (src_bbs, i, bb)
- {
- rtx_insn *jump = BB_END (bb);
+ return seq;
+}
- if (!JUMP_P (jump) || JUMP_LABEL (jump) != label)
- continue;
+/* Return a sequence to be used as the prologue for the current function,
+ or NULL. */
- e = find_edge (bb, last_bb);
+static rtx_insn *
+make_prologue_seq (void)
+{
+ if (!targetm.have_prologue ())
+ return NULL;
- /* If we have an unconditional jump, we can replace that
- with a simple return instruction. */
- if (simplejump_p (jump))
- {
- /* The use of the return register might be present in the exit
- fallthru block. Either:
- - removing the use is safe, and we should remove the use in
- the exit fallthru block, or
- - removing the use is not safe, and we should add it here.
- For now, we conservatively choose the latter. Either of the
- 2 helps in crossjumping. */
- emit_use_return_register_into_block (bb);
-
- emit_return_into_block (simple_p, bb);
- delete_insn (jump);
- }
+ start_sequence ();
+ rtx_insn *seq = targetm.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 (crtl->profile && frame_pointer_needed)
+ emit_use (hard_frame_pointer_rtx);
+
+ /* Retain a map of the prologue insns. */
+ record_insns (seq, NULL, &prologue_insn_hash);
+ emit_note (NOTE_INSN_PROLOGUE_END);
+
+ /* 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 (!targetm.profile_before_prologue () && crtl->profile)
+ emit_insn (gen_blockage ());
- /* If we have a conditional jump branching to the last
- block, we can try to replace that with a conditional
- return instruction. */
- else if (condjump_p (jump))
- {
- rtx dest;
+ seq = get_insns ();
+ end_sequence ();
+ set_insn_locations (seq, prologue_location);
- if (simple_p)
- dest = simple_return_rtx;
- else
- dest = ret_rtx;
- if (!redirect_jump (as_a <rtx_jump_insn *> (jump), dest, 0))
- {
- if (targetm.have_simple_return () && simple_p)
- {
- if (dump_file)
- fprintf (dump_file,
- "Failed to redirect bb %d branch.\n", bb->index);
- unconverted.safe_push (e);
- }
- continue;
- }
+ return seq;
+}
- /* See comment in simplejump_p case above. */
- emit_use_return_register_into_block (bb);
+/* Return a sequence to be used as the epilogue for the current function,
+ or NULL. */
- /* If this block has only one successor, it both jumps
- and falls through to the fallthru block, so we can't
- delete the edge. */
- if (single_succ_p (bb))
- continue;
- }
- else
- {
- if (targetm.have_simple_return () && simple_p)
- {
- if (dump_file)
- fprintf (dump_file,
- "Failed to redirect bb %d branch.\n", bb->index);
- unconverted.safe_push (e);
- }
- continue;
- }
+static rtx_insn *
+make_epilogue_seq (void)
+{
+ if (!targetm.have_epilogue ())
+ return NULL;
- /* Fix up the CFG for the successful change we just made. */
- redirect_edge_succ (e, EXIT_BLOCK_PTR_FOR_FN (cfun));
- e->flags &= ~EDGE_CROSSING;
- }
- src_bbs.release ();
- return unconverted;
-}
+ start_sequence ();
+ emit_note (NOTE_INSN_EPILOGUE_BEG);
+ rtx_insn *seq = targetm.gen_epilogue ();
+ if (seq)
+ emit_jump_insn (seq);
-/* Emit a return insn for the exit fallthru block. */
-basic_block
-emit_return_for_exit (edge exit_fallthru_edge, bool simple_p)
-{
- basic_block last_bb = exit_fallthru_edge->src;
+ /* Retain a map of the epilogue insns. */
+ record_insns (seq, NULL, &epilogue_insn_hash);
+ set_insn_locations (seq, epilogue_location);
- if (JUMP_P (BB_END (last_bb)))
- {
- last_bb = split_edge (exit_fallthru_edge);
- exit_fallthru_edge = single_succ_edge (last_bb);
- }
- emit_barrier_after (BB_END (last_bb));
- emit_return_into_block (simple_p, last_bb);
- exit_fallthru_edge->flags &= ~EDGE_FALLTHRU;
- return last_bb;
+ seq = get_insns ();
+ rtx_insn *returnjump = get_last_insn ();
+ end_sequence ();
+
+ if (JUMP_P (returnjump))
+ set_return_jump_label (returnjump);
+
+ return seq;
}
void
thread_prologue_and_epilogue_insns (void)
{
- bool inserted;
- vec<edge> unconverted_simple_returns = vNULL;
- bitmap_head bb_flags;
- rtx_insn *returnjump;
- rtx_insn *epilogue_end ATTRIBUTE_UNUSED;
- rtx_insn *prologue_seq ATTRIBUTE_UNUSED, *split_prologue_seq ATTRIBUTE_UNUSED;
- edge e, entry_edge, orig_entry_edge, exit_fallthru_edge;
- edge_iterator ei;
-
df_analyze ();
- rtl_profile_for_bb (ENTRY_BLOCK_PTR_FOR_FN (cfun));
-
- inserted = false;
- epilogue_end = NULL;
- returnjump = NULL;
-
/* Can't deal with multiple successors of the entry block at the
moment. Function should always have at least one entry
point. */
gcc_assert (single_succ_p (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
- entry_edge = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun));
- orig_entry_edge = entry_edge;
-
- split_prologue_seq = NULL;
- if (flag_split_stack
- && (lookup_attribute ("no_split_stack", DECL_ATTRIBUTES (cfun->decl))
- == NULL))
- {
-#ifndef HAVE_split_stack_prologue
- gcc_unreachable ();
-#else
- gcc_assert (HAVE_split_stack_prologue);
-
- start_sequence ();
- emit_insn (gen_split_stack_prologue ());
- split_prologue_seq = get_insns ();
- end_sequence ();
-
- record_insns (split_prologue_seq, NULL, &prologue_insn_hash);
- set_insn_locations (split_prologue_seq, prologue_location);
-#endif
- }
-
- prologue_seq = NULL;
-#ifdef HAVE_prologue
- if (HAVE_prologue)
- {
- start_sequence ();
- rtx_insn *seq = safe_as_a <rtx_insn *> (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 (crtl->profile && frame_pointer_needed)
- emit_use (hard_frame_pointer_rtx);
-
- /* Retain a map of the prologue insns. */
- record_insns (seq, NULL, &prologue_insn_hash);
- emit_note (NOTE_INSN_PROLOGUE_END);
-
- /* 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 (!targetm.profile_before_prologue () && crtl->profile)
- emit_insn (gen_blockage ());
- prologue_seq = get_insns ();
- end_sequence ();
- set_insn_locations (prologue_seq, prologue_location);
- }
-#endif
+ edge entry_edge = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun));
+ edge orig_entry_edge = entry_edge;
- bitmap_initialize (&bb_flags, &bitmap_default_obstack);
+ rtx_insn *split_prologue_seq = make_split_prologue_seq ();
+ rtx_insn *prologue_seq = make_prologue_seq ();
+ rtx_insn *epilogue_seq = make_epilogue_seq ();
/* Try to perform a kind of shrink-wrapping, making sure the
prologue/epilogue is emitted only around those parts of the
function that require it. */
+ try_shrink_wrapping (&entry_edge, prologue_seq);
- try_shrink_wrapping (&entry_edge, orig_entry_edge, &bb_flags, prologue_seq);
+ /* If the target can handle splitting the prologue/epilogue into separate
+ components, try to shrink-wrap these components separately. */
+ try_shrink_wrapping_separate (entry_edge->dest);
- if (split_prologue_seq != NULL_RTX)
- {
- insert_insn_on_edge (split_prologue_seq, orig_entry_edge);
- inserted = true;
- }
- if (prologue_seq != NULL_RTX)
+ /* If that did anything for any component we now need the generate the
+ "main" prologue again. Because some targets require some of these
+ to be called in a specific order (i386 requires the split prologue
+ to be first, for example), we create all three sequences again here.
+ If this does not work for some target, that target should not enable
+ separate shrink-wrapping. */
+ if (crtl->shrink_wrapped_separate)
{
- insert_insn_on_edge (prologue_seq, entry_edge);
- inserted = true;
+ split_prologue_seq = make_split_prologue_seq ();
+ prologue_seq = make_prologue_seq ();
+ epilogue_seq = make_epilogue_seq ();
}
- /* If the exit block has no non-fake predecessors, we don't need
- an epilogue. */
- FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
- if ((e->flags & EDGE_FAKE) == 0)
- break;
- if (e == NULL)
- goto epilogue_done;
-
rtl_profile_for_bb (EXIT_BLOCK_PTR_FOR_FN (cfun));
- exit_fallthru_edge = find_fallthru_edge (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds);
-
- if (targetm.have_simple_return () && entry_edge != orig_entry_edge)
- exit_fallthru_edge
- = get_unconverted_simple_return (exit_fallthru_edge, bb_flags,
- &unconverted_simple_returns,
- &returnjump);
- if (targetm.have_return ())
- {
- if (exit_fallthru_edge == NULL)
- goto epilogue_done;
-
- if (optimize)
- {
- basic_block last_bb = exit_fallthru_edge->src;
-
- if (LABEL_P (BB_HEAD (last_bb))
- && !active_insn_between (BB_HEAD (last_bb), BB_END (last_bb)))
- convert_jumps_to_returns (last_bb, false, vNULL);
-
- if (EDGE_COUNT (last_bb->preds) != 0
- && single_succ_p (last_bb))
- {
- last_bb = emit_return_for_exit (exit_fallthru_edge, false);
- epilogue_end = returnjump = BB_END (last_bb);
-
- /* Emitting the return may add a basic block.
- Fix bb_flags for the added block. */
- if (targetm.have_simple_return ()
- && last_bb != exit_fallthru_edge->src)
- bitmap_set_bit (&bb_flags, last_bb->index);
-
- goto epilogue_done;
- }
- }
- }
-
/* A small fib -- epilogue is not yet completed, but we wish to re-use
this marker for the splits of EH_RETURN patterns, and nothing else
uses the flag in the meantime. */
epilogue_completed = 1;
-#ifdef HAVE_eh_return
/* Find non-fallthru edges that end with EH_RETURN instructions. On
some targets, these get split to a special version of the epilogue
code. In order to be able to properly annotate these with unwind
info, try to split them now. If we get a valid split, drop an
EPILOGUE_BEG note and mark the insns as epilogue insns. */
+ edge e;
+ edge_iterator ei;
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
{
rtx_insn *prev, *last, *trial;
record_insns (NEXT_INSN (prev), NEXT_INSN (trial), &epilogue_insn_hash);
emit_note_after (NOTE_INSN_EPILOGUE_BEG, prev);
}
-#endif
-
- /* If nothing falls through into the exit block, we don't need an
- epilogue. */
- if (exit_fallthru_edge == NULL)
- goto epilogue_done;
+ edge exit_fallthru_edge = find_fallthru_edge (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds);
- if (HAVE_epilogue)
+ if (exit_fallthru_edge)
{
- start_sequence ();
- epilogue_end = emit_note (NOTE_INSN_EPILOGUE_BEG);
- rtx_insn *seq = as_a <rtx_insn *> (gen_epilogue ());
- if (seq)
- emit_jump_insn (seq);
-
- /* Retain a map of the epilogue insns. */
- record_insns (seq, NULL, &epilogue_insn_hash);
- set_insn_locations (seq, epilogue_location);
-
- seq = get_insns ();
- returnjump = get_last_insn ();
- end_sequence ();
-
- insert_insn_on_edge (seq, exit_fallthru_edge);
- inserted = true;
-
- if (JUMP_P (returnjump))
- set_return_jump_label (returnjump);
- }
- else
- {
- basic_block cur_bb;
+ if (epilogue_seq)
+ {
+ insert_insn_on_edge (epilogue_seq, exit_fallthru_edge);
+ commit_edge_insertions ();
- if (! next_active_insn (BB_END (exit_fallthru_edge->src)))
- goto epilogue_done;
- /* We have a fall-through edge to the exit block, the source is not
- at the end of the function, and there will be an assembler epilogue
- at the end of the function.
- We can't use force_nonfallthru here, because that would try to
- use return. Inserting a jump 'by hand' is extremely messy, so
- we take advantage of cfg_layout_finalize using
- fixup_fallthru_exit_predecessor. */
- cfg_layout_initialize (0);
- FOR_EACH_BB_FN (cur_bb, cfun)
- if (cur_bb->index >= NUM_FIXED_BLOCKS
- && cur_bb->next_bb->index >= NUM_FIXED_BLOCKS)
- cur_bb->aux = cur_bb->next_bb;
- cfg_layout_finalize ();
+ /* The epilogue insns we inserted may cause the exit edge to no longer
+ be fallthru. */
+ FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
+ {
+ if (((e->flags & EDGE_FALLTHRU) != 0)
+ && returnjump_p (BB_END (e->src)))
+ e->flags &= ~EDGE_FALLTHRU;
+ }
+ }
+ else if (next_active_insn (BB_END (exit_fallthru_edge->src)))
+ {
+ /* We have a fall-through edge to the exit block, the source is not
+ at the end of the function, and there will be an assembler epilogue
+ at the end of the function.
+ We can't use force_nonfallthru here, because that would try to
+ use return. Inserting a jump 'by hand' is extremely messy, so
+ we take advantage of cfg_layout_finalize using
+ fixup_fallthru_exit_predecessor. */
+ cfg_layout_initialize (0);
+ basic_block cur_bb;
+ FOR_EACH_BB_FN (cur_bb, cfun)
+ if (cur_bb->index >= NUM_FIXED_BLOCKS
+ && cur_bb->next_bb->index >= NUM_FIXED_BLOCKS)
+ cur_bb->aux = cur_bb->next_bb;
+ cfg_layout_finalize ();
+ }
}
-epilogue_done:
+ /* Insert the prologue. */
- default_rtl_profile ();
+ rtl_profile_for_bb (ENTRY_BLOCK_PTR_FOR_FN (cfun));
- if (inserted)
+ if (split_prologue_seq || prologue_seq)
{
- sbitmap blocks;
+ if (split_prologue_seq)
+ insert_insn_on_edge (split_prologue_seq, orig_entry_edge);
+
+ if (prologue_seq)
+ insert_insn_on_edge (prologue_seq, entry_edge);
commit_edge_insertions ();
/* Look for basic blocks within the prologue insns. */
- blocks = sbitmap_alloc (last_basic_block_for_fn (cfun));
+ auto_sbitmap blocks (last_basic_block_for_fn (cfun));
bitmap_clear (blocks);
bitmap_set_bit (blocks, entry_edge->dest->index);
bitmap_set_bit (blocks, orig_entry_edge->dest->index);
find_many_sub_basic_blocks (blocks);
- sbitmap_free (blocks);
-
- /* The epilogue insns we inserted may cause the exit edge to no longer
- be fallthru. */
- FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
- {
- if (((e->flags & EDGE_FALLTHRU) != 0)
- && returnjump_p (BB_END (e->src)))
- e->flags &= ~EDGE_FALLTHRU;
- }
}
- if (targetm.have_simple_return ())
- convert_to_simple_return (entry_edge, orig_entry_edge, bb_flags,
- returnjump, unconverted_simple_returns);
+ default_rtl_profile ();
-#ifdef HAVE_sibcall_epilogue
/* Emit sibling epilogues before any sibling call sites. */
- for (ei = ei_start (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds); (e =
- ei_safe_edge (ei));
- )
- {
- basic_block bb = e->src;
- rtx_insn *insn = BB_END (bb);
- rtx ep_seq;
-
- if (!CALL_P (insn)
- || ! SIBLING_CALL_P (insn)
- || (targetm.have_simple_return ()
- && entry_edge != orig_entry_edge
- && !bitmap_bit_p (&bb_flags, bb->index)))
+ for (ei = ei_start (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds);
+ (e = ei_safe_edge (ei));
+ ei_next (&ei))
+ {
+ /* Skip those already handled, the ones that run without prologue. */
+ if (e->flags & EDGE_IGNORE)
{
- ei_next (&ei);
+ e->flags &= ~EDGE_IGNORE;
continue;
}
- ep_seq = gen_sibcall_epilogue ();
- if (ep_seq)
+ rtx_insn *insn = BB_END (e->src);
+
+ if (!(CALL_P (insn) && SIBLING_CALL_P (insn)))
+ continue;
+
+ if (rtx_insn *ep_seq = targetm.gen_sibcall_epilogue ())
{
start_sequence ();
emit_note (NOTE_INSN_EPILOGUE_BEG);
emit_insn_before (seq, insn);
}
- ei_next (&ei);
}
-#endif
- if (epilogue_end)
+ if (epilogue_seq)
{
rtx_insn *insn, *next;
of such a note. Also possibly move
NOTE_INSN_FUNCTION_BEG notes, as those can be relevant for debug
info generation. */
- for (insn = epilogue_end; insn; insn = next)
+ for (insn = epilogue_seq; insn; insn = next)
{
next = NEXT_INSN (insn);
if (NOTE_P (insn)
&& (NOTE_KIND (insn) == NOTE_INSN_FUNCTION_BEG))
- reorder_insns (insn, insn, PREV_INSN (epilogue_end));
+ reorder_insns (insn, insn, PREV_INSN (epilogue_seq));
}
}
- bitmap_clear (&bb_flags);
-
/* Threading the prologue and epilogue changes the artificial refs
in the entry and exit blocks. */
epilogue_completed = 1;
void
reposition_prologue_and_epilogue_notes (void)
{
-#if ! defined (HAVE_prologue) && ! defined (HAVE_sibcall_epilogue)
- if (!HAVE_epilogue)
+ if (!targetm.have_prologue ()
+ && !targetm.have_epilogue ()
+ && !targetm.have_sibcall_epilogue ())
return;
-#endif
/* Since the hash table is created on demand, the fact that it is
non-null is a signal that it is non-empty. */
static unsigned int
rest_of_handle_thread_prologue_and_epilogue (void)
{
+ /* prepare_shrink_wrap is sensitive to the block structure of the control
+ flow graph, so clean it up first. */
if (optimize)
- cleanup_cfg (CLEANUP_EXPENSIVE);
+ cleanup_cfg (0);
/* On some machines, the prologue and epilogue code, or parts thereof,
can be represented as RTL. Doing so lets us schedule insns between
scheduling to operate in the epilogue. */
thread_prologue_and_epilogue_insns ();
+ /* Some non-cold blocks may now be only reachable from cold blocks.
+ Fix that up. */
+ fixup_partitions ();
+
/* Shrink-wrapping can result in unreachable edges in the epilogue,
see PR57320. */
- cleanup_cfg (0);
+ cleanup_cfg (optimize ? CLEANUP_EXPENSIVE : 0);
/* The stack usage info is finalized during prologue expansion. */
if (flag_stack_usage_info)
void
add_local_decl (struct function *fun, tree d)
{
- gcc_assert (TREE_CODE (d) == VAR_DECL);
+ gcc_assert (VAR_P (d));
vec_safe_push (fun->local_decls, d);
}