tree-object-size.c [plain text]
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "tree-pass.h"
#include "tree-ssa-propagate.h"
struct object_size_info
{
int object_size_type;
bitmap visited, reexamine;
int pass;
bool changed;
unsigned int *depths;
unsigned int *stack, *tos;
};
static unsigned HOST_WIDE_INT unknown[4] = { -1, -1, 0, 0 };
static tree compute_object_offset (tree, tree);
static unsigned HOST_WIDE_INT addr_object_size (tree, int);
static unsigned HOST_WIDE_INT alloc_object_size (tree, int);
static tree pass_through_call (tree);
static void collect_object_sizes_for (struct object_size_info *, tree);
static void expr_object_size (struct object_size_info *, tree, tree);
static bool merge_object_sizes (struct object_size_info *, tree, tree,
unsigned HOST_WIDE_INT);
static bool plus_expr_object_size (struct object_size_info *, tree, tree);
static void compute_object_sizes (void);
static void init_offset_limit (void);
static void check_for_plus_in_loops (struct object_size_info *, tree);
static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
unsigned int);
static unsigned HOST_WIDE_INT *object_sizes[4];
static bitmap computed[4];
static unsigned HOST_WIDE_INT offset_limit;
static void
init_offset_limit (void)
{
if (host_integerp (TYPE_MAX_VALUE (sizetype), 1))
offset_limit = tree_low_cst (TYPE_MAX_VALUE (sizetype), 1);
else
offset_limit = -1;
offset_limit /= 2;
}
static tree
compute_object_offset (tree expr, tree var)
{
enum tree_code code = PLUS_EXPR;
tree base, off, t;
if (expr == var)
return size_zero_node;
switch (TREE_CODE (expr))
{
case COMPONENT_REF:
base = compute_object_offset (TREE_OPERAND (expr, 0), var);
if (base == error_mark_node)
return base;
t = TREE_OPERAND (expr, 1);
off = size_binop (PLUS_EXPR, DECL_FIELD_OFFSET (t),
size_int (tree_low_cst (DECL_FIELD_BIT_OFFSET (t), 1)
/ BITS_PER_UNIT));
break;
case REALPART_EXPR:
case NOP_EXPR:
case CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
case NON_LVALUE_EXPR:
return compute_object_offset (TREE_OPERAND (expr, 0), var);
case IMAGPART_EXPR:
base = compute_object_offset (TREE_OPERAND (expr, 0), var);
if (base == error_mark_node)
return base;
off = TYPE_SIZE_UNIT (TREE_TYPE (expr));
break;
case ARRAY_REF:
base = compute_object_offset (TREE_OPERAND (expr, 0), var);
if (base == error_mark_node)
return base;
t = TREE_OPERAND (expr, 1);
if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) < 0)
{
code = MINUS_EXPR;
t = fold (build1 (NEGATE_EXPR, TREE_TYPE (t), t));
}
t = convert (sizetype, t);
off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), t);
break;
default:
return error_mark_node;
}
return size_binop (code, base, off);
}
static unsigned HOST_WIDE_INT
addr_object_size (tree ptr, int object_size_type)
{
tree pt_var;
gcc_assert (TREE_CODE (ptr) == ADDR_EXPR);
pt_var = TREE_OPERAND (ptr, 0);
if (REFERENCE_CLASS_P (pt_var))
pt_var = get_base_address (pt_var);
if (pt_var
&& (SSA_VAR_P (pt_var) || TREE_CODE (pt_var) == STRING_CST)
&& TYPE_SIZE_UNIT (TREE_TYPE (pt_var))
&& host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1)
&& (unsigned HOST_WIDE_INT)
tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1) < offset_limit)
{
tree bytes;
if (pt_var != TREE_OPERAND (ptr, 0))
{
tree var;
if (object_size_type & 1)
{
var = TREE_OPERAND (ptr, 0);
while (var != pt_var
&& TREE_CODE (var) != BIT_FIELD_REF
&& TREE_CODE (var) != COMPONENT_REF
&& TREE_CODE (var) != ARRAY_REF
&& TREE_CODE (var) != ARRAY_RANGE_REF
&& TREE_CODE (var) != REALPART_EXPR
&& TREE_CODE (var) != IMAGPART_EXPR)
var = TREE_OPERAND (var, 0);
if (var != pt_var && TREE_CODE (var) == ARRAY_REF)
var = TREE_OPERAND (var, 0);
if (! TYPE_SIZE_UNIT (TREE_TYPE (var))
|| ! host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (var)), 1)
|| tree_int_cst_lt (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)),
TYPE_SIZE_UNIT (TREE_TYPE (var))))
var = pt_var;
}
else
var = pt_var;
bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
if (bytes != error_mark_node)
{
if (TREE_CODE (bytes) == INTEGER_CST
&& tree_int_cst_lt (TYPE_SIZE_UNIT (TREE_TYPE (var)), bytes))
bytes = size_zero_node;
else
bytes = size_binop (MINUS_EXPR,
TYPE_SIZE_UNIT (TREE_TYPE (var)), bytes);
}
}
else
bytes = TYPE_SIZE_UNIT (TREE_TYPE (pt_var));
if (host_integerp (bytes, 1))
return tree_low_cst (bytes, 1);
}
return unknown[object_size_type];
}
static unsigned HOST_WIDE_INT
alloc_object_size (tree call, int object_size_type)
{
tree callee, arglist, a, bytes = NULL_TREE;
unsigned int arg_mask = 0;
gcc_assert (TREE_CODE (call) == CALL_EXPR);
callee = get_callee_fndecl (call);
arglist = TREE_OPERAND (call, 1);
if (callee
&& DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (callee))
{
case BUILT_IN_MALLOC:
case BUILT_IN_ALLOCA:
arg_mask = 1;
break;
case BUILT_IN_CALLOC:
arg_mask = 3;
break;
default:
break;
}
for (a = arglist; arg_mask && a; arg_mask >>= 1, a = TREE_CHAIN (a))
if (arg_mask & 1)
{
tree arg = TREE_VALUE (a);
if (TREE_CODE (arg) != INTEGER_CST)
break;
if (! bytes)
bytes = fold_convert (sizetype, arg);
else
bytes = size_binop (MULT_EXPR, bytes,
fold_convert (sizetype, arg));
}
if (! arg_mask && bytes && host_integerp (bytes, 1))
return tree_low_cst (bytes, 1);
return unknown[object_size_type];
}
static tree
pass_through_call (tree call)
{
tree callee = get_callee_fndecl (call);
tree arglist = TREE_OPERAND (call, 1);
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_STRCAT_CHK:
case BUILT_IN_STRNCAT_CHK:
if (arglist)
return TREE_VALUE (arglist);
break;
default:
break;
}
return NULL_TREE;
}
unsigned HOST_WIDE_INT
compute_builtin_object_size (tree ptr, int object_size_type)
{
gcc_assert (object_size_type >= 0 && object_size_type <= 3);
if (! offset_limit)
init_offset_limit ();
if (TREE_CODE (ptr) == ADDR_EXPR)
return addr_object_size (ptr, object_size_type);
else if (TREE_CODE (ptr) == CALL_EXPR)
{
tree arg = pass_through_call (ptr);
if (arg)
return compute_builtin_object_size (arg, object_size_type);
else
return alloc_object_size (ptr, object_size_type);
}
else if (TREE_CODE (ptr) == SSA_NAME
&& POINTER_TYPE_P (TREE_TYPE (ptr))
&& object_sizes[object_size_type] != NULL)
{
if (!bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr)))
{
struct object_size_info osi;
bitmap_iterator bi;
unsigned int i;
if (dump_file)
{
fprintf (dump_file, "Computing %s %sobject size for ",
(object_size_type & 2) ? "minimum" : "maximum",
(object_size_type & 1) ? "sub" : "");
print_generic_expr (dump_file, ptr, dump_flags);
fprintf (dump_file, ":\n");
}
osi.visited = BITMAP_ALLOC (NULL);
osi.reexamine = BITMAP_ALLOC (NULL);
osi.object_size_type = object_size_type;
osi.depths = NULL;
osi.stack = NULL;
osi.tos = NULL;
osi.pass = 0;
osi.changed = false;
collect_object_sizes_for (&osi, ptr);
if (! bitmap_empty_p (osi.reexamine))
{
bitmap reexamine = BITMAP_ALLOC (NULL);
if (object_size_type & 2)
{
osi.depths = xcalloc (num_ssa_names, sizeof (unsigned int));
osi.stack = xmalloc (num_ssa_names * sizeof (unsigned int));
osi.tos = osi.stack;
osi.pass = 1;
bitmap_copy (reexamine, osi.reexamine);
EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
if (bitmap_bit_p (osi.reexamine, i))
check_for_plus_in_loops (&osi, ssa_name (i));
free (osi.depths);
osi.depths = NULL;
free (osi.stack);
osi.stack = NULL;
osi.tos = NULL;
}
do
{
osi.pass = 2;
osi.changed = false;
bitmap_copy (reexamine, osi.reexamine);
EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
if (bitmap_bit_p (osi.reexamine, i))
{
collect_object_sizes_for (&osi, ssa_name (i));
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Reexamining ");
print_generic_expr (dump_file, ssa_name (i),
dump_flags);
fprintf (dump_file, "\n");
}
}
}
while (osi.changed);
BITMAP_FREE (reexamine);
}
EXECUTE_IF_SET_IN_BITMAP (osi.reexamine, 0, i, bi)
bitmap_set_bit (computed[object_size_type], i);
if (dump_file)
{
EXECUTE_IF_SET_IN_BITMAP (osi.visited, 0, i, bi)
if (object_sizes[object_size_type][i]
!= unknown[object_size_type])
{
print_generic_expr (dump_file, ssa_name (i),
dump_flags);
fprintf (dump_file,
": %s %sobject size "
HOST_WIDE_INT_PRINT_UNSIGNED "\n",
(object_size_type & 2) ? "minimum" : "maximum",
(object_size_type & 1) ? "sub" : "",
object_sizes[object_size_type][i]);
}
}
BITMAP_FREE (osi.reexamine);
BITMAP_FREE (osi.visited);
}
return object_sizes[object_size_type][SSA_NAME_VERSION (ptr)];
}
return unknown[object_size_type];
}
static void
expr_object_size (struct object_size_info *osi, tree ptr, tree value)
{
int object_size_type = osi->object_size_type;
unsigned int varno = SSA_NAME_VERSION (ptr);
unsigned HOST_WIDE_INT bytes;
gcc_assert (object_sizes[object_size_type][varno]
!= unknown[object_size_type]);
gcc_assert (osi->pass == 0);
if (TREE_CODE (value) == WITH_SIZE_EXPR)
value = TREE_OPERAND (value, 0);
gcc_assert (TREE_CODE (value) != SSA_NAME
|| !POINTER_TYPE_P (TREE_TYPE (value)));
if (TREE_CODE (value) == ADDR_EXPR)
bytes = addr_object_size (value, object_size_type);
else if (TREE_CODE (value) == CALL_EXPR)
bytes = alloc_object_size (value, object_size_type);
else
bytes = unknown[object_size_type];
if ((object_size_type & 2) == 0)
{
if (object_sizes[object_size_type][varno] < bytes)
object_sizes[object_size_type][varno] = bytes;
}
else
{
if (object_sizes[object_size_type][varno] > bytes)
object_sizes[object_size_type][varno] = bytes;
}
}
static bool
merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
unsigned HOST_WIDE_INT offset)
{
int object_size_type = osi->object_size_type;
unsigned int varno = SSA_NAME_VERSION (dest);
unsigned HOST_WIDE_INT orig_bytes;
if (object_sizes[object_size_type][varno] == unknown[object_size_type])
return false;
if (offset >= offset_limit)
{
object_sizes[object_size_type][varno] = unknown[object_size_type];
return false;
}
if (osi->pass == 0)
collect_object_sizes_for (osi, orig);
orig_bytes = object_sizes[object_size_type][SSA_NAME_VERSION (orig)];
if (orig_bytes != unknown[object_size_type])
orig_bytes = (offset > orig_bytes)
? (unsigned HOST_WIDE_INT) 0 : orig_bytes - offset;
if ((object_size_type & 2) == 0)
{
if (object_sizes[object_size_type][varno] < orig_bytes)
{
object_sizes[object_size_type][varno] = orig_bytes;
osi->changed = true;
}
}
else
{
if (object_sizes[object_size_type][varno] > orig_bytes)
{
object_sizes[object_size_type][varno] = orig_bytes;
osi->changed = true;
}
}
return bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (orig));
}
static bool
plus_expr_object_size (struct object_size_info *osi, tree var, tree value)
{
tree op0 = TREE_OPERAND (value, 0);
tree op1 = TREE_OPERAND (value, 1);
bool ptr1_p = POINTER_TYPE_P (TREE_TYPE (op0))
&& TREE_CODE (op0) != INTEGER_CST;
bool ptr2_p = POINTER_TYPE_P (TREE_TYPE (op1))
&& TREE_CODE (op1) != INTEGER_CST;
int object_size_type = osi->object_size_type;
unsigned int varno = SSA_NAME_VERSION (var);
unsigned HOST_WIDE_INT bytes;
gcc_assert (TREE_CODE (value) == PLUS_EXPR);
if (object_sizes[object_size_type][varno] == unknown[object_size_type])
return false;
if (ptr2_p && !ptr1_p)
{
tree tem = op0;
op0 = op1;
op1 = tem;
ptr1_p = true;
ptr2_p = false;
}
if (ptr1_p
&& !ptr2_p
&& TREE_CODE (op1) == INTEGER_CST
&& (TREE_CODE (op0) == SSA_NAME
|| TREE_CODE (op0) == ADDR_EXPR))
{
if (! host_integerp (op1, 1))
bytes = unknown[object_size_type];
else if (TREE_CODE (op0) == SSA_NAME)
return merge_object_sizes (osi, var, op0, tree_low_cst (op1, 1));
else
{
unsigned HOST_WIDE_INT off = tree_low_cst (op1, 1);
bytes = compute_builtin_object_size (value, object_size_type);
if (off > offset_limit)
bytes = unknown[object_size_type];
else if (off > bytes)
bytes = 0;
else
bytes -= off;
}
}
else
bytes = unknown[object_size_type];
if ((object_size_type & 2) == 0)
{
if (object_sizes[object_size_type][varno] < bytes)
object_sizes[object_size_type][varno] = bytes;
}
else
{
if (object_sizes[object_size_type][varno] > bytes)
object_sizes[object_size_type][varno] = bytes;
}
return false;
}
static void
collect_object_sizes_for (struct object_size_info *osi, tree var)
{
int object_size_type = osi->object_size_type;
unsigned int varno = SSA_NAME_VERSION (var);
tree stmt;
bool reexamine;
if (bitmap_bit_p (computed[object_size_type], varno))
return;
if (osi->pass == 0)
{
if (! bitmap_bit_p (osi->visited, varno))
{
bitmap_set_bit (osi->visited, varno);
object_sizes[object_size_type][varno]
= (object_size_type & 2) ? -1 : 0;
}
else
{
bitmap_set_bit (osi->reexamine, varno);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Found a dependency loop at ");
print_generic_expr (dump_file, var, dump_flags);
fprintf (dump_file, "\n");
}
return;
}
}
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Visiting use-def links for ");
print_generic_expr (dump_file, var, dump_flags);
fprintf (dump_file, "\n");
}
stmt = SSA_NAME_DEF_STMT (var);
reexamine = false;
switch (TREE_CODE (stmt))
{
case RETURN_EXPR:
if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR)
abort ();
stmt = TREE_OPERAND (stmt, 0);
case MODIFY_EXPR:
{
tree rhs = TREE_OPERAND (stmt, 1), arg;
STRIP_NOPS (rhs);
if (TREE_CODE (rhs) == CALL_EXPR)
{
arg = pass_through_call (rhs);
if (arg)
rhs = arg;
}
if (TREE_CODE (rhs) == SSA_NAME
&& POINTER_TYPE_P (TREE_TYPE (rhs)))
reexamine = merge_object_sizes (osi, var, rhs, 0);
else if (TREE_CODE (rhs) == PLUS_EXPR)
reexamine = plus_expr_object_size (osi, var, rhs);
else
expr_object_size (osi, var, rhs);
break;
}
case ASM_EXPR:
object_sizes[object_size_type][varno] = unknown[object_size_type];
break;
case NOP_EXPR:
{
tree decl = SSA_NAME_VAR (var);
gcc_assert (IS_EMPTY_STMT (stmt));
if (TREE_CODE (decl) != PARM_DECL && DECL_INITIAL (decl))
expr_object_size (osi, var, DECL_INITIAL (decl));
else
expr_object_size (osi, var, decl);
}
break;
case PHI_NODE:
{
int i;
for (i = 0; i < PHI_NUM_ARGS (stmt); i++)
{
tree rhs = PHI_ARG_DEF (stmt, i);
if (object_sizes[object_size_type][varno]
== unknown[object_size_type])
break;
if (TREE_CODE (rhs) == SSA_NAME)
reexamine |= merge_object_sizes (osi, var, rhs, 0);
else if (osi->pass == 0)
expr_object_size (osi, var, rhs);
}
break;
}
default:
gcc_unreachable ();
}
if (! reexamine
|| object_sizes[object_size_type][varno] == unknown[object_size_type])
{
bitmap_set_bit (computed[object_size_type], varno);
bitmap_clear_bit (osi->reexamine, varno);
}
else
{
bitmap_set_bit (osi->reexamine, varno);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Need to reexamine ");
print_generic_expr (dump_file, var, dump_flags);
fprintf (dump_file, "\n");
}
}
}
static void
check_for_plus_in_loops_1 (struct object_size_info *osi, tree var,
unsigned int depth)
{
tree stmt = SSA_NAME_DEF_STMT (var);
unsigned int varno = SSA_NAME_VERSION (var);
if (osi->depths[varno])
{
if (osi->depths[varno] != depth)
{
unsigned int *sp;
for (sp = osi->tos; sp > osi->stack; )
{
--sp;
bitmap_clear_bit (osi->reexamine, *sp);
bitmap_set_bit (computed[osi->object_size_type], *sp);
object_sizes[osi->object_size_type][*sp] = 0;
if (*sp == varno)
break;
}
}
return;
}
else if (! bitmap_bit_p (osi->reexamine, varno))
return;
osi->depths[varno] = depth;
*osi->tos++ = varno;
switch (TREE_CODE (stmt))
{
case RETURN_EXPR:
if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR)
abort ();
stmt = TREE_OPERAND (stmt, 0);
case MODIFY_EXPR:
{
tree rhs = TREE_OPERAND (stmt, 1), arg;
STRIP_NOPS (rhs);
if (TREE_CODE (rhs) == CALL_EXPR)
{
arg = pass_through_call (rhs);
if (arg)
rhs = arg;
}
if (TREE_CODE (rhs) == SSA_NAME)
check_for_plus_in_loops_1 (osi, rhs, depth);
else if (TREE_CODE (rhs) == PLUS_EXPR)
{
tree op0 = TREE_OPERAND (rhs, 0);
tree op1 = TREE_OPERAND (rhs, 1);
tree cst, basevar;
if (TREE_CODE (op0) == SSA_NAME)
{
basevar = op0;
cst = op1;
}
else
{
basevar = op1;
cst = op0;
gcc_assert (TREE_CODE (basevar) == SSA_NAME);
}
gcc_assert (TREE_CODE (cst) == INTEGER_CST);
check_for_plus_in_loops_1 (osi, basevar,
depth + !integer_zerop (cst));
}
else
gcc_unreachable ();
break;
}
case PHI_NODE:
{
int i;
for (i = 0; i < PHI_NUM_ARGS (stmt); i++)
{
tree rhs = PHI_ARG_DEF (stmt, i);
if (TREE_CODE (rhs) == SSA_NAME)
check_for_plus_in_loops_1 (osi, rhs, depth);
}
break;
}
default:
gcc_unreachable ();
}
osi->depths[varno] = 0;
osi->tos--;
}
static void
check_for_plus_in_loops (struct object_size_info *osi, tree var)
{
tree stmt = SSA_NAME_DEF_STMT (var);
switch (TREE_CODE (stmt))
{
case RETURN_EXPR:
if (TREE_CODE (TREE_OPERAND (stmt, 0)) != MODIFY_EXPR)
abort ();
stmt = TREE_OPERAND (stmt, 0);
case MODIFY_EXPR:
{
tree rhs = TREE_OPERAND (stmt, 1), arg;
STRIP_NOPS (rhs);
if (TREE_CODE (rhs) == CALL_EXPR)
{
arg = pass_through_call (rhs);
if (arg)
rhs = arg;
}
if (TREE_CODE (rhs) == PLUS_EXPR)
{
tree op0 = TREE_OPERAND (rhs, 0);
tree op1 = TREE_OPERAND (rhs, 1);
tree cst, basevar;
if (TREE_CODE (op0) == SSA_NAME)
{
basevar = op0;
cst = op1;
}
else
{
basevar = op1;
cst = op0;
gcc_assert (TREE_CODE (basevar) == SSA_NAME);
}
gcc_assert (TREE_CODE (cst) == INTEGER_CST);
if (integer_zerop (cst))
break;
osi->depths[SSA_NAME_VERSION (basevar)] = 1;
*osi->tos++ = SSA_NAME_VERSION (basevar);
check_for_plus_in_loops_1 (osi, var, 2);
osi->depths[SSA_NAME_VERSION (basevar)] = 0;
osi->tos--;
}
break;
}
default:
break;
}
}
void
init_object_sizes (void)
{
int object_size_type;
if (object_sizes[0])
return;
for (object_size_type = 0; object_size_type <= 3; object_size_type++)
{
object_sizes[object_size_type]
= xmalloc (num_ssa_names * sizeof (HOST_WIDE_INT));
computed[object_size_type] = BITMAP_ALLOC (NULL);
}
init_offset_limit ();
}
void
fini_object_sizes (void)
{
int object_size_type;
for (object_size_type = 0; object_size_type <= 3; object_size_type++)
{
free (object_sizes[object_size_type]);
BITMAP_FREE (computed[object_size_type]);
object_sizes[object_size_type] = NULL;
}
}
static void
compute_object_sizes (void)
{
basic_block bb;
FOR_EACH_BB (bb)
{
block_stmt_iterator i;
for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
{
tree *stmtp = bsi_stmt_ptr (i);
tree call = get_rhs (*stmtp);
tree callee, result;
if (!call || TREE_CODE (call) != CALL_EXPR)
continue;
callee = get_callee_fndecl (call);
if (!callee
|| DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
|| DECL_FUNCTION_CODE (callee) != BUILT_IN_OBJECT_SIZE)
continue;
init_object_sizes ();
result = fold_builtin (call, false);
if (!result)
{
tree arglist = TREE_OPERAND (call, 1);
if (arglist != NULL
&& POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
&& TREE_CHAIN (arglist) != NULL
&& TREE_CHAIN (TREE_CHAIN (arglist)) == NULL)
{
tree ost = TREE_VALUE (TREE_CHAIN (arglist));
if (host_integerp (ost, 1))
{
unsigned HOST_WIDE_INT object_size_type
= tree_low_cst (ost, 1);
if (object_size_type < 2)
result = fold_convert (size_type_node,
integer_minus_one_node);
else if (object_size_type < 4)
result = size_zero_node;
}
}
if (!result)
continue;
}
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Simplified\n ");
print_generic_stmt (dump_file, *stmtp, dump_flags);
}
if (!set_rhs (stmtp, result))
abort ();
modify_stmt (*stmtp);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "to\n ");
print_generic_stmt (dump_file, *stmtp, dump_flags);
fprintf (dump_file, "\n");
}
}
}
fini_object_sizes ();
}
struct tree_opt_pass pass_object_sizes =
{
"objsz",
NULL,
compute_object_sizes,
NULL,
NULL,
0,
0,
PROP_cfg | PROP_ssa | PROP_alias,
0,
0,
0,
TODO_dump_func | TODO_verify_ssa,
0
};