OSDN Git Service

2009-08-28 Sebastian Pop <sebastian.pop@amd.com>
[pf3gnuchains/gcc-fork.git] / gcc / tree-inline.c
index 78cb716..97c9261 100644 (file)
@@ -119,7 +119,6 @@ eni_weights eni_time_weights;
 /* Prototypes.  */
 
 static tree declare_return_variable (copy_body_data *, tree, tree, tree *);
-static bool inlinable_function_p (tree);
 static void remap_block (tree *, copy_body_data *);
 static void copy_bind_expr (tree *, int *, copy_body_data *);
 static tree mark_local_for_remap_r (tree *, int *, void *);
@@ -288,7 +287,10 @@ remap_decl (tree decl, copy_body_data *id)
       return t;
     }
 
-  return unshare_expr (*n);
+  if (id->do_not_unshare)
+    return *n;
+  else
+    return unshare_expr (*n);
 }
 
 static tree
@@ -501,7 +503,7 @@ remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id)
              && (var_ann (old_var) || !gimple_in_ssa_p (cfun)))
            cfun->local_decls = tree_cons (NULL_TREE, old_var,
                                                   cfun->local_decls);
-         if (debug_info_level > DINFO_LEVEL_TERSE
+         if ((!optimize || debug_info_level > DINFO_LEVEL_TERSE)
              && !DECL_IGNORED_P (old_var)
              && nonlocalized_list)
            VEC_safe_push (tree, gc, *nonlocalized_list, origin_var);
@@ -519,7 +521,7 @@ remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id)
        ;
       else if (!new_var)
         {
-         if (debug_info_level > DINFO_LEVEL_TERSE
+         if ((!optimize || debug_info_level > DINFO_LEVEL_TERSE)
              && !DECL_IGNORED_P (old_var)
              && nonlocalized_list)
            VEC_safe_push (tree, gc, *nonlocalized_list, origin_var);
@@ -598,10 +600,16 @@ copy_statement_list (tree *tp)
   new_tree = alloc_stmt_list ();
   ni = tsi_start (new_tree);
   oi = tsi_start (*tp);
+  TREE_TYPE (new_tree) = TREE_TYPE (*tp);
   *tp = new_tree;
 
   for (; !tsi_end_p (oi); tsi_next (&oi))
-    tsi_link_after (&ni, tsi_stmt (oi), TSI_NEW_STMT);
+    {
+      tree stmt = tsi_stmt (oi);
+      if (TREE_CODE (stmt) == STATEMENT_LIST)
+       copy_statement_list (&stmt);
+      tsi_link_after (&ni, stmt, TSI_CONTINUE_LINKING);
+    }
 }
 
 static void
@@ -776,7 +784,8 @@ remap_gimple_op_r (tree *tp, int *walk_subtrees, void *data)
                {
                  if (TREE_CODE (new_tree) == ADDR_EXPR)
                    {
-                     *tp = fold_indirect_ref_1 (type, new_tree);
+                     *tp = fold_indirect_ref_1 (EXPR_LOCATION (new_tree),
+                                                type, new_tree);
                      /* ???  We should either assert here or build
                         a VIEW_CONVERT_EXPR instead of blindly leaking
                         incompatible types to our IL.  */
@@ -918,7 +927,8 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data)
     }
   else if (TREE_CODE (*tp) == STATEMENT_LIST)
     copy_statement_list (tp);
-  else if (TREE_CODE (*tp) == SAVE_EXPR)
+  else if (TREE_CODE (*tp) == SAVE_EXPR
+          || TREE_CODE (*tp) == TARGET_EXPR)
     remap_save_expr (tp, id->decl_map, walk_subtrees);
   else if (TREE_CODE (*tp) == LABEL_DECL
           && (! DECL_CONTEXT (*tp)
@@ -974,7 +984,7 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data)
              STRIP_TYPE_NOPS (value);
              if (TREE_CONSTANT (value) || TREE_READONLY (value))
                {
-                 *tp = build_empty_stmt ();
+                 *tp = build_empty_stmt (EXPR_LOCATION (*tp));
                  return copy_tree_body_r (tp, walk_subtrees, data);
                }
            }
@@ -998,14 +1008,18 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data)
                 but we absolutely rely on that.  As fold_indirect_ref
                 does other useful transformations, try that first, though.  */
              tree type = TREE_TYPE (TREE_TYPE (*n));
-             new_tree = unshare_expr (*n);
+             if (id->do_not_unshare)
+               new_tree = *n;
+             else
+               new_tree = unshare_expr (*n);
              old = *tp;
              *tp = gimple_fold_indirect_ref (new_tree);
              if (! *tp)
                {
                  if (TREE_CODE (new_tree) == ADDR_EXPR)
                    {
-                     *tp = fold_indirect_ref_1 (type, new_tree);
+                     *tp = fold_indirect_ref_1 (EXPR_LOCATION (new_tree),
+                                                type, new_tree);
                      /* ???  We should either assert here or build
                         a VIEW_CONVERT_EXPR instead of blindly leaking
                         incompatible types to our IL.  */
@@ -1370,8 +1384,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale,
        {
          tree new_rhs;
          new_rhs = force_gimple_operand_gsi (&seq_gsi,
-                                             gimple_assign_rhs1 (stmt),
-                                             true, NULL, true, GSI_SAME_STMT);
+                                             gimple_assign_rhs1 (stmt),
+                                             true, NULL, false, GSI_NEW_STMT);
          gimple_assign_set_rhs1 (stmt, new_rhs);
          id->regimplify = false;
        }
@@ -1483,64 +1497,69 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale,
             callgraph edges and update or duplicate them.  */
          if (is_gimple_call (stmt))
            {
-             struct cgraph_edge *edge = cgraph_edge (id->src_node, orig_stmt);
+             struct cgraph_edge *edge;
              int flags;
 
              switch (id->transform_call_graph_edges)
                {
-             case CB_CGE_DUPLICATE:
-               if (edge)
-                 cgraph_clone_edge (edge, id->dst_node, stmt,
-                                          REG_BR_PROB_BASE, 1,
-                                          edge->frequency, true);
-               break;
-
-             case CB_CGE_MOVE_CLONES:
-               cgraph_set_call_stmt_including_clones (id->dst_node, orig_stmt, stmt);
-               break;
-
-             case CB_CGE_MOVE:
-               if (edge)
-                 cgraph_set_call_stmt (edge, stmt);
-               break;
-
-             default:
-               gcc_unreachable ();
+               case CB_CGE_DUPLICATE:
+                 edge = cgraph_edge (id->src_node, orig_stmt);
+                 if (edge)
+                   edge = cgraph_clone_edge (edge, id->dst_node, stmt,
+                                             REG_BR_PROB_BASE, 1,
+                                             edge->frequency, true);
+                 break;
+
+               case CB_CGE_MOVE_CLONES:
+                 cgraph_set_call_stmt_including_clones (id->dst_node,
+                                                        orig_stmt, stmt);
+                 edge = cgraph_edge (id->dst_node, stmt);
+                 break;
+
+               case CB_CGE_MOVE:
+                 edge = cgraph_edge (id->dst_node, orig_stmt);
+                 if (edge)
+                   cgraph_set_call_stmt (edge, stmt);
+                 break;
+
+               default:
+                 gcc_unreachable ();
                }
 
-           /* Constant propagation on argument done during inlining
-              may create new direct call.  Produce an edge for it.  */
-           if (!edge && is_gimple_call (stmt)
-               && (fn = gimple_call_fndecl (stmt)) != NULL
-               && !cgraph_edge (id->dst_node, stmt))
-             {
-               struct cgraph_node *dest = cgraph_node (fn);
-
-               /* We have missing edge in the callgraph.  This can happen in one case
-                  where previous inlining turned indirect call into direct call by
-                  constant propagating arguments.  In all other cases we hit a bug
-                  (incorrect node sharing is most common reason for missing edges.  */
-               gcc_assert (dest->needed || !dest->analyzed);
-               if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES)
-                 cgraph_create_edge_including_clones (id->dst_node, dest, stmt,
-                                                      bb->count,
-                                                      compute_call_stmt_bb_frequency (id->dst_node->decl, bb),
-                                                      bb->loop_depth,
-                                                      CIF_ORIGINALLY_INDIRECT_CALL);
-               else
-                 cgraph_create_edge (id->dst_node, dest, stmt,
-                                     bb->count, CGRAPH_FREQ_BASE,
-                                     bb->loop_depth)->inline_failed
-                   = CIF_ORIGINALLY_INDIRECT_CALL;
-               if (dump_file)
-                 {
-                    fprintf (dump_file, "Created new direct edge to %s",
-                             cgraph_node_name (dest));
-                 }
-             }
+             /* Constant propagation on argument done during inlining
+                may create new direct call.  Produce an edge for it.  */
+             if ((!edge 
+                  || (edge->indirect_call
+                      && id->transform_call_graph_edges == CB_CGE_MOVE_CLONES))
+                 && is_gimple_call (stmt)
+                 && (fn = gimple_call_fndecl (stmt)) != NULL)
+               {
+                 struct cgraph_node *dest = cgraph_node (fn);
+
+                 /* We have missing edge in the callgraph.  This can happen
+                    when previous inlining turned an indirect call into a
+                    direct call by constant propagating arguments.  In all
+                    other cases we hit a bug (incorrect node sharing is the
+                    most common reason for missing edges).  */
+                 gcc_assert (dest->needed || !dest->analyzed);
+                 if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES)
+                   cgraph_create_edge_including_clones
+                     (id->dst_node, dest, stmt, bb->count,
+                      compute_call_stmt_bb_frequency (id->dst_node->decl, bb),
+                      bb->loop_depth, CIF_ORIGINALLY_INDIRECT_CALL);
+                 else
+                   cgraph_create_edge (id->dst_node, dest, stmt,
+                                       bb->count, CGRAPH_FREQ_BASE,
+                                       bb->loop_depth)->inline_failed
+                     = CIF_ORIGINALLY_INDIRECT_CALL;
+                 if (dump_file)
+                   {
+                     fprintf (dump_file, "Created new direct edge to %s",
+                              cgraph_node_name (dest));
+                   }
+               }
 
              flags = gimple_call_flags (stmt);
-
              if (flags & ECF_MAY_BE_ALLOCA)
                cfun->calls_alloca = true;
              if (flags & ECF_RETURNS_TWICE)
@@ -1810,7 +1829,8 @@ copy_phis_for_bb (basic_block bb, copy_body_data *id)
                  new_arg = force_gimple_operand (new_arg, &stmts, true, NULL);
                  gsi_insert_seq_on_edge_immediate (new_edge, stmts);
                }
-             add_phi_arg (new_phi, new_arg, new_edge);
+             add_phi_arg (new_phi, new_arg, new_edge, 
+                          gimple_phi_arg_location_from_edge (phi, old_edge));
            }
        }
     }
@@ -1991,6 +2011,20 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency,
   return new_fndecl;
 }
 
+/* Make a copy of the body of SRC_FN so that it can be inserted inline in
+   another function.  */
+
+static tree
+copy_tree_body (copy_body_data *id)
+{
+  tree fndecl = id->src_fn;
+  tree body = DECL_SAVED_TREE (fndecl);
+
+  walk_tree (&body, copy_tree_body_r, id, NULL);
+
+  return body;
+}
+
 static tree
 copy_body (copy_body_data *id, gcov_type count, int frequency,
           basic_block entry_block_map, basic_block exit_block_map)
@@ -2433,26 +2467,32 @@ declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest,
   return var;
 }
 
-/* Returns nonzero if a function can be inlined as a tree.  */
+/* Callback through walk_tree.  Determine if a DECL_INITIAL makes reference
+   to a local label.  */
 
-bool
-tree_inlinable_function_p (tree fn)
+static tree
+has_label_address_in_static_1 (tree *nodep, int *walk_subtrees, void *fnp)
 {
-  return inlinable_function_p (fn);
-}
+  tree node = *nodep;
+  tree fn = (tree) fnp;
 
-static const char *inline_forbidden_reason;
+  if (TREE_CODE (node) == LABEL_DECL && DECL_CONTEXT (node) == fn)
+    return node;
+
+  if (TYPE_P (node))
+    *walk_subtrees = 0;
+
+  return NULL_TREE;
+}
 
-/* A callback for walk_gimple_seq to handle tree operands.  Returns
-   NULL_TREE if a function can be inlined, otherwise sets the reason
-   why not and returns a tree representing the offending operand. */
+/* Callback through walk_tree.  Determine if we've got an aggregate
+   type that we can't support; return non-null if so.  */
 
 static tree
-inline_forbidden_p_op (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
-                         void *fnp ATTRIBUTE_UNUSED)
+cannot_copy_type_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
+                    void *data ATTRIBUTE_UNUSED)
 {
-  tree node = *nodep;
-  tree t;
+  tree t, node = *nodep;
 
   if (TREE_CODE (node) == RECORD_TYPE || TREE_CODE (node) == UNION_TYPE)
     {
@@ -2471,21 +2511,78 @@ inline_forbidden_p_op (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
         cycle to try to find out.  This should be checked for 4.1.  */
       for (t = TYPE_FIELDS (node); t; t = TREE_CHAIN (t))
        if (variably_modified_type_p (TREE_TYPE (t), NULL))
-         {
-           inline_forbidden_reason
-             = G_("function %q+F can never be inlined "
-                  "because it uses variable sized variables");
-           return node;
-         }
+         return node;
     }
 
   return NULL_TREE;
 }
 
 
-/* A callback for walk_gimple_seq to handle statements.  Returns
-   non-NULL iff a function can not be inlined.  Also sets the reason
-   why. */
+/* Determine if the function can be copied.  If so return NULL.  If
+   not return a string describng the reason for failure.  */
+
+static const char *
+copy_forbidden (struct function *fun, tree fndecl)
+{
+  const char *reason = fun->cannot_be_copied_reason;
+  tree step;
+
+  /* Only examine the function once.  */
+  if (fun->cannot_be_copied_set)
+    return reason;
+
+  /* We cannot copy a function that receives a non-local goto
+     because we cannot remap the destination label used in the
+     function that is performing the non-local goto.  */
+  /* ??? Actually, this should be possible, if we work at it.
+     No doubt there's just a handful of places that simply
+     assume it doesn't happen and don't substitute properly.  */
+  if (fun->has_nonlocal_label)
+    {
+      reason = G_("function %q+F can never be copied "
+                 "because it receives a non-local goto");
+      goto fail;
+    }
+
+  for (step = fun->local_decls; step; step = TREE_CHAIN (step))
+    {
+      tree decl = TREE_VALUE (step);
+
+      if (TREE_CODE (decl) == VAR_DECL
+         && TREE_STATIC (decl)
+         && !DECL_EXTERNAL (decl)
+         && DECL_INITIAL (decl)
+         && walk_tree_without_duplicates (&DECL_INITIAL (decl),
+                                          has_label_address_in_static_1,
+                                          fndecl))
+       {
+         reason = G_("function %q+F can never be copied because it saves "
+                     "address of local label in a static variable");
+         goto fail;
+       }
+
+      if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl)
+         && variably_modified_type_p (TREE_TYPE (decl), NULL)
+         && walk_tree_without_duplicates (&TREE_TYPE (decl),
+                                          cannot_copy_type_1, NULL))
+       {
+         reason = G_("function %q+F can never be copied "
+                     "because it uses variable sized variables");
+         goto fail;
+       }
+    }
+
+ fail:
+  fun->cannot_be_copied_reason = reason;
+  fun->cannot_be_copied_set = true;
+  return reason;
+}
+
+
+static const char *inline_forbidden_reason;
+
+/* A callback for walk_gimple_seq to handle statements.  Returns non-null
+   iff a function can not be inlined.  Also sets the reason why. */
 
 static tree
 inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
@@ -2594,21 +2691,6 @@ inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
        }
       break;
 
-    case GIMPLE_LABEL:
-      t = gimple_label_label (stmt);
-      if (DECL_NONLOCAL (t))
-       {
-         /* We cannot inline a function that receives a non-local goto
-            because we cannot remap the destination label used in the
-            function that is performing the non-local goto.  */
-         inline_forbidden_reason
-           = G_("function %q+F can never be inlined "
-                "because it receives a non-local goto");
-         *handled_ops_p = true;
-         return t;
-       }
-      break;
-
     default:
       break;
     }
@@ -2617,42 +2699,25 @@ inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
   return NULL_TREE;
 }
 
-
-static tree
-inline_forbidden_p_2 (tree *nodep, int *walk_subtrees,
-                     void *fnp)
-{
-  tree node = *nodep;
-  tree fn = (tree) fnp;
-
-  if (TREE_CODE (node) == LABEL_DECL && DECL_CONTEXT (node) == fn)
-    {
-      inline_forbidden_reason
-       = G_("function %q+F can never be inlined "
-            "because it saves address of local label in a static variable");
-      return node;
-    }
-
-  if (TYPE_P (node))
-    *walk_subtrees = 0;
-
-  return NULL_TREE;
-}
-
 /* Return true if FNDECL is a function that cannot be inlined into
    another one.  */
 
 static bool
 inline_forbidden_p (tree fndecl)
 {
-  location_t saved_loc = input_location;
   struct function *fun = DECL_STRUCT_FUNCTION (fndecl);
-  tree step;
   struct walk_stmt_info wi;
   struct pointer_set_t *visited_nodes;
   basic_block bb;
   bool forbidden_p = false;
 
+  /* First check for shared reasons not to copy the code.  */
+  inline_forbidden_reason = copy_forbidden (fun, fndecl);
+  if (inline_forbidden_reason != NULL)
+    return true;
+
+  /* Next, walk the statements of the function looking for
+     constraucts we can't handle, or are non-optimal for inlining.  */
   visited_nodes = pointer_set_create ();
   memset (&wi, 0, sizeof (wi));
   wi.info = (void *) fndecl;
@@ -2662,41 +2727,21 @@ inline_forbidden_p (tree fndecl)
     {
       gimple ret;
       gimple_seq seq = bb_seq (bb);
-      ret = walk_gimple_seq (seq, inline_forbidden_p_stmt,
-                            inline_forbidden_p_op, &wi);
+      ret = walk_gimple_seq (seq, inline_forbidden_p_stmt, NULL, &wi);
       forbidden_p = (ret != NULL);
       if (forbidden_p)
-       goto egress;
-    }
-
-  for (step = fun->local_decls; step; step = TREE_CHAIN (step))
-    {
-      tree decl = TREE_VALUE (step);
-      if (TREE_CODE (decl) == VAR_DECL
-         && TREE_STATIC (decl)
-         && !DECL_EXTERNAL (decl)
-         && DECL_INITIAL (decl))
-        {
-         tree ret;
-         ret = walk_tree_without_duplicates (&DECL_INITIAL (decl),
-                                             inline_forbidden_p_2, fndecl);
-         forbidden_p = (ret != NULL);
-         if (forbidden_p)
-           goto egress;
-        }
+       break;
     }
 
-egress:
   pointer_set_destroy (visited_nodes);
-  input_location = saved_loc;
   return forbidden_p;
 }
 
 /* Returns nonzero if FN is a function that does not have any
    fundamental inline blocking properties.  */
 
-static bool
-inlinable_function_p (tree fn)
+bool
+tree_inlinable_function_p (tree fn)
 {
   bool inlinable = true;
   bool do_warning;
@@ -3072,7 +3117,6 @@ estimate_num_insns (gimple stmt, eni_weights *weights)
     case GIMPLE_NOP:
     case GIMPLE_PHI:
     case GIMPLE_RETURN:
-    case GIMPLE_CHANGE_DYNAMIC_TYPE:
     case GIMPLE_PREDICT:
       return 0;
 
@@ -3156,12 +3200,6 @@ estimate_num_insns_fn (tree fndecl, eni_weights *weights)
 void
 init_inline_once (void)
 {
-  eni_inlining_weights.call_cost = PARAM_VALUE (PARAM_INLINE_CALL_COST);
-  eni_inlining_weights.target_builtin_call_cost = 1;
-  eni_inlining_weights.div_mod_cost = 10;
-  eni_inlining_weights.omp_cost = 40;
-  eni_inlining_weights.time_based = true;
-
   eni_size_weights.call_cost = 1;
   eni_size_weights.target_builtin_call_cost = 1;
   eni_size_weights.div_mod_cost = 1;
@@ -3429,13 +3467,6 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id)
   /* Declare the return variable for the function.  */
   retvar = declare_return_variable (id, return_slot, modify_dest, &use_retvar);
 
-  if (DECL_IS_OPERATOR_NEW (fn))
-    {
-      gcc_assert (TREE_CODE (retvar) == VAR_DECL
-                 && POINTER_TYPE_P (TREE_TYPE (retvar)));
-      DECL_NO_TBAA_P (retvar) = 1;
-    }
-
   /* Add local vars in this inlined callee to caller.  */
   t_step = id->src_cfun->local_decls;
   for (; t_step; t_step = TREE_CHAIN (t_step))
@@ -3459,6 +3490,13 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id)
      duplicate our body before altering anything.  */
   copy_body (id, bb->count, bb->frequency, bb, return_block);
 
+  /* Reset the escaped and callused solutions.  */
+  if (cfun->gimple_df)
+    {
+      pt_solution_reset (&cfun->gimple_df->escaped);
+      pt_solution_reset (&cfun->gimple_df->callused);
+    }
+
   /* Clean up.  */
   pointer_map_destroy (id->decl_map);
   id->decl_map = st;
@@ -3891,7 +3929,8 @@ unsave_r (tree *tp, int *walk_subtrees, void *data)
     gcc_unreachable ();
   else if (TREE_CODE (*tp) == BIND_EXPR)
     copy_bind_expr (tp, walk_subtrees, id);
-  else if (TREE_CODE (*tp) == SAVE_EXPR)
+  else if (TREE_CODE (*tp) == SAVE_EXPR
+          || TREE_CODE (*tp) == TARGET_EXPR)
     remap_save_expr (tp, st, walk_subtrees);
   else
     {
@@ -4187,12 +4226,12 @@ copy_decl_to_var (tree decl, copy_body_data *id)
 
   type = TREE_TYPE (decl);
 
-  copy = build_decl (VAR_DECL, DECL_NAME (decl), type);
+  copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
+                    VAR_DECL, DECL_NAME (decl), type);
   TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
   TREE_READONLY (copy) = TREE_READONLY (decl);
   TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
   DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (decl);
-  DECL_NO_TBAA_P (copy) = DECL_NO_TBAA_P (decl);
 
   return copy_decl_for_dup_finish (id, decl, copy);
 }
@@ -4212,14 +4251,14 @@ copy_result_decl_to_var (tree decl, copy_body_data *id)
   if (DECL_BY_REFERENCE (decl))
     type = TREE_TYPE (type);
 
-  copy = build_decl (VAR_DECL, DECL_NAME (decl), type);
+  copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
+                    VAR_DECL, DECL_NAME (decl), type);
   TREE_READONLY (copy) = TREE_READONLY (decl);
   TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
   if (!DECL_BY_REFERENCE (decl))
     {
       TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
       DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (decl);
-      DECL_NO_TBAA_P (copy) = DECL_NO_TBAA_P (decl);
     }
 
   return copy_decl_for_dup_finish (id, decl, copy);
@@ -4310,17 +4349,12 @@ copy_static_chain (tree static_chain, copy_body_data * id)
 
 /* Return true if the function is allowed to be versioned.
    This is a guard for the versioning functionality.  */
+
 bool
 tree_versionable_function_p (tree fndecl)
 {
-  if (fndecl == NULL_TREE)
-    return false;
-  /* ??? There are cases where a function is
-     uninlinable but can be versioned.  */
-  if (!tree_inlinable_function_p (fndecl))
-    return false;
-  
-  return true;
+  return (!lookup_attribute ("noclone", DECL_ATTRIBUTES (fndecl))
+         && copy_forbidden (DECL_STRUCT_FUNCTION (fndecl), fndecl) == NULL);
 }
 
 /* Delete all unreachable basic blocks and update callgraph.
@@ -4417,6 +4451,42 @@ delete_unreachable_blocks_update_callgraph (copy_body_data *id)
   return changed;
 }
 
+/* Update clone info after duplication.  */
+
+static void
+update_clone_info (copy_body_data * id)
+{
+  struct cgraph_node *node;
+  if (!id->dst_node->clones)
+    return;
+  for (node = id->dst_node->clones; node != id->dst_node;)
+    {
+      /* First update replace maps to match the new body.  */
+      if (node->clone.tree_map)
+        {
+         unsigned int i;
+          for (i = 0; i < VEC_length (ipa_replace_map_p, node->clone.tree_map); i++)
+           {
+             struct ipa_replace_map *replace_info;
+             replace_info = VEC_index (ipa_replace_map_p, node->clone.tree_map, i);
+             walk_tree (&replace_info->old_tree, copy_tree_body_r, id, NULL);
+             walk_tree (&replace_info->new_tree, copy_tree_body_r, id, NULL);
+           }
+       }
+      if (node->clones)
+       node = node->clones;
+      else if (node->next_sibling_clone)
+       node = node->next_sibling_clone;
+      else
+       {
+         while (node != id->dst_node && !node->next_sibling_clone)
+           node = node->clone_of;
+         if (node != id->dst_node)
+           node = node->next_sibling_clone;
+       }
+    }
+}
+
 /* Create a copy of a function's tree.
    OLD_DECL and NEW_DECL are FUNCTION_DECL tree nodes
    of the original function and the new copied function
@@ -4426,7 +4496,8 @@ delete_unreachable_blocks_update_callgraph (copy_body_data *id)
    trees. If UPDATE_CLONES is set, the call_stmt fields
    of edges of clones of the function will be updated.  */
 void
-tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc)* tree_map,
+tree_function_versioning (tree old_decl, tree new_decl,
+                         VEC(ipa_replace_map_p,gc)* tree_map,
                          bool update_clones, bitmap args_to_skip)
 {
   struct cgraph_node *old_version_node;
@@ -4553,7 +4624,8 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc
       }
   
   /* Copy the Function's body.  */
-  copy_body (&id, old_entry_block->count, old_entry_block->frequency, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR);
+  copy_body (&id, old_entry_block->count, old_entry_block->frequency,
+            ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR);
   
   if (DECL_RESULT (old_decl) != NULL_TREE)
     {
@@ -4571,6 +4643,17 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc
       while (VEC_length (gimple, init_stmts))
        insert_init_stmt (bb, VEC_pop (gimple, init_stmts));
     }
+  update_clone_info (&id);
+
+  /* Remap the nonlocal_goto_save_area, if any.  */
+  if (cfun->nonlocal_goto_save_area)
+    {
+      struct walk_stmt_info wi;
+
+      memset (&wi, 0, sizeof (wi));
+      wi.info = &id;
+      walk_tree (&cfun->nonlocal_goto_save_area, remap_gimple_op_r, &wi, NULL);
+    }
 
   /* Clean up.  */
   pointer_map_destroy (id.decl_map);
@@ -4593,6 +4676,60 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc
   return;
 }
 
+/* EXP is CALL_EXPR present in a GENERIC expression tree.  Try to integrate
+   the callee and return the inlined body on success.  */
+
+tree
+maybe_inline_call_in_expr (tree exp)
+{
+  tree fn = get_callee_fndecl (exp);
+
+  /* We can only try to inline "const" functions.  */
+  if (fn && TREE_READONLY (fn) && DECL_SAVED_TREE (fn))
+    {
+      struct pointer_map_t *decl_map = pointer_map_create ();
+      call_expr_arg_iterator iter;
+      copy_body_data id;
+      tree param, arg, t;
+
+      /* Remap the parameters.  */
+      for (param = DECL_ARGUMENTS (fn), arg = first_call_expr_arg (exp, &iter);
+          param;
+          param = TREE_CHAIN (param), arg = next_call_expr_arg (&iter))
+       *pointer_map_insert (decl_map, param) = arg;
+
+      memset (&id, 0, sizeof (id));
+      id.src_fn = fn;
+      id.dst_fn = current_function_decl;
+      id.src_cfun = DECL_STRUCT_FUNCTION (fn);
+      id.decl_map = decl_map;
+
+      id.copy_decl = copy_decl_no_change;
+      id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+      id.transform_new_cfg = false;
+      id.transform_return_to_modify = true;
+      id.transform_lang_insert_block = false;
+
+      /* Make sure not to unshare trees behind the front-end's back
+        since front-end specific mechanisms may rely on sharing.  */
+      id.regimplify = false;
+      id.do_not_unshare = true;
+
+      /* We're not inside any EH region.  */
+      id.eh_region = -1;
+
+      t = copy_tree_body (&id);
+      pointer_map_destroy (decl_map);
+
+      /* We can only return something suitable for use in a GENERIC
+        expression tree.  */
+      if (TREE_CODE (t) == MODIFY_EXPR)
+       return TREE_OPERAND (t, 1);
+    }
+
+   return NULL_TREE;
+}
+
 /* Duplicate a type, fields and all.  */
 
 tree
@@ -4617,9 +4754,10 @@ build_duplicate_type (tree type)
 }
 
 /* Return whether it is safe to inline a function because it used different
-   target specific options or different optimization options.  */
+   target specific options or call site actual types mismatch parameter types.
+   E is the call edge to be checked.  */
 bool
-tree_can_inline_p (tree caller, tree callee)
+tree_can_inline_p (struct cgraph_edge *e)
 {
 #if 0
   /* This causes a regression in SPEC in that it prevents a cold function from
@@ -4648,7 +4786,25 @@ tree_can_inline_p (tree caller, tree callee)
        return false;
     }
 #endif
+  tree caller, callee;
+
+  caller = e->caller->decl;
+  callee = e->callee->decl;
 
   /* Allow the backend to decide if inlining is ok.  */
-  return targetm.target_option.can_inline_p (caller, callee);
+  if (!targetm.target_option.can_inline_p (caller, callee))
+    {
+      e->inline_failed = CIF_TARGET_OPTION_MISMATCH;
+      gimple_call_set_cannot_inline (e->call_stmt, true);
+      return false;
+    }
+
+  if (!gimple_check_call_args (e->call_stmt))
+    {
+      e->inline_failed = CIF_MISMATCHED_ARGUMENTS;
+      gimple_call_set_cannot_inline (e->call_stmt, true);
+      return false;
+    }
+
+  return true;
 }