Commit ca3a4a55 by Chung-Ju Wu Committed by Chung-Ju Wu

[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
parent 30044989
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.
2018-04-07 Monk Chiang <sh.chiang04@gmail.com>
......
......@@ -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
......
......@@ -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
......
......@@ -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. */
......
......@@ -323,6 +323,9 @@ nds32_init_machine_status (void)
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;
......@@ -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;
......@@ -3817,14 +3850,39 @@ 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 ());
......
......@@ -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
......
......@@ -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;
})
;; ----------------------------------------------------------------------------
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment