[backport proposed gcc-4.8/trunk fix for SEH handling <mikpe@it.uu.se>]

From: Eric Botcazou <ebotcazou at adacore dot com>
Subject: [i386] Fix unwind/debug info for nested functions on 64-bit Windows
Date: Mon, 01 Oct 2012 10:11:26 +0200
List-Archive: <http://gcc.gnu.org/ml/gcc-patches/>

Hi,

in the section of ix86_expand_prologue establishing the frame for Windows 
targets, there is:

      /* Note that SEH directives need to continue tracking the stack
	 pointer even after the frame pointer has been set up.  */
      if (m->fs.cfa_reg == stack_pointer_rtx || TARGET_SEH)
	{
	  if (m->fs.cfa_reg == stack_pointer_rtx)
	    m->fs.cfa_offset += allocate;

	  RTX_FRAME_RELATED_P (insn) = 1;
	  add_reg_note (insn, REG_FRAME_RELATED_EXPR,
			gen_rtx_SET (VOIDmode, stack_pointer_rtx,
				     plus_constant (Pmode, stack_pointer_rtx,
						    -allocate)));
	}

But there is also a few lines above:

      if (eax_live)
	{
	  emit_insn (gen_push (eax));
	  allocate -= UNITS_PER_WORD;
	}
      if (r10_live)
	{
	  r10 = gen_rtx_REG (Pmode, R10_REG);
	  emit_insn (gen_push (r10));
	  allocate -= UNITS_PER_WORD;
	}

and these 2 pushes aren't marked, which can result in wrong SEH unwind and 
DWARF debug info on 64-bit Windows (we have an example of each kind in Ada).

Tested on x86_64-suse-linux and with a 4.7-based SEH-enabled compiler for 64-
Bit Windows.  OK for mainline and 4.7 branch?


2012-10-01  Eric Botcazou  <ebotcazou@adacore.com>

	* config/i386/i386.c (ix86_expand_prologue): Emit frame info for the
	special register pushes before frame probing and allocation.

--- gcc-4.7.2/gcc/config/i386/i386.c.~1~	2012-09-13 19:32:23.000000000 +0200
+++ gcc-4.7.2/gcc/config/i386/i386.c	2012-10-06 13:26:45.000000000 +0200
@@ -10339,7 +10339,7 @@ ix86_expand_prologue (void)
       rtx eax = gen_rtx_REG (Pmode, AX_REG);
       rtx r10 = NULL;
       rtx (*adjust_stack_insn)(rtx, rtx, rtx);
-
+      const bool sp_is_cfa_reg = (m->fs.cfa_reg == stack_pointer_rtx);
       bool eax_live = false;
       bool r10_live = false;
 
@@ -10348,16 +10348,31 @@ ix86_expand_prologue (void)
       if (!TARGET_64BIT_MS_ABI)
         eax_live = ix86_eax_live_at_start_p ();
 
+      /* Note that SEH directives need to continue tracking the stack
+	 pointer even after the frame pointer has been set up.  */
       if (eax_live)
 	{
-	  emit_insn (gen_push (eax));
+	  insn = emit_insn (gen_push (eax));
 	  allocate -= UNITS_PER_WORD;
+	  if (sp_is_cfa_reg || TARGET_SEH)
+	    {
+	      if (sp_is_cfa_reg)
+		m->fs.cfa_offset += UNITS_PER_WORD;
+	      RTX_FRAME_RELATED_P (insn) = 1;
+	    }
 	}
+
       if (r10_live)
 	{
 	  r10 = gen_rtx_REG (Pmode, R10_REG);
-	  emit_insn (gen_push (r10));
+	  insn = emit_insn (gen_push (r10));
 	  allocate -= UNITS_PER_WORD;
+	  if (sp_is_cfa_reg || TARGET_SEH)
+	    {
+	      if (sp_is_cfa_reg)
+		m->fs.cfa_offset += UNITS_PER_WORD;
+	      RTX_FRAME_RELATED_P (insn) = 1;
+	    }
 	}
 
       emit_move_insn (eax, GEN_INT (allocate));
@@ -10371,13 +10386,10 @@ ix86_expand_prologue (void)
       insn = emit_insn (adjust_stack_insn (stack_pointer_rtx,
 					   stack_pointer_rtx, eax));
 
-      /* Note that SEH directives need to continue tracking the stack
-	 pointer even after the frame pointer has been set up.  */
-      if (m->fs.cfa_reg == stack_pointer_rtx || TARGET_SEH)
+      if (sp_is_cfa_reg || TARGET_SEH)
 	{
-	  if (m->fs.cfa_reg == stack_pointer_rtx)
+	  if (sp_is_cfa_reg)
 	    m->fs.cfa_offset += allocate;
-
 	  RTX_FRAME_RELATED_P (insn) = 1;
 	  add_reg_note (insn, REG_FRAME_RELATED_EXPR,
 			gen_rtx_SET (VOIDmode, stack_pointer_rtx,
