From ca3a4a555d8e973aa7b8d851038c18efec4c2dcd Mon Sep 17 00:00:00 2001 From: Chung-Ju Wu Date: Sat, 7 Apr 2018 10:52:19 +0000 Subject: [PATCH] [NDS32] Support dwarf exception handling. gcc/ * config/nds32/constants.md (unspec_volatile_element): Add UNSPEC_VOLATILE_EH_RETURN. * config/nds32/nds32-md-auxiliary.c (nds32_output_stack_push, nds32_output_stack_pop): Support dwarf exception handling process. * config/nds32/nds32-protos.h (nds32_dynamic_chain_address): Declare. * config/nds32/nds32.c (nds32_init_machine_status): Support dwarf exception handling process. (nds32_compute_stack_frame): Likewise. (nds32_return_addr_rtx): Likewise. (nds32_initial_elimination_offset): Likewise. (nds32_expand_prologue): Likewise. (nds32_expand_epilogue): Likewise. (nds32_dynamic_chain_address): New function. * config/nds32/nds32.h (machine_function): Add fields for dwarf exception handling. (DYNAMIC_CHAIN_ADDRESS): Define. (EH_RETURN_DATA_REGNO): Define. (EH_RETURN_STACKADJ_RTX): Define. * config/nds32/nds32.md (eh_return, nds32_eh_return): Implement patterns for dwarf exception handling. From-SVN: r259210 --- gcc/ChangeLog | 23 +++++ gcc/config/nds32/constants.md | 1 + gcc/config/nds32/nds32-md-auxiliary.c | 38 +++++++ gcc/config/nds32/nds32-protos.h | 1 + gcc/config/nds32/nds32.c | 140 ++++++++++++++++++++++++-- gcc/config/nds32/nds32.h | 23 +++++ gcc/config/nds32/nds32.md | 63 ++++++++++++ 7 files changed, 280 insertions(+), 9 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f758d3712b1..2c44527ad1d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,26 @@ +2018-04-07 Chung-Ju Wu + + * config/nds32/constants.md (unspec_volatile_element): Add + UNSPEC_VOLATILE_EH_RETURN. + * config/nds32/nds32-md-auxiliary.c (nds32_output_stack_push, + nds32_output_stack_pop): Support dwarf exception handling process. + * config/nds32/nds32-protos.h (nds32_dynamic_chain_address): Declare. + * config/nds32/nds32.c (nds32_init_machine_status): Support dwarf + exception handling process. + (nds32_compute_stack_frame): Likewise. + (nds32_return_addr_rtx): Likewise. + (nds32_initial_elimination_offset): Likewise. + (nds32_expand_prologue): Likewise. + (nds32_expand_epilogue): Likewise. + (nds32_dynamic_chain_address): New function. + * config/nds32/nds32.h (machine_function): Add fields for dwarf + exception handling. + (DYNAMIC_CHAIN_ADDRESS): Define. + (EH_RETURN_DATA_REGNO): Define. + (EH_RETURN_STACKADJ_RTX): Define. + * config/nds32/nds32.md (eh_return, nds32_eh_return): Implement + patterns for dwarf exception handling. + 2018-04-07 Chung-Ju Wu * config/nds32/nds32.h: Clean up obsolete macros. diff --git a/gcc/config/nds32/constants.md b/gcc/config/nds32/constants.md index 3facf5ba898..a9593f7894c 100644 --- a/gcc/config/nds32/constants.md +++ b/gcc/config/nds32/constants.md @@ -66,6 +66,7 @@ ;; The unspec_volatile operation index. (define_c_enum "unspec_volatile_element" [ + UNSPEC_VOLATILE_EH_RETURN UNSPEC_VOLATILE_ISYNC UNSPEC_VOLATILE_ISB UNSPEC_VOLATILE_DSB diff --git a/gcc/config/nds32/nds32-md-auxiliary.c b/gcc/config/nds32/nds32-md-auxiliary.c index 01ce38b5980..95bd82969ab 100644 --- a/gcc/config/nds32/nds32-md-auxiliary.c +++ b/gcc/config/nds32/nds32-md-auxiliary.c @@ -1712,6 +1712,10 @@ nds32_output_stack_push (rtx par_rtx) int last_argument_regno = NDS32_FIRST_GPR_REGNUM + NDS32_MAX_GPR_REGS_FOR_ARGS - 1; + /* Pick up first and last eh data regno for further use. */ + int rb_eh_data = cfun->machine->eh_return_data_first_regno; + int re_eh_data = cfun->machine->eh_return_data_last_regno; + int first_eh_data_regno = EH_RETURN_DATA_REGNO (0); /* Pick up callee-saved first regno and last regno for further use. */ int rb_callee_saved = cfun->machine->callee_saved_first_gpr_regno; int re_callee_saved = cfun->machine->callee_saved_last_gpr_regno; @@ -1731,6 +1735,22 @@ nds32_output_stack_push (rtx par_rtx) return ""; } + /* If last_argument_regno is not mentioned in par_rtx, we can confirm that + we do not need to push argument registers for variadic function. + But we still need to check if we need to push exception handling + data registers. */ + if (reg_mentioned_p (gen_rtx_REG (SImode, first_eh_data_regno), par_rtx)) + { + /* Set operands[0] and operands[1]. */ + operands[0] = gen_rtx_REG (SImode, rb_eh_data); + operands[1] = gen_rtx_REG (SImode, re_eh_data); + /* Create assembly code pattern: "Rb, Re, { }". */ + snprintf (pattern, sizeof (pattern), "push.s\t%s", "%0, %1, { }"); + /* We use output_asm_insn() to output assembly code by ourself. */ + output_asm_insn (pattern, operands); + return ""; + } + /* If we step here, we are going to do v3push or multiple push operation. */ /* The v3push/v3pop instruction should only be applied on @@ -1833,10 +1853,28 @@ nds32_output_stack_pop (rtx par_rtx ATTRIBUTE_UNUSED) char pattern[100]; /* The operands array which will be used in output_asm_insn(). */ rtx operands[3]; + /* Pick up first and last eh data regno for further use. */ + int rb_eh_data = cfun->machine->eh_return_data_first_regno; + int re_eh_data = cfun->machine->eh_return_data_last_regno; + int first_eh_data_regno = EH_RETURN_DATA_REGNO (0); /* Pick up callee-saved first regno and last regno for further use. */ int rb_callee_saved = cfun->machine->callee_saved_first_gpr_regno; int re_callee_saved = cfun->machine->callee_saved_last_gpr_regno; + /* We need to check if we need to push exception handling + data registers. */ + if (reg_mentioned_p (gen_rtx_REG (SImode, first_eh_data_regno), par_rtx)) + { + /* Set operands[0] and operands[1]. */ + operands[0] = gen_rtx_REG (SImode, rb_eh_data); + operands[1] = gen_rtx_REG (SImode, re_eh_data); + /* Create assembly code pattern: "Rb, Re, { }". */ + snprintf (pattern, sizeof (pattern), "pop.s\t%s", "%0, %1, { }"); + /* We use output_asm_insn() to output assembly code by ourself. */ + output_asm_insn (pattern, operands); + return ""; + } + /* If we step here, we are going to do v3pop or multiple pop operation. */ /* The v3push/v3pop instruction should only be applied on diff --git a/gcc/config/nds32/nds32-protos.h b/gcc/config/nds32/nds32-protos.h index 0b460dadec9..2f077005b06 100644 --- a/gcc/config/nds32/nds32-protos.h +++ b/gcc/config/nds32/nds32-protos.h @@ -40,6 +40,7 @@ extern enum reg_class nds32_regno_reg_class (int); /* -- Basic Stack Layout. */ +extern rtx nds32_dynamic_chain_address (rtx); extern rtx nds32_return_addr_rtx (int, rtx); /* -- Eliminating Frame Pointer and Arg Pointer. */ diff --git a/gcc/config/nds32/nds32.c b/gcc/config/nds32/nds32.c index 98835096afe..0ad0e858876 100644 --- a/gcc/config/nds32/nds32.c +++ b/gcc/config/nds32/nds32.c @@ -323,6 +323,9 @@ nds32_init_machine_status (void) struct machine_function *machine; machine = ggc_cleared_alloc (); + /* Initially assume this function does not use __builtin_eh_return. */ + machine->use_eh_return_p = 0; + /* Initially assume this function needs prologue/epilogue. */ machine->naked_p = 0; @@ -346,6 +349,36 @@ nds32_compute_stack_frame (void) needs prologue/epilogue. */ cfun->machine->naked_p = 0; + + /* If __builtin_eh_return is used, we better have frame pointer needed + so that we can easily locate the stack slot of return address. */ + if (crtl->calls_eh_return) + { + frame_pointer_needed = 1; + + /* We need to mark eh data registers that need to be saved + in the stack. */ + cfun->machine->eh_return_data_first_regno = EH_RETURN_DATA_REGNO (0); + for (r = 0; EH_RETURN_DATA_REGNO (r) != INVALID_REGNUM; r++) + cfun->machine->eh_return_data_last_regno = r; + + cfun->machine->eh_return_data_regs_size + = 4 * (cfun->machine->eh_return_data_last_regno + - cfun->machine->eh_return_data_first_regno + + 1); + cfun->machine->use_eh_return_p = 1; + } + else + { + /* Assigning SP_REGNUM to eh_first_regno and eh_last_regno means we + do not need to handle __builtin_eh_return case in this function. */ + cfun->machine->eh_return_data_first_regno = SP_REGNUM; + cfun->machine->eh_return_data_last_regno = SP_REGNUM; + + cfun->machine->eh_return_data_regs_size = 0; + cfun->machine->use_eh_return_p = 0; + } + /* Get variadic arguments size to prepare pretend arguments and we will push them into stack at prologue by ourself. */ cfun->machine->va_args_size = crtl->args.pretend_args_size; @@ -3816,15 +3849,40 @@ nds32_regno_reg_class (int regno) /* -- Basic Stack Layout. */ +rtx +nds32_dynamic_chain_address (rtx frameaddr) +{ + if (TARGET_V3PUSH) + { + /* If -mv3push is specified, we push $fp, $gp, and $lp into stack. + We can access dynamic chain address from stack by [$fp - 12]. */ + return plus_constant (Pmode, frameaddr, -12); + } + else + { + /* For general case we push $fp and $lp into stack at prologue. + We can access dynamic chain address from stack by [$fp - 8]. */ + return plus_constant (Pmode, frameaddr, -8); + } +} + rtx nds32_return_addr_rtx (int count, - rtx frameaddr ATTRIBUTE_UNUSED) + rtx frameaddr) { - /* There is no way to determine the return address - if frameaddr is the frame that has 'count' steps - up from current frame. */ + int offset; + rtx addr; + if (count != 0) - return NULL_RTX; + { + /* In nds32 ABI design, we can expect that $lp is always available + from stack by [$fp - 4] location. */ + offset = -4; + addr = plus_constant (Pmode, frameaddr, offset); + addr = memory_address (Pmode, addr); + + return gen_rtx_MEM (Pmode, addr); + } /* If count == 0, it means we are at current frame, the return address is $r30 ($lp). */ @@ -3843,7 +3901,8 @@ nds32_initial_elimination_offset (unsigned int from_reg, unsigned int to_reg) nds32_compute_stack_frame (); /* Remember to consider - cfun->machine->callee_saved_area_gpr_padding_bytes + cfun->machine->callee_saved_area_gpr_padding_bytes and + cfun->machine->eh_return_data_regs_size when calculating offset. */ if (from_reg == ARG_POINTER_REGNUM && to_reg == STACK_POINTER_REGNUM) { @@ -3853,6 +3912,7 @@ nds32_initial_elimination_offset (unsigned int from_reg, unsigned int to_reg) + cfun->machine->callee_saved_gpr_regs_size + cfun->machine->callee_saved_area_gpr_padding_bytes + cfun->machine->callee_saved_fpr_regs_size + + cfun->machine->eh_return_data_regs_size + cfun->machine->local_size + cfun->machine->out_args_size); } @@ -3874,7 +3934,8 @@ nds32_initial_elimination_offset (unsigned int from_reg, unsigned int to_reg) + cfun->machine->lp_size + cfun->machine->callee_saved_gpr_regs_size + cfun->machine->callee_saved_area_gpr_padding_bytes - + cfun->machine->callee_saved_fpr_regs_size); + + cfun->machine->callee_saved_fpr_regs_size + + cfun->machine->eh_return_data_regs_size); } else { @@ -3960,12 +4021,24 @@ nds32_expand_prologue (void) false); } + /* Save eh data registers. */ + if (cfun->machine->use_eh_return_p) + { + Rb = cfun->machine->eh_return_data_first_regno; + Re = cfun->machine->eh_return_data_last_regno; + + /* No need to push $fp, $gp, or $lp. + Also, this is not variadic arguments push. */ + nds32_emit_stack_push_multiple (Rb, Re, false, false, false, false); + } + /* Check frame_pointer_needed to see if we shall emit fp adjustment instruction. */ if (frame_pointer_needed) { /* adjust $fp = $sp + ($fp size) + ($gp size) + ($lp size) + (4 * callee-saved-registers) + + (4 * exception-handling-data-registers) Note: No need to adjust cfun->machine->callee_saved_area_gpr_padding_bytes, because, at this point, stack pointer is just @@ -3973,7 +4046,8 @@ nds32_expand_prologue (void) fp_adjust = cfun->machine->fp_size + cfun->machine->gp_size + cfun->machine->lp_size - + cfun->machine->callee_saved_gpr_regs_size; + + cfun->machine->callee_saved_gpr_regs_size + + cfun->machine->eh_return_data_regs_size; nds32_emit_adjust_frame (hard_frame_pointer_rtx, stack_pointer_rtx, @@ -4122,6 +4196,7 @@ nds32_expand_epilogue (bool sibcall_p) + cfun->machine->gp_size + cfun->machine->lp_size + cfun->machine->callee_saved_gpr_regs_size + + cfun->machine->eh_return_data_regs_size + cfun->machine->callee_saved_area_gpr_padding_bytes + cfun->machine->callee_saved_fpr_regs_size; @@ -4145,7 +4220,8 @@ nds32_expand_epilogue (bool sibcall_p) sp_adjust = cfun->machine->fp_size + cfun->machine->gp_size + cfun->machine->lp_size - + cfun->machine->callee_saved_gpr_regs_size; + + cfun->machine->callee_saved_gpr_regs_size + + cfun->machine->eh_return_data_regs_size; nds32_emit_adjust_frame (stack_pointer_rtx, hard_frame_pointer_rtx, @@ -4193,6 +4269,16 @@ nds32_expand_epilogue (bool sibcall_p) } } + /* Restore eh data registers. */ + if (cfun->machine->use_eh_return_p) + { + Rb = cfun->machine->eh_return_data_first_regno; + Re = cfun->machine->eh_return_data_last_regno; + + /* No need to pop $fp, $gp, or $lp. */ + nds32_emit_stack_pop_multiple (Rb, Re, false, false, false); + } + /* Get callee_first_regno and callee_last_regno. */ Rb = cfun->machine->callee_saved_first_gpr_regno; Re = cfun->machine->callee_saved_last_gpr_regno; @@ -4226,6 +4312,42 @@ nds32_expand_epilogue (bool sibcall_p) sp_adjust); } + /* If this function uses __builtin_eh_return, make stack adjustment + for exception handler. */ + if (cfun->machine->use_eh_return_p) + { + /* We need to unwind the stack by the offset computed by + EH_RETURN_STACKADJ_RTX. However, at this point the CFA is + based on SP. Ideally we would update the SP and define the + CFA along the lines of: + + SP = SP + EH_RETURN_STACKADJ_RTX + (regnote CFA = SP - EH_RETURN_STACKADJ_RTX) + + However the dwarf emitter only understands a constant + register offset. + + The solution chosen here is to use the otherwise $ta ($r15) + as a temporary register to hold the current SP value. The + CFA is described using $ta then SP is modified. */ + + rtx ta_reg; + rtx insn; + + ta_reg = gen_rtx_REG (SImode, TA_REGNUM); + + insn = emit_move_insn (ta_reg, stack_pointer_rtx); + add_reg_note (insn, REG_CFA_DEF_CFA, ta_reg); + RTX_FRAME_RELATED_P (insn) = 1; + + emit_insn (gen_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + EH_RETURN_STACKADJ_RTX)); + + /* Ensure the assignment to $ta does not get optimized away. */ + emit_use (ta_reg); + } + /* Generate return instruction. */ if (!sibcall_p) emit_jump_insn (gen_return_internal ()); diff --git a/gcc/config/nds32/nds32.h b/gcc/config/nds32/nds32.h index 330252fb57a..3bb5a720896 100644 --- a/gcc/config/nds32/nds32.h +++ b/gcc/config/nds32/nds32.h @@ -274,6 +274,17 @@ struct GTY(()) machine_function /* The last required register that should be saved on stack for va_args. */ int va_args_last_regno; + /* Number of bytes on the stack for saving exception handling registers. */ + int eh_return_data_regs_size; + /* The first register of passing exception handling information. */ + int eh_return_data_first_regno; + /* The last register of passing exception handling information. */ + int eh_return_data_last_regno; + + /* Indicate that whether this function + calls __builtin_eh_return. */ + int use_eh_return_p; + /* Indicate that whether this function needs prologue/epilogue code generation. */ int naked_p; @@ -888,6 +899,11 @@ enum reg_class #define FIRST_PARM_OFFSET(fundecl) \ (NDS32_DOUBLE_WORD_ALIGN_P (crtl->args.pretend_args_size) ? 0 : 4) +/* A C expression whose value is RTL representing the address in a stack frame + where the pointer to the caller's frame is stored. */ +#define DYNAMIC_CHAIN_ADDRESS(frameaddr) \ + nds32_dynamic_chain_address (frameaddr) + #define RETURN_ADDR_RTX(count, frameaddr) \ nds32_return_addr_rtx (count, frameaddr) @@ -899,6 +915,13 @@ enum reg_class #define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, LP_REGNUM) #define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (LP_REGNUM) +/* Use $r0 $r1 to pass exception handling information. */ +#define EH_RETURN_DATA_REGNO(N) (((N) < 2) ? (N) : INVALID_REGNUM) +/* The register $r2 that represents a location in which to store a stack + adjustment to be applied before function return. + This is used to unwind the stack to an exception handler's call frame. */ +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, 2) + #define DBX_REGISTER_NUMBER(REGNO) nds32_dbx_register_number (REGNO) #define STACK_POINTER_REGNUM SP_REGNUM diff --git a/gcc/config/nds32/nds32.md b/gcc/config/nds32/nds32.md index 99731fecb7a..9e3e20a52fa 100644 --- a/gcc/config/nds32/nds32.md +++ b/gcc/config/nds32/nds32.md @@ -1967,3 +1967,66 @@ ) ;; ---------------------------------------------------------------------------- + +;; Patterns for exception handling + +(define_expand "eh_return" + [(use (match_operand 0 "general_operand"))] + "" +{ + emit_insn (gen_nds32_eh_return (operands[0])); + DONE; +}) + +(define_insn_and_split "nds32_eh_return" + [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")] UNSPEC_VOLATILE_EH_RETURN)] + "" + "#" + "reload_completed" + [(const_int 0)] +{ + rtx place; + rtx addr; + + /* The operands[0] is the handler address. We need to assign it + to return address rtx so that we can jump to exception handler + when returning from current function. */ + + if (cfun->machine->lp_size == 0) + { + /* If $lp is not saved in the stack frame, we can take $lp directly. */ + place = gen_rtx_REG (SImode, LP_REGNUM); + } + else + { + /* Otherwise, we need to locate the stack slot of return address. + The return address is generally saved in [$fp-4] location. + However, DSE (dead store elimination) does not detect an alias + between [$fp-x] and [$sp+y]. This can result in a store to save + $lp introduced by builtin_eh_return() being incorrectly deleted + if it is based on $fp. The solution we take here is to compute + the offset relative to stack pointer and then use $sp to access + location so that the alias can be detected. + FIXME: What if the immediate value "offset" is too large to be + fit in a single addi instruction? */ + HOST_WIDE_INT offset; + + offset = (cfun->machine->fp_size + + cfun->machine->gp_size + + cfun->machine->lp_size + + cfun->machine->callee_saved_gpr_regs_size + + cfun->machine->callee_saved_area_gpr_padding_bytes + + cfun->machine->callee_saved_fpr_regs_size + + cfun->machine->eh_return_data_regs_size + + cfun->machine->local_size + + cfun->machine->out_args_size); + + addr = plus_constant (Pmode, stack_pointer_rtx, offset - 4); + place = gen_frame_mem (SImode, addr); + } + + emit_move_insn (place, operands[0]); + DONE; +}) + +;; ---------------------------------------------------------------------------- -- 2.30.2