#include "tree-flow.h"
#include "tree-dump.h"
#include "tree-ssa-live.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "debug.h"
#include "flags.h"
+#include "gimple.h"
#ifdef ENABLE_CHECKING
static void verify_live_on_entry (tree_live_info_p);
&& (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)))
+ mark_all_vars_used (&DECL_INITIAL (t), data);
set_is_used (t);
}
+ /* remove_unused_scope_block_p requires information about labels
+ which are not DECL_IGNORED_P to tell if they might be used in the IL. */
+ if (TREE_CODE (t) == LABEL_DECL)
+ /* Although the TREE_USED values that the frontend uses would be
+ acceptable (albeit slightly over-conservative) for our purposes,
+ init_vars_expansion clears TREE_USED for LABEL_DECLs too, so we
+ must re-compute it here. */
+ TREE_USED (t) = 1;
if (IS_TYPE_OR_DECL_P (t))
*walk_subtrees = 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;
}
else if ((ann = var_ann (*t)) != NULL
&& ann->used)
unused = false;
+ else if (TREE_CODE (*t) == LABEL_DECL && TREE_USED (*t))
+ /* For labels that are still used in the IL, the decision to
+ preserve them must not depend DEBUG_INFO_LEVEL, otherwise we
+ risk having different ordering in debug vs. non-debug builds
+ during inlining or versioning.
+ A label appearing here (we have already checked DECL_IGNORED_P)
+ should not be used in the IL unless it has been explicitly used
+ before, so we use TREE_USED as an approximation. */
+ /* In principle, we should do the same here as for the debug case
+ below, however, when debugging, there might be additional nested
+ levels that keep an upper level with a label live, so we have to
+ force this block to be considered used, too. */
+ unused = false;
/* When we are not doing full debug info, we however can keep around
only the used variables for cfgexpand's memory packing saving quite
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;
referenced_var_iterator rvi;
var_ann_t ann;
bitmap global_unused_vars = NULL;
- unsigned ix;
+ unsigned srcidx, dstidx, num;
/* 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. */
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))
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)
&& ann->used)
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. */
+ /* Remove unused variables from REFERENCED_VARs. */
FOR_EACH_REFERENCED_VAR (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))
+ && !ann->is_heapvar)
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. */