winnt.c (i386_pe_seh_end_prologue): Move code to ...
authorTristan Gingold <gingold@adacore.com>
Mon, 25 Jun 2012 08:25:43 +0000 (08:25 +0000)
committerTristan Gingold <gingold@gcc.gnu.org>
Mon, 25 Jun 2012 08:25:43 +0000 (08:25 +0000)
2012-06-25  Tristan Gingold  <gingold@adacore.com>

* config/i386/winnt.c (i386_pe_seh_end_prologue): Move code to ...
(seh_cfa_adjust_cfa): ... that function.
(seh_emit_stackalloc): Do not emit out of range values.
* config/i386/i386.md: Delete unused UNSPEC_REG_SAVE,
UNSPEC_DEF_CFA constants.
* config/i386/i386.h (SEH_MAX_FRAME_SIZE): Define.
* config/i386/i386.c (ix86_frame_pointer_required): Required
for very large frames on SEH target.
(ix86_compute_frame_layout): Save area is before frame pointer
on SEH target.  Handle very large frames.
(ix86_expand_prologue): Likewise.

From-SVN: r188928

gcc/ChangeLog
gcc/config/i386/i386.c
gcc/config/i386/i386.h
gcc/config/i386/i386.md
gcc/config/i386/winnt.c

index ef1f53c175b37b45dcd7c72832e34e789cae224b..fb64279ac68260c79ae665d41e00abf66b95e965 100644 (file)
@@ -1,3 +1,17 @@
+2012-06-25  Tristan Gingold  <gingold@adacore.com>
+
+       * config/i386/winnt.c (i386_pe_seh_end_prologue): Move code to ...
+       (seh_cfa_adjust_cfa): ... that function.
+       (seh_emit_stackalloc): Do not emit out of range values.
+       * config/i386/i386.md: Delete unused UNSPEC_REG_SAVE,
+       UNSPEC_DEF_CFA constants.
+       * config/i386/i386.h (SEH_MAX_FRAME_SIZE): Define.
+       * config/i386/i386.c (ix86_frame_pointer_required): Required
+       for very large frames on SEH target.
+       (ix86_compute_frame_layout): Save area is before frame pointer
+       on SEH target.  Handle very large frames.
+       (ix86_expand_prologue): Likewise.
+
 2012-06-24  Steven Bosscher  <steven@gcc.gnu.org>
 
        * output.h: (current_function_is_leaf,
index 7bcde8a2e164bcc42f5338f9907885774a3b54a2..7e2352d0fc6d71b612caab1b06deeb65d7bd7a1e 100644 (file)
@@ -8558,6 +8558,11 @@ ix86_frame_pointer_required (void)
   if (TARGET_32BIT_MS_ABI && cfun->calls_setjmp)
     return true;
 
+  /* Win64 SEH, very large frames need a frame-pointer as maximum stack
+     allocation is 4GB.  */
+  if (TARGET_64BIT_MS_ABI && get_frame_size () > SEH_MAX_FRAME_SIZE)
+    return true;
+
   /* In ix86_option_override_internal, TARGET_OMIT_LEAF_FRAME_POINTER
      turns off the frame pointer by default.  Turn it back on now if
      we've not got a leaf function.  */
@@ -9051,6 +9056,11 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
   offset += frame->nregs * UNITS_PER_WORD;
   frame->reg_save_offset = offset;
 
+  /* On SEH target, registers are pushed just before the frame pointer
+     location.  */
+  if (TARGET_SEH)
+    frame->hard_frame_pointer_offset = offset;
+
   /* Align and set SSE register save area.  */
   if (frame->nsseregs)
     {
@@ -9142,9 +9152,12 @@ ix86_compute_frame_layout (struct ix86_frame *frame)
     {
       HOST_WIDE_INT diff;
 
-      /* If we can leave the frame pointer where it is, do so.  */
+      /* If we can leave the frame pointer where it is, do so.  Also, returns
+        the establisher frame for __builtin_frame_address (0).  */
       diff = frame->stack_pointer_offset - frame->hard_frame_pointer_offset;
-      if (diff > 240 || (diff & 15) != 0)
+      if (diff <= SEH_MAX_FRAME_SIZE
+         && (diff > 240 || (diff & 15) != 0)
+         && !crtl->accesses_prior_frames)
        {
          /* Ideally we'd determine what portion of the local stack frame
             (within the constraint of the lowest 240) is most heavily used.
@@ -10146,6 +10159,7 @@ ix86_expand_prologue (void)
   struct ix86_frame frame;
   HOST_WIDE_INT allocate;
   bool int_registers_saved;
+  bool sse_registers_saved;
 
   ix86_finalize_stack_realign_flags ();
 
@@ -10298,6 +10312,9 @@ ix86_expand_prologue (void)
       m->fs.realigned = true;
     }
 
+  int_registers_saved = (frame.nregs == 0);
+  sse_registers_saved = (frame.nsseregs == 0);
+
   if (frame_pointer_needed && !m->fs.fp_valid)
     {
       /* Note: AT&T enter does NOT have reversed args.  Enter is probably
@@ -10305,6 +10322,17 @@ ix86_expand_prologue (void)
       insn = emit_insn (gen_push (hard_frame_pointer_rtx));
       RTX_FRAME_RELATED_P (insn) = 1;
 
+      /* Push registers now, before setting the frame pointer
+        on SEH target.  */
+      if (!int_registers_saved
+         && TARGET_SEH
+         && !frame.save_regs_using_mov)
+       {
+         ix86_emit_save_regs ();
+         int_registers_saved = true;
+         gcc_assert (m->fs.sp_offset == frame.reg_save_offset);
+       }
+
       if (m->fs.sp_offset == frame.hard_frame_pointer_offset)
        {
          insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
@@ -10317,8 +10345,6 @@ ix86_expand_prologue (void)
        }
     }
 
-  int_registers_saved = (frame.nregs == 0);
-
   if (!int_registers_saved)
     {
       /* If saving registers via PUSH, do so now.  */
@@ -10395,6 +10421,27 @@ ix86_expand_prologue (void)
       current_function_static_stack_size = stack_size;
     }
 
+  /* On SEH target with very large frame size, allocate an area to save
+     SSE registers (as the very large allocation won't be described).  */
+  if (TARGET_SEH
+      && frame.stack_pointer_offset > SEH_MAX_FRAME_SIZE
+      && !sse_registers_saved)
+    {
+      HOST_WIDE_INT sse_size =
+       frame.sse_reg_save_offset - frame.reg_save_offset;
+
+      gcc_assert (int_registers_saved);
+
+      /* No need to do stack checking as the area will be immediately
+        written.  */
+      pro_epilogue_adjust_stack (stack_pointer_rtx, stack_pointer_rtx,
+                                GEN_INT (-sse_size), -1,
+                                m->fs.cfa_reg == stack_pointer_rtx);
+      allocate -= sse_size;
+      ix86_emit_save_sse_regs_using_mov (frame.sse_reg_save_offset);
+      sse_registers_saved = true;
+    }
+
   /* The stack has already been decremented by the instruction calling us
      so probe if the size is non-negative to preserve the protection area.  */
   if (allocate >= 0 && flag_stack_check == STATIC_BUILTIN_STACK_CHECK)
@@ -10519,7 +10566,7 @@ ix86_expand_prologue (void)
 
   if (!int_registers_saved)
     ix86_emit_save_regs_using_mov (frame.reg_save_offset);
-  if (frame.nsseregs)
+  if (!sse_registers_saved)
     ix86_emit_save_sse_regs_using_mov (frame.sse_reg_save_offset);
 
   pic_reg_used = false;
@@ -10975,8 +11022,13 @@ ix86_expand_epilogue (int style)
        }
 
       /* First step is to deallocate the stack frame so that we can
-        pop the registers.  */
-      if (!m->fs.sp_valid)
+        pop the registers.  Also do it on SEH target for very large
+        frame as the emitted instructions aren't allowed by the ABI in
+        epilogues.  */
+      if (!m->fs.sp_valid
+         || (TARGET_SEH
+             && (m->fs.sp_offset - frame.reg_save_offset
+                 >= SEH_MAX_FRAME_SIZE)))
        {
          pro_epilogue_adjust_stack (stack_pointer_rtx, hard_frame_pointer_rtx,
                                     GEN_INT (m->fs.fp_offset
index f7f13d20cdcb91c48fb17b201fedd55cb597fd53..18633faa90aacf7cc842f69e998e7b3d0f62e900 100644 (file)
@@ -729,6 +729,18 @@ enum target_cpu_default
 /* Boundary (in *bits*) on which the incoming stack is aligned.  */
 #define INCOMING_STACK_BOUNDARY ix86_incoming_stack_boundary
 
+/* According to Windows x64 software convention, the maximum stack allocatable
+   in the prologue is 4G - 8 bytes.  Furthermore, there is a limited set of
+   instructions allowed to adjust the stack pointer in the epilog, forcing the
+   use of frame pointer for frames larger than 2 GB.  This theorical limit
+   is reduced by 256, an over-estimated upper bound for the stack use by the
+   prologue.
+   We define only one threshold for both the prolog and the epilog.  When the
+   frame size is larger than this threshold, we allocate the are to save SSE
+   regs, then save them, and then allocate the remaining.  There is no SEH
+   unwind info for this later allocation.  */
+#define SEH_MAX_FRAME_SIZE ((2U << 30) - 256)
+
 /* Target OS keeps a vector-aligned (128-bit, 16-byte) stack.  This is
    mandatory for the 64-bit ABI, and may or may not be true for other
    operating systems.  */
index da2f4b273483540c93b60d2beb1156cf5a732518..727ec2b1723a513b3f698331b9864d123c057fc1 100644 (file)
@@ -84,8 +84,6 @@
   ;; Prologue support
   UNSPEC_STACK_ALLOC
   UNSPEC_SET_GOT
-  UNSPEC_REG_SAVE
-  UNSPEC_DEF_CFA
   UNSPEC_SET_RIP
   UNSPEC_SET_GOT_OFFSET
   UNSPEC_MEMORY_BLOCKAGE
index c1ed1c0436923791996314a4d9a3da6329a46b65..10cdee8002235344cd3e94573ad598192f60b211 100644 (file)
@@ -829,22 +829,6 @@ i386_pe_seh_end_prologue (FILE *f)
     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;
 
@@ -915,7 +899,10 @@ seh_emit_stackalloc (FILE *f, struct seh_frame_state *seh,
     seh->cfa_offset += offset;
   seh->sp_offset += offset;
 
-  fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset);
+  /* Do not output the stackalloc in that case (it won't work as there is no
+     encoding for very large frame size).  */
+  if (offset < SEH_MAX_FRAME_SIZE)
+    fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset);
 }
 
 /* Process REG_CFA_ADJUST_CFA for SEH.  */
@@ -948,8 +935,19 @@ seh_cfa_adjust_cfa (FILE *f, struct seh_frame_state *seh, rtx pat)
     seh_emit_stackalloc (f, seh, reg_offset);
   else if (dest_regno == HARD_FRAME_POINTER_REGNUM)
     {
+      HOST_WIDE_INT offset;
+
       seh->cfa_reg = dest;
       seh->cfa_offset -= reg_offset;
+
+      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);
     }
   else
     gcc_unreachable ();