Commit 3258e996 by Richard Kenner Committed by Richard Kenner

function.c (keep_stack_depressed): Major rework.

	* function.c (keep_stack_depressed): Major rework.
	(handle_epilogue_set, emit_equiv_load): New functions.
	(thread_prologue_and_epilogue_insns): keep_stack_depressed now
	has return value.
	* jump.c (returnjump_p_1): Also return 1 if SET with SET_IS_RETURN_P.
	* rtl.h (SET_IS_RETURN_P): New macro.

From-SVN: r47250
parent c11c10d8
Wed Nov 21 17:37:16 2001 Richard Kenner <kenner@vlsi1.ultra.nyu.edu> Wed Nov 21 17:37:16 2001 Richard Kenner <kenner@vlsi1.ultra.nyu.edu>
* function.c (keep_stack_depressed): Major rework.
(handle_epilogue_set, emit_equiv_load): New functions.
(thread_prologue_and_epilogue_insns): keep_stack_depressed now
has return value.
* jump.c (returnjump_p_1): Also return 1 if SET with SET_IS_RETURN_P.
* rtl.h (SET_IS_RETURN_P): New macro.
* expr.c (expand_expr, case VIEW_CONVERT_EXPR): Refine slightly * expr.c (expand_expr, case VIEW_CONVERT_EXPR): Refine slightly
and also support TREE_ADDRESSABLE. and also support TREE_ADDRESSABLE.
* tree.def (VIEW_CONVERT_EXPR): Document TREE_ADDRESSABLE. * tree.def (VIEW_CONVERT_EXPR): Document TREE_ADDRESSABLE.
......
...@@ -288,8 +288,8 @@ static void put_addressof_into_stack PARAMS ((rtx, struct hash_table *)); ...@@ -288,8 +288,8 @@ static void put_addressof_into_stack PARAMS ((rtx, struct hash_table *));
static bool purge_addressof_1 PARAMS ((rtx *, rtx, int, int, static bool purge_addressof_1 PARAMS ((rtx *, rtx, int, int,
struct hash_table *)); struct hash_table *));
static void purge_single_hard_subreg_set PARAMS ((rtx)); static void purge_single_hard_subreg_set PARAMS ((rtx));
#ifdef HAVE_epilogue #if defined(HAVE_epilogue) && defined(INCOMING_RETURN_ADDR_RTX)
static void keep_stack_depressed PARAMS ((rtx)); static rtx keep_stack_depressed PARAMS ((rtx));
#endif #endif
static int is_addressof PARAMS ((rtx *, void *)); static int is_addressof PARAMS ((rtx *, void *));
static struct hash_entry *insns_for_mem_newfunc PARAMS ((struct hash_entry *, static struct hash_entry *insns_for_mem_newfunc PARAMS ((struct hash_entry *,
...@@ -7115,68 +7115,296 @@ emit_return_into_block (bb, line_note) ...@@ -7115,68 +7115,296 @@ emit_return_into_block (bb, line_note)
} }
#endif /* HAVE_return */ #endif /* HAVE_return */
#ifdef HAVE_epilogue #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 leavs 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.
If we can't be sure of what a relevant epilogue insn is doing, we abort.
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. */
};
static void handle_epilogue_set PARAMS ((rtx, struct epi_info *));
static void emit_equiv_load PARAMS ((struct epi_info *));
/* Modify SEQ, a SEQUENCE that is part of the epilogue, to no modifications /* Modify SEQ, a SEQUENCE that is part of the epilogue, to no modifications
to the stack pointer. */ to the stack pointer. Return the new sequence. */
static void static rtx
keep_stack_depressed (seq) keep_stack_depressed (seq)
rtx seq; rtx seq;
{ {
int i; int i, j;
rtx sp_from_reg = 0; struct epi_info info;
int sp_modified_unknown = 0;
/* If the epilogue is just a single instruction, it's OK as is */ /* If the epilogue is just a single instruction, it ust be OK as is. */
if (GET_CODE (seq) != SEQUENCE) if (GET_CODE (seq) != SEQUENCE)
return; return seq;
/* Scan all insns in SEQ looking for ones that modified the stack /* Otherwise, start a sequence, initialize the information we have, and
pointer. Record if it modified the stack pointer by copying it process all the insns we were given. */
from the frame pointer or if it modified it in some other way. start_sequence ();
Then modify any subsequent stack pointer references to take that
into account. We start by only allowing SP to be copied from a info.sp_equiv_reg = stack_pointer_rtx;
register (presumably FP) and then be subsequently referenced. */ info.sp_offset = 0;
info.equiv_reg_src = 0;
for (i = 0; i < XVECLEN (seq, 0); i++) for (i = 0; i < XVECLEN (seq, 0); i++)
{ {
rtx insn = XVECEXP (seq, 0, i); rtx insn = XVECEXP (seq, 0, i);
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i') if (!INSN_P (insn))
{
add_insn (insn);
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 (GET_CODE (retaddr) == REG)
{
emit_equiv_load (&info);
add_insn (insn);
continue; continue;
}
else if (GET_CODE (retaddr) == MEM
&& GET_CODE (XEXP (retaddr, 0)) == REG)
base = gen_rtx_REG (Pmode, REGNO (XEXP (retaddr, 0))), offset = 0;
else if (GET_CODE (retaddr) == MEM
&& GET_CODE (XEXP (retaddr, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (retaddr, 0), 0)) == REG
&& GET_CODE (XEXP (XEXP (retaddr, 0), 1)) == CONST_INT)
{
base = gen_rtx_REG (Pmode, REGNO (XEXP (XEXP (retaddr, 0), 0)));
offset = INTVAL (XEXP (XEXP (retaddr, 0), 1));
}
else
abort ();
if (reg_set_p (stack_pointer_rtx, insn)) /* 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);
/* 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))
{ {
rtx set = single_set (insn); unsigned int regno;
rtx reg;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (HARD_REGNO_MODE_OK (regno, Pmode)
&& !fixed_regs[regno] && call_used_regs[regno]
&& !FUNCTION_VALUE_REGNO_P (regno))
break;
/* If SP is set as a side-effect, we can't support this. */ if (regno == FIRST_PSEUDO_REGISTER)
if (set == 0)
abort (); abort ();
if (GET_CODE (SET_SRC (set)) == REG) reg = gen_rtx_REG (Pmode, regno);
sp_from_reg = SET_SRC (set); 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);
if (jump_set == 0)
abort ();
else else
sp_modified_unknown = 1; 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)))
{
if (! validate_replace_rtx (stack_pointer_rtx,
plus_constant (info.sp_equiv_reg,
info.sp_offset),
insn))
abort ();
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);
/* Don't allow the SP modification to happen. We don't call info.sp_equiv_reg = info.new_sp_equiv_reg;
delete_insn here since INSN isn't in any chain. */ info.sp_offset = info.new_sp_offset;
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
} }
else if (reg_referenced_p (stack_pointer_rtx, PATTERN (insn)))
seq = gen_sequence ();
end_sequence ();
return seq;
}
/* SET is a SET from an insn in the epilogue. P is a pointr 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 (set, p)
rtx set;
struct epi_info *p;
{
/* First handle the case where we are setting SP. Record what it is being
set from. If unknown, abort. */
if (reg_set_p (stack_pointer_rtx, set))
{ {
if (sp_modified_unknown) if (SET_DEST (set) != stack_pointer_rtx)
abort (); abort ();
else if (sp_from_reg != 0) if (GET_CODE (SET_SRC (set)) == PLUS
PATTERN (insn) && GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT)
= replace_rtx (PATTERN (insn), stack_pointer_rtx, sp_from_reg); {
p->new_sp_equiv_reg = XEXP (SET_SRC (set), 0);
p->new_sp_offset = INTVAL (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;
}
if (p->new_sp_equiv_reg == 0 || GET_CODE (p->new_sp_equiv_reg) != REG)
abort ();
return;
}
/* Next handle the case where we are setting SP's equivalent register.
If we already have a value to set it to, abort. We could update, but
there seems little point in handling that case. */
else if (p->sp_equiv_reg != 0 && reg_set_p (p->sp_equiv_reg, set))
{
if (!rtx_equal_p (p->sp_equiv_reg, SET_DEST (set))
|| p->equiv_reg_src != 0)
abort ();
else
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);
} }
} }
/* Emit an insn to do the load shown in p->equiv_reg_src, if needed. */
static void
emit_equiv_load (p)
struct epi_info *p;
{
if (p->equiv_reg_src != 0)
emit_move_insn (p->sp_equiv_reg, p->equiv_reg_src);
p->equiv_reg_src = 0;
}
#endif #endif
/* Generate the prologue and epilogue RTL if the machine supports it. Thread /* Generate the prologue and epilogue RTL if the machine supports it. Thread
...@@ -7360,11 +7588,13 @@ thread_prologue_and_epilogue_insns (f) ...@@ -7360,11 +7588,13 @@ thread_prologue_and_epilogue_insns (f)
seq = gen_epilogue (); seq = gen_epilogue ();
/* If this function returns with the stack depressed, massage #ifdef INCOMING_RETURN_ADDR_RTX
the epilogue to actually do that. */ /* 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 if (TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE
&& TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (current_function_decl))) && TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (current_function_decl)))
keep_stack_depressed (seq); seq = keep_stack_depressed (seq);
#endif
emit_jump_insn (seq); emit_jump_insn (seq);
......
...@@ -1244,7 +1244,9 @@ returnjump_p_1 (loc, data) ...@@ -1244,7 +1244,9 @@ returnjump_p_1 (loc, data)
void *data ATTRIBUTE_UNUSED; void *data ATTRIBUTE_UNUSED;
{ {
rtx x = *loc; rtx x = *loc;
return x && GET_CODE (x) == RETURN;
return x && (GET_CODE (x) == RETURN
|| (GET_CODE (x) == SET && SET_IS_RETURN_P (x)));
} }
int int
......
...@@ -130,7 +130,8 @@ struct rtx_def ...@@ -130,7 +130,8 @@ struct rtx_def
/* 1 in an INSN if it can alter flow of control /* 1 in an INSN if it can alter flow of control
within this function. within this function.
MEM_KEEP_ALIAS_SET_P in a MEM. MEM_KEEP_ALIAS_SET_P in a MEM.
LINK_COST_ZERO in an INSN_LIST. */ LINK_COST_ZERO in an INSN_LIST.
SET_IS_RETURN_P in a SET. */
unsigned int jump : 1; unsigned int jump : 1;
/* 1 in an INSN if it can call another function. /* 1 in an INSN if it can call another function.
LINK_COST_FREE in an INSN_LIST. */ LINK_COST_FREE in an INSN_LIST. */
...@@ -965,6 +966,7 @@ extern unsigned int subreg_regno PARAMS ((rtx)); ...@@ -965,6 +966,7 @@ extern unsigned int subreg_regno PARAMS ((rtx));
and SET_SRC is the value it is set to. */ and SET_SRC is the value it is set to. */
#define SET_DEST(RTX) XC2EXP(RTX, 0, SET, CLOBBER) #define SET_DEST(RTX) XC2EXP(RTX, 0, SET, CLOBBER)
#define SET_SRC(RTX) XCEXP(RTX, 1, SET) #define SET_SRC(RTX) XCEXP(RTX, 1, SET)
#define SET_IS_RETURN_P(RTX) ((RTX)->jump)
/* For a TRAP_IF rtx, TRAP_CONDITION is an expression. */ /* For a TRAP_IF rtx, TRAP_CONDITION is an expression. */
#define TRAP_CONDITION(RTX) XCEXP(RTX, 0, TRAP_IF) #define TRAP_CONDITION(RTX) XCEXP(RTX, 0, TRAP_IF)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment