i386.c (legitimize_tls_address): Generate tls_initial_exec_64_sun only when !TARGET_X32.
[gcc.git] / gcc / gimple-fold.c
index 4afced899a0a3fdde99478b35a2806b25c379d28..b2bd33788028349ad5087231f0ae209d3de24046 100644 (file)
@@ -1,5 +1,5 @@
 /* Statement simplification on GIMPLE.
-   Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc.
    Split out from tree-ssa-ccp.c.
 
 This file is part of GCC.
@@ -33,8 +33,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 
 /* Return true when DECL can be referenced from current unit.
-   We can get declarations that are not possible to reference for
-   various reasons:
+   FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
+   We can get declarations that are not possible to reference for various
+   reasons:
 
      1) When analyzing C++ virtual tables.
        C++ virtual tables do have known constructors even
@@ -54,25 +55,35 @@ along with GCC; see the file COPYING3.  If not see
         directly.  */
 
 static bool
-can_refer_decl_in_current_unit_p (tree decl)
+can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
 {
   struct varpool_node *vnode;
   struct cgraph_node *node;
-
-  if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
+  symtab_node snode;
+
+  /* We will later output the initializer, so we can reffer to it.
+     So we are concerned only when DECL comes from initializer of
+     external var.  */
+  if (!from_decl
+      || TREE_CODE (from_decl) != VAR_DECL
+      || !DECL_EXTERNAL (from_decl)
+      || (symtab_get_node (from_decl)->symbol.in_other_partition))
     return true;
-  /* External flag is set, so we deal with C++ reference
-     to static object from other file.  */
-  if (DECL_EXTERNAL (decl) && TREE_STATIC (decl)
-      && TREE_CODE (decl) == VAR_DECL)
-    {
-      /* Just be sure it is not big in frontend setting
-        flags incorrectly.  Those variables should never
-        be finalized.  */
-      gcc_checking_assert (!(vnode = varpool_get_node (decl))
-                          || !vnode->finalized);
-      return false;
-    }
+  /* We are concerned ony about static/external vars and functions.  */
+  if ((!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
+      || (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != FUNCTION_DECL))
+    return true;
+  /* Weakrefs have somewhat confusing DECL_EXTERNAL flag set; they are always safe.  */
+  if (DECL_EXTERNAL (decl)
+      && lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
+    return true;
+  /* We are folding reference from external vtable.  The vtable may reffer
+     to a symbol keyed to other compilation unit.  The other compilation
+     unit may be in separate DSO and the symbol may be hidden.  */
+  if (DECL_VISIBILITY_SPECIFIED (decl)
+      && DECL_EXTERNAL (decl)
+      && (!(snode = symtab_get_node (decl)) || !snode->symbol.in_other_partition))
+    return false;
   /* When function is public, we always can introduce new reference.
      Exception are the COMDAT functions where introducing a direct
      reference imply need to include function body in the curren tunit.  */
@@ -81,14 +92,19 @@ can_refer_decl_in_current_unit_p (tree decl)
   /* We are not at ltrans stage; so don't worry about WHOPR.
      Also when still gimplifying all referred comdat functions will be
      produced.
-     ??? as observed in PR20991 for already optimized out comdat virtual functions
-     we may not neccesarily give up because the copy will be output elsewhere when
-     corresponding vtable is output.  */
+
+     As observed in PR20991 for already optimized out comdat virtual functions
+     it may be tempting to not necessarily give up because the copy will be
+     output elsewhere when corresponding vtable is output.  
+     This is however not possible - ABI specify that COMDATs are output in
+     units where they are used and when the other unit was compiled with LTO
+     it is possible that vtable was kept public while the function itself
+     was privatized. */
   if (!flag_ltrans && (!DECL_COMDAT (decl) || !cgraph_function_flags_ready))
     return true;
-  /* If we already output the function body, we are safe.  */
-  if (TREE_ASM_WRITTEN (decl))
-    return true;
+
+  /* OK we are seeing either COMDAT or static variable.  In this case we must
+     check that the definition is still around so we can refer it.  */
   if (TREE_CODE (decl) == FUNCTION_DECL)
     {
       node = cgraph_get_node (decl);
@@ -98,22 +114,29 @@ can_refer_decl_in_current_unit_p (tree decl)
          compilation stage when making a new reference no longer makes callee
          to be compiled.  */
       if (!node || !node->analyzed || node->global.inlined_to)
-       return false;
+       {
+         gcc_checking_assert (!TREE_ASM_WRITTEN (decl));
+         return false;
+       }
     }
   else if (TREE_CODE (decl) == VAR_DECL)
     {
       vnode = varpool_get_node (decl);
-      if (!vnode || !vnode->finalized)
-       return false;
+      if (!vnode || !vnode->analyzed)
+       {
+         gcc_checking_assert (!TREE_ASM_WRITTEN (decl));
+         return false;
+       }
     }
   return true;
 }
 
 /* CVAL is value taken from DECL_INITIAL of variable.  Try to transform it into
-   acceptable form for is_gimple_min_invariant.   */
+   acceptable form for is_gimple_min_invariant.
+   FROM_DECL (if non-NULL) specify variable whose constructor contains CVAL.  */
 
 tree
-canonicalize_constructor_val (tree cval)
+canonicalize_constructor_val (tree cval, tree from_decl)
 {
   STRIP_NOPS (cval);
   if (TREE_CODE (cval) == POINTER_PLUS_EXPR
@@ -131,18 +154,33 @@ canonicalize_constructor_val (tree cval)
   if (TREE_CODE (cval) == ADDR_EXPR)
     {
       tree base = get_base_address (TREE_OPERAND (cval, 0));
+      if (!base && TREE_CODE (TREE_OPERAND (cval, 0)) == COMPOUND_LITERAL_EXPR)
+       {
+         base = COMPOUND_LITERAL_EXPR_DECL (TREE_OPERAND (cval, 0));
+         if (base)
+           TREE_OPERAND (cval, 0) = base;
+       }
+      if (!base)
+       return NULL_TREE;
 
-      if (base
-         && (TREE_CODE (base) == VAR_DECL
-             || TREE_CODE (base) == FUNCTION_DECL)
-         && !can_refer_decl_in_current_unit_p (base))
+      if ((TREE_CODE (base) == VAR_DECL
+          || TREE_CODE (base) == FUNCTION_DECL)
+         && !can_refer_decl_in_current_unit_p (base, from_decl))
        return NULL_TREE;
-      if (base && TREE_CODE (base) == VAR_DECL)
+      if (TREE_CODE (base) == VAR_DECL)
        {
          TREE_ADDRESSABLE (base) = 1;
-         if (cfun && gimple_referenced_vars (cfun))
+         if (cfun && gimple_referenced_vars (cfun)
+             && !is_global_var (base))
            add_referenced_var (base);
        }
+      else if (TREE_CODE (base) == FUNCTION_DECL)
+       {
+         /* Make sure we create a cgraph node for functions we'll reference.
+            They can be non-existent if the reference comes from an entry
+            of an external vtable for example.  */
+         cgraph_get_create_node (base);
+       }
       /* Fixup types in global initializers.  */
       if (TREE_TYPE (TREE_TYPE (cval)) != TREE_TYPE (TREE_OPERAND (cval, 0)))
        cval = build_fold_addr_expr (TREE_OPERAND (cval, 0));
@@ -161,7 +199,7 @@ get_symbol_constant_value (tree sym)
       tree val = DECL_INITIAL (sym);
       if (val)
        {
-         val = canonicalize_constructor_val (val);
+         val = canonicalize_constructor_val (val, sym);
          if (val && is_gimple_min_invariant (val))
            return val;
          else
@@ -540,9 +578,8 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
   tree lhs;
   gimple stmt, new_stmt;
   gimple_stmt_iterator i;
-  gimple_seq stmts = gimple_seq_alloc();
+  gimple_seq stmts = NULL;
   struct gimplify_ctx gctx;
-  gimple last;
   gimple laststore;
   tree reaching_vuse;
 
@@ -591,8 +628,11 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
   for (i = gsi_last (stmts); !gsi_end_p (i); gsi_prev (&i))
     {
       new_stmt = gsi_stmt (i);
-      if (gimple_assign_single_p (new_stmt)
-         && !is_gimple_reg (gimple_assign_lhs (new_stmt)))
+      if ((gimple_assign_single_p (new_stmt)
+          && !is_gimple_reg (gimple_assign_lhs (new_stmt)))
+         || (is_gimple_call (new_stmt)
+             && (gimple_call_flags (new_stmt)
+                 & (ECF_NOVOPS | ECF_PURE | ECF_CONST | ECF_NORETURN)) == 0))
        {
          tree vdef;
          if (!laststore)
@@ -608,21 +648,13 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
 
   /* Second iterate over the statements forward, assigning virtual
      operands to their uses.  */
-  last = NULL;
   reaching_vuse = gimple_vuse (stmt);
   for (i = gsi_start (stmts); !gsi_end_p (i); gsi_next (&i))
     {
-      /* Do not insert the last stmt in this loop but remember it
-         for replacing the original statement.  */
-      if (last)
-       {
-         gsi_insert_before (si_p, last, GSI_NEW_STMT);
-         gsi_next (si_p);
-       }
       new_stmt = gsi_stmt (i);
       /* The replacement can expose previously unreferenced variables.  */
       if (gimple_in_ssa_p (cfun))
-       find_new_referenced_vars (new_stmt);
+       find_referenced_vars_in (new_stmt);
       /* If the new statement possibly has a VUSE, update it with exact SSA
         name we know will reach this one.  */
       if (gimple_has_mem_ops (new_stmt))
@@ -630,7 +662,6 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
       gimple_set_modified (new_stmt, true);
       if (gimple_vdef (new_stmt))
        reaching_vuse = gimple_vdef (new_stmt);
-      last = new_stmt;
     }
 
   /* If the new sequence does not do a store release the virtual
@@ -647,8 +678,8 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
        }
     }
 
-  /* Finally replace rhe original statement with the last.  */
-  gsi_replace (si_p, last, false);
+  /* Finally replace the original statement with the sequence.  */
+  gsi_replace_with_seq (si_p, stmts, false);
 }
 
 /* Return the string length, maximum string length or maximum value of
@@ -668,13 +699,10 @@ get_maxval_strlen (tree arg, tree *length, bitmap visited, int type)
 
   if (TREE_CODE (arg) != SSA_NAME)
     {
-      if (TREE_CODE (arg) == COND_EXPR)
-        return get_maxval_strlen (COND_EXPR_THEN (arg), length, visited, type)
-               && get_maxval_strlen (COND_EXPR_ELSE (arg), length, visited, type);
       /* We can end up with &(*iftmp_1)[0] here as well, so handle it.  */
-      else if (TREE_CODE (arg) == ADDR_EXPR
-              && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF
-              && integer_zerop (TREE_OPERAND (TREE_OPERAND (arg, 0), 1)))
+      if (TREE_CODE (arg) == ADDR_EXPR
+         && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF
+         && integer_zerop (TREE_OPERAND (TREE_OPERAND (arg, 0), 1)))
        {
          tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
          if (TREE_CODE (aop0) == INDIRECT_REF
@@ -734,6 +762,13 @@ get_maxval_strlen (tree arg, tree *length, bitmap visited, int type)
             tree rhs = gimple_assign_rhs1 (def_stmt);
             return get_maxval_strlen (rhs, length, visited, type);
           }
+       else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
+         {
+           tree op2 = gimple_assign_rhs2 (def_stmt);
+           tree op3 = gimple_assign_rhs3 (def_stmt);
+           return get_maxval_strlen (op2, length, visited, type)
+                  && get_maxval_strlen (op3, length, visited, type);
+          }
         return false;
 
       case GIMPLE_PHI:
@@ -834,6 +869,7 @@ gimple_fold_builtin (gimple stmt)
     case BUILT_IN_MEMMOVE_CHK:
     case BUILT_IN_MEMSET_CHK:
     case BUILT_IN_STRNCPY_CHK:
+    case BUILT_IN_STPNCPY_CHK:
       arg_idx = 2;
       type = 2;
       break;
@@ -940,12 +976,14 @@ gimple_fold_builtin (gimple stmt)
       break;
 
     case BUILT_IN_STRNCPY_CHK:
+    case BUILT_IN_STPNCPY_CHK:
       if (val[2] && is_gimple_val (val[2]) && nargs == 4)
-       result = fold_builtin_strncpy_chk (loc, gimple_call_arg (stmt, 0),
+       result = fold_builtin_stxncpy_chk (loc, gimple_call_arg (stmt, 0),
                                            gimple_call_arg (stmt, 1),
                                            gimple_call_arg (stmt, 2),
                                            gimple_call_arg (stmt, 3),
-                                          val[2]);
+                                          val[2], ignore,
+                                          DECL_FUNCTION_CODE (callee));
       break;
 
     case BUILT_IN_SNPRINTF_CHK:
@@ -964,29 +1002,6 @@ gimple_fold_builtin (gimple stmt)
   return result;
 }
 
-/* Generate code adjusting the first parameter of a call statement determined
-   by GSI by DELTA.  */
-
-void
-gimple_adjust_this_by_delta (gimple_stmt_iterator *gsi, tree delta)
-{
-  gimple call_stmt = gsi_stmt (*gsi);
-  tree parm, tmp;
-  gimple new_stmt;
-
-  delta = convert_to_ptrofftype (delta);
-  gcc_assert (gimple_call_num_args (call_stmt) >= 1);
-  parm = gimple_call_arg (call_stmt, 0);
-  gcc_assert (POINTER_TYPE_P (TREE_TYPE (parm)));
-  tmp = create_tmp_var (TREE_TYPE (parm), NULL);
-  add_referenced_var (tmp);
-
-  tmp = make_ssa_name (tmp, NULL);
-  new_stmt = gimple_build_assign_with_ops (POINTER_PLUS_EXPR, tmp, parm, delta);
-  SSA_NAME_DEF_STMT (tmp) = new_stmt;
-  gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
-  gimple_call_set_arg (call_stmt, 0, tmp);
-}
 
 /* Return a binfo to be used for devirtualization of calls based on an object
    represented by a declaration (i.e. a global or automatically allocated one)
@@ -1045,7 +1060,7 @@ gimple_extract_devirt_binfo_from_cst (tree cst)
       type = TREE_TYPE (fld);
       offset -= pos;
     }
-  /* Artifical sub-objects are ancestors, we do not want to use them for
+  /* Artificial sub-objects are ancestors, we do not want to use them for
      devirtualization, at least not here.  */
   if (last_artificial)
     return NULL_TREE;
@@ -1117,21 +1132,10 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
   if (callee && DECL_BUILT_IN (callee))
     {
       tree result = gimple_fold_builtin (stmt);
-      if (result
-         /* Disallow EH edge removal here.  We can't call
-            gimple_purge_dead_eh_edges here.  */
-         && (lookup_stmt_eh_lp (stmt) == 0
-             || tree_could_throw_p (result)))
+      if (result)
        {
           if (!update_call_from_tree (gsi, result))
            gimplify_and_update_call_from_tree (gsi, result);
-         if (!gsi_end_p (*gsi))
-           {
-             gimple new_stmt = gsi_stmt (*gsi);
-             bool update_eh ATTRIBUTE_UNUSED
-               = maybe_clean_or_replace_eh_stmt (stmt, new_stmt);
-             gcc_assert (!update_eh);
-           }
          changed = true;
        }
     }
@@ -1255,6 +1259,18 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace)
                  changed = true;
                }
            }
+         else if (val
+                  && TREE_CODE (val) == ADDR_EXPR)
+           {
+             tree ref = TREE_OPERAND (val, 0);
+             tree tem = maybe_fold_reference (ref, false);
+             if (tem)
+               {
+                 tem = build_fold_addr_expr_with_type (tem, TREE_TYPE (val));
+                 gimple_debug_bind_set_value (stmt, tem);
+                 changed = true;
+               }
+           }
        }
       break;
 
@@ -2435,7 +2451,7 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree))
              else if (TREE_CODE (rhs) == ADDR_EXPR
                       && !is_gimple_min_invariant (rhs))
                {
-                 HOST_WIDE_INT offset;
+                 HOST_WIDE_INT offset = 0;
                  tree base;
                  base = get_addr_base_and_unit_offset_1 (TREE_OPERAND (rhs, 0),
                                                          &offset,
@@ -2452,21 +2468,22 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree))
                           == TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
                {
                  unsigned i;
-                 tree val, list;
+                 tree val, *vec;
 
-                 list = NULL_TREE;
+                 vec = XALLOCAVEC (tree,
+                                   TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs)));
                  FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val)
                    {
                      val = (*valueize) (val);
                      if (TREE_CODE (val) == INTEGER_CST
                          || TREE_CODE (val) == REAL_CST
                          || TREE_CODE (val) == FIXED_CST)
-                       list = tree_cons (NULL_TREE, val, list);
+                       vec[i] = val;
                      else
                        return NULL_TREE;
                    }
 
-                 return build_vector (TREE_TYPE (rhs), nreverse (list));
+                 return build_vector (TREE_TYPE (rhs), vec);
                }
 
               if (kind == tcc_reference)
@@ -2646,7 +2663,7 @@ gimple_fold_stmt_to_constant (gimple stmt, tree (*valueize) (tree))
 
 static tree fold_ctor_reference (tree type, tree ctor,
                                 unsigned HOST_WIDE_INT offset,
-                                unsigned HOST_WIDE_INT size);
+                                unsigned HOST_WIDE_INT size, tree);
 
 /* See if we can find constructor defining value of BASE.
    When we know the consructor with constant offset (such as
@@ -2754,14 +2771,15 @@ fold_string_cst_ctor_reference (tree type, tree ctor,
 static tree
 fold_array_ctor_reference (tree type, tree ctor,
                           unsigned HOST_WIDE_INT offset,
-                          unsigned HOST_WIDE_INT size)
+                          unsigned HOST_WIDE_INT size,
+                          tree from_decl)
 {
   unsigned HOST_WIDE_INT cnt;
   tree cfield, cval;
   double_int low_bound, elt_size;
   double_int index, max_index;
   double_int access_index;
-  tree domain_type = NULL_TREE;
+  tree domain_type = NULL_TREE, index_type = NULL_TREE;
   HOST_WIDE_INT inner_offset;
 
   /* Compute low bound and elt size.  */
@@ -2771,6 +2789,7 @@ fold_array_ctor_reference (tree type, tree ctor,
     {
       /* Static constructors for variably sized objects makes no sense.  */
       gcc_assert (TREE_CODE (TYPE_MIN_VALUE (domain_type)) == INTEGER_CST);
+      index_type = TREE_TYPE (TYPE_MIN_VALUE (domain_type));
       low_bound = tree_to_double_int (TYPE_MIN_VALUE (domain_type));
     }
   else
@@ -2794,6 +2813,10 @@ fold_array_ctor_reference (tree type, tree ctor,
   access_index = double_int_udiv (uhwi_to_double_int (offset / BITS_PER_UNIT),
                                  elt_size, TRUNC_DIV_EXPR);
   access_index = double_int_add (access_index, low_bound);
+  if (index_type)
+    access_index = double_int_ext (access_index,
+                                  TYPE_PRECISION (index_type),
+                                  TYPE_UNSIGNED (index_type));
 
   /* And offset within the access.  */
   inner_offset = offset % (double_int_to_uhwi (elt_size) * BITS_PER_UNIT);
@@ -2804,6 +2827,11 @@ fold_array_ctor_reference (tree type, tree ctor,
     return NULL_TREE;
 
   index = double_int_sub (low_bound, double_int_one);
+  if (index_type)
+    index = double_int_ext (index,
+                           TYPE_PRECISION (index_type),
+                           TYPE_UNSIGNED (index_type));
+
   FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval)
     {
       /* Array constructor might explicitely set index, or specify range
@@ -2821,12 +2849,20 @@ fold_array_ctor_reference (tree type, tree ctor,
            }
        }
       else
-       max_index = index = double_int_add (index, double_int_one);
+       {
+         index = double_int_add (index, double_int_one);
+         if (index_type)
+           index = double_int_ext (index,
+                                   TYPE_PRECISION (index_type),
+                                   TYPE_UNSIGNED (index_type));
+         max_index = index;
+       }
 
       /* Do we have match?  */
       if (double_int_cmp (access_index, index, 1) >= 0
          && double_int_cmp (access_index, max_index, 1) <= 0)
-       return fold_ctor_reference (type, cval, inner_offset, size);
+       return fold_ctor_reference (type, cval, inner_offset, size,
+                                   from_decl);
     }
   /* When memory is not explicitely mentioned in constructor,
      it is 0 (or out of range).  */
@@ -2839,7 +2875,8 @@ fold_array_ctor_reference (tree type, tree ctor,
 static tree
 fold_nonarray_ctor_reference (tree type, tree ctor,
                              unsigned HOST_WIDE_INT offset,
-                             unsigned HOST_WIDE_INT size)
+                             unsigned HOST_WIDE_INT size,
+                             tree from_decl)
 {
   unsigned HOST_WIDE_INT cnt;
   tree cfield, cval;
@@ -2894,7 +2931,8 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
          if (double_int_cmp (uhwi_to_double_int (offset), bitoffset, 0) < 0)
            return NULL_TREE;
          return fold_ctor_reference (type, cval,
-                                     double_int_to_uhwi (inner_offset), size);
+                                     double_int_to_uhwi (inner_offset), size,
+                                     from_decl);
        }
     }
   /* When memory is not explicitely mentioned in constructor, it is 0.  */
@@ -2906,14 +2944,14 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
 
 static tree
 fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
-                    unsigned HOST_WIDE_INT size)
+                    unsigned HOST_WIDE_INT size, tree from_decl)
 {
   tree ret;
 
   /* We found the field with exact match.  */
   if (useless_type_conversion_p (type, TREE_TYPE (ctor))
       && !offset)
-    return canonicalize_constructor_val (ctor);
+    return canonicalize_constructor_val (ctor, from_decl);
 
   /* We are at the end of walk, see if we can view convert the
      result.  */
@@ -2922,7 +2960,7 @@ fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
       && operand_equal_p (TYPE_SIZE (type),
                          TYPE_SIZE (TREE_TYPE (ctor)), 0))
     {
-      ret = canonicalize_constructor_val (ctor);
+      ret = canonicalize_constructor_val (ctor, from_decl);
       ret = fold_unary (VIEW_CONVERT_EXPR, type, ret);
       if (ret)
        STRIP_NOPS (ret);
@@ -2935,9 +2973,11 @@ fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
 
       if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE
          || TREE_CODE (TREE_TYPE (ctor)) == VECTOR_TYPE)
-       return fold_array_ctor_reference (type, ctor, offset, size);
+       return fold_array_ctor_reference (type, ctor, offset, size,
+                                         from_decl);
       else
-       return fold_nonarray_ctor_reference (type, ctor, offset, size);
+       return fold_nonarray_ctor_reference (type, ctor, offset, size,
+                                            from_decl);
     }
 
   return NULL_TREE;
@@ -2976,18 +3016,23 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
       if (TREE_CODE (TREE_OPERAND (t, 1)) == SSA_NAME
          && valueize
          && (idx = (*valueize) (TREE_OPERAND (t, 1)))
-         && host_integerp (idx, 0))
+         && TREE_CODE (idx) == INTEGER_CST)
        {
          tree low_bound, unit_size;
+         double_int doffset;
 
          /* If the resulting bit-offset is constant, track it.  */
          if ((low_bound = array_ref_low_bound (t),
-              host_integerp (low_bound, 0))
+              TREE_CODE (low_bound) == INTEGER_CST)
              && (unit_size = array_ref_element_size (t),
-                 host_integerp (unit_size, 1)))
+                 host_integerp (unit_size, 1))
+             && (doffset = double_int_sext
+                           (double_int_sub (TREE_INT_CST (idx),
+                                            TREE_INT_CST (low_bound)),
+                            TYPE_PRECISION (TREE_TYPE (idx))),
+                 double_int_fits_in_shwi_p (doffset)))
            {
-             offset = TREE_INT_CST_LOW (idx);
-             offset -= TREE_INT_CST_LOW (low_bound);
+             offset = double_int_to_shwi (doffset);
              offset *= TREE_INT_CST_LOW (unit_size);
              offset *= BITS_PER_UNIT;
 
@@ -3005,7 +3050,8 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
                return NULL_TREE;
              return fold_ctor_reference (TREE_TYPE (t), ctor, offset,
                                          TREE_INT_CST_LOW (unit_size)
-                                         * BITS_PER_UNIT);
+                                         * BITS_PER_UNIT,
+                                         base);
            }
        }
       /* Fallthru.  */
@@ -3031,7 +3077,8 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
       if (offset < 0)
        return NULL_TREE;
 
-      return fold_ctor_reference (TREE_TYPE (t), ctor, offset, size);
+      return fold_ctor_reference (TREE_TYPE (t), ctor, offset, size,
+                                 base);
 
     case REALPART_EXPR:
     case IMAGPART_EXPR:
@@ -3065,9 +3112,9 @@ tree
 gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo)
 {
   unsigned HOST_WIDE_INT offset, size;
-  tree v, fn;
+  tree v, fn, vtable;
 
-  v = BINFO_VTABLE (known_binfo);
+  vtable = v = BINFO_VTABLE (known_binfo);
   /* If there is no virtual methods table, leave the OBJ_TYPE_REF alone.  */
   if (!v)
     return NULL_TREE;
@@ -3093,8 +3140,8 @@ gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo)
   size = tree_low_cst (TYPE_SIZE (TREE_TYPE (TREE_TYPE (v))), 1);
   offset += token * size;
   fn = fold_ctor_reference (TREE_TYPE (TREE_TYPE (v)), DECL_INITIAL (v),
-                           offset, size);
-  if (!fn)
+                           offset, size, vtable);
+  if (!fn || integer_zerop (fn))
     return NULL_TREE;
   gcc_assert (TREE_CODE (fn) == ADDR_EXPR
              || TREE_CODE (fn) == FDESC_EXPR);
@@ -3105,9 +3152,14 @@ gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo)
      devirtualize.  This can happen in WHOPR when the actual method
      ends up in other partition, because we found devirtualization
      possibility too late.  */
-  if (!can_refer_decl_in_current_unit_p (fn))
+  if (!can_refer_decl_in_current_unit_p (fn, vtable))
     return NULL_TREE;
 
+  /* Make sure we create a cgraph node for functions we'll reference.
+     They can be non-existent if the reference comes from an entry
+     of an external vtable for example.  */
+  cgraph_get_create_node (fn);
+
   return fn;
 }