Makefile.in (OBJS): Add gimple-ssa-warn-alloca.o.

* 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
This commit is contained in:
Aldy Hernandez 2016-10-18 21:40:58 +00:00 committed by Aldy Hernandez
parent c354ab1fb2
commit adc577c558
22 changed files with 1030 additions and 3 deletions

View 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.

View 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 \

View 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.

View 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. */

View 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.

View 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

View File

@ -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);
}

View 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);

View File

@ -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" }
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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 *-*-* } }
}

View File

@ -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));
}

View File

@ -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'" }
}

View File

@ -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 *));
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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--);
}

View File

@ -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);
}

View 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);