/* __builtin_object_size (ptr, object_size_type) computation
- Copyright (C) 2004-2016 Free Software Foundation, Inc.
+ Copyright (C) 2004-2020 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>
This file is part of GCC.
#include "gimple-fold.h"
#include "gimple-iterator.h"
#include "tree-cfg.h"
+#include "stringpool.h"
+#include "attribs.h"
struct object_size_info
{
int object_size_type;
- bitmap visited, reexamine;
- int pass;
+ unsigned char pass;
bool changed;
+ bitmap visited, reexamine;
unsigned int *depths;
unsigned int *stack, *tos;
};
static tree compute_object_offset (const_tree, const_tree);
static bool addr_object_size (struct object_size_info *,
- const_tree, int, unsigned HOST_WIDE_INT *);
+ const_tree, int, unsigned HOST_WIDE_INT *,
+ tree * = NULL, tree * = NULL);
static unsigned HOST_WIDE_INT alloc_object_size (const gcall *, int);
static tree pass_through_call (const gcall *);
static void collect_object_sizes_for (struct object_size_info *, tree);
static bool
addr_object_size (struct object_size_info *osi, const_tree ptr,
- int object_size_type, unsigned HOST_WIDE_INT *psize)
+ int object_size_type, unsigned HOST_WIDE_INT *psize,
+ tree *pdecl /* = NULL */, tree *poff /* = NULL */)
{
tree pt_var, pt_var_size = NULL_TREE, var_size, bytes;
+ tree dummy_decl, dummy_off = size_zero_node;
+ if (!pdecl)
+ pdecl = &dummy_decl;
+ if (!poff)
+ poff = &dummy_off;
+
gcc_assert (TREE_CODE (ptr) == ADDR_EXPR);
/* Set to unknown and overwrite just before returning if the size
|| TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
{
compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
- object_size_type & ~1, &sz);
+ object_size_type & ~1, &sz, pdecl, poff);
}
else
{
}
if (sz != unknown[object_size_type])
{
- offset_int dsz = wi::sub (sz, mem_ref_offset (pt_var));
- if (wi::neg_p (dsz))
- sz = 0;
- else if (wi::fits_uhwi_p (dsz))
- sz = dsz.to_uhwi ();
+ offset_int mem_offset;
+ if (mem_ref_offset (pt_var).is_constant (&mem_offset))
+ {
+ offset_int dsz = wi::sub (sz, mem_offset);
+ if (wi::neg_p (dsz))
+ sz = 0;
+ else if (wi::fits_uhwi_p (dsz))
+ sz = dsz.to_uhwi ();
+ else
+ sz = unknown[object_size_type];
+ }
else
sz = unknown[object_size_type];
}
&& DECL_P (pt_var)
&& tree_fits_uhwi_p (DECL_SIZE_UNIT (pt_var))
&& tree_to_uhwi (DECL_SIZE_UNIT (pt_var)) < offset_limit)
- pt_var_size = DECL_SIZE_UNIT (pt_var);
+ {
+ *pdecl = pt_var;
+ pt_var_size = DECL_SIZE_UNIT (pt_var);
+ }
else if (pt_var
&& TREE_CODE (pt_var) == STRING_CST
&& TYPE_SIZE_UNIT (TREE_TYPE (pt_var))
bytes = size_zero_node;
else
bytes = size_binop (MINUS_EXPR, var_size, bytes);
+ *poff = bytes;
}
if (var != pt_var
&& pt_var_size
bytes2 = size_zero_node;
else
bytes2 = size_binop (MINUS_EXPR, pt_var_size, bytes2);
+ *poff = size_binop (PLUS_EXPR, *poff, bytes2);
bytes = size_binop (MIN_EXPR, bytes, bytes2);
}
}
/* 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);
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:
- case BUILT_IN_ALLOCA_WITH_ALIGN:
- 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
|| 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)),
static tree
pass_through_call (const gcall *call)
{
- tree callee = gimple_call_fndecl (call);
+ unsigned rf = gimple_call_return_flags (call);
+ if (rf & ERF_RETURNS_ARG)
+ {
+ unsigned argnum = rf & ERF_RETURN_ARG_MASK;
+ if (argnum < gimple_call_num_args (call))
+ return gimple_call_arg (call, argnum);
+ }
- if (callee
- && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
- switch (DECL_FUNCTION_CODE (callee))
- {
- case BUILT_IN_MEMCPY:
- case BUILT_IN_MEMMOVE:
- case BUILT_IN_MEMSET:
- case BUILT_IN_STRCPY:
- case BUILT_IN_STRNCPY:
- case BUILT_IN_STRCAT:
- case BUILT_IN_STRNCAT:
- case BUILT_IN_MEMCPY_CHK:
- case BUILT_IN_MEMMOVE_CHK:
- case BUILT_IN_MEMSET_CHK:
- case BUILT_IN_STRCPY_CHK:
- case BUILT_IN_STRNCPY_CHK:
- case BUILT_IN_STPNCPY_CHK:
- case BUILT_IN_STRCAT_CHK:
- case BUILT_IN_STRNCAT_CHK:
- case BUILT_IN_ASSUME_ALIGNED:
- if (gimple_call_num_args (call) >= 1)
- return gimple_call_arg (call, 0);
- break;
- default:
- break;
- }
+ /* __builtin_assume_aligned is intentionally not marked RET1. */
+ if (gimple_call_builtin_p (call, BUILT_IN_ASSUME_ALIGNED))
+ return gimple_call_arg (call, 0);
return NULL_TREE;
}
/* Compute __builtin_object_size value for PTR and set *PSIZE to
- the resulting value. OBJECT_SIZE_TYPE is the second argument
- to __builtin_object_size. Return true on success and false
- when the object size could not be determined. */
+ the resulting value. If the declared object is known and PDECL
+ is nonnull, sets *PDECL to the object's DECL. OBJECT_SIZE_TYPE
+ is the second argument to __builtin_object_size.
+ Returns true on success and false when the object size could not
+ be determined. */
bool
compute_builtin_object_size (tree ptr, int object_size_type,
- unsigned HOST_WIDE_INT *psize)
+ unsigned HOST_WIDE_INT *psize,
+ tree *pdecl /* = NULL */, tree *poff /* = NULL */)
{
gcc_assert (object_size_type >= 0 && object_size_type <= 3);
+ tree dummy_decl, dummy_off = size_zero_node;
+ if (!pdecl)
+ pdecl = &dummy_decl;
+ if (!poff)
+ poff = &dummy_off;
+
/* Set to unknown and overwrite just before returning if the size
could be determined. */
*psize = unknown[object_size_type];
init_offset_limit ();
if (TREE_CODE (ptr) == ADDR_EXPR)
- return addr_object_size (NULL, ptr, object_size_type, psize);
+ return addr_object_size (NULL, ptr, object_size_type, psize, pdecl, poff);
if (TREE_CODE (ptr) != SSA_NAME
|| !POINTER_TYPE_P (TREE_TYPE (ptr)))
tree offset = gimple_assign_rhs2 (def);
ptr = gimple_assign_rhs1 (def);
- if (cst_and_fits_in_hwi (offset)
- && compute_builtin_object_size (ptr, object_size_type, psize))
+ if (tree_fits_shwi_p (offset)
+ && compute_builtin_object_size (ptr, object_size_type,
+ psize, pdecl, poff))
{
/* Return zero when the offset is out of bounds. */
unsigned HOST_WIDE_INT off = tree_to_shwi (offset);
*psize = off < *psize ? *psize - off : 0;
+ *poff = offset;
return true;
}
}
else
expr_object_size (osi, var, then_);
+ if (object_sizes[object_size_type][varno] == unknown[object_size_type])
+ return reexamine;
+
if (TREE_CODE (else_) == SSA_NAME)
reexamine |= merge_object_sizes (osi, var, else_, 0);
else
/* Destroy data structures after the object size computation. */
-static void
+void
fini_object_sizes (void)
{
int object_size_type;
fprintf (dump_file, "Simplified\n ");
print_gimple_stmt (dump_file, call, 0, dump_flags);
fprintf (dump_file, " to ");
- print_generic_expr (dump_file, result, 0);
+ print_generic_expr (dump_file, result);
fprintf (dump_file, "\n");
}