OSDN Git Service

2010-11-05 Kai Tietz <kai.tietz@onevision.com>
[pf3gnuchains/gcc-fork.git] / gcc / config / i386 / winnt.c
index c20a2ae..251bb5a 100644 (file)
@@ -30,11 +30,13 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree.h"
 #include "flags.h"
 #include "tm_p.h"
+#include "diagnostic-core.h"
 #include "toplev.h"
 #include "hashtab.h"
 #include "langhooks.h"
 #include "ggc.h"
 #include "target.h"
+#include "except.h"
 #include "lto-streamer.h"
 
 /* i386/PE specific attribute support.
@@ -324,10 +326,7 @@ i386_pe_binds_local_p (const_tree exp)
   /* Or a weak one, now that they are supported.  */
   if ((TREE_CODE (exp) == VAR_DECL || TREE_CODE (exp) == FUNCTION_DECL)
       && DECL_WEAK (exp))
-    /* But x64 gets confused and attempts to use unsupported GOTPCREL
-       relocations if we tell it the truth, so we still return true in
-       that case until the deeper problem can be fixed.  */
-    return (TARGET_64BIT && DEFAULT_ABI == MS_ABI);
+    return false;
 
   return true;
 }
@@ -722,11 +721,391 @@ i386_pe_file_end (void)
       drectve_section ();
       for (q = export_head; q != NULL; q = q->next)
        {
-         fprintf (asm_out_file, "\t.ascii \" -export:%s%s\"\n",
+         fprintf (asm_out_file, "\t.ascii \" -export:\\\"%s\\\"%s\"\n",
                   default_strip_name_encoding (q->name),
                   (q->is_data ? ",data" : ""));
        }
     }
 }
 
+\f
+/* x64 Structured Exception Handling unwind info.  */
+
+struct seh_frame_state
+{
+  /* SEH records saves relative to the "current" stack pointer, whether
+     or not there's a frame pointer in place.  This tracks the current
+     stack pointer offset from the CFA.  */
+  HOST_WIDE_INT sp_offset;
+
+  /* The CFA is located at CFA_REG + CFA_OFFSET.  */
+  HOST_WIDE_INT cfa_offset;
+  rtx cfa_reg;
+};
+
+/* Set up data structures beginning output for SEH.  */
+
+void
+i386_pe_seh_init (FILE *f)
+{
+  struct seh_frame_state *seh;
+
+  if (!TARGET_SEH)
+    return;
+  if (cfun->is_thunk)
+    return;
+
+  /* We cannot support DRAP with SEH.  We turned off support for it by
+     re-defining MAX_STACK_ALIGNMENT when SEH is enabled.  */
+  gcc_assert (!stack_realign_drap);
+
+  seh = XCNEW (struct seh_frame_state);
+  cfun->machine->seh = seh;
+
+  seh->sp_offset = INCOMING_FRAME_SP_OFFSET;
+  seh->cfa_offset = INCOMING_FRAME_SP_OFFSET;
+  seh->cfa_reg = stack_pointer_rtx;
+
+  fputs ("\t.seh_proc\t", f);
+  assemble_name (f, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (cfun->decl)));
+  fputc ('\n', f);
+}
+
+void
+i386_pe_seh_end_prologue (FILE *f)
+{
+  struct seh_frame_state *seh;
+
+  if (!TARGET_SEH)
+    return;
+  if (cfun->is_thunk)
+    return;
+  seh = cfun->machine->seh;
+
+  /* Emit an assembler directive to set up the frame pointer.  Always do
+     this last.  The documentation talks about doing this "before" any
+     other code that uses offsets, but (experimentally) that's after we
+     emit the codes in reverse order (handled by the assembler).  */
+  if (seh->cfa_reg != stack_pointer_rtx)
+    {
+      HOST_WIDE_INT offset = seh->sp_offset - seh->cfa_offset;
+
+      gcc_assert ((offset & 15) == 0);
+      gcc_assert (IN_RANGE (offset, 0, 240));
+
+      fputs ("\t.seh_setframe\t", f);
+      print_reg (seh->cfa_reg, 0, f);
+      fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset);
+    }
+
+  XDELETE (seh);
+  cfun->machine->seh = NULL;
+
+  fputs ("\t.seh_endprologue\n", f);
+}
+
+static void
+i386_pe_seh_fini (FILE *f)
+{
+  if (!TARGET_SEH)
+    return;
+  if (cfun->is_thunk)
+    return;
+  fputs ("\t.seh_endproc\n", f);
+}
+
+/* Emit an assembler directive to save REG via a PUSH.  */
+
+static void
+seh_emit_push (FILE *f, struct seh_frame_state *seh, rtx reg)
+{
+  unsigned int regno = REGNO (reg);
+
+  gcc_checking_assert (GENERAL_REGNO_P (regno));
+
+  seh->sp_offset += UNITS_PER_WORD;
+  if (seh->cfa_reg == stack_pointer_rtx)
+    seh->cfa_offset += UNITS_PER_WORD;
+
+  fputs ("\t.seh_pushreg\t", f);
+  print_reg (reg, 0, f);
+  fputc ('\n', f);
+}
+
+/* Emit an assembler directive to save REG at CFA - CFA_OFFSET.  */
+
+static void
+seh_emit_save (FILE *f, struct seh_frame_state *seh,
+              rtx reg, HOST_WIDE_INT cfa_offset)
+{
+  unsigned int regno = REGNO (reg);
+  HOST_WIDE_INT offset;
+
+  /* Negative save offsets are of course not supported, since that
+     would be a store below the stack pointer and thus clobberable.  */
+  gcc_assert (seh->sp_offset >= cfa_offset);
+  offset = seh->sp_offset - cfa_offset;
+
+  fputs ((SSE_REGNO_P (regno) ? "\t.seh_savexmm\t"
+        : GENERAL_REGNO_P (regno) ?  "\t.seh_savereg\t"
+        : (gcc_unreachable (), "")), f);
+  print_reg (reg, 0, f);
+  fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset);
+}
+
+/* Emit an assembler directive to adjust RSP by OFFSET.  */
+
+static void
+seh_emit_stackalloc (FILE *f, struct seh_frame_state *seh,
+                    HOST_WIDE_INT offset)
+{
+  /* We're only concerned with prologue stack allocations, which all
+     are subtractions from the stack pointer.  */
+  gcc_assert (offset < 0);
+  offset = -offset;
+
+  if (seh->cfa_reg == stack_pointer_rtx)
+    seh->cfa_offset += offset;
+  seh->sp_offset += offset;
+
+  fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset);
+}
+
+/* Process REG_CFA_ADJUST_CFA for SEH.  */
+
+static void
+seh_cfa_adjust_cfa (FILE *f, struct seh_frame_state *seh, rtx pat)
+{
+  rtx dest, src;
+  HOST_WIDE_INT reg_offset = 0;
+  unsigned int dest_regno;
+
+  dest = SET_DEST (pat);
+  src = SET_SRC (pat);
+
+  if (GET_CODE (src) == PLUS)
+    {
+      reg_offset = INTVAL (XEXP (src, 1));
+      src = XEXP (src, 0);
+    }
+  else if (GET_CODE (src) == MINUS)
+    {
+      reg_offset = -INTVAL (XEXP (src, 1));
+      src = XEXP (src, 0);
+    }
+  gcc_assert (src == stack_pointer_rtx);
+  gcc_assert (seh->cfa_reg == stack_pointer_rtx);
+  dest_regno = REGNO (dest);
+
+  if (dest_regno == STACK_POINTER_REGNUM)
+    seh_emit_stackalloc (f, seh, reg_offset);
+  else if (dest_regno == HARD_FRAME_POINTER_REGNUM)
+    {
+      seh->cfa_reg = dest;
+      seh->cfa_offset -= reg_offset;
+    }
+  else
+    gcc_unreachable ();
+}
+
+/* Process REG_CFA_OFFSET for SEH.  */
+
+static void
+seh_cfa_offset (FILE *f, struct seh_frame_state *seh, rtx pat)
+{
+  rtx dest, src;
+  HOST_WIDE_INT reg_offset;
+
+  dest = SET_DEST (pat);
+  src = SET_SRC (pat);
+
+  gcc_assert (MEM_P (dest));
+  dest = XEXP (dest, 0);
+  if (REG_P (dest))
+    reg_offset = 0;
+  else
+    {
+      gcc_assert (GET_CODE (dest) == PLUS);
+      reg_offset = INTVAL (XEXP (dest, 1));
+      dest = XEXP (dest, 0);
+    }
+  gcc_assert (dest == seh->cfa_reg);
+
+  seh_emit_save (f, seh, src, seh->cfa_offset - reg_offset);
+}
+
+/* Process a FRAME_RELATED_EXPR for SEH.  */
+
+static void
+seh_frame_related_expr (FILE *f, struct seh_frame_state *seh, rtx pat)
+{
+  rtx dest, src;
+  HOST_WIDE_INT addend;
+
+  /* See the full loop in dwarf2out_frame_debug_expr.  */
+  if (GET_CODE (pat) == PARALLEL || GET_CODE (pat) == SEQUENCE)
+    {
+      int i, n = XVECLEN (pat, 0), pass, npass;
+
+      npass = (GET_CODE (pat) == PARALLEL ? 2 : 1);
+      for (pass = 0; pass < npass; ++pass)
+       for (i = 0; i < n; ++i)
+         {
+           rtx ele = XVECEXP (pat, 0, i);
+
+           if (GET_CODE (ele) != SET)
+             continue;
+           dest = SET_DEST (ele);
+
+           /* Process each member of the PARALLEL independently.  The first
+              member is always processed; others only if they are marked.  */
+           if (i == 0 || RTX_FRAME_RELATED_P (ele))
+             {
+               /* Evaluate all register saves in the first pass and all
+                  register updates in the second pass.  */
+               if ((MEM_P (dest) ^ pass) || npass == 1)
+                 seh_frame_related_expr (f, seh, ele);
+             }
+         }
+      return;
+    }
+
+  dest = SET_DEST (pat);
+  src = SET_SRC (pat);
+
+  switch (GET_CODE (dest))
+    {
+    case REG:
+      switch (GET_CODE (src))
+       {
+       case REG:
+         /* REG = REG: This should be establishing a frame pointer.  */
+         gcc_assert (src == stack_pointer_rtx);
+         gcc_assert (dest == hard_frame_pointer_rtx);
+         seh_cfa_adjust_cfa (f, seh, pat);
+         break;
+
+       case PLUS:
+         addend = INTVAL (XEXP (src, 1));
+         src = XEXP (src, 0);
+         if (dest == hard_frame_pointer_rtx)
+           seh_cfa_adjust_cfa (f, seh, pat);
+         else if (dest == stack_pointer_rtx)
+           {
+             gcc_assert (src == stack_pointer_rtx);
+             seh_emit_stackalloc (f, seh, addend);
+           }
+         else
+           gcc_unreachable ();
+         break;
+
+       default:
+         gcc_unreachable ();
+       }
+      break;
+
+    case MEM:
+      /* A save of some kind.  */
+      dest = XEXP (dest, 0);
+      if (GET_CODE (dest) == PRE_DEC)
+       {
+         gcc_checking_assert (GET_MODE (src) == Pmode);
+         gcc_checking_assert (REG_P (src));
+         seh_emit_push (f, seh, src);
+       }
+      else
+       seh_cfa_offset (f, seh, pat);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* This function looks at a single insn and emits any SEH directives
+   required for unwind of this insn.  */
+
+void
+i386_pe_seh_unwind_emit (FILE *asm_out_file, rtx insn)
+{
+  rtx note, pat;
+  bool handled_one = false;
+  struct seh_frame_state *seh;
+
+  if (!TARGET_SEH)
+    return;
+
+  /* We free the SEH data once done with the prologue.  Ignore those
+     RTX_FRAME_RELATED_P insns that are associated with the epilogue.  */
+  seh = cfun->machine->seh;
+  if (seh == NULL)
+    return;
+
+  if (NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn))
+    return;
+
+  for (note = REG_NOTES (insn); note ; note = XEXP (note, 1))
+    {
+      pat = XEXP (note, 0);
+      switch (REG_NOTE_KIND (note))
+       {
+       case REG_FRAME_RELATED_EXPR:
+         goto found;
+
+       case REG_CFA_DEF_CFA:
+       case REG_CFA_EXPRESSION:
+         /* Only emitted with DRAP, which we disable.  */
+         gcc_unreachable ();
+         break;
+
+       case REG_CFA_REGISTER:
+         /* Only emitted in epilogues, which we skip.  */
+         gcc_unreachable ();
+
+       case REG_CFA_ADJUST_CFA:
+         if (pat == NULL)
+           {
+             pat = PATTERN (insn);
+             if (GET_CODE (pat) == PARALLEL)
+               pat = XVECEXP (pat, 0, 0);
+           }
+         seh_cfa_adjust_cfa (asm_out_file, seh, pat);
+         handled_one = true;
+         break;
+
+       case REG_CFA_OFFSET:
+         if (pat == NULL)
+           pat = single_set (insn);
+         seh_cfa_offset (asm_out_file, seh, pat);
+         handled_one = true;
+         break;
+
+       default:
+         break;
+       }
+    }
+  if (handled_one)
+    return;
+  pat = PATTERN (insn);
+ found:
+  seh_frame_related_expr (asm_out_file, seh, pat);
+}
+\f
+void
+i386_pe_start_function (FILE *f, const char *name, tree decl)
+{
+  i386_pe_maybe_record_exported_symbol (decl, name, 0);
+  if (write_symbols != SDB_DEBUG)
+    i386_pe_declare_function_type (f, name, TREE_PUBLIC (decl));
+  ASM_OUTPUT_FUNCTION_LABEL (f, name, decl);
+}
+
+void
+i386_pe_end_function (FILE *f, const char *name ATTRIBUTE_UNUSED,
+                     tree decl ATTRIBUTE_UNUSED)
+{
+  i386_pe_seh_fini (f);
+}
+\f
+
 #include "gt-winnt.h"