]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/tree-object-size.c
Merge from trunk.
[thirdparty/gcc.git] / gcc / tree-object-size.c
index 443f2808c2dad9bdb82b93056f616782ed7fdbb1..ced1f60f1e4f9fe4a5bf975340aff13ad7f6a44d 100644 (file)
@@ -1,6 +1,5 @@
 /* __builtin_object_size (ptr, object_size_type) computation
-   Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009
-   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.
@@ -24,11 +23,19 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "tree.h"
-#include "toplev.h"
-#include "diagnostic.h"
-#include "tree-flow.h"
+#include "tree-object-size.h"
+#include "diagnostic-core.h"
+#include "gimple-pretty-print.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
 {
@@ -40,7 +47,7 @@ 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 *,
@@ -52,7 +59,7 @@ 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_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);
@@ -78,8 +85,8 @@ 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);
+  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;
@@ -107,7 +114,7 @@ compute_object_offset (const_tree expr, const_tree var)
 
       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;
 
@@ -140,6 +147,10 @@ compute_object_offset (const_tree expr, const_tree var)
       off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), t);
       break;
 
+    case MEM_REF:
+      gcc_assert (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR);
+      return wide_int_to_tree (sizetype, mem_ref_offset (expr));
+
     default:
       return error_mark_node;
     }
@@ -161,19 +172,20 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
   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) == INDIRECT_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)
-       sz = compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
-                                         object_size_type & ~1);
+      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);
+       }
       else
        {
          tree var = TREE_OPERAND (pt_var, 0);
@@ -185,16 +197,30 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
          else
            sz = unknown[object_size_type];
        }
+      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 = unknown[object_size_type];
+       }
 
       if (sz != unknown[object_size_type] && sz < offset_limit)
        pt_var_size = size_int (sz);
     }
   else if (pt_var
-          && (SSA_VAR_P (pt_var) || TREE_CODE (pt_var) == STRING_CST)
+          && 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);
+  else if (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)
+          && 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
@@ -219,12 +245,12 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
          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)))))
            var = pt_var;
-         else if (var != pt_var && TREE_CODE (pt_var) == INDIRECT_REF)
+         else if (var != pt_var && TREE_CODE (pt_var) == MEM_REF)
            {
              tree v = var;
              /* For &X->fld, compute object size only if fld isn't the last
@@ -274,8 +300,8 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
                        && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
                           == RECORD_TYPE)
                      {
-                       tree fld_chain = TREE_CHAIN (TREE_OPERAND (v, 1));
-                       for (; fld_chain; fld_chain = TREE_CHAIN (fld_chain))
+                       tree fld_chain = DECL_CHAIN (TREE_OPERAND (v, 1));
+                       for (; fld_chain; fld_chain = DECL_CHAIN (fld_chain))
                          if (TREE_CODE (fld_chain) == FIELD_DECL)
                            break;
 
@@ -327,7 +353,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
        }
       if (var != pt_var
          && pt_var_size
-         && TREE_CODE (pt_var) == INDIRECT_REF
+         && TREE_CODE (pt_var) == MEM_REF
          && bytes != error_mark_node)
        {
          tree bytes2 = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var);
@@ -347,8 +373,8 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
   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];
 }
@@ -372,7 +398,8 @@ alloc_object_size (const_gimple call, int 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);
@@ -381,7 +408,7 @@ alloc_object_size (const_gimple call, int object_size_type)
       if (TREE_CHAIN (p))
         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))
       {
@@ -390,6 +417,7 @@ alloc_object_size (const_gimple call, int object_size_type)
        /* fall through */
       case BUILT_IN_MALLOC:
       case BUILT_IN_ALLOCA:
+      case BUILT_IN_ALLOCA_WITH_ALIGN:
        arg1 = 0;
       default:
        break;
@@ -397,10 +425,10 @@ alloc_object_size (const_gimple call, int object_size_type)
 
   if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call)
       || TREE_CODE (gimple_call_arg (call, arg1)) != INTEGER_CST
-      || (arg2 >= 0 
+      || (arg2 >= 0
          && (arg2 >= (int)gimple_call_num_args (call)
              || TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
-    return unknown[object_size_type];    
+    return unknown[object_size_type];
 
   if (arg2 >= 0)
     bytes = size_binop (MULT_EXPR,
@@ -409,8 +437,8 @@ alloc_object_size (const_gimple call, int object_size_type)
   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];
 }
@@ -441,8 +469,10 @@ pass_through_call (const_gimple call)
       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;
@@ -745,10 +775,20 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple stmt)
   unsigned HOST_WIDE_INT bytes;
   tree op0, op1;
 
-  gcc_assert (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR);
-
-  op0 = gimple_assign_rhs1 (stmt);
-  op1 = gimple_assign_rhs2 (stmt);
+  if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+    {
+      op0 = gimple_assign_rhs1 (stmt);
+      op1 = gimple_assign_rhs2 (stmt);
+    }
+  else if (gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+    {
+      tree rhs = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
+      gcc_assert (TREE_CODE (rhs) == MEM_REF);
+      op0 = TREE_OPERAND (rhs, 0);
+      op1 = TREE_OPERAND (rhs, 1);
+    }
+  else
+    gcc_unreachable ();
 
   if (object_sizes[object_size_type][varno] == unknown[object_size_type])
     return false;
@@ -758,13 +798,13 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple stmt)
       && (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);
@@ -795,25 +835,25 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple stmt)
 }
 
 
-/* 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);
@@ -861,9 +901,8 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 
   if (osi->pass == 0)
     {
-      if (! bitmap_bit_p (osi->visited, varno))
+      if (bitmap_set_bit (osi->visited, varno))
        {
-         bitmap_set_bit (osi->visited, varno);
          object_sizes[object_size_type][varno]
            = (object_size_type & 2) ? -1 : 0;
        }
@@ -896,18 +935,19 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     {
     case GIMPLE_ASSIGN:
       {
-        if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+       tree rhs = gimple_assign_rhs1 (stmt);
+        if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
+           || (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))
           {
-            tree rhs = gimple_assign_rhs1 (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);
           }
@@ -924,8 +964,6 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
             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);
           }
@@ -940,14 +978,12 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
       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:
@@ -1111,7 +1147,7 @@ check_for_plus_in_loops (struct object_size_info *osi, tree var)
     {
       tree basevar = gimple_assign_rhs1 (stmt);
       tree cst = gimple_assign_rhs2 (stmt);
-           
+
       gcc_assert (TREE_CODE (cst) == INTEGER_CST);
 
       if (integer_zerop (cst))
@@ -1148,7 +1184,7 @@ init_object_sizes (void)
 
 /* Destroy data structures after the object size computation.  */
 
-void
+static void
 fini_object_sizes (void)
 {
   int object_size_type;
@@ -1173,16 +1209,9 @@ compute_object_sizes (void)
       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 ();
@@ -1194,16 +1223,16 @@ compute_object_sizes (void)
                {
                  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,
                                               integer_minus_one_node);
                      else if (object_size_type < 4)
-                       result = size_zero_node;
+                       result = build_zero_cst (size_type_node);
                    }
                }
 
@@ -1211,23 +1240,32 @@ compute_object_sizes (void)
                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));
            }
        }
     }
@@ -1236,21 +1274,40 @@ compute_object_sizes (void)
   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_dump_func | 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);
+}