OSDN Git Service

2005-06-28 Paul Brook <paul@codesourcery.com>
[pf3gnuchains/gcc-fork.git] / gcc / config / arm / arm.c
index 5348c79..4e17d3a 100644 (file)
@@ -171,6 +171,10 @@ static bool arm_default_short_enums (void);
 static bool arm_align_anon_bitfield (void);
 static bool arm_return_in_msb (tree);
 static bool arm_must_pass_in_stack (enum machine_mode, tree);
+#ifdef TARGET_UNWIND_INFO
+static void arm_unwind_emit (FILE *, rtx);
+static bool arm_output_ttype (rtx);
+#endif
 
 static tree arm_cxx_guard_type (void);
 static bool arm_cxx_guard_mask_bit (void);
@@ -337,6 +341,18 @@ static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode);
 #undef TARGET_MUST_PASS_IN_STACK
 #define TARGET_MUST_PASS_IN_STACK arm_must_pass_in_stack
 
+#ifdef TARGET_UNWIND_INFO
+#undef TARGET_UNWIND_EMIT
+#define TARGET_UNWIND_EMIT arm_unwind_emit
+
+/* EABI unwinding tables use a different format for the typeinfo tables.  */
+#undef TARGET_ASM_TTYPE
+#define TARGET_ASM_TTYPE arm_output_ttype
+
+#undef TARGET_ARM_EABI_UNWINDER
+#define TARGET_ARM_EABI_UNWINDER true
+#endif /* TARGET_UNWIND_INFO */
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Obstack for minipool constant handling.  */
@@ -12417,6 +12433,21 @@ thumb_pushpop (FILE *f, unsigned long mask, int push, int *cfa_offset,
       return;
     }
 
+  if (ARM_EABI_UNWIND_TABLES && push)
+    {
+      fprintf (f, "\t.save\t{");
+      for (regno = 0; regno < 15; regno++)
+       {
+         if (real_regs & (1 << regno))
+           {
+             if (real_regs & ((1 << regno) -1))
+               fprintf (f, ", ");
+             asm_fprintf (f, "%r", regno);
+           }
+       }
+      fprintf (f, "}\n");
+    }
+
   fprintf (f, "\t%s\t{", push ? "push" : "pop");
 
   /* Look at the low registers first.  */
@@ -13370,6 +13401,11 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 
   if (current_function_pretend_args_size)
     {
+      /* Output unwind directive for the stack adjustment.  */
+      if (ARM_EABI_UNWIND_TABLES)
+       fprintf (f, "\t.pad #%d\n",
+                current_function_pretend_args_size);
+
       if (cfun->machine->uses_anonymous_args)
        {
          int num_pushes;
@@ -13435,6 +13471,9 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 
       work_register = thumb_find_work_register (live_regs_mask);
 
+      if (ARM_EABI_UNWIND_TABLES)
+       asm_fprintf (f, "\t.pad #16\n");
+
       asm_fprintf
        (f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n",
         SP_REGNUM, SP_REGNUM);
@@ -14662,3 +14701,261 @@ arm_dbx_register_number (unsigned int regno)
   gcc_unreachable ();
 }
 
+
+#ifdef TARGET_UNWIND_INFO
+/* Emit unwind directives for a store-multiple instruction.  This should
+   only ever be generated by the function prologue code, so we expect it
+   to have a particular form.  */
+
+static void
+arm_unwind_emit_stm (FILE * asm_out_file, rtx p)
+{
+  int i;
+  HOST_WIDE_INT offset;
+  HOST_WIDE_INT nregs;
+  int reg_size;
+  unsigned reg;
+  unsigned lastreg;
+  rtx e;
+
+  /* First insn will adjust the stack pointer.  */
+  e = XVECEXP (p, 0, 0);
+  if (GET_CODE (e) != SET
+      || GET_CODE (XEXP (e, 0)) != REG
+      || REGNO (XEXP (e, 0)) != SP_REGNUM
+      || GET_CODE (XEXP (e, 1)) != PLUS)
+    abort ();
+
+  offset = -INTVAL (XEXP (XEXP (e, 1), 1));
+  nregs = XVECLEN (p, 0) - 1;
+
+  reg = REGNO (XEXP (XVECEXP (p, 0, 1), 1));
+  if (reg < 16)
+    {
+      /* The function prologue may also push pc, but not annotate it as it is
+        never restored.  We turn this into an stack pointer adjustment.  */
+      if (nregs * 4 == offset - 4)
+       {
+         fprintf (asm_out_file, "\t.pad #4\n");
+         offset -= 4;
+       }
+      reg_size = 4;
+    }
+  else if (IS_VFP_REGNUM (reg))
+    {
+      /* FPA register saves use an additional word.  */
+      offset -= 4;
+      reg_size = 8;
+    }
+  else if (reg >= FIRST_FPA_REGNUM && reg <= LAST_FPA_REGNUM)
+    {
+      /* FPA registers are done differently.  */
+      asm_fprintf (asm_out_file, "\t.save %r, %d\n", reg, nregs);
+      return;
+    }
+  else
+    /* Unknown register type.  */
+    abort ();
+
+  /* If the stack increment doesn't match the size of the saved registers,
+     something has gone horribly wrong.  */
+  if (offset != nregs * reg_size)
+    abort ();
+
+  fprintf (asm_out_file, "\t.save {");
+
+  offset = 0;
+  lastreg = 0;
+  /* The remaining insns will describe the stores.  */
+  for (i = 1; i <= nregs; i++)
+    {
+      /* Expect (set (mem <addr>) (reg)).
+         Where <addr> is (reg:SP) or (plus (reg:SP) (const_int)).  */
+      e = XVECEXP (p, 0, i);
+      if (GET_CODE (e) != SET
+         || GET_CODE (XEXP (e, 0)) != MEM
+         || GET_CODE (XEXP (e, 1)) != REG)
+       abort ();
+      
+      reg = REGNO (XEXP (e, 1));
+      if (reg < lastreg)
+       abort ();
+         
+      if (i != 1)
+       fprintf (asm_out_file, ", ");
+      /* We can't use %r for vfp because we need to use the
+        double precision register names.  */
+      if (IS_VFP_REGNUM (reg))
+       asm_fprintf (asm_out_file, "d%d", (reg - FIRST_VFP_REGNUM) / 2);
+      else
+       asm_fprintf (asm_out_file, "%r", reg);
+
+#ifdef ENABLE_CHECKING
+      /* Check that the addresses are consecutive.  */
+      e = XEXP (XEXP (e, 0), 0);
+      if (GET_CODE (e) == PLUS)
+       {
+         offset += reg_size;
+         if (GET_CODE (XEXP (e, 0)) != REG
+             || REGNO (XEXP (e, 0)) != SP_REGNUM
+             || GET_CODE (XEXP (e, 1)) != CONST_INT
+             || offset != INTVAL (XEXP (e, 1)))
+           abort ();
+       }
+      else if (i != 1
+              || GET_CODE (e) != REG
+              || REGNO (e) != SP_REGNUM)
+       abort ();
+#endif
+    }
+  fprintf (asm_out_file, "}\n");
+}
+
+/*  Emit unwind directives for a SET.  */
+
+static void
+arm_unwind_emit_set (FILE * asm_out_file, rtx p)
+{
+  rtx e0;
+  rtx e1;
+
+  e0 = XEXP (p, 0);
+  e1 = XEXP (p, 1);
+  switch (GET_CODE (e0))
+    {
+    case MEM:
+      /* Pushing a single register.  */
+      if (GET_CODE (XEXP (e0, 0)) != PRE_DEC
+         || GET_CODE (XEXP (XEXP (e0, 0), 0)) != REG
+         || REGNO (XEXP (XEXP (e0, 0), 0)) != SP_REGNUM)
+       abort ();
+
+      asm_fprintf (asm_out_file, "\t.save ");
+      if (IS_VFP_REGNUM (REGNO (e1)))
+       asm_fprintf(asm_out_file, "{d%d}\n",
+                   (REGNO (e1) - FIRST_VFP_REGNUM) / 2);
+      else
+       asm_fprintf(asm_out_file, "{%r}\n", REGNO (e1));
+      break;
+
+    case REG:
+      if (REGNO (e0) == SP_REGNUM)
+       {
+         /* A stack increment.  */
+         if (GET_CODE (e1) != PLUS
+             || GET_CODE (XEXP (e1, 0)) != REG
+             || REGNO (XEXP (e1, 0)) != SP_REGNUM
+             || GET_CODE (XEXP (e1, 1)) != CONST_INT)
+           abort ();
+
+         asm_fprintf (asm_out_file, "\t.pad #%d\n",
+                      -INTVAL (XEXP (e1, 1)));
+       }
+      else if (REGNO (e0) == HARD_FRAME_POINTER_REGNUM)
+       {
+         HOST_WIDE_INT offset;
+         unsigned reg;
+         
+         if (GET_CODE (e1) == PLUS)
+           {
+             if (GET_CODE (XEXP (e1, 0)) != REG
+                 || GET_CODE (XEXP (e1, 1)) != CONST_INT)
+               abort ();
+             reg = REGNO (XEXP (e1, 0));
+             offset = INTVAL (XEXP (e1, 1));
+             asm_fprintf (asm_out_file, "\t.setfp %r, %r, #%d\n",
+                          HARD_FRAME_POINTER_REGNUM, reg,
+                          INTVAL (XEXP (e1, 1)));
+           }
+         else if (GET_CODE (e1) == REG)
+           {
+             reg = REGNO (e1);
+             asm_fprintf (asm_out_file, "\t.setfp %r, %r\n",
+                          HARD_FRAME_POINTER_REGNUM, reg);
+           }
+         else
+           abort ();
+       }
+      else if (GET_CODE (e1) == REG && REGNO (e1) == SP_REGNUM)
+       {
+         /* Move from sp to reg.  */
+         asm_fprintf (asm_out_file, "\t.movsp %r\n", REGNO (e0));
+       }
+      else
+       abort ();
+      break;
+
+    default:
+      abort ();
+    }
+}
+
+
+/* Emit unwind directives for the given insn.  */
+
+static void
+arm_unwind_emit (FILE * asm_out_file, rtx insn)
+{
+  rtx pat;
+
+  if (!ARM_EABI_UNWIND_TABLES)
+    return;
+
+  if (GET_CODE (insn) == NOTE || !RTX_FRAME_RELATED_P (insn))
+    return;
+
+  pat = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX);
+  if (pat)
+    pat = XEXP (pat, 0);
+  else
+    pat = PATTERN (insn);
+
+  switch (GET_CODE (pat))
+    {
+    case SET:
+      arm_unwind_emit_set (asm_out_file, pat);
+      break;
+
+    case SEQUENCE:
+      /* Store multiple.  */
+      arm_unwind_emit_stm (asm_out_file, pat);
+      break;
+
+    default:
+      abort();
+    }
+}
+
+
+/* Output a reference from a function exception table to the type_info
+   object X.  The EABI specifies that the symbol should be relocated by
+   an R_ARM_TARGET2 relocation.  */
+
+static bool
+arm_output_ttype (rtx x)
+{
+  fputs ("\t.word\t", asm_out_file);
+  output_addr_const (asm_out_file, x);
+  /* Use special relocations for symbol references.  */
+  if (GET_CODE (x) != CONST_INT)
+    fputs ("(TARGET2)", asm_out_file);
+  fputc ('\n', asm_out_file);
+
+  return TRUE;
+}
+#endif /* TARGET_UNWIND_INFO */
+
+
+/* Output unwind directives for the start/end of a function.  */
+
+void
+arm_output_fn_unwind (FILE * f, bool prologue)
+{
+  if (!ARM_EABI_UNWIND_TABLES)
+    return;
+
+  if (prologue)
+    fputs ("\t.fnstart\n", f);
+  else
+    fputs ("\t.fnend\n", f);
+}