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 sell...@imgtec.com 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;