PR target/10127
authorebotcazou <ebotcazou@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 3 Nov 2009 22:49:37 +0000 (22:49 +0000)
committerebotcazou <ebotcazou@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 3 Nov 2009 22:49:37 +0000 (22:49 +0000)
PR ada/20548
* expr.h (STACK_CHECK_PROBE_INTERVAL): Delete.
(STACK_CHECK_PROBE_INTERVAL_EXP): New macro.
(STACK_CHECK_MOVING_SP): Likewise.
* system.h (STACK_CHECK_PROBE_INTERVAL): Poison it.
* doc/tm.texi (Stack Checking): Delete STACK_CHECK_PROBE_INTERVAL.
Document STACK_CHECK_PROBE_INTERVAL_EXP and STACK_CHECK_MOVING_SP.
* doc/md.texi (Standard Pattern Names): Tweak entry of CHECK_STACK.
Document PROBE_STACK.
* explow.c (anti_adjust_stack_and_probe): New function.
(allocate_dynamic_stack_space): Do not directly allocate space if
STACK_CHECK_MOVING_SP, instead invoke above function.
(emit_stack_probe): Handle probe_stack insn.
(PROBE_INTERVAL): New macro.
(STACK_GROW_OPTAB): Likewise.
(STACK_GROW_OFF): Likewise.
(probe_stack_range): Use Pmode and memory_address consistently.  Fix
loop condition in the small constant case.  Rewrite in the general
case to be immune to wraparounds.  Make sure the address of probes
is valid.  Try to use [base + disp] addressing mode if possible.
* ira.c (setup_eliminable_regset): Set frame_pointer_needed if stack
checking is enabled and STACK_CHECK_MOVING_SP.
* rtlanal.c (may_trap_p_1) <MEM>: If stack checking is enabled,
return 1 for volatile references to the stack pointer.
* tree.c (build_common_builtin_nodes): Do not set ECF_NOTHROW on
__builtin_alloca if stack checking is enabled.
* unwind-dw2.c (uw_identify_context): Take into account whether the
context is that of a signal frame or not.
* config/i386/linux.h (STACK_CHECK_MOVING_SP): Define to 1.
* config/i386/linux64.h (STACK_CHECK_MOVING_SP): Likewise.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@153877 138bc75d-0d04-0410-961f-82ee72b054a4

15 files changed:
gcc/ChangeLog
gcc/config/i386/linux.h
gcc/config/i386/linux64.h
gcc/doc/md.texi
gcc/doc/tm.texi
gcc/explow.c
gcc/expr.h
gcc/ira.c
gcc/rtlanal.c
gcc/system.h
gcc/testsuite/ChangeLog
gcc/testsuite/gnat.dg/stack_check1.adb [new file with mode: 0644]
gcc/testsuite/gnat.dg/stack_check2.adb [new file with mode: 0644]
gcc/tree.c
gcc/unwind-dw2.c

index 0da48a4..ce89099 100644 (file)
@@ -1,3 +1,37 @@
+2009-11-03  Eric Botcazou  <ebotcazou@adacore.com>
+
+       PR target/10127
+       PR ada/20548
+       * expr.h (STACK_CHECK_PROBE_INTERVAL): Delete.
+       (STACK_CHECK_PROBE_INTERVAL_EXP): New macro.
+       (STACK_CHECK_MOVING_SP): Likewise.
+       * system.h (STACK_CHECK_PROBE_INTERVAL): Poison it.
+       * doc/tm.texi (Stack Checking): Delete STACK_CHECK_PROBE_INTERVAL.
+       Document STACK_CHECK_PROBE_INTERVAL_EXP and STACK_CHECK_MOVING_SP.
+       * doc/md.texi (Standard Pattern Names): Tweak entry of CHECK_STACK.
+       Document PROBE_STACK.
+       * explow.c (anti_adjust_stack_and_probe): New function.
+       (allocate_dynamic_stack_space): Do not directly allocate space if
+       STACK_CHECK_MOVING_SP, instead invoke above function.
+       (emit_stack_probe): Handle probe_stack insn.
+       (PROBE_INTERVAL): New macro.
+       (STACK_GROW_OPTAB): Likewise.
+       (STACK_GROW_OFF): Likewise.
+       (probe_stack_range): Use Pmode and memory_address consistently.  Fix
+       loop condition in the small constant case.  Rewrite in the general
+       case to be immune to wraparounds.  Make sure the address of probes
+       is valid.  Try to use [base + disp] addressing mode if possible.
+       * ira.c (setup_eliminable_regset): Set frame_pointer_needed if stack
+       checking is enabled and STACK_CHECK_MOVING_SP.
+       * rtlanal.c (may_trap_p_1) <MEM>: If stack checking is enabled,
+       return 1 for volatile references to the stack pointer.
+       * tree.c (build_common_builtin_nodes): Do not set ECF_NOTHROW on
+       __builtin_alloca if stack checking is enabled.
+       * unwind-dw2.c (uw_identify_context): Take into account whether the
+       context is that of a signal frame or not.
+       * config/i386/linux.h (STACK_CHECK_MOVING_SP): Define to 1.
+       * config/i386/linux64.h (STACK_CHECK_MOVING_SP): Likewise.
+
 2009-11-03  Jakub Jelinek  <jakub@redhat.com>
 
        PR rtl-optimization/41917
index 5e2e013..5d8e5ad 100644 (file)
@@ -207,6 +207,9 @@ along with GCC; see the file COPYING3.  If not see
 
 #define MD_UNWIND_SUPPORT "config/i386/linux-unwind.h"
 
+/* The stack pointer needs to be moved while checking the stack.  */
+#define STACK_CHECK_MOVING_SP 1
+
 /* This macro may be overridden in i386/k*bsd-gnu.h.  */
 #define REG_NAME(reg) reg
 
index cfa3f49..d07547a 100644 (file)
@@ -110,6 +110,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 #define MD_UNWIND_SUPPORT "config/i386/linux-unwind.h"
 
+/* The stack pointer needs to be moved while checking the stack.  */
+#define STACK_CHECK_MOVING_SP 1
+
 /* This macro may be overridden in i386/k*bsd-gnu.h.  */
 #define REG_NAME(reg) reg
 
index dcfba92..2974dcf 100644 (file)
@@ -5037,11 +5037,19 @@ operations in addition to updating the stack pointer.
 @item @samp{check_stack}
 If stack checking cannot be done on your system by probing the stack with
 a load or store instruction (@pxref{Stack Checking}), define this pattern
-to perform the needed check and signaling an error if the stack
-has overflowed.  The single operand is the location in the stack furthest
-from the current stack pointer that you need to validate.  Normally,
-on machines where this pattern is needed, you would obtain the stack
-limit from a global or thread-specific variable or register.
+to perform the needed check and signal an error if the stack has overflowed.
+The single operand is the address in the stack furthest from the current
+stack pointer that you need to validate.  Normally, on machines where this
+pattern is needed, you would obtain the stack limit from a global or
+thread-specific variable or register.
+
+@cindex @code{probe_stack} instruction pattern
+@item @samp{probe_stack}
+If stack checking can be done on your system by probing the stack but doing
+it with a load or store instruction is not optimal (@pxref{Stack Checking}),
+define this pattern to do the probing differently and signal an error if
+the stack has overflowed.  The single operand is the memory location in the
+stack that needs to be probed.
 
 @cindex @code{nonlocal_goto} instruction pattern
 @item @samp{nonlocal_goto}
index 984bbd7..c69ef0c 100644 (file)
@@ -3556,11 +3556,12 @@ like to do static stack checking in some more efficient way than the generic
 approach.  The default value of this macro is zero.
 @end defmac
 
-@defmac STACK_CHECK_PROBE_INTERVAL
-An integer representing the interval at which GCC must generate stack
-probe instructions.  You will normally define this macro to be no larger
-than the size of the ``guard pages'' at the end of a stack area.  The
-default value of 4096 is suitable for most systems.
+@defmac STACK_CHECK_PROBE_INTERVAL_EXP
+An integer specifying the interval at which GCC must generate stack probe
+instructions, defined as 2 raised to this integer.  You will normally
+define this macro so that the interval be no larger than the size of
+the ``guard pages'' at the end of a stack area.  The default value
+of 12 (4096-byte interval) is suitable for most systems.
 @end defmac
 
 @defmac STACK_CHECK_PROBE_LOAD
@@ -3569,6 +3570,15 @@ as a load instruction and zero if GCC should use a store instruction.
 The default is zero, which is the most efficient choice on most systems.
 @end defmac
 
+@defmac STACK_CHECK_MOVING_SP
+An integer which is nonzero if GCC should move the stack pointer page by page
+when doing probes.  This can be necessary on systems where the stack pointer
+contains the bottom address of the memory area accessible to the executing
+thread at any point in time.  In this situation an alternate signal stack
+is required in order to be able to recover from a stack overflow.  The
+default value of this macro is zero.
+@end defmac
+
 @defmac STACK_CHECK_PROTECT
 The number of bytes of stack needed to recover from a stack overflow,
 for languages where such a recovery is supported.  The default value of
index c38682d..0bbbc00 100644 (file)
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 
 static rtx break_out_memory_refs (rtx);
 static void emit_stack_probe (rtx);
+static void anti_adjust_stack_and_probe (rtx);
 
 
 /* Truncate and perhaps sign-extend C as appropriate for MODE.  */
@@ -1233,9 +1234,11 @@ allocate_dynamic_stack_space (rtx size, rtx target, int known_align)
   gcc_assert (!(stack_pointer_delta
                % (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)));
 
-  /* If needed, check that we have the required amount of stack.
-     Take into account what has already been checked.  */
-  if (flag_stack_check == GENERIC_STACK_CHECK)
+  /* If needed, check that we have the required amount of stack.  Take into
+     account what has already been checked.  */
+  if (STACK_CHECK_MOVING_SP)
+    ;
+  else if (flag_stack_check == GENERIC_STACK_CHECK)
     probe_stack_range (STACK_OLD_CHECK_PROTECT + STACK_CHECK_MAX_FRAME_SIZE,
                       size);
   else if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK)
@@ -1304,7 +1307,10 @@ allocate_dynamic_stack_space (rtx size, rtx target, int known_align)
          emit_label (space_available);
        }
 
-      anti_adjust_stack (size);
+      if (flag_stack_check && STACK_CHECK_MOVING_SP)
+       anti_adjust_stack_and_probe (size);
+      else
+       anti_adjust_stack (size);
 
 #ifdef STACK_GROWS_DOWNWARD
       emit_move_insn (target, virtual_stack_dynamic_rtx);
@@ -1355,6 +1361,12 @@ emit_stack_probe (rtx address)
 
   MEM_VOLATILE_P (memref) = 1;
 
+  /* See if we have an insn to probe the stack.  */
+#ifdef HAVE_probe_stack
+  if (HAVE_probe_stack)
+    emit_insn (gen_probe_stack (memref));
+  else
+#endif
   if (STACK_CHECK_PROBE_LOAD)
     emit_move_insn (gen_reg_rtx (word_mode), memref);
   else
@@ -1362,15 +1374,20 @@ emit_stack_probe (rtx address)
 }
 
 /* Probe a range of stack addresses from FIRST to FIRST+SIZE, inclusive.
-   FIRST is a constant and size is a Pmode RTX.  These are offsets from the
-   current stack pointer.  STACK_GROWS_DOWNWARD says whether to add or
-   subtract from the stack.  If SIZE is constant, this is done
-   with a fixed number of probes.  Otherwise, we must make a loop.  */
+   FIRST is a constant and size is a Pmode RTX.  These are offsets from
+   the current stack pointer.  STACK_GROWS_DOWNWARD says whether to add
+   or subtract them from the stack pointer.  */
+
+#define PROBE_INTERVAL (1 << STACK_CHECK_PROBE_INTERVAL_EXP)
 
 #ifdef STACK_GROWS_DOWNWARD
 #define STACK_GROW_OP MINUS
+#define STACK_GROW_OPTAB sub_optab
+#define STACK_GROW_OFF(off) -(off)
 #else
 #define STACK_GROW_OP PLUS
+#define STACK_GROW_OPTAB add_optab
+#define STACK_GROW_OFF(off) (off)
 #endif
 
 void
@@ -1380,113 +1397,272 @@ probe_stack_range (HOST_WIDE_INT first, rtx size)
   if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode)
     size = convert_to_mode (Pmode, size, 1);
 
-  /* Next see if the front end has set up a function for us to call to
-     check the stack.  */
-  if (stack_check_libfunc != 0)
+  /* Next see if we have a function to check the stack.  */
+  if (stack_check_libfunc)
     {
-      rtx addr = memory_address (QImode,
+      rtx addr = memory_address (Pmode,
                                 gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
                                                 stack_pointer_rtx,
                                                 plus_constant (size, first)));
-
-      addr = convert_memory_address (ptr_mode, addr);
-      emit_library_call (stack_check_libfunc, LCT_NORMAL, VOIDmode, 1, addr,
-                        ptr_mode);
+      emit_library_call (stack_check_libfunc, LCT_NORMAL, VOIDmode, 1, addr);
     }
 
-  /* Next see if we have an insn to check the stack.  Use it if so.  */
+  /* Next see if we have an insn to check the stack.  */
 #ifdef HAVE_check_stack
   else if (HAVE_check_stack)
     {
-      insn_operand_predicate_fn pred;
-      rtx last_addr
-       = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
-                                        stack_pointer_rtx,
-                                        plus_constant (size, first)),
-                        NULL_RTX);
-
-      pred = insn_data[(int) CODE_FOR_check_stack].operand[0].predicate;
-      if (pred && ! ((*pred) (last_addr, Pmode)))
-       last_addr = copy_to_mode_reg (Pmode, last_addr);
+      rtx addr = memory_address (Pmode,
+                                gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
+                                                stack_pointer_rtx,
+                                                plus_constant (size, first)));
+      insn_operand_predicate_fn pred
+       = insn_data[(int) CODE_FOR_check_stack].operand[0].predicate;
+      if (pred && !((*pred) (addr, Pmode)))
+       addr = copy_to_mode_reg (Pmode, addr);
 
-      emit_insn (gen_check_stack (last_addr));
+      emit_insn (gen_check_stack (addr));
     }
 #endif
 
-  /* If we have to generate explicit probes, see if we have a constant
-     small number of them to generate.  If so, that's the easy case.  */
-  else if (CONST_INT_P (size)
-          && INTVAL (size) < 10 * STACK_CHECK_PROBE_INTERVAL)
+  /* Otherwise we have to generate explicit probes.  If we have a constant
+     small number of them to generate, that's the easy case.  */
+  else if (CONST_INT_P (size) && INTVAL (size) < 7 * PROBE_INTERVAL)
     {
-      HOST_WIDE_INT offset;
-
-      /* Start probing at FIRST + N * STACK_CHECK_PROBE_INTERVAL
-        for values of N from 1 until it exceeds LAST.  If only one
-        probe is needed, this will not generate any code.  Then probe
-        at LAST.  */
-      for (offset = first + STACK_CHECK_PROBE_INTERVAL;
-          offset < INTVAL (size);
-          offset = offset + STACK_CHECK_PROBE_INTERVAL)
-       emit_stack_probe (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
-                                         stack_pointer_rtx,
-                                         GEN_INT (offset)));
-
-      emit_stack_probe (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
-                                       stack_pointer_rtx,
-                                       plus_constant (size, first)));
+      HOST_WIDE_INT isize = INTVAL (size), i;
+      rtx addr;
+
+      /* Probe at FIRST + N * PROBE_INTERVAL for values of N from 1 until
+        it exceeds SIZE.  If only one probe is needed, this will not
+        generate any code.  Then probe at FIRST + SIZE.  */
+      for (i = PROBE_INTERVAL; i < isize; i += PROBE_INTERVAL)
+       {
+         addr = memory_address (Pmode,
+                                plus_constant (stack_pointer_rtx,
+                                               STACK_GROW_OFF (first + i)));
+         emit_stack_probe (addr);
+       }
+
+      addr = memory_address (Pmode,
+                            plus_constant (stack_pointer_rtx,
+                                           STACK_GROW_OFF (first + isize)));
+      emit_stack_probe (addr);
     }
 
-  /* In the variable case, do the same as above, but in a loop.  We emit loop
-     notes so that loop optimization can be done.  */
+  /* In the variable case, do the same as above, but in a loop.  Note that we
+     must be extra careful with variables wrapping around because we might be
+     at the very top (or the very bottom) of the address space and we have to
+     be able to handle this case properly; in particular, we use an equality
+     test for the loop condition.  */
   else
     {
-      rtx test_addr
-       = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
-                                        stack_pointer_rtx,
-                                        GEN_INT (first + STACK_CHECK_PROBE_INTERVAL)),
-                        NULL_RTX);
-      rtx last_addr
-       = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
-                                        stack_pointer_rtx,
-                                        plus_constant (size, first)),
-                        NULL_RTX);
-      rtx incr = GEN_INT (STACK_CHECK_PROBE_INTERVAL);
+      rtx rounded_size, rounded_size_op, test_addr, last_addr, temp;
       rtx loop_lab = gen_label_rtx ();
-      rtx test_lab = gen_label_rtx ();
       rtx end_lab = gen_label_rtx ();
-      rtx temp;
 
-      if (!REG_P (test_addr)
-         || REGNO (test_addr) < FIRST_PSEUDO_REGISTER)
-       test_addr = force_reg (Pmode, test_addr);
 
-      emit_jump (test_lab);
+      /* Step 1: round SIZE to the previous multiple of the interval.  */
+
+      /* ROUNDED_SIZE = SIZE & -PROBE_INTERVAL  */
+      rounded_size
+       = simplify_gen_binary (AND, Pmode, size, GEN_INT (-PROBE_INTERVAL));
+      rounded_size_op = force_operand (rounded_size, NULL_RTX);
+
+
+      /* Step 2: compute initial and final value of the loop counter.  */
+
+      /* TEST_ADDR = SP + FIRST.  */
+      test_addr = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
+                                                stack_pointer_rtx,
+                                                GEN_INT (first)), NULL_RTX);
+
+      /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE.  */
+      last_addr = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
+                                                test_addr,
+                                                rounded_size_op), NULL_RTX);
+
+
+      /* Step 3: the loop
+
+        while (TEST_ADDR != LAST_ADDR)
+          {
+            TEST_ADDR = TEST_ADDR + PROBE_INTERVAL
+            probe at TEST_ADDR
+          }
+
+        probes at FIRST + N * PROBE_INTERVAL for values of N from 1
+        until it is equal to ROUNDED_SIZE.  */
 
       emit_label (loop_lab);
-      emit_stack_probe (test_addr);
 
-#ifdef STACK_GROWS_DOWNWARD
-#define CMP_OPCODE GTU
-      temp = expand_binop (Pmode, sub_optab, test_addr, incr, test_addr,
-                          1, OPTAB_WIDEN);
-#else
-#define CMP_OPCODE LTU
-      temp = expand_binop (Pmode, add_optab, test_addr, incr, test_addr,
+      /* Jump to END_LAB if TEST_ADDR == LAST_ADDR.  */
+      emit_cmp_and_jump_insns (test_addr, last_addr, EQ, NULL_RTX, Pmode, 1,
+                              end_lab);
+
+      /* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL.  */
+      temp = expand_binop (Pmode, STACK_GROW_OPTAB, test_addr,
+                          GEN_INT (PROBE_INTERVAL), test_addr,
                           1, OPTAB_WIDEN);
-#endif
 
       gcc_assert (temp == test_addr);
 
-      emit_label (test_lab);
-      emit_cmp_and_jump_insns (test_addr, last_addr, CMP_OPCODE,
-                              NULL_RTX, Pmode, 1, loop_lab);
-      emit_jump (end_lab);
+      /* Probe at TEST_ADDR.  */
+      emit_stack_probe (test_addr);
+
+      emit_jump (loop_lab);
+
       emit_label (end_lab);
 
-      emit_stack_probe (last_addr);
+
+      /* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time
+        that SIZE is equal to ROUNDED_SIZE.  */
+
+      /* TEMP = SIZE - ROUNDED_SIZE.  */
+      temp = simplify_gen_binary (MINUS, Pmode, size, rounded_size);
+      if (temp != const0_rtx)
+       {
+         rtx addr;
+
+         if (GET_CODE (temp) == CONST_INT)
+           {
+             /* Use [base + disp} addressing mode if supported.  */
+             HOST_WIDE_INT offset = INTVAL (temp);
+             addr = memory_address (Pmode,
+                                    plus_constant (last_addr,
+                                                   STACK_GROW_OFF (offset)));
+           }
+         else
+           {
+             /* Manual CSE if the difference is not known at compile-time.  */
+             temp = gen_rtx_MINUS (Pmode, size, rounded_size_op);
+             addr = memory_address (Pmode,
+                                    gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
+                                                    last_addr, temp));
+           }
+
+         emit_stack_probe (addr);
+       }
     }
 }
-\f
+
+/* Adjust the stack by SIZE bytes while probing it.  Note that we skip the
+   probe for the first interval + a small dope of 4 words and instead probe
+   that many bytes past the specified size to maintain a protection area.  */
+
+static void
+anti_adjust_stack_and_probe (rtx size)
+{
+  const int dope = 4 * UNITS_PER_WORD;
+
+  /* First ensure SIZE is Pmode.  */
+  if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode)
+    size = convert_to_mode (Pmode, size, 1);
+
+  /* If we have a constant small number of probes to generate, that's the
+     easy case.  */
+  if (GET_CODE (size) == CONST_INT && INTVAL (size) < 7 * PROBE_INTERVAL)
+    {
+      HOST_WIDE_INT isize = INTVAL (size), i;
+      bool first_probe = true;
+
+      /* Adjust SP and probe to PROBE_INTERVAL + N * PROBE_INTERVAL for
+        values of N from 1 until it exceeds SIZE.  If only one probe is
+        needed, this will not generate any code.  Then adjust and probe
+        to PROBE_INTERVAL + SIZE.  */
+      for (i = PROBE_INTERVAL; i < isize; i += PROBE_INTERVAL)
+       {
+         if (first_probe)
+           {
+             anti_adjust_stack (GEN_INT (2 * PROBE_INTERVAL + dope));
+             first_probe = false;
+           }
+         else
+           anti_adjust_stack (GEN_INT (PROBE_INTERVAL));
+         emit_stack_probe (stack_pointer_rtx);
+       }
+
+      if (first_probe)
+       anti_adjust_stack (plus_constant (size, PROBE_INTERVAL + dope));
+      else
+       anti_adjust_stack (plus_constant (size, PROBE_INTERVAL - i));
+      emit_stack_probe (stack_pointer_rtx);
+    }
+
+  /* In the variable case, do the same as above, but in a loop.  Note that we
+     must be extra careful with variables wrapping around because we might be
+     at the very top (or the very bottom) of the address space and we have to
+     be able to handle this case properly; in particular, we use an equality
+     test for the loop condition.  */
+  else
+    {
+      rtx rounded_size, rounded_size_op, last_addr, temp;
+      rtx loop_lab = gen_label_rtx ();
+      rtx end_lab = gen_label_rtx ();
+
+
+      /* Step 1: round SIZE to the previous multiple of the interval.  */
+
+      /* ROUNDED_SIZE = SIZE & -PROBE_INTERVAL  */
+      rounded_size
+       = simplify_gen_binary (AND, Pmode, size, GEN_INT (-PROBE_INTERVAL));
+      rounded_size_op = force_operand (rounded_size, NULL_RTX);
+
+
+      /* Step 2: compute initial and final value of the loop counter.  */
+
+      /* SP = SP_0 + PROBE_INTERVAL.  */
+      anti_adjust_stack (GEN_INT (PROBE_INTERVAL + dope));
+
+      /* LAST_ADDR = SP_0 + PROBE_INTERVAL + ROUNDED_SIZE.  */
+      last_addr = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
+                                                stack_pointer_rtx,
+                                                rounded_size_op), NULL_RTX);
+
+
+      /* Step 3: the loop
+
+         while (SP != LAST_ADDR)
+           {
+             SP = SP + PROBE_INTERVAL
+             probe at SP
+           }
+
+        adjusts SP and probes to PROBE_INTERVAL + N * PROBE_INTERVAL for
+        values of N from 1 until it is equal to ROUNDED_SIZE.  */
+
+      emit_label (loop_lab);
+
+      /* Jump to END_LAB if SP == LAST_ADDR.  */
+      emit_cmp_and_jump_insns (stack_pointer_rtx, last_addr, EQ, NULL_RTX,
+                              Pmode, 1, end_lab);
+
+      /* SP = SP + PROBE_INTERVAL and probe at SP.  */
+      anti_adjust_stack (GEN_INT (PROBE_INTERVAL));
+      emit_stack_probe (stack_pointer_rtx);
+
+      emit_jump (loop_lab);
+
+      emit_label (end_lab);
+
+
+      /* Step 4: adjust SP and probe to PROBE_INTERVAL + SIZE if we cannot
+        assert at compile-time that SIZE is equal to ROUNDED_SIZE.  */
+
+      /* TEMP = SIZE - ROUNDED_SIZE.  */
+      temp = simplify_gen_binary (MINUS, Pmode, size, rounded_size);
+      if (temp != const0_rtx)
+       {
+         /* Manual CSE if the difference is not known at compile-time.  */
+         if (GET_CODE (temp) != CONST_INT)
+           temp = gen_rtx_MINUS (Pmode, size, rounded_size_op);
+         anti_adjust_stack (temp);
+         emit_stack_probe (stack_pointer_rtx);
+       }
+    }
+
+  /* Adjust back to account for the additional first interval.  */
+  adjust_stack (GEN_INT (PROBE_INTERVAL + dope));
+}
+
 /* Return an rtx representing the register or memory location
    in which a scalar value of data type VALTYPE
    was returned by a function call to function FUNC.
index 0eceb6e..e847796 100644 (file)
@@ -218,9 +218,9 @@ do {                                                                \
 #define STACK_CHECK_STATIC_BUILTIN 0
 #endif
 
-/* The default interval is one page.  */
-#ifndef STACK_CHECK_PROBE_INTERVAL
-#define STACK_CHECK_PROBE_INTERVAL 4096
+/* The default interval is one page (4096 bytes).  */
+#ifndef STACK_CHECK_PROBE_INTERVAL_EXP
+#define STACK_CHECK_PROBE_INTERVAL_EXP 12
 #endif
 
 /* The default is to do a store into the stack.  */
@@ -228,6 +228,11 @@ do {                                                               \
 #define STACK_CHECK_PROBE_LOAD 0
 #endif
 
+/* The default is not to move the stack pointer.  */
+#ifndef STACK_CHECK_MOVING_SP
+#define STACK_CHECK_MOVING_SP 0
+#endif
+
 /* This is a kludge to try to capture the discrepancy between the old
    mechanism (generic stack checking) and the new mechanism (static
    builtin stack checking).  STACK_CHECK_PROTECT needs to be bumped
@@ -252,7 +257,7 @@ do {                                                                \
    one probe per function.  */
 #ifndef STACK_CHECK_MAX_FRAME_SIZE
 #define STACK_CHECK_MAX_FRAME_SIZE \
-  (STACK_CHECK_PROBE_INTERVAL - UNITS_PER_WORD)
+  ((1 << STACK_CHECK_PROBE_INTERVAL_EXP) - UNITS_PER_WORD)
 #endif
 
 /* This is arbitrary, but should be large enough everywhere.  */
@@ -779,10 +784,9 @@ extern void update_nonlocal_goto_save_area (void);
 extern rtx allocate_dynamic_stack_space (rtx, rtx, int);
 
 /* Probe a range of stack addresses from FIRST to FIRST+SIZE, inclusive.
-   FIRST is a constant and size is a Pmode RTX.  These are offsets from the
-   current stack pointer.  STACK_GROWS_DOWNWARD says whether to add or
-   subtract from the stack.  If SIZE is constant, this is done
-   with a fixed number of probes.  Otherwise, we must make a loop.  */
+   FIRST is a constant and size is a Pmode RTX.  These are offsets from
+   the current stack pointer.  STACK_GROWS_DOWNWARD says whether to add
+   or subtract them from the stack pointer.  */
 extern void probe_stack_range (HOST_WIDE_INT, rtx);
 
 /* Return an rtx that refers to the value returned by a library call
index 962d099..a3e899f 100644 (file)
--- a/gcc/ira.c
+++ b/gcc/ira.c
@@ -1442,6 +1442,9 @@ ira_setup_eliminable_regset (void)
   int need_fp
     = (! flag_omit_frame_pointer
        || (cfun->calls_alloca && EXIT_IGNORE_STACK)
+       /* We need the frame pointer to catch stack overflow exceptions
+         if the stack pointer is moving.  */
+       || (flag_stack_check && STACK_CHECK_MOVING_SP)
        || crtl->accesses_prior_frames
        || crtl->stack_realign_needed
        || targetm.frame_pointer_required ());
index 120d37a..ab88f23 100644 (file)
@@ -2252,6 +2252,11 @@ may_trap_p_1 (const_rtx x, unsigned flags)
 
       /* Memory ref can trap unless it's a static var or a stack slot.  */
     case MEM:
+      /* Recognize specific pattern of stack checking probes.  */
+      if (flag_stack_check
+         && MEM_VOLATILE_P (x)
+         && XEXP (x, 0) == stack_pointer_rtx)
+       return 1;
       if (/* MEM_NOTRAP_P only relates to the actual position of the memory
             reference; moving it out of context such as when moving code
             when optimizing, might cause its address to become invalid.  */
index 0c846cf..03910d0 100644 (file)
@@ -761,7 +761,7 @@ extern void fancy_abort (const char *, int, const char *) ATTRIBUTE_NORETURN;
        TARGET_ASM_EXCEPTION_SECTION TARGET_ASM_EH_FRAME_SECTION           \
        SMALL_ARG_MAX ASM_OUTPUT_SHARED_BSS ASM_OUTPUT_SHARED_COMMON       \
        ASM_OUTPUT_SHARED_LOCAL UNALIGNED_WORD_ASM_OP                      \
-       ASM_MAKE_LABEL_LINKONCE
+       ASM_MAKE_LABEL_LINKONCE STACK_CHECK_PROBE_INTERVAL
 
 /* Hooks that are no longer used.  */
  #pragma GCC poison LANG_HOOKS_FUNCTION_MARK LANG_HOOKS_FUNCTION_FREE  \
index 220a4c3..4a499e2 100644 (file)
@@ -1,3 +1,8 @@
+2009-11-03  Eric Botcazou  <ebotcazou@adacore.com>
+
+       * gnat.dg/stack_check.adb1: New test.
+       * gnat.dg/stack_check.adb2: Likewise.
+
 2009-11-03  Jakub Jelinek  <jakub@redhat.com>
 
        PR rtl-optimization/41917
diff --git a/gcc/testsuite/gnat.dg/stack_check1.adb b/gcc/testsuite/gnat.dg/stack_check1.adb
new file mode 100644 (file)
index 0000000..51ee1a6
--- /dev/null
@@ -0,0 +1,38 @@
+-- { dg-do run }
+-- { dg-options "-fstack-check" }
+
+-- This test requires architecture- and OS-specific support code for unwinding
+-- through signal frames (typically located in *-unwind.h) to pass.  Feel free
+-- to disable it if this code hasn't been implemented yet.
+
+procedure Stack_Check1 is
+
+  type A is Array (1..2048) of Integer;
+
+  procedure Consume_Stack (N : Integer) is
+    My_A : A; -- 8 KB static
+  begin
+    My_A (1) := 0;
+    if N <= 0 then
+      return;
+    end if;
+    Consume_Stack (N-1);
+  end;
+
+  Task T;
+
+  Task body T is
+  begin
+    begin
+      Consume_Stack (Integer'Last);
+      raise Program_Error;
+    exception
+      when Storage_Error => null;
+    end;
+
+    Consume_Stack (128);
+  end;
+
+begin
+  null;
+end;
diff --git a/gcc/testsuite/gnat.dg/stack_check2.adb b/gcc/testsuite/gnat.dg/stack_check2.adb
new file mode 100644 (file)
index 0000000..4a3008b
--- /dev/null
@@ -0,0 +1,43 @@
+-- { dg-do run }
+-- { dg-options "-fstack-check" }
+
+-- This test requires architecture- and OS-specific support code for unwinding
+-- through signal frames (typically located in *-unwind.h) to pass.  Feel free
+-- to disable it if this code hasn't been implemented yet.
+
+procedure Stack_Check2 is
+
+  function UB return Integer is
+  begin
+    return 2048;
+  end;
+
+  type A is Array (Positive range <>) of Integer;
+
+  procedure Consume_Stack (N : Integer) is
+    My_A : A (1..UB); -- 8 KB dynamic
+  begin
+    My_A (1) := 0;
+    if N <= 0 then
+      return;
+    end if;
+    Consume_Stack (N-1);
+  end;
+
+  Task T;
+
+  Task body T is
+  begin
+    begin
+      Consume_Stack (Integer'Last);
+      raise Program_Error;
+    exception
+      when Storage_Error => null;
+    end;
+
+    Consume_Stack (128);
+  end;
+
+begin
+  null;
+end;
index 6079725..f3970dd 100644 (file)
@@ -9009,7 +9009,8 @@ build_common_builtin_nodes (void)
       tmp = tree_cons (NULL_TREE, size_type_node, void_list_node);
       ftype = build_function_type (ptr_type_node, tmp);
       local_define_builtin ("__builtin_alloca", ftype, BUILT_IN_ALLOCA,
-                           "alloca", ECF_NOTHROW | ECF_MALLOC);
+                           "alloca",
+                           ECF_MALLOC | (flag_stack_check ? 0 : ECF_NOTHROW));
     }
 
   tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
index 82958b0..2208f17 100644 (file)
@@ -1559,7 +1559,13 @@ uw_install_context_1 (struct _Unwind_Context *current,
 static inline _Unwind_Ptr
 uw_identify_context (struct _Unwind_Context *context)
 {
-  return _Unwind_GetCFA (context);
+  /* The CFA is not sufficient to disambiguate the context of a function
+     interrupted by a signal before establishing its frame and the context
+     of the signal itself.  */
+  if (STACK_GROWS_DOWNWARD)
+    return _Unwind_GetCFA (context) - _Unwind_IsSignalFrame (context);
+  else
+    return _Unwind_GetCFA (context) + _Unwind_IsSignalFrame (context);
 }