diff mbox

[3/3] First take at SEH.

Message ID 1281474444-8772-4-git-send-email-rth@redhat.com
State New
Headers show

Commit Message

Richard Henderson Aug. 10, 2010, 9:07 p.m. UTC
From: Richard Henderson <rth@twiddle.net>

Horrid parsing code manages to build 2 SEH frames for DRAP functions.
Which doesn't work during the epilogues, and it is unknown if it will
even work for the body of the function.
---
 gcc/config/i386/cygming.h     |   24 ++-
 gcc/config/i386/i386-protos.h |    7 +
 gcc/config/i386/i386.c        |   56 +----
 gcc/config/i386/i386.h        |   52 ++++
 gcc/config/i386/winnt.c       |  561 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 641 insertions(+), 59 deletions(-)
diff mbox

Patch

diff --git a/gcc/config/i386/cygming.h b/gcc/config/i386/cygming.h
index 4962921..9bd04c7 100644
--- a/gcc/config/i386/cygming.h
+++ b/gcc/config/i386/cygming.h
@@ -36,6 +36,15 @@  along with GCC; see the file COPYING3.  If not see
 #undef TARGET_SEH
 #define TARGET_SEH  TARGET_64BIT_MS_ABI
 
+/* Support hooks for SEH.  */
+#undef  TARGET_ASM_UNWIND_EMIT
+#define TARGET_ASM_UNWIND_EMIT  i386_pe_seh_unwind_emit
+#undef  TARGET_ASM_UNWIND_EMIT_BEFORE_INSN
+#define TARGET_ASM_UNWIND_EMIT_BEFORE_INSN  false
+#undef  TARGET_ASM_FUNCTION_END_PROLOGUE
+#define TARGET_ASM_FUNCTION_END_PROLOGUE  i386_pe_seh_end_prologue
+#define SUBTARGET_ASM_UNWIND_INIT  i386_pe_seh_init
+
 #undef DEFAULT_ABI
 #define DEFAULT_ABI (TARGET_64BIT ? MS_ABI : SYSV_ABI)
 
@@ -270,15 +279,12 @@  do {						\
    properly.  If we are generating SDB debugging information, this
    will happen automatically, so we only need to handle other cases.  */
 #undef ASM_DECLARE_FUNCTION_NAME
-#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL)			\
-  do									\
-    {									\
-      i386_pe_maybe_record_exported_symbol (DECL, NAME, 0);		\
-      if (write_symbols != SDB_DEBUG)					\
-	i386_pe_declare_function_type (FILE, NAME, TREE_PUBLIC (DECL));	\
-      ASM_OUTPUT_FUNCTION_LABEL (FILE, NAME, DECL);			\
-    }									\
-  while (0)
+#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
+  i386_pe_start_function (FILE, NAME, DECL)
+
+#undef ASM_DECLARE_FUNCTION_SIZE
+#define ASM_DECLARE_FUNCTION_SIZE(FILE,NAME,DECL) \
+  i386_pe_end_function (FILE, NAME, DECL)
 
 /* Add an external function to the list of functions to be declared at
    the end of the file.  */
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index e41d810..cad9ee3 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -31,6 +31,7 @@  extern void ix86_setup_frame_addresses (void);
 extern HOST_WIDE_INT ix86_initial_elimination_offset (int, int);
 extern void ix86_expand_prologue (void);
 extern void ix86_expand_epilogue (int);
+extern void ix86_compute_frame_layout (struct ix86_frame *);
 
 extern void ix86_output_addr_vec_elt (FILE *, int);
 extern void ix86_output_addr_diff_elt (FILE *, int, int);
@@ -230,8 +231,14 @@  extern void i386_pe_asm_output_aligned_decl_common (FILE *, tree,
 						    HOST_WIDE_INT,
 						    HOST_WIDE_INT);
 extern void i386_pe_file_end (void);
+extern void i386_pe_start_function (FILE *, const char *, tree);
+extern void i386_pe_end_function (FILE *, const char *, tree);
 extern tree i386_pe_mangle_decl_assembler_name (tree, tree);
 
+extern void i386_pe_seh_init (FILE *);
+extern void i386_pe_seh_end_prologue (FILE *);
+extern void i386_pe_seh_unwind_emit (FILE *, rtx);
+
 /* In winnt-cxx.c and winnt-stubs.c  */
 extern void i386_pe_adjust_class_at_definition (tree);
 extern bool i386_pe_type_dllimport_p (tree);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 938a686..006611b 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -1818,53 +1818,6 @@  struct GTY(()) stack_local_entry {
   struct stack_local_entry *next;
 };
 
-/* Structure describing stack frame layout.
-   Stack grows downward:
-
-   [arguments]
-					<- ARG_POINTER
-   saved pc
-
-   saved static chain			if ix86_static_chain_on_stack
-
-   saved frame pointer			if frame_pointer_needed
-					<- HARD_FRAME_POINTER
-   [saved regs]
-					<- regs_save_offset
-   [padding0]
-
-   [saved SSE regs]
-					<- sse_regs_save_offset
-   [padding1]          |
-		       |		<- FRAME_POINTER
-   [va_arg registers]  |
-		       |
-   [frame]	       |
-		       |
-   [padding2]	       | = to_allocate
-					<- STACK_POINTER
-  */
-struct ix86_frame
-{
-  int nsseregs;
-  int nregs;
-  int va_arg_size;
-  int red_zone_size;
-  int outgoing_arguments_size;
-  HOST_WIDE_INT frame;
-
-  /* The offsets relative to ARG_POINTER.  */
-  HOST_WIDE_INT frame_pointer_offset;
-  HOST_WIDE_INT hard_frame_pointer_offset;
-  HOST_WIDE_INT stack_pointer_offset;
-  HOST_WIDE_INT reg_save_offset;
-  HOST_WIDE_INT sse_reg_save_offset;
-
-  /* When save_regs_using_mov is set, emit prologue using
-     move instead of push instructions.  */
-  bool save_regs_using_mov;
-};
-
 /* Code model option.  */
 enum cmodel ix86_cmodel;
 /* Asm dialect.  */
@@ -1976,7 +1929,6 @@  static rtx ix86_function_value (const_tree, const_tree, bool);
 static bool ix86_function_value_regno_p (const unsigned int);
 static rtx ix86_static_chain (const_tree, bool);
 static int ix86_function_regparm (const_tree, const_tree);
-static void ix86_compute_frame_layout (struct ix86_frame *);
 static bool ix86_expand_vector_init_one_nonzero (bool, enum machine_mode,
 						 rtx, rtx, int);
 static void ix86_add_new_builtins (int);
@@ -2005,7 +1957,6 @@  static void ix86_set_current_function (tree);
 static unsigned int ix86_minimum_incoming_stack_boundary (bool);
 
 static enum calling_abi ix86_function_abi (const_tree);
-
 
 #ifndef SUBTARGET32_DEFAULT_CPU
 #define SUBTARGET32_DEFAULT_CPU "i386"
@@ -5166,6 +5117,10 @@  ix86_asm_output_function_label (FILE *asm_out_file, const char *fname,
         fprintf (asm_out_file, ASM_LONG " %#x\n", filler_cc);
     }
 
+#ifdef SUBTARGET_ASM_UNWIND_INIT
+  SUBTARGET_ASM_UNWIND_INIT (asm_out_file);
+#endif
+
   ASM_OUTPUT_LABEL (asm_out_file, fname);
 
   /* Output magic byte marker, if hot-patch attribute is set.  */
@@ -8318,7 +8273,7 @@  ix86_builtin_setjmp_frame_value (void)
 
 /* Fill structure ix86_frame about frame of currently computed function.  */
 
-static void
+void
 ix86_compute_frame_layout (struct ix86_frame *frame)
 {
   unsigned int stack_alignment_needed;
@@ -31476,6 +31431,7 @@  ix86_enum_va_list (int idx, const char **pname, tree *ptree)
 
   return 0;
 }
+
 
 /* Initialize the GCC target structure.  */
 #undef TARGET_RETURN_IN_MEMORY
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index eb73df7..e998d15 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2333,6 +2333,55 @@  struct GTY(()) machine_frame_state
   BOOL_BITFIELD realigned : 1;
 };
 
+/* Structure describing stack frame layout.
+   Stack grows downward:
+
+   [arguments]
+					<- ARG_POINTER
+   saved pc
+
+   saved static chain			if ix86_static_chain_on_stack
+
+   saved frame pointer			if frame_pointer_needed
+					<- HARD_FRAME_POINTER
+   [saved regs]
+					<- regs_save_offset
+   [padding0]
+
+   [saved SSE regs]
+					<- sse_regs_save_offset
+   [padding1]          |
+		       |		<- FRAME_POINTER
+   [va_arg registers]  |
+		       |
+   [frame]	       |
+		       |
+   [padding2]	       | = to_allocate
+					<- STACK_POINTER
+  */
+struct ix86_frame
+{
+  int nsseregs;
+  int nregs;
+  int va_arg_size;
+  int red_zone_size;
+  int outgoing_arguments_size;
+  HOST_WIDE_INT frame;
+
+  /* The offsets relative to ARG_POINTER.  */
+  HOST_WIDE_INT frame_pointer_offset;
+  HOST_WIDE_INT hard_frame_pointer_offset;
+  HOST_WIDE_INT stack_pointer_offset;
+  HOST_WIDE_INT reg_save_offset;
+  HOST_WIDE_INT sse_reg_save_offset;
+
+  /* When save_regs_using_mov is set, emit prologue using
+     move instead of push instructions.  */
+  bool save_regs_using_mov;
+};
+
+struct seh_frame_state;
+
 struct GTY(()) machine_function {
   struct stack_local_entry *stack_locals;
   const char *some_ld_name;
@@ -2376,6 +2425,9 @@  struct GTY(()) machine_function {
   /* During prologue/epilogue generation, the current frame state.
      Otherwise, the frame state at the end of the prologue.  */
   struct machine_frame_state fs;
+
+  /* During SEH output, this is non-null.  */
+  struct seh_frame_state * GTY((skip(""))) seh;
 };
 #endif
 
diff --git a/gcc/config/i386/winnt.c b/gcc/config/i386/winnt.c
index 60a8b79..f432fda 100644
--- a/gcc/config/i386/winnt.c
+++ b/gcc/config/i386/winnt.c
@@ -730,4 +730,565 @@  i386_pe_file_end (void)
     }
 }
 
+
+/* x64 Structured Exception Handling unwind info.  */
+
+struct seh_frame_state
+{
+  /* The frame data for this function.  */
+  struct ix86_frame frame;
+
+  /* SEH uses unsigned offsets for all register saves.  When using a frame
+     pointer, these offsets are relative to FP_BASE = FP_REG - FP_OFFSET.
+     Not to be confusing with too many "offset" values, but this variable
+     contains the CFA offset of the FP_BASE we've chosen for this function.
+     This makes it easy fo compare with other CFA offsets we'll be
+     extracting from the RTX_FRAME_RELATED expressions.  */
+  HOST_WIDE_INT fp_base_offset;
+
+  /* The CFA is located at CFA_REG + CFA_OFFSET.  */
+  HOST_WIDE_INT cfa_offset;
+  unsigned int cfa_regno;
+  
+  /* From the SEH docs: "If an FP reg is used, then any unwind code
+     taking an offset must only be used after the FP reg is established
+     in the prolog."  This records if such an event has ocurred.  */
+  bool reg_save_emitted;
+
+  /* This is all kinds of tricky:
+
+     In order to support functions with a DRAP, we actually emit two SEH
+     regions; the first covers from the function entry point until we've
+     re-aligned the stack.  The second covers the balance of the function.
+     In the first region we set up DRAP as the FP.  This keeps track of
+     the entry stack value while we perform the re-alignment.  In the
+     second region we set up RBP as the FP as normal, and use the saved
+     CFA within the stack frame as the saved RSP value.
+
+     This is complicated by the fact that we can't begin this second SEH
+     frame until we've performed the DRAP store into the re-aligned frame,
+     because we have to immediately record the RSP store.  Thus we must
+     queue other register stores that happen beforehand.  */
+  bool saw_realign;
+  bool saw_realign_frame;
+
+  /* If we see a register store while SAW_REALIGN, then record the CFA
+     offset of the store here.  */
+#define MAX_SEH_SAVE 32
+  int realign_save_count;
+  rtx realign_save_reg[MAX_SEH_SAVE];
+  int realign_save_off[MAX_SEH_SAVE];
+};
+
+static void seh_emit_realign_frame (FILE *, struct seh_frame_state *,
+				    HOST_WIDE_INT);
+
+/* Set up data structures beginning output for SEH.  */
+
+void
+i386_pe_seh_init (FILE *f)
+{
+  struct seh_frame_state *seh;
+
+  if (!TARGET_SEH)
+    return;
+
+  seh = XCNEW (struct seh_frame_state);
+  cfun->machine->seh = seh;
+
+  ix86_compute_frame_layout (&seh->frame);
+
+  if (frame_pointer_needed)
+    {
+      /* Careful: FP_OFFSET must be a multiple of 16.  If we have no
+	 SSE registers to save, then we won't have aligned the SSE
+	 register save area, and so this offset might not be aligned.  */
+      if (seh->frame.nsseregs == 0)
+	seh->frame.sse_reg_save_offset += seh->frame.sse_reg_save_offset & 8;
+      gcc_checking_assert ((seh->frame.sse_reg_save_offset & 15) == 0);
+    }
+
+  seh->cfa_offset = INCOMING_FRAME_SP_OFFSET;
+  seh->cfa_regno = STACK_POINTER_REGNUM;
+
+  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)
+{
+  if (!TARGET_SEH)
+    return;
+
+  XDELETE (cfun->machine->seh);
+  cfun->machine->seh = NULL;
+
+  fputs ("\t.seh_endprologue\n", f);
+}
+
+static void
+i386_pe_seh_fini (FILE *f)
+{
+  if (!TARGET_SEH)
+    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));
+
+  if (seh->cfa_regno == STACK_POINTER_REGNUM)
+    seh->cfa_offset += UNITS_PER_WORD;
+
+  if (seh->saw_realign && !seh->saw_realign_frame)
+    {
+      gcc_checking_assert (seh->cfa_regno == STACK_POINTER_REGNUM);
+      if (regno == REGNO (crtl->drap_reg))
+	seh_emit_realign_frame (f, seh, seh->cfa_offset);
+      else
+	{
+	  int i = seh->realign_save_count++;
+	  seh->realign_save_reg[i] = reg;
+	  seh->realign_save_off[i] = seh->cfa_offset;
+	}
+    }
+  else
+    {
+      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);
+
+  if (seh->saw_realign && !seh->saw_realign_frame)
+    {
+      if (regno == REGNO (crtl->drap_reg))
+	seh_emit_realign_frame (f, seh, cfa_offset);
+      else
+	{
+	  int i = seh->realign_save_count++;
+	  seh->realign_save_reg[i] = reg;
+	  seh->realign_save_off[i] = cfa_offset;
+	}
+    }
+  else
+    {
+      HOST_WIDE_INT base_offset, offset;
+
+      if (seh->cfa_regno == STACK_POINTER_REGNUM)
+	base_offset = seh->cfa_offset;
+      else
+	base_offset = seh->fp_base_offset;
+
+      /* Negative save offsets are not supported.  */
+      gcc_assert (base_offset >= cfa_offset);
+      offset = base_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);
+
+      seh->reg_save_emitted = true;
+    }
+}
+
+/* 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_regno == STACK_POINTER_REGNUM)
+    seh->cfa_offset += offset;
+
+  if (!seh->saw_realign || seh->saw_realign_frame)
+    fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset);
+}
+
+/* Emit an assembler directive to set up the frame pointer.  */
+
+static void
+seh_emit_setframe (FILE *f, struct seh_frame_state *seh,
+		   rtx reg, HOST_WIDE_INT cfa_offset,
+		   HOST_WIDE_INT max_offset)
+{
+  HOST_WIDE_INT offset;
+
+  if (seh->saw_realign && !seh->saw_realign_frame)
+    return;
+
+  offset = max_offset - seh->cfa_offset;
+  gcc_assert (seh->cfa_regno == STACK_POINTER_REGNUM);
+  gcc_assert (offset >= 0);
+  gcc_assert ((offset & 15) == 0);
+
+  seh->cfa_regno = REGNO (reg);
+  seh->cfa_offset = cfa_offset;
+  seh->fp_base_offset = max_offset;
+
+  fputs ("\t.seh_setframe\t", f);
+  print_reg (reg, 0, f);
+  fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset);
+}
+
+/* Emit assembler directives to establish a realigned stack frame.  */
+
+static void
+seh_emit_realign_frame (FILE *f, struct seh_frame_state *seh,
+			HOST_WIDE_INT drap_cfa_offset)
+{
+  int i;
+
+  gcc_assert (seh->saw_realign);
+  gcc_assert (!seh->saw_realign_frame);
+  seh->saw_realign_frame = true;
+
+  /* End the first SEH frame and begin another.  For some reason the
+     syntax of .seh_proc requires a symbol; invent a new "r.foo" symbol
+     to describe the second SEH frame.  */
+  fputs ("\t.seh_endproc\n", f);
+  fputs ("\t.seh_proc\tr.", f);
+  assemble_name (f, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (cfun->decl)));
+  fputc ('\n', f);
+
+  /* We've been tracking the current stack offset via CFA_OFFSET,
+     but we havn't been able to record anything so far.  Do so now.  */
+  gcc_assert (seh->cfa_regno == STACK_POINTER_REGNUM);
+  fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n",
+	   seh->cfa_offset);
+
+  /* Establish the frame pointer for the realigned frame.  */
+  seh_emit_setframe (f, seh, hard_frame_pointer_rtx,
+		     seh->frame.hard_frame_pointer_offset,
+		     seh->frame.sse_reg_save_offset);
+
+  /* Record all of the register saves we've seen to this point.  */
+  for (i = seh->realign_save_count - 1; i >= 0; --i)
+    seh_emit_save (f, seh, seh->realign_save_reg[i], seh->realign_save_off[i]);
+
+  /* Record the save of the DRAP as a save of RSP.  */
+  seh_emit_save (f, seh, stack_pointer_rtx, drap_cfa_offset);
+}
+
+/* Process REG_CFA_DEF_CFA for SEH.  */
+
+static void
+seh_cfa_def_cfa (FILE *f, struct seh_frame_state *seh, rtx pat)
+{
+  HOST_WIDE_INT addend = 0;
+
+  /* Another bit of a hack; see ix86_emit_save_reg_using_mov for
+     the original.  We've stored the DRAP register into the frame.
+     For dwarf2 we want to establish a new CFA.  For SEH we want
+     to record this as a save of the original RSP. 
+
+     The other instances of REG_CFA_DEF_CFA are in the epilogue,
+     which are not relevant here.  */
+  gcc_assert (MEM_P (pat));
+  pat = XEXP (pat, 0);
+  if (GET_CODE (pat) == PLUS)
+    {
+      addend = INTVAL (XEXP (pat, 1));
+      pat = XEXP (pat, 0);
+    }
+  gcc_assert (pat == hard_frame_pointer_rtx);
+	  
+  seh_emit_realign_frame (f, seh,
+			  seh->frame.hard_frame_pointer_offset - addend);
+}
+
+/* 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);
+    }
+  gcc_assert (src == stack_pointer_rtx);
+  gcc_assert (seh->cfa_regno == STACK_POINTER_REGNUM);
+  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_emit_setframe (f, seh, dest, seh->cfa_offset - reg_offset,
+		       seh->frame.sse_reg_save_offset);
+  else if (crtl->drap_reg && dest_regno == REGNO (crtl->drap_reg))
+    seh_emit_setframe (f, seh, dest, seh->cfa_offset - reg_offset,
+		       seh->cfa_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 (REGNO (dest) == seh->cfa_regno);
+
+  seh_emit_save (f, seh, src, seh->cfa_offset - reg_offset);
+}
+
+/* Process REG_CFA_EXPRESSION for SEH.  */
+
+static void
+seh_cfa_expression (FILE *f, struct seh_frame_state *seh, rtx pat)
+{
+  /* ??? This is valid only under extremely limited circumstances.
+     This should be a re-aligned stack frame with DRAP.  For dwarf2,
+     the CFA is still up with the DRAP and we need the CFA_expression
+     in order to save a register relative to RBP instead of relative
+     to the CFA.  For SEH, we should be in the (second) aligned frame
+     with the frame pointer established.  */
+  gcc_checking_assert (seh->saw_realign);
+  seh_cfa_offset (f, seh, pat);
+}
+
+/* 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)
+		  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_emit_setframe (f, seh, dest, seh->cfa_offset,
+			     seh->frame.sse_reg_save_offset);
+	  break;
+
+	case PLUS:
+	  addend = INTVAL (XEXP (src, 1));
+	  src = XEXP (src, 0);
+	  if (REGNO (src) == seh->cfa_regno)
+	    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;
+
+	case AND:
+	  gcc_assert (dest == stack_pointer_rtx);
+	  if (stack_realign_drap)
+	    {
+	      seh->saw_realign = true;
+	      seh->cfa_regno = STACK_POINTER_REGNUM;
+	      seh->cfa_offset = 0;
+
+	      fputs ("\t.seh_endprologue\n", f);
+	    }
+	  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);
+
+	  /* During drap, we push a copy of the return address so that
+	     it's in the aligned stack frame.  We need do nothing except
+	     account for the stack usage.  */
+	  if (MEM_P (src))
+	    seh_emit_stackalloc (f, seh, -UNITS_PER_WORD);
+	  else
+	    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;
+  if (NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn))
+    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;
+
+  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:
+	  seh_cfa_def_cfa (asm_out_file, seh, pat);
+	  handled_one = true;
+	  break;
+
+	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;
+
+	case REG_CFA_REGISTER:
+	  gcc_unreachable ();
+
+	case REG_CFA_EXPRESSION:
+	  seh_cfa_expression (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);
+}
+
+
+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);
+}
+
+
 #include "gt-winnt.h"