OSDN Git Service

2009-10-01 Tobias Burnus <burnus@net-b.de>
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-ccp.c
index c331619..76ea0e4 100644 (file)
@@ -229,6 +229,7 @@ typedef enum
 static prop_value_t *const_val;
 
 static void canonicalize_float_value (prop_value_t *);
+static bool ccp_fold_stmt (gimple_stmt_iterator *);
 
 /* Dump constant propagation value VAL to file OUTF prefixed by PREFIX.  */
 
@@ -276,7 +277,8 @@ tree
 get_symbol_constant_value (tree sym)
 {
   if (TREE_STATIC (sym)
-      && TREE_READONLY (sym))
+      && (TREE_READONLY (sym)
+         || TREE_CODE (sym) == CONST_DECL))
     {
       tree val = DECL_INITIAL (sym);
       if (val)
@@ -288,7 +290,11 @@ get_symbol_constant_value (tree sym)
                {
                  tree base = get_base_address (TREE_OPERAND (val, 0));
                  if (base && TREE_CODE (base) == VAR_DECL)
-                   add_referenced_var (base);
+                   {
+                     TREE_ADDRESSABLE (base) = 1;
+                     if (gimple_referenced_vars (cfun))
+                       add_referenced_var (base);
+                   }
                }
              return val;
            }
@@ -645,7 +651,15 @@ ccp_initialize (void)
       for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
         {
          gimple stmt = gsi_stmt (i);
-         bool is_varying = surely_varying_stmt_p (stmt);
+         bool is_varying;
+
+         /* If the statement is a control insn, then we do not
+            want to avoid simulating the statement once.  Failure
+            to do so means that those edges will never get added.  */
+         if (stmt_ends_bb_p (stmt))
+           is_varying = false;
+         else
+           is_varying = surely_varying_stmt_p (stmt);
 
          if (is_varying)
            {
@@ -711,7 +725,7 @@ ccp_finalize (void)
 
   do_dbg_cnt ();
   /* Perform substitutions based on the known constant values.  */
-  something_changed = substitute_and_fold (const_val, false);
+  something_changed = substitute_and_fold (const_val, ccp_fold_stmt);
 
   free (const_val);
   const_val = NULL;
@@ -910,6 +924,7 @@ may_propagate_address_into_dereference (tree addr, tree deref)
 static tree
 ccp_fold (gimple stmt)
 {
+  location_t loc = gimple_location (stmt);
   switch (gimple_code (stmt))
     {
     case GIMPLE_ASSIGN:
@@ -958,6 +973,30 @@ ccp_fold (gimple stmt)
                        }
                    }
                }
+             else if (TREE_CODE (rhs) == CONSTRUCTOR
+                      && TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE
+                      && (CONSTRUCTOR_NELTS (rhs)
+                          == TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
+               {
+                 unsigned i;
+                 tree val, list;
+
+                 list = NULL_TREE;
+                 FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val)
+                   {
+                     if (TREE_CODE (val) == SSA_NAME
+                         && get_value (val)->lattice_val == CONSTANT)
+                       val = get_value (val)->value;
+                     if (TREE_CODE (val) == INTEGER_CST
+                         || TREE_CODE (val) == REAL_CST
+                         || TREE_CODE (val) == FIXED_CST)
+                       list = tree_cons (NULL_TREE, val, list);
+                     else
+                       return NULL_TREE;
+                   }
+
+                 return build_vector (TREE_TYPE (rhs), nreverse (list));
+               }
 
               if (kind == tcc_reference)
                {
@@ -968,7 +1007,8 @@ ccp_fold (gimple stmt)
                    {
                      prop_value_t *val = get_value (TREE_OPERAND (rhs, 0));
                      if (val->lattice_val == CONSTANT)
-                       return fold_unary (TREE_CODE (rhs),
+                       return fold_unary_loc (EXPR_LOCATION (rhs),
+                                          TREE_CODE (rhs),
                                           TREE_TYPE (rhs), val->value);
                    }
                  else if (TREE_CODE (rhs) == INDIRECT_REF
@@ -1024,14 +1064,16 @@ ccp_fold (gimple stmt)
                  if (!useless_type_conversion_p (TREE_TYPE (lhs),
                                                  TREE_TYPE (op0))
                      && ((tem = maybe_fold_offset_to_address
-                                  (op0, integer_zero_node, TREE_TYPE (lhs)))
+                          (loc,
+                           op0, integer_zero_node, TREE_TYPE (lhs)))
                          != NULL_TREE))
                    return tem;
                  return op0;
                }
 
-              return fold_unary_ignore_overflow (subcode,
-                                                gimple_expr_type (stmt), op0);
+              return 
+               fold_unary_ignore_overflow_loc (loc, subcode,
+                                               gimple_expr_type (stmt), op0);
             }
 
           case GIMPLE_BINARY_RHS:
@@ -1060,14 +1102,14 @@ ccp_fold (gimple stmt)
                  && TREE_CODE (op0) == ADDR_EXPR
                  && TREE_CODE (op1) == INTEGER_CST)
                {
-                 tree lhs = gimple_assign_lhs (stmt);
-                 tree tem = maybe_fold_offset_to_address (op0, op1,
-                                                          TREE_TYPE (lhs));
+                 tree tem = maybe_fold_offset_to_address
+                   (loc, op0, op1, TREE_TYPE (op0));
                  if (tem != NULL_TREE)
                    return tem;
                }
 
-              return fold_binary (subcode, gimple_expr_type (stmt), op0, op1);
+              return fold_binary_loc (loc, subcode,
+                                 gimple_expr_type (stmt), op0, op1);
             }
 
           default:
@@ -1104,9 +1146,10 @@ ccp_fold (gimple stmt)
                      args[i] = val->value;
                  }
              }
-           call = build_call_array (gimple_call_return_type (stmt),
-                                    fn, gimple_call_num_args (stmt), args);
-           retval = fold_call_expr (call, false);
+           call = build_call_array_loc (loc,
+                                        gimple_call_return_type (stmt),
+                                        fn, gimple_call_num_args (stmt), args);
+           retval = fold_call_expr (EXPR_LOCATION (call), call, false);
            if (retval)
              /* fold_call_expr wraps the result inside a NOP_EXPR.  */
              STRIP_NOPS (retval);
@@ -1137,7 +1180,7 @@ ccp_fold (gimple stmt)
               op1 = val->value;
           }
 
-        return fold_binary (code, boolean_type_node, op0, op1);
+        return fold_binary_loc (loc, code, boolean_type_node, op0, op1);
       }
 
     case GIMPLE_SWITCH:
@@ -1319,7 +1362,8 @@ fold_const_aggregate_ref (tree t)
       {
        tree c = fold_const_aggregate_ref (TREE_OPERAND (t, 0));
        if (c && TREE_CODE (c) == COMPLEX_CST)
-         return fold_build1 (TREE_CODE (t), TREE_TYPE (t), c);
+         return fold_build1_loc (EXPR_LOCATION (t),
+                             TREE_CODE (t), TREE_TYPE (t), c);
        break;
       }
 
@@ -1380,8 +1424,8 @@ evaluate_stmt (gimple stmt)
       else if (code == GIMPLE_SWITCH)
         simplified = gimple_switch_index (stmt);
       else
-        /* These cannot satisfy is_gimple_min_invariant without folding.  */
-        gcc_assert (code == GIMPLE_CALL || code == GIMPLE_COND);
+       /* These cannot satisfy is_gimple_min_invariant without folding.  */
+       gcc_assert (code == GIMPLE_CALL || code == GIMPLE_COND);
     }
 
   is_constant = simplified && is_gimple_min_invariant (simplified);
@@ -1429,6 +1473,34 @@ evaluate_stmt (gimple stmt)
   return val;
 }
 
+/* Fold the stmt at *GSI with CCP specific information that propagating
+   and regular folding does not catch.  */
+
+static bool
+ccp_fold_stmt (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  prop_value_t val;
+
+  if (gimple_code (stmt) != GIMPLE_COND)
+    return false;
+
+  /* Statement evaluation will handle type mismatches in constants
+     more gracefully than the final propagation.  This allows us to
+     fold more conditionals here.  */
+  val = evaluate_stmt (stmt);
+  if (val.lattice_val != CONSTANT
+      || TREE_CODE (val.value) != INTEGER_CST)
+    return false;
+
+  if (integer_zerop (val.value))
+    gimple_cond_make_false (stmt);
+  else 
+    gimple_cond_make_true (stmt);
+
+  return true;
+}
+
 /* Visit the assignment statement STMT.  Set the value of its LHS to the
    value computed by the RHS and store LHS in *OUTPUT_P.  If STMT
    creates virtual definitions, set the value of each new name to that
@@ -1620,10 +1692,13 @@ struct gimple_opt_pass pass_ccp =
 
 /* A subroutine of fold_stmt.  Attempts to fold *(A+O) to A[X].
    BASE is an array type.  OFFSET is a byte displacement.  ORIG_TYPE
-   is the desired result type.  */
+   is the desired result type.
+
+   LOC is the location of the original expression.  */
 
 static tree
-maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type,
+maybe_fold_offset_to_array_ref (location_t loc, tree base, tree offset,
+                               tree orig_type,
                                bool allow_negative_idx)
 {
   tree min_idx, idx, idx_type, elt_offset = integer_zero_node;
@@ -1756,17 +1831,23 @@ maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type,
           && compare_tree_int (idx, 0) < 0)
     return NULL_TREE;
 
-  return build4 (ARRAY_REF, elt_type, base, idx, NULL_TREE, NULL_TREE);
+  {
+    tree t = build4 (ARRAY_REF, elt_type, base, idx, NULL_TREE, NULL_TREE);
+    SET_EXPR_LOCATION (t, loc);
+    return t;
+  }
 }
 
 
 /* Attempt to fold *(S+O) to S.X.
    BASE is a record type.  OFFSET is a byte displacement.  ORIG_TYPE
-   is the desired result type.  */
+   is the desired result type.
+
+   LOC is the location of the original expression.  */
 
 static tree
-maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
-                                   tree orig_type, bool base_is_ptr)
+maybe_fold_offset_to_component_ref (location_t loc, tree record_type,
+                                   tree base, tree offset, tree orig_type)
 {
   tree f, t, field_type, tail_array_field, field_offset;
   tree ret;
@@ -1818,8 +1899,6 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
       if (cmp == 0
          && useless_type_conversion_p (orig_type, field_type))
        {
-         if (base_is_ptr)
-           base = build1 (INDIRECT_REF, record_type, base);
          t = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
          return t;
        }
@@ -1844,19 +1923,16 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
 
       /* If we matched, then set offset to the displacement into
         this field.  */
-      if (base_is_ptr)
-       new_base = build1 (INDIRECT_REF, record_type, base);
-      else
-       new_base = base;
-      new_base = build3 (COMPONENT_REF, field_type, new_base, f, NULL_TREE);
+      new_base = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
+      SET_EXPR_LOCATION (new_base, loc);
 
       /* Recurse to possibly find the match.  */
-      ret = maybe_fold_offset_to_array_ref (new_base, t, orig_type,
+      ret = maybe_fold_offset_to_array_ref (loc, new_base, t, orig_type,
                                            f == TYPE_FIELDS (record_type));
       if (ret)
        return ret;
-      ret = maybe_fold_offset_to_component_ref (field_type, new_base, t,
-                                               orig_type, false);
+      ret = maybe_fold_offset_to_component_ref (loc, field_type, new_base, t,
+                                               orig_type);
       if (ret)
        return ret;
     }
@@ -1870,97 +1946,90 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
 
   /* If we get here, we've got an aggregate field, and a possibly 
      nonzero offset into them.  Recurse and hope for a valid match.  */
-  if (base_is_ptr)
-    base = build1 (INDIRECT_REF, record_type, base);
   base = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
+  SET_EXPR_LOCATION (base, loc);
 
-  t = maybe_fold_offset_to_array_ref (base, offset, orig_type,
+  t = maybe_fold_offset_to_array_ref (loc, base, offset, orig_type,
                                      f == TYPE_FIELDS (record_type));
   if (t)
     return t;
-  return maybe_fold_offset_to_component_ref (field_type, base, offset,
-                                            orig_type, false);
+  return maybe_fold_offset_to_component_ref (loc, field_type, base, offset,
+                                            orig_type);
 }
 
 /* Attempt to express (ORIG_TYPE)BASE+OFFSET as BASE->field_of_orig_type
-   or BASE[index] or by combination of those. 
+   or BASE[index] or by combination of those.
+
+   LOC is the location of original expression.
 
    Before attempting the conversion strip off existing ADDR_EXPRs and
    handled component refs.  */
 
 tree
-maybe_fold_offset_to_reference (tree base, tree offset, tree orig_type)
+maybe_fold_offset_to_reference (location_t loc, tree base, tree offset,
+                               tree orig_type)
 {
   tree ret;
   tree type;
-  bool base_is_ptr = true;
 
   STRIP_NOPS (base);
-  if (TREE_CODE (base) == ADDR_EXPR)
-    {
-      base_is_ptr = false;
-
-      base = TREE_OPERAND (base, 0);
+  if (TREE_CODE (base) != ADDR_EXPR)
+    return NULL_TREE;
 
-      /* Handle case where existing COMPONENT_REF pick e.g. wrong field of union,
-        so it needs to be removed and new COMPONENT_REF constructed.
-        The wrong COMPONENT_REF are often constructed by folding the
-        (type *)&object within the expression (type *)&object+offset  */
-      if (handled_component_p (base))
+  base = TREE_OPERAND (base, 0);
+
+  /* Handle case where existing COMPONENT_REF pick e.g. wrong field of union,
+     so it needs to be removed and new COMPONENT_REF constructed.
+     The wrong COMPONENT_REF are often constructed by folding the
+     (type *)&object within the expression (type *)&object+offset  */
+  if (handled_component_p (base))
+    {
+      HOST_WIDE_INT sub_offset, size, maxsize;
+      tree newbase;
+      newbase = get_ref_base_and_extent (base, &sub_offset,
+                                        &size, &maxsize);
+      gcc_assert (newbase);
+      if (size == maxsize
+         && size != -1
+         && !(sub_offset & (BITS_PER_UNIT - 1)))
        {
-          HOST_WIDE_INT sub_offset, size, maxsize;
-         tree newbase;
-         newbase = get_ref_base_and_extent (base, &sub_offset,
-                                            &size, &maxsize);
-         gcc_assert (newbase);
-         if (size == maxsize
-             && size != -1
-             && !(sub_offset & (BITS_PER_UNIT - 1)))
-           {
-             base = newbase;
-             if (sub_offset)
-               offset = int_const_binop (PLUS_EXPR, offset,
-                                         build_int_cst (TREE_TYPE (offset),
-                                         sub_offset / BITS_PER_UNIT), 1);
-           }
+         base = newbase;
+         if (sub_offset)
+           offset = int_const_binop (PLUS_EXPR, offset,
+                                     build_int_cst (TREE_TYPE (offset),
+                                                    sub_offset / BITS_PER_UNIT), 1);
        }
-      if (useless_type_conversion_p (orig_type, TREE_TYPE (base))
-         && integer_zerop (offset))
-       return base;
-      type = TREE_TYPE (base);
     }
-  else
-    {
-      base_is_ptr = true;
-      if (!POINTER_TYPE_P (TREE_TYPE (base)))
-       return NULL_TREE;
-      type = TREE_TYPE (TREE_TYPE (base));
-    }
-  ret = maybe_fold_offset_to_component_ref (type, base, offset,
-                                           orig_type, base_is_ptr);
+  if (useless_type_conversion_p (orig_type, TREE_TYPE (base))
+      && integer_zerop (offset))
+    return base;
+  type = TREE_TYPE (base);
+
+  ret = maybe_fold_offset_to_component_ref (loc, type, base, offset, orig_type);
   if (!ret)
-    {
-      if (base_is_ptr)
-       base = build1 (INDIRECT_REF, type, base);
-      ret = maybe_fold_offset_to_array_ref (base, offset, orig_type, true);
-    }
+    ret = maybe_fold_offset_to_array_ref (loc, base, offset, orig_type, true);
+
   return ret;
 }
 
 /* Attempt to express (ORIG_TYPE)&BASE+OFFSET as &BASE->field_of_orig_type
    or &BASE[index] or by combination of those.
 
+   LOC is the location of the original expression.
+
    Before attempting the conversion strip off existing component refs.  */
 
 tree
-maybe_fold_offset_to_address (tree addr, tree offset, tree orig_type)
+maybe_fold_offset_to_address (location_t loc, tree addr, tree offset,
+                             tree orig_type)
 {
   tree t;
 
   gcc_assert (POINTER_TYPE_P (TREE_TYPE (addr))
              && POINTER_TYPE_P (orig_type));
 
-  t = maybe_fold_offset_to_reference (addr, offset, TREE_TYPE (orig_type));
+  t = maybe_fold_offset_to_reference (loc, addr, offset,
+                                     TREE_TYPE (orig_type));
   if (t != NULL_TREE)
     {
       tree orig = addr;
@@ -1997,7 +2066,7 @@ maybe_fold_offset_to_address (tree addr, tree offset, tree orig_type)
       ptr_type = build_pointer_type (TREE_TYPE (t));
       if (!useless_type_conversion_p (orig_type, ptr_type))
        return NULL_TREE;
-      return build_fold_addr_expr_with_type (t, ptr_type);
+      return build_fold_addr_expr_with_type_loc (loc, t, ptr_type);
     }
 
   return NULL_TREE;
@@ -2011,6 +2080,7 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
 {
   tree t;
   bool volatile_p = TREE_THIS_VOLATILE (expr);
+  location_t loc = EXPR_LOCATION (expr);
 
   /* We may well have constructed a double-nested PLUS_EXPR via multiple
      substitutions.  Fold that down to one.  Remove NON_LVALUE_EXPRs that
@@ -2051,7 +2121,7 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
        return DECL_INITIAL (base);
 
       /* Try folding *(&B+O) to B.X.  */
-      t = maybe_fold_offset_to_reference (base_addr, offset,
+      t = maybe_fold_offset_to_reference (loc, base_addr, offset,
                                          TREE_TYPE (expr));
       if (t)
        {
@@ -2090,7 +2160,7 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
       /* Try folding *(B+O) to B->X.  Still an improvement.  */
       if (POINTER_TYPE_P (TREE_TYPE (base)))
        {
-          t = maybe_fold_offset_to_reference (base, offset,
+          t = maybe_fold_offset_to_reference (loc, base, offset,
                                              TREE_TYPE (expr));
          if (t)
            return t;
@@ -2115,7 +2185,7 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
    which may be able to propagate further.  */
 
 tree
-maybe_fold_stmt_addition (tree res_type, tree op0, tree op1)
+maybe_fold_stmt_addition (location_t loc, tree res_type, tree op0, tree op1)
 {
   tree ptd_type;
   tree t;
@@ -2143,16 +2213,16 @@ maybe_fold_stmt_addition (tree res_type, tree op0, tree op1)
              && TREE_CODE (gimple_assign_rhs2 (offset_def)) == INTEGER_CST
              && tree_int_cst_equal (gimple_assign_rhs2 (offset_def),
                                     TYPE_SIZE_UNIT (TREE_TYPE (op0))))
-           return build1 (ADDR_EXPR, res_type,
-                          build4 (ARRAY_REF, TREE_TYPE (op0),
+           return build_fold_addr_expr
+                         (build4 (ARRAY_REF, TREE_TYPE (op0),
                                   TREE_OPERAND (op0, 0),
                                   gimple_assign_rhs1 (offset_def),
                                   TREE_OPERAND (op0, 2),
                                   TREE_OPERAND (op0, 3)));
          else if (integer_onep (TYPE_SIZE_UNIT (TREE_TYPE (op0)))
                   && gimple_assign_rhs_code (offset_def) != MULT_EXPR)
-           return build1 (ADDR_EXPR, res_type,
-                          build4 (ARRAY_REF, TREE_TYPE (op0),
+           return build_fold_addr_expr
+                         (build4 (ARRAY_REF, TREE_TYPE (op0),
                                   TREE_OPERAND (op0, 0),
                                   op1,
                                   TREE_OPERAND (op0, 2),
@@ -2212,12 +2282,15 @@ maybe_fold_stmt_addition (tree res_type, tree op0, tree op1)
     ptd_type = TREE_TYPE (TREE_TYPE (op0));
 
   /* At which point we can try some of the same things as for indirects.  */
-  t = maybe_fold_offset_to_array_ref (op0, op1, ptd_type, true);
+  t = maybe_fold_offset_to_array_ref (loc, op0, op1, ptd_type, true);
   if (!t)
-    t = maybe_fold_offset_to_component_ref (TREE_TYPE (op0), op0, op1,
-                                           ptd_type, false);
+    t = maybe_fold_offset_to_component_ref (loc, TREE_TYPE (op0), op0, op1,
+                                           ptd_type);
   if (t)
-    t = build1 (ADDR_EXPR, res_type, t);
+    {
+      t = build1 (ADDR_EXPR, res_type, t);
+      SET_EXPR_LOCATION (t, loc);
+    }
 
   return t;
 }
@@ -2275,6 +2348,19 @@ maybe_fold_reference (tree expr, bool is_lhs)
          return expr;
        }
     }
+  else if (!is_lhs
+          && DECL_P (*t))
+    {
+      tree tem = get_symbol_constant_value (*t);
+      if (tem)
+       {
+         *t = tem;
+         tem = maybe_fold_reference (expr, is_lhs);
+         if (tem)
+           return tem;
+         return expr;
+       }
+    }
 
   return NULL_TREE;
 }
@@ -2413,6 +2499,7 @@ ccp_fold_builtin (gimple stmt)
   bitmap visited;
   bool ignore;
   int nargs;
+  location_t loc = gimple_location (stmt);
 
   gcc_assert (is_gimple_call (stmt));
 
@@ -2509,7 +2596,7 @@ ccp_fold_builtin (gimple stmt)
 
     case BUILT_IN_STRCPY:
       if (val[1] && is_gimple_val (val[1]) && nargs == 2)
-       result = fold_builtin_strcpy (callee,
+       result = fold_builtin_strcpy (loc, callee,
                                       gimple_call_arg (stmt, 0),
                                       gimple_call_arg (stmt, 1),
                                      val[1]);
@@ -2517,7 +2604,7 @@ ccp_fold_builtin (gimple stmt)
 
     case BUILT_IN_STRNCPY:
       if (val[1] && is_gimple_val (val[1]) && nargs == 3)
-       result = fold_builtin_strncpy (callee,
+       result = fold_builtin_strncpy (loc, callee,
                                        gimple_call_arg (stmt, 0),
                                        gimple_call_arg (stmt, 1),
                                        gimple_call_arg (stmt, 2),
@@ -2526,14 +2613,14 @@ ccp_fold_builtin (gimple stmt)
 
     case BUILT_IN_FPUTS:
       if (nargs == 2)
-       result = fold_builtin_fputs (gimple_call_arg (stmt, 0),
+       result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0),
                                     gimple_call_arg (stmt, 1),
                                     ignore, false, val[0]);
       break;
 
     case BUILT_IN_FPUTS_UNLOCKED:
       if (nargs == 2)
-       result = fold_builtin_fputs (gimple_call_arg (stmt, 0),
+       result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0),
                                     gimple_call_arg (stmt, 1),
                                     ignore, true, val[0]);
       break;
@@ -2543,7 +2630,7 @@ ccp_fold_builtin (gimple stmt)
     case BUILT_IN_MEMMOVE_CHK:
     case BUILT_IN_MEMSET_CHK:
       if (val[2] && is_gimple_val (val[2]) && nargs == 4)
-       result = fold_builtin_memory_chk (callee,
+       result = fold_builtin_memory_chk (loc, callee,
                                           gimple_call_arg (stmt, 0),
                                           gimple_call_arg (stmt, 1),
                                           gimple_call_arg (stmt, 2),
@@ -2555,7 +2642,7 @@ ccp_fold_builtin (gimple stmt)
     case BUILT_IN_STRCPY_CHK:
     case BUILT_IN_STPCPY_CHK:
       if (val[1] && is_gimple_val (val[1]) && nargs == 3)
-       result = fold_builtin_stxcpy_chk (callee,
+       result = fold_builtin_stxcpy_chk (loc, callee,
                                           gimple_call_arg (stmt, 0),
                                           gimple_call_arg (stmt, 1),
                                           gimple_call_arg (stmt, 2),
@@ -2565,7 +2652,7 @@ ccp_fold_builtin (gimple stmt)
 
     case BUILT_IN_STRNCPY_CHK:
       if (val[2] && is_gimple_val (val[2]) && nargs == 4)
-       result = fold_builtin_strncpy_chk (gimple_call_arg (stmt, 0),
+       result = fold_builtin_strncpy_chk (loc, gimple_call_arg (stmt, 0),
                                            gimple_call_arg (stmt, 1),
                                            gimple_call_arg (stmt, 2),
                                            gimple_call_arg (stmt, 3),
@@ -2598,6 +2685,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
 {
   gimple stmt = gsi_stmt (*si);
   enum tree_code subcode = gimple_assign_rhs_code (stmt);
+  location_t loc = gimple_location (stmt);
 
   tree result = NULL_TREE;
 
@@ -2613,11 +2701,13 @@ fold_gimple_assign (gimple_stmt_iterator *si)
            tree op0 = COND_EXPR_COND (rhs);
            tree tem;
            bool set = false;
+           location_t cond_loc = EXPR_LOCATION (rhs);
 
            if (COMPARISON_CLASS_P (op0))
              {
                fold_defer_overflow_warnings ();
-               tem = fold_binary (TREE_CODE (op0), TREE_TYPE (op0),
+               tem = fold_binary_loc (cond_loc,
+                                  TREE_CODE (op0), TREE_TYPE (op0),
                                   TREE_OPERAND (op0, 0),
                                   TREE_OPERAND (op0, 1));
                /* This is actually a conditional expression, not a GIMPLE
@@ -2636,7 +2726,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
              return NULL_TREE;
 
            if (set)
-             result = fold_build3 (COND_EXPR, TREE_TYPE (rhs), tem,
+             result = fold_build3_loc (cond_loc, COND_EXPR, TREE_TYPE (rhs), tem,
                                    COND_EXPR_THEN (rhs), COND_EXPR_ELSE (rhs));
           }
 
@@ -2651,9 +2741,31 @@ fold_gimple_assign (gimple_stmt_iterator *si)
            tree tem = maybe_fold_reference (TREE_OPERAND (rhs, 0), true);
            if (tem)
              result = fold_convert (TREE_TYPE (rhs),
-                                    build_fold_addr_expr (tem));
+                                    build_fold_addr_expr_loc (loc, tem));
+         }
+
+       else if (TREE_CODE (rhs) == CONSTRUCTOR
+                && TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE
+                && (CONSTRUCTOR_NELTS (rhs)
+                    == TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
+         {
+           /* Fold a constant vector CONSTRUCTOR to VECTOR_CST.  */
+           unsigned i;
+           tree val;
+
+           FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val)
+             if (TREE_CODE (val) != INTEGER_CST
+                 && TREE_CODE (val) != REAL_CST
+                 && TREE_CODE (val) != FIXED_CST)
+               return NULL_TREE;
+
+           return build_vector_from_ctor (TREE_TYPE (rhs),
+                                          CONSTRUCTOR_ELTS (rhs));
          }
 
+       else if (DECL_P (rhs))
+         return get_symbol_constant_value (rhs);
+
         /* If we couldn't fold the RHS, hand over to the generic
            fold routines.  */
         if (result == NULL_TREE)
@@ -2675,7 +2787,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
       {
        tree rhs = gimple_assign_rhs1 (stmt);
 
-       result = fold_unary (subcode, gimple_expr_type (stmt), rhs);
+       result = fold_unary_loc (loc, subcode, gimple_expr_type (stmt), rhs);
        if (result)
          {
            /* If the operation was a conversion do _not_ mark a
@@ -2697,7 +2809,8 @@ fold_gimple_assign (gimple_stmt_iterator *si)
                 && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt))))
          {
            tree type = gimple_expr_type (stmt);
-           tree t = maybe_fold_offset_to_address (gimple_assign_rhs1 (stmt),
+           tree t = maybe_fold_offset_to_address (loc,
+                                                  gimple_assign_rhs1 (stmt),
                                                   integer_zero_node, type);
            if (t)
              return t;
@@ -2717,13 +2830,14 @@ fold_gimple_assign (gimple_stmt_iterator *si)
                    (TREE_TYPE (gimple_assign_lhs (stmt)), type))
                type = TREE_TYPE (gimple_assign_rhs1 (stmt));
            }
-         result = maybe_fold_stmt_addition (type,
+         result = maybe_fold_stmt_addition (gimple_location (stmt),
+                                            type,
                                             gimple_assign_rhs1 (stmt),
                                             gimple_assign_rhs2 (stmt));
        }
 
       if (!result)
-        result = fold_binary (subcode,
+        result = fold_binary_loc (loc, subcode,
                               TREE_TYPE (gimple_assign_lhs (stmt)),
                               gimple_assign_rhs1 (stmt),
                               gimple_assign_rhs2 (stmt));
@@ -2762,7 +2876,8 @@ fold_gimple_assign (gimple_stmt_iterator *si)
 static bool
 fold_gimple_cond (gimple stmt)
 {
-  tree result = fold_binary (gimple_cond_code (stmt),
+  tree result = fold_binary_loc (gimple_location (stmt),
+                            gimple_cond_code (stmt),
                              boolean_type_node,
                              gimple_cond_lhs (stmt),
                              gimple_cond_rhs (stmt));
@@ -2780,6 +2895,7 @@ fold_gimple_cond (gimple stmt)
   return false;
 }
 
+static void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
 
 /* Attempt to fold a call statement referenced by the statement iterator GSI.
    The statement may be replaced by another statement, e.g., if the call
@@ -2800,7 +2916,11 @@ fold_gimple_call (gimple_stmt_iterator *gsi)
       tree result = ccp_fold_builtin (stmt);
 
       if (result)
-        return update_call_from_tree (gsi, result);
+       {
+          if (!update_call_from_tree (gsi, result))
+           gimplify_and_update_call_from_tree (gsi, result);
+         return true;
+       }
     }
   else
     {
@@ -2974,9 +3094,8 @@ fold_stmt_inplace (gimple stmt)
 static tree
 optimize_stack_restore (gimple_stmt_iterator i)
 {
-  tree callee, rhs;
-  gimple stmt, stack_save;
-  gimple_stmt_iterator stack_save_gsi;
+  tree callee;
+  gimple stmt;
 
   basic_block bb = gsi_bb (i);
   gimple call = gsi_stmt (i);
@@ -3000,32 +3119,49 @@ optimize_stack_restore (gimple_stmt_iterator i)
        return NULL_TREE;
 
       if (DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_RESTORE)
-       break;
+       goto second_stack_restore;
     }
 
-  if (gsi_end_p (i)
-      && (! single_succ_p (bb)
-         || single_succ_edge (bb)->dest != EXIT_BLOCK_PTR))
+  if (!gsi_end_p (i))
     return NULL_TREE;
 
-  stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
-  if (gimple_code (stack_save) != GIMPLE_CALL
-      || gimple_call_lhs (stack_save) != gimple_call_arg (call, 0)
-      || stmt_could_throw_p (stack_save)
-      || !has_single_use (gimple_call_arg (call, 0)))
-    return NULL_TREE;
+  /* Allow one successor of the exit block, or zero successors.  */
+  switch (EDGE_COUNT (bb->succs))
+    {
+    case 0:
+      break;
+    case 1:
+      if (single_succ_edge (bb)->dest != EXIT_BLOCK_PTR)
+       return NULL_TREE;
+      break;
+    default:
+      return NULL_TREE;
+    }
+ second_stack_restore:
 
-  callee = gimple_call_fndecl (stack_save);
-  if (!callee
-      || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
-      || DECL_FUNCTION_CODE (callee) != BUILT_IN_STACK_SAVE
-      || gimple_call_num_args (stack_save) != 0)
-    return NULL_TREE;
+  /* If there's exactly one use, then zap the call to __builtin_stack_save.
+     If there are multiple uses, then the last one should remove the call.
+     In any case, whether the call to __builtin_stack_save can be removed
+     or not is irrelevant to removing the call to __builtin_stack_restore.  */
+  if (has_single_use (gimple_call_arg (call, 0)))
+    {
+      gimple stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
+      if (is_gimple_call (stack_save))
+       {
+         callee = gimple_call_fndecl (stack_save);
+         if (callee
+             && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
+             && DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_SAVE)
+           {
+             gimple_stmt_iterator stack_save_gsi;
+             tree rhs;
 
-  stack_save_gsi = gsi_for_stmt (stack_save);
-  rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
-  if (!update_call_from_tree (&stack_save_gsi, rhs))
-    return NULL_TREE;
+             stack_save_gsi = gsi_for_stmt (stack_save);
+             rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
+             update_call_from_tree (&stack_save_gsi, rhs);
+           }
+       }
+    }
 
   /* No effect, so the statement will be deleted.  */
   return integer_zero_node;
@@ -3041,6 +3177,7 @@ optimize_stdarg_builtin (gimple call)
 {
   tree callee, lhs, rhs, cfun_va_list;
   bool va_list_simple_ptr;
+  location_t loc = gimple_location (call);
 
   if (gimple_code (call) != GIMPLE_CALL)
     return NULL_TREE;
@@ -3069,10 +3206,10 @@ optimize_stdarg_builtin (gimple call)
             != TYPE_MAIN_VARIANT (cfun_va_list))
        return NULL_TREE;
       
-      lhs = build_fold_indirect_ref (lhs);
-      rhs = build_call_expr (built_in_decls[BUILT_IN_NEXT_ARG],
+      lhs = build_fold_indirect_ref_loc (loc, lhs);
+      rhs = build_call_expr_loc (loc, built_in_decls[BUILT_IN_NEXT_ARG],
                              1, integer_zero_node);
-      rhs = fold_convert (TREE_TYPE (lhs), rhs);
+      rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs);
       return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs);
 
     case BUILT_IN_VA_COPY:
@@ -3088,13 +3225,13 @@ optimize_stdarg_builtin (gimple call)
             != TYPE_MAIN_VARIANT (cfun_va_list))
        return NULL_TREE;
 
-      lhs = build_fold_indirect_ref (lhs);
+      lhs = build_fold_indirect_ref_loc (loc, lhs);
       rhs = gimple_call_arg (call, 1);
       if (TYPE_MAIN_VARIANT (TREE_TYPE (rhs))
          != TYPE_MAIN_VARIANT (cfun_va_list))
        return NULL_TREE;
 
-      rhs = fold_convert (TREE_TYPE (lhs), rhs);
+      rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs);
       return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs);
 
     case BUILT_IN_VA_END: