/* __builtin_object_size (ptr, object_size_type) computation
- Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ Copyright (C) 2004-2013 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>
This file is part of GCC.
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
+#include "tree-object-size.h"
#include "diagnostic-core.h"
-#include "tree-pretty-print.h"
#include "gimple-pretty-print.h"
-#include "tree-flow.h"
+#include "bitmap.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
#include "tree-pass.h"
#include "tree-ssa-propagate.h"
+#include "tree-phinodes.h"
+#include "ssa-iterators.h"
struct object_size_info
{
unsigned int *stack, *tos;
};
-static unsigned HOST_WIDE_INT unknown[4] = { -1, -1, 0, 0 };
+static const unsigned HOST_WIDE_INT unknown[4] = { -1, -1, 0, 0 };
static tree compute_object_offset (const_tree, const_tree);
static unsigned HOST_WIDE_INT addr_object_size (struct object_size_info *,
static bool merge_object_sizes (struct object_size_info *, tree, tree,
unsigned HOST_WIDE_INT);
static bool plus_stmt_object_size (struct object_size_info *, tree, gimple);
-static bool cond_expr_object_size (struct object_size_info *, tree, tree);
+static bool cond_expr_object_size (struct object_size_info *, tree, gimple);
static unsigned int compute_object_sizes (void);
static void init_offset_limit (void);
static void check_for_plus_in_loops (struct object_size_info *, tree);
static void
init_offset_limit (void)
{
- if (host_integerp (TYPE_MAX_VALUE (sizetype), 1))
- offset_limit = tree_low_cst (TYPE_MAX_VALUE (sizetype), 1);
+ if (tree_fits_uhwi_p (TYPE_MAX_VALUE (sizetype)))
+ offset_limit = tree_to_uhwi (TYPE_MAX_VALUE (sizetype));
else
offset_limit = -1;
offset_limit /= 2;
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)
+ size_int (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (t))
/ BITS_PER_UNIT));
break;
case MEM_REF:
gcc_assert (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR);
- return double_int_to_tree (sizetype, mem_ref_offset (expr));
+ return wide_int_to_tree (sizetype, mem_ref_offset (expr));
default:
return error_mark_node;
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);
+ while (handled_component_p (pt_var))
+ pt_var = TREE_OPERAND (pt_var, 0);
if (pt_var
- && TREE_CODE (pt_var) == MEM_REF
- && TREE_CODE (TREE_OPERAND (pt_var, 0)) == SSA_NAME
- && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (pt_var, 0))))
+ && TREE_CODE (pt_var) == MEM_REF)
{
unsigned HOST_WIDE_INT sz;
- if (!osi || (object_size_type & 1) != 0)
+ if (!osi || (object_size_type & 1) != 0
+ || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
{
sz = compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
object_size_type & ~1);
- if (host_integerp (TREE_OPERAND (pt_var, 1), 0))
- sz -= TREE_INT_CST_LOW (TREE_OPERAND (pt_var, 1));
- else
- sz = offset_limit;
}
else
{
sz = object_sizes[object_size_type][SSA_NAME_VERSION (var)];
else
sz = unknown[object_size_type];
- if (host_integerp (TREE_OPERAND (pt_var, 1), 0))
- sz -= TREE_INT_CST_LOW (TREE_OPERAND (pt_var, 1));
+ }
+ 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 ();
else
- sz = offset_limit;
+ sz = unknown[object_size_type];
}
if (sz != unknown[object_size_type] && sz < offset_limit)
}
else if (pt_var
&& DECL_P (pt_var)
- && host_integerp (DECL_SIZE_UNIT (pt_var), 1)
- && (unsigned HOST_WIDE_INT)
- tree_low_cst (DECL_SIZE_UNIT (pt_var), 1) < offset_limit)
+ && 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);
else if (pt_var
- && (SSA_VAR_P (pt_var) || TREE_CODE (pt_var) == STRING_CST)
+ && 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)
+ && tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)))
+ && tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)))
< offset_limit)
pt_var_size = TYPE_SIZE_UNIT (TREE_TYPE (pt_var));
else
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_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (var)))
|| (pt_var_size
&& tree_int_cst_lt (pt_var_size,
TYPE_SIZE_UNIT (TREE_TYPE (var)))))
else
bytes = pt_var_size;
- if (host_integerp (bytes, 1))
- return tree_low_cst (bytes, 1);
+ if (tree_fits_uhwi_p (bytes))
+ return tree_to_uhwi (bytes);
return unknown[object_size_type];
}
if (!callee)
return unknown[object_size_type];
- alloc_size = lookup_attribute ("alloc_size", TYPE_ATTRIBUTES (TREE_TYPE(callee)));
+ alloc_size = lookup_attribute ("alloc_size",
+ TYPE_ATTRIBUTES (TREE_TYPE (callee)));
if (alloc_size && TREE_VALUE (alloc_size))
{
tree p = TREE_VALUE (alloc_size);
/* fall through */
case BUILT_IN_MALLOC:
case BUILT_IN_ALLOCA:
+ case BUILT_IN_ALLOCA_WITH_ALIGN:
arg1 = 0;
default:
break;
else if (arg1 >= 0)
bytes = fold_convert (sizetype, gimple_call_arg (call, arg1));
- if (bytes && host_integerp (bytes, 1))
- return tree_low_cst (bytes, 1);
+ if (bytes && tree_fits_uhwi_p (bytes))
+ return tree_to_uhwi (bytes);
return unknown[object_size_type];
}
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;
&& (TREE_CODE (op0) == SSA_NAME
|| TREE_CODE (op0) == ADDR_EXPR))
{
- if (! host_integerp (op1, 1))
+ if (! tree_fits_uhwi_p (op1))
bytes = unknown[object_size_type];
else if (TREE_CODE (op0) == SSA_NAME)
- return merge_object_sizes (osi, var, op0, tree_low_cst (op1, 1));
+ return merge_object_sizes (osi, var, op0, tree_to_uhwi (op1));
else
{
- unsigned HOST_WIDE_INT off = tree_low_cst (op1, 1);
+ unsigned HOST_WIDE_INT off = tree_to_uhwi (op1);
/* op0 will be ADDR_EXPR here. */
bytes = addr_object_size (osi, op0, object_size_type);
}
-/* Compute object_sizes for VAR, defined to VALUE, which is
+/* Compute object_sizes for VAR, defined at STMT, which is
a COND_EXPR. Return true if the object size might need reexamination
later. */
static bool
-cond_expr_object_size (struct object_size_info *osi, tree var, tree value)
+cond_expr_object_size (struct object_size_info *osi, tree var, gimple stmt)
{
tree then_, else_;
int object_size_type = osi->object_size_type;
unsigned int varno = SSA_NAME_VERSION (var);
bool reexamine = false;
- gcc_assert (TREE_CODE (value) == COND_EXPR);
+ gcc_assert (gimple_assign_rhs_code (stmt) == COND_EXPR);
if (object_sizes[object_size_type][varno] == unknown[object_size_type])
return false;
- then_ = COND_EXPR_THEN (value);
- else_ = COND_EXPR_ELSE (value);
+ then_ = gimple_assign_rhs2 (stmt);
+ else_ = gimple_assign_rhs3 (stmt);
if (TREE_CODE (then_) == SSA_NAME)
reexamine |= merge_object_sizes (osi, var, then_, 0);
|| (gimple_assign_rhs_code (stmt) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF))
reexamine = plus_stmt_object_size (osi, var, stmt);
+ else if (gimple_assign_rhs_code (stmt) == COND_EXPR)
+ reexamine = cond_expr_object_size (osi, var, stmt);
else if (gimple_assign_single_p (stmt)
|| gimple_assign_unary_nop_p (stmt))
{
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) == COND_EXPR)
- reexamine = cond_expr_object_size (osi, var, rhs);
else
expr_object_size (osi, var, rhs);
}
if (TREE_CODE (arg) == SSA_NAME
&& POINTER_TYPE_P (TREE_TYPE (arg)))
reexamine = merge_object_sizes (osi, var, arg, 0);
- else if (TREE_CODE (arg) == COND_EXPR)
- reexamine = cond_expr_object_size (osi, var, arg);
else
expr_object_size (osi, var, arg);
}
break;
case GIMPLE_NOP:
- {
- tree decl = SSA_NAME_VAR (var);
-
- if (TREE_CODE (decl) != PARM_DECL && DECL_INITIAL (decl))
- expr_object_size (osi, var, DECL_INITIAL (decl));
- else
- expr_object_size (osi, var, decl);
- }
+ if (SSA_NAME_VAR (var)
+ && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
+ expr_object_size (osi, var, SSA_NAME_VAR (var));
+ else
+ /* Uninitialized SSA names point nowhere. */
+ object_sizes[object_size_type][varno] = unknown[object_size_type];
break;
case GIMPLE_PHI:
/* Destroy data structures after the object size computation. */
-void
+static void
fini_object_sizes (void)
{
int object_size_type;
gimple_stmt_iterator i;
for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
{
- tree callee, result;
+ tree result;
gimple call = gsi_stmt (i);
-
- if (gimple_code (call) != GIMPLE_CALL)
- continue;
-
- callee = gimple_call_fndecl (call);
- if (!callee
- || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
- || DECL_FUNCTION_CODE (callee) != BUILT_IN_OBJECT_SIZE)
+ if (!gimple_call_builtin_p (call, BUILT_IN_OBJECT_SIZE))
continue;
init_object_sizes ();
{
tree ost = gimple_call_arg (call, 1);
- if (host_integerp (ost, 1))
+ if (tree_fits_uhwi_p (ost))
{
unsigned HOST_WIDE_INT object_size_type
- = tree_low_cst (ost, 1);
+ = tree_to_uhwi (ost);
if (object_size_type < 2)
result = fold_convert (size_type_node,
continue;
}
+ gcc_assert (TREE_CODE (result) == INTEGER_CST);
+
if (dump_file && (dump_flags & TDF_DETAILS))
{
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);
+ fprintf (dump_file, "\n");
}
- if (!update_call_from_tree (&i, result))
- gcc_unreachable ();
-
- /* NOTE: In the pre-tuples code, we called update_stmt here. This is
- now handled by gsi_replace, called from update_call_from_tree. */
+ tree lhs = gimple_call_lhs (call);
+ if (!lhs)
+ continue;
- if (dump_file && (dump_flags & TDF_DETAILS))
+ /* Propagate into all uses and fold those stmts. */
+ gimple use_stmt;
+ imm_use_iterator iter;
+ FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
{
- fprintf (dump_file, "to\n ");
- print_gimple_stmt (dump_file, call, 0, dump_flags);
- fprintf (dump_file, "\n");
+ use_operand_p use_p;
+ FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+ SET_USE (use_p, result);
+ gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
+ fold_stmt (&gsi);
+ update_stmt (gsi_stmt (gsi));
}
}
}
return 0;
}
-struct gimple_opt_pass pass_object_sizes =
+namespace {
+
+const pass_data pass_data_object_sizes =
{
- {
- GIMPLE_PASS,
- "objsz", /* name */
- NULL, /* gate */
- compute_object_sizes, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_NONE, /* tv_id */
- PROP_cfg | PROP_ssa, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_verify_ssa /* todo_flags_finish */
- }
+ GIMPLE_PASS, /* type */
+ "objsz", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ false, /* has_gate */
+ true, /* has_execute */
+ TV_NONE, /* tv_id */
+ ( PROP_cfg | PROP_ssa ), /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_verify_ssa, /* todo_flags_finish */
};
+
+class pass_object_sizes : public gimple_opt_pass
+{
+public:
+ pass_object_sizes (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_object_sizes, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ opt_pass * clone () { return new pass_object_sizes (m_ctxt); }
+ unsigned int execute () { return compute_object_sizes (); }
+
+}; // class pass_object_sizes
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_object_sizes (gcc::context *ctxt)
+{
+ return new pass_object_sizes (ctxt);
+}