mirror of git://gcc.gnu.org/git/gcc.git
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
This commit is contained in:
parent
3e6837c2a5
commit
302db8ba61
|
|
@ -1,3 +1,13 @@
|
||||||
|
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>
|
2018-12-14 Martin Sebor <msebor@redhat.com>
|
||||||
|
|
||||||
PR rtl-optimization/87096
|
PR rtl-optimization/87096
|
||||||
|
|
|
||||||
31
gcc/calls.c
31
gcc/calls.c
|
|
@ -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
|
/* Diagnose a call EXP to function FN decorated with attribute alloc_size
|
||||||
whose argument numbers given by IDX with values given by ARGS exceed
|
whose argument numbers given by IDX with values given by ARGS exceed
|
||||||
the maximum object size or cause an unsigned oveflow (wrapping) when
|
the maximum object size or cause an unsigned oveflow (wrapping) when
|
||||||
multiplied. When ARGS[0] is null the function does nothing. ARGS[1]
|
multiplied. FN is null when EXP is a call via a function pointer.
|
||||||
may be null for functions like malloc, and non-null for those like
|
When ARGS[0] is null the function does nothing. ARGS[1] may be null
|
||||||
calloc that are decorated with a two-argument attribute alloc_size. */
|
for functions like malloc, and non-null for those like calloc that
|
||||||
|
are decorated with a two-argument attribute alloc_size. */
|
||||||
|
|
||||||
void
|
void
|
||||||
maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
|
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);
|
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;
|
bool warned = false;
|
||||||
|
|
||||||
/* Validate each argument individually. */
|
/* Validate each argument individually. */
|
||||||
|
|
@ -1382,11 +1385,11 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
|
||||||
friends.
|
friends.
|
||||||
Also avoid issuing the warning for calls to function named
|
Also avoid issuing the warning for calls to function named
|
||||||
"alloca". */
|
"alloca". */
|
||||||
if ((DECL_FUNCTION_CODE (fn) == BUILT_IN_ALLOCA
|
if ((fncode == BUILT_IN_ALLOCA
|
||||||
&& IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6)
|
&& IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6)
|
||||||
|| (DECL_FUNCTION_CODE (fn) != BUILT_IN_ALLOCA
|
|| (fncode != BUILT_IN_ALLOCA
|
||||||
&& !lookup_attribute ("returns_nonnull",
|
&& !lookup_attribute ("returns_nonnull",
|
||||||
TYPE_ATTRIBUTES (TREE_TYPE (fn)))))
|
TYPE_ATTRIBUTES (fntype))))
|
||||||
warned = warning_at (loc, OPT_Walloc_zero,
|
warned = warning_at (loc, OPT_Walloc_zero,
|
||||||
"%Kargument %i value is zero",
|
"%Kargument %i value is zero",
|
||||||
exp, idx[i] + 1);
|
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
|
size overflow. There's no good way to detect C++98 here
|
||||||
so avoid diagnosing these calls for all C++ modes. */
|
so avoid diagnosing these calls for all C++ modes. */
|
||||||
if (i == 0
|
if (i == 0
|
||||||
|
&& fn
|
||||||
&& !args[1]
|
&& !args[1]
|
||||||
&& lang_GNU_CXX ()
|
&& lang_GNU_CXX ()
|
||||||
&& DECL_IS_OPERATOR_NEW (fn)
|
&& 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);
|
location_t fnloc = DECL_SOURCE_LOCATION (fn);
|
||||||
|
|
||||||
|
|
@ -1933,14 +1937,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
|
||||||
|
|
||||||
bitmap_obstack_release (NULL);
|
bitmap_obstack_release (NULL);
|
||||||
|
|
||||||
/* Extract attribute alloc_size and if set, store the indices of
|
/* Extract attribute alloc_size from the type of the called expression
|
||||||
the corresponding arguments in ALLOC_IDX, and then the actual
|
(which could be a function or a function pointer) and if set, store
|
||||||
argument(s) at those indices in ALLOC_ARGS. */
|
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 };
|
int alloc_idx[2] = { -1, -1 };
|
||||||
if (tree alloc_size
|
if (tree alloc_size = lookup_attribute ("alloc_size",
|
||||||
= (fndecl ? lookup_attribute ("alloc_size",
|
TYPE_ATTRIBUTES (fntype)))
|
||||||
TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))
|
|
||||||
: NULL_TREE))
|
|
||||||
{
|
{
|
||||||
tree args = TREE_VALUE (alloc_size);
|
tree args = TREE_VALUE (alloc_size);
|
||||||
alloc_idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
|
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
|
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
|
is either the value of the single function argument specified or the product
|
||||||
of the two function arguments specified. Argument numbering starts at
|
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,
|
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
|
The @code{warn_if_not_aligned} attribute can also be used for types
|
||||||
(@pxref{Common Type Attributes}.)
|
(@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})
|
@item cleanup (@var{cleanup_function})
|
||||||
@cindex @code{cleanup} variable attribute
|
@cindex @code{cleanup} variable attribute
|
||||||
The @code{cleanup} attribute runs a function when the variable goes
|
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}.
|
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
|
@item copy
|
||||||
@itemx copy (@var{expression})
|
@itemx copy (@var{expression})
|
||||||
@cindex @code{copy} type attribute
|
@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})
|
@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
|
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
|
@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
|
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}
|
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
|
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_islessequal
|
||||||
@findex __builtin_islessgreater
|
@findex __builtin_islessgreater
|
||||||
@findex __builtin_isunordered
|
@findex __builtin_isunordered
|
||||||
|
@findex __builtin_object_size
|
||||||
@findex __builtin_powi
|
@findex __builtin_powi
|
||||||
@findex __builtin_powif
|
@findex __builtin_powif
|
||||||
@findex __builtin_powil
|
@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.
|
and GCC does not issue a warning.
|
||||||
@end deftypefn
|
@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)
|
@deftypefn {Built-in Function} double __builtin_huge_val (void)
|
||||||
Returns a positive infinity, if supported by the floating-point format,
|
Returns a positive infinity, if supported by the floating-point format,
|
||||||
else @code{DBL_MAX}. This function is suitable for implementing the
|
else @code{DBL_MAX}. This function is suitable for implementing the
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
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>
|
2018-12-14 Martin Sebor <msebor@redhat.com>
|
||||||
|
|
||||||
PR tree-optimization/87096
|
PR tree-optimization/87096
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
/* 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.
|
/* Compute __builtin_object_size for CALL, which is a GIMPLE_CALL.
|
||||||
Handles various allocation calls. OBJECT_SIZE_TYPE is the second
|
Handles calls to functions declared with attribute alloc_size.
|
||||||
argument from __builtin_object_size. If unknown, return
|
OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
|
||||||
unknown[object_size_type]. */
|
If unknown, return unknown[object_size_type]. */
|
||||||
|
|
||||||
static unsigned HOST_WIDE_INT
|
static unsigned HOST_WIDE_INT
|
||||||
alloc_object_size (const gcall *call, int object_size_type)
|
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));
|
gcc_assert (is_gimple_call (call));
|
||||||
|
|
||||||
callee = gimple_call_fndecl (call);
|
tree calltype;
|
||||||
if (!callee)
|
if (tree callfn = gimple_call_fndecl (call))
|
||||||
|
calltype = TREE_TYPE (callfn);
|
||||||
|
else
|
||||||
|
calltype = gimple_call_fntype (call);
|
||||||
|
|
||||||
|
if (!calltype)
|
||||||
return unknown[object_size_type];
|
return unknown[object_size_type];
|
||||||
|
|
||||||
alloc_size = lookup_attribute ("alloc_size",
|
/* Set to positions of alloc_size arguments. */
|
||||||
TYPE_ATTRIBUTES (TREE_TYPE (callee)));
|
int arg1 = -1, arg2 = -1;
|
||||||
|
tree alloc_size = lookup_attribute ("alloc_size",
|
||||||
|
TYPE_ATTRIBUTES (calltype));
|
||||||
if (alloc_size && TREE_VALUE (alloc_size))
|
if (alloc_size && TREE_VALUE (alloc_size))
|
||||||
{
|
{
|
||||||
tree p = 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;
|
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)
|
if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call)
|
||||||
|| TREE_CODE (gimple_call_arg (call, arg1)) != INTEGER_CST
|
|| TREE_CODE (gimple_call_arg (call, arg1)) != INTEGER_CST
|
||||||
|| (arg2 >= 0
|
|| (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)))
|
|| TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
|
||||||
return unknown[object_size_type];
|
return unknown[object_size_type];
|
||||||
|
|
||||||
|
tree bytes = NULL_TREE;
|
||||||
if (arg2 >= 0)
|
if (arg2 >= 0)
|
||||||
bytes = size_binop (MULT_EXPR,
|
bytes = size_binop (MULT_EXPR,
|
||||||
fold_convert (sizetype, gimple_call_arg (call, arg1)),
|
fold_convert (sizetype, gimple_call_arg (call, arg1)),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue