Makefile.in (OBJS): Add gimple-ssa-warn-alloca.o.
authorAldy Hernandez <aldyh@redhat.com>
Tue, 18 Oct 2016 21:40:58 +0000 (21:40 +0000)
committerAldy Hernandez <aldyh@gcc.gnu.org>
Tue, 18 Oct 2016 21:40:58 +0000 (21:40 +0000)
* Makefile.in (OBJS): Add gimple-ssa-warn-alloca.o.
* passes.def: Add two instances of pass_walloca.
* tree-pass.h (make_pass_walloca): New.
* gimple-ssa-warn-walloca.c: New file.
* doc/invoke.texi: Document -Walloca, -Walloca-larger-than=, and
-Wvla-larger-than= options.

From-SVN: r241318

22 files changed:
gcc/ChangeLog
gcc/Makefile.in
gcc/c-family/ChangeLog
gcc/c-family/c-opts.c
gcc/c-family/c.opt
gcc/doc/invoke.texi
gcc/gimple-ssa-warn-alloca.c [new file with mode: 0644]
gcc/passes.def
gcc/testsuite/gcc.dg/Walloca-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-10.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-7.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-9.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wvla-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wvla-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wvla-3.c [new file with mode: 0644]
gcc/tree-pass.h

index 6002ddee5bf48d9a686d436c87331390151355dd..2431b30627e3f4351894dd6a80553322602fa4f0 100644 (file)
@@ -1,3 +1,12 @@
+2016-10-18  Aldy Hernandez  <aldyh@redhat.com>
+
+       * Makefile.in (OBJS): Add gimple-ssa-warn-alloca.o.
+       * passes.def: Add two instances of pass_walloca.
+       * tree-pass.h (make_pass_walloca): New.
+       * gimple-ssa-warn-alloca.c: New file.
+       * doc/invoke.texi: Document -Walloca, -Walloca-larger-than=, and
+       -Wvla-larger-than= options.
+
 2016-10-18  Thomas Schwinge  <thomas@codesourcery.com>
 
        * cfg.c (clear_bb_flags): Use FOR_ALL_BB_FN.
index c512cd709703016006af28f8444c7dbc800dcddb..c08da80be2775fa197544b98f5db06b69b12e574 100644 (file)
@@ -1298,6 +1298,7 @@ OBJS = \
        gimple-ssa-split-paths.o \
        gimple-ssa-strength-reduction.o \
        gimple-ssa-sprintf.o \
+       gimple-ssa-warn-alloca.o \
        gimple-streamer-in.o \
        gimple-streamer-out.o \
        gimple-walk.o \
index d5e90905fe8b0902c30fb557cc2b128b30b7e463..f305593006527c840fe63b6a8ce09b7dbb5cb342 100644 (file)
@@ -1,3 +1,9 @@
+2016-06-16  Aldy Hernandez  <aldyh@redhat.com>
+
+       * c.opt (Walloca): New.
+       (Walloca-larger-than=): New.
+       (Wvla-larger-than=): New.
+
 2016-10-17  Marek Polacek  <polacek@redhat.com>
 
        * c-warn.c (find_array_ref_with_const_idx_r): Remove parameter names.
index a0c0c624f96b65749414cad5d516c8e8bfd28d0d..c39930708d6192cb00a956e8dd05f68c13671c41 100644 (file)
@@ -380,6 +380,16 @@ c_common_handle_option (size_t scode, const char *arg, int value,
       cpp_opts->warn_num_sign_change = value;
       break;
 
+    case OPT_Walloca_larger_than_:
+      if (!value)
+       inform (loc, "-Walloca-larger-than=0 is meaningless");
+      break;
+
+    case OPT_Wvla_larger_than_:
+      if (!value)
+       inform (loc, "-Wvla-larger-than=0 is meaningless");
+      break;
+
     case OPT_Wunknown_pragmas:
       /* Set to greater than 1, so that even unknown pragmas in
         system headers will be warned about.  */
index 82c992c1f5ab0a57063b149f36df97b1adc5567c..458d453cdd8c9f9ebc82ab40a7e8d9bf6f1ea8ff 100644 (file)
@@ -295,6 +295,16 @@ Wall
 C ObjC C++ ObjC++ Warning
 Enable most warning messages.
 
+Walloca
+C ObjC C++ ObjC++ Var(warn_alloca) Warning
+Warn on any use of alloca.
+
+Walloca-larger-than=
+C ObjC C++ ObjC++ Var(warn_alloca_limit) Warning Joined RejectNegative UInteger
+-Walloca-larger-than=<number> Warn on unbounded uses of
+alloca, and on bounded uses of alloca whose bound can be larger than
+<number> bytes.
+
 Warray-bounds
 LangEnabledBy(C ObjC C++ ObjC++,Wall)
 ; in common.opt
@@ -1030,6 +1040,12 @@ Wvla
 C ObjC C++ ObjC++ Var(warn_vla) Init(-1) Warning
 Warn if a variable length array is used.
 
+Wvla-larger-than=
+C ObjC C++ ObjC++ Var(warn_vla_limit) Warning Joined RejectNegative UInteger
+-Wvla-larger-than=<number> Warn on unbounded uses of variable-length arrays, and
+on bounded uses of variable-length arrays whose bound can be
+larger than <number> bytes.
+
 Wvolatile-register-var
 C ObjC C++ ObjC++ Var(warn_volatile_register_var) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
 Warn when a register variable is declared volatile.
index 795ad1b63a914de50113cd83a1eb7094306f0634..556ad36be0dda811462affdfbb6b08b3495892ac 100644 (file)
@@ -255,6 +255,7 @@ Objective-C and Objective-C++ Dialects}.
 @gccoptlist{-fsyntax-only  -fmax-errors=@var{n}  -Wpedantic @gol
 -pedantic-errors @gol
 -w  -Wextra  -Wall  -Waddress  -Waggregate-return  @gol
+-Walloca -Walloca-larger-than=@var{n} @gol
 -Wno-aggressive-loop-optimizations -Warray-bounds -Warray-bounds=@var{n} @gol
 -Wno-attributes -Wbool-compare -Wbool-operation @gol
 -Wno-builtin-macro-redefined -Wc90-c99-compat -Wc99-c11-compat @gol
@@ -313,7 +314,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wunused-const-variable -Wunused-const-variable=@var{n} @gol
 -Wunused-but-set-parameter -Wunused-but-set-variable @gol
 -Wuseless-cast -Wvariadic-macros -Wvector-operation-performance @gol
--Wvla -Wvolatile-register-var  -Wwrite-strings @gol
+-Wvla -Wvla-larger-than=@var{n} -Wvolatile-register-var  -Wwrite-strings @gol
 -Wzero-as-null-pointer-constant -Whsa}
 
 @item C and Objective-C-only Warning Options
@@ -4976,6 +4977,73 @@ annotations.
 Warn about overriding virtual functions that are not marked with the override
 keyword.
 
+@item -Walloca
+@opindex Wno-alloca
+@opindex Walloca
+This option warns on all uses of @code{alloca} in the source.
+
+@item -Walloca-larger-than=@var{n}
+This option warns on calls to @code{alloca} that are not bounded by a
+controlling predicate limiting its size to @var{n} bytes, or calls to
+@code{alloca} where the bound is unknown.
+
+For example, a bounded case of @code{alloca} could be:
+
+@smallexample
+void func (size_t n)
+@{
+  void *p;
+  if (n <= 1000)
+    p = alloca (n);
+  else
+    p = malloc (n);
+  f (p);
+@}
+@end smallexample
+
+In the above example, passing @code{-Walloca=1000} would not issue a
+warning because the call to @code{alloca} is known to be at most 1000
+bytes.  However, if @code{-Walloca=500} was passed, the compiler would
+have emitted a warning.
+
+Unbounded uses, on the other hand, are uses of @code{alloca} with no
+controlling predicate verifying its size.  For example:
+
+@smallexample
+void func ()
+@{
+  void *p = alloca (n);
+  f (p);
+@}
+@end smallexample
+
+If @code{-Walloca=500} was passed, the above would trigger a warning,
+but this time because of the lack of bounds checking.
+
+Note, that even seemingly correct code involving signed integers could
+cause a warning:
+
+@smallexample
+void func (signed int n)
+@{
+  if (n < 500)
+    @{
+      p = alloca (n);
+      f (p);
+    @}
+@}
+@end smallexample
+
+In the above example, @var{n} could be negative, causing a larger than
+expected argument to be implicitly casted into the @code{alloca} call.
+
+This option also warns when @code{alloca} is used in a loop.
+
+This warning is not enabled by @option{-Wall}, and is only active when
+@option{-ftree-vrp} is active (default for @option{-O2} and above).
+
+See also @option{-Wvla-larger-than=@var{n}}.
+
 @item -Warray-bounds
 @itemx -Warray-bounds=@var{n}
 @opindex Wno-array-bounds
@@ -6172,9 +6240,25 @@ moving from a moved-from object, this warning can be disabled.
 @item -Wvla
 @opindex Wvla
 @opindex Wno-vla
-Warn if variable length array is used in the code.
+Warn if a variable-length array is used in the code.
 @option{-Wno-vla} prevents the @option{-Wpedantic} warning of
-the variable length array.
+the variable-length array.
+
+@item -Wvla-larger-than=@var{n}
+If this option is used, the compiler will warn on uses of
+variable-length arrays where the size is either unbounded, or bounded
+by an argument that can be larger than @var{n} bytes.  This is similar
+to how @option{-Walloca-larger-than=@var{n}} works, but with
+variable-length arrays.
+
+Note that GCC may optimize small variable-length arrays of a known
+value into plain arrays, so this warning may not get triggered for
+such arrays.
+
+This warning is not enabled by @option{-Wall}, and is only active when
+@option{-ftree-vrp} is active (default for @option{-O2} and above).
+
+See also @option{-Walloca-larger-than=@var{n}}.
 
 @item -Wvolatile-register-var
 @opindex Wvolatile-register-var
diff --git a/gcc/gimple-ssa-warn-alloca.c b/gcc/gimple-ssa-warn-alloca.c
new file mode 100644 (file)
index 0000000..e75f2fa
--- /dev/null
@@ -0,0 +1,524 @@
+/* Warn on problematic uses of alloca and variable length arrays.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Aldy Hernandez <aldyh@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "diagnostic-core.h"
+#include "fold-const.h"
+#include "gimple-iterator.h"
+#include "tree-ssa.h"
+#include "params.h"
+#include "tree-cfg.h"
+#include "calls.h"
+#include "cfgloop.h"
+#include "intl.h"
+
+const pass_data pass_data_walloca = {
+  GIMPLE_PASS,
+  "walloca",
+  OPTGROUP_NONE,
+  TV_NONE,
+  PROP_cfg, // properties_required
+  0,       // properties_provided
+  0,       // properties_destroyed
+  0,       // properties_start
+  0,       // properties_finish
+};
+
+class pass_walloca : public gimple_opt_pass
+{
+public:
+  pass_walloca (gcc::context *ctxt)
+    : gimple_opt_pass(pass_data_walloca, ctxt), first_time_p (false)
+  {}
+  opt_pass *clone () { return new pass_walloca (m_ctxt); }
+  void set_pass_param (unsigned int n, bool param)
+    {
+      gcc_assert (n == 0);
+      first_time_p = param;
+    }
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *);
+
+ private:
+  // Set to TRUE the first time we run this pass on a function.
+  bool first_time_p;
+};
+
+bool
+pass_walloca::gate (function *fun ATTRIBUTE_UNUSED)
+{
+  // The first time this pass is called, it is called before
+  // optimizations have been run and range information is unavailable,
+  // so we can only perform strict alloca checking.
+  if (first_time_p)
+    return warn_alloca != 0;
+
+  return warn_alloca_limit > 0 || warn_vla_limit > 0;
+}
+
+// Possible problematic uses of alloca.
+enum alloca_type {
+  // Alloca argument is within known bounds that are appropriate.
+  ALLOCA_OK,
+
+  // Alloca argument is KNOWN to have a value that is too large.
+  ALLOCA_BOUND_DEFINITELY_LARGE,
+
+  // Alloca argument may be too large.
+  ALLOCA_BOUND_MAYBE_LARGE,
+
+  // Alloca argument is bounded but of an indeterminate size.
+  ALLOCA_BOUND_UNKNOWN,
+
+  // Alloca argument was casted from a signed integer.
+  ALLOCA_CAST_FROM_SIGNED,
+
+  // Alloca appears in a loop.
+  ALLOCA_IN_LOOP,
+
+  // Alloca argument is 0.
+  ALLOCA_ARG_IS_ZERO,
+
+  // Alloca call is unbounded.  That is, there is no controlling
+  // predicate for its argument.
+  ALLOCA_UNBOUNDED
+};
+
+// Type of an alloca call with its corresponding limit, if applicable.
+struct alloca_type_and_limit {
+  enum alloca_type type;
+  // For ALLOCA_BOUND_MAYBE_LARGE and ALLOCA_BOUND_DEFINITELY_LARGE
+  // types, this field indicates the assumed limit if known or
+  // integer_zero_node if unknown.  For any other alloca types, this
+  // field is undefined.
+  wide_int limit;
+  alloca_type_and_limit ();
+  alloca_type_and_limit (enum alloca_type type,
+                        wide_int i) : type(type), limit(i) { }
+  alloca_type_and_limit (enum alloca_type type) : type(type) { }
+};
+
+// NOTE: When we get better range info, this entire function becomes
+// irrelevant, as it should be possible to get range info for an SSA
+// name at any point in the program.
+//
+// We have a few heuristics up our sleeve to determine if a call to
+// alloca() is within bounds.  Try them out and return the type of
+// alloca call with its assumed limit (if applicable).
+//
+// Given a known argument (ARG) to alloca() and an EDGE (E)
+// calculating said argument, verify that the last statement in the BB
+// in E->SRC is a gate comparing ARG to an acceptable bound for
+// alloca().  See examples below.
+//
+// If set, ARG_CASTED is the possible unsigned argument to which ARG
+// was casted to.  This is to handle cases where the controlling
+// predicate is looking at a casted value, not the argument itself.
+//    arg_casted = (size_t) arg;
+//    if (arg_casted < N)
+//      goto bb3;
+//    else
+//      goto bb5;
+//
+// MAX_SIZE is WARN_ALLOCA= adjusted for VLAs.  It is the maximum size
+// in bytes we allow for arg.
+
+static struct alloca_type_and_limit
+alloca_call_type_by_arg (tree arg, tree arg_casted, edge e, unsigned max_size)
+{
+  basic_block bb = e->src;
+  gimple_stmt_iterator gsi = gsi_last_bb (bb);
+  gimple *last = gsi_stmt (gsi);
+  if (!last || gimple_code (last) != GIMPLE_COND)
+    return alloca_type_and_limit (ALLOCA_UNBOUNDED);
+
+  enum tree_code cond_code = gimple_cond_code (last);
+  if (e->flags & EDGE_TRUE_VALUE)
+    ;
+  else if (e->flags & EDGE_FALSE_VALUE)
+    cond_code = invert_tree_comparison (cond_code, false);
+  else
+    return alloca_type_and_limit (ALLOCA_UNBOUNDED);
+
+  // Check for:
+  //   if (ARG .COND. N)
+  //     goto <bb 3>;
+  //   else
+  //     goto <bb 4>;
+  //   <bb 3>:
+  //   alloca(ARG);
+  if ((cond_code == LE_EXPR
+       || cond_code == LT_EXPR
+       || cond_code == GT_EXPR
+       || cond_code == GE_EXPR)
+      && (gimple_cond_lhs (last) == arg
+         || gimple_cond_lhs (last) == arg_casted))
+    {
+      if (TREE_CODE (gimple_cond_rhs (last)) == INTEGER_CST)
+       {
+         tree rhs = gimple_cond_rhs (last);
+         int tst = wi::cmpu (wi::to_widest (rhs), max_size);
+         if ((cond_code == LT_EXPR && tst == -1)
+             || (cond_code == LE_EXPR && (tst == -1 || tst == 0)))
+           return alloca_type_and_limit (ALLOCA_OK);
+         else
+           {
+             // Let's not get too specific as to how large the limit
+             // may be.  Someone's clearly an idiot when things
+             // degrade into "if (N > Y) alloca(N)".
+             if (cond_code == GT_EXPR || cond_code == GE_EXPR)
+               rhs = integer_zero_node;
+             return alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE, rhs);
+           }
+       }
+      else
+       return alloca_type_and_limit (ALLOCA_BOUND_UNKNOWN);
+    }
+
+  // Similarly, but check for a comparison with an unknown LIMIT.
+  //   if (LIMIT .COND. ARG)
+  //     alloca(arg);
+  //
+  //   Where LIMIT has a bound of unknown range.
+  //
+  // Note: All conditions of the form (ARG .COND. XXXX) where covered
+  // by the previous check above, so we only need to look for (LIMIT
+  // .COND. ARG) here.
+  tree limit = gimple_cond_lhs (last);
+  if ((gimple_cond_rhs (last) == arg
+       || gimple_cond_rhs (last) == arg_casted)
+      && TREE_CODE (limit) == SSA_NAME)
+    {
+      wide_int min, max;
+      value_range_type range_type = get_range_info (limit, &min, &max);
+
+      if (range_type == VR_UNDEFINED || range_type == VR_VARYING)
+       return alloca_type_and_limit (ALLOCA_BOUND_UNKNOWN);
+
+      // ?? It looks like the above `if' is unnecessary, as we never
+      // get any VR_RANGE or VR_ANTI_RANGE here.  If we had a range
+      // for LIMIT, I suppose we would have taken care of it in
+      // alloca_call_type(), or handled above where we handle (ARG .COND. N).
+      //
+      // If this ever triggers, we should probably figure out why and
+      // handle it, though it is likely to be just an ALLOCA_UNBOUNDED.
+      return alloca_type_and_limit (ALLOCA_UNBOUNDED);
+    }
+
+  return alloca_type_and_limit (ALLOCA_UNBOUNDED);
+}
+
+// Return TRUE if SSA's definition is a cast from a signed type.
+// If so, set *INVALID_CASTED_TYPE to the signed type.
+
+static bool
+cast_from_signed_p (tree ssa, tree *invalid_casted_type)
+{
+  gimple *def = SSA_NAME_DEF_STMT (ssa);
+  if (def
+      && !gimple_nop_p (def)
+      && gimple_assign_cast_p (def)
+      && !TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (def))))
+    {
+      *invalid_casted_type = TREE_TYPE (gimple_assign_rhs1 (def));
+      return true;
+    }
+  return false;
+}
+
+// Return TRUE if X has a maximum range of MAX, basically covering the
+// entire domain, in which case it's no range at all.
+
+static bool
+is_max (tree x, wide_int max)
+{
+  return wi::max_value (TREE_TYPE (x)) == 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.
+//
+// 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
+// problematic signed type.
+
+static struct alloca_type_and_limit
+alloca_call_type (gimple *stmt, bool is_vla, tree *invalid_casted_type)
+{
+  gcc_assert (gimple_alloca_call_p (stmt));
+  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);
+
+  gcc_assert (!is_vla || warn_vla_limit > 0);
+  gcc_assert (is_vla || warn_alloca_limit > 0);
+
+  // Adjust warn_alloca_max_size for VLAs, by taking the underlying
+  // type into account.
+  unsigned HOST_WIDE_INT max_size;
+  if (is_vla)
+    max_size = (unsigned HOST_WIDE_INT) warn_vla_limit;
+  else
+    max_size = (unsigned HOST_WIDE_INT) warn_alloca_limit;
+
+  // Check for the obviously bounded case.
+  if (TREE_CODE (len) == INTEGER_CST)
+    {
+      if (tree_to_uhwi (len) > max_size)
+       return alloca_type_and_limit (ALLOCA_BOUND_DEFINITELY_LARGE, len);
+      if (integer_zerop (len))
+       return alloca_type_and_limit (ALLOCA_ARG_IS_ZERO);
+      ret = alloca_type_and_limit (ALLOCA_OK);
+    }
+  // Check the range info if available.
+  else if (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);
+         else
+           {
+             // A cast may have created a range we don't care
+             // about.  For instance, a cast from 16-bit to
+             // 32-bit creates a range of 0..65535, even if there
+             // is not really a determinable range in the
+             // underlying code.  In this case, look through the
+             // cast at the original argument, and fall through
+             // to look at other alternatives.
+             //
+             // We only look at through the cast when its from
+             // unsigned to unsigned, otherwise we may risk
+             // looking at SIGNED_INT < N, which is clearly not
+             // what we want.  In this case, we'd be interested
+             // in a VR_RANGE of [0..N].
+             //
+             // Note: None of this is perfect, and should all go
+             // away with better range information.  But it gets
+             // most of the cases.
+             gimple *def = SSA_NAME_DEF_STMT (len);
+             if (gimple_assign_cast_p (def)
+                 && TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (def))))
+
+               {
+                 len_casted = gimple_assign_rhs1 (def);
+                 range_type = get_range_info (len_casted, &min, &max);
+               }
+             // An unknown range or a range of the entire domain is
+             // really no range at all.
+             if (range_type == VR_VARYING
+                 || (!len_casted && is_max (len, max))
+                 || (len_casted && is_max (len_casted, max)))
+               {
+                 // Fall through.
+               }
+             else if (range_type != VR_VARYING)
+               return
+                 alloca_type_and_limit (ALLOCA_BOUND_MAYBE_LARGE, max);
+           }
+       }
+      else if (range_type == VR_ANTI_RANGE)
+       {
+         // There may be some wrapping around going on.  Catch it
+         // with this heuristic.  Hopefully, this VR_ANTI_RANGE
+         // nonsense will go away, and we won't have to catch the
+         // sign conversion problems with this crap.
+         if (cast_from_signed_p (len, invalid_casted_type))
+           return alloca_type_and_limit (ALLOCA_CAST_FROM_SIGNED);
+       }
+      // No easily determined range and try other things.
+    }
+
+  // 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)
+    {
+      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)
+           return ret;
+       }
+    }
+
+  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.
+
+static bool
+in_loop_p (bool is_vla, 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;
+}
+
+unsigned int
+pass_walloca::execute (function *fun)
+{
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, fun)
+    {
+      for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);
+          gsi_next (&si))
+       {
+         gimple *stmt = gsi_stmt (si);
+         location_t loc = gimple_location (stmt);
+
+         if (!gimple_alloca_call_p (stmt))
+           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));
+
+         // Strict mode whining for VLAs is handled by the front-end,
+         // so we can safely ignore this case.  Also, ignore VLAs if
+         // the user doesn't care about them.
+         if (is_vla
+             && (warn_vla > 0 || !warn_vla_limit))
+           continue;
+
+         if (!is_vla && (warn_alloca || !warn_alloca_limit))
+           {
+             if (warn_alloca)
+               warning_at (loc, OPT_Walloca, G_("use of %<alloca%>"));
+             continue;
+           }
+
+         tree invalid_casted_type = NULL;
+         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))
+           t = alloca_type_and_limit (ALLOCA_IN_LOOP);
+
+         enum opt_code wcode
+           = is_vla ? OPT_Wvla_larger_than_ : OPT_Walloca_larger_than_;
+         char buff[WIDE_INT_MAX_PRECISION / 4 + 4];
+         switch (t.type)
+           {
+           case ALLOCA_OK:
+             break;
+           case ALLOCA_BOUND_MAYBE_LARGE:
+             if (warning_at (loc, wcode,
+                             is_vla ? G_("argument to variable-length array "
+                                         "may be too large")
+                             : G_("argument to %<alloca%> may be too large"))
+                 && t.limit != integer_zero_node)
+               {
+                 print_decu (t.limit, buff);
+                 inform (loc, G_("limit is %u bytes, but argument "
+                                 "may be as large as %s"),
+                         is_vla ? warn_vla_limit : warn_alloca_limit, buff);
+               }
+             break;
+           case ALLOCA_BOUND_DEFINITELY_LARGE:
+             if (warning_at (loc, wcode,
+                             is_vla ? G_("argument to variable-length array "
+                                         "is too large")
+                             : G_("argument to %<alloca%> is too large"))
+                 && t.limit != integer_zero_node)
+               {
+                 print_decu (t.limit, buff);
+                 inform (loc, G_("limit is %u bytes, but argument is %s"),
+                         is_vla ? warn_vla_limit : warn_alloca_limit, buff);
+               }
+             break;
+           case ALLOCA_BOUND_UNKNOWN:
+             warning_at (loc, wcode,
+                         is_vla ? G_("variable-length array bound is unknown")
+                         : G_("%<alloca%> bound is unknown"));
+             break;
+           case ALLOCA_UNBOUNDED:
+             warning_at (loc, wcode,
+                         is_vla ? G_("unbounded use of variable-length array")
+                         : G_("unbounded use of %<alloca%>"));
+             break;
+           case ALLOCA_IN_LOOP:
+             gcc_assert (!is_vla);
+             warning_at (loc, wcode, G_("use of %<alloca%> within a loop"));
+             break;
+           case ALLOCA_CAST_FROM_SIGNED:
+             gcc_assert (invalid_casted_type != NULL_TREE);
+             warning_at (loc, wcode,
+                         is_vla ? G_("argument to variable-length array "
+                                     "may be too large due to "
+                                     "conversion from %qT to %qT")
+                         : G_("argument to %<alloca%> may be too large "
+                              "due to conversion from %qT to %qT"),
+                         invalid_casted_type, size_type_node);
+             break;
+           case ALLOCA_ARG_IS_ZERO:
+             warning_at (loc, wcode,
+                         is_vla ? G_("argument to variable-length array "
+                                     "is zero")
+                         : G_("argument to %<alloca%> is zero"));
+             break;
+           default:
+             gcc_unreachable ();
+           }
+       }
+    }
+  return 0;
+}
+
+gimple_opt_pass *
+make_pass_walloca (gcc::context *ctxt)
+{
+  return new pass_walloca (ctxt);
+}
index 1375254e971fa70e8a8b4d8e50b6b369f8811a0c..51e9aa7eba4af631a7d2c8c8a0ece08ad94e5543 100644 (file)
@@ -44,6 +44,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_expand_omp);
   NEXT_PASS (pass_build_cgraph_edges);
   NEXT_PASS (pass_sprintf_length, false);
+  NEXT_PASS (pass_walloca, /*strict_mode_p=*/true);
   TERMINATE_PASS_LIST (all_lowering_passes)
 
   /* Interprocedural optimization passes.  */
@@ -251,6 +252,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_laddress);
       NEXT_PASS (pass_lim);
       NEXT_PASS (pass_split_crit_edges);
+      NEXT_PASS (pass_walloca, false);
       NEXT_PASS (pass_pre);
       NEXT_PASS (pass_sink_code);
       NEXT_PASS (pass_sancov);
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c
new file mode 100644 (file)
index 0000000..34a20c3
--- /dev/null
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+
+#define alloca __builtin_alloca
+
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen(const char *);
+
+extern void useit (char *);
+
+int num;
+
+void foo1 (size_t len, size_t len2, size_t len3)
+{
+  int i;
+
+  for (i=0; i < 123; ++i)
+    {
+      char *s = alloca (566);  /* { dg-warning "'alloca' within a loop" } */
+      useit (s);
+    }
+
+  char *s = alloca (123);
+  useit (s);                   // OK, constant argument to alloca
+
+  s = alloca (num);            // { dg-warning "large due to conversion" }
+  useit (s);
+
+  s = alloca(90000);           /* { dg-warning "is too large" } */
+  useit (s);
+
+  if (len < 2000)
+    {
+      s = alloca(len);         // OK, bounded
+      useit (s);
+    }
+
+  if (len + len2 < 2000)       // OK, bounded
+    {
+      s = alloca(len + len2);
+      useit (s);
+    }
+
+  if (len3 <= 2001)
+    {
+      s = alloca(len3);                /* { dg-warning "may be too large" } */
+      useit(s);
+    }
+}
+
+void foo2 (__SIZE_TYPE__ len)
+{
+  // Test that a direct call to __builtin_alloca_with_align is not confused
+  // with a VLA.
+  void *p = __builtin_alloca_with_align (len, 8); // { dg-warning "unbounded use of 'alloca'" }
+  useit (p);
+}
+
+void foo3 (unsigned char a)
+{
+  if (a == 0)
+    useit (__builtin_alloca (a)); // { dg-warning "argument to 'alloca' is zero" }
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-10.c b/gcc/testsuite/gcc.dg/Walloca-10.c
new file mode 100644 (file)
index 0000000..69549fc
--- /dev/null
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+
+// Test when the conditionals are incorrectly reversed.
+
+void f (void *);
+void foo (__SIZE_TYPE__ len)
+{
+  void *p;
+  if (len < 500)
+    p = __builtin_malloc (len);
+  else
+    p = __builtin_alloca (len); // { dg-warning "argument to .alloca. may be too large" }
+  f (p);
+}
+
+void bar (__SIZE_TYPE__ len)
+{
+  void *p;
+  if (len > 500)
+    p = __builtin_alloca (len); // { dg-warning "argument to .alloca. may be too large" }
+  else
+    p = __builtin_malloc (len);
+  f (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-2.c b/gcc/testsuite/gcc.dg/Walloca-2.c
new file mode 100644 (file)
index 0000000..284b34e
--- /dev/null
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+
+void f (void *);
+
+void
+g1 (int n)
+{
+  void *p;
+  if (n > 0 && n < 2000)
+    p = __builtin_alloca (n);
+  else
+    p = __builtin_malloc (n);
+  f (p);
+}
+
+void
+g2 (int n)
+{
+  void *p;
+  if (n < 2000)
+    p = __builtin_alloca (n); // { dg-warning "large due to conversion" }
+  else
+    p = __builtin_malloc (n);
+  f (p);
+}
+
+void
+g3 (int n)
+{
+  void *p;
+  if (n > 0 && n < 3000)
+    {
+      p = __builtin_alloca (n); // { dg-warning "'alloca' may be too large" }
+      // { dg-message "note:.*argument may be as large as 2999" "note" { target *-*-* } 34 }
+    }
+  else
+    p = __builtin_malloc (n);
+  f (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-3.c b/gcc/testsuite/gcc.dg/Walloca-3.c
new file mode 100644 (file)
index 0000000..5345197
--- /dev/null
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+
+void f (void *);
+
+__SIZE_TYPE__ LIMIT;
+
+// Warn when there is an alloca bound, but it is an unknown bound.
+
+void
+g1 (__SIZE_TYPE__ n)
+{
+  void *p;
+  if (n < LIMIT)
+    p = __builtin_alloca (n); // { dg-warning "'alloca' bound is unknown" }
+  else
+    p = __builtin_malloc (n);
+  f (p);
+}
+
+// Similar to the above, but do not get confused by the upcast.
+
+unsigned short SHORT_LIMIT;
+void
+g2 (unsigned short n)
+{
+  void *p;
+  if (n < SHORT_LIMIT)
+    p = __builtin_alloca (n); // { dg-warning "'alloca' bound is unknown" }
+  else
+    p = __builtin_malloc (n);
+  f (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-4.c b/gcc/testsuite/gcc.dg/Walloca-4.c
new file mode 100644 (file)
index 0000000..a559534
--- /dev/null
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca-larger-than=5000 -O2" } */
+
+ char *
+ _i18n_number_rewrite (char *w, char *rear_ptr)
+{
+
+  char *src;
+ _Bool 
+      use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U);
+ if (use_alloca)
+    src = (char *) __builtin_alloca ((rear_ptr - w) * sizeof (char));
+  else
+    src = (char *) __builtin_malloc ((rear_ptr - w) * sizeof (char));
+  return src;
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-5.c b/gcc/testsuite/gcc.dg/Walloca-5.c
new file mode 100644 (file)
index 0000000..5ed1171
--- /dev/null
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca-larger-than=123 -O2" } */
+/* { dg-xfail-if "Currently broken but Andrew's work should fix this" { *-*-* } } */
+
+/* The argument to alloca ends up having a range of 0..MAXINT(32bits),
+   so we think we have a range because of the upcast.  Consequently,
+   we warn with "alloca may be too large", but we should technically
+   warn with "unbounded use of alloca".
+
+   We currently drill through casts to figure this stuff out, but we
+   get confused because it's not just a cast.  It's a cast, plus a
+   multiply.
+
+   <bb 2>:
+  # RANGE [0, 4294967295] NONZERO 4294967295
+  _1 = (long unsigned int) something_4(D);
+  # RANGE [0, 34359738360] NONZERO 34359738360
+  _2 = _1 * 8;
+  _3 = __builtin_alloca (_2);
+
+  I don't know whether it's even worth such fine-grained warnings.
+  Perhaps we should generically warn everywhere with "alloca may be
+  too large".
+
+  I'm hoping that this particular case will be easier to diagnose with
+  Andrew's work.  */
+
+void useit(void *);
+void foobar(unsigned int something)
+{
+  useit(__builtin_alloca (something * sizeof (const char *))); // { dg-warning "unbounded use of alloca" "" { xfail *-*-* } }
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-6.c b/gcc/testsuite/gcc.dg/Walloca-6.c
new file mode 100644 (file)
index 0000000..b4d8d41
--- /dev/null
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca-larger-than=256 -O2" } */
+/* { dg-xfail-if "Currently broken but Andrew's work should fix this" { *-*-* } } */
+
+void f (void*);
+void g (__SIZE_TYPE__ n)
+{
+  // No warning on this case.  Range is easily determinable.
+  if (n > 0 && n < 256)
+    f (__builtin_alloca (n));
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-7.c b/gcc/testsuite/gcc.dg/Walloca-7.c
new file mode 100644 (file)
index 0000000..d6581a5
--- /dev/null
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca -O0" } */
+
+extern void f(void *);
+
+void foo(void)
+{
+  // Test that strict -Walloca works even without optimization.
+  f (__builtin_alloca(500)); // { dg-warning "use of 'alloca'" }
+}
+
+void bar(void)
+{
+  // Test that we warn on alloca() calls, not just __builtin_alloca calls.
+  extern void *alloca(__SIZE_TYPE__);
+  f (alloca (123)); // { dg-warning "use of 'alloca'" }
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-8.c b/gcc/testsuite/gcc.dg/Walloca-8.c
new file mode 100644 (file)
index 0000000..a4a1204
--- /dev/null
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+
+void *p;
+void
+foo (__SIZE_TYPE__ len)
+{
+  if (len < 2000 / sizeof (void *))
+    p = __builtin_alloca (len * sizeof (void *));
+  else
+    p = __builtin_malloc (len * sizeof (void *));
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-9.c b/gcc/testsuite/gcc.dg/Walloca-9.c
new file mode 100644 (file)
index 0000000..c67d9d6
--- /dev/null
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca-larger-than=5000 -O2" } */
+
+extern void useit(char *);
+
+int
+foobar (unsigned short length)
+{
+  char *pbuf;
+  __SIZE_TYPE__ size = (__SIZE_TYPE__) length;
+
+  if (size < 4032)
+    pbuf = (char *) __builtin_alloca(size);
+  else
+    pbuf = (char *) __builtin_malloc (size);
+
+  useit(pbuf);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/Wvla-1.c b/gcc/testsuite/gcc.dg/Wvla-1.c
new file mode 100644 (file)
index 0000000..384c930
--- /dev/null
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-Wvla-larger-than=100 -O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void useit (char *);
+
+int num;
+
+void test_vlas (size_t num)
+{
+  char str2[num];              /* { dg-warning "unbounded use" } */
+  useit(str2);
+
+  num = 98;
+  for (int i=0; i < 1234; ++i) {
+    char str[num];             // OK, VLA in a loop, but it is a
+                               // known size *AND* the compiler takes
+                               // care of cleaning up between
+                               // iterations with
+                               // __builtin_stack_restore.
+    useit(str);
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Wvla-2.c b/gcc/testsuite/gcc.dg/Wvla-2.c
new file mode 100644 (file)
index 0000000..96814dc
--- /dev/null
@@ -0,0 +1,70 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -Wvla-larger-than=40" } */
+
+#include <stdint.h>
+
+void f0 (void *);
+void
+f1 (__SIZE_TYPE__ a)
+{
+  if (a <= 10)
+    {
+      // 10 * 4 bytes = 40: OK!
+      uint32_t x[a];
+      f0 (x);
+    }
+}
+
+void
+f2 (__SIZE_TYPE__ a)
+{
+  if (a <= 11)
+    {
+      // 11 * 4 bytes = 44: Not OK.
+      uint32_t x[a]; // { dg-warning "array may be too large" }
+      // { dg-message "note:.*argument may be as large as 44" "note" { target *-*-* } 25 }
+      f0 (x);
+    }
+}
+
+void
+f3 (__SIZE_TYPE__ a, __SIZE_TYPE__ b)
+{
+  if (a <= 5 && b <= 3)
+    {
+      // 5 * 3 * 4 bytes = 60: Not OK.
+      uint32_t x[a][b]; // { dg-warning "array may be too large" }
+      f0 (x);
+    }
+}
+
+void
+f4 (__SIZE_TYPE__ a, __SIZE_TYPE__ b)
+{
+  if (a <= 5 && b <= 2)
+    {
+      // 5 * 2 * 4 bytes = 40 bytes: OK!
+      uint32_t x[a][b];
+      f0 (x);
+    }
+}
+
+void
+f5 (__SIZE_TYPE__ len)
+{
+  // Test that a direct call to __builtin_alloca_with_align is not
+  // confused with a VLA.
+  void *p = __builtin_alloca_with_align (len, 8);
+  f0 (p);
+}
+
+void
+f6 (unsigned stuff)
+{
+  int n = 7000;
+  do {
+    char a[n]; // { dg-warning "variable-length array is too large" }
+    f0 (a);
+  } while (stuff--);
+}
diff --git a/gcc/testsuite/gcc.dg/Wvla-3.c b/gcc/testsuite/gcc.dg/Wvla-3.c
new file mode 100644 (file)
index 0000000..5124476
--- /dev/null
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-Walloca -O2" } */
+
+// Make sure we don't warn on VLA with -Walloca.
+
+void f (void*);
+
+void h1 (unsigned n)
+{
+  int a [n];
+  f (a);
+}
index 5903fde0bb5050e73d74b5346915c0e4fcb5968a..dda693b213eba1fe55e8572fb87d671b84c34632 100644 (file)
@@ -472,6 +472,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_oacc_kernels (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_gen_hsail (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_warn_nonnull_compare (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_sprintf_length (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_walloca (gcc::context *ctxt);
 
 /* IPA Passes */
 extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);