#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 *);
}
#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. */
{
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. */