-#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
-