This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] New -fstack-check implementation (1/n)


Hi,

-fstack-check is broken in the 4.x series of compilers in the sense that you 
cannot recover from a stack overflow condition (for example in Ada).  This is 
a regression from the 3.x series although there were bugs in that series too.

Moreover, the original implementation of -fstack-check comes with several 
drawbacks:
- Modified allocation strategy for large objects: they will always be
allocated dynamically if their size exceeds a fixed threshold.
- Fixed limit on the size of the static frame of functions: when it is
topped by a particular function, stack checking is not reliable and
a warning is issued by the compiler.
- Inefficiency: because of both the modified allocation strategy and the
generic implementation, the performances of the code are unnecessarily 
hampered.

We are proposing a new implementation of -fstack-check that eliminates the 
aforementioned drawback.  It is based on partial support code in the 
architecture back-ends, modelled on what the Alpha back-end already features 
because of the requirement of stack checking by the Tru64 ABI.  We have 
implemented it on Alpha/Tru64, x86/Linux, x86/Solaris, x86/FreeBSD, 
x86/Windows, x86-64/Linux, MIPS/IRIX, SPARC/Solaris, PA/HP-UX, PowerPC/AIX, 
and IA-64/Linux in a 3.4.x based compiler and are in the process of porting 
it to 4.1.x.

The new mechanism doesn't eliminate the old one, the option -fold-stack-check 
even makes it possible to select the latter when the former is available.  On 
architectures without specific support code, -fstack-check is equivalent to 
-fold-stack-check.

This first patch contains the generic implementation of the new mechanism and 
also repairs the old one, so the 3 ACATS tests that require stack checking 
(c52103x c52104x c52104y) will pass even with the old mechanism on platforms 
that are capable of unwinding through signal handlers.  We'll also submit a 
few new *-unwind.h files for non-Linux platforms.

Bootstrapped/regtested on x86_64-suse-linux, OK for mainline?


2006-03-20  Eric Botcazou  <ebotcazou@adacore.com>

	PR ada/20548
	* common.opt (-fstack-check): Do not declare the variable here.
	(-fold-stack-check): New option.
	* doc/invoke.texi (Code Gen Options): Document it.
	* expr.h (STACK_CHECK_PROBE_INTERVAL): Delete.
	(STACK_CHECK_PROBE_INTERVAL_EXP): New macro.
	(STACK_CHECK_MAX_FRAME_SIZE): Adjust for above change.
	(STACK_CHECK_STATIC_BUILTIN): New macro.
	(STACK_OLD_CHECK_PROTECT): Likewise.
	(STACK_CHECK_PROTECT): Bump to 3 pages if DWARF-2 EH is used.
	* system.h (STACK_CHECK_PROBE_INTERVAL): Poison it.
	* doc/tm.texi (Stack Checking): Delete STACK_CHECK_PROBE_INTERVAL
	entry.  Add new entries for STACK_CHECK_PROBE_INTERVAL_EXP and
	STACK_CHECK_STATIC_BUILTIN.
	* opts.c: Include expr.h.
	(common_handle_option) <OPT_fold_stack_check>: New case.
	<OPT_fstack_check>: Likewise.
	* calls.c (emit_library_call_value_1): Clear the ECF_NOTHROW flag if
	the libcall is LCT_MAY_THROW.
	(initialize_argument_information): Use TYPE_SIZE_UNIT consistently
	in the test for variable-sized types.  Adjust for new behaviour of
	flag_stack_check.
	* function.c (gimplify_parameters): Use DECL_SIZE_UNIT in the test
	for variable-sized parameters.  Treat all parameters whose size is
	greater than STACK_CHECK_MAX_VAR_SIZE as variable-sized if generic
	stack checking is enabled.
	(expand_function_end): Adjust for new behaviour of flag_stack_check.
	* reload1.c (reload): Likewise.
	* stmt.c (expand_decl): Assert that all automatic variables have
	fixed size at this point and remove dead code.
	* gimplify.c (gimplify_decl_expr): Use more stringent test on
	DECL_SIZE_UNIT to detect variable-sized objects.  Treat all
	objects whose size is greater than STACK_CHECK_MAX_VAR_SIZE
	as variable-sized if generic stack checking is enabled.
	* explow.c: Include except.h.
	(allocate_dynamic_stack_space): Do not take into account
	STACK_CHECK_MAX_FRAME_SIZE for static builtin stack checking.
	(set_stack_check_libfunc): Delete.
	(stack_check_libfunc): Make public.
	(PROBE_INTERVAL): New macro.
	(STACK_GROW_OPTAB): Likewise.
	(probe_stack_range): Cope with SPARC_STACK_BIAS.  Pass LCT_MAY_THROW
	to emit_library_call for the checking routine.  Remove support code
	for MD pattern.  Fix loop condition in the small constant case.
	Rewrite in the general case to be immune to wrap-around.
	Do not include gt-explow.h.
	* Makefile.in (explow.o): Remove gt-explow.h.
	(list of gt files): Likewise.
	* rtl.h (set_stack_check_libfunc): Delete.
	(stack_check_libfunc): Declare.
	(libcall_type enum): Add LCT_MAY_THROW.
	* flags.h (stack_check_type): New enumeration type.
	(flag_stack_check): Change type to above.
	* toplev.c (flag_stack_check): Likewise.
	* tree.c (build_common_builtin_nodes): Do not set ECF_NOTHROW on
	__builtin_alloca if stack checking is enabled.

ada/

	* decl.c (gnat_to_gnu_entity): Use DECL_SIZE_UNIT consistently in the
	setjmp test.  Adjust for new behaviour of flag_stack_check.
	* trans.c (gigi): For targets that do not use probes, initialize
	stack_check_libfunc here...
	(gnat_init_stmt_group): ...and not there.
	* utils2.c (build_call_alloc_dealloc): Remove redundant test of
 	flag_stack_check.  Adjust for new behaviour of flag_stack_check.

testsuite/

	* ada/acats/norun.lst: Remove c52103x, c52104x and c52104y.


-- 
Eric Botcazou
Index: doc/tm.texi
===================================================================
--- doc/tm.texi	(revision 112130)
+++ doc/tm.texi	(working copy)
@@ -3202,45 +3202,57 @@ linkage is necessary.  The default is @c
 @node Stack Checking
 @subsection Specifying How Stack Checking is Done
 
-GCC will check that stack references are within the boundaries of
-the stack, if the @option{-fstack-check} is specified, in one of three ways:
+GCC will check that stack references are within the boundaries of the
+stack, if the option @option{-fstack-check} is specified, in one of
+three ways:
 
 @enumerate
 @item
 If the value of the @code{STACK_CHECK_BUILTIN} macro is nonzero, GCC
-will assume that you have arranged for stack checking to be done at
-appropriate places in the configuration files, e.g., in
-@code{TARGET_ASM_FUNCTION_PROLOGUE}.  GCC will do not other special
-processing.
+will assume that you have arranged for full stack checking to be done
+at appropriate places in the configuration files.  GCC will not do
+other special processing.
 
 @item
-If @code{STACK_CHECK_BUILTIN} is zero and you defined a named pattern
-called @code{check_stack} in your @file{md} file, GCC will call that
-pattern with one argument which is the address to compare the stack
-value against.  You must arrange for this pattern to report an error if
-the stack pointer is out of range.
+If @code{STACK_CHECK_BUILTIN} is zero and the value of the
+@code{STACK_CHECK_STATIC_BUILTIN} macro is nonzero, GCC will assume
+that you have arranged for static stack checking (checking of the
+static stack frame of functions) to be done at appropriate places
+in the configuration files.  GCC will only emit code to do dynamic
+stack checking (checking on dynamic stack allocations) using the third
+approach below.
 
 @item
 If neither of the above are true, GCC will generate code to periodically
 ``probe'' the stack pointer using the values of the macros defined below.
 @end enumerate
 
-Normally, you will use the default values of these macros, so GCC
-will use the third approach.
+If neither STACK_CHECK_BUILTIN nor STACK_CHECK_STATIC_BUILTIN is defined,
+GCC will change its allocation strategy for large objects if the option
+@option{-fstack-check} is specified: they will always be allocated
+dynamically if their size exceeds @code{STACK_CHECK_MAX_VAR_SIZE} bytes.
 
 @defmac STACK_CHECK_BUILTIN
 A nonzero value if stack checking is done by the configuration files in a
 machine-dependent manner.  You should define this macro if stack checking
-is require by the ABI of your machine or if you would like to have to stack
-checking in some more efficient way than GCC's portable 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.
+is require by the ABI of your machine or if you would like to do stack
+checking in some more efficient way than the generic approach.  The default
+value of this macro is zero.
+@end defmac
+
+@defmac STACK_CHECK_STATIC_BUILTIN
+A nonzero value if static stack checking is done by the configuration files
+in a machine-dependent manner.  You should define this macro if you would
+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_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
@@ -3255,6 +3267,10 @@ for languages where such a recovery is s
 75 words should be adequate for most machines.
 @end defmac
 
+The following macros are relevant only if neither STACK_CHECK_BUILTIN
+nor STACK_CHECK_STATIC_BUILTIN is defined; you can omit them altogether
+in the opposite case.
+
 @defmac STACK_CHECK_MAX_FRAME_SIZE
 The maximum size of a stack frame, in bytes.  GCC will generate probe
 instructions in non-leaf functions to ensure at least this many bytes of
Index: doc/invoke.texi
===================================================================
--- doc/invoke.texi	(revision 112130)
+++ doc/invoke.texi	(working copy)
@@ -770,7 +770,8 @@ See S/390 and zSeries Options.
 -fno-jump-tables @gol
 -freg-struct-return  -fshort-enums @gol
 -fshort-double  -fshort-wchar @gol
--fverbose-asm  -fpack-struct[=@var{n}]  -fstack-check @gol
+-fverbose-asm  -fpack-struct[=@var{n}] @gol
+-fstack-check  -fold-stack-check @gol
 -fstack-limit-register=@var{reg}  -fstack-limit-symbol=@var{sym} @gol
 -fargument-alias  -fargument-noalias @gol
 -fargument-noalias-global  -fleading-underscore @gol
@@ -13306,8 +13307,33 @@ a single-threaded environment since stac
 detected on nearly all systems if there is only one stack.
 
 Note that this switch does not actually cause checking to be done; the
-operating system must do that.  The switch causes generation of code
-to ensure that the operating system sees the stack being extended.
+operating system or the language runtime must do that.  The switch causes
+generation of code to ensure that they see the stack being extended.
+
+@item -fold-stack-check
+@opindex fold-stack-check
+Same as above, but forces the use of old-style, pre-GCC4 stack checking.
+Old-style stack checking is a generic mechanism that requires no specific
+target support in the compiler but comes with the following drawbacks:
+
+@enumerate
+@item
+Modified allocation strategy for large objects: they will always be
+allocated dynamically if their size exceeds a fixed threshold.
+
+@item
+Fixed limit on the size of the static frame of functions: when it is
+topped by a particular function, stack checking is not reliable and
+a warning is issued by the compiler.
+
+@item
+Inefficiency: because of both the modified allocation strategy and the
+generic implementation, the performances of the code are hampered.
+@end enumerate
+
+Note that old-style stack checking is also the fallback method for
+@option{-fstack-check} if no specific target support has been added
+in the compiler.
 
 @item -fstack-limit-register=@var{reg}
 @itemx -fstack-limit-symbol=@var{sym}
Index: flags.h
===================================================================
--- flags.h	(revision 112130)
+++ flags.h	(working copy)
@@ -250,6 +250,27 @@ extern int flag_var_tracking;
    warning message in case flag was set by -fprofile-{generate,use}.  */
 extern bool flag_speculative_prefetching_set;
 
+/* Type of stack check.  */
+enum stack_check_type
+{
+  /* Do not check the stack.  */
+  NO_STACK_CHECK = 0,
+
+  /* Check the stack generically, i.e. assume no specific support
+     from the target configuration files.  */
+  GENERIC_STACK_CHECK,
+
+  /* Check the stack and rely on the target configuration files to
+     check the static frame of functions, i.e. use the generic
+     mechanism only for dynamic stack allocations.  */
+  STATIC_BUILTIN_STACK_CHECK,
+
+  /* Check the stack and entirely rely on the target configuration
+     files, i.e. do not use the generic mechanism at all.  */
+  FULL_BUILTIN_STACK_CHECK
+};
+extern enum stack_check_type flag_stack_check;
+
 /* A string that's used when a random name is required.  NULL means
    to make it really random.  */
 
Index: tree.c
===================================================================
--- tree.c	(revision 112130)
+++ tree.c	(working copy)
@@ -6571,7 +6571,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: toplev.c
===================================================================
--- toplev.c	(revision 112130)
+++ toplev.c	(working copy)
@@ -339,6 +339,9 @@ int flag_renumber_insns = 1;
    to optimize, debug_info_level and debug_hooks in process_options ().  */
 int flag_var_tracking = AUTODETECT_VALUE;
 
+/* Type of stack check.  */
+enum stack_check_type flag_stack_check = NO_STACK_CHECK;
+
 /* True if the user has tagged the function with the 'section'
    attribute.  */
 
Index: testsuite/ada/acats/norun.lst
===================================================================
--- testsuite/ada/acats/norun.lst	(revision 112130)
+++ testsuite/ada/acats/norun.lst	(working copy)
@@ -1,8 +1,4 @@
 c380004
-c52103x
-c52104x
-c52104y
 templat
 # Tests must be sorted in alphabetical order
 # c380004: should be front-end compile time error, PR ada/18817
-# c52103x, c52104x, c52104y: -fstack-check doesn't work in 4.x, PR ada/20548
Index: expr.h
===================================================================
--- expr.h	(revision 112130)
+++ expr.h	(working copy)
@@ -202,13 +202,19 @@ do {							\
 
 /* Provide default values for the macros controlling stack checking.  */
 
+/* The default is neither full builtin stack checking...  */
 #ifndef STACK_CHECK_BUILTIN
 #define STACK_CHECK_BUILTIN 0
 #endif
 
-/* The default interval is one page.  */
-#ifndef STACK_CHECK_PROBE_INTERVAL
-#define STACK_CHECK_PROBE_INTERVAL 4096
+/* ...nor static builtin stack checking.  */
+#ifndef STACK_CHECK_STATIC_BUILTIN
+#define STACK_CHECK_STATIC_BUILTIN 0
+#endif
+
+/* 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.  */
@@ -216,16 +222,31 @@ do {							\
 #define STACK_CHECK_PROBE_LOAD 0
 #endif
 
-/* This value is arbitrary, but should be sufficient for most machines.  */
+/* 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
+   for the latter because part of the protection area is effectively
+   included in STACK_CHECK_MAX_FRAME_SIZE for the former.  */
+#ifdef STACK_CHECK_PROTECT
+#define STACK_OLD_CHECK_PROTECT STACK_CHECK_PROTECT
+#else
+#define STACK_OLD_CHECK_PROTECT \
+ (USING_SJLJ_EXCEPTIONS ? 75 * UNITS_PER_WORD : 8 * 1024)
+#endif
+
+/* Minimum amount of stack required to recover from an anticipated stack
+   overflow detection.  The default value conveys an estimate of the amount
+   of stack required to propagate an exception.  */
 #ifndef STACK_CHECK_PROTECT
-#define STACK_CHECK_PROTECT (75 * UNITS_PER_WORD)
+#define STACK_CHECK_PROTECT \
+ (USING_SJLJ_EXCEPTIONS ? 75 * UNITS_PER_WORD : 12 * 1024)
 #endif
 
 /* Make the maximum frame size be the largest we can and still only need
    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.  */
Index: opts.c
===================================================================
--- opts.c	(revision 112130)
+++ opts.c	(working copy)
@@ -26,6 +26,7 @@ Software Foundation, 51 Franklin Street,
 #include "tm.h"
 #include "tree.h"
 #include "rtl.h"
+#include "expr.h"
 #include "ggc.h"
 #include "output.h"
 #include "langhooks.h"
@@ -994,6 +995,26 @@ common_handle_option (size_t scode, cons
       flag_sched_stalled_insns_dep = value;
       break;
 
+    case OPT_fstack_check:
+      if (value)
+	flag_stack_check = STACK_CHECK_BUILTIN
+			   ? FULL_BUILTIN_STACK_CHECK
+			   : STACK_CHECK_STATIC_BUILTIN
+			     ? STATIC_BUILTIN_STACK_CHECK
+			     : GENERIC_STACK_CHECK;
+      else
+	flag_stack_check = NO_STACK_CHECK;
+      break;
+
+    case OPT_fold_stack_check:
+      if (value)
+	flag_stack_check = STACK_CHECK_BUILTIN
+			   ? FULL_BUILTIN_STACK_CHECK
+			   : GENERIC_STACK_CHECK;
+      else
+	flag_stack_check = NO_STACK_CHECK;
+      break;
+
     case OPT_fstack_limit:
       /* The real switch is -fno-stack-limit.  */
       if (value)
Index: ada/trans.c
===================================================================
--- ada/trans.c	(revision 112130)
+++ ada/trans.c	(working copy)
@@ -216,6 +216,10 @@ gigi (Node_Id gnat_root, int max_gnat_no
       TYPE_SIZE_UNIT (void_type_node) = size_zero_node;
     }
 
+  /* Enable GNAT stack checking method if needed */
+  if (!Stack_Check_Probes_On_Target)
+    stack_check_libfunc = gen_rtx_SYMBOL_REF (Pmode, "_gnat_stack_check");
+
   /* Save the type we made for integer as the type for Standard.Integer.
      Then make the rest of the standard types.  Note that some of these
      may be subtypes.  */
@@ -287,10 +291,6 @@ gnat_init_stmt_group ()
   /* Initialize ourselves.  */
   init_code_table ();
   start_stmt_group ();
-
-  /* Enable GNAT stack checking method if needed */
-  if (!Stack_Check_Probes_On_Target)
-    set_stack_check_libfunc (gen_rtx_SYMBOL_REF (Pmode, "_gnat_stack_check"));
 }
 
 /* Subroutine of gnat_to_gnu to translate gnat_node, an N_Identifier,
Index: ada/decl.c
===================================================================
--- ada/decl.c	(revision 112130)
+++ ada/decl.c	(working copy)
@@ -1144,12 +1144,12 @@ gnat_to_gnu_entity (Entity_Id gnat_entit
 	if (Present (Address_Clause (gnat_entity)) && used_by_ref)
 	  DECL_POINTER_ALIAS_SET (gnu_decl) = 0;
 
-	if (definition && DECL_SIZE (gnu_decl)
+	if (definition && DECL_SIZE_UNIT (gnu_decl)
 	    && get_block_jmpbuf_decl ()
-	    && (TREE_CODE (DECL_SIZE (gnu_decl)) != INTEGER_CST
-		|| (flag_stack_check && !STACK_CHECK_BUILTIN
-		    && 0 < compare_tree_int (DECL_SIZE_UNIT (gnu_decl),
-					     STACK_CHECK_MAX_VAR_SIZE))))
+	    && (TREE_CODE (DECL_SIZE_UNIT (gnu_decl)) != INTEGER_CST
+		|| (flag_stack_check == GENERIC_STACK_CHECK
+		    && compare_tree_int (DECL_SIZE_UNIT (gnu_decl),
+					 STACK_CHECK_MAX_VAR_SIZE) > 0)))
 	  add_stmt_with_node (build_call_1_expr
 			      (update_setjmp_buf_decl,
 			       build_unary_op (ADDR_EXPR, NULL_TREE,
Index: ada/utils2.c
===================================================================
--- ada/utils2.c	(revision 112130)
+++ ada/utils2.c	(working copy)
@@ -1784,11 +1784,11 @@ build_call_alloc_dealloc (tree gnu_obj, 
   /* ??? For now, disable variable-sized allocators in the stack since
      we can't yet gimplify an ALLOCATE_EXPR.  */
   else if (gnat_pool == -1
-	   && TREE_CODE (gnu_size) == INTEGER_CST && !flag_stack_check)
+	   && TREE_CODE (gnu_size) == INTEGER_CST
+	   && flag_stack_check != GENERIC_STACK_CHECK)
     {
       /* If the size is a constant, we can put it in the fixed portion of
 	 the stack frame to avoid the need to adjust the stack pointer.  */
-      if (TREE_CODE (gnu_size) == INTEGER_CST && !flag_stack_check)
 	{
 	  tree gnu_range
 	    = build_range_type (NULL_TREE, size_one_node, gnu_size);
@@ -1801,9 +1801,8 @@ build_call_alloc_dealloc (tree gnu_obj, 
 	  return convert (ptr_void_type_node,
 			  build_unary_op (ADDR_EXPR, NULL_TREE, gnu_decl));
 	}
-      else
-	gcc_unreachable ();
 #if 0
+      else
 	return build2 (ALLOCATE_EXPR, ptr_void_type_node, gnu_size, gnu_align);
 #endif
     }
Index: function.c
===================================================================
--- function.c	(revision 112130)
+++ function.c	(working copy)
@@ -3180,7 +3180,7 @@ gimplify_parameters (void)
       walk_tree_without_duplicates (&data.passed_type,
 				    gimplify_parm_type, &stmts);
 
-      if (!TREE_CONSTANT (DECL_SIZE (parm)))
+      if (TREE_CODE (DECL_SIZE_UNIT (parm)) != INTEGER_CST)
 	{
 	  gimplify_one_sizepos (&DECL_SIZE (parm), &stmts);
 	  gimplify_one_sizepos (&DECL_SIZE_UNIT (parm), &stmts);
@@ -3194,9 +3194,12 @@ gimplify_parameters (void)
 	    {
 	      tree local, t;
 
-	      /* For constant sized objects, this is trivial; for
+	      /* For constant-sized objects, this is trivial; for
 		 variable-sized objects, we have to play games.  */
-	      if (TREE_CONSTANT (DECL_SIZE (parm)))
+	      if (TREE_CODE (DECL_SIZE_UNIT (parm)) == INTEGER_CST
+		  && !(flag_stack_check == GENERIC_STACK_CHECK
+		       && compare_tree_int (DECL_SIZE_UNIT (parm),
+					    STACK_CHECK_MAX_VAR_SIZE) > 0))
 		{
 		  local = create_tmp_var (type, get_name (parm));
 		  DECL_IGNORED_P (local) = 0;
@@ -4349,10 +4352,10 @@ expand_function_end (void)
   if (arg_pointer_save_area && ! cfun->arg_pointer_save_area_init)
     get_arg_pointer_save_area (cfun);
 
-  /* If we are doing stack checking and this function makes calls,
+  /* If we are doing generic stack checking and this function makes calls,
      do a stack probe at the start of the function to ensure we have enough
      space for another stack frame.  */
-  if (flag_stack_check && ! STACK_CHECK_BUILTIN)
+  if (flag_stack_check == GENERIC_STACK_CHECK)
     {
       rtx insn, seq;
 
@@ -4360,7 +4363,7 @@ expand_function_end (void)
 	if (CALL_P (insn))
 	  {
 	    start_sequence ();
-	    probe_stack_range (STACK_CHECK_PROTECT,
+	    probe_stack_range (STACK_OLD_CHECK_PROTECT,
 			       GEN_INT (STACK_CHECK_MAX_FRAME_SIZE));
 	    seq = get_insns ();
 	    end_sequence ();
Index: gimplify.c
===================================================================
--- gimplify.c	(revision 112130)
+++ gimplify.c	(working copy)
@@ -1164,7 +1164,10 @@ gimplify_decl_expr (tree *stmt_p)
     {
       tree init = DECL_INITIAL (decl);
 
-      if (!TREE_CONSTANT (DECL_SIZE (decl)))
+      if (TREE_CODE (DECL_SIZE_UNIT (decl)) != INTEGER_CST
+	  || (flag_stack_check == GENERIC_STACK_CHECK
+	      && compare_tree_int (DECL_SIZE_UNIT (decl),
+				   STACK_CHECK_MAX_VAR_SIZE) > 0))
 	{
 	  /* This is a variable-sized decl.  Simplify its size and mark it
 	     for deferred expansion.  Note that mudflap depends on the format
Index: calls.c
===================================================================
--- calls.c	(revision 112130)
+++ calls.c	(working copy)
@@ -1024,10 +1024,10 @@ initialize_argument_information (int num
 	      rtx copy;
 
 	      if (!COMPLETE_TYPE_P (type)
-		  || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
-		  || (flag_stack_check && ! STACK_CHECK_BUILTIN
-		      && (0 < compare_tree_int (TYPE_SIZE_UNIT (type),
-						STACK_CHECK_MAX_VAR_SIZE))))
+		  || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
+		  || (flag_stack_check == GENERIC_STACK_CHECK
+		      && compare_tree_int (TYPE_SIZE_UNIT (type),
+					   STACK_CHECK_MAX_VAR_SIZE) > 0))
 		{
 		  /* This is a variable-sized object.  Make space on the stack
 		     for it.  */
@@ -3293,6 +3293,9 @@ emit_library_call_value_1 (int retval, r
     case LCT_THROW:
       flags = ECF_NORETURN;
       break;
+    case LCT_MAY_THROW:
+      flags &= ~ECF_NOTHROW;
+      break;
     case LCT_RETURNS_TWICE:
       flags = ECF_RETURNS_TWICE;
       break;
Index: explow.c
===================================================================
--- explow.c	(revision 112130)
+++ explow.c	(working copy)
@@ -30,6 +30,7 @@ Software Foundation, 51 Franklin Street,
 #include "tree.h"
 #include "tm_p.h"
 #include "flags.h"
+#include "except.h"
 #include "function.h"
 #include "expr.h"
 #include "optabs.h"
@@ -1206,8 +1207,11 @@ allocate_dynamic_stack_space (rtx size, 
 
   /* If needed, check that we have the required amount of stack.  Take into
      account what has already been checked.  */
-  if (flag_stack_check && ! STACK_CHECK_BUILTIN)
-    probe_stack_range (STACK_CHECK_MAX_FRAME_SIZE + STACK_CHECK_PROTECT, size);
+  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)
+    probe_stack_range (STACK_CHECK_PROTECT, size);
 
   /* Don't use a TARGET that isn't a pseudo or is the wrong mode.  */
   if (target == 0 || !REG_P (target)
@@ -1303,16 +1307,9 @@ allocate_dynamic_stack_space (rtx size, 
 }
 
 /* A front end may want to override GCC's stack checking by providing a
-   run-time routine to call to check the stack, so provide a mechanism for
-   calling that routine.  */
+   run-time routine to call to check the stack.  */
 
-static GTY(()) rtx stack_check_libfunc;
-
-void
-set_stack_check_libfunc (rtx libfunc)
-{
-  stack_check_libfunc = libfunc;
-}
+rtx stack_check_libfunc;
 
 /* Emit one stack probe at ADDRESS, an address within the stack.  */
 
@@ -1335,22 +1332,31 @@ emit_stack_probe (rtx address)
    subtract from the stack.  If SIZE is constant, this is done
    with a fixed number of probes.  Otherwise, we must make a loop.  */
 
+#define PROBE_INTERVAL (1 << STACK_CHECK_PROBE_INTERVAL_EXP)
+
 #ifdef STACK_GROWS_DOWNWARD
-#define STACK_GROW_OP MINUS
+#define STACK_GROW_OP     MINUS
+#define STACK_GROW_OPTAB  sub_optab
 #else
-#define STACK_GROW_OP PLUS
+#define STACK_GROW_OP     PLUS
+#define STACK_GROW_OPTAB  add_optab
 #endif
 
 void
 probe_stack_range (HOST_WIDE_INT first, rtx size)
 {
+#ifdef SPARC_STACK_BIAS
+  /* The probe offsets are counted negatively whereas the stack bias is
+     counted positively.  */
+  first -= SPARC_STACK_BIAS;
+#endif
+
   /* First ensure SIZE is Pmode.  */
   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 the runtime has got a function for us to call.  */
+  if (stack_check_libfunc)
     {
       rtx addr = memory_address (QImode,
 				 gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
@@ -1358,100 +1364,109 @@ probe_stack_range (HOST_WIDE_INT first, 
 					         plus_constant (size, first)));
 
       addr = convert_memory_address (ptr_mode, addr);
-      emit_library_call (stack_check_libfunc, LCT_NORMAL, VOIDmode, 1, addr,
+      emit_library_call (stack_check_libfunc, LCT_MAY_THROW, VOIDmode, 1, addr,
 			 ptr_mode);
     }
 
-  /* Next see if we have an insn to check the stack.  Use it if so.  */
-#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);
-
-      emit_insn (gen_check_stack (last_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 (GET_CODE (size) == CONST_INT
-	   && INTVAL (size) < 10 * STACK_CHECK_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)
+  /* 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 (GET_CODE (size) == CONST_INT && INTVAL (size) < 7 * PROBE_INTERVAL)
+    {
+      HOST_WIDE_INT i;
+
+      /* 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 SIZE.  */
+      for (i = PROBE_INTERVAL; i < INTVAL (size); i += PROBE_INTERVAL)
 	emit_stack_probe (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
 					  stack_pointer_rtx,
-					  GEN_INT (offset)));
+					  GEN_INT (i + first)));
 
       emit_stack_probe (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
 					stack_pointer_rtx,
 					plus_constant (size, first)));
     }
 
-  /* 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);
+      /* 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);
 
-      emit_jump (test_lab);
+
+      /* 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 exceeds 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 SIZE if we cannot assert at compile-time that
+	 it 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);
+	  emit_stack_probe (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
+					    last_addr,
+					    temp));
+	}
     }
 }
 
@@ -1544,5 +1559,3 @@ rtx_to_tree_code (enum rtx_code code)
     }
   return ((int) tcode);
 }
-
-#include "gt-explow.h"
Index: common.opt
===================================================================
--- common.opt	(revision 112130)
+++ common.opt	(working copy)
@@ -815,12 +815,14 @@ fvariable-expansion-in-unroller
 Common Report Var(flag_variable_expansion_in_unroller) 
 Apply variable expansion when loops are unrolled
 
-; Emit code to probe the stack, to help detect stack overflow; also
-; may cause large objects to be allocated dynamically.
 fstack-check
-Common Report Var(flag_stack_check)
+Common Report
 Insert stack checking code into the program
 
+fold-stack-check
+Common Report
+Insert old-style stack checking code into the program
+
 fstack-limit
 Common
 
Index: rtl.h
===================================================================
--- rtl.h	(revision 112130)
+++ rtl.h	(working copy)
@@ -1438,7 +1438,7 @@ extern int ceil_log2 (unsigned HOST_WIDE
 extern rtx expand_builtin_expect_jump (tree, rtx, rtx);
 
 /* In explow.c */
-extern void set_stack_check_libfunc (rtx);
+extern GTY(()) rtx stack_check_libfunc;
 extern HOST_WIDE_INT trunc_int_for_mode	(HOST_WIDE_INT, enum machine_mode);
 extern rtx plus_constant (rtx, HOST_WIDE_INT);
 
@@ -2194,7 +2194,8 @@ enum libcall_type
   LCT_PURE_MAKE_BLOCK = 4,
   LCT_NORETURN = 5,
   LCT_THROW = 6,
-  LCT_RETURNS_TWICE = 7
+  LCT_MAY_THROW = 7,
+  LCT_RETURNS_TWICE = 8
 };
 
 extern void emit_library_call (rtx, enum libcall_type, enum machine_mode, int,
Index: system.h
===================================================================
--- system.h	(revision 112130)
+++ system.h	(working copy)
@@ -734,7 +734,8 @@ extern void fancy_abort (const char *, i
 	EXTRA_SECTIONS EXTRA_SECTION_FUNCTIONS READONLY_DATA_SECTION	   \
 	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_OUTPUT_SHARED_LOCAL UNALIGNED_WORD_ASM_OP			   \
+	STACK_CHECK_PROBE_INTERVAL
 
 /* Hooks that are no longer used.  */
  #pragma GCC poison LANG_HOOKS_FUNCTION_MARK LANG_HOOKS_FUNCTION_FREE	\
Index: Makefile.in
===================================================================
--- Makefile.in	(revision 112130)
+++ Makefile.in	(working copy)
@@ -2201,8 +2201,7 @@ expmed.o : expmed.c $(CONFIG_H) $(SYSTEM
    toplev.h $(TM_P_H) langhooks.h
 explow.o : explow.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
    $(FLAGS_H) hard-reg-set.h insn-config.h $(EXPR_H) $(OPTABS_H) $(RECOG_H) \
-   toplev.h $(FUNCTION_H) $(GGC_H) $(TM_P_H) langhooks.h gt-explow.h target.h \
-   output.h
+   toplev.h $(FUNCTION_H) $(GGC_H) $(TM_P_H) langhooks.h target.h output.h
 optabs.o : optabs.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TREE_H) $(FLAGS_H) insn-config.h $(EXPR_H) $(OPTABS_H) libfuncs.h \
    $(RECOG_H) reload.h toplev.h $(GGC_H) real.h $(TM_P_H) except.h \
@@ -2843,7 +2842,7 @@ $(ALL_GTFILES_H) : s-gtype ; @true
 
 gt-cgraph.h gt-coverage.h gtype-desc.h gtype-desc.c gt-except.h \
 gt-function.h gt-integrate.h gt-tree.h gt-varasm.h \
-gt-emit-rtl.h gt-explow.h gt-stor-layout.h gt-regclass.h \
+gt-emit-rtl.h gt-stor-layout.h gt-regclass.h \
 gt-lists.h gt-alias.h gt-cselib.h gt-gcse.h \
 gt-expr.h gt-sdbout.h gt-optabs.h gt-bitmap.h gt-dojump.h \
 gt-dwarf2out.h gt-dwarf2asm.h \
Index: stmt.c
===================================================================
--- stmt.c	(revision 112130)
+++ stmt.c	(working copy)
@@ -1866,8 +1866,8 @@ expand_decl (tree decl)
     SET_DECL_RTL (decl, gen_rtx_MEM (BLKmode, const0_rtx));
 
   else if (DECL_SIZE (decl) == 0)
-    /* Variable with incomplete type.  */
     {
+      /* Variable with incomplete type.  */
       rtx x;
       if (DECL_INITIAL (decl) == 0)
 	/* Error message was already done; now avoid a crash.  */
@@ -1906,16 +1906,15 @@ expand_decl (tree decl)
 	}
     }
 
-  else if (TREE_CODE (DECL_SIZE_UNIT (decl)) == INTEGER_CST
-	   && ! (flag_stack_check && ! STACK_CHECK_BUILTIN
-		 && 0 < compare_tree_int (DECL_SIZE_UNIT (decl),
-					  STACK_CHECK_MAX_VAR_SIZE)))
+  else
     {
-      /* Variable of fixed size that goes on the stack.  */
       rtx oldaddr = 0;
       rtx addr;
       rtx x;
 
+      /* Variable-sized decls should have been handled in the gimplifier.  */
+      gcc_assert (TREE_CODE (DECL_SIZE_UNIT (decl)) == INTEGER_CST);
+
       /* If we previously made RTL for this decl, it must be an array
 	 whose size was determined by the initializer.
 	 The old address was a register; set that register now
@@ -1943,41 +1942,6 @@ expand_decl (tree decl)
 	    emit_move_insn (oldaddr, addr);
 	}
     }
-  else
-    /* Dynamic-size object: must push space on the stack.  */
-    {
-      rtx address, size, x;
-
-      /* Record the stack pointer on entry to block, if have
-	 not already done so.  */
-      do_pending_stack_adjust ();
-
-      /* Compute the variable's size, in bytes.  This will expand any
-	 needed SAVE_EXPRs for the first time.  */
-      size = expand_normal (DECL_SIZE_UNIT (decl));
-      free_temp_slots ();
-
-      /* Allocate space on the stack for the variable.  Note that
-	 DECL_ALIGN says how the variable is to be aligned and we
-	 cannot use it to conclude anything about the alignment of
-	 the size.  */
-      address = allocate_dynamic_stack_space (size, NULL_RTX,
-					      TYPE_ALIGN (TREE_TYPE (decl)));
-
-      /* Reference the variable indirect through that rtx.  */
-      x = gen_rtx_MEM (DECL_MODE (decl), address);
-      set_mem_attributes (x, decl, 1);
-      SET_DECL_RTL (decl, x);
-
-
-      /* Indicate the alignment we actually gave this variable.  */
-#ifdef STACK_BOUNDARY
-      DECL_ALIGN (decl) = STACK_BOUNDARY;
-#else
-      DECL_ALIGN (decl) = BIGGEST_ALIGNMENT;
-#endif
-      DECL_USER_ALIGN (decl) = 0;
-    }
 }
 
 /* Emit code to save the current value of stack.  */
Index: reload1.c
===================================================================
--- reload1.c	(revision 112130)
+++ reload1.c	(working copy)
@@ -1221,9 +1221,9 @@ reload (rtx first, int global)
 	cleanup_subreg_operands (insn);
       }
 
-  /* If we are doing stack checking, give a warning if this function's
-     frame size is larger than we expect.  */
-  if (flag_stack_check && ! STACK_CHECK_BUILTIN)
+  /* If we are doing generic stack checking, give a warning if this
+     function's frame size is larger than we expect.  */
+  if (flag_stack_check == GENERIC_STACK_CHECK)
     {
       HOST_WIDE_INT size = get_frame_size () + STACK_CHECK_FIXED_FRAME_SIZE;
       static int verbose_warned = 0;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]