/* Liveness for SSA trees.
- Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2010
+ Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
Free Software Foundation, Inc.
Contributed by Andrew MacLeod <amacleod@redhat.com>
#include "tree-dump.h"
#include "tree-ssa-live.h"
#include "diagnostic-core.h"
-#include "toplev.h"
#include "debug.h"
#include "flags.h"
+#include "gimple.h"
#ifdef ENABLE_CHECKING
static void verify_live_on_entry (tree_live_info_p);
{
var_map_base_fini (map);
partition_delete (map->var_partition);
- if (map->partition_to_view)
- free (map->partition_to_view);
- if (map->view_to_partition)
- free (map->view_to_partition);
+ free (map->partition_to_view);
+ free (map->view_to_partition);
free (map);
}
&& (b = TREE_BLOCK (t)) != NULL)
TREE_USED (b) = true;
- /* Ignore TREE_ORIGINAL for TARGET_MEM_REFS, as well as other
- fields that do not contain vars. */
+ /* Ignore TMR_OFFSET and TMR_STEP for TARGET_MEM_REFS, as those
+ fields do not contain vars. */
if (TREE_CODE (t) == TARGET_MEM_REF)
{
- mark_all_vars_used (&TMR_SYMBOL (t), data);
mark_all_vars_used (&TMR_BASE (t), data);
mark_all_vars_used (&TMR_INDEX (t), data);
+ mark_all_vars_used (&TMR_INDEX2 (t), data);
*walk_subtrees = 0;
return NULL;
}
eliminated as unused. */
if (TREE_CODE (t) == VAR_DECL)
{
- if (data != NULL && bitmap_bit_p ((bitmap) data, DECL_UID (t)))
- {
- bitmap_clear_bit ((bitmap) data, DECL_UID (t));
- mark_all_vars_used (&DECL_INITIAL (t), data);
- }
+ if (data != NULL && bitmap_clear_bit ((bitmap) data, DECL_UID (t))
+ && DECL_CONTEXT (t) == current_function_decl)
+ mark_all_vars_used (&DECL_INITIAL (t), data);
set_is_used (t);
}
/* remove_unused_scope_block_p requires information about labels
{
tree *t, *next;
bool unused = !TREE_USED (scope);
- var_ann_t ann;
int nsubblocks = 0;
for (t = &BLOCK_VARS (scope); *t; t = next)
{
- next = &TREE_CHAIN (*t);
+ next = &DECL_CHAIN (*t);
/* Debug info of nested function refers to the block of the
function. We might stil call it even if all statements
else if (TREE_CODE (*t) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (*t))
unused = false;
- /* Remove everything we don't generate debug info for. */
- else if (DECL_IGNORED_P (*t))
+ /* Remove everything we don't generate debug info for.
+ Don't remove larger vars though, because BLOCK_VARS are
+ used also during expansion to determine which variables
+ might share stack space. */
+ else if (DECL_IGNORED_P (*t) && is_gimple_reg (*t))
{
- *t = TREE_CHAIN (*t);
+ *t = DECL_CHAIN (*t);
next = t;
}
info about optimized-out variables in the scope blocks.
Exception are the scope blocks not containing any instructions
at all so user can't get into the scopes at first place. */
- else if ((ann = var_ann (*t)) != NULL
- && ann->used)
+ else if (var_ann (*t) != NULL && is_used_p (*t))
unused = false;
else if (TREE_CODE (*t) == LABEL_DECL && TREE_USED (*t))
/* For labels that are still used in the IL, the decision to
can be considered dead. We only want to keep around blocks user can
breakpoint into and ask about value of optimized out variables.
- Similarly we need to keep around types at least until all variables of
- all nested blocks are gone. We track no information on whether given
- type is used or not. */
+ Similarly we need to keep around types at least until all
+ variables of all nested blocks are gone. We track no
+ information on whether given type is used or not, so we have
+ to keep them even when not emitting debug information,
+ otherwise we may end up remapping variables and their (local)
+ types in different orders depending on whether debug
+ information is being generated. */
- else if (debug_info_level == DINFO_LEVEL_NORMAL
+ else if (TREE_CODE (*t) == TYPE_DECL
+ || debug_info_level == DINFO_LEVEL_NORMAL
|| debug_info_level == DINFO_LEVEL_VERBOSE)
;
else
{
- *t = TREE_CHAIN (*t);
+ *t = DECL_CHAIN (*t);
next = t;
}
}
}
}
fprintf (file, " \n");
- for (var = BLOCK_VARS (scope); var; var = TREE_CHAIN (var))
+ for (var = BLOCK_VARS (scope); var; var = DECL_CHAIN (var))
{
bool used = false;
- var_ann_t ann;
- if ((ann = var_ann (var))
- && ann->used)
- used = true;
+ if (var_ann (var))
+ used = is_used_p (var);
- fprintf (file, "%*s",indent, "");
+ fprintf (file, "%*s", indent, "");
print_generic_decl (file, var, flags);
fprintf (file, "%s\n", used ? "" : " (unused)");
}
basic_block bb;
tree var, t;
referenced_var_iterator rvi;
- var_ann_t ann;
bitmap global_unused_vars = NULL;
- unsigned ix;
+ unsigned srcidx, dstidx, num;
+ bool have_local_clobbers = false;
/* Removing declarations from lexical blocks when not optimizing is
not only a waste of time, it actually causes differences in stack
if (!optimize)
return;
+ timevar_push (TV_REMOVE_UNUSED);
+
mark_scope_block_unused (DECL_INITIAL (current_function_decl));
/* Assume all locals are unused. */
- FOR_EACH_REFERENCED_VAR (t, rvi)
- var_ann (t)->used = false;
+ FOR_EACH_REFERENCED_VAR (cfun, t, rvi)
+ clear_is_used (t);
/* Walk the CFG marking all referenced symbols. */
FOR_EACH_BB (bb)
if (is_gimple_debug (stmt))
continue;
+ if (gimple_clobber_p (stmt))
+ {
+ have_local_clobbers = true;
+ continue;
+ }
+
if (b)
TREE_USED (b) = true;
TREE_USED (e->goto_block) = true;
}
+ /* We do a two-pass approach about the out-of-scope clobbers. We want
+ to remove them if they are the only references to a local variable,
+ but we want to retain them when there's any other. So the first pass
+ ignores them, and the second pass (if there were any) tries to remove
+ them. */
+ if (have_local_clobbers)
+ FOR_EACH_BB (bb)
+ {
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+ {
+ gimple stmt = gsi_stmt (gsi);
+ tree b = gimple_block (stmt);
+
+ if (gimple_clobber_p (stmt))
+ {
+ tree lhs = gimple_assign_lhs (stmt);
+ lhs = get_base_address (lhs);
+ if (TREE_CODE (lhs) == SSA_NAME)
+ lhs = SSA_NAME_VAR (lhs);
+ if (DECL_P (lhs) && (!var_ann (lhs) || !is_used_p (lhs)))
+ {
+ unlink_stmt_vdef (stmt);
+ gsi_remove (&gsi, true);
+ release_defs (stmt);
+ continue;
+ }
+ if (b)
+ TREE_USED (b) = true;
+ }
+ gsi_next (&gsi);
+ }
+ }
+
cfun->has_local_explicit_reg_vars = false;
/* Remove unmarked local vars from local_decls. */
- for (ix = 0; VEC_iterate (tree, cfun->local_decls, ix, var); )
+ num = VEC_length (tree, cfun->local_decls);
+ for (srcidx = 0, dstidx = 0; srcidx < num; srcidx++)
{
+ var = VEC_index (tree, cfun->local_decls, srcidx);
if (TREE_CODE (var) != FUNCTION_DECL
- && (!(ann = var_ann (var))
- || !ann->used))
+ && (!var_ann (var)
+ || !is_used_p (var)))
{
if (is_global_var (var))
{
bitmap_set_bit (global_unused_vars, DECL_UID (var));
}
else
- {
- VEC_unordered_remove (tree, cfun->local_decls, ix);
- continue;
- }
+ continue;
}
else if (TREE_CODE (var) == VAR_DECL
&& DECL_HARD_REGISTER (var)
&& !is_global_var (var))
cfun->has_local_explicit_reg_vars = true;
- ix++;
+ if (srcidx != dstidx)
+ VEC_replace (tree, cfun->local_decls, dstidx, var);
+ dstidx++;
}
+ if (dstidx != num)
+ VEC_truncate (tree, cfun->local_decls, dstidx);
/* Remove unmarked global vars from local_decls. */
if (global_unused_vars != NULL)
FOR_EACH_LOCAL_DECL (cfun, ix, var)
if (TREE_CODE (var) == VAR_DECL
&& is_global_var (var)
- && (ann = var_ann (var)) != NULL
- && ann->used)
+ && var_ann (var) != NULL
+ && is_used_p (var)
+ && DECL_CONTEXT (var) == current_function_decl)
mark_all_vars_used (&DECL_INITIAL (var), global_unused_vars);
- for (ix = 0; VEC_iterate (tree, cfun->local_decls, ix, var); )
- if (TREE_CODE (var) == VAR_DECL
- && is_global_var (var)
- && bitmap_bit_p (global_unused_vars, DECL_UID (var)))
- VEC_unordered_remove (tree, cfun->local_decls, ix);
- else
- ix++;
+ num = VEC_length (tree, cfun->local_decls);
+ for (srcidx = 0, dstidx = 0; srcidx < num; srcidx++)
+ {
+ var = VEC_index (tree, cfun->local_decls, srcidx);
+ if (TREE_CODE (var) == VAR_DECL
+ && is_global_var (var)
+ && bitmap_bit_p (global_unused_vars, DECL_UID (var)))
+ continue;
+
+ if (srcidx != dstidx)
+ VEC_replace (tree, cfun->local_decls, dstidx, var);
+ dstidx++;
+ }
+ if (dstidx != num)
+ VEC_truncate (tree, cfun->local_decls, dstidx);
BITMAP_FREE (global_unused_vars);
}
- /* Remove unused variables from REFERENCED_VARs. As a special
- exception keep the variables that are believed to be aliased.
- Those can't be easily removed from the alias sets and operand
- caches. They will be removed shortly after the next may_alias
- pass is performed. */
- FOR_EACH_REFERENCED_VAR (t, rvi)
+ /* Remove unused variables from REFERENCED_VARs. */
+ FOR_EACH_REFERENCED_VAR (cfun, t, rvi)
if (!is_global_var (t)
&& TREE_CODE (t) != PARM_DECL
&& TREE_CODE (t) != RESULT_DECL
- && !(ann = var_ann (t))->used
- && !ann->is_heapvar
- && !TREE_ADDRESSABLE (t))
+ && !is_used_p (t))
remove_referenced_var (t);
remove_unused_scope_block_p (DECL_INITIAL (current_function_decl));
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "Scope blocks after cleanups:\n");
dump_scope_blocks (dump_file, dump_flags);
}
+
+ timevar_pop (TV_REMOVE_UNUSED);
}
}
}
+struct GTY(()) numbered_tree_d
+{
+ tree t;
+ int num;
+};
+typedef struct numbered_tree_d numbered_tree;
+
+DEF_VEC_O (numbered_tree);
+DEF_VEC_ALLOC_O (numbered_tree, heap);
+
+/* Compare two declarations references by their DECL_UID / sequence number.
+ Called via qsort. */
+
+static int
+compare_decls_by_uid (const void *pa, const void *pb)
+{
+ const numbered_tree *nt_a = ((const numbered_tree *)pa);
+ const numbered_tree *nt_b = ((const numbered_tree *)pb);
+
+ if (DECL_UID (nt_a->t) != DECL_UID (nt_b->t))
+ return DECL_UID (nt_a->t) - DECL_UID (nt_b->t);
+ return nt_a->num - nt_b->num;
+}
+
+/* Called via walk_gimple_stmt / walk_gimple_op by dump_enumerated_decls. */
+static tree
+dump_enumerated_decls_push (tree *tp, int *walk_subtrees, void *data)
+{
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+ VEC (numbered_tree, heap) **list = (VEC (numbered_tree, heap) **) &wi->info;
+ numbered_tree nt;
+
+ if (!DECL_P (*tp))
+ return NULL_TREE;
+ nt.t = *tp;
+ nt.num = VEC_length (numbered_tree, *list);
+ VEC_safe_push (numbered_tree, heap, *list, &nt);
+ *walk_subtrees = 0;
+ return NULL_TREE;
+}
+
+/* Find all the declarations used by the current function, sort them by uid,
+ and emit the sorted list. Each declaration is tagged with a sequence
+ number indicating when it was found during statement / tree walking,
+ so that TDF_NOUID comparisons of anonymous declarations are still
+ meaningful. Where a declaration was encountered more than once, we
+ emit only the sequence number of the first encounter.
+ FILE is the dump file where to output the list and FLAGS is as in
+ print_generic_expr. */
+void
+dump_enumerated_decls (FILE *file, int flags)
+{
+ basic_block bb;
+ struct walk_stmt_info wi;
+ VEC (numbered_tree, heap) *decl_list = VEC_alloc (numbered_tree, heap, 40);
+
+ memset (&wi, '\0', sizeof (wi));
+ wi.info = (void*) decl_list;
+ FOR_EACH_BB (bb)
+ {
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ if (!is_gimple_debug (gsi_stmt (gsi)))
+ walk_gimple_stmt (&gsi, NULL, dump_enumerated_decls_push, &wi);
+ }
+ decl_list = (VEC (numbered_tree, heap) *) wi.info;
+ VEC_qsort (numbered_tree, decl_list, compare_decls_by_uid);
+ if (VEC_length (numbered_tree, decl_list))
+ {
+ unsigned ix;
+ numbered_tree *ntp;
+ tree last = NULL_TREE;
+
+ fprintf (file, "Declarations used by %s, sorted by DECL_UID:\n",
+ current_function_name ());
+ FOR_EACH_VEC_ELT (numbered_tree, decl_list, ix, ntp)
+ {
+ if (ntp->t == last)
+ continue;
+ fprintf (file, "%d: ", ntp->num);
+ print_generic_decl (file, ntp->t, flags);
+ fprintf (file, "\n");
+ last = ntp->t;
+ }
+ }
+ VEC_free (numbered_tree, heap, decl_list);
+}
#ifdef ENABLE_CHECKING
/* Verify that SSA_VAR is a non-virtual SSA_NAME. */