diff options
Diffstat (limited to 'gcc/tree-dfa.c')
-rw-r--r-- | gcc/tree-dfa.c | 1200 |
1 files changed, 1200 insertions, 0 deletions
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c new file mode 100644 index 00000000000..99f998f9c1d --- /dev/null +++ b/gcc/tree-dfa.c @@ -0,0 +1,1200 @@ +/* Data flow functions for trees. + Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Contributed by Diego Novillo <dnovillo@redhat.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "hashtab.h" +#include "tree.h" +#include "rtl.h" +#include "tm_p.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "output.h" +#include "errors.h" +#include "timevar.h" +#include "expr.h" +#include "ggc.h" +#include "langhooks.h" +#include "flags.h" +#include "function.h" +#include "diagnostic.h" +#include "tree-dump.h" +#include "tree-gimple.h" +#include "tree-flow.h" +#include "tree-inline.h" +#include "tree-alias-common.h" +#include "tree-pass.h" +#include "convert.h" +#include "params.h" + +/* Build and maintain data flow information for trees. */ + +/* Counters used to display DFA and SSA statistics. */ +struct dfa_stats_d +{ + long num_stmt_anns; + long num_var_anns; + long num_defs; + long num_uses; + long num_phis; + long num_phi_args; + int max_num_phi_args; + long num_v_may_defs; + long num_vuses; + long num_v_must_defs; +}; + + +/* State information for find_vars_r. */ +struct walk_state +{ + /* Hash table used to avoid adding the same variable more than once. */ + htab_t vars_found; +}; + + +/* Local functions. */ +static void collect_dfa_stats (struct dfa_stats_d *); +static tree collect_dfa_stats_r (tree *, int *, void *); +static void add_immediate_use (tree, tree); +static tree find_vars_r (tree *, int *, void *); +static void add_referenced_var (tree, struct walk_state *); +static void compute_immediate_uses_for_phi (tree, bool (*)(tree)); +static void compute_immediate_uses_for_stmt (tree, int, bool (*)(tree)); +static void find_hidden_use_vars (tree); +static tree find_hidden_use_vars_r (tree *, int *, void *); + + +/* Global declarations. */ + +/* Array of all variables referenced in the function. */ +varray_type referenced_vars; + + +/*--------------------------------------------------------------------------- + Dataflow analysis (DFA) routines +---------------------------------------------------------------------------*/ +/* Find all the variables referenced in the function. This function + builds the global arrays REFERENCED_VARS and CALL_CLOBBERED_VARS. + + Note that this function does not look for statement operands, it simply + determines what variables are referenced in the program and detects + various attributes for each variable used by alias analysis and the + optimizer. */ + +static void +find_referenced_vars (void) +{ + htab_t vars_found; + basic_block bb; + block_stmt_iterator si; + struct walk_state walk_state; + tree block; + + /* Walk the lexical blocks in the function looking for variables that may + have been used to declare VLAs and for nested functions. Both + constructs create hidden uses of variables. + + Note that at this point we may have multiple blocks hung off + DECL_INITIAL chained through the BLOCK_CHAIN field due to + how inlining works. Egad. */ + block = DECL_INITIAL (current_function_decl); + while (block) + { + find_hidden_use_vars (block); + block = BLOCK_CHAIN (block); + } + + vars_found = htab_create (50, htab_hash_pointer, htab_eq_pointer, NULL); + memset (&walk_state, 0, sizeof (walk_state)); + walk_state.vars_found = vars_found; + + FOR_EACH_BB (bb) + for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si)) + { + tree *stmt_p = bsi_stmt_ptr (si); + walk_tree (stmt_p, find_vars_r, &walk_state, NULL); + } + + htab_delete (vars_found); +} + +struct tree_opt_pass pass_referenced_vars = +{ + NULL, /* name */ + NULL, /* gate */ + find_referenced_vars, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_gimple_leh | PROP_cfg, /* properties_required */ + PROP_referenced_vars, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + + +/* Compute immediate uses. + + CALC_FOR is an optional function pointer which indicates whether + immediate uses information should be calculated for a given SSA + variable. If NULL, then information is computed for all + variables. + + FLAGS is one of {TDFA_USE_OPS, TDFA_USE_VOPS}. It is used by + compute_immediate_uses_for_stmt to determine whether to look at + virtual and/or real operands while computing def-use chains. */ + +void +compute_immediate_uses (int flags, bool (*calc_for)(tree)) +{ + basic_block bb; + block_stmt_iterator si; + + FOR_EACH_BB (bb) + { + tree phi; + + for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) + compute_immediate_uses_for_phi (phi, calc_for); + + for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si)) + { + tree stmt = bsi_stmt (si); + get_stmt_operands (stmt); + compute_immediate_uses_for_stmt (stmt, flags, calc_for); + } + } +} + + +/* Invalidates dataflow information for a statement STMT. */ + +static void +free_df_for_stmt (tree stmt) +{ + stmt_ann_t ann = stmt_ann (stmt); + + if (ann && ann->df) + { + /* If we have a varray of immediate uses, then go ahead and release + it for re-use. */ + if (ann->df->immediate_uses) + ggc_free (ann->df->immediate_uses); + + /* Similarly for the main dataflow structure. */ + ggc_free (ann->df); + ann->df = NULL; + } +} + + +/* Invalidate dataflow information for the whole function. */ + +void +free_df (void) +{ + basic_block bb; + block_stmt_iterator si; + + FOR_EACH_BB (bb) + { + tree phi; + + for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) + free_df_for_stmt (phi); + + for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si)) + { + tree stmt = bsi_stmt (si); + free_df_for_stmt (stmt); + } + } +} + + +/* Helper for compute_immediate_uses. Check all the USE and/or VUSE + operands in phi node PHI and add a def-use edge between their + defining statement and PHI. CALC_FOR is as in + compute_immediate_uses. + + PHI nodes are easy, we only need to look at their arguments. */ + +static void +compute_immediate_uses_for_phi (tree phi, bool (*calc_for)(tree)) +{ + int i; + +#ifdef ENABLE_CHECKING + if (TREE_CODE (phi) != PHI_NODE) + abort (); +#endif + + for (i = 0; i < PHI_NUM_ARGS (phi); i++) + { + tree arg = PHI_ARG_DEF (phi, i); + + if (TREE_CODE (arg) == SSA_NAME && (!calc_for || calc_for (arg))) + { + tree imm_rdef_stmt = SSA_NAME_DEF_STMT (PHI_ARG_DEF (phi, i)); + if (!IS_EMPTY_STMT (imm_rdef_stmt)) + add_immediate_use (imm_rdef_stmt, phi); + } + } +} + + +/* Another helper for compute_immediate_uses. Depending on the value + of FLAGS, check all the USE and/or VUSE operands in STMT and add a + def-use edge between their defining statement and STMT. CALC_FOR + is as in compute_immediate_uses. */ + +static void +compute_immediate_uses_for_stmt (tree stmt, int flags, bool (*calc_for)(tree)) +{ + size_t i; + use_optype uses; + vuse_optype vuses; + v_may_def_optype v_may_defs; + stmt_ann_t ann; + +#ifdef ENABLE_CHECKING + /* PHI nodes are handled elsewhere. */ + if (TREE_CODE (stmt) == PHI_NODE) + abort (); +#endif + + /* Look at USE_OPS or VUSE_OPS according to FLAGS. */ + ann = stmt_ann (stmt); + if (flags & TDFA_USE_OPS) + { + uses = USE_OPS (ann); + for (i = 0; i < NUM_USES (uses); i++) + { + tree use = USE_OP (uses, i); + tree imm_stmt = SSA_NAME_DEF_STMT (use); + if (!IS_EMPTY_STMT (imm_stmt) && (!calc_for || calc_for (use))) + add_immediate_use (imm_stmt, stmt); + } + } + + if (flags & TDFA_USE_VOPS) + { + vuses = VUSE_OPS (ann); + for (i = 0; i < NUM_VUSES (vuses); i++) + { + tree vuse = VUSE_OP (vuses, i); + tree imm_rdef_stmt = SSA_NAME_DEF_STMT (vuse); + if (!IS_EMPTY_STMT (imm_rdef_stmt) && (!calc_for || calc_for (vuse))) + add_immediate_use (imm_rdef_stmt, stmt); + } + + v_may_defs = V_MAY_DEF_OPS (ann); + for (i = 0; i < NUM_V_MAY_DEFS (v_may_defs); i++) + { + tree vuse = V_MAY_DEF_OP (v_may_defs, i); + tree imm_rdef_stmt = SSA_NAME_DEF_STMT (vuse); + if (!IS_EMPTY_STMT (imm_rdef_stmt) && (!calc_for || calc_for (vuse))) + add_immediate_use (imm_rdef_stmt, stmt); + } + } +} + + +/* Add statement USE_STMT to the list of statements that use definitions + made by STMT. */ + +static void +add_immediate_use (tree stmt, tree use_stmt) +{ + stmt_ann_t ann = get_stmt_ann (stmt); + struct dataflow_d *df; + + df = ann->df; + if (df == NULL) + { + df = ann->df = ggc_alloc (sizeof (struct dataflow_d)); + memset ((void *) df, 0, sizeof (struct dataflow_d)); + df->uses[0] = use_stmt; + return; + } + + if (!df->uses[1]) + { + df->uses[1] = use_stmt; + return; + } + + if (ann->df->immediate_uses == NULL) + VARRAY_TREE_INIT (ann->df->immediate_uses, 4, "immediate_uses"); + + VARRAY_PUSH_TREE (ann->df->immediate_uses, use_stmt); +} + + +/* If the immediate use of USE points to OLD, then redirect it to NEW. */ + +static void +redirect_immediate_use (tree use, tree old, tree new) +{ + tree imm_stmt = SSA_NAME_DEF_STMT (use); + struct dataflow_d *df = get_stmt_ann (imm_stmt)->df; + unsigned int num_uses = num_immediate_uses (df); + unsigned int i; + + for (i = 0; i < num_uses; i++) + { + if (immediate_use (df, i) == old) + { + if (i == 0 || i == 1) + df->uses[i] = new; + else + VARRAY_TREE (df->immediate_uses, i - 2) = new; + } + } +} + + +/* Redirect all immediate uses for operands in OLD so that they point + to NEW. This routine should have no knowledge of how immediate + uses are stored. */ + +void +redirect_immediate_uses (tree old, tree new) +{ + stmt_ann_t ann = get_stmt_ann (old); + use_optype uses = USE_OPS (ann); + vuse_optype vuses = VUSE_OPS (ann); + v_may_def_optype v_may_defs = V_MAY_DEF_OPS (ann); + unsigned int i; + + /* Look at USE_OPS or VUSE_OPS according to FLAGS. */ + for (i = 0; i < NUM_USES (uses); i++) + redirect_immediate_use (USE_OP (uses, i), old, new); + + for (i = 0; i < NUM_VUSES (vuses); i++) + redirect_immediate_use (VUSE_OP (vuses, i), old, new); + + for (i = 0; i < NUM_V_MAY_DEFS (v_may_defs); i++) + redirect_immediate_use (V_MAY_DEF_OP (v_may_defs, i), old, new); +} + + +/*--------------------------------------------------------------------------- + Manage annotations +---------------------------------------------------------------------------*/ +/* Create a new annotation for a _DECL node T. */ + +var_ann_t +create_var_ann (tree t) +{ + var_ann_t ann; + +#if defined ENABLE_CHECKING + if (t == NULL_TREE + || !DECL_P (t) + || (t->common.ann + && t->common.ann->common.type != VAR_ANN)) + abort (); +#endif + + ann = ggc_alloc (sizeof (*ann)); + memset ((void *) ann, 0, sizeof (*ann)); + + ann->common.type = VAR_ANN; + + t->common.ann = (tree_ann_t) ann; + + return ann; +} + + +/* Create a new annotation for a statement node T. */ + +stmt_ann_t +create_stmt_ann (tree t) +{ + stmt_ann_t ann; + +#if defined ENABLE_CHECKING + if ((!is_gimple_stmt (t)) + || (t->common.ann + && t->common.ann->common.type != STMT_ANN)) + abort (); +#endif + + ann = ggc_alloc (sizeof (*ann)); + memset ((void *) ann, 0, sizeof (*ann)); + + ann->common.type = STMT_ANN; + + /* Since we just created the annotation, mark the statement modified. */ + ann->modified = true; + + t->common.ann = (tree_ann_t) ann; + + return ann; +} + + +/* Create a new annotation for a tree T. */ + +tree_ann_t +create_tree_ann (tree t) +{ + tree_ann_t ann; + +#if defined ENABLE_CHECKING + if (t == NULL_TREE + || (t->common.ann + && t->common.ann->common.type != TREE_ANN_COMMON)) + abort (); +#endif + + ann = ggc_alloc (sizeof (*ann)); + memset ((void *) ann, 0, sizeof (*ann)); + + ann->common.type = TREE_ANN_COMMON; + t->common.ann = ann; + + return ann; +} + +/* Build a temporary. Make sure and register it to be renamed. */ + +tree +make_rename_temp (tree type, const char *prefix) +{ + tree t = create_tmp_var (type, prefix); + add_referenced_tmp_var (t); + bitmap_set_bit (vars_to_rename, var_ann (t)->uid); + return t; +} + + + +/*--------------------------------------------------------------------------- + Debugging functions +---------------------------------------------------------------------------*/ +/* Dump the list of all the referenced variables in the current function to + FILE. */ + +void +dump_referenced_vars (FILE *file) +{ + size_t i; + + fprintf (file, "\nReferenced variables in %s: %u\n\n", + get_name (current_function_decl), (unsigned) num_referenced_vars); + + for (i = 0; i < num_referenced_vars; i++) + { + tree var = referenced_var (i); + fprintf (file, "Variable: "); + dump_variable (file, var); + fprintf (file, "\n"); + } +} + + +/* Dump the list of all the referenced variables to stderr. */ + +void +debug_referenced_vars (void) +{ + dump_referenced_vars (stderr); +} + + +/* Dump variable VAR and its may-aliases to FILE. */ + +void +dump_variable (FILE *file, tree var) +{ + var_ann_t ann; + + if (var == NULL_TREE) + { + fprintf (file, "<nil>"); + return; + } + + print_generic_expr (file, var, dump_flags); + + if (TREE_CODE (var) == SSA_NAME) + var = SSA_NAME_VAR (var); + + ann = var_ann (var); + + fprintf (file, ", UID %u", (unsigned) ann->uid); + + if (ann->has_hidden_use) + fprintf (file, ", has hidden uses"); + + if (ann->type_mem_tag) + { + fprintf (file, ", type memory tag: "); + print_generic_expr (file, ann->type_mem_tag, dump_flags); + } + + if (ann->is_alias_tag) + fprintf (file, ", is an alias tag"); + + if (needs_to_live_in_memory (var)) + fprintf (file, ", is %s", TREE_STATIC (var) ? "static" : "global"); + + if (is_call_clobbered (var)) + fprintf (file, ", call clobbered"); + + if (ann->default_def) + { + fprintf (file, ", default def: "); + print_generic_expr (file, ann->default_def, dump_flags); + } + + if (ann->may_aliases) + { + fprintf (file, ", may aliases: "); + dump_may_aliases_for (file, var); + } + + fprintf (file, "\n"); +} + + +/* Dump variable VAR and its may-aliases to stderr. */ + +void +debug_variable (tree var) +{ + dump_variable (stderr, var); +} + + +/* Dump def-use edges on FILE. */ + +void +dump_immediate_uses (FILE *file) +{ + basic_block bb; + block_stmt_iterator si; + const char *funcname + = lang_hooks.decl_printable_name (current_function_decl, 2); + + fprintf (file, "\nDef-use edges for function %s\n", funcname); + + FOR_EACH_BB (bb) + { + tree phi; + + for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) + dump_immediate_uses_for (file, phi); + + for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si)) + dump_immediate_uses_for (file, bsi_stmt (si)); + } + + fprintf (file, "\n"); +} + + +/* Dump def-use edges on stderr. */ + +void +debug_immediate_uses (void) +{ + dump_immediate_uses (stderr); +} + + +/* Dump all immediate uses for STMT on FILE. */ + +void +dump_immediate_uses_for (FILE *file, tree stmt) +{ + dataflow_t df = get_immediate_uses (stmt); + int num_imm_uses = num_immediate_uses (df); + + if (num_imm_uses > 0) + { + int i; + + fprintf (file, "-> "); + print_generic_stmt (file, stmt, TDF_SLIM); + fprintf (file, "\n"); + + for (i = 0; i < num_imm_uses; i++) + { + fprintf (file, "\t"); + print_generic_stmt (file, immediate_use (df, i), TDF_SLIM); + fprintf (file, "\n"); + } + + fprintf (file, "\n"); + } +} + + +/* Dump immediate uses for STMT on stderr. */ + +void +debug_immediate_uses_for (tree stmt) +{ + dump_immediate_uses_for (stderr, stmt); +} + + +/* Dump various DFA statistics to FILE. */ + +void +dump_dfa_stats (FILE *file) +{ + struct dfa_stats_d dfa_stats; + + unsigned long size, total = 0; + const char * const fmt_str = "%-30s%-13s%12s\n"; + const char * const fmt_str_1 = "%-30s%13lu%11lu%c\n"; + const char * const fmt_str_3 = "%-43s%11lu%c\n"; + const char *funcname + = lang_hooks.decl_printable_name (current_function_decl, 2); + + collect_dfa_stats (&dfa_stats); + + fprintf (file, "\nDFA Statistics for %s\n\n", funcname); + + fprintf (file, "---------------------------------------------------------\n"); + fprintf (file, fmt_str, "", " Number of ", "Memory"); + fprintf (file, fmt_str, "", " instances ", "used "); + fprintf (file, "---------------------------------------------------------\n"); + + size = num_referenced_vars * sizeof (tree); + total += size; + fprintf (file, fmt_str_1, "Referenced variables", num_referenced_vars, + SCALE (size), LABEL (size)); + + size = dfa_stats.num_stmt_anns * sizeof (struct stmt_ann_d); + total += size; + fprintf (file, fmt_str_1, "Statements annotated", dfa_stats.num_stmt_anns, + SCALE (size), LABEL (size)); + + size = dfa_stats.num_var_anns * sizeof (struct var_ann_d); + total += size; + fprintf (file, fmt_str_1, "Variables annotated", dfa_stats.num_var_anns, + SCALE (size), LABEL (size)); + + size = dfa_stats.num_uses * sizeof (tree *); + total += size; + fprintf (file, fmt_str_1, "USE operands", dfa_stats.num_uses, + SCALE (size), LABEL (size)); + + size = dfa_stats.num_defs * sizeof (tree *); + total += size; + fprintf (file, fmt_str_1, "DEF operands", dfa_stats.num_defs, + SCALE (size), LABEL (size)); + + size = dfa_stats.num_vuses * sizeof (tree *); + total += size; + fprintf (file, fmt_str_1, "VUSE operands", dfa_stats.num_vuses, + SCALE (size), LABEL (size)); + + size = dfa_stats.num_v_may_defs * sizeof (tree *); + total += size; + fprintf (file, fmt_str_1, "V_MAY_DEF operands", dfa_stats.num_v_may_defs, + SCALE (size), LABEL (size)); + + size = dfa_stats.num_v_must_defs * sizeof (tree *); + total += size; + fprintf (file, fmt_str_1, "V_MUST_DEF operands", dfa_stats.num_v_must_defs, + SCALE (size), LABEL (size)); + + size = dfa_stats.num_phis * sizeof (struct tree_phi_node); + total += size; + fprintf (file, fmt_str_1, "PHI nodes", dfa_stats.num_phis, + SCALE (size), LABEL (size)); + + size = dfa_stats.num_phi_args * sizeof (struct phi_arg_d); + total += size; + fprintf (file, fmt_str_1, "PHI arguments", dfa_stats.num_phi_args, + SCALE (size), LABEL (size)); + + fprintf (file, "---------------------------------------------------------\n"); + fprintf (file, fmt_str_3, "Total memory used by DFA/SSA data", SCALE (total), + LABEL (total)); + fprintf (file, "---------------------------------------------------------\n"); + fprintf (file, "\n"); + + if (dfa_stats.num_phis) + fprintf (file, "Average number of arguments per PHI node: %.1f (max: %d)\n", + (float) dfa_stats.num_phi_args / (float) dfa_stats.num_phis, + dfa_stats.max_num_phi_args); + + fprintf (file, "\n"); +} + + +/* Dump DFA statistics on stderr. */ + +void +debug_dfa_stats (void) +{ + dump_dfa_stats (stderr); +} + + +/* Collect DFA statistics and store them in the structure pointed by + DFA_STATS_P. */ + +static void +collect_dfa_stats (struct dfa_stats_d *dfa_stats_p) +{ + htab_t htab; + basic_block bb; + block_stmt_iterator i; + + if (dfa_stats_p == NULL) + abort (); + + memset ((void *)dfa_stats_p, 0, sizeof (struct dfa_stats_d)); + + /* Walk all the trees in the function counting references. Start at + basic block 0, but don't stop at block boundaries. */ + htab = htab_create (30, htab_hash_pointer, htab_eq_pointer, NULL); + + for (i = bsi_start (BASIC_BLOCK (0)); !bsi_end_p (i); bsi_next (&i)) + walk_tree (bsi_stmt_ptr (i), collect_dfa_stats_r, (void *) dfa_stats_p, + (void *) htab); + + htab_delete (htab); + + FOR_EACH_BB (bb) + { + tree phi; + for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) + { + dfa_stats_p->num_phis++; + dfa_stats_p->num_phi_args += PHI_NUM_ARGS (phi); + if (PHI_NUM_ARGS (phi) > dfa_stats_p->max_num_phi_args) + dfa_stats_p->max_num_phi_args = PHI_NUM_ARGS (phi); + } + } +} + + +/* Callback for walk_tree to collect DFA statistics for a tree and its + children. */ + +static tree +collect_dfa_stats_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, + void *data) +{ + tree t = *tp; + struct dfa_stats_d *dfa_stats_p = (struct dfa_stats_d *)data; + + if (t->common.ann) + { + switch (ann_type (t->common.ann)) + { + case STMT_ANN: + { + stmt_ann_t ann = (stmt_ann_t) t->common.ann; + dfa_stats_p->num_stmt_anns++; + dfa_stats_p->num_defs += NUM_DEFS (DEF_OPS (ann)); + dfa_stats_p->num_uses += NUM_USES (USE_OPS (ann)); + dfa_stats_p->num_v_may_defs += + NUM_V_MAY_DEFS (V_MAY_DEF_OPS (ann)); + dfa_stats_p->num_vuses += NUM_VUSES (VUSE_OPS (ann)); + dfa_stats_p->num_v_must_defs += + NUM_V_MUST_DEFS (V_MUST_DEF_OPS (ann)); + break; + } + + case VAR_ANN: + dfa_stats_p->num_var_anns++; + break; + + default: + break; + } + } + + return NULL; +} + + +/*--------------------------------------------------------------------------- + Miscellaneous helpers +---------------------------------------------------------------------------*/ +/* Callback for walk_tree. Used to collect variables referenced in + the function. */ + +static tree +find_vars_r (tree *tp, int *walk_subtrees, void *data) +{ + struct walk_state *walk_state = (struct walk_state *) data; + + /* If T is a regular variable that the optimizers are interested + in, add it to the list of variables. */ + if (SSA_VAR_P (*tp)) + add_referenced_var (*tp, walk_state); + + /* Type, _DECL and constant nodes have no interesting children. + Ignore them. */ + else if (DECL_P (*tp) + || TYPE_P (*tp) + || TREE_CODE_CLASS (TREE_CODE (*tp)) == 'c') + *walk_subtrees = 0; + + return NULL_TREE; +} + + +/* Add VAR to the list of dereferenced variables. + + WALK_STATE contains a hash table used to avoid adding the same + variable more than once. Note that this function assumes that + VAR is a valid SSA variable. If WALK_STATE is NULL, no + duplicate checking is done. */ + +static void +add_referenced_var (tree var, struct walk_state *walk_state) +{ + void **slot; + var_ann_t v_ann; + + v_ann = get_var_ann (var); + + if (walk_state) + slot = htab_find_slot (walk_state->vars_found, (void *) var, INSERT); + else + slot = NULL; + + if (slot == NULL || *slot == NULL) + { + /* This is the first time we find this variable, add it to the + REFERENCED_VARS array and annotate it with attributes that are + intrinsic to the variable. */ + if (slot) + *slot = (void *) var; + v_ann->uid = num_referenced_vars; + VARRAY_PUSH_TREE (referenced_vars, var); + + /* Global and static variables are call-clobbered, always. */ + if (needs_to_live_in_memory (var)) + mark_call_clobbered (var); + + /* DECL_NONLOCAL variables should not be removed, as they are needed + to emit nested functions. */ + if (DECL_NONLOCAL (var)) + v_ann->used = 1; + } +} + + +/* Return the virtual variable associated to the non-scalar variable VAR. */ + +tree +get_virtual_var (tree var) +{ + STRIP_NOPS (var); + + if (TREE_CODE (var) == SSA_NAME) + var = SSA_NAME_VAR (var); + + while (TREE_CODE (var) == REALPART_EXPR || TREE_CODE (var) == IMAGPART_EXPR + || handled_component_p (var)) + var = TREE_OPERAND (var, 0); + +#ifdef ENABLE_CHECKING + /* Treating GIMPLE registers as virtual variables makes no sense. + Also complain if we couldn't extract a _DECL out of the original + expression. */ + if (!SSA_VAR_P (var) + || is_gimple_reg (var)) + abort (); +#endif + + return var; +} + + +/* Mark variables in BLOCK that have hidden uses. A hidden use can + occur due to VLA declarations or nested functions. */ + +static void +find_hidden_use_vars (tree block) +{ + tree sub, decl, tem; + + /* Check all the arrays declared in the block for VLAs. + While scanning the block's variables, also see if there is + a nested function at this scope. */ + for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl)) + { + int inside_vla = 0; + walk_tree (&decl, find_hidden_use_vars_r, &inside_vla, NULL); + } + + /* Now repeat the search in any sub-blocks. */ + for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub)) + find_hidden_use_vars (sub); + + /* A VLA parameter may use a variable which as set from another + parameter to declare the size of the VLA. We need to mark the + variable as having a hidden use since it is used to declare the + VLA parameter and that declaration is not seen by the SSA code. + + Note get_pending_sizes clears the PENDING_SIZES chain, so we + must restore it. */ + tem = get_pending_sizes (); + put_pending_sizes (tem); + for (; tem; tem = TREE_CHAIN (tem)) + { + int inside_vla = 1; + walk_tree (&TREE_VALUE (tem), find_hidden_use_vars_r, &inside_vla, NULL); + } +} + + +/* Callback for walk_tree used by find_hidden_use_vars to analyze each + variable in a lexical block. If the variable's size has a variable + size, then mark all objects needed to compute the variable's size + as having hidden uses. */ + +static tree +find_hidden_use_vars_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + int *inside_vla = (int *) data; + + /* We need to look for hidden uses due to VLAs in variable + definitions. We originally used to look for these hidden + uses in the variable's type, but that's unreliable if the + type's size contains a SAVE_EXPR for a different function + context than the variable is used within. */ + if (SSA_VAR_P (*tp) + && ((DECL_SIZE (*tp) + && ! really_constant_p (DECL_SIZE (*tp))) + || (DECL_SIZE_UNIT (*tp) + && ! really_constant_p (DECL_SIZE_UNIT (*tp))))) + { + int save = *inside_vla; + + *inside_vla = 1; + walk_tree (&DECL_SIZE (*tp), find_hidden_use_vars_r, inside_vla, NULL); + walk_tree (&DECL_SIZE_UNIT (*tp), find_hidden_use_vars_r, + inside_vla, NULL); + *inside_vla = save; + } + else if (*inside_vla && SSA_VAR_P (*tp)) + set_has_hidden_use (*tp); + + return NULL_TREE; +} + + +/* Add a temporary variable to REFERENCED_VARS. This is similar to + add_referenced_var, but is used by passes that need to add new temps to + the REFERENCED_VARS array after the program has been scanned for + variables. The variable will just receive a new UID and be added + to the REFERENCED_VARS array without checking for duplicates. */ + +void +add_referenced_tmp_var (tree var) +{ + add_referenced_var (var, NULL); +} + +/* Return true if V_MAY_DEFS_AFTER contains fewer entries than + V_MAY_DEFS_BEFORE. Note that this assumes that both varrays + are V_MAY_DEF operands for the same statement. */ + +static inline bool +v_may_defs_disappeared_p (v_may_def_optype v_may_defs_before, + v_may_def_optype v_may_defs_after) +{ + /* If there was nothing before, nothing could've disappeared. */ + if (v_may_defs_before == NULL) + return false; + + /* All/some of them gone. */ + if (v_may_defs_after == NULL + || NUM_V_MAY_DEFS (v_may_defs_before) > + NUM_V_MAY_DEFS (v_may_defs_after)) + return true; + + return false; +} + +/* Return true if V_MUST_DEFS_AFTER contains fewer entries than + V_MUST_DEFS_BEFORE. Note that this assumes that both varrays + are V_MUST_DEF operands for the same statement. */ + +static inline bool +v_must_defs_disappeared_p (v_must_def_optype v_must_defs_before, + v_must_def_optype v_must_defs_after) +{ + /* If there was nothing before, nothing could've disappeared. */ + if (v_must_defs_before == NULL) + return false; + + /* All/some of them gone. */ + if (v_must_defs_after == NULL + || NUM_V_MUST_DEFS (v_must_defs_before) > + NUM_V_MUST_DEFS (v_must_defs_after)) + return true; + + return false; +} + + +/* Add all the non-SSA variables found in STMT's operands to the bitmap + VARS_TO_RENAME. */ + +void +mark_new_vars_to_rename (tree stmt, bitmap vars_to_rename) +{ + def_optype defs; + use_optype uses; + v_may_def_optype v_may_defs; + vuse_optype vuses; + v_must_def_optype v_must_defs; + size_t i; + bitmap vars_in_vops_to_rename; + bool found_exposed_symbol = false; + v_may_def_optype v_may_defs_before, v_may_defs_after; + v_must_def_optype v_must_defs_before, v_must_defs_after; + stmt_ann_t ann; + + vars_in_vops_to_rename = BITMAP_XMALLOC (); + + /* Before re-scanning the statement for operands, mark the existing + virtual operands to be renamed again. We do this because when new + symbols are exposed, the virtual operands that were here before due to + aliasing will probably be removed by the call to get_stmt_operand. + Therefore, we need to flag them to be renamed beforehand. + + We flag them in a separate bitmap because we don't really want to + rename them if there are not any newly exposed symbols in the + statement operands. */ + ann = stmt_ann (stmt); + v_may_defs_before = v_may_defs = V_MAY_DEF_OPS (ann); + for (i = 0; i < NUM_V_MAY_DEFS (v_may_defs); i++) + { + tree var = V_MAY_DEF_RESULT (v_may_defs, i); + if (!DECL_P (var)) + var = SSA_NAME_VAR (var); + bitmap_set_bit (vars_in_vops_to_rename, var_ann (var)->uid); + } + + vuses = VUSE_OPS (ann); + for (i = 0; i < NUM_VUSES (vuses); i++) + { + tree var = VUSE_OP (vuses, i); + if (!DECL_P (var)) + var = SSA_NAME_VAR (var); + bitmap_set_bit (vars_in_vops_to_rename, var_ann (var)->uid); + } + + v_must_defs_before = v_must_defs = V_MUST_DEF_OPS (ann); + for (i = 0; i < NUM_V_MUST_DEFS (v_must_defs); i++) + { + tree var = V_MUST_DEF_OP (v_must_defs, i); + if (!DECL_P (var)) + var = SSA_NAME_VAR (var); + bitmap_set_bit (vars_in_vops_to_rename, var_ann (var)->uid); + } + + /* Now force an operand re-scan on the statement and mark any newly + exposed variables. */ + modify_stmt (stmt); + get_stmt_operands (stmt); + + defs = DEF_OPS (ann); + for (i = 0; i < NUM_DEFS (defs); i++) + { + tree var = DEF_OP (defs, i); + if (DECL_P (var)) + { + found_exposed_symbol = true; + bitmap_set_bit (vars_to_rename, var_ann (var)->uid); + } + } + + uses = USE_OPS (ann); + for (i = 0; i < NUM_USES (uses); i++) + { + tree var = USE_OP (uses, i); + if (DECL_P (var)) + { + found_exposed_symbol = true; + bitmap_set_bit (vars_to_rename, var_ann (var)->uid); + } + } + + v_may_defs_after = v_may_defs = V_MAY_DEF_OPS (ann); + for (i = 0; i < NUM_V_MAY_DEFS (v_may_defs); i++) + { + tree var = V_MAY_DEF_RESULT (v_may_defs, i); + if (DECL_P (var)) + { + found_exposed_symbol = true; + bitmap_set_bit (vars_to_rename, var_ann (var)->uid); + } + } + + vuses = VUSE_OPS (ann); + for (i = 0; i < NUM_VUSES (vuses); i++) + { + tree var = VUSE_OP (vuses, i); + if (DECL_P (var)) + { + found_exposed_symbol = true; + bitmap_set_bit (vars_to_rename, var_ann (var)->uid); + } + } + + v_must_defs_after = v_must_defs = V_MUST_DEF_OPS (ann); + for (i = 0; i < NUM_V_MUST_DEFS (v_must_defs); i++) + { + tree var = V_MUST_DEF_OP (v_must_defs, i); + if (DECL_P (var)) + { + found_exposed_symbol = true; + bitmap_set_bit (vars_to_rename, var_ann (var)->uid); + } + } + + /* If we found any newly exposed symbols, or if there are fewer VDEF + operands in the statement, add the variables we had set in + VARS_IN_VOPS_TO_RENAME to VARS_TO_RENAME. We need to check for + vanishing VDEFs because in those cases, the names that were formerly + generated by this statement are not going to be available anymore. */ + if (found_exposed_symbol + || v_may_defs_disappeared_p (v_may_defs_before, v_may_defs_after) + || v_must_defs_disappeared_p (v_must_defs_before, v_must_defs_after)) + bitmap_a_or_b (vars_to_rename, vars_to_rename, vars_in_vops_to_rename); + + BITMAP_XFREE (vars_in_vops_to_rename); +} |