[NDS32] Support dwarf exception handling.
authorChung-Ju Wu <jasonwucj@gmail.com>
Sat, 7 Apr 2018 10:52:19 +0000 (10:52 +0000)
committerChung-Ju Wu <jasonwucj@gcc.gnu.org>
Sat, 7 Apr 2018 10:52:19 +0000 (10:52 +0000)
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
gcc/config/nds32/constants.md
gcc/config/nds32/nds32-md-auxiliary.c
gcc/config/nds32/nds32-protos.h
gcc/config/nds32/nds32.c
gcc/config/nds32/nds32.h
gcc/config/nds32/nds32.md

index f758d3712b11ef5a4d6eb526383234bb8681e7f5..2c44527ad1d35a58f41166dadcde53642941fcc6 100644 (file)
@@ -1,3 +1,26 @@
+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.
index 3facf5ba898814dadc99113aefd61839202ed76a..a9593f7894c7e49fd614c2e66a78be1756eab79b 100644 (file)
@@ -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
index 01ce38b5980a1ae89d48f7be9b01f6c8ed38a64e..95bd82969abb860fe7b19aa3dad4fa37fe6929ed 100644 (file)
@@ -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
index 0b460dadec9f38a0d9601fe72e3b2b0bb23f95f1..2f077005b067827edb9c673e03cbbe542eea50cc 100644 (file)
@@ -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.  */
index 98835096afea599a8c00b904afa6dbf4d9d8d5ee..0ad0e85887697b12d395949d4b1c48482a273ce8 100644 (file)
@@ -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;
@@ -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 ());
index 330252fb57ae4263f24cd2d50eb2ae87b84f1ab1..3bb5a72089601d13d9c2fcdfc20b68b8d54760e7 100644 (file)
@@ -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
index 99731fecb7abc2737d606c45bf95e394df199092..9e3e20a52fa1b0aa0030efe00278d921994a14ee 100644 (file)
 )
 
 ;; ----------------------------------------------------------------------------
+
+;; 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;
+})
+
+;; ----------------------------------------------------------------------------