if (old_stack_level == 0)
{
- old_stack_level = copy_to_mode_reg (Pmode, stack_pointer_rtx);
+ emit_stack_save (SAVE_BLOCK, &old_stack_level, 0);
old_pending_adj = pending_stack_adjust;
pending_stack_adjust = 0;
}
{
if (old_stack_level == 0)
{
- old_stack_level = copy_to_mode_reg (Pmode, stack_pointer_rtx);
+ emit_stack_save (SAVE_BLOCK, &old_stack_level, 0);
old_pending_adj = pending_stack_adjust;
pending_stack_adjust = 0;
}
if (old_stack_level)
{
- emit_move_insn (stack_pointer_rtx, old_stack_level);
+ emit_stack_restore (SAVE_BLOCK, old_stack_level, 0);
pending_stack_adjust = old_pending_adj;
}
}
#endif
- /* If this was alloca, record the new stack level for nonlocal gotos. */
- if (may_be_alloca && nonlocal_goto_stack_level != 0)
- emit_move_insn (nonlocal_goto_stack_level, stack_pointer_rtx);
+ /* If this was alloca, record the new stack level for nonlocal gotos.
+ Check for the handler slots since we might not have a save area
+ for non-local gotos. */
+
+ if (may_be_alloca && nonlocal_goto_handler_slot != 0)
+ emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, 0);
pop_temp_slots ();
;; Next come insns related to the calling sequence.
;;
;; First, an insn to allocate new stack space for dynamic use (e.g., alloca).
-;; We move the back-chain and decrement the stack pointer. This is slightly
-;; less efficient than it needs to be for long constants, but that case
-;; should be rare.
+;; We move the back-chain and decrement the stack pointer.
(define_expand "allocate_stack"
[(set (reg:SI 1)
emit_move_insn (stack_bot, chain);
DONE;
}")
+
+;; These patterns say how to save and restore the stack pointer. We need not
+;; save the stack pointer at function level since we are careful to
+;; preserve the backchain. At block level, we have to restore the backchain
+;; when we restore the stack pointer.
+;;
+;; For nonlocal gotos, we must save both the stack pointer and its
+;; backchain and restore both. Note that in the nonlocal case, the
+;; save area is a memory location.
+
+(define_expand "save_stack_function"
+ [(use (const_int 0))]
+ ""
+ "")
+
+(define_expand "restore_stack_function"
+ [(use (const_int 0))]
+ ""
+ "")
+
+(define_expand "restore_stack_block"
+ [(set (match_dup 2) (mem:SI (match_operand:SI 0 "register_operand" "")))
+ (set (match_dup 0) (match_operand:SI 1 "register_operand" ""))
+ (set (mem:SI (match_dup 0)) (match_dup 2))]
+ ""
+ "
+{ operands[2] = gen_reg_rtx (SImode); }")
+
+(define_expand "save_stack_nonlocal"
+ [(match_operand:DI 0 "memory_operand" "")
+ (match_operand:SI 1 "register_operand" "")]
+ ""
+ "
+{
+ rtx temp = gen_reg_rtx (SImode);
+
+ /* Copy the backchain to the first word, sp to the second. */
+ emit_move_insn (temp, gen_rtx (MEM, SImode, operands[1]));
+ emit_move_insn (operand_subword (operands[0], 0, 0, DImode), temp);
+ emit_move_insn (operand_subword (operands[0], 1, 0, DImode), operands[1]);
+ DONE;
+}")
+
+(define_expand "restore_stack_nonlocal"
+ [(match_operand:SI 0 "register_operand" "")
+ (match_operand:DI 1 "memory_operand" "")]
+ ""
+ "
+{
+ rtx temp = gen_reg_rtx (SImode);
+
+ /* Restore the backchain from the first word, sp from the second. */
+ emit_move_insn (temp, operand_subword (operands[1], 0, 0, DImode));
+ emit_move_insn (operands[0], operand_subword (operands[1], 1, 0, DImode));
+ emit_move_insn (gen_rtx (MEM, SImode, operands[0]), temp);
+ DONE;
+}")
\f
;; A function pointer is a pointer to a data area whose first word contains
;; the actual address of the function, whose second word contains a pointer
return size;
}
\f
+/* Save the stack pointer for the purpose in SAVE_LEVEL. PSAVE is a pointer
+ to a previously-created save area. If no save area has been allocated,
+ this function will allocate one. If a save area is specified, it
+ must be of the proper mode.
+
+ The insns are emitted after insn AFTER, if nonzero, otherwise the insns
+ are emitted at the current position. */
+
+void
+emit_stack_save (save_level, psave, after)
+ enum save_level save_level;
+ rtx *psave;
+ rtx after;
+{
+ rtx sa = *psave;
+ /* The default is that we use a move insn and save in a Pmode object. */
+ rtx (*fcn) () = gen_move_insn;
+ enum machine_mode mode = Pmode;
+
+ /* See if this machine has anything special to do for this kind of save. */
+ switch (save_level)
+ {
+#ifdef HAVE_save_stack_block
+ case SAVE_BLOCK:
+ if (HAVE_save_stack_block)
+ {
+ fcn = gen_save_stack_block;
+ mode = insn_operand_mode[CODE_FOR_save_stack_block][0];
+ }
+ break;
+#endif
+#ifdef HAVE_save_stack_function
+ case SAVE_FUNCTION:
+ if (HAVE_save_stack_function)
+ {
+ fcn = gen_save_stack_function;
+ mode = insn_operand_mode[CODE_FOR_save_stack_function][0];
+ }
+ break;
+#endif
+#ifdef HAVE_save_stack_nonlocal
+ case SAVE_NONLOCAL:
+ if (HAVE_save_stack_nonlocal)
+ {
+ fcn = gen_save_stack_nonlocal;
+ mode = insn_operand_mode[CODE_FOR_save_stack_nonlocal][0];
+ }
+ break;
+#endif
+ }
+
+ /* If there is no save area and we have to allocate one, do so. Otherwise
+ verify the save area is the proper mode. */
+
+ if (sa == 0)
+ {
+ if (mode != VOIDmode)
+ {
+ if (save_level == SAVE_NONLOCAL)
+ *psave = sa = assign_stack_local (mode, GET_MODE_SIZE (mode), 0);
+ else
+ *psave = sa = gen_reg_rtx (mode);
+ }
+ }
+ else
+ {
+ if (mode == VOIDmode || GET_MODE (sa) != mode)
+ abort ();
+ }
+
+ if (after)
+ emit_insn_after (fcn (sa, stack_pointer_rtx), after);
+ else
+ emit_insn (fcn (sa, stack_pointer_rtx));
+}
+
+/* Restore the stack pointer for the purpose in SAVE_LEVEL. SA is the save
+ area made by emit_stack_save. If it is zero, we have nothing to do.
+
+ Put any emitted insns after insn AFTER, if nonzero, otherwise at
+ current position. */
+
+void
+emit_stack_restore (save_level, sa, after)
+ enum save_level save_level;
+ rtx after;
+ rtx sa;
+{
+ /* The default is that we use a move insn. */
+ rtx (*fcn) () = gen_move_insn;
+
+ /* See if this machine has anything special to do for this kind of save. */
+ switch (save_level)
+ {
+#ifdef HAVE_restore_stack_block
+ case SAVE_BLOCK:
+ if (HAVE_restore_stack_block)
+ fcn = gen_restore_stack_block;
+ break;
+#endif
+#ifdef HAVE_restore_stack_function
+ case SAVE_FUNCTION:
+ if (HAVE_restore_stack_function)
+ fcn = gen_restore_stack_function;
+ break;
+#endif
+#ifdef HAVE_restore_stack_nonlocal
+
+ case SAVE_NONLOCAL:
+ if (HAVE_restore_stack_nonlocal)
+ fcn = gen_restore_stack_nonlocal;
+ break;
+#endif
+ }
+
+ if (after)
+ emit_insn_after (fcn (stack_pointer_rtx, sa), after);
+ else
+ emit_insn (fcn (stack_pointer_rtx, sa));
+}
+\f
/* Return an rtx representing the address of an area of memory dynamically
pushed on the stack. This region of memory is always aligned to
a multiple of BIGGEST_ALIGNMENT.
/* Add some bytes to the stack. An rtx says how many. */
extern void anti_adjust_stack ();
+/* This enum is used for the following two functions. */
+enum save_level {SAVE_BLOCK, SAVE_FUNCTION, SAVE_NONLOCAL};
+
+/* Save the stack pointer at the specified level. */
+extern void emit_stack_save ();
+
+/* Restore the stack pointer from a save area of the specified level. */
+extern void emit_stack_restore ();
+
/* Allocate some space on the stack dynamically and return its address. An rtx
says how many bytes. */
extern rtx allocate_dynamic_stack_space ();
if (GET_CODE (insn) == CODE_LABEL)
LABEL_PRESERVE_P (insn) = 0;
if (GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == SET
- && (SET_DEST (PATTERN (insn)) == nonlocal_goto_handler_slot
- || SET_SRC (PATTERN (insn)) == nonlocal_goto_handler_slot
- || SET_DEST (PATTERN (insn)) == nonlocal_goto_stack_level
- || SET_SRC (PATTERN (insn)) == nonlocal_goto_stack_level))
+ && ((nonlocal_goto_handler_slot != 0
+ && reg_mentioned_p (nonlocal_goto_handler_slot, PATTERN (insn)))
+ || (nonlocal_goto_stack_level != 0
+ && reg_mentioned_p (nonlocal_goto_stack_level,
+ PATTERN (insn)))))
delete_insn (insn);
}
}
#endif
if (current_function_calls_alloca)
{
- rtx tem = gen_reg_rtx (Pmode);
- emit_insn_after (gen_rtx (SET, VOIDmode, tem, stack_pointer_rtx),
- parm_birth_insn);
- emit_insn (gen_rtx (SET, VOIDmode, stack_pointer_rtx, tem));
+ rtx tem = 0;
+
+ emit_stack_save (SAVE_FUNCTION, &tem, parm_birth_insn);
+ emit_stack_restore (SAVE_FUNCTION, tem, 0);
}
/* If scalar return value was computed in a pseudo-reg,
{
nonlocal_goto_handler_slot
= assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
- nonlocal_goto_stack_level
- = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
- emit_insn_before (gen_move_insn (nonlocal_goto_stack_level,
- stack_pointer_rtx),
- tail_recursion_reentry);
+ emit_stack_save (SAVE_NONLOCAL,
+ &nonlocal_goto_stack_level,
+ PREV_INSN (tail_recursion_reentry));
}
}
struct function *p = find_function_data (context);
rtx temp;
p->has_nonlocal_label = 1;
+
+ /* Copy the rtl for the slots so that they won't be shared in
+ case the virtual stack vars register gets instantiated differently
+ in the parent than in the child. */
+
#if HAVE_nonlocal_goto
if (HAVE_nonlocal_goto)
emit_insn (gen_nonlocal_goto (lookup_static_chain (label),
- p->nonlocal_goto_handler_slot,
- p->nonlocal_goto_stack_level,
+ copy_rtx (p->nonlocal_goto_handler_slot),
+ copy_rtx (p->nonlocal_goto_stack_level),
gen_rtx (LABEL_REF, Pmode,
label_rtx (label))));
else
#endif
{
+ rtx addr;
+
/* Restore frame pointer for containing function.
This sets the actual hard register used for the frame pointer
to the location of the function's incoming static chain info.
The non-local goto handler will then adjust it to contain the
proper value and reload the argument pointer, if needed. */
emit_move_insn (frame_pointer_rtx, lookup_static_chain (label));
+
+ /* We have now loaded the frame pointer hardware register with
+ the address of that corresponds to the start of the virtual
+ stack vars. So replace virtual_stack_vars_rtx in all
+ addresses we use with stack_pointer_rtx. */
+
/* Get addr of containing function's current nonlocal goto handler,
which will do any cleanups and then jump to the label. */
- temp = copy_to_reg (p->nonlocal_goto_handler_slot);
+ addr = copy_rtx (p->nonlocal_goto_handler_slot);
+ temp = copy_to_reg (replace_rtx (addr, virtual_stack_vars_rtx,
+ frame_pointer_rtx));
+
/* Restore the stack pointer. Note this uses fp just restored. */
- emit_move_insn (stack_pointer_rtx, p->nonlocal_goto_stack_level);
+ addr = p->nonlocal_goto_stack_level;
+ if (addr)
+ addr = replace_rtx (copy_rtx (p->nonlocal_goto_stack_level),
+ replace_rtx (addr, virtual_stack_vars_rtx,
+ frame_pointer_rtx));
+
+ emit_stack_restore (SAVE_NONLOCAL, addr, 0);
+
/* Put in the static chain register the nonlocal label address. */
emit_move_insn (static_chain_rtx,
gen_rtx (LABEL_REF, Pmode, label_rtx (label)));
the stack pointer. This one should be deleted as dead by flow. */
clear_pending_stack_adjust ();
do_pending_stack_adjust ();
- emit_move_insn (stack_pointer_rtx, stack_level);
+ emit_stack_restore (SAVE_BLOCK, stack_level, 0);
}
if (body != 0 && DECL_TOO_LATE (body))
/* Restore stack level for the biggest contour that this
jump jumps out of. */
if (f->stack_level)
- emit_insn_after (gen_move_insn (stack_pointer_rtx, f->stack_level),
- f->before_jump);
+ emit_stack_restore (SAVE_BLOCK, f->stack_level, f->before_jump);
f->before_jump = 0;
}
}
if (thisblock->data.block.stack_level != 0)
{
- emit_move_insn (stack_pointer_rtx,
- thisblock->data.block.stack_level);
- if (nonlocal_goto_stack_level != 0)
- emit_move_insn (nonlocal_goto_stack_level, stack_pointer_rtx);
+ emit_stack_restore (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
+ thisblock->data.block.stack_level, 0);
+ if (nonlocal_goto_handler_slot != 0)
+ emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, 0);
}
/* Any gotos out of this block must also do these things.
- Also report any gotos with fixups that came to labels in this level. */
+ Also report any gotos with fixups that came to labels in this
+ level. */
fixup_gotos (thisblock,
thisblock->data.block.stack_level,
thisblock->data.block.cleanups,
if (thisblock->data.block.stack_level == 0)
{
do_pending_stack_adjust ();
- thisblock->data.block.stack_level
- = copy_to_reg (stack_pointer_rtx);
+ emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
+ &thisblock->data.block.stack_level,
+ thisblock->data.block.first_insn);
stack_block_stack = thisblock;
}
0, VOIDmode, 0);
free_temp_slots ();
+ /* This is equivalent to calling alloca. */
+ current_function_calls_alloca = 1;
+
/* Allocate space on the stack for the variable. */
address = allocate_dynamic_stack_space (size, 0, DECL_ALIGN (decl));
- if (nonlocal_goto_stack_level != 0)
- emit_move_insn (nonlocal_goto_stack_level, stack_pointer_rtx);
+ if (nonlocal_goto_handler_slot != 0)
+ emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, 0);
/* Reference the variable indirect through that rtx. */
DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), address);