I don't know if there are any CFI experts out there but I am working on
dynamic stack alignment for MIPS. I think I have it working in the 'normal'
case but when I try to do stack unwinding through a routine with an aligned
stack, then I have problems. I was wondering if someone can help me understand
what CFI directives to generate to allow stack unwinding. Using
gcc.dg/cleanup-8.c as an example (because it fails with my stack alignment
code), if I generate code with no dynamic stack alignment (but forcing the
use of the frame pointer), the routine fn2 looks like this on MIPS:
fn2:
.frame $fp,32,$31 # vars= 0, regs= 2/0, args= 16, gp= 8
.mask 0xc0000000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
lui $2,%hi(null)
addiu $sp,$sp,-32
.cfi_def_cfa_offset 32
lw $2,%lo(null)($2)
sw $fp,24($sp)
.cfi_offset 30, -8
move $fp,$sp
.cfi_def_cfa_register 30
sw $31,28($sp)
.cfi_offset 31, -4
jal abort
sb $0,0($2)
There are .cfi directives when incrementing the stack pointer, saving the
frame pointer, and copying the stack pointer to the frame pointer.
When I generate code to dynamically align the stack my code looks like
this:
fn2:
.frame $fp,32,$31 # vars= 0, regs= 2/0, args= 16, gp= 8
.mask 0xc0000000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
lui $2,%hi(null)
li $3,-16 # 0xfffffffffffffff0
lw $2,%lo(null)($2)
and $sp,$sp,$3
addiu $sp,$sp,-32
.cfi_def_cfa_offset 32
sw $fp,24($sp)
.cfi_offset 30, -8
move $fp,$sp
.cfi_def_cfa_register 30
sw $31,28($sp)
.cfi_offset 31, -4
jal abort
sb $0,0($2)
The 'and' instruction is where the stack gets aligned and if I remove that
one instruction, everything works. I think I need to put out some new CFI
psuedo-ops to handle this but I am not sure what they should be. I am just
not very familiar with the CFI directives.
I looked at ix86_emit_save_reg_using_mov where there is some special
code for handling the drap register and for saving registers on a
realigned stack but I don't really understand what they are trying
to do.
Any help?
Steve Ellcey
[email protected]
P.S. For completeness sake I have attached my current dynamic
alignment changes in case anyone wants to see them.
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 4f9a31d..386c2ce 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -5737,6 +5737,29 @@ expand_stack_alignment (void)
gcc_assert (targetm.calls.get_drap_rtx != NULL);
drap_rtx = targetm.calls.get_drap_rtx ();
+ /* I am not doing this in get_drap_rtx because we are also calling
+ that from expand_function_end in order to get/set the drap_reg
+ and vdrap_reg variables and doing these instructions at that
+ point is not working. */
+
+ if (drap_rtx != NULL_RTX)
+ {
+ rtx_insn *insn, *seq;
+
+ start_sequence ();
+ emit_move_insn (crtl->vdrap_reg, crtl->drap_reg);
+ seq = get_insns ();
+ insn = get_last_insn ();
+ end_sequence ();
+ emit_insn_at_entry (seq);
+ if (!optimize)
+ {
+ add_reg_note (insn, REG_CFA_SET_VDRAP, crtl->vdrap_reg);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ }
+
+
/* stack_realign_drap and drap_rtx must match. */
gcc_assert ((stack_realign_drap != 0) == (drap_rtx != NULL));
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index ce21a0f..b6ab30a 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -746,6 +746,8 @@ static const struct attribute_spec mips_attribute_table[] = {
{ "use_shadow_register_set", 0, 0, false, true, true, NULL, false },
{ "keep_interrupts_masked", 0, 0, false, true, true, NULL, false },
{ "use_debug_exception_return", 0, 0, false, true, true, NULL, false },
+ { "align_stack", 0, 0, true, false, false, NULL, false },
+ { "no_align_stack", 0, 0, true, false, false, NULL, false },
{ NULL, 0, 0, false, false, false, NULL, false }
};
@@ -1528,6 +1530,61 @@ mips_merge_decl_attributes (tree olddecl, tree newdecl)
DECL_ATTRIBUTES (newdecl));
}
+static bool
+mips_cfun_has_msa_p (void)
+{
+ /* For now, for testing, assume all functions use MSA
+ (and thus need alignment). */
+#if 0
+ if (!cfun || !TARGET_MSA)
+ return FALSE;
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (MSA_SUPPORTED_MODE_P (GET_MODE (insn)))
+ return TRUE;
+ }
+
+ return FALSE;
+#else
+ return TRUE;
+#endif
+}
+
+bool
+mips_align_stack_p (void)
+{
+ bool want_alignment = TARGET_ALIGN_STACK && mips_cfun_has_msa_p ();
+
+ if (current_function_decl)
+ {
+ tree attr = DECL_ATTRIBUTES (current_function_decl);
+ if (lookup_attribute ("no_align_stack", attr))
+ want_alignment = FALSE;
+ if (lookup_attribute ("align_stack", attr))
+ want_alignment = TRUE;
+
+ if (want_alignment
+ && mips_get_compress_mode (current_function_decl) & MASK_MIPS16)
+ /* Should have warnings here ??? */
+ want_alignment = FALSE;
+ }
+
+ if (want_alignment && cfun
+ && (cfun->calls_setjmp || cfun->has_nonlocal_label))
+ /* Should have warnings here ??? */
+ want_alignment = FALSE;
+
+#if 1
+ if (want_alignment && (flag_exceptions || flag_unwind_tables))
+ want_alignment = FALSE;
+#endif
+
+ return want_alignment;
+}
+
+
+
/* Implement TARGET_CAN_INLINE_P. */
static bool
@@ -10455,6 +10512,14 @@ mips_frame_pointer_required (void)
if (cfun->calls_alloca)
return true;
+ /* If we are going to dynamically relalign the stack, we need to use
+ a frame pointer because we cannot save/restore the stack pointer
+ with a simple addiu $sp,$sp,-N at the start and then +N at the end
+ of a function. We need to save and restore it using the frame
+ pointer. */
+ if (mips_align_stack_p ())
+ return true;
+
/* In MIPS16 mode, we need a frame pointer for a large frame; otherwise,
reload may be unable to compute the address of a local variable,
since there is no way to add a large constant to the stack pointer
@@ -10535,6 +10600,8 @@ mips_extra_live_on_entry (bitmap regs)
/* See the comment above load_call<mode> for details. */
bitmap_set_bit (regs, GOT_VERSION_REGNUM);
}
+ if (mips_align_stack_p ())
+ bitmap_set_bit (regs, MIPS_PROLOGUE_TEMP2_REGNUM);
}
/* Implement RETURN_ADDR_RTX. We do not support moving back to a
@@ -11382,6 +11449,48 @@ mips_expand_prologue (void)
mips_emit_probe_stack_range (STACK_CHECK_PROTECT, size);
}
+ /* Copy the stack pointer to drap register here, before we increment it.
+ We need drap_reg to be the 'unincremented' stack due to
+ instantiate_new_reg (in function.c) where the offset for accessing
+ arguments is reset when using a drap reg vs the normal argument pointer
+ (stack or frame).
+
+ Align the stack pointer before copying it to the frame pointer so that
+ $fp is also aligned. local variables and spill slots are accessed
+ using $fp, not $sp when using dynamic stack alignment.
+
+ Do the alignment before saving registers so that expand_epilogue can
+ use the aligned $fp/$sp to restore registers. */
+
+
+ if (mips_align_stack_p ())
+ {
+ rtx cfi_note;
+ rtx_insn *insn;
+ rtx (*and_insn) (rtx, rtx, rtx);
+ rtx drap_reg_rtx = MIPS_PROLOGUE_TEMP2 (Pmode);
+ insn = mips_emit_move (drap_reg_rtx, stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+
+ mips_frame_barrier ();
+
+ gcc_assert (frame_pointer_needed);
+ gcc_assert (frame->hard_frame_pointer_offset == 0);
+ /* Cannot do 'and' instruction with a constant because the constant
+ would be too big. Need to put it in a register. */
+ and_insn = (Pmode == SImode) ? gen_andsi3 : gen_anddi3;
+ mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (-16));
+ insn = emit_insn (and_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ MIPS_PROLOGUE_TEMP (Pmode)));
+#if 1
+ cfi_note = alloc_reg_note (REG_CFA_DEF_CFA, stack_pointer_rtx, NULL_RTX);
+ REG_NOTES (insn) = cfi_note;
+#endif
+ RTX_FRAME_RELATED_P (insn) = 1;
+ mips_frame_barrier ();
+ }
+
/* Save the registers. Allocate up to MIPS_MAX_FIRST_STACK_STEP
bytes beforehand; this is enough to cover the register save area
without going out of range. */
@@ -11877,6 +11986,11 @@ mips_expand_epilogue (bool sibcall_p)
emit_insn (gen_cop0_move (gen_rtx_REG (SImode, COP0_STATUS_REG_NUM),
gen_rtx_REG (SImode, K0_REG_NUM)));
}
+ else if (mips_align_stack_p ())
+ {
+ emit_move_insn (stack_pointer_rtx, crtl->drap_reg);
+ mips_epilogue_set_cfa (stack_pointer_rtx, 0);
+ }
else if (TARGET_MICROMIPS
&& !crtl->calls_eh_return
&& !sibcall_p
@@ -17728,6 +17842,10 @@ mips_option_override (void)
if (!TARGET_USE_GOT || !TARGET_EXPLICIT_RELOCS)
target_flags &= ~MASK_RELAX_PIC_CALLS;
+ /* Turn off flag_optimize_sibling_calls if aligning stack. */
+ if (mips_align_stack_p ())
+ flag_optimize_sibling_calls = 0;
+
/* Save base state of options. */
mips_base_target_flags = target_flags;
mips_base_schedule_insns = flag_schedule_insns;
@@ -17937,6 +18055,9 @@ mips_epilogue_uses (unsigned int regno)
&& mips_interrupt_extra_call_saved_reg_p (regno))
return true;
+ if (mips_align_stack_p () && (regno == REGNO (crtl->drap_reg)))
+ return true;
+
return false;
}
@@ -19384,6 +19505,53 @@ mips_ira_change_pseudo_allocno_class (int regno, reg_class_t allocno_class)
return GR_REGS;
return allocno_class;
}
+
+/* Implement TARGET_GET_DRAP_RTX. */
+static rtx
+mips_get_drap_rtx (void)
+{
+ rtx_insn *seq, *insn;
+
+ if (mips_align_stack_p ())
+ {
+ if (crtl->vdrap_reg == NULL_RTX)
+ {
+ rtx_insn *seq, *insn;
+
+ crtl->need_drap = true;
+ crtl->stack_realign_needed = true;
+ crtl->drap_reg = MIPS_PROLOGUE_TEMP2 (Pmode);
+ crtl->vdrap_reg = gen_reg_rtx (Pmode);
+ }
+ return crtl->vdrap_reg;
+ }
+ else
+ return NULL_RTX;
+}
+
+/* Add code to copy vdrap to drap at end of a function. */
+static void
+mips_add_exit_insns (void)
+{
+ /* Call mips_get_drap_rtx to make sure drap and vdrap regs are set. */
+ mips_get_drap_rtx ();
+ if (crtl->drap_reg != NULL_RTX)
+ emit_move_insn (crtl->drap_reg, crtl->vdrap_reg);
+}
+
+/* Add code to copy vdrap to drap at end of a function. */
+/* Update preferred stack boundary. */
+
+static void
+mips_update_stack_boundary (void)
+{
+ if (mips_align_stack_p ())
+ {
+ crtl->need_drap = true;
+ crtl->preferred_stack_boundary = 128;
+ crtl->stack_realign_needed = true;
+ }
+}
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
@@ -19643,6 +19811,13 @@ mips_ira_change_pseudo_allocno_class (int regno, reg_class_t allocno_class)
#undef TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS
#define TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS mips_ira_change_pseudo_allocno_class
+#undef TARGET_GET_DRAP_RTX
+#define TARGET_GET_DRAP_RTX mips_get_drap_rtx
+#undef TARGET_ADD_EXIT_INSNS
+#define TARGET_ADD_EXIT_INSNS mips_add_exit_insns
+#undef TARGET_UPDATE_STACK_BOUNDARY
+#define TARGET_UPDATE_STACK_BOUNDARY mips_update_stack_boundary
+
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-mips.h"
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 7a6f917..3ba5cfb 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -2321,6 +2321,10 @@ enum reg_class
#define OUTGOING_REG_PARM_STACK_SPACE(FNTYPE) 1
#define STACK_BOUNDARY (TARGET_NEWABI ? 128 : 64)
+
+#define MAX_STACK_ALIGNMENT 128
+#define PREFERRED_STACK_BOUNDARY (mips_align_stack_p () ? 128 : STACK_BOUNDARY)
+#define INCOMING_STACK_BOUNDARY STACK_BOUNDARY
/* Symbolic macros for the registers used to return integer and floating
point values. */
@@ -3109,6 +3113,7 @@ extern const struct mips_cpu_info *mips_tune_info;
extern unsigned int mips_base_compression_flags;
extern GTY(()) struct target_globals *mips16_globals;
extern GTY(()) struct target_globals *micromips_globals;
+extern bool mips_align_stack_p (void);
#endif
/* Enable querying of DFA units. */
diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt
index 348c6e0..8f15e2b 100644
--- a/gcc/config/mips/mips.opt
+++ b/gcc/config/mips/mips.opt
@@ -372,6 +372,10 @@ msplit-addresses
Target Report Mask(SPLIT_ADDRESSES)
Optimize lui/addiu address loads
+malign-stack
+Target Report Var(TARGET_ALIGN_STACK)
+Realign stack in prologue
+
msym32
Target Report Var(TARGET_SYM32)
Assume all symbols have 32-bit values
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index a16cd92..55cbbc6 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11483,6 +11483,10 @@ argument list due to stack realignment. Return @code{NULL} if no DRAP
is needed.
@end deftypefn
+@deftypefn {Target Hook} void TARGET_ADD_EXIT_INSNS (void)
+This hook will insert instructions at the exit point of a function.
+@end deftypefn
+
@deftypefn {Target Hook} bool TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS (void)
When optimization is disabled, this hook indicates whether or not
arguments should be allocated to stack slots. Normally, GCC allocates
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 93fb41c..f2e0576 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8181,6 +8181,8 @@ and the associated definitions of those functions.
@hook TARGET_GET_DRAP_RTX
+@hook TARGET_ADD_EXIT_INSNS
+
@hook TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
@hook TARGET_CONST_ANCHOR
diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
index b567b23..51f0818 100644
--- a/gcc/dwarf2cfi.c
+++ b/gcc/dwarf2cfi.c
@@ -1772,6 +1772,7 @@ dwarf2out_frame_debug_expr (rtx expr)
== dwf_regno (XEXP (src, 0)));
fde->stack_realign = 1;
fde->stack_realignment = INTVAL (XEXP (src, 1));
+ fde->stack_realignment = -16;
cur_trace->cfa_store.offset = 0;
if (cur_cfa->reg != dw_stack_pointer_regnum
diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h
index f52c335..0d31393 100644
--- a/gcc/emit-rtl.h
+++ b/gcc/emit-rtl.h
@@ -120,6 +120,7 @@ struct GTY(()) rtl_data {
/* Dynamic Realign Argument Pointer used for realigning stack. */
rtx drap_reg;
+ rtx vdrap_reg;
/* Offset to end of allocated area of stack frame.
If stack grows down, this is the address of the last stack slot allocated.
diff --git a/gcc/function.c b/gcc/function.c
index 8ee79d3..fed743d 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -5460,6 +5460,10 @@ expand_function_end (void)
emit_stack_restore (SAVE_FUNCTION, tem);
}
+
+ if (targetm.calls.add_exit_insns)
+ targetm.calls.add_exit_insns ();
+
/* ??? This should no longer be necessary since stupid is no longer with
us, but there are some parts of the compiler (eg reload_combine, and
sh mach_dep_reorg) that still try and compute their own lifetime info
diff --git a/gcc/target.def b/gcc/target.def
index a2f3554..ec54921 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4525,6 +4525,11 @@ argument list due to stack realignment. Return @code{NULL} if no DRAP\n\
is needed.",
rtx, (void), NULL)
+DEFHOOK
+(add_exit_insns,
+ "This hook will insert instructions at the exit point of a function.",
+ void, (void), NULL)
+
/* Return true if all function parameters should be spilled to the
stack. */
DEFHOOK
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index ebd0cfa..d11981a 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -1100,7 +1100,9 @@ adjust_mems (rtx loc, const_rtx old_rtx, void *data)
&& hard_frame_pointer_adjustment != -1
&& cfa_base_rtx)
return compute_cfa_pointer (hard_frame_pointer_adjustment);
- gcc_checking_assert (loc != virtual_incoming_args_rtx);
+ /* On MIPS we don't need to adjust the incoming args reg. */
+ /* gcc_checking_assert (loc != virtual_incoming_args_rtx); */
+ /* Gives abort on gcc.target/mips/fpr-moves-7.c in 64 bit mode. */
return loc;
case MEM:
mem = loc;