+2018-04-07 Chung-Ju Wu <jasonwucj@gmail.com>
+
+ * 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 <jasonwucj@gmail.com>
* config/nds32/nds32.h: Clean up obsolete macros.
;; The unspec_volatile operation index.
(define_c_enum "unspec_volatile_element" [
+ UNSPEC_VOLATILE_EH_RETURN
UNSPEC_VOLATILE_ISYNC
UNSPEC_VOLATILE_ISB
UNSPEC_VOLATILE_DSB
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;
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
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
/* -- Basic Stack Layout. */
+extern rtx nds32_dynamic_chain_address (rtx);
extern rtx nds32_return_addr_rtx (int, rtx);
/* -- Eliminating Frame Pointer and Arg Pointer. */
struct machine_function *machine;
machine = ggc_cleared_alloc<machine_function> ();
+ /* 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;
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;
/* -- 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). */
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)
{
+ 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);
}
+ 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
{
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
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,
+ 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;
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,
}
}
+ /* 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;
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 ());
/* 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;
#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)
#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
)
;; ----------------------------------------------------------------------------
+
+;; 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;
+})
+
+;; ----------------------------------------------------------------------------