Commit 302db8ba by Martin Sebor Committed by Martin Sebor

PR tree-optimization/88372 - alloc_size attribute is ignored on function pointers

gcc/ChangeLog:

	PR tree-optimization/88372
	* calls.c (maybe_warn_alloc_args_overflow): Handle function pointers.
	* tree-object-size.c (alloc_object_size): Same.  Simplify.
	* doc/extend.texi (Object Size Checking): Update.
	(Other Builtins): Add __builtin_object_size.
	(Common Type Attributes): Add alloc_size.
	(Common Variable Attributes): Ditto.

gcc/testsuite/ChangeLog:

	PR tree-optimization/88372
	* gcc.dg/Walloc-size-larger-than-18.c: New test.
	* gcc.dg/builtin-object-size-19.c: Same.

From-SVN: r267158
parent 3e6837c2
2018-12-14 Martin Sebor <msebor@redhat.com>
PR tree-optimization/88372
* calls.c (maybe_warn_alloc_args_overflow): Handle function pointers.
* tree-object-size.c (alloc_object_size): Same. Simplify.
* doc/extend.texi (Object Size Checking): Update.
(Other Builtins): Add __builtin_object_size.
(Common Type Attributes): Add alloc_size.
(Common Variable Attributes): Ditto.
2018-12-14 Martin Sebor <msebor@redhat.com>
PR rtl-optimization/87096
* gimple-ssa-sprintf.c (sprintf_dom_walker::handle_gimple_call): Avoid
folding calls whose bound may exceed INT_MAX. Diagnose bound ranges
......@@ -1342,9 +1342,10 @@ get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
/* Diagnose a call EXP to function FN decorated with attribute alloc_size
whose argument numbers given by IDX with values given by ARGS exceed
the maximum object size or cause an unsigned oveflow (wrapping) when
multiplied. When ARGS[0] is null the function does nothing. ARGS[1]
may be null for functions like malloc, and non-null for those like
calloc that are decorated with a two-argument attribute alloc_size. */
multiplied. FN is null when EXP is a call via a function pointer.
When ARGS[0] is null the function does nothing. ARGS[1] may be null
for functions like malloc, and non-null for those like calloc that
are decorated with a two-argument attribute alloc_size. */
void
maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
......@@ -1357,6 +1358,8 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
location_t loc = EXPR_LOCATION (exp);
tree fntype = fn ? TREE_TYPE (fn) : TREE_TYPE (TREE_TYPE (exp));
built_in_function fncode = fn ? DECL_FUNCTION_CODE (fn) : BUILT_IN_NONE;
bool warned = false;
/* Validate each argument individually. */
......@@ -1382,11 +1385,11 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
friends.
Also avoid issuing the warning for calls to function named
"alloca". */
if ((DECL_FUNCTION_CODE (fn) == BUILT_IN_ALLOCA
if ((fncode == BUILT_IN_ALLOCA
&& IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6)
|| (DECL_FUNCTION_CODE (fn) != BUILT_IN_ALLOCA
|| (fncode != BUILT_IN_ALLOCA
&& !lookup_attribute ("returns_nonnull",
TYPE_ATTRIBUTES (TREE_TYPE (fn)))))
TYPE_ATTRIBUTES (fntype))))
warned = warning_at (loc, OPT_Walloc_zero,
"%Kargument %i value is zero",
exp, idx[i] + 1);
......@@ -1398,6 +1401,7 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
size overflow. There's no good way to detect C++98 here
so avoid diagnosing these calls for all C++ modes. */
if (i == 0
&& fn
&& !args[1]
&& lang_GNU_CXX ()
&& DECL_IS_OPERATOR_NEW (fn)
......@@ -1481,7 +1485,7 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
}
}
if (warned)
if (warned && fn)
{
location_t fnloc = DECL_SOURCE_LOCATION (fn);
......@@ -1933,14 +1937,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
bitmap_obstack_release (NULL);
/* Extract attribute alloc_size and if set, store the indices of
the corresponding arguments in ALLOC_IDX, and then the actual
argument(s) at those indices in ALLOC_ARGS. */
/* Extract attribute alloc_size from the type of the called expression
(which could be a function or a function pointer) and if set, store
the indices of the corresponding arguments in ALLOC_IDX, and then
the actual argument(s) at those indices in ALLOC_ARGS. */
int alloc_idx[2] = { -1, -1 };
if (tree alloc_size
= (fndecl ? lookup_attribute ("alloc_size",
TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))
: NULL_TREE))
if (tree alloc_size = lookup_attribute ("alloc_size",
TYPE_ATTRIBUTES (fntype)))
{
tree args = TREE_VALUE (alloc_size);
alloc_idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
......
......@@ -2522,7 +2522,7 @@ The function parameter(s) denoting the allocated size are specified by
one or two integer arguments supplied to the attribute. The allocated size
is either the value of the single function argument specified or the product
of the two function arguments specified. Argument numbering starts at
one.
one for ordinary functions, and at two for C++ non-static member functions.
For instance,
......@@ -6343,6 +6343,34 @@ This warning can be disabled by @option{-Wno-if-not-aligned}.
The @code{warn_if_not_aligned} attribute can also be used for types
(@pxref{Common Type Attributes}.)
@item alloc_size (@var{position})
@itemx alloc_size (@var{position-1}, @var{position-2})
@cindex @code{alloc_size} variable attribute
The @code{alloc_size} variable attribute may be applied to the declaration
of a pointer to a function that returns a pointer and takes at least one
argument of an integer type. It indicates that the returned pointer points
to an object whose size is given by the function argument at @var{position-1},
or by the product of the arguments at @var{position-1} and @var{position-2}.
Meaningful sizes are positive values less than @code{PTRDIFF_MAX}. Other
sizes are disagnosed when detected. GCC uses this information to improve
the results of @code{__builtin_object_size}.
For instance, the following declarations
@smallexample
typedef __attribute__ ((alloc_size (1, 2))) void*
(*calloc_ptr) (size_t, size_t);
typedef __attribute__ ((alloc_size (1))) void*
(*malloc_ptr) (size_t);
@end smallexample
@noindent
specify that @code{calloc_ptr} is a pointer of a function that, like
the standard C function @code{calloc}, returns an object whose size
is given by the product of arguments 1 and 2, and similarly, that
@code{malloc_ptr}, like the standard C function @code{malloc},
returns an object whose size is given by argument 1 to the function.
@item cleanup (@var{cleanup_function})
@cindex @code{cleanup} variable attribute
The @code{cleanup} attribute runs a function when the variable goes
......@@ -7328,6 +7356,34 @@ struct __attribute__ ((aligned (8))) foo
This warning can be disabled by @option{-Wno-if-not-aligned}.
@item alloc_size (@var{position})
@itemx alloc_size (@var{position-1}, @var{position-2})
@cindex @code{alloc_size} type attribute
The @code{alloc_size} type attribute may be applied to the definition
of a type of a function that returns a pointer and takes at least one
argument of an integer type. It indicates that the returned pointer
points to an object whose size is given by the function argument at
@var{position-1}, or by the product of the arguments at @var{position-1}
and @var{position-2}. Meaningful sizes are positive values less than
@code{PTRDIFF_MAX}. Other sizes are disagnosed when detected. GCC uses
this information to improve the results of @code{__builtin_object_size}.
For instance, the following declarations
@smallexample
typedef __attribute__ ((alloc_size (1, 2))) void*
calloc_type (size_t, size_t);
typedef __attribute__ ((alloc_size (1))) void*
malloc_type (size_t);
@end smallexample
@noindent
specify that @code{calloc_type} is a type of a function that, like
the standard C function @code{calloc}, returns an object whose size
is given by the product of arguments 1 and 2, and that
@code{malloc_type}, like the standard C function @code{malloc},
returns an object whose size is given by argument 1 to the function.
@item copy
@itemx copy (@var{expression})
@cindex @code{copy} type attribute
......@@ -11193,7 +11249,10 @@ a limited extent, they can be used without optimization as well.
@deftypefn {Built-in Function} {size_t} __builtin_object_size (const void * @var{ptr}, int @var{type})
is a built-in construct that returns a constant number of bytes from
@var{ptr} to the end of the object @var{ptr} pointer points to
(if known at compile time). @code{__builtin_object_size} never evaluates
(if known at compile time). To determine the sizes of dynamically allocated
objects the function relies on the allocation functions called to obtain
the storage to be declared with the @code{alloc_size} attribute (@xref{Common
Function Attributes}). @code{__builtin_object_size} never evaluates
its arguments for side effects. If there are any side effects in them, it
returns @code{(size_t) -1} for @var{type} 0 or 1 and @code{(size_t) 0}
for @var{type} 2 or 3. If there are multiple objects @var{ptr} can
......@@ -11318,6 +11377,7 @@ is called and the @var{flag} argument passed to it.
@findex __builtin_islessequal
@findex __builtin_islessgreater
@findex __builtin_isunordered
@findex __builtin_object_size
@findex __builtin_powi
@findex __builtin_powif
@findex __builtin_powil
......@@ -12561,6 +12621,10 @@ is evaluated if it includes side effects but no other code is generated
and GCC does not issue a warning.
@end deftypefn
@deftypefn {Built-in Function}{size_t} __builtin_object_size (const void * @var{ptr}, int @var{type})
Returns the size of an object pointed to by @var{ptr}. @xref{Object Size Checking} for a detailed description of the function.
@end deftypefn
@deftypefn {Built-in Function} double __builtin_huge_val (void)
Returns a positive infinity, if supported by the floating-point format,
else @code{DBL_MAX}. This function is suitable for implementing the
......
2018-12-14 Martin Sebor <msebor@redhat.com>
PR tree-optimization/88372
* gcc.dg/Walloc-size-larger-than-18.c: New test.
* gcc.dg/builtin-object-size-19.c: Same.
2018-12-14 Martin Sebor <msebor@redhat.com>
PR tree-optimization/87096
* gcc.dg/tree-ssa/builtin-snprintf-4.c: New test.
......
/* PR tree-optimization/88372 - alloc_size attribute is ignored
on function pointers
Verify that calls via function pointers declared alloc_size
with zero or excessive size trigger either -Walloc-zero or
-Walloc-size-larger-than warnings.
{ dg-do compile }
{ dg-options "-O2 -Wall -Walloc-zero -ftrack-macro-expansion=0" } */
#define ATTR(...) __attribute__ ((__VA_ARGS__))
typedef __SIZE_TYPE__ size_t;
void sink (void*);
#define T(call) sink (call)
ATTR (alloc_size (1)) void* (*ai1)(int, int);
ATTR (alloc_size (2)) void* (*ai2)(int, int);
ATTR (alloc_size (1, 2)) void* (*ai1_2)(int, int);
ATTR (alloc_size (1)) void* (*asz1)(size_t, size_t);
ATTR (alloc_size (2)) void* (*asz2)(size_t, size_t);
ATTR (alloc_size (1, 2)) void* (*asz1_2)(size_t, size_t);
void test_alloc_ptr_zero (void)
{
T (asz1 (0, 0)); /* { dg-warning "argument 1 value is zero" } */
T (asz1 (0, 1)); /* { dg-warning "argument 1 value is zero" } */
T (asz1 (1, 0));
T (asz1 (1, 1));
T (asz2 (0, 0)); /* { dg-warning "argument 2 value is zero" } */
T (asz2 (0, 1));
T (asz2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */
T (asz2 (1, 1));
T (asz1_2 (0, 0)); /* { dg-warning "argument \[12\] value is zero" } */
T (asz1_2 (1, 0)); /* { dg-warning "argument 2 value is zero" } */
T (asz1_2 (0, 1)); /* { dg-warning "argument 1 value is zero" } */
T (asz1_2 (1, 1));
}
void test_alloc_ptr_negative (int n)
{
T (ai1 (-1, -1)); /* { dg-warning "argument 1 value .-1. is negative" } */
T (ai1 (-2, 1)); /* { dg-warning "argument 1 value .-2. is negative" } */
T (ai1 ( 1, -1));
T (ai1 ( 1, 1));
T (ai2 (-1, -3)); /* { dg-warning "argument 2 value .-3. is negative" } */
T (ai2 (-1, 1));
T (ai2 ( 1, -4)); /* { dg-warning "argument 2 value .-4. is negative" } */
T (ai2 ( 1, 1));
T (ai1_2 (-5, -6)); /* { dg-warning "argument \[12\] value .-\[56\]. is negative" } */
T (ai1_2 ( 1, -7)); /* { dg-warning "argument 2 value .-7. is negative" } */
T (ai1_2 (-8, 1)); /* { dg-warning "argument 1 value .-8. is negative" } */
T (ai1_2 ( 1, 1));
if (n > -1)
n = -1;
/* Also verify a simple range. */
T (ai1_2 ( 1, n)); /* { dg-warning "argument 2 range \\\[-\[0-9\]+, -1] is negative" } */
T (ai1_2 ( n, 1)); /* { dg-warning "argument 1 range \\\[-\[0-9\]+, -1] is negative" } */
}
void test_alloc_ptr_too_big (void)
{
size_t x = (__SIZE_MAX__ >> 1) + 1;
size_t y = __SIZE_MAX__ / 5;
T (asz1 (x, x)); /* { dg-warning "argument 1 value .\[0-9\]+. exceeds" } */
T (asz1 (x, 1)); /* { dg-warning "argument 1 value .\[0-9\]+. exceeds" } */
T (asz1 (1, x));
T (asz1 (1, 1));
T (asz2 (x, x)); /* { dg-warning "argument 2 value .\[0-9\]+. exceeds" } */
T (asz2 (x, 1));
T (asz2 (1, x)); /* { dg-warning "argument 2 value .\[0-9\]+. exceeds" } */
T (asz2 (1, 1));
T (asz1_2 (x, x)); /* { dg-warning "argument \[12\] value .\[0-9\]+. exceeds" } */
T (asz1_2 (y, 3)); /* { dg-warning "product .\[0-9\]+ \\\* 3. of arguments 1 and 2 exceeds" } */
T (asz1_2 (y, y)); /* { dg-warning "product .\[0-9\]+ \\\* \[0-9\]+. of arguments 1 and 2 exceeds" } */
T (asz1_2 (1, x)); /* { dg-warning "argument 2 value .\[0-9\]+. exceeds" } */
T (asz1_2 (x, 1)); /* { dg-warning "argument 1 value .\[0-9\]+. exceeds" } */
T (asz1_2 (1, 1));
}
/* PR tree-optimization/88372 - alloc_size attribute is ignored
on function pointers { dg-do compile }
{ dg-options "-O2 -fdump-tree-optimized" } */
typedef __SIZE_TYPE__ size_t;
#define ATTR(...) __attribute__ ((__VA_ARGS__))
#define CONCAT(x, y) x ## y
#define CAT(x, y) CONCAT (x, y)
#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
#define FAIL(name) do { \
extern void FAILNAME (name) (void); \
FAILNAME (name)(); \
} while (0)
/* Macro to emit a call to function named
call_in_true_branch_not_eliminated_on_line_NNN()
for each call that's expected to be eliminated. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that no such call appears in output. */
#define ELIM(expr) \
if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
void sink (void*);
#define T(alloc, n) do { \
void *p = alloc; \
sink (p); \
ELIM (n == __builtin_object_size (p, 0)); \
ELIM (n == __builtin_object_size (p, 1)); \
ELIM (n == __builtin_object_size (p, 2)); \
ELIM (n == __builtin_object_size (p, 3)); \
} while (0)
ATTR (alloc_size (1)) void* (*alloc_1_x)(size_t, size_t);
ATTR (alloc_size (2)) void* (*alloc_x_2)(size_t, size_t);
/* Verify that things work when attribute alloc_size is applied
to a typedef that is then used to declared a pointer. */
typedef ATTR (alloc_size (1, 2)) void* (alloc_1_2_t)(size_t, size_t);
void test_alloc_ptr (alloc_1_2_t *alloc_1_2)
{
T (alloc_1_x (0, 0), 0);
T (alloc_1_x (1, 0), 1);
T (alloc_1_x (3, 0), 3);
T (alloc_1_x (9, 5), 9);
T (alloc_x_2 (0, 0), 0);
T (alloc_x_2 (1, 0), 0);
T (alloc_x_2 (0, 1), 1);
T (alloc_x_2 (9, 5), 5);
T (alloc_1_2 (0, 0), 0);
T (alloc_1_2 (1, 0), 0);
T (alloc_1_2 (0, 1), 0);
T (alloc_1_2 (9, 5), 45);
}
/* Verify that object size is detected even in indirect calls via
function pointers to built-in allocation functions, even without
explicit use of attribute alloc_size on the pointers. */
typedef void *(allocfn_1) (size_t);
typedef void *(allocfn_1_2) (size_t, size_t);
static inline void *
call_alloc (allocfn_1 *fn1, allocfn_1_2 *fn2, size_t n1, size_t n2)
{
return fn1 ? fn1 (n1) : fn2 (n1, n2);
}
static inline void *
call_malloc (size_t n)
{
return call_alloc (__builtin_malloc, 0, n, 0);
}
static inline void *
call_calloc (size_t n1, size_t n2)
{
return call_alloc (0, __builtin_calloc, n1, n2);
}
void test_builtin_ptr (void)
{
T (call_malloc (0), 0);
T (call_malloc (1), 1);
T (call_malloc (9), 9);
T (call_calloc (0, 0), 0);
T (call_calloc (0, 1), 0);
T (call_calloc (1, 0), 0);
T (call_calloc (1, 1), 1);
T (call_calloc (1, 3), 3);
T (call_calloc (2, 3), 6);
}
/* { dg-final { scan-tree-dump-not "not_eliminated" "optimized" } } */
......@@ -401,25 +401,28 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
/* Compute __builtin_object_size for CALL, which is a GIMPLE_CALL.
Handles various allocation calls. OBJECT_SIZE_TYPE is the second
argument from __builtin_object_size. If unknown, return
unknown[object_size_type]. */
Handles calls to functions declared with attribute alloc_size.
OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
If unknown, return unknown[object_size_type]. */
static unsigned HOST_WIDE_INT
alloc_object_size (const gcall *call, int object_size_type)
{
tree callee, bytes = NULL_TREE;
tree alloc_size;
int arg1 = -1, arg2 = -1;
gcc_assert (is_gimple_call (call));
callee = gimple_call_fndecl (call);
if (!callee)
tree calltype;
if (tree callfn = gimple_call_fndecl (call))
calltype = TREE_TYPE (callfn);
else
calltype = gimple_call_fntype (call);
if (!calltype)
return unknown[object_size_type];
alloc_size = lookup_attribute ("alloc_size",
TYPE_ATTRIBUTES (TREE_TYPE (callee)));
/* Set to positions of alloc_size arguments. */
int arg1 = -1, arg2 = -1;
tree alloc_size = lookup_attribute ("alloc_size",
TYPE_ATTRIBUTES (calltype));
if (alloc_size && TREE_VALUE (alloc_size))
{
tree p = TREE_VALUE (alloc_size);
......@@ -429,19 +432,6 @@ alloc_object_size (const gcall *call, int object_size_type)
arg2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (p)))-1;
}
if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (callee))
{
case BUILT_IN_CALLOC:
arg2 = 1;
/* fall through */
case BUILT_IN_MALLOC:
CASE_BUILT_IN_ALLOCA:
arg1 = 0;
default:
break;
}
if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call)
|| TREE_CODE (gimple_call_arg (call, arg1)) != INTEGER_CST
|| (arg2 >= 0
......@@ -449,6 +439,7 @@ alloc_object_size (const gcall *call, int object_size_type)
|| TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
return unknown[object_size_type];
tree bytes = NULL_TREE;
if (arg2 >= 0)
bytes = size_binop (MULT_EXPR,
fold_convert (sizetype, gimple_call_arg (call, arg1)),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment