Removal of Return with Depressed Stack Pointer support
Removal of Return with Depressed Stack Pointer support * tree.h (TYPE_RETURNS_STACK_DEPRESSED): Delete. (ECF_SP_DEPRESSED): Likewise. (ECF_LIBCALL_BLOCK, ECF_NOVOPS): Adjust. * calls.c (emit_call_1): Do not test ECF_SP_DEPRESSED. (flags_from_decl_or_type): Do not test TYPE_RETURNS_STACK_DEPRESSED. (expand_call): Do not test ECF_SP_DEPRESSED. * dse.c (dse_step0): Do not test TYPE_RETURNS_STACK_DEPRESSED. * function.c (keep_stack_depressed): Delete. (handle_epilogue_set): Likewise. (update_epilogue_consts): Likewise. (emit_equiv_load): Likewise. (thread_prologue_and_epilogue_insns): Remove support for Return with Depressed Stack Pointer. * print-tree.c (print_node): Do not test TYPE_RETURNS_STACK_DEPRESSED. ada/ * gigi.h (create_subprog_type): Remove returns_with_dsp parameter. * decl.c (gnat_to_gnu_entity): Adjust for above new prototype. * utils.c (create_subprog_type): Remove returns_with_dsp parameter. * trans.c (gnat_to_gnu) <N_Return_Statement>: Remove code dealing with Return by Depressed Stack Pointer. From-SVN: r133976
This commit is contained in:
parent
f9985df51b
commit
9dd9bf80a8
|
@ -1,3 +1,21 @@
|
|||
2008-04-07 Eric Botcazou <ebotcazou@adacore.com>
|
||||
|
||||
Removal of Return with Depressed Stack Pointer support
|
||||
* tree.h (TYPE_RETURNS_STACK_DEPRESSED): Delete.
|
||||
(ECF_SP_DEPRESSED): Likewise.
|
||||
(ECF_LIBCALL_BLOCK, ECF_NOVOPS): Adjust.
|
||||
* calls.c (emit_call_1): Do not test ECF_SP_DEPRESSED.
|
||||
(flags_from_decl_or_type): Do not test TYPE_RETURNS_STACK_DEPRESSED.
|
||||
(expand_call): Do not test ECF_SP_DEPRESSED.
|
||||
* dse.c (dse_step0): Do not test TYPE_RETURNS_STACK_DEPRESSED.
|
||||
* function.c (keep_stack_depressed): Delete.
|
||||
(handle_epilogue_set): Likewise.
|
||||
(update_epilogue_consts): Likewise.
|
||||
(emit_equiv_load): Likewise.
|
||||
(thread_prologue_and_epilogue_insns): Remove support for Return with
|
||||
Depressed Stack Pointer.
|
||||
* print-tree.c (print_node): Do not test TYPE_RETURNS_STACK_DEPRESSED.
|
||||
|
||||
2008-04-06 Richard Guenther <rguenther@suse.de>
|
||||
|
||||
PR tree-optimization/35400
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
2008-04-07 Eric Botcazou <ebotcazou@adacore.com>
|
||||
|
||||
* gigi.h (create_subprog_type): Remove returns_with_dsp parameter.
|
||||
* decl.c (gnat_to_gnu_entity): Adjust for above new prototype.
|
||||
* utils.c (create_subprog_type): Remove returns_with_dsp parameter.
|
||||
* trans.c (gnat_to_gnu) <N_Return_Statement>: Remove code dealing with
|
||||
Return by Depressed Stack Pointer.
|
||||
|
||||
2008-04-06 Eric Botcazou <ebotcazou@adacore.com>
|
||||
|
||||
* decl.c (is_variable_size): Do not unconditionally return false
|
||||
|
|
|
@ -3863,17 +3863,13 @@ gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, int definition)
|
|||
gnu_type
|
||||
= create_subprog_type (gnu_return_type, gnu_param_list,
|
||||
gnu_return_list, returns_unconstrained,
|
||||
returns_by_ref,
|
||||
Function_Returns_With_DSP (gnat_entity),
|
||||
returns_by_target_ptr);
|
||||
returns_by_ref, returns_by_target_ptr);
|
||||
|
||||
if (has_stub)
|
||||
gnu_stub_type
|
||||
= create_subprog_type (gnu_return_type, gnu_stub_param_list,
|
||||
gnu_return_list, returns_unconstrained,
|
||||
returns_by_ref,
|
||||
Function_Returns_With_DSP (gnat_entity),
|
||||
returns_by_target_ptr);
|
||||
returns_by_ref, returns_by_target_ptr);
|
||||
|
||||
/* A subprogram (something that doesn't return anything) shouldn't
|
||||
be considered Pure since there would be no reason for such a
|
||||
|
|
|
@ -522,13 +522,11 @@ extern void rest_of_record_type_compilation (tree record_type);
|
|||
copy-in/copy-out list to be stored into TYPE_CI_CO_LIST.
|
||||
RETURNS_UNCONSTRAINED is true if the function returns an unconstrained
|
||||
object. RETURNS_BY_REF is true if the function returns by reference.
|
||||
RETURNS_WITH_DSP is true if the function is to return with a
|
||||
depressed stack pointer. RETURNS_BY_TARGET_PTR is true if the function
|
||||
is to be passed (as its first parameter) the address of the place to copy
|
||||
its result. */
|
||||
RETURNS_BY_TARGET_PTR is true if the function is to be passed (as its
|
||||
first parameter) the address of the place to copy its result. */
|
||||
extern tree create_subprog_type (tree return_type, tree param_decl_list,
|
||||
tree cico_list, bool returns_unconstrained,
|
||||
bool returns_by_ref, bool returns_with_dsp,
|
||||
bool returns_by_ref,
|
||||
bool returns_by_target_ptr);
|
||||
|
||||
/* Return a copy of TYPE, but safe to modify in any way. */
|
||||
|
|
|
@ -4182,26 +4182,13 @@ gnat_to_gnu (Node_Id gnat_node)
|
|||
else if (TYPE_RETURNS_UNCONSTRAINED_P (gnu_subprog_type))
|
||||
{
|
||||
gnu_ret_val = maybe_unconstrained_array (gnu_ret_val);
|
||||
|
||||
/* We have two cases: either the function returns with
|
||||
depressed stack or not. If not, we allocate on the
|
||||
secondary stack. If so, we allocate in the stack frame.
|
||||
if no copy is needed, the front end will set By_Ref,
|
||||
which we handle in the case above. */
|
||||
if (TYPE_RETURNS_STACK_DEPRESSED (gnu_subprog_type))
|
||||
gnu_ret_val
|
||||
= build_allocator (TREE_TYPE (gnu_ret_val),
|
||||
gnu_ret_val,
|
||||
TREE_TYPE (gnu_subprog_type),
|
||||
0, -1, gnat_node, false);
|
||||
else
|
||||
gnu_ret_val
|
||||
= build_allocator (TREE_TYPE (gnu_ret_val),
|
||||
gnu_ret_val,
|
||||
TREE_TYPE (gnu_subprog_type),
|
||||
Procedure_To_Call (gnat_node),
|
||||
Storage_Pool (gnat_node),
|
||||
gnat_node, false);
|
||||
gnu_ret_val
|
||||
= build_allocator (TREE_TYPE (gnu_ret_val),
|
||||
gnu_ret_val,
|
||||
TREE_TYPE (gnu_subprog_type),
|
||||
Procedure_To_Call (gnat_node),
|
||||
Storage_Pool (gnat_node),
|
||||
gnat_node, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1255,17 +1255,15 @@ split_plus (tree in, tree *pvar)
|
|||
otherwise we are dealing with a function. PARAM_DECL_LIST is a list of
|
||||
PARM_DECL nodes that are the subprogram arguments. CICO_LIST is the
|
||||
copy-in/copy-out list to be stored into TYPE_CICO_LIST.
|
||||
RETURNS_UNCONSTRAINED is nonzero if the function returns an unconstrained
|
||||
object. RETURNS_BY_REF is nonzero if the function returns by reference.
|
||||
RETURNS_WITH_DSP is nonzero if the function is to return with a
|
||||
depressed stack pointer. RETURNS_BY_TARGET_PTR is true if the function
|
||||
is to be passed (as its first parameter) the address of the place to copy
|
||||
its result. */
|
||||
RETURNS_UNCONSTRAINED is true if the function returns an unconstrained
|
||||
object. RETURNS_BY_REF is true if the function returns by reference.
|
||||
RETURNS_BY_TARGET_PTR is true if the function is to be passed (as its
|
||||
first parameter) the address of the place to copy its result. */
|
||||
|
||||
tree
|
||||
create_subprog_type (tree return_type, tree param_decl_list, tree cico_list,
|
||||
bool returns_unconstrained, bool returns_by_ref,
|
||||
bool returns_with_dsp, bool returns_by_target_ptr)
|
||||
bool returns_by_target_ptr)
|
||||
{
|
||||
/* A chain of TREE_LIST nodes whose TREE_VALUEs are the data type nodes of
|
||||
the subprogram formal parameters. This list is generated by traversing the
|
||||
|
@ -1302,7 +1300,6 @@ create_subprog_type (tree return_type, tree param_decl_list, tree cico_list,
|
|||
|
||||
TYPE_CI_CO_LIST (type) = cico_list;
|
||||
TYPE_RETURNS_UNCONSTRAINED_P (type) = returns_unconstrained;
|
||||
TYPE_RETURNS_STACK_DEPRESSED (type) = returns_with_dsp;
|
||||
TYPE_RETURNS_BY_REF_P (type) = returns_by_ref;
|
||||
TYPE_RETURNS_BY_TARGET_PTR_P (type) = returns_by_target_ptr;
|
||||
return type;
|
||||
|
|
29
gcc/calls.c
29
gcc/calls.c
|
@ -297,7 +297,7 @@ emit_call_1 (rtx funexp, tree fntree, tree fndecl ATTRIBUTE_UNUSED,
|
|||
even if the call has no arguments to pop. */
|
||||
#if defined (HAVE_call) && defined (HAVE_call_value)
|
||||
if (HAVE_call && HAVE_call_value && HAVE_call_pop && HAVE_call_value_pop
|
||||
&& n_popped > 0 && ! (ecf_flags & ECF_SP_DEPRESSED))
|
||||
&& n_popped > 0)
|
||||
#else
|
||||
if (HAVE_call_pop && HAVE_call_value_pop)
|
||||
#endif
|
||||
|
@ -432,7 +432,7 @@ emit_call_1 (rtx funexp, tree fntree, tree fndecl ATTRIBUTE_UNUSED,
|
|||
|
||||
if (rounded_stack_size != 0)
|
||||
{
|
||||
if (ecf_flags & (ECF_SP_DEPRESSED | ECF_NORETURN))
|
||||
if (ecf_flags & ECF_NORETURN)
|
||||
/* Just pretend we did the pop. */
|
||||
stack_pointer_delta -= rounded_stack_size;
|
||||
else if (flag_defer_pop && inhibit_defer_pop == 0
|
||||
|
@ -602,14 +602,6 @@ flags_from_decl_or_type (const_tree exp)
|
|||
if (TREE_THIS_VOLATILE (exp))
|
||||
flags |= ECF_NORETURN;
|
||||
|
||||
/* Mark if the function returns with the stack pointer depressed. We
|
||||
cannot consider it pure or constant in that case. */
|
||||
if (TREE_CODE (type) == FUNCTION_TYPE && TYPE_RETURNS_STACK_DEPRESSED (type))
|
||||
{
|
||||
flags |= ECF_SP_DEPRESSED;
|
||||
flags &= ~(ECF_PURE | ECF_CONST);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
@ -2354,13 +2346,12 @@ expand_call (tree exp, rtx target, int ignore)
|
|||
/* Don't let pending stack adjusts add up to too much.
|
||||
Also, do all pending adjustments now if there is any chance
|
||||
this might be a call to alloca or if we are expanding a sibling
|
||||
call sequence or if we are calling a function that is to return
|
||||
with stack pointer depressed.
|
||||
call sequence.
|
||||
Also do the adjustments before a throwing call, otherwise
|
||||
exception handling can fail; PR 19225. */
|
||||
if (pending_stack_adjust >= 32
|
||||
|| (pending_stack_adjust > 0
|
||||
&& (flags & (ECF_MAY_BE_ALLOCA | ECF_SP_DEPRESSED)))
|
||||
&& (flags & ECF_MAY_BE_ALLOCA))
|
||||
|| (pending_stack_adjust > 0
|
||||
&& flag_exceptions && !(flags & ECF_NOTHROW))
|
||||
|| pass == 0)
|
||||
|
@ -3071,7 +3062,7 @@ expand_call (tree exp, rtx target, int ignore)
|
|||
/* If size of args is variable or this was a constructor call for a stack
|
||||
argument, restore saved stack-pointer value. */
|
||||
|
||||
if (old_stack_level && ! (flags & ECF_SP_DEPRESSED))
|
||||
if (old_stack_level)
|
||||
{
|
||||
emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
|
||||
stack_pointer_delta = old_stack_pointer_delta;
|
||||
|
@ -3177,16 +3168,6 @@ expand_call (tree exp, rtx target, int ignore)
|
|||
|
||||
currently_expanding_call--;
|
||||
|
||||
/* If this function returns with the stack pointer depressed, ensure
|
||||
this block saves and restores the stack pointer, show it was
|
||||
changed, and adjust for any outgoing arg space. */
|
||||
if (flags & ECF_SP_DEPRESSED)
|
||||
{
|
||||
clear_pending_stack_adjust ();
|
||||
emit_insn (gen_rtx_CLOBBER (VOIDmode, stack_pointer_rtx));
|
||||
emit_move_insn (virtual_stack_dynamic_rtx, stack_pointer_rtx);
|
||||
}
|
||||
|
||||
if (stack_usage_map_buf)
|
||||
free (stack_usage_map_buf);
|
||||
|
||||
|
|
12
gcc/dse.c
12
gcc/dse.c
|
@ -522,11 +522,8 @@ struct clear_alias_mode_holder
|
|||
|
||||
static alloc_pool clear_alias_mode_pool;
|
||||
|
||||
/* This is true except for two cases:
|
||||
(1) current_function_stdarg -- i.e. we cannot do this
|
||||
for vararg functions because they play games with the frame.
|
||||
(2) In ada, it is sometimes not safe to do assume that any stores
|
||||
based off the stack frame go dead at the exit to a function. */
|
||||
/* This is true except if current_function_stdarg -- i.e. we cannot do
|
||||
this for vararg functions because they play games with the frame. */
|
||||
static bool stores_off_frame_dead_at_return;
|
||||
|
||||
/* Counter for stats. */
|
||||
|
@ -712,10 +709,7 @@ dse_step0 (void)
|
|||
bb_table = XCNEWVEC (bb_info_t, last_basic_block);
|
||||
rtx_group_next_id = 0;
|
||||
|
||||
stores_off_frame_dead_at_return =
|
||||
(!(TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE
|
||||
&& (TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (current_function_decl)))))
|
||||
&& (!current_function_stdarg);
|
||||
stores_off_frame_dead_at_return = !current_function_stdarg;
|
||||
|
||||
init_alias_analysis ();
|
||||
|
||||
|
|
390
gcc/function.c
390
gcc/function.c
|
@ -207,9 +207,6 @@ 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 (void);
|
||||
static void do_clobber_return_reg (rtx, void *);
|
||||
static void do_use_return_reg (rtx, void *);
|
||||
|
@ -4720,383 +4717,6 @@ emit_return_into_block (basic_block bb)
|
|||
}
|
||||
#endif /* HAVE_return */
|
||||
|
||||
#if defined(HAVE_epilogue) && defined(INCOMING_RETURN_ADDR_RTX)
|
||||
|
||||
/* These functions convert the epilogue into a variant that does not
|
||||
modify the stack pointer. This is used in cases where a function
|
||||
returns an object whose size is not known until it is computed.
|
||||
The called function leaves the object on the stack, leaves the
|
||||
stack depressed, and returns a pointer to the object.
|
||||
|
||||
What we need to do is track all modifications and references to the
|
||||
stack pointer, deleting the modifications and changing the
|
||||
references to point to the location the stack pointer would have
|
||||
pointed to had the modifications taken place.
|
||||
|
||||
These functions need to be portable so we need to make as few
|
||||
assumptions about the epilogue as we can. However, the epilogue
|
||||
basically contains three things: instructions to reset the stack
|
||||
pointer, instructions to reload registers, possibly including the
|
||||
frame pointer, and an instruction to return to the caller.
|
||||
|
||||
We must be sure of what a relevant epilogue insn is doing. We also
|
||||
make no attempt to validate the insns we make since if they are
|
||||
invalid, we probably can't do anything valid. The intent is that
|
||||
these routines get "smarter" as more and more machines start to use
|
||||
them and they try operating on different epilogues.
|
||||
|
||||
We use the following structure to track what the part of the
|
||||
epilogue that we've already processed has done. We keep two copies
|
||||
of the SP equivalence, one for use during the insn we are
|
||||
processing and one for use in the next insn. The difference is
|
||||
because one part of a PARALLEL may adjust SP and the other may use
|
||||
it. */
|
||||
|
||||
struct epi_info
|
||||
{
|
||||
rtx sp_equiv_reg; /* REG that SP is set from, perhaps SP. */
|
||||
HOST_WIDE_INT sp_offset; /* Offset from SP_EQUIV_REG of present SP. */
|
||||
rtx new_sp_equiv_reg; /* REG to be used at end of insn. */
|
||||
HOST_WIDE_INT new_sp_offset; /* Offset to be used at end of insn. */
|
||||
rtx equiv_reg_src; /* If nonzero, the value that SP_EQUIV_REG
|
||||
should be set to once we no longer need
|
||||
its value. */
|
||||
rtx const_equiv[FIRST_PSEUDO_REGISTER]; /* Any known constant equivalences
|
||||
for registers. */
|
||||
};
|
||||
|
||||
static void handle_epilogue_set (rtx, struct epi_info *);
|
||||
static void update_epilogue_consts (rtx, 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
|
||||
no modifications to the stack pointer. Return the new list of insns. */
|
||||
|
||||
static rtx
|
||||
keep_stack_depressed (rtx insns)
|
||||
{
|
||||
int j;
|
||||
struct epi_info info;
|
||||
rtx insn, next;
|
||||
|
||||
/* If the epilogue is just a single instruction, it must be OK as is. */
|
||||
if (NEXT_INSN (insns) == NULL_RTX)
|
||||
return insns;
|
||||
|
||||
/* Otherwise, start a sequence, initialize the information we have, and
|
||||
process all the insns we were given. */
|
||||
start_sequence ();
|
||||
|
||||
info.sp_equiv_reg = stack_pointer_rtx;
|
||||
info.sp_offset = 0;
|
||||
info.equiv_reg_src = 0;
|
||||
|
||||
for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
|
||||
info.const_equiv[j] = 0;
|
||||
|
||||
insn = insns;
|
||||
next = NULL_RTX;
|
||||
while (insn != NULL_RTX)
|
||||
{
|
||||
next = NEXT_INSN (insn);
|
||||
|
||||
if (!INSN_P (insn))
|
||||
{
|
||||
add_insn (insn);
|
||||
insn = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If this insn references the register that SP is equivalent to and
|
||||
we have a pending load to that register, we must force out the load
|
||||
first and then indicate we no longer know what SP's equivalent is. */
|
||||
if (info.equiv_reg_src != 0
|
||||
&& reg_referenced_p (info.sp_equiv_reg, PATTERN (insn)))
|
||||
{
|
||||
emit_equiv_load (&info);
|
||||
info.sp_equiv_reg = 0;
|
||||
}
|
||||
|
||||
info.new_sp_equiv_reg = info.sp_equiv_reg;
|
||||
info.new_sp_offset = info.sp_offset;
|
||||
|
||||
/* If this is a (RETURN) and the return address is on the stack,
|
||||
update the address and change to an indirect jump. */
|
||||
if (GET_CODE (PATTERN (insn)) == RETURN
|
||||
|| (GET_CODE (PATTERN (insn)) == PARALLEL
|
||||
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == RETURN))
|
||||
{
|
||||
rtx retaddr = INCOMING_RETURN_ADDR_RTX;
|
||||
rtx base = 0;
|
||||
HOST_WIDE_INT offset = 0;
|
||||
rtx jump_insn, jump_set;
|
||||
|
||||
/* If the return address is in a register, we can emit the insn
|
||||
unchanged. Otherwise, it must be a MEM and we see what the
|
||||
base register and offset are. In any case, we have to emit any
|
||||
pending load to the equivalent reg of SP, if any. */
|
||||
if (REG_P (retaddr))
|
||||
{
|
||||
emit_equiv_load (&info);
|
||||
add_insn (insn);
|
||||
insn = next;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
rtx ret_ptr;
|
||||
gcc_assert (MEM_P (retaddr));
|
||||
|
||||
ret_ptr = XEXP (retaddr, 0);
|
||||
|
||||
if (REG_P (ret_ptr))
|
||||
{
|
||||
base = gen_rtx_REG (Pmode, REGNO (ret_ptr));
|
||||
offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gcc_assert (GET_CODE (ret_ptr) == PLUS
|
||||
&& REG_P (XEXP (ret_ptr, 0))
|
||||
&& GET_CODE (XEXP (ret_ptr, 1)) == CONST_INT);
|
||||
base = gen_rtx_REG (Pmode, REGNO (XEXP (ret_ptr, 0)));
|
||||
offset = INTVAL (XEXP (ret_ptr, 1));
|
||||
}
|
||||
}
|
||||
|
||||
/* If the base of the location containing the return pointer
|
||||
is SP, we must update it with the replacement address. Otherwise,
|
||||
just build the necessary MEM. */
|
||||
retaddr = plus_constant (base, offset);
|
||||
if (base == stack_pointer_rtx)
|
||||
retaddr = simplify_replace_rtx (retaddr, stack_pointer_rtx,
|
||||
plus_constant (info.sp_equiv_reg,
|
||||
info.sp_offset));
|
||||
|
||||
retaddr = gen_rtx_MEM (Pmode, retaddr);
|
||||
MEM_NOTRAP_P (retaddr) = 1;
|
||||
|
||||
/* If there is a pending load to the equivalent register for SP
|
||||
and we reference that register, we must load our address into
|
||||
a scratch register and then do that load. */
|
||||
if (info.equiv_reg_src
|
||||
&& reg_overlap_mentioned_p (info.equiv_reg_src, retaddr))
|
||||
{
|
||||
unsigned int regno;
|
||||
rtx reg;
|
||||
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
if (HARD_REGNO_MODE_OK (regno, Pmode)
|
||||
&& !fixed_regs[regno]
|
||||
&& TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)
|
||||
&& !REGNO_REG_SET_P
|
||||
(DF_LR_IN (EXIT_BLOCK_PTR), regno)
|
||||
&& !refers_to_regno_p (regno,
|
||||
end_hard_regno (Pmode, regno),
|
||||
info.equiv_reg_src, NULL)
|
||||
&& info.const_equiv[regno] == 0)
|
||||
break;
|
||||
|
||||
gcc_assert (regno < FIRST_PSEUDO_REGISTER);
|
||||
|
||||
reg = gen_rtx_REG (Pmode, regno);
|
||||
emit_move_insn (reg, retaddr);
|
||||
retaddr = reg;
|
||||
}
|
||||
|
||||
emit_equiv_load (&info);
|
||||
jump_insn = emit_jump_insn (gen_indirect_jump (retaddr));
|
||||
|
||||
/* Show the SET in the above insn is a RETURN. */
|
||||
jump_set = single_set (jump_insn);
|
||||
gcc_assert (jump_set);
|
||||
SET_IS_RETURN_P (jump_set) = 1;
|
||||
}
|
||||
|
||||
/* If SP is not mentioned in the pattern and its equivalent register, if
|
||||
any, is not modified, just emit it. Otherwise, if neither is set,
|
||||
replace the reference to SP and emit the insn. If none of those are
|
||||
true, handle each SET individually. */
|
||||
else if (!reg_mentioned_p (stack_pointer_rtx, PATTERN (insn))
|
||||
&& (info.sp_equiv_reg == stack_pointer_rtx
|
||||
|| !reg_set_p (info.sp_equiv_reg, insn)))
|
||||
add_insn (insn);
|
||||
else if (! reg_set_p (stack_pointer_rtx, insn)
|
||||
&& (info.sp_equiv_reg == stack_pointer_rtx
|
||||
|| !reg_set_p (info.sp_equiv_reg, insn)))
|
||||
{
|
||||
int changed;
|
||||
|
||||
changed = validate_replace_rtx (stack_pointer_rtx,
|
||||
plus_constant (info.sp_equiv_reg,
|
||||
info.sp_offset),
|
||||
insn);
|
||||
gcc_assert (changed);
|
||||
|
||||
add_insn (insn);
|
||||
}
|
||||
else if (GET_CODE (PATTERN (insn)) == SET)
|
||||
handle_epilogue_set (PATTERN (insn), &info);
|
||||
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
|
||||
{
|
||||
for (j = 0; j < XVECLEN (PATTERN (insn), 0); j++)
|
||||
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET)
|
||||
handle_epilogue_set (XVECEXP (PATTERN (insn), 0, j), &info);
|
||||
}
|
||||
else
|
||||
add_insn (insn);
|
||||
|
||||
info.sp_equiv_reg = info.new_sp_equiv_reg;
|
||||
info.sp_offset = info.new_sp_offset;
|
||||
|
||||
/* Now update any constants this insn sets. */
|
||||
note_stores (PATTERN (insn), update_epilogue_consts, &info);
|
||||
insn = next;
|
||||
}
|
||||
|
||||
insns = get_insns ();
|
||||
end_sequence ();
|
||||
return insns;
|
||||
}
|
||||
|
||||
/* SET is a SET from an insn in the epilogue. P is a pointer to the epi_info
|
||||
structure that contains information about what we've seen so far. We
|
||||
process this SET by either updating that data or by emitting one or
|
||||
more insns. */
|
||||
|
||||
static void
|
||||
handle_epilogue_set (rtx set, struct epi_info *p)
|
||||
{
|
||||
/* First handle the case where we are setting SP. Record what it is being
|
||||
set from, which we must be able to determine */
|
||||
if (reg_set_p (stack_pointer_rtx, set))
|
||||
{
|
||||
gcc_assert (SET_DEST (set) == stack_pointer_rtx);
|
||||
|
||||
if (GET_CODE (SET_SRC (set)) == PLUS)
|
||||
{
|
||||
p->new_sp_equiv_reg = XEXP (SET_SRC (set), 0);
|
||||
if (GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT)
|
||||
p->new_sp_offset = INTVAL (XEXP (SET_SRC (set), 1));
|
||||
else
|
||||
{
|
||||
gcc_assert (REG_P (XEXP (SET_SRC (set), 1))
|
||||
&& (REGNO (XEXP (SET_SRC (set), 1))
|
||||
< FIRST_PSEUDO_REGISTER)
|
||||
&& p->const_equiv[REGNO (XEXP (SET_SRC (set), 1))]);
|
||||
p->new_sp_offset
|
||||
= INTVAL (p->const_equiv[REGNO (XEXP (SET_SRC (set), 1))]);
|
||||
}
|
||||
}
|
||||
else
|
||||
p->new_sp_equiv_reg = SET_SRC (set), p->new_sp_offset = 0;
|
||||
|
||||
/* If we are adjusting SP, we adjust from the old data. */
|
||||
if (p->new_sp_equiv_reg == stack_pointer_rtx)
|
||||
{
|
||||
p->new_sp_equiv_reg = p->sp_equiv_reg;
|
||||
p->new_sp_offset += p->sp_offset;
|
||||
}
|
||||
|
||||
gcc_assert (p->new_sp_equiv_reg && REG_P (p->new_sp_equiv_reg));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Next handle the case where we are setting SP's equivalent
|
||||
register. We must not already have a value to set it to. We
|
||||
could update, but there seems little point in handling that case.
|
||||
Note that we have to allow for the case where we are setting the
|
||||
register set in the previous part of a PARALLEL inside a single
|
||||
insn. But use the old offset for any updates within this insn.
|
||||
We must allow for the case where the register is being set in a
|
||||
different (usually wider) mode than Pmode). */
|
||||
else if (p->new_sp_equiv_reg != 0 && reg_set_p (p->new_sp_equiv_reg, set))
|
||||
{
|
||||
gcc_assert (!p->equiv_reg_src
|
||||
&& REG_P (p->new_sp_equiv_reg)
|
||||
&& REG_P (SET_DEST (set))
|
||||
&& (GET_MODE_BITSIZE (GET_MODE (SET_DEST (set)))
|
||||
<= BITS_PER_WORD)
|
||||
&& REGNO (p->new_sp_equiv_reg) == REGNO (SET_DEST (set)));
|
||||
p->equiv_reg_src
|
||||
= simplify_replace_rtx (SET_SRC (set), stack_pointer_rtx,
|
||||
plus_constant (p->sp_equiv_reg,
|
||||
p->sp_offset));
|
||||
}
|
||||
|
||||
/* Otherwise, replace any references to SP in the insn to its new value
|
||||
and emit the insn. */
|
||||
else
|
||||
{
|
||||
SET_SRC (set) = simplify_replace_rtx (SET_SRC (set), stack_pointer_rtx,
|
||||
plus_constant (p->sp_equiv_reg,
|
||||
p->sp_offset));
|
||||
SET_DEST (set) = simplify_replace_rtx (SET_DEST (set), stack_pointer_rtx,
|
||||
plus_constant (p->sp_equiv_reg,
|
||||
p->sp_offset));
|
||||
emit_insn (set);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the tracking information for registers set to constants. */
|
||||
|
||||
static void
|
||||
update_epilogue_consts (rtx dest, const_rtx x, void *data)
|
||||
{
|
||||
struct epi_info *p = (struct epi_info *) data;
|
||||
rtx new;
|
||||
|
||||
if (!REG_P (dest) || REGNO (dest) >= FIRST_PSEUDO_REGISTER)
|
||||
return;
|
||||
|
||||
/* If we are either clobbering a register or doing a partial set,
|
||||
show we don't know the value. */
|
||||
else if (GET_CODE (x) == CLOBBER || ! rtx_equal_p (dest, SET_DEST (x)))
|
||||
p->const_equiv[REGNO (dest)] = 0;
|
||||
|
||||
/* If we are setting it to a constant, record that constant. */
|
||||
else if (GET_CODE (SET_SRC (x)) == CONST_INT)
|
||||
p->const_equiv[REGNO (dest)] = SET_SRC (x);
|
||||
|
||||
/* If this is a binary operation between a register we have been tracking
|
||||
and a constant, see if we can compute a new constant value. */
|
||||
else if (ARITHMETIC_P (SET_SRC (x))
|
||||
&& REG_P (XEXP (SET_SRC (x), 0))
|
||||
&& REGNO (XEXP (SET_SRC (x), 0)) < FIRST_PSEUDO_REGISTER
|
||||
&& p->const_equiv[REGNO (XEXP (SET_SRC (x), 0))] != 0
|
||||
&& GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
|
||||
&& 0 != (new = simplify_binary_operation
|
||||
(GET_CODE (SET_SRC (x)), GET_MODE (dest),
|
||||
p->const_equiv[REGNO (XEXP (SET_SRC (x), 0))],
|
||||
XEXP (SET_SRC (x), 1)))
|
||||
&& GET_CODE (new) == CONST_INT)
|
||||
p->const_equiv[REGNO (dest)] = new;
|
||||
|
||||
/* Otherwise, we can't do anything with this value. */
|
||||
else
|
||||
p->const_equiv[REGNO (dest)] = 0;
|
||||
}
|
||||
|
||||
/* Emit an insn to do the load shown in p->equiv_reg_src, if needed. */
|
||||
|
||||
static void
|
||||
emit_equiv_load (struct epi_info *p)
|
||||
{
|
||||
if (p->equiv_reg_src != 0)
|
||||
{
|
||||
rtx dest = p->sp_equiv_reg;
|
||||
|
||||
if (GET_MODE (p->equiv_reg_src) != GET_MODE (dest))
|
||||
dest = gen_rtx_REG (GET_MODE (p->equiv_reg_src),
|
||||
REGNO (p->sp_equiv_reg));
|
||||
|
||||
emit_move_insn (dest, p->equiv_reg_src);
|
||||
p->equiv_reg_src = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate the prologue and epilogue RTL if the machine supports it. Thread
|
||||
this into place with notes indicating where the prologue ends and where
|
||||
the epilogue begins. Update the basic block information when possible. */
|
||||
|
@ -5274,17 +4894,7 @@ thread_prologue_and_epilogue_insns (void)
|
|||
{
|
||||
start_sequence ();
|
||||
epilogue_end = emit_note (NOTE_INSN_EPILOGUE_BEG);
|
||||
|
||||
seq = gen_epilogue ();
|
||||
|
||||
#ifdef INCOMING_RETURN_ADDR_RTX
|
||||
/* If this function returns with the stack depressed and we can support
|
||||
it, massage the epilogue to actually do that. */
|
||||
if (TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE
|
||||
&& TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (current_function_decl)))
|
||||
seq = keep_stack_depressed (seq);
|
||||
#endif
|
||||
|
||||
emit_jump_insn (seq);
|
||||
|
||||
/* Retain a map of the epilogue insns. */
|
||||
|
|
|
@ -565,9 +565,6 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
|
|||
else if (TREE_CODE (node) == INTEGER_TYPE
|
||||
&& TYPE_IS_SIZETYPE (node))
|
||||
fputs (" sizetype", file);
|
||||
else if (TREE_CODE (node) == FUNCTION_TYPE
|
||||
&& TYPE_RETURNS_STACK_DEPRESSED (node))
|
||||
fputs (" returns-stack-depressed", file);
|
||||
|
||||
if (TYPE_STRING_FLAG (node))
|
||||
fputs (" string-flag", file);
|
||||
|
|
12
gcc/tree.h
12
gcc/tree.h
|
@ -2207,11 +2207,6 @@ struct tree_block GTY(())
|
|||
#define TYPE_IS_SIZETYPE(NODE) \
|
||||
(INTEGER_TYPE_CHECK (NODE)->type.no_force_blk_flag)
|
||||
|
||||
/* In a FUNCTION_TYPE, indicates that the function returns with the stack
|
||||
pointer depressed. */
|
||||
#define TYPE_RETURNS_STACK_DEPRESSED(NODE) \
|
||||
(FUNCTION_TYPE_CHECK (NODE)->type.no_force_blk_flag)
|
||||
|
||||
/* Nonzero in a type considered volatile as a whole. */
|
||||
#define TYPE_VOLATILE(NODE) (TYPE_CHECK (NODE)->base.volatile_flag)
|
||||
|
||||
|
@ -5002,14 +4997,11 @@ extern tree build_duplicate_type (tree);
|
|||
/* Nonzero if this is a call to "pure" function (like const function,
|
||||
but may read memory. */
|
||||
#define ECF_PURE 128
|
||||
/* Nonzero if this is a call to a function that returns with the stack
|
||||
pointer depressed. */
|
||||
#define ECF_SP_DEPRESSED 256
|
||||
/* Create libcall block around the call. */
|
||||
#define ECF_LIBCALL_BLOCK 512
|
||||
#define ECF_LIBCALL_BLOCK 256
|
||||
/* Function does not read or write memory (but may have side effects, so
|
||||
it does not necessarily fit ECF_CONST). */
|
||||
#define ECF_NOVOPS 1024
|
||||
#define ECF_NOVOPS 512
|
||||
|
||||
extern int flags_from_decl_or_type (const_tree);
|
||||
extern int call_expr_flags (const_tree);
|
||||
|
|
Loading…
Reference in New Issue