/* Tree inlining.
- Copyright (C) 2001-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2015 Free Software Foundation, Inc.
Contributed by Alexandre Oliva <aoliva@redhat.com>
This file is part of GCC.
#include "coretypes.h"
#include "tm.h"
#include "diagnostic-core.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
#include "stor-layout.h"
#include "calls.h"
#include "tree-inline.h"
#include "flags.h"
#include "params.h"
-#include "input.h"
#include "insn-config.h"
-#include "hashtab.h"
#include "langhooks.h"
#include "predict.h"
-#include "vec.h"
-#include "hash-set.h"
-#include "machmode.h"
#include "hard-reg-set.h"
#include "function.h"
#include "dominance.h"
#include "gimple-fold.h"
#include "tree-eh.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "stringpool.h"
#include "tree-ssanames.h"
#include "tree-into-ssa.h"
+#include "rtl.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
#include "expr.h"
#include "tree-dfa.h"
#include "tree-ssa.h"
#include "tree-pretty-print.h"
#include "except.h"
#include "debug.h"
-#include "hash-map.h"
-#include "plugin-api.h"
-#include "ipa-ref.h"
#include "cgraph.h"
#include "alloc-pool.h"
+#include "symbol-summary.h"
#include "ipa-prop.h"
#include "value-prof.h"
#include "tree-pass.h"
if (!gimple_in_ssa_p (id->src_cfun))
return;
- if (!MAY_HAVE_DEBUG_STMTS)
+ if (!opt_for_fn (id->dst_fn, flag_var_tracking_assignments))
return;
if (!target_for_debug_bind (key))
if (clique == 0)
return 0;
if (!id->dependence_map)
- id->dependence_map
- = new hash_map<unsigned short, unsigned short, dependence_hasher>;
+ id->dependence_map = new hash_map<dependence_hash, unsigned short>;
bool existed;
unsigned short &newc = id->dependence_map->get_or_insert (clique, &existed);
if (!existed)
bool skip_first = false;
gimple_seq stmts = NULL;
+ if (is_gimple_debug (stmt)
+ && !opt_for_fn (id->dst_fn, flag_var_tracking_assignments))
+ return stmts;
+
/* Begin by recognizing trees that we'll completely rewrite for the
inlining context. Our output for these trees is completely
different from out input (e.g. RETURN_EXPR is deleted, and morphs
/* Clear flags that need revisiting. */
if (gcall *call_stmt = dyn_cast <gcall *> (copy))
- if (gimple_call_tail_p (call_stmt))
- gimple_call_set_tail (call_stmt, false);
+ {
+ if (gimple_call_tail_p (call_stmt))
+ gimple_call_set_tail (call_stmt, false);
+ if (gimple_call_from_thunk_p (call_stmt))
+ gimple_call_set_from_thunk (call_stmt, false);
+ }
/* Remap the region numbers for __builtin_eh_{pointer,filter},
RESX and EH_DISPATCH. */
/* create_basic_block() will append every new block to
basic_block_info automatically. */
- copy_basic_block = create_basic_block (NULL, (void *) 0,
- (basic_block) prev->aux);
+ copy_basic_block = create_basic_block (NULL, (basic_block) prev->aux);
copy_basic_block->count = apply_scale (bb->count, count_scale);
/* We are going to rebuild frequencies from scratch. These values
gsi_replace (©_gsi, new_call, false);
stmt = new_call;
}
- else if (is_gimple_call (stmt)
+ else if (call_stmt
&& id->call_stmt
&& (decl = gimple_call_fndecl (stmt))
&& DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
gsi_replace (©_gsi, new_stmt, false);
stmt = new_stmt;
}
+ else if (call_stmt
+ && id->call_stmt
+ && gimple_call_internal_p (stmt)
+ && gimple_call_internal_fn (stmt) == IFN_TSAN_FUNC_EXIT)
+ {
+ /* Drop TSAN_FUNC_EXIT () internal calls during inlining. */
+ gsi_remove (©_gsi, false);
+ continue;
+ }
/* Statements produced by inlining can be unfolded, especially
when we constant propagated some operands. We can't fold
redirect_all_calls (copy_body_data * id, basic_block bb)
{
gimple_stmt_iterator si;
+ gimple last = last_stmt (bb);
for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
{
- if (is_gimple_call (gsi_stmt (si)))
+ gimple stmt = gsi_stmt (si);
+ if (is_gimple_call (stmt))
{
- struct cgraph_edge *edge = id->dst_node->get_edge (gsi_stmt (si));
+ struct cgraph_edge *edge = id->dst_node->get_edge (stmt);
if (edge)
- edge->redirect_call_stmt_to_callee ();
+ {
+ edge->redirect_call_stmt_to_callee ();
+ if (stmt == last && id->call_stmt && maybe_clean_eh_stmt (stmt))
+ gimple_purge_dead_eh_edges (bb);
+ }
}
}
}
maybe_move_debug_stmts_to_successors (id, (basic_block) bb->aux);
/* Update call edge destinations. This can not be done before loop
info is updated, because we may split basic blocks. */
- if (id->transform_call_graph_edges == CB_CGE_DUPLICATE)
+ if (id->transform_call_graph_edges == CB_CGE_DUPLICATE
+ && bb->index != ENTRY_BLOCK
+ && bb->index != EXIT_BLOCK)
redirect_all_calls (id, (basic_block)bb->aux);
((basic_block)bb->aux)->aux = NULL;
bb->aux = NULL;
if (!gimple_in_ssa_p (id->src_cfun))
return NULL;
- if (!MAY_HAVE_DEBUG_STMTS)
+ if (!opt_for_fn (id->dst_fn, flag_var_tracking_assignments))
return NULL;
tracked_var = target_for_debug_bind (var);
base_stmt = gsi_stmt (gsi);
}
- note = gimple_build_debug_bind (tracked_var, value, base_stmt);
+ note = gimple_build_debug_bind (tracked_var, unshare_expr (value), base_stmt);
if (bb)
{
gsi_insert_after (&si, init_stmt, GSI_NEW_STMT);
gimple_regimplify_operands (init_stmt, &si);
- if (!is_gimple_debug (init_stmt) && MAY_HAVE_DEBUG_STMTS)
+ if (!is_gimple_debug (init_stmt))
{
tree def = gimple_assign_lhs (init_stmt);
insert_init_debug_bind (id, bb, def, def, init_stmt);
/* Determine if the function can be copied. If so return NULL. If
not return a string describng the reason for failure. */
-static const char *
+const char *
copy_forbidden (struct function *fun, tree fndecl)
{
const char *reason = fun->cannot_be_copied_reason;
return (estimate_num_insns_seq (gimple_try_eval (stmt), weights)
+ estimate_num_insns_seq (gimple_try_cleanup (stmt), weights));
- /* OpenMP directives are generally very expensive. */
+ /* OMP directives are generally very expensive. */
case GIMPLE_OMP_RETURN:
case GIMPLE_OMP_SECTIONS_SWITCH:
}
}
+/* Add to BINDINGS a debug stmt resetting SRCVAR if inlining might
+ have brought in or introduced any debug stmts for SRCVAR. */
+
+static inline void
+reset_debug_binding (copy_body_data *id, tree srcvar, gimple_seq *bindings)
+{
+ tree *remappedvarp = id->decl_map->get (srcvar);
+
+ if (!remappedvarp)
+ return;
+
+ if (TREE_CODE (*remappedvarp) != VAR_DECL)
+ return;
+
+ if (*remappedvarp == id->retvar || *remappedvarp == id->retbnd)
+ return;
+
+ tree tvar = target_for_debug_bind (*remappedvarp);
+ if (!tvar)
+ return;
+
+ gdebug *stmt = gimple_build_debug_bind (tvar, NULL_TREE,
+ id->call_stmt);
+ gimple_seq_add_stmt (bindings, stmt);
+}
+
+/* For each inlined variable for which we may have debug bind stmts,
+ add before GSI a final debug stmt resetting it, marking the end of
+ its life, so that var-tracking knows it doesn't have to compute
+ further locations for it. */
+
+static inline void
+reset_debug_bindings (copy_body_data *id, gimple_stmt_iterator gsi)
+{
+ tree var;
+ unsigned ix;
+ gimple_seq bindings = NULL;
+
+ if (!gimple_in_ssa_p (id->src_cfun))
+ return;
+
+ if (!opt_for_fn (id->dst_fn, flag_var_tracking_assignments))
+ return;
+
+ for (var = DECL_ARGUMENTS (id->src_fn);
+ var; var = DECL_CHAIN (var))
+ reset_debug_binding (id, var, &bindings);
+
+ FOR_EACH_LOCAL_DECL (id->src_cfun, ix, var)
+ reset_debug_binding (id, var, &bindings);
+
+ gsi_insert_seq_before_without_update (&gsi, bindings, GSI_SAME_STMT);
+}
+
/* If STMT is a GIMPLE_CALL, replace it with its inline expansion. */
static bool
id->src_cfun = DECL_STRUCT_FUNCTION (fn);
id->call_stmt = stmt;
+ /* If the the src function contains an IFN_VA_ARG, then so will the dst
+ function after inlining. */
+ if ((id->src_cfun->curr_properties & PROP_gimple_lva) == 0)
+ {
+ struct function *dst_cfun = DECL_STRUCT_FUNCTION (id->dst_fn);
+ dst_cfun->curr_properties &= ~PROP_gimple_lva;
+ }
+
gcc_assert (!id->src_cfun->after_inlining);
id->entry_bb = bb;
GCOV_COMPUTE_SCALE (cg_edge->frequency, CGRAPH_FREQ_BASE),
bb, return_block, NULL);
+ reset_debug_bindings (id, stmt_gsi);
+
/* Reset the escaped solution. */
if (cfun->gimple_df)
pt_solution_reset (&cfun->gimple_df->escaped);
gimple_expand_calls_inline (basic_block bb, copy_body_data *id)
{
gimple_stmt_iterator gsi;
+ bool inlined = false;
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi);)
{
gimple stmt = gsi_stmt (gsi);
+ gsi_prev (&gsi);
if (is_gimple_call (stmt)
- && !gimple_call_internal_p (stmt)
- && expand_call_inline (bb, stmt, id))
- return true;
+ && !gimple_call_internal_p (stmt))
+ inlined |= expand_call_inline (bb, stmt, id);
}
- return false;
+ return inlined;
}