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] Enhance support for -Wstack-usage/-Wvla-larger-than/-Walloca-larger-than


Hi,

a big limitation of -Wstack-usage/-Wvla-larger-than/-Walloca-larger-than is 
that you need -O2 (or more precisely -ftree-vrp) in order to be able to say 
something sensible for dynamically-sized objects/VLAs/calls to alloca.  That 
can be problematic, for example if the coding guidelines prevents you from 
using anything beyond -O1 for production builds.

Now in Ada it is very easy and common to use integer types with custom bounds 
(the compiler automatically generates the appropriate run-time bound checks) 
so it is very easy to be able to say something sensible about dynamically-
sized objects and VLAs (Ada doesn't have alloca) even at -O0 or -O1.

That's why the attached patch introduces a way for front-ends to communicate 
an upper bound for the size of dynamically-sized objects/VLAs/calls to alloca 
to the -Wstack-usage/-Wvla-larger-than/-Walloca-larger-than machineries, based 
on a 3rd form of the BUILT_IN_ALLOCA builtin which takes a 3rd parameter in 
addition to the 2 parameters of BUILT_IN_ALLOCA_WITH_ALIGN.  This 3rd form is 
only used when the front-end can put an upper bound via max_int_size_in_bytes, 
which invokes lang_hooks.types.max_size, for the time being, but its usage 
could easily be extended.

Macros and helper function are provided to manipulate the variants as a single 
builtin, so that code not directly tied to their support is little modified.
The -Wstack-usage and -Wvla-larger-than/-Walloca-larger-than machineries are 
enhanced to take into account the upper bound, if it exists.

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


2017-10-16  Eric Botcazou  <ebotcazou@adacore.com>

	* asan.c (handle_builtin_alloca): Deal with all alloca variants.
	(get_mem_refs_of_builtin_call): Likewise.
	* builtins.c (expand_builtin_apply): Adjust call to
	allocate_dynamic_stack_space.
	(expand_builtin_alloca): For __builtin_alloca_with_align_and_max, pass
	the third argument to allocate_dynamic_stack_space, otherwise -1.
	(expand_builtin): Deal with all alloca variants.
	(is_inexpensive_builtin): Likewise.
	* builtins.def (BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX): New.
	* calls.c (special_function_p): Deal with all alloca variants.
	(initialize_argument_information): Adjust call to
	allocate_dynamic_stack_space.
	(expand_call): Likewise.
	* cfgexpand.c (expand_stack_vars): Likewise.
	(expand_call_stmt): Deal with all alloca variants.
	* doc/extend.texi (Built-ins): Add __builtin_alloca_with_align_and_max
	* explow.c (allocate_dynamic_stack_space): Add MAX_SIZE parameter and
	use it for the stack usage computation.
	* explow.h (allocate_dynamic_stack_space): Adjust prototype.
	* function.c (gimplify_parameters): Turn BUILT_IN_ALLOCA_WITH_ALIGN
	into BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX and pass maximum size.
	* gimple-ssa-warn-alloca.c (alloca_call_type): Simplify control flow.
	Take into account 3rd argument of __builtin_alloca_with_align_and_max.
	(in_loop_p): Remove first argument and useless check.
	(pass_walloca::execute): Remove useless test and adjust call to above.
	* gimple.c (gimple_build_call_from_tree): Deal with all alloc variants
	* gimplify.c (gimplify_vla_decl): Turn BUILT_IN_ALLOCA_WITH_ALIGN into
	BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX and pass maximum size.
	(gimplify_call_expr): Deal with all alloca variants.
	* hsa-gen.c (gen_hsa_alloca): Likewise.
	(gen_hsa_insns_for_call): Likewise.
	* ipa-pure-const.c (special_builtin_state): Likewise.
	* tree-chkp.c (chkp_build_returned_bound): Likewise.
	* tree-object-size.c (alloc_object_size): Likewise.
	* tree-ssa-alias.c (ref_maybe_used_by_call_p_1): Likewise.
	(call_may_clobber_ref_p_1): Likewise.
	* tree-ssa-ccp.c (evaluate_stmt): Likewise.
	(ccp_fold_stmt): Likewise.
	(optimize_stack_restore): Likewise.
	* tree-ssa-dce.c (mark_stmt_if_obviously_necessary): Likewise.
	(mark_all_reaching_defs_necessary_1): Likewise.
	(propagate_necessity): Likewise.
	(eliminate_unnecessary_stmts): Likewise.
	* tree.c (build_common_builtin_nodes): Build
	BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX.
	* tree.h (ALLOCA_FUNCTION_CODE_P): New macro.
	(CASE_BUILT_IN_ALLOCA): Likewise.
	* varasm.c (incorporeal_function_p): Deal with all alloca variants.
ada/
	* gcc-interface/utils.c (max_size): Deal with SSA names.
c-family/
	* c-common.c (check_builtin_function_arguments): Also check arguments
	of __builtin_alloca_with_align_and_max.


2017-10-16  Eric Botcazou  <ebotcazou@adacore.com>

	* gcc.dg/Walloca-15.c: New test.
	* gnat.dg/stack_usage4.adb: Likewise.
	* gnat.dg/stack_usage4_pkg.ads: New helper.

-- 
Eric Botcazou
Index: ada/gcc-interface/utils.c
===================================================================
--- ada/gcc-interface/utils.c	(revision 253767)
+++ ada/gcc-interface/utils.c	(working copy)
@@ -3595,6 +3595,10 @@ max_size (tree exp, bool max_p)
     case tcc_constant:
       return exp;
 
+    case tcc_exceptional:
+      gcc_assert (code == SSA_NAME);
+      return exp;
+
     case tcc_vl_exp:
       if (code == CALL_EXPR)
 	{
Index: asan.c
===================================================================
--- asan.c	(revision 253767)
+++ asan.c	(working copy)
@@ -628,10 +628,9 @@ handle_builtin_alloca (gcall *call, gimp
   tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
 					 : ptr_type_node;
   tree partial_size = NULL_TREE;
-  bool alloca_with_align
-    = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA_WITH_ALIGN;
   unsigned int align
-    = alloca_with_align ? tree_to_uhwi (gimple_call_arg (call, 1)) : 0;
+    = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
+      ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
 
   /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
      bytes of allocated space.  Otherwise, align alloca to ASAN_RED_ZONE_SIZE
@@ -793,8 +792,7 @@ get_mem_refs_of_builtin_call (gcall *cal
       handle_builtin_stack_restore (call, iter);
       break;
 
-    case BUILT_IN_ALLOCA_WITH_ALIGN:
-    case BUILT_IN_ALLOCA:
+    CASE_BUILT_IN_ALLOCA:
       handle_builtin_alloca (call, iter);
       break;
     /* And now the __atomic* and __sync builtins.
Index: builtins.c
===================================================================
--- builtins.c	(revision 253767)
+++ builtins.c	(working copy)
@@ -1608,7 +1608,7 @@ expand_builtin_apply (rtx function, rtx
      arguments to the outgoing arguments address.  We can pass TRUE
      as the 4th argument because we just saved the stack pointer
      and will restore it right after the call.  */
-  allocate_dynamic_stack_space (argsize, 0, BIGGEST_ALIGNMENT, true);
+  allocate_dynamic_stack_space (argsize, 0, BIGGEST_ALIGNMENT, -1, true);
 
   /* Set DRAP flag to true, even though allocate_dynamic_stack_space
      may have already set current_function_calls_alloca to true.
@@ -4857,19 +4857,22 @@ expand_builtin_alloca (tree exp)
   rtx result;
   unsigned int align;
   tree fndecl = get_callee_fndecl (exp);
-  bool alloca_with_align = (DECL_FUNCTION_CODE (fndecl)
-			    == BUILT_IN_ALLOCA_WITH_ALIGN);
+  HOST_WIDE_INT max_size;
+  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
   bool alloca_for_var = CALL_ALLOCA_FOR_VAR_P (exp);
   bool valid_arglist
-    = (alloca_with_align
-       ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)
-       : validate_arglist (exp, INTEGER_TYPE, VOID_TYPE));
+    = (fcode == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX
+       ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, INTEGER_TYPE,
+			   VOID_TYPE)
+       : fcode == BUILT_IN_ALLOCA_WITH_ALIGN
+	 ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)
+	 : validate_arglist (exp, INTEGER_TYPE, VOID_TYPE));
 
   if (!valid_arglist)
     return NULL_RTX;
 
-  if ((alloca_with_align && !warn_vla_limit)
-      || (!alloca_with_align && !warn_alloca_limit))
+  if ((alloca_for_var && !warn_vla_limit)
+      || (!alloca_for_var && !warn_alloca_limit))
     {
       /* -Walloca-larger-than and -Wvla-larger-than settings override
 	 the more general -Walloc-size-larger-than so unless either of
@@ -4884,13 +4887,19 @@ expand_builtin_alloca (tree exp)
   op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
 
   /* Compute the alignment.  */
-  align = (alloca_with_align
-	   ? TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 1))
-	   : BIGGEST_ALIGNMENT);
+  align = (fcode == BUILT_IN_ALLOCA
+	   ? BIGGEST_ALIGNMENT
+	   : TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 1)));
+
+  /* Compute the maximum size.  */
+  max_size = (fcode == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX
+              ? TREE_INT_CST_LOW (CALL_EXPR_ARG (exp, 2))
+              : -1);
 
   /* Allocate the desired space.  If the allocation stems from the declaration
      of a variable-sized object, it cannot accumulate.  */
-  result = allocate_dynamic_stack_space (op0, 0, align, alloca_for_var);
+  result
+    = allocate_dynamic_stack_space (op0, 0, align, max_size, alloca_for_var);
   result = convert_memory_address (ptr_mode, result);
 
   return result;
@@ -6481,8 +6490,7 @@ expand_builtin (tree exp, rtx target, rt
       && fcode != BUILT_IN_EXECLE
       && fcode != BUILT_IN_EXECVP
       && fcode != BUILT_IN_EXECVE
-      && fcode != BUILT_IN_ALLOCA
-      && fcode != BUILT_IN_ALLOCA_WITH_ALIGN
+      && !ALLOCA_FUNCTION_CODE_P (fcode)
       && fcode != BUILT_IN_FREE
       && fcode != BUILT_IN_CHKP_SET_PTR_BOUNDS
       && fcode != BUILT_IN_CHKP_INIT_PTR_BOUNDS
@@ -6711,8 +6719,7 @@ expand_builtin (tree exp, rtx target, rt
       else
 	return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
 
-    case BUILT_IN_ALLOCA:
-    case BUILT_IN_ALLOCA_WITH_ALIGN:
+    CASE_BUILT_IN_ALLOCA:
       target = expand_builtin_alloca (exp);
       if (target)
 	return target;
@@ -10424,8 +10431,7 @@ is_inexpensive_builtin (tree decl)
     switch (DECL_FUNCTION_CODE (decl))
       {
       case BUILT_IN_ABS:
-      case BUILT_IN_ALLOCA:
-      case BUILT_IN_ALLOCA_WITH_ALIGN:
+      CASE_BUILT_IN_ALLOCA:
       case BUILT_IN_BSWAP16:
       case BUILT_IN_BSWAP32:
       case BUILT_IN_BSWAP64:
Index: builtins.def
===================================================================
--- builtins.def	(revision 253767)
+++ builtins.def	(working copy)
@@ -921,6 +921,7 @@ DEF_BUILTIN_STUB (BUILT_IN_SETJMP_RECEIV
 DEF_BUILTIN_STUB (BUILT_IN_STACK_SAVE, "__builtin_stack_save")
 DEF_BUILTIN_STUB (BUILT_IN_STACK_RESTORE, "__builtin_stack_restore")
 DEF_BUILTIN_STUB (BUILT_IN_ALLOCA_WITH_ALIGN, "__builtin_alloca_with_align")
+DEF_BUILTIN_STUB (BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX, "__builtin_alloca_with_align_and_max")
 
 /* An internal version of memcmp, used when the result is only tested for
    equality with zero.  */
Index: c-family/c-common.c
===================================================================
--- c-family/c-common.c	(revision 253767)
+++ c-family/c-common.c	(working copy)
@@ -5696,6 +5696,7 @@ check_builtin_function_arguments (locati
   switch (DECL_FUNCTION_CODE (fndecl))
     {
     case BUILT_IN_ALLOCA_WITH_ALIGN:
+    case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX:
       {
 	/* Get the requested alignment (in bits) if it's a constant
 	   integer expression.  */
Index: calls.c
===================================================================
--- calls.c	(revision 253767)
+++ calls.c	(working copy)
@@ -607,16 +607,9 @@ special_function_p (const_tree fndecl, i
 	flags |= ECF_RETURNS_TWICE;
     }
 
-  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
-    switch (DECL_FUNCTION_CODE (fndecl))
-      {
-      case BUILT_IN_ALLOCA:
-      case BUILT_IN_ALLOCA_WITH_ALIGN:
-	flags |= ECF_MAY_BE_ALLOCA;
-	break;
-      default:
-	break;
-      }
+  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+      && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (fndecl)))
+    flags |= ECF_MAY_BE_ALLOCA;
 
   return flags;
 }
@@ -698,8 +691,7 @@ gimple_alloca_call_p (const gimple *stmt
   if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
     switch (DECL_FUNCTION_CODE (fndecl))
       {
-      case BUILT_IN_ALLOCA:
-      case BUILT_IN_ALLOCA_WITH_ALIGN:
+      CASE_BUILT_IN_ALLOCA:
         return true;
       default:
 	break;
@@ -719,8 +711,7 @@ alloca_call_p (const_tree exp)
       && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
     switch (DECL_FUNCTION_CODE (fndecl))
       {
-      case BUILT_IN_ALLOCA:
-      case BUILT_IN_ALLOCA_WITH_ALIGN:
+      CASE_BUILT_IN_ALLOCA:
         return true;
       default:
 	break;
@@ -1819,6 +1810,8 @@ initialize_argument_information (int num
 		  copy = allocate_dynamic_stack_space (size_rtx,
 						       TYPE_ALIGN (type),
 						       TYPE_ALIGN (type),
+						       max_int_size_in_bytes
+						       (type),
 						       true);
 		  copy = gen_rtx_MEM (BLKmode, copy);
 		  set_mem_attributes (copy, type, 1);
@@ -3638,8 +3631,8 @@ expand_call (tree exp, rtx target, int i
 	      /* We can pass TRUE as the 4th argument because we just
 		 saved the stack pointer and will restore it right after
 		 the call.  */
-	      allocate_dynamic_stack_space (push_size, 0,
-					    BIGGEST_ALIGNMENT, true);
+	      allocate_dynamic_stack_space (push_size, 0, BIGGEST_ALIGNMENT,
+					    -1, true);
 	    }
 
 	  /* If argument evaluation might modify the stack pointer,
Index: cfgexpand.c
===================================================================
--- cfgexpand.c	(revision 253767)
+++ cfgexpand.c	(working copy)
@@ -2634,8 +2634,7 @@ expand_call_stmt (gcall *stmt)
   CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
   if (decl
       && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
-      && (DECL_FUNCTION_CODE (decl) == BUILT_IN_ALLOCA
-	  || DECL_FUNCTION_CODE (decl) == BUILT_IN_ALLOCA_WITH_ALIGN))
+      && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (decl)))
     CALL_ALLOCA_FOR_VAR_P (exp) = gimple_call_alloca_for_var_p (stmt);
   else
     CALL_FROM_THUNK_P (exp) = gimple_call_from_thunk_p (stmt);
Index: doc/extend.texi
===================================================================
--- doc/extend.texi	(revision 253767)
+++ doc/extend.texi	(working copy)
@@ -10869,6 +10869,7 @@ in the Cilk Plus language manual which c
 @cindex built-in functions
 @findex __builtin_alloca
 @findex __builtin_alloca_with_align
+@findex __builtin_alloca_with_align_and_max
 @findex __builtin_call_with_static_chain
 @findex __builtin_fpclassify
 @findex __builtin_isfinite
@@ -11516,6 +11517,13 @@ an extension.  @xref{Variable Length}, f
 
 @end deftypefn
 
+@deftypefn {Built-in Function} void *__builtin_alloca_with_align_and_max (size_t size, size_t alignment, size_t max)
+Similar to @code{__builtin_alloca_with_align} but takes an extra argument
+giving an upper bound for @var{size}, in case it is not a constant, for
+use by the @option{-fstack-usage} and @option{-Wstack-usage} computations.
+
+@end deftypefn
+
 @deftypefn {Built-in Function} int __builtin_types_compatible_p (@var{type1}, @var{type2})
 
 You can use the built-in function @code{__builtin_types_compatible_p} to
Index: explow.c
===================================================================
--- explow.c	(revision 253767)
+++ explow.c	(working copy)
@@ -1322,6 +1322,9 @@ get_stack_check_protect (void)
    REQUIRED_ALIGN is the alignment (in bits) required for the region
    of memory.
 
+   MAX_SIZE is an upper bound for SIZE, if SIZE is not constant, or -1 if
+   no such upper bound is known.
+
    If CANNOT_ACCUMULATE is set to TRUE, the caller guarantees that the
    stack space allocated by the generated code cannot be added with itself
    in the course of the execution of the function.  It is always safe to
@@ -1331,7 +1334,9 @@ get_stack_check_protect (void)
 
 rtx
 allocate_dynamic_stack_space (rtx size, unsigned size_align,
-			      unsigned required_align, bool cannot_accumulate)
+			      unsigned required_align,
+			      HOST_WIDE_INT max_size,
+			      bool cannot_accumulate)
 {
   HOST_WIDE_INT stack_usage_size = -1;
   rtx_code_label *final_label;
@@ -1370,7 +1375,11 @@ allocate_dynamic_stack_space (rtx size,
 	    }
 	}
 
-      /* If the size is not constant, we can't say anything.  */
+      /* If the size is not constant, try the maximum size.  */
+      if (stack_usage_size == - 1)
+	stack_usage_size = max_size;
+
+      /* If the size is still not constant, we can't say anything.  */
       if (stack_usage_size == -1)
 	{
 	  current_function_has_unbounded_dynamic_stack_size = 1;
Index: explow.h
===================================================================
--- explow.h	(revision 253767)
+++ explow.h	(working copy)
@@ -94,7 +94,8 @@ extern void update_nonlocal_goto_save_ar
 extern void record_new_stack_level (void);
 
 /* Allocate some space on the stack dynamically and return its address.  */
-extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned, bool);
+extern rtx allocate_dynamic_stack_space (rtx, unsigned, unsigned,
+					 HOST_WIDE_INT, bool);
 
 /* Calculate the necessary size of a constant dynamic stack allocation from the
    size of the variable area.  */
Index: function.c
===================================================================
--- function.c	(revision 253767)
+++ function.c	(working copy)
@@ -4049,10 +4049,9 @@ gimplify_parameters (void)
 		  DECL_IGNORED_P (addr) = 0;
 		  local = build_fold_indirect_ref (addr);
 
-		  t = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN);
-		  t = build_call_expr (t, 2, DECL_SIZE_UNIT (parm),
-				       size_int (DECL_ALIGN (parm)));
-
+		  t = build_alloca_call_expr (DECL_SIZE_UNIT (parm),
+					      DECL_ALIGN (parm),
+					      max_int_size_in_bytes (type));
 		  /* The call has been built for a variable-sized object.  */
 		  CALL_ALLOCA_FOR_VAR_P (t) = 1;
 		  t = fold_convert (ptr_type, t);
Index: gimple-ssa-warn-alloca.c
===================================================================
--- gimple-ssa-warn-alloca.c	(revision 253767)
+++ gimple-ssa-warn-alloca.c	(working copy)
@@ -264,7 +264,7 @@ is_max (tree x, wide_int max)
 
 // Analyze the alloca call in STMT and return the alloca type with its
 // corresponding limit (if applicable).  IS_VLA is set if the alloca
-// call is really a BUILT_IN_ALLOCA_WITH_ALIGN, signifying a VLA.
+// call was created by the gimplifier for a VLA.
 //
 // If the alloca call may be too large because of a cast from a signed
 // type to an unsigned type, set *INVALID_CASTED_TYPE to the
@@ -278,7 +278,8 @@ alloca_call_type (gimple *stmt, bool is_
   tree len = gimple_call_arg (stmt, 0);
   tree len_casted = NULL;
   wide_int min, max;
-  struct alloca_type_and_limit ret = alloca_type_and_limit (ALLOCA_UNBOUNDED);
+  edge_iterator ei;
+  edge e;
 
   gcc_assert (!is_vla || (unsigned HOST_WIDE_INT) warn_vla_limit > 0);
   gcc_assert (is_vla || (unsigned HOST_WIDE_INT) warn_alloca_limit > 0);
@@ -299,16 +300,18 @@ alloca_call_type (gimple *stmt, bool is_
 				      wi::to_wide (len));
       if (integer_zerop (len))
 	return alloca_type_and_limit (ALLOCA_ARG_IS_ZERO);
-      ret = alloca_type_and_limit (ALLOCA_OK);
+
+      return alloca_type_and_limit (ALLOCA_OK);
     }
+
   // Check the range info if available.
-  else if (TREE_CODE (len) == SSA_NAME)
+  if (TREE_CODE (len) == SSA_NAME)
     {
       value_range_type range_type = get_range_info (len, &min, &max);
       if (range_type == VR_RANGE)
 	{
 	  if (wi::leu_p (max, max_size))
-	    ret = alloca_type_and_limit (ALLOCA_OK);
+	    return alloca_type_and_limit (ALLOCA_OK);
 	  else
 	    {
 	      // A cast may have created a range we don't care
@@ -388,55 +391,37 @@ alloca_call_type (gimple *stmt, bool is_
       // No easily determined range and try other things.
     }
 
+  // If we have a declared maximum size not larger than MAX_SIZE, it's easy.
+  if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX)
+      && tree_to_uhwi (gimple_call_arg (stmt, 2)) <= max_size)
+    return alloca_type_and_limit (ALLOCA_OK);
+
   // If we couldn't find anything, try a few heuristics for things we
   // can easily determine.  Check these misc cases but only accept
   // them if all predecessors have a known bound.
-  basic_block bb = gimple_bb (stmt);
-  if (ret.type == ALLOCA_UNBOUNDED)
+  struct alloca_type_and_limit ret = alloca_type_and_limit (ALLOCA_OK);
+  FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->preds)
     {
-      ret.type = ALLOCA_OK;
-      for (unsigned ix = 0; ix < EDGE_COUNT (bb->preds); ix++)
-	{
-	  gcc_assert (!len_casted || TYPE_UNSIGNED (TREE_TYPE (len_casted)));
-	  ret = alloca_call_type_by_arg (len, len_casted,
-					 EDGE_PRED (bb, ix), max_size);
-	  if (ret.type != ALLOCA_OK)
-	    break;
-	}
+      gcc_assert (!len_casted || TYPE_UNSIGNED (TREE_TYPE (len_casted)));
+      ret = alloca_call_type_by_arg (len, len_casted, e, max_size);
+      if (ret.type != ALLOCA_OK)
+	break;
     }
 
   if (tentative_cast_from_signed && ret.type != ALLOCA_OK)
     return alloca_type_and_limit (ALLOCA_CAST_FROM_SIGNED);
+
   return ret;
 }
 
-// Return TRUE if the alloca call in STMT is in a loop, otherwise
-// return FALSE. As an exception, ignore alloca calls for VLAs that
-// occur in a loop since those will be cleaned up when they go out of
-// scope.
+// Return TRUE if STMT is in a loop, otherwise return FALSE.
 
 static bool
-in_loop_p (bool is_vla, gimple *stmt)
+in_loop_p (gimple *stmt)
 {
   basic_block bb = gimple_bb (stmt);
-  if (bb->loop_father
-      && bb->loop_father->header != ENTRY_BLOCK_PTR_FOR_FN (cfun))
-    {
-      // Do not warn on VLAs occurring in a loop, since VLAs are
-      // guaranteed to be cleaned up when they go out of scope.
-      // That is, there is a corresponding __builtin_stack_restore
-      // at the end of the scope in which the VLA occurs.
-      tree fndecl = gimple_call_fn (stmt);
-      while (TREE_CODE (fndecl) == ADDR_EXPR)
-	fndecl = TREE_OPERAND (fndecl, 0);
-      if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
-	  && is_vla
-	  && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA_WITH_ALIGN)
-	return false;
-
-      return true;
-    }
-  return false;
+  return
+    bb->loop_father && bb->loop_father->header != ENTRY_BLOCK_PTR_FOR_FN (cfun);
 }
 
 unsigned int
@@ -455,8 +440,8 @@ pass_walloca::execute (function *fun)
 	    continue;
 	  gcc_assert (gimple_call_num_args (stmt) >= 1);
 
-	  bool is_vla = gimple_alloca_call_p (stmt)
-	    && gimple_call_alloca_for_var_p (as_a <gcall *> (stmt));
+	  const bool is_vla
+	    = gimple_call_alloca_for_var_p (as_a <gcall *> (stmt));
 
 	  // Strict mode whining for VLAs is handled by the front-end,
 	  // so we can safely ignore this case.  Also, ignore VLAs if
@@ -476,9 +461,10 @@ pass_walloca::execute (function *fun)
 	  struct alloca_type_and_limit t
 	    = alloca_call_type (stmt, is_vla, &invalid_casted_type);
 
-	  // Even if we think the alloca call is OK, make sure it's
-	  // not in a loop.
-	  if (t.type == ALLOCA_OK && in_loop_p (is_vla, stmt))
+	  // Even if we think the alloca call is OK, make sure it's not in a
+	  // loop, except for a VLA, since VLAs are guaranteed to be cleaned
+	  // up when they go out of scope, including in a loop.
+	  if (t.type == ALLOCA_OK && !is_vla && in_loop_p (stmt))
 	    t = alloca_type_and_limit (ALLOCA_IN_LOOP);
 
 	  enum opt_code wcode
Index: gimple.c
===================================================================
--- gimple.c	(revision 253767)
+++ gimple.c	(working copy)
@@ -369,8 +369,7 @@ gimple_build_call_from_tree (tree t)
   gimple_call_set_return_slot_opt (call, CALL_EXPR_RETURN_SLOT_OPT (t));
   if (fndecl
       && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
-      && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA
-	  || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA_WITH_ALIGN))
+      && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (fndecl)))
     gimple_call_set_alloca_for_var (call, CALL_ALLOCA_FOR_VAR_P (t));
   else
     gimple_call_set_from_thunk (call, CALL_FROM_THUNK_P (t));
Index: gimplify.c
===================================================================
--- gimplify.c	(revision 253767)
+++ gimplify.c	(working copy)
@@ -1574,9 +1574,8 @@ gimplify_vla_decl (tree decl, gimple_seq
   SET_DECL_VALUE_EXPR (decl, t);
   DECL_HAS_VALUE_EXPR_P (decl) = 1;
 
-  t = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN);
-  t = build_call_expr (t, 2, DECL_SIZE_UNIT (decl),
-		       size_int (DECL_ALIGN (decl)));
+  t = build_alloca_call_expr (DECL_SIZE_UNIT (decl), DECL_ALIGN (decl),
+			      max_int_size_in_bytes (TREE_TYPE (decl)));
   /* The call has been built for a variable-sized object.  */
   CALL_ALLOCA_FOR_VAR_P (t) = 1;
   t = fold_convert (ptr_type, t);
@@ -3173,8 +3172,7 @@ gimplify_call_expr (tree *expr_p, gimple
       && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
     switch (DECL_FUNCTION_CODE (fndecl))
       {
-      case BUILT_IN_ALLOCA:
-      case BUILT_IN_ALLOCA_WITH_ALIGN:
+      CASE_BUILT_IN_ALLOCA:
 	/* If the call has been built for a variable-sized object, then we
 	   want to restore the stack level when the enclosing BIND_EXPR is
 	   exited to reclaim the allocated space; otherwise, we precisely
Index: hsa-gen.c
===================================================================
--- hsa-gen.c	(revision 253767)
+++ hsa-gen.c	(working copy)
@@ -4238,12 +4238,11 @@ gen_hsa_alloca (gcall *call, hsa_bb *hbb
 
   built_in_function fn = DECL_FUNCTION_CODE (gimple_call_fndecl (call));
 
-  gcc_checking_assert (fn == BUILT_IN_ALLOCA
-		       || fn == BUILT_IN_ALLOCA_WITH_ALIGN);
+  gcc_checking_assert (ALLOCA_FUNCTION_CODE_P (fn));
 
   unsigned bit_alignment = 0;
 
-  if (fn == BUILT_IN_ALLOCA_WITH_ALIGN)
+  if (fn != BUILT_IN_ALLOCA)
     {
       tree alignment_tree = gimple_call_arg (call, 1);
       if (TREE_CODE (alignment_tree) != INTEGER_CST)
@@ -5656,8 +5655,7 @@ gen_hsa_insns_for_call (gimple *stmt, hs
 
 	break;
       }
-    case BUILT_IN_ALLOCA:
-    case BUILT_IN_ALLOCA_WITH_ALIGN:
+    CASE_BUILT_IN_ALLOCA:
       {
 	gen_hsa_alloca (call, hbb);
 	break;
Index: ipa-pure-const.c
===================================================================
--- ipa-pure-const.c	(revision 253767)
+++ ipa-pure-const.c	(working copy)
@@ -518,8 +518,7 @@ special_builtin_state (enum pure_const_s
       {
 	case BUILT_IN_RETURN:
 	case BUILT_IN_UNREACHABLE:
-	case BUILT_IN_ALLOCA:
-	case BUILT_IN_ALLOCA_WITH_ALIGN:
+	CASE_BUILT_IN_ALLOCA:
 	case BUILT_IN_STACK_SAVE:
 	case BUILT_IN_STACK_RESTORE:
 	case BUILT_IN_EH_POINTER:
Index: tree-chkp.c
===================================================================
--- tree-chkp.c	(revision 253767)
+++ tree-chkp.c	(working copy)
@@ -2276,8 +2276,7 @@ chkp_build_returned_bound (gcall *call)
      it separately.  */
   if (fndecl
       && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
-      && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA
-	  || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA_WITH_ALIGN))
+      && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (fndecl)))
     {
       tree size = gimple_call_arg (call, 0);
       gimple_stmt_iterator iter = gsi_for_stmt (call);
Index: tree-object-size.c
===================================================================
--- tree-object-size.c	(revision 253767)
+++ tree-object-size.c	(working copy)
@@ -430,8 +430,7 @@ alloc_object_size (const gcall *call, in
 	arg2 = 1;
 	/* fall through */
       case BUILT_IN_MALLOC:
-      case BUILT_IN_ALLOCA:
-      case BUILT_IN_ALLOCA_WITH_ALIGN:
+      CASE_BUILT_IN_ALLOCA:
 	arg1 = 0;
       default:
 	break;
Index: tree-ssa-alias.c
===================================================================
--- tree-ssa-alias.c	(revision 253767)
+++ tree-ssa-alias.c	(working copy)
@@ -1779,8 +1779,7 @@ ref_maybe_used_by_call_p_1 (gcall *call,
 	case BUILT_IN_POSIX_MEMALIGN:
 	case BUILT_IN_ALIGNED_ALLOC:
 	case BUILT_IN_CALLOC:
-	case BUILT_IN_ALLOCA:
-	case BUILT_IN_ALLOCA_WITH_ALIGN:
+	CASE_BUILT_IN_ALLOCA:
 	case BUILT_IN_STACK_SAVE:
 	case BUILT_IN_STACK_RESTORE:
 	case BUILT_IN_MEMSET:
@@ -2118,8 +2117,7 @@ call_may_clobber_ref_p_1 (gcall *call, a
 	    return true;
 	  return false;
 	case BUILT_IN_STACK_SAVE:
-	case BUILT_IN_ALLOCA:
-	case BUILT_IN_ALLOCA_WITH_ALIGN:
+	CASE_BUILT_IN_ALLOCA:
 	case BUILT_IN_ASSUME_ALIGNED:
 	  return false;
 	/* But posix_memalign stores a pointer into the memory pointed to
Index: tree-ssa-ccp.c
===================================================================
--- tree-ssa-ccp.c	(revision 253767)
+++ tree-ssa-ccp.c	(working copy)
@@ -1886,11 +1886,10 @@ evaluate_stmt (gimple *stmt)
 			   / BITS_PER_UNIT - 1);
 	      break;
 
-	    case BUILT_IN_ALLOCA:
-	    case BUILT_IN_ALLOCA_WITH_ALIGN:
-	      align = (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA_WITH_ALIGN
-		       ? TREE_INT_CST_LOW (gimple_call_arg (stmt, 1))
-		       : BIGGEST_ALIGNMENT);
+	    CASE_BUILT_IN_ALLOCA:
+	      align = (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA
+		       ? BIGGEST_ALIGNMENT
+		       : TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)));
 	      val.lattice_val = CONSTANT;
 	      val.value = build_int_cst (TREE_TYPE (gimple_get_lhs (stmt)), 0);
 	      val.mask = ~((HOST_WIDE_INT) align / BITS_PER_UNIT - 1);
@@ -2243,7 +2242,8 @@ ccp_fold_stmt (gimple_stmt_iterator *gsi
         /* The heuristic of fold_builtin_alloca_with_align differs before and
 	   after inlining, so we don't require the arg to be changed into a
 	   constant for folding, but just to be constant.  */
-        if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
+        if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN)
+	    || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX))
           {
             tree new_rhs = fold_builtin_alloca_with_align (stmt);
             if (new_rhs)
@@ -2535,8 +2535,7 @@ optimize_stack_restore (gimple_stmt_iter
       if (!callee
 	  || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
 	  /* All regular builtins are ok, just obviously not alloca.  */
-	  || DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
-	  || DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA_WITH_ALIGN)
+	  || ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callee)))
 	return NULL_TREE;
 
       if (DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_RESTORE)
Index: tree-ssa-dce.c
===================================================================
--- tree-ssa-dce.c	(revision 253767)
+++ tree-ssa-dce.c	(working copy)
@@ -231,8 +231,7 @@ mark_stmt_if_obviously_necessary (gimple
 	    case BUILT_IN_MALLOC:
 	    case BUILT_IN_ALIGNED_ALLOC:
 	    case BUILT_IN_CALLOC:
-	    case BUILT_IN_ALLOCA:
-	    case BUILT_IN_ALLOCA_WITH_ALIGN:
+	    CASE_BUILT_IN_ALLOCA:
 	    case BUILT_IN_STRDUP:
 	    case BUILT_IN_STRNDUP:
 	      return;
@@ -576,8 +575,7 @@ mark_all_reaching_defs_necessary_1 (ao_r
 	  case BUILT_IN_MALLOC:
 	  case BUILT_IN_ALIGNED_ALLOC:
 	  case BUILT_IN_CALLOC:
-	  case BUILT_IN_ALLOCA:
-	  case BUILT_IN_ALLOCA_WITH_ALIGN:
+	  CASE_BUILT_IN_ALLOCA:
 	  case BUILT_IN_FREE:
 	    return false;
 
@@ -845,9 +843,7 @@ propagate_necessity (bool aggressive)
 		      || DECL_FUNCTION_CODE (callee) == BUILT_IN_CALLOC
 		      || DECL_FUNCTION_CODE (callee) == BUILT_IN_FREE
 		      || DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_END
-		      || DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
-		      || (DECL_FUNCTION_CODE (callee)
-			  == BUILT_IN_ALLOCA_WITH_ALIGN)
+		      || ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callee))
 		      || DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_SAVE
 		      || DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_RESTORE
 		      || DECL_FUNCTION_CODE (callee) == BUILT_IN_ASSUME_ALIGNED))
@@ -1348,9 +1344,8 @@ eliminate_unnecessary_stmts (void)
 		      || (DECL_FUNCTION_CODE (call) != BUILT_IN_ALIGNED_ALLOC
 			  && DECL_FUNCTION_CODE (call) != BUILT_IN_MALLOC
 			  && DECL_FUNCTION_CODE (call) != BUILT_IN_CALLOC
-			  && DECL_FUNCTION_CODE (call) != BUILT_IN_ALLOCA
-			  && (DECL_FUNCTION_CODE (call)
-			      != BUILT_IN_ALLOCA_WITH_ALIGN)))
+			  && !ALLOCA_FUNCTION_CODE_P
+			      (DECL_FUNCTION_CODE (call))))
 		  /* Avoid doing so for bndret calls for the same reason.  */
 		  && !chkp_gimple_call_builtin_p (stmt, BUILT_IN_CHKP_BNDRET))
 		{
Index: tree.c
===================================================================
--- tree.c	(revision 253767)
+++ tree.c	(working copy)
@@ -9960,6 +9960,13 @@ build_common_builtin_nodes (void)
 			"__builtin_alloca_with_align",
 			alloca_flags);
 
+  ftype = build_function_type_list (ptr_type_node, size_type_node,
+				    size_type_node, size_type_node, NULL_TREE);
+  local_define_builtin ("__builtin_alloca_with_align_and_max", ftype,
+			BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX,
+			"__builtin_alloca_with_align_and_max",
+			alloca_flags);
+
   ftype = build_function_type_list (void_type_node,
 				    ptr_type_node, ptr_type_node,
 				    ptr_type_node, NULL_TREE);
@@ -10701,6 +10708,33 @@ maybe_build_call_expr_loc (location_t lo
     }
 }
 
+/* Return a function call to the appropriate builtin alloca variant.
+
+   SIZE is the size to be allocated.  ALIGN, if non-zero, is the requested
+   alignment of the allocated area.  MAX_SIZE, if non-negative, is an upper
+   bound for SIZE in case it is not a fixed value.  */
+
+tree
+build_alloca_call_expr (tree size, unsigned int align, HOST_WIDE_INT max_size)
+{
+  if (max_size >= 0)
+    {
+      tree t = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX);
+      return
+	build_call_expr (t, 3, size, size_int (align), size_int (max_size));
+    }
+  else if (align > 0)
+    {
+      tree t = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN);
+      return build_call_expr (t, 2, size, size_int (align));
+    }
+  else
+    {
+      tree t = builtin_decl_explicit (BUILT_IN_ALLOCA);
+      return build_call_expr (t, 1, size);
+    }
+}
+
 /* Create a new constant string literal and return a char* pointer to it.
    The STRING_CST value is the LEN characters at STR.  */
 tree
Index: tree.h
===================================================================
--- tree.h	(revision 253767)
+++ tree.h	(working copy)
@@ -2396,6 +2396,18 @@ extern machine_mode vector_type_mode (co
 #define DECL_FUNCTION_CODE(NODE) \
   (FUNCTION_DECL_CHECK (NODE)->function_decl.function_code)
 
+/* Test if FCODE is a function code for an alloca operation.  */
+#define ALLOCA_FUNCTION_CODE_P(FCODE)				\
+  ((FCODE) == BUILT_IN_ALLOCA					\
+   || (FCODE) == BUILT_IN_ALLOCA_WITH_ALIGN			\
+   || (FCODE) == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX)
+
+/* Generate case for an alloca operation.  */
+#define CASE_BUILT_IN_ALLOCA			\
+  case BUILT_IN_ALLOCA:				\
+  case BUILT_IN_ALLOCA_WITH_ALIGN:		\
+  case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX
+
 #define DECL_FUNCTION_PERSONALITY(NODE) \
   (FUNCTION_DECL_CHECK (NODE)->function_decl.personality)
 
@@ -4050,6 +4062,7 @@ extern tree build_call_expr_internal_loc
 						tree, int, const tree *);
 extern tree maybe_build_call_expr_loc (location_t, combined_fn, tree,
 				       int, ...);
+extern tree build_alloca_call_expr (tree, unsigned int, HOST_WIDE_INT);
 extern tree build_string_literal (int, const char *);
 
 /* Construct various nodes representing data types.  */
Index: varasm.c
===================================================================
--- varasm.c	(revision 253767)
+++ varasm.c	(working copy)
@@ -2399,8 +2399,7 @@ incorporeal_function_p (tree decl)
       const char *name;
 
       if (DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
-	  && (DECL_FUNCTION_CODE (decl) == BUILT_IN_ALLOCA
-	      || DECL_FUNCTION_CODE (decl) == BUILT_IN_ALLOCA_WITH_ALIGN))
+	  && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (decl)))
 	return true;
 
       name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
/* { dg-do compile } */
/* { dg-require-effective-target alloca } */
/* { dg-options "-Walloca-larger-than=128" } */

void f (void*);

void g (unsigned int n)
{
  f (__builtin_alloca_with_align_and_max (n, 8, 128));
}
-- { dg-do compile }
-- { dg-options "-Wstack-usage=512" }

with Stack_Usage4_Pkg; use Stack_Usage4_Pkg;

procedure Stack_Usage4 is
   BS : Bounded_String := Get;
   S : String := BS.Data (BS.Data'First .. BS.Len);
begin
   null;
end;
package Stack_Usage4_Pkg is

   subtype Name_Index_Type is Natural range 1 .. 63;

   type Bounded_String is record
      Len  : Name_Index_Type;
      Data : String (Name_Index_Type'Range);
   end record;

   function Get return Bounded_String;

end Stack_Usage4_Pkg;

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