/* Rewrite a program in Normal form into SSA.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 2001-2021 Free Software Foundation, Inc.
Contributed by Diego Novillo <dnovillo@redhat.com>
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "rtl.h"
#include "tree.h"
-#include "flags.h"
-#include "tm_p.h"
-#include "langhooks.h"
-#include "basic-block.h"
-#include "function.h"
-#include "gimple-pretty-print.h"
-#include "bitmap.h"
-#include "tree-flow.h"
#include "gimple.h"
-#include "tree-inline.h"
-#include "hashtab.h"
#include "tree-pass.h"
-#include "cfgloop.h"
-#include "domwalk.h"
-#include "params.h"
-#include "vecprim.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
#include "diagnostic-core.h"
+#include "langhooks.h"
+#include "cfganal.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "tree-into-ssa.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
+#include "domwalk.h"
+#include "statistics.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "asan.h"
+#include "attr-fnspec.h"
+#define PERCENT(x,y) ((float)(x) * 100.0 / (float)(y))
/* This file builds the SSA form for a function as described in:
R. Cytron, J. Ferrante, B. Rosen, M. Wegman, and K. Zadeck. Efficiently
/* Structure to map a variable VAR to the set of blocks that contain
definitions for VAR. */
-struct def_blocks_d
+struct def_blocks
{
/* Blocks that contain definitions of VAR. Bit I will be set if the
Ith block contains a definition of VAR. */
bitmap livein_blocks;
};
-typedef struct def_blocks_d *def_blocks_p;
-
-
/* Stack of trees used to restore the global currdefs to its original
state after completing rewriting of a block and its dominator
children. Its elements have the following properties:
- A NULL node at the top entry is used to mark the last slot
associated with the current block. */
-static VEC(tree,heap) *block_defs_stack;
+static vec<tree> block_defs_stack;
/* Set of existing SSA names being replaced by update_ssa. */
the operations done on them are presence tests. */
static sbitmap new_ssa_names;
-sbitmap interesting_blocks;
+static sbitmap interesting_blocks;
/* Set of SSA names that have been marked to be released after they
were registered in the replacement table. They will be finally
released after we finish updating the SSA web. */
-static bitmap names_to_release;
+bitmap names_to_release;
-/* VEC of VECs of PHIs to rewrite in a basic block. Element I corresponds
+/* vec of vec of PHIs to rewrite in a basic block. Element I corresponds
the to basic block with index I. Allocated once per compilation, *not*
released between different functions. */
-static VEC(gimple_vec, heap) *phis_to_rewrite;
+static vec< vec<gphi *> > phis_to_rewrite;
/* The bitmap of non-NULL elements of PHIS_TO_REWRITE. */
static bitmap blocks_with_phis_to_rewrite;
/* Growth factor for NEW_SSA_NAMES and OLD_SSA_NAMES. These sets need
- to grow as the callers to register_new_name_mapping will typically
- create new names on the fly. FIXME. Currently set to 1/3 to avoid
- frequent reallocations but still need to find a reasonable growth
- strategy. */
+ to grow as the callers to create_new_def_for will create new names on
+ the fly.
+ FIXME. Currently set to 1/3 to avoid frequent reallocations but still
+ need to find a reasonable growth strategy. */
#define NAME_SETS_GROWTH_FACTOR (MAX (3, num_ssa_names / 3))
/* The function the SSA updating data structures have been initialized for.
- NULL if they need to be initialized by register_new_name_mapping. */
+ NULL if they need to be initialized by create_new_def_for. */
static struct function *update_ssa_initialized_fn = NULL;
/* Global data to attach to the main dominator walk structure. */
bitmap kills;
};
+/* It is advantageous to avoid things like life analysis for variables which
+ do not need PHI nodes. This enum describes whether or not a particular
+ variable may need a PHI node. */
+
+enum need_phi_state {
+ /* This is the default. If we are still in this state after finding
+ all the definition and use sites, then we will assume the variable
+ needs PHI nodes. This is probably an overly conservative assumption. */
+ NEED_PHI_STATE_UNKNOWN,
+
+ /* This state indicates that we have seen one or more sets of the
+ variable in a single basic block and that the sets dominate all
+ uses seen so far. If after finding all definition and use sites
+ we are still in this state, then the variable does not need any
+ PHI nodes. */
+ NEED_PHI_STATE_NO,
+
+ /* This state indicates that we have either seen multiple definitions of
+ the variable in multiple blocks, or that we encountered a use in a
+ block that was not dominated by the block containing the set(s) of
+ this variable. This variable is assumed to need PHI nodes. */
+ NEED_PHI_STATE_MAYBE
+};
+
/* Information stored for both SSA names and decls. */
-struct common_info_d
+struct common_info
{
/* This field indicates whether or not the variable may need PHI nodes.
See the enum's definition for more detailed information about the
tree current_def;
/* Definitions for this var. */
- struct def_blocks_d def_blocks;
+ struct def_blocks def_blocks;
};
-/* The information associated with decls and SSA names. */
-typedef struct common_info_d *common_info_p;
-
/* Information stored for decls. */
-struct var_info_d
+struct var_info
{
/* The variable. */
tree var;
/* Information stored for both SSA names and decls. */
- struct common_info_d info;
+ common_info info;
+};
+
+
+/* VAR_INFOS hashtable helpers. */
+
+struct var_info_hasher : free_ptr_hash <var_info>
+{
+ static inline hashval_t hash (const value_type &);
+ static inline bool equal (const value_type &, const compare_type &);
};
-/* The information associated with decls. */
-typedef struct var_info_d *var_info_p;
+inline hashval_t
+var_info_hasher::hash (const value_type &p)
+{
+ return DECL_UID (p->var);
+}
+
+inline bool
+var_info_hasher::equal (const value_type &p1, const compare_type &p2)
+{
+ return p1->var == p2->var;
+}
-DEF_VEC_P(var_info_p);
-DEF_VEC_ALLOC_P(var_info_p,heap);
/* Each entry in VAR_INFOS contains an element of type STRUCT
VAR_INFO_D. */
-static htab_t var_infos;
+static hash_table<var_info_hasher> *var_infos;
/* Information stored for SSA names. */
bitmap repl_set;
/* Information stored for both SSA names and decls. */
- struct common_info_d info;
+ common_info info;
};
-/* The information associated with names. */
-typedef struct ssa_name_info *ssa_name_info_p;
-DEF_VEC_P (ssa_name_info_p);
-DEF_VEC_ALLOC_P (ssa_name_info_p, heap);
-
-static VEC(ssa_name_info_p, heap) *info_for_ssa_name;
+static vec<ssa_name_info *> info_for_ssa_name;
static unsigned current_info_for_ssa_name_age;
static bitmap_obstack update_ssa_obstack;
REWRITE_UPDATE
};
-
-
-
-/* Prototypes for debugging functions. */
-extern void dump_tree_ssa (FILE *);
-extern void debug_tree_ssa (void);
-extern void debug_def_blocks (void);
-extern void dump_tree_ssa_stats (FILE *);
-extern void debug_tree_ssa_stats (void);
-extern void dump_update_ssa (FILE *);
-extern void debug_update_ssa (void);
-extern void dump_names_replaced_by (FILE *, tree);
-extern void debug_names_replaced_by (tree);
-extern void dump_var_infos (FILE *);
-extern void debug_var_infos (void);
-extern void dump_defs_stack (FILE *, int);
-extern void debug_defs_stack (int);
-extern void dump_currdefs (FILE *);
-extern void debug_currdefs (void);
-
-
/* The set of symbols we ought to re-write into SSA form in update_ssa. */
static bitmap symbols_to_rename_set;
-static VEC(tree,heap) *symbols_to_rename;
+static vec<tree> symbols_to_rename;
/* Mark SYM for renaming. */
if (!symbols_to_rename_set)
symbols_to_rename_set = BITMAP_ALLOC (NULL);
if (bitmap_set_bit (symbols_to_rename_set, DECL_UID (sym)))
- VEC_safe_push (tree, heap, symbols_to_rename, sym);
+ symbols_to_rename.safe_push (sym);
}
/* Return true if SYM is marked for renaming. */
decided in mark_def_sites. */
static inline bool
-rewrite_uses_p (gimple stmt)
+rewrite_uses_p (gimple *stmt)
{
return gimple_visited_p (stmt);
}
/* Set the rewrite marker on STMT to the value given by REWRITE_P. */
static inline void
-set_rewrite_uses (gimple stmt, bool rewrite_p)
+set_rewrite_uses (gimple *stmt, bool rewrite_p)
{
gimple_set_visited (stmt, rewrite_p);
}
registered, but they don't need to have their uses renamed. */
static inline bool
-register_defs_p (gimple stmt)
+register_defs_p (gimple *stmt)
{
return gimple_plf (stmt, GF_PLF_1) != 0;
}
/* If REGISTER_DEFS_P is true, mark STMT to have its DEFs registered. */
static inline void
-set_register_defs (gimple stmt, bool register_defs_p)
+set_register_defs (gimple *stmt, bool register_defs_p)
{
gimple_set_plf (stmt, GF_PLF_1, register_defs_p);
}
/* Get the information associated with NAME. */
-static inline ssa_name_info_p
+static inline ssa_name_info *
get_ssa_name_ann (tree name)
{
unsigned ver = SSA_NAME_VERSION (name);
- unsigned len = VEC_length (ssa_name_info_p, info_for_ssa_name);
+ unsigned len = info_for_ssa_name.length ();
struct ssa_name_info *info;
/* Re-allocate the vector at most once per update/into-SSA. */
if (ver >= len)
- VEC_safe_grow_cleared (ssa_name_info_p, heap,
- info_for_ssa_name, num_ssa_names);
+ info_for_ssa_name.safe_grow_cleared (num_ssa_names, true);
/* But allocate infos lazily. */
- info = VEC_index (ssa_name_info_p, info_for_ssa_name, ver);
+ info = info_for_ssa_name[ver];
if (!info)
{
info = XCNEW (struct ssa_name_info);
info->age = current_info_for_ssa_name_age;
info->info.need_phi_state = NEED_PHI_STATE_UNKNOWN;
- VEC_replace (ssa_name_info_p, info_for_ssa_name, ver, info);
+ info_for_ssa_name[ver] = info;
}
if (info->age < current_info_for_ssa_name_age)
/* Return and allocate the auxiliar information for DECL. */
-static inline var_info_p
+static inline var_info *
get_var_info (tree decl)
{
- struct var_info_d vi;
- void **slot;
+ var_info vi;
+ var_info **slot;
vi.var = decl;
- slot = htab_find_slot_with_hash (var_infos, &vi, DECL_UID (decl), INSERT);
+ slot = var_infos->find_slot_with_hash (&vi, DECL_UID (decl), INSERT);
if (*slot == NULL)
{
- var_info_p v = XCNEW (struct var_info_d);
+ var_info *v = XCNEW (var_info);
v->var = decl;
- *slot = (void *)v;
+ *slot = v;
return v;
}
- return (var_info_p) *slot;
+ return *slot;
}
/* Get access to the auxiliar information stored per SSA name or decl. */
-static inline common_info_p
+static inline common_info *
get_common_info (tree var)
{
if (TREE_CODE (var) == SSA_NAME)
static void
initialize_flags_in_bb (basic_block bb)
{
- gimple stmt;
+ gimple *stmt;
gimple_stmt_iterator gsi;
for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
- gimple phi = gsi_stmt (gsi);
+ gimple *phi = gsi_stmt (gsi);
set_rewrite_uses (phi, false);
set_register_defs (phi, false);
}
/* We are going to use the operand cache API, such as
SET_USE, SET_DEF, and FOR_EACH_IMM_USE_FAST. The operand
cache for each statement should be up-to-date. */
- gcc_assert (!gimple_modified_p (stmt));
+ gcc_checking_assert (!gimple_modified_p (stmt));
set_rewrite_uses (stmt, false);
set_register_defs (stmt, false);
}
static void
mark_block_for_update (basic_block bb)
{
- gcc_assert (blocks_to_update != NULL);
+ gcc_checking_assert (blocks_to_update != NULL);
if (!bitmap_set_bit (blocks_to_update, bb->index))
return;
initialize_flags_in_bb (bb);
where VAR is live on entry (livein). If no entry is found in
DEF_BLOCKS, a new one is created and returned. */
-static inline struct def_blocks_d *
-get_def_blocks_for (common_info_p info)
+static inline def_blocks *
+get_def_blocks_for (common_info *info)
{
- struct def_blocks_d *db_p = &info->def_blocks;
+ def_blocks *db_p = &info->def_blocks;
if (!db_p->def_blocks)
{
db_p->def_blocks = BITMAP_ALLOC (&update_ssa_obstack);
static void
set_def_block (tree var, basic_block bb, bool phi_p)
{
- struct def_blocks_d *db_p;
- common_info_p info;
+ def_blocks *db_p;
+ common_info *info;
info = get_common_info (var);
db_p = get_def_blocks_for (info);
static void
set_livein_block (tree var, basic_block bb)
{
- common_info_p info;
- struct def_blocks_d *db_p;
+ common_info *info;
+ def_blocks *db_p;
info = get_common_info (var);
db_p = get_def_blocks_for (info);
if (def_block_index == -1
|| ! dominated_by_p (CDI_DOMINATORS, bb,
- BASIC_BLOCK (def_block_index)))
+ BASIC_BLOCK_FOR_FN (cfun, def_block_index)))
info->need_phi_state = NEED_PHI_STATE_MAYBE;
}
else
is_old_name (tree name)
{
unsigned ver = SSA_NAME_VERSION (name);
- if (!new_ssa_names)
+ if (!old_ssa_names)
return false;
- return (ver < SBITMAP_SIZE (new_ssa_names)
- && TEST_BIT (old_ssa_names, ver));
+ return (ver < SBITMAP_SIZE (old_ssa_names)
+ && bitmap_bit_p (old_ssa_names, ver));
}
if (!new_ssa_names)
return false;
return (ver < SBITMAP_SIZE (new_ssa_names)
- && TEST_BIT (new_ssa_names, ver));
+ && bitmap_bit_p (new_ssa_names, ver));
}
static void
add_new_name_mapping (tree new_tree, tree old)
{
- timevar_push (TV_TREE_SSA_INCREMENTAL);
-
/* OLD and NEW_TREE must be different SSA names for the same symbol. */
- gcc_assert (new_tree != old && SSA_NAME_VAR (new_tree) == SSA_NAME_VAR (old));
+ gcc_checking_assert (new_tree != old
+ && SSA_NAME_VAR (new_tree) == SSA_NAME_VAR (old));
/* We may need to grow NEW_SSA_NAMES and OLD_SSA_NAMES because our
caller may have created new names since the set was created. */
/* Register NEW_TREE and OLD in NEW_SSA_NAMES and OLD_SSA_NAMES,
respectively. */
- SET_BIT (new_ssa_names, SSA_NAME_VERSION (new_tree));
- SET_BIT (old_ssa_names, SSA_NAME_VERSION (old));
-
- timevar_pop (TV_TREE_SSA_INCREMENTAL);
+ bitmap_set_bit (new_ssa_names, SSA_NAME_VERSION (new_tree));
+ bitmap_set_bit (old_ssa_names, SSA_NAME_VERSION (old));
}
we create. */
static void
-mark_def_sites (basic_block bb, gimple stmt, bitmap kills)
+mark_def_sites (basic_block bb, gimple *stmt, bitmap kills)
{
tree def;
use_operand_p use_p;
form, force an operand scan on every statement. */
update_stmt (stmt);
- gcc_assert (blocks_to_update == NULL);
+ gcc_checking_assert (blocks_to_update == NULL);
set_register_defs (stmt, false);
set_rewrite_uses (stmt, false);
FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE)
{
tree sym = USE_FROM_PTR (use_p);
- gcc_assert (DECL_P (sym));
+ gcc_checking_assert (DECL_P (sym));
set_rewrite_uses (stmt, true);
}
if (rewrite_uses_p (stmt))
- SET_BIT (interesting_blocks, bb->index);
+ bitmap_set_bit (interesting_blocks, bb->index);
return;
}
FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES)
{
tree sym = USE_FROM_PTR (use_p);
- gcc_assert (DECL_P (sym));
+ if (TREE_CODE (sym) == SSA_NAME)
+ continue;
+ gcc_checking_assert (DECL_P (sym));
if (!bitmap_bit_p (kills, DECL_UID (sym)))
set_livein_block (sym, bb);
set_rewrite_uses (stmt, true);
each def to the set of killed symbols. */
FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
{
- gcc_assert (DECL_P (def));
+ if (TREE_CODE (def) == SSA_NAME)
+ continue;
+ gcc_checking_assert (DECL_P (def));
set_def_block (def, bb, false);
bitmap_set_bit (kills, DECL_UID (def));
set_register_defs (stmt, true);
/* If we found the statement interesting then also mark the block BB
as interesting. */
if (rewrite_uses_p (stmt) || register_defs_p (stmt))
- SET_BIT (interesting_blocks, bb->index);
+ bitmap_set_bit (interesting_blocks, bb->index);
}
/* Structure used by prune_unused_phi_nodes to record bounds of the intervals
static void
prune_unused_phi_nodes (bitmap phis, bitmap kills, bitmap uses)
{
- VEC(int, heap) *worklist;
bitmap_iterator bi;
unsigned i, b, p, u, top;
bitmap live_phis;
adef = 1;
EXECUTE_IF_SET_IN_BITMAP (to_remove, 0, i, bi)
{
- def_bb = BASIC_BLOCK (i);
+ def_bb = BASIC_BLOCK_FOR_FN (cfun, i);
defs[adef].bb_index = i;
defs[adef].dfs_num = bb_dom_dfs_in (CDI_DOMINATORS, def_bb);
defs[adef + 1].bb_index = i;
dfs_out numbers, increase the dfs number by one (so that it corresponds
to the start of the following interval, not to the end of the current
one). We use WORKLIST as a stack. */
- worklist = VEC_alloc (int, heap, n_defs + 1);
- VEC_quick_push (int, worklist, 1);
+ auto_vec<int> worklist (n_defs + 1);
+ worklist.quick_push (1);
top = 1;
n_defs = 1;
for (i = 1; i < adef; i++)
{
/* This is a closing element. Interval corresponding to the top
of the stack after removing it follows. */
- VEC_pop (int, worklist);
- top = VEC_index (int, worklist, VEC_length (int, worklist) - 1);
+ worklist.pop ();
+ top = worklist[worklist.length () - 1];
defs[n_defs].bb_index = top;
defs[n_defs].dfs_num = defs[i].dfs_num + 1;
}
it to the correct position. */
defs[n_defs].bb_index = defs[i].bb_index;
defs[n_defs].dfs_num = defs[i].dfs_num;
- VEC_quick_push (int, worklist, b);
+ worklist.quick_push (b);
top = b;
}
else
n_defs++;
}
- VEC_pop (int, worklist);
- gcc_assert (VEC_empty (int, worklist));
+ worklist.pop ();
+ gcc_assert (worklist.is_empty ());
/* Now process the uses. */
live_phis = BITMAP_ALLOC (NULL);
EXECUTE_IF_SET_IN_BITMAP (uses, 0, i, bi)
{
- VEC_safe_push (int, heap, worklist, i);
+ worklist.safe_push (i);
}
- while (!VEC_empty (int, worklist))
+ while (!worklist.is_empty ())
{
- b = VEC_pop (int, worklist);
+ b = worklist.pop ();
if (b == ENTRY_BLOCK)
continue;
p = b;
else
{
- use_bb = get_immediate_dominator (CDI_DOMINATORS, BASIC_BLOCK (b));
+ use_bb = get_immediate_dominator (CDI_DOMINATORS,
+ BASIC_BLOCK_FOR_FN (cfun, b));
p = find_dfsnum_interval (defs, n_defs,
bb_dom_dfs_in (CDI_DOMINATORS, use_bb));
if (!bitmap_bit_p (phis, p))
continue;
/* Add the new uses to the worklist. */
- def_bb = BASIC_BLOCK (p);
+ def_bb = BASIC_BLOCK_FOR_FN (cfun, p);
FOR_EACH_EDGE (e, ei, def_bb->preds)
{
u = e->src->index;
continue;
bitmap_set_bit (uses, u);
- VEC_safe_push (int, heap, worklist, u);
+ worklist.safe_push (u);
}
}
- VEC_free (int, heap, worklist);
bitmap_copy (phis, live_phis);
BITMAP_FREE (live_phis);
free (defs);
where VAR is live on entry (livein). Return NULL, if no entry is
found in DEF_BLOCKS. */
-static inline struct def_blocks_d *
+static inline def_blocks *
find_def_blocks_for (tree var)
{
- def_blocks_p p = &get_common_info (var)->def_blocks;
+ def_blocks *p = &get_common_info (var)->def_blocks;
if (!p->def_blocks)
return NULL;
return p;
/* Marks phi node PHI in basic block BB for rewrite. */
static void
-mark_phi_for_rewrite (basic_block bb, gimple phi)
+mark_phi_for_rewrite (basic_block bb, gphi *phi)
{
- gimple_vec phis;
+ vec<gphi *> phis;
unsigned n, idx = bb->index;
if (rewrite_uses_p (phi))
if (!blocks_with_phis_to_rewrite)
return;
- bitmap_set_bit (blocks_with_phis_to_rewrite, idx);
-
- n = (unsigned) last_basic_block + 1;
- if (VEC_length (gimple_vec, phis_to_rewrite) < n)
- VEC_safe_grow_cleared (gimple_vec, heap, phis_to_rewrite, n);
+ if (bitmap_set_bit (blocks_with_phis_to_rewrite, idx))
+ {
+ n = (unsigned) last_basic_block_for_fn (cfun) + 1;
+ if (phis_to_rewrite.length () < n)
+ phis_to_rewrite.safe_grow_cleared (n, true);
- phis = VEC_index (gimple_vec, phis_to_rewrite, idx);
- if (!phis)
- phis = VEC_alloc (gimple, heap, 10);
+ phis = phis_to_rewrite[idx];
+ gcc_assert (!phis.exists ());
+ phis.create (10);
+ }
+ else
+ phis = phis_to_rewrite[idx];
- VEC_safe_push (gimple, heap, phis, phi);
- VEC_replace (gimple_vec, phis_to_rewrite, idx, phis);
+ phis.safe_push (phi);
+ phis_to_rewrite[idx] = phis;
}
/* Insert PHI nodes for variable VAR using the iterated dominance
{
unsigned bb_index;
edge e;
- gimple phi;
+ gphi *phi;
basic_block bb;
bitmap_iterator bi;
- struct def_blocks_d *def_map;
-
- def_map = find_def_blocks_for (var);
- gcc_assert (def_map);
+ def_blocks *def_map = find_def_blocks_for (var);
/* Remove the blocks where we already have PHI nodes for VAR. */
bitmap_and_compl_into (phi_insertion_points, def_map->phi_blocks);
/* And insert the PHI nodes. */
EXECUTE_IF_SET_IN_BITMAP (phi_insertion_points, 0, bb_index, bi)
{
- bb = BASIC_BLOCK (bb_index);
+ bb = BASIC_BLOCK_FOR_FN (cfun, bb_index);
if (update_p)
mark_block_for_update (bb);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "creating PHI node in block #%d for ", bb_index);
+ print_generic_expr (dump_file, var, TDF_SLIM);
+ fprintf (dump_file, "\n");
+ }
phi = NULL;
if (TREE_CODE (var) == SSA_NAME)
edge_iterator ei;
tree new_lhs;
- gcc_assert (update_p);
+ gcc_checking_assert (update_p);
new_lhs = duplicate_ssa_name (var, NULL);
phi = create_phi_node (new_lhs, bb);
add_new_name_mapping (new_lhs, var);
{
tree tracked_var;
- gcc_assert (DECL_P (var));
+ gcc_checking_assert (DECL_P (var));
phi = create_phi_node (var, bb);
tracked_var = target_for_debug_bind (var);
if (tracked_var)
{
- gimple note = gimple_build_debug_bind (tracked_var,
- PHI_RESULT (phi),
+ gimple *note = gimple_build_debug_bind (tracked_var,
+ PHI_RESULT (phi),
phi);
gimple_stmt_iterator si = gsi_after_labels (bb);
gsi_insert_before (&si, note, GSI_SAME_STMT);
static int
insert_phi_nodes_compare_var_infos (const void *a, const void *b)
{
- const struct var_info_d *defa = *(struct var_info_d * const *)a;
- const struct var_info_d *defb = *(struct var_info_d * const *)b;
+ const var_info *defa = *(var_info * const *)a;
+ const var_info *defb = *(var_info * const *)b;
if (DECL_UID (defa->var) < DECL_UID (defb->var))
return -1;
else
static void
insert_phi_nodes (bitmap_head *dfs)
{
- htab_iterator hi;
+ hash_table<var_info_hasher>::iterator hi;
unsigned i;
- var_info_p info;
- VEC(var_info_p,heap) *vars;
+ var_info *info;
+
+ /* When the gimplifier introduces SSA names it cannot easily avoid
+ situations where abnormal edges added by CFG construction break
+ the use-def dominance requirement. For this case rewrite SSA
+ names with broken use-def dominance out-of-SSA and register them
+ for PHI insertion. We only need to do this if abnormal edges
+ can appear in the function. */
+ tree name;
+ if (cfun->calls_setjmp
+ || cfun->has_nonlocal_label)
+ FOR_EACH_SSA_NAME (i, name, cfun)
+ {
+ gimple *def_stmt = SSA_NAME_DEF_STMT (name);
+ if (SSA_NAME_IS_DEFAULT_DEF (name))
+ continue;
- timevar_push (TV_TREE_INSERT_PHI_NODES);
+ basic_block def_bb = gimple_bb (def_stmt);
+ imm_use_iterator it;
+ gimple *use_stmt;
+ bool need_phis = false;
+ FOR_EACH_IMM_USE_STMT (use_stmt, it, name)
+ {
+ basic_block use_bb = gimple_bb (use_stmt);
+ if (use_bb != def_bb
+ && ! dominated_by_p (CDI_DOMINATORS, use_bb, def_bb))
+ need_phis = true;
+ }
+ if (need_phis)
+ {
+ tree var = create_tmp_reg (TREE_TYPE (name));
+ use_operand_p use_p;
+ FOR_EACH_IMM_USE_STMT (use_stmt, it, name)
+ {
+ basic_block use_bb = gimple_bb (use_stmt);
+ FOR_EACH_IMM_USE_ON_STMT (use_p, it)
+ SET_USE (use_p, var);
+ update_stmt (use_stmt);
+ set_livein_block (var, use_bb);
+ set_rewrite_uses (use_stmt, true);
+ bitmap_set_bit (interesting_blocks, use_bb->index);
+ }
+ def_operand_p def_p;
+ ssa_op_iter dit;
+ FOR_EACH_SSA_DEF_OPERAND (def_p, def_stmt, dit, SSA_OP_DEF)
+ if (DEF_FROM_PTR (def_p) == name)
+ SET_DEF (def_p, var);
+ update_stmt (def_stmt);
+ set_def_block (var, def_bb, false);
+ set_register_defs (def_stmt, true);
+ bitmap_set_bit (interesting_blocks, def_bb->index);
+ release_ssa_name (name);
+ }
+ }
- vars = VEC_alloc (var_info_p, heap, htab_elements (var_infos));
- FOR_EACH_HTAB_ELEMENT (var_infos, info, var_info_p, hi)
+ auto_vec<var_info *> vars (var_infos->elements ());
+ FOR_EACH_HASH_TABLE_ELEMENT (*var_infos, info, var_info_p, hi)
if (info->info.need_phi_state != NEED_PHI_STATE_NO)
- VEC_quick_push (var_info_p, vars, info);
+ vars.quick_push (info);
/* Do two stages to avoid code generation differences for UID
differences but no UID ordering differences. */
- VEC_qsort (var_info_p, vars, insert_phi_nodes_compare_var_infos);
+ vars.qsort (insert_phi_nodes_compare_var_infos);
- FOR_EACH_VEC_ELT (var_info_p, vars, i, info)
+ FOR_EACH_VEC_ELT (vars, i, info)
{
bitmap idf = compute_idf (info->info.def_blocks.def_blocks, dfs);
insert_phi_nodes_for (info->var, idf, false);
BITMAP_FREE (idf);
}
-
- VEC_free(var_info_p, heap, vars);
-
- timevar_pop (TV_TREE_INSERT_PHI_NODES);
}
static void
register_new_def (tree def, tree sym)
{
- common_info_p info = get_common_info (sym);
+ common_info *info = get_common_info (sym);
tree currdef;
/* If this variable is set in a single basic block and all uses are
in the stack so that we know which symbol is being defined by
this SSA name when we unwind the stack. */
if (currdef && !is_gimple_reg (sym))
- VEC_safe_push (tree, heap, block_defs_stack, sym);
+ block_defs_stack.safe_push (sym);
/* Push the current reaching definition into BLOCK_DEFS_STACK. This
stack is later used by the dominator tree callbacks to restore
block after a recursive visit to all its immediately dominated
blocks. If there is no current reaching definition, then just
record the underlying _DECL node. */
- VEC_safe_push (tree, heap, block_defs_stack, currdef ? currdef : sym);
+ block_defs_stack.safe_push (currdef ? currdef : sym);
/* Set the current reaching definition for SYM to be DEF. */
info->current_def = def;
static tree
get_reaching_def (tree var)
{
- common_info_p info = get_common_info (var);
+ common_info *info = get_common_info (var);
tree currdef;
/* Lookup the current reaching definition for VAR. */
if (currdef == NULL_TREE)
{
tree sym = DECL_P (var) ? var : SSA_NAME_VAR (var);
+ if (! sym)
+ sym = create_tmp_reg (TREE_TYPE (var));
currdef = get_or_create_ssa_default_def (cfun, sym);
}
/* Helper function for rewrite_stmt. Rewrite uses in a debug stmt. */
static void
-rewrite_debug_stmt_uses (gimple stmt)
+rewrite_debug_stmt_uses (gimple *stmt)
{
use_operand_p use_p;
ssa_op_iter iter;
FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE)
{
tree var = USE_FROM_PTR (use_p), def;
- common_info_p info = get_common_info (var);
- gcc_assert (DECL_P (var));
+ common_info *info = get_common_info (var);
+ gcc_checking_assert (DECL_P (var));
def = info->current_def;
if (!def)
{
- if (TREE_CODE (var) == PARM_DECL && single_succ_p (ENTRY_BLOCK_PTR))
+ if (TREE_CODE (var) == PARM_DECL
+ && single_succ_p (ENTRY_BLOCK_PTR_FOR_FN (cfun)))
{
gimple_stmt_iterator gsi
- = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR));
+ =
+ gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
int lim;
/* Search a few source bind stmts at the start of first bb to
see if a DEBUG_EXPR_DECL can't be reused. */
!gsi_end_p (gsi) && lim > 0;
gsi_next (&gsi), lim--)
{
- gimple gstmt = gsi_stmt (gsi);
+ gimple *gstmt = gsi_stmt (gsi);
if (!gimple_debug_source_bind_p (gstmt))
break;
if (gimple_debug_source_bind_get_value (gstmt) == var)
/* If not, add a new source bind stmt. */
if (def == NULL_TREE)
{
- gimple def_temp;
+ gimple *def_temp;
def = make_node (DEBUG_EXPR_DECL);
def_temp = gimple_build_debug_source_bind (def, var, NULL);
DECL_ARTIFICIAL (def) = 1;
TREE_TYPE (def) = TREE_TYPE (var);
- DECL_MODE (def) = DECL_MODE (var);
- gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR));
+ SET_DECL_MODE (def, DECL_MODE (var));
+ gsi =
+ gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT);
}
update = true;
;
else
{
- struct def_blocks_d *db_p = get_def_blocks_for (info);
+ def_blocks *db_p = get_def_blocks_for (info);
/* If there are some non-debug uses in the current bb,
it is fine. */
use_operand_p use_p;
def_operand_p def_p;
ssa_op_iter iter;
- gimple stmt = gsi_stmt (*si);
+ gimple *stmt = gsi_stmt (*si);
/* If mark_def_sites decided that we don't need to rewrite this
statement, ignore it. */
FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES)
{
tree var = USE_FROM_PTR (use_p);
- gcc_assert (DECL_P (var));
+ if (TREE_CODE (var) == SSA_NAME)
+ continue;
+ gcc_checking_assert (DECL_P (var));
SET_USE (use_p, get_reaching_def (var));
}
}
tree name;
tree tracked_var;
- gcc_assert (DECL_P (var));
+ if (TREE_CODE (var) == SSA_NAME)
+ continue;
+ gcc_checking_assert (DECL_P (var));
if (gimple_clobber_p (stmt)
&& is_gimple_reg (var))
{
/* If we rewrite a DECL into SSA form then drop its
clobber stmts and replace uses with a new default def. */
- gcc_assert (TREE_CODE (var) == VAR_DECL
- && !gimple_vdef (stmt));
+ gcc_checking_assert (VAR_P (var) && !gimple_vdef (stmt));
gsi_replace (si, gimple_build_nop (), true);
register_new_def (get_or_create_ssa_default_def (cfun, var), var);
break;
SET_DEF (def_p, name);
register_new_def (DEF_FROM_PTR (def_p), var);
+ /* Do not insert debug stmts if the stmt ends the BB. */
+ if (stmt_ends_bb_p (stmt))
+ continue;
+
tracked_var = target_for_debug_bind (var);
if (tracked_var)
{
- gimple note = gimple_build_debug_bind (tracked_var, name, stmt);
+ gimple *note = gimple_build_debug_bind (tracked_var, name, stmt);
gsi_insert_after (si, note, GSI_SAME_STMT);
}
}
FOR_EACH_EDGE (e, ei, bb->succs)
{
- gimple phi;
- gimple_stmt_iterator gsi;
+ gphi *phi;
+ gphi_iterator gsi;
for (gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi);
gsi_next (&gsi))
{
- tree currdef;
- gimple stmt;
-
- phi = gsi_stmt (gsi);
- currdef = get_reaching_def (SSA_NAME_VAR (gimple_phi_result (phi)));
- stmt = SSA_NAME_DEF_STMT (currdef);
- add_phi_arg (phi, currdef, e, gimple_location (stmt));
+ tree currdef, res;
+ location_t loc;
+
+ phi = gsi.phi ();
+ res = gimple_phi_result (phi);
+ currdef = get_reaching_def (SSA_NAME_VAR (res));
+ /* Virtual operand PHI args do not need a location. */
+ if (virtual_operand_p (res))
+ loc = UNKNOWN_LOCATION;
+ else
+ loc = gimple_location (SSA_NAME_DEF_STMT (currdef));
+ add_phi_arg (phi, currdef, e, loc);
}
}
}
+class rewrite_dom_walker : public dom_walker
+{
+public:
+ rewrite_dom_walker (cdi_direction direction)
+ : dom_walker (direction, ALL_BLOCKS, NULL) {}
+
+ virtual edge before_dom_children (basic_block);
+ virtual void after_dom_children (basic_block);
+};
+
/* SSA Rewriting Step 1. Initialization, create a block local stack
of reaching definitions for new SSA names produced in this block
(BLOCK_DEFS). Register new definitions for every PHI node in the
block. */
-static void
-rewrite_enter_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
- basic_block bb)
+edge
+rewrite_dom_walker::before_dom_children (basic_block bb)
{
- gimple_stmt_iterator gsi;
-
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "\n\nRenaming block #%d\n\n", bb->index);
/* Mark the unwind point for this block. */
- VEC_safe_push (tree, heap, block_defs_stack, NULL_TREE);
+ block_defs_stack.safe_push (NULL_TREE);
/* Step 1. Register new definitions for every PHI node in the block.
Conceptually, all the PHI nodes are executed in parallel and each PHI
node introduces a new version for the associated variable. */
- for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
{
tree result = gimple_phi_result (gsi_stmt (gsi));
register_new_def (result, SSA_NAME_VAR (result));
/* Step 2. Rewrite every variable used in each statement in the block
with its immediate reaching definitions. Update the current definition
of a variable when a new real or virtual definition is found. */
- if (TEST_BIT (interesting_blocks, bb->index))
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ if (bitmap_bit_p (interesting_blocks, bb->index))
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
rewrite_stmt (&gsi);
/* Step 3. Visit all the successor blocks of BB looking for PHI nodes.
reaching definition for the variable and the edge through which that
definition is reaching the PHI node. */
rewrite_add_phi_arguments (bb);
+
+ return NULL;
}
/* Called after visiting all the statements in basic block BB and all
of its dominator children. Restore CURRDEFS to its original value. */
-static void
-rewrite_leave_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
- basic_block bb ATTRIBUTE_UNUSED)
+void
+rewrite_dom_walker::after_dom_children (basic_block bb ATTRIBUTE_UNUSED)
{
/* Restore CURRDEFS to its original state. */
- while (VEC_length (tree, block_defs_stack) > 0)
+ while (block_defs_stack.length () > 0)
{
- tree tmp = VEC_pop (tree, block_defs_stack);
+ tree tmp = block_defs_stack.pop ();
tree saved_def, var;
if (tmp == NULL_TREE)
saved_def = tmp;
var = SSA_NAME_VAR (saved_def);
if (!is_gimple_reg (var))
- var = VEC_pop (tree, block_defs_stack);
+ var = block_defs_stack.pop ();
}
else
{
}
-/* Dump bitmap SET (assumed to contain VAR_DECLs) to FILE. */
-
-void
-dump_decl_set (FILE *file, bitmap set)
-{
- if (set)
- {
- bitmap_iterator bi;
- unsigned i;
-
- fprintf (file, "{ ");
-
- EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
- {
- fprintf (file, "D.%u", i);
- fprintf (file, " ");
- }
-
- fprintf (file, "}");
- }
- else
- fprintf (file, "NIL");
-}
-
-
/* Dump bitmap SET (assumed to contain VAR_DECLs) to FILE. */
DEBUG_FUNCTION void
i = 1;
fprintf (file, "Level %d (current level)\n", i);
- for (j = (int) VEC_length (tree, block_defs_stack) - 1; j >= 0; j--)
+ for (j = (int) block_defs_stack.length () - 1; j >= 0; j--)
{
tree name, var;
- name = VEC_index (tree, block_defs_stack, j);
+ name = block_defs_stack[j];
if (name == NULL_TREE)
{
i++;
if (!is_gimple_reg (var))
{
j--;
- var = VEC_index (tree, block_defs_stack, j);
+ var = block_defs_stack[j];
}
}
fprintf (file, " Previous CURRDEF (");
- print_generic_expr (file, var, 0);
+ print_generic_expr (file, var);
fprintf (file, ") = ");
if (name)
- print_generic_expr (file, name, 0);
+ print_generic_expr (file, name);
else
fprintf (file, "<NIL>");
fprintf (file, "\n");
void
dump_currdefs (FILE *file)
{
- unsigned i;
- tree var;
-
- if (VEC_empty (tree, symbols_to_rename))
+ if (symbols_to_rename.is_empty ())
return;
fprintf (file, "\n\nCurrent reaching definitions\n\n");
- FOR_EACH_VEC_ELT (tree, symbols_to_rename, i, var)
+ for (tree var : symbols_to_rename)
{
- common_info_p info = get_common_info (var);
+ common_info *info = get_common_info (var);
fprintf (file, "CURRDEF (");
- print_generic_expr (file, var, 0);
+ print_generic_expr (file, var);
fprintf (file, ") = ");
if (info->current_def)
- print_generic_expr (file, info->current_def, 0);
+ print_generic_expr (file, info->current_def);
else
fprintf (file, "<NIL>");
fprintf (file, "\n");
/* Dump statistics for the hash table HTAB. */
static void
-htab_statistics (FILE *file, htab_t htab)
+htab_statistics (FILE *file, const hash_table<var_info_hasher> &htab)
{
fprintf (file, "size %ld, %ld elements, %f collision/search ratio\n",
- (long) htab_size (htab),
- (long) htab_elements (htab),
- htab_collisions (htab));
+ (long) htab.size (),
+ (long) htab.elements (),
+ htab.collisions ());
}
{
fprintf (file, "\nHash table statistics:\n");
fprintf (file, " var_infos: ");
- htab_statistics (file, var_infos);
+ htab_statistics (file, *var_infos);
fprintf (file, "\n");
}
}
}
-/* Hashing and equality functions for VAR_INFOS. */
-
-static hashval_t
-var_info_hash (const void *p)
-{
- return DECL_UID (((const struct var_info_d *)p)->var);
-}
-
-static int
-var_info_eq (const void *p1, const void *p2)
-{
- return ((const struct var_info_d *)p1)->var
- == ((const struct var_info_d *)p2)->var;
-}
-
-
/* Callback for htab_traverse to dump the VAR_INFOS hash table. */
-static int
-debug_var_infos_r (void **slot, void *data)
+int
+debug_var_infos_r (var_info **slot, FILE *file)
{
- FILE *file = (FILE *) data;
- struct var_info_d *info = (struct var_info_d *) *slot;
+ var_info *info = *slot;
fprintf (file, "VAR: ");
print_generic_expr (file, info->var, dump_flags);
{
fprintf (file, "\n\nDefinition and live-in blocks:\n\n");
if (var_infos)
- htab_traverse (var_infos, debug_var_infos_r, file);
+ var_infos->traverse <FILE *, debug_var_infos_r> (file);
}
static inline void
register_new_update_single (tree new_name, tree old_name)
{
- common_info_p info = get_common_info (old_name);
+ common_info *info = get_common_info (old_name);
tree currdef = info->current_def;
/* Push the current reaching definition into BLOCK_DEFS_STACK.
restore the reaching definitions for all the variables
defined in the block after a recursive visit to all its
immediately dominated blocks. */
- VEC_reserve (tree, heap, block_defs_stack, 2);
- VEC_quick_push (tree, block_defs_stack, currdef);
- VEC_quick_push (tree, block_defs_stack, old_name);
+ block_defs_stack.reserve (2);
+ block_defs_stack.quick_push (currdef);
+ block_defs_stack.quick_push (old_name);
/* Set the current reaching definition for OLD_NAME to be
NEW_NAME. */
}
+/* If DEF has x_5 = ASAN_POISON () as its current def, add
+ ASAN_POISON_USE (x_5) stmt before GSI to denote the stmt writes into
+ a poisoned (out of scope) variable. */
+
+static void
+maybe_add_asan_poison_write (tree def, gimple_stmt_iterator *gsi)
+{
+ tree cdef = get_current_def (def);
+ if (cdef != NULL
+ && TREE_CODE (cdef) == SSA_NAME
+ && gimple_call_internal_p (SSA_NAME_DEF_STMT (cdef), IFN_ASAN_POISON))
+ {
+ gcall *call
+ = gimple_build_call_internal (IFN_ASAN_POISON_USE, 1, cdef);
+ gimple_set_location (call, gimple_location (gsi_stmt (*gsi)));
+ gsi_insert_before (gsi, call, GSI_SAME_STMT);
+ }
+}
+
+
/* If the operand pointed to by DEF_P is an SSA name in NEW_SSA_NAMES
or OLD_SSA_NAMES, or if it is a symbol marked for renaming,
register it as the current definition for the names replaced by
- DEF_P. */
+ DEF_P. Returns whether the statement should be removed. */
-static inline void
-maybe_register_def (def_operand_p def_p, gimple stmt,
+static inline bool
+maybe_register_def (def_operand_p def_p, gimple *stmt,
gimple_stmt_iterator gsi)
{
tree def = DEF_FROM_PTR (def_p);
tree sym = DECL_P (def) ? def : SSA_NAME_VAR (def);
+ bool to_delete = false;
/* If DEF is a naked symbol that needs renaming, create a new
name for it. */
{
if (DECL_P (def))
{
- tree tracked_var;
-
- def = make_ssa_name (def, stmt);
+ if (gimple_clobber_p (stmt) && is_gimple_reg (sym))
+ {
+ gcc_checking_assert (VAR_P (sym));
+ /* Replace clobber stmts with a default def. This new use of a
+ default definition may make it look like SSA_NAMEs have
+ conflicting lifetimes, so we need special code to let them
+ coalesce properly. */
+ to_delete = true;
+ def = get_or_create_ssa_default_def (cfun, sym);
+ }
+ else
+ {
+ if (asan_sanitize_use_after_scope ())
+ maybe_add_asan_poison_write (def, &gsi);
+ def = make_ssa_name (def, stmt);
+ }
SET_DEF (def_p, def);
- tracked_var = target_for_debug_bind (sym);
+ tree tracked_var = target_for_debug_bind (sym);
if (tracked_var)
{
- gimple note = gimple_build_debug_bind (tracked_var, def, stmt);
+ gimple *note = gimple_build_debug_bind (tracked_var, def, stmt);
/* If stmt ends the bb, insert the debug stmt on the single
non-EH edge from the stmt. */
if (gsi_one_before_end_p (gsi) && stmt_ends_bb_p (stmt))
FOR_EACH_EDGE (e, ei, bb->succs)
if (!(e->flags & EDGE_EH))
{
- gcc_assert (!ef);
+ gcc_checking_assert (!ef);
ef = e;
}
/* If there are other predecessors to ef->dest, then
bind stmts, but there wouldn't be a PC to bind
them to either, so avoid diverging the CFG. */
if (ef && single_pred_p (ef->dest)
- && ef->dest != EXIT_BLOCK_PTR)
+ && ef->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
{
/* If there were PHI nodes in the node, we'd
have to make sure the value we're binding
if (is_old_name (def))
register_new_update_single (def, def);
}
+
+ return to_delete;
}
OLD_SSA_NAMES used by SI will be updated to their current reaching
definition. Names in OLD_SSA_NAMES or NEW_SSA_NAMES defined by SI
will be registered as a new definition for their corresponding name
- in OLD_SSA_NAMES. */
+ in OLD_SSA_NAMES. Returns whether STMT should be removed. */
-static void
-rewrite_update_stmt (gimple stmt, gimple_stmt_iterator gsi)
+static bool
+rewrite_update_stmt (gimple *stmt, gimple_stmt_iterator gsi)
{
use_operand_p use_p;
def_operand_p def_p;
/* Only update marked statements. */
if (!rewrite_uses_p (stmt) && !register_defs_p (stmt))
- return;
+ return false;
if (dump_file && (dump_flags & TDF_DETAILS))
{
/* Register definitions of names in NEW_SSA_NAMES and OLD_SSA_NAMES.
Also register definitions for names whose underlying symbol is
marked for renaming. */
+ bool to_delete = false;
if (register_defs_p (stmt))
FOR_EACH_SSA_DEF_OPERAND (def_p, stmt, iter, SSA_OP_ALL_DEFS)
- maybe_register_def (def_p, stmt, gsi);
+ to_delete |= maybe_register_def (def_p, stmt, gsi);
+
+ return to_delete;
}
{
edge e;
edge_iterator ei;
- unsigned i;
FOR_EACH_EDGE (e, ei, bb->succs)
{
- gimple phi;
- gimple_vec phis;
+ vec<gphi *> phis;
if (!bitmap_bit_p (blocks_with_phis_to_rewrite, e->dest->index))
continue;
- phis = VEC_index (gimple_vec, phis_to_rewrite, e->dest->index);
- FOR_EACH_VEC_ELT (gimple, phis, i, phi)
+ phis = phis_to_rewrite[e->dest->index];
+ for (gphi *phi : phis)
{
tree arg, lhs_sym, reaching_def = NULL;
use_operand_p arg_p;
- gcc_assert (rewrite_uses_p (phi));
+ gcc_checking_assert (rewrite_uses_p (phi));
arg_p = PHI_ARG_DEF_PTR_FROM_EDGE (phi, e);
arg = USE_FROM_PTR (arg_p);
/* Update the argument if there is a reaching def. */
if (reaching_def)
{
- gimple stmt;
- source_location locus;
+ location_t locus;
int arg_i = PHI_ARG_INDEX_FROM_USE (arg_p);
SET_USE (arg_p, reaching_def);
- stmt = SSA_NAME_DEF_STMT (reaching_def);
- /* Single element PHI nodes behave like copies, so get the
- location from the phi argument. */
- if (gimple_code (stmt) == GIMPLE_PHI &&
- gimple_phi_num_args (stmt) == 1)
- locus = gimple_phi_arg_location (stmt, 0);
+ /* Virtual operands do not need a location. */
+ if (virtual_operand_p (reaching_def))
+ locus = UNKNOWN_LOCATION;
else
- locus = gimple_location (stmt);
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (reaching_def);
+ gphi *other_phi = dyn_cast <gphi *> (stmt);
+
+ /* Single element PHI nodes behave like copies, so get the
+ location from the phi argument. */
+ if (other_phi
+ && gimple_phi_num_args (other_phi) == 1)
+ locus = gimple_phi_arg_location (other_phi, 0);
+ else
+ locus = gimple_location (stmt);
+ }
gimple_phi_arg_set_location (phi, arg_i, locus);
}
}
}
+class rewrite_update_dom_walker : public dom_walker
+{
+public:
+ rewrite_update_dom_walker (cdi_direction direction)
+ : dom_walker (direction, ALL_BLOCKS, NULL) {}
+
+ virtual edge before_dom_children (basic_block);
+ virtual void after_dom_children (basic_block);
+};
/* Initialization of block data structures for the incremental SSA
update pass. Create a block local stack of reaching definitions
for new SSA names produced in this block (BLOCK_DEFS). Register
new definitions for every PHI node in the block. */
-static void
-rewrite_update_enter_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
- basic_block bb)
+edge
+rewrite_update_dom_walker::before_dom_children (basic_block bb)
{
bool is_abnormal_phi;
- gimple_stmt_iterator gsi;
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "Registering new PHI nodes in block #%d\n",
bb->index);
/* Mark the unwind point for this block. */
- VEC_safe_push (tree, heap, block_defs_stack, NULL_TREE);
+ block_defs_stack.safe_push (NULL_TREE);
if (!bitmap_bit_p (blocks_to_update, bb->index))
- return;
+ return NULL;
/* Mark the LHS if any of the arguments flows through an abnormal
edge. */
register it as a new definition for its corresponding name. Also
register definitions for names whose underlying symbols are
marked for renaming. */
- for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
{
tree lhs, lhs_sym;
- gimple phi = gsi_stmt (gsi);
+ gphi *phi = gsi.phi ();
if (!register_defs_p (phi))
continue;
}
/* Step 2. Rewrite every variable used in each statement in the block. */
- if (TEST_BIT (interesting_blocks, bb->index))
+ if (bitmap_bit_p (interesting_blocks, bb->index))
{
- gcc_assert (bitmap_bit_p (blocks_to_update, bb->index));
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- rewrite_update_stmt (gsi_stmt (gsi), gsi);
+ gcc_checking_assert (bitmap_bit_p (blocks_to_update, bb->index));
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
+ if (rewrite_update_stmt (gsi_stmt (gsi), gsi))
+ gsi_remove (&gsi, true);
+ else
+ gsi_next (&gsi);
}
/* Step 3. Update PHI nodes. */
rewrite_update_phi_arguments (bb);
+
+ return NULL;
}
/* Called after visiting block BB. Unwind BLOCK_DEFS_STACK to restore
unwinding must be done in the opposite order to what is done in
register_new_update_set. */
-static void
-rewrite_update_leave_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
- basic_block bb ATTRIBUTE_UNUSED)
+void
+rewrite_update_dom_walker::after_dom_children (basic_block bb ATTRIBUTE_UNUSED)
{
- while (VEC_length (tree, block_defs_stack) > 0)
+ while (block_defs_stack.length () > 0)
{
- tree var = VEC_pop (tree, block_defs_stack);
+ tree var = block_defs_stack.pop ();
tree saved_def;
/* NULL indicates the unwind stop point for this block (see
if (var == NULL)
return;
- saved_def = VEC_pop (tree, block_defs_stack);
+ saved_def = block_defs_stack.pop ();
get_common_info (var)->current_def = saved_def;
}
}
static void
rewrite_blocks (basic_block entry, enum rewrite_mode what)
{
- struct dom_walk_data walk_data;
-
- /* Rewrite all the basic blocks in the program. */
- timevar_push (TV_TREE_SSA_REWRITE_BLOCKS);
-
- /* Setup callbacks for the generic dominator tree walker. */
- memset (&walk_data, 0, sizeof (walk_data));
-
- walk_data.dom_direction = CDI_DOMINATORS;
+ block_defs_stack.create (10);
+ /* Recursively walk the dominator tree rewriting each statement in
+ each basic block. */
if (what == REWRITE_ALL)
- {
- walk_data.before_dom_children = rewrite_enter_block;
- walk_data.after_dom_children = rewrite_leave_block;
- }
+ rewrite_dom_walker (CDI_DOMINATORS).walk (entry);
else if (what == REWRITE_UPDATE)
- {
- walk_data.before_dom_children = rewrite_update_enter_block;
- walk_data.after_dom_children = rewrite_update_leave_block;
- }
+ rewrite_update_dom_walker (CDI_DOMINATORS).walk (entry);
else
gcc_unreachable ();
- block_defs_stack = VEC_alloc (tree, heap, 10);
-
- /* Initialize the dominator walker. */
- init_walk_dominator_tree (&walk_data);
-
- /* Recursively walk the dominator tree rewriting each statement in
- each basic block. */
- walk_dominator_tree (&walk_data, entry);
-
- /* Finalize the dominator walker. */
- fini_walk_dominator_tree (&walk_data);
-
/* Debugging dumps. */
if (dump_file && (dump_flags & TDF_STATS))
{
dump_tree_ssa_stats (dump_file);
}
- VEC_free (tree, heap, block_defs_stack);
-
- timevar_pop (TV_TREE_SSA_REWRITE_BLOCKS);
+ block_defs_stack.release ();
}
-
-/* Block processing routine for mark_def_sites. Clear the KILLS bitmap
- at the start of each block, and call mark_def_sites for each statement. */
-
-static void
-mark_def_sites_block (struct dom_walk_data *walk_data, basic_block bb)
+class mark_def_dom_walker : public dom_walker
{
- struct mark_def_sites_global_data *gd;
- bitmap kills;
- gimple_stmt_iterator gsi;
-
- gd = (struct mark_def_sites_global_data *) walk_data->global_data;
- kills = gd->kills;
-
- bitmap_clear (kills);
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- mark_def_sites (bb, gsi_stmt (gsi), kills);
-}
-
-
-/* Mark the definition site blocks for each variable, so that we know
- where the variable is actually live.
-
- The INTERESTING_BLOCKS global will be filled in with all the blocks
- that should be processed by the renamer. It is assumed that the
- caller has already initialized and zeroed it. */
-
-static void
-mark_def_site_blocks (void)
-{
- struct dom_walk_data walk_data;
- struct mark_def_sites_global_data mark_def_sites_global_data;
+public:
+ mark_def_dom_walker (cdi_direction direction);
+ ~mark_def_dom_walker ();
- /* Setup callbacks for the generic dominator tree walker to find and
- mark definition sites. */
- walk_data.dom_direction = CDI_DOMINATORS;
- walk_data.initialize_block_local_data = NULL;
- walk_data.before_dom_children = mark_def_sites_block;
- walk_data.after_dom_children = NULL;
+ virtual edge before_dom_children (basic_block);
+private:
/* Notice that this bitmap is indexed using variable UIDs, so it must be
large enough to accommodate all the variables referenced in the
function, not just the ones we are renaming. */
- mark_def_sites_global_data.kills = BITMAP_ALLOC (NULL);
- walk_data.global_data = &mark_def_sites_global_data;
+ bitmap m_kills;
+};
- /* We do not have any local data. */
- walk_data.block_local_data_size = 0;
+mark_def_dom_walker::mark_def_dom_walker (cdi_direction direction)
+ : dom_walker (direction, ALL_BLOCKS, NULL), m_kills (BITMAP_ALLOC (NULL))
+{
+}
- /* Initialize the dominator walker. */
- init_walk_dominator_tree (&walk_data);
+mark_def_dom_walker::~mark_def_dom_walker ()
+{
+ BITMAP_FREE (m_kills);
+}
- /* Recursively walk the dominator tree. */
- walk_dominator_tree (&walk_data, ENTRY_BLOCK_PTR);
+/* Block processing routine for mark_def_sites. Clear the KILLS bitmap
+ at the start of each block, and call mark_def_sites for each statement. */
- /* Finalize the dominator walker. */
- fini_walk_dominator_tree (&walk_data);
+edge
+mark_def_dom_walker::before_dom_children (basic_block bb)
+{
+ gimple_stmt_iterator gsi;
- /* We no longer need this bitmap, clear and free it. */
- BITMAP_FREE (mark_def_sites_global_data.kills);
+ bitmap_clear (m_kills);
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ mark_def_sites (bb, gsi_stmt (gsi), m_kills);
+ return NULL;
}
-
/* Initialize internal data needed during renaming. */
static void
cfun->gimple_df->in_ssa_p = false;
/* Allocate memory for the DEF_BLOCKS hash table. */
- gcc_assert (var_infos == NULL);
- var_infos = htab_create (VEC_length (tree, cfun->local_decls),
- var_info_hash, var_info_eq, NULL);
+ gcc_assert (!var_infos);
+ var_infos = new hash_table<var_info_hasher>
+ (vec_safe_length (cfun->local_decls));
bitmap_obstack_initialize (&update_ssa_obstack);
}
static void
fini_ssa_renamer (void)
{
- if (var_infos)
- {
- htab_delete (var_infos);
- var_infos = NULL;
- }
+ delete var_infos;
+ var_infos = NULL;
bitmap_obstack_release (&update_ssa_obstack);
insert PHI nodes and rename the function in dominator tree
order.
- 2- Find and mark all the blocks that define variables
- (mark_def_site_blocks).
+ 2- Find and mark all the blocks that define variables.
3- Insert PHI nodes at dominance frontiers (insert_phi_nodes).
Steps 3 and 4 are done using the dominator tree walker
(walk_dominator_tree). */
-static unsigned int
-rewrite_into_ssa (void)
+namespace {
+
+const pass_data pass_data_build_ssa =
+{
+ GIMPLE_PASS, /* type */
+ "ssa", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_TREE_INTO_SSA, /* tv_id */
+ PROP_cfg, /* properties_required */
+ PROP_ssa, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_remove_unused_locals, /* todo_flags_finish */
+};
+
+class pass_build_ssa : public gimple_opt_pass
+{
+public:
+ pass_build_ssa (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_build_ssa, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *fun)
+ {
+ /* Do nothing for funcions that was produced already in SSA form. */
+ return !(fun->curr_properties & PROP_ssa);
+ }
+
+ virtual unsigned int execute (function *);
+
+}; // class pass_build_ssa
+
+unsigned int
+pass_build_ssa::execute (function *fun)
{
bitmap_head *dfs;
basic_block bb;
- unsigned i;
+
+ /* Increase the set of variables we can rewrite into SSA form
+ by clearing TREE_ADDRESSABLE and transform the IL to support this. */
+ if (optimize)
+ execute_update_addresses_taken ();
/* Initialize operand data structures. */
- init_ssa_operands (cfun);
+ init_ssa_operands (fun);
/* Initialize internal data needed by the renamer. */
init_ssa_renamer ();
/* Initialize the set of interesting blocks. The callback
mark_def_sites will add to this set those blocks that the renamer
should process. */
- interesting_blocks = sbitmap_alloc (last_basic_block);
- sbitmap_zero (interesting_blocks);
+ interesting_blocks = sbitmap_alloc (last_basic_block_for_fn (fun));
+ bitmap_clear (interesting_blocks);
/* Initialize dominance frontier. */
- dfs = XNEWVEC (bitmap_head, last_basic_block);
- FOR_EACH_BB (bb)
+ dfs = XNEWVEC (bitmap_head, last_basic_block_for_fn (fun));
+ FOR_EACH_BB_FN (bb, fun)
bitmap_initialize (&dfs[bb->index], &bitmap_default_obstack);
/* 1- Compute dominance frontiers. */
compute_dominance_frontiers (dfs);
/* 2- Find and mark definition sites. */
- mark_def_site_blocks ();
+ mark_def_dom_walker (CDI_DOMINATORS).walk (fun->cfg->x_entry_block_ptr);
/* 3- Insert PHI nodes at dominance frontiers of definition blocks. */
insert_phi_nodes (dfs);
/* 4- Rename all the blocks. */
- rewrite_blocks (ENTRY_BLOCK_PTR, REWRITE_ALL);
+ rewrite_blocks (ENTRY_BLOCK_PTR_FOR_FN (fun), REWRITE_ALL);
/* Free allocated memory. */
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, fun)
bitmap_clear (&dfs[bb->index]);
free (dfs);
/* Try to get rid of all gimplifier generated temporaries by making
its SSA names anonymous. This way we can garbage collect them
all after removing unused locals which we do in our TODO. */
- for (i = 1; i < num_ssa_names; ++i)
+ unsigned i;
+ tree name;
+
+ FOR_EACH_SSA_NAME (i, name, cfun)
{
- tree decl, name = ssa_name (i);
- if (!name
- || SSA_NAME_IS_DEFAULT_DEF (name))
+ if (SSA_NAME_IS_DEFAULT_DEF (name))
continue;
- decl = SSA_NAME_VAR (name);
+ tree decl = SSA_NAME_VAR (name);
if (decl
- && TREE_CODE (decl) == VAR_DECL
+ && VAR_P (decl)
&& !VAR_DECL_IS_VIRTUAL_OPERAND (decl)
- && DECL_ARTIFICIAL (decl)
- && DECL_IGNORED_P (decl)
- && !DECL_NAME (decl))
- SET_SSA_NAME_VAR_OR_IDENTIFIER (name, NULL_TREE);
+ && DECL_IGNORED_P (decl))
+ SET_SSA_NAME_VAR_OR_IDENTIFIER (name, DECL_NAME (decl));
+ }
+
+ /* Initialize SSA_NAME_POINTS_TO_READONLY_MEMORY. */
+ tree fnspec_tree
+ = lookup_attribute ("fn spec",
+ TYPE_ATTRIBUTES (TREE_TYPE (fun->decl)));
+ if (fnspec_tree)
+ {
+ attr_fnspec fnspec (TREE_VALUE (TREE_VALUE (fnspec_tree)));
+ unsigned i = 0;
+ for (tree arg = DECL_ARGUMENTS (cfun->decl);
+ arg; arg = DECL_CHAIN (arg), ++i)
+ {
+ if (!fnspec.arg_specified_p (i))
+ break;
+ if (fnspec.arg_readonly_p (i))
+ {
+ tree name = ssa_default_def (fun, arg);
+ if (name)
+ SSA_NAME_POINTS_TO_READONLY_MEMORY (name) = 1;
+ }
+ }
}
return 0;
}
+} // anon namespace
-struct gimple_opt_pass pass_build_ssa =
-{
- {
- GIMPLE_PASS,
- "ssa", /* name */
- NULL, /* gate */
- rewrite_into_ssa, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_TREE_SSA_OTHER, /* tv_id */
- PROP_cfg, /* properties_required */
- PROP_ssa, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_verify_ssa
- | TODO_remove_unused_locals /* todo_flags_finish */
- }
-};
+gimple_opt_pass *
+make_pass_build_ssa (gcc::context *ctxt)
+{
+ return new pass_build_ssa (ctxt);
+}
/* Mark the definition of VAR at STMT and BB as interesting for the
renamer. BLOCKS is the set of blocks that need updating. */
static void
-mark_def_interesting (tree var, gimple stmt, basic_block bb, bool insert_phi_p)
+mark_def_interesting (tree var, gimple *stmt, basic_block bb,
+ bool insert_phi_p)
{
- gcc_assert (bitmap_bit_p (blocks_to_update, bb->index));
+ gcc_checking_assert (bitmap_bit_p (blocks_to_update, bb->index));
set_register_defs (stmt, true);
if (insert_phi_p)
nodes. */
static inline void
-mark_use_interesting (tree var, gimple stmt, basic_block bb, bool insert_phi_p)
+mark_use_interesting (tree var, gimple *stmt, basic_block bb,
+ bool insert_phi_p)
{
basic_block def_bb = gimple_bb (stmt);
mark_block_for_update (bb);
if (gimple_code (stmt) == GIMPLE_PHI)
- mark_phi_for_rewrite (def_bb, stmt);
+ mark_phi_for_rewrite (def_bb, as_a <gphi *> (stmt));
else
{
set_rewrite_uses (stmt, true);
replace it). */
if (insert_phi_p)
{
- struct def_blocks_d *db_p = get_def_blocks_for (get_common_info (var));
+ def_blocks *db_p = get_def_blocks_for (get_common_info (var));
if (!bitmap_bit_p (db_p->def_blocks, bb->index))
set_livein_block (var, bb);
}
}
-
-/* Do a dominator walk starting at BB processing statements that
- reference symbols in SSA operands. This is very similar to
- mark_def_sites, but the scan handles statements whose operands may
- already be SSA names.
+/* Processing statements in BB that reference symbols in SSA operands.
+ This is very similar to mark_def_sites, but the scan handles
+ statements whose operands may already be SSA names.
If INSERT_PHI_P is true, mark those uses as live in the
corresponding block. This is later used by the PHI placement
that. */
static void
-prepare_block_for_update (basic_block bb, bool insert_phi_p)
+prepare_block_for_update_1 (basic_block bb, bool insert_phi_p)
{
- basic_block son;
- gimple_stmt_iterator si;
edge e;
edge_iterator ei;
/* Process PHI nodes marking interesting those that define or use
the symbols that we are interested in. */
- for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
+ for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si);
+ gsi_next (&si))
{
- gimple phi = gsi_stmt (si);
+ gphi *phi = si.phi ();
tree lhs_sym, lhs = gimple_phi_result (phi);
if (TREE_CODE (lhs) == SSA_NAME
}
/* Process the statements. */
- for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
+ for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);
+ gsi_next (&si))
{
- gimple stmt;
+ gimple *stmt;
ssa_op_iter i;
use_operand_p use_p;
def_operand_p def_p;
}
}
- /* Now visit all the blocks dominated by BB. */
- for (son = first_dom_son (CDI_DOMINATORS, bb);
- son;
- son = next_dom_son (CDI_DOMINATORS, son))
- prepare_block_for_update (son, insert_phi_p);
}
+/* Do a dominator walk starting at BB processing statements that
+ reference symbols in SSA operands. This is very similar to
+ mark_def_sites, but the scan handles statements whose operands may
+ already be SSA names.
+
+ If INSERT_PHI_P is true, mark those uses as live in the
+ corresponding block. This is later used by the PHI placement
+ algorithm to make PHI pruning decisions.
+
+ FIXME. Most of this would be unnecessary if we could associate a
+ symbol to all the SSA names that reference it. But that
+ sounds like it would be expensive to maintain. Still, it
+ would be interesting to see if it makes better sense to do
+ that. */
+static void
+prepare_block_for_update (basic_block bb, bool insert_phi_p)
+{
+ size_t sp = 0;
+ basic_block *worklist;
+
+ /* Allocate the worklist. */
+ worklist = XNEWVEC (basic_block, n_basic_blocks_for_fn (cfun));
+ /* Add the BB to the worklist. */
+ worklist[sp++] = bb;
+
+ while (sp)
+ {
+ basic_block bb;
+ basic_block son;
+
+ /* Pick a block from the worklist. */
+ bb = worklist[--sp];
+
+ prepare_block_for_update_1 (bb, insert_phi_p);
+
+ /* Now add all the blocks dominated by BB to the worklist. */
+ for (son = first_dom_son (CDI_DOMINATORS, bb);
+ son;
+ son = next_dom_son (CDI_DOMINATORS, son))
+ worklist[sp++] = son;
+ }
+ free (worklist);
+}
/* Helper for prepare_names_to_update. Mark all the use sites for
NAME as interesting. BLOCKS and INSERT_PHI_P are as in
use_operand_p use_p;
imm_use_iterator iter;
+ /* If we rename virtual operands do not update them. */
+ if (virtual_operand_p (name)
+ && cfun->gimple_df->rename_vops)
+ return;
+
FOR_EACH_IMM_USE_FAST (use_p, iter, name)
{
- gimple stmt = USE_STMT (use_p);
+ gimple *stmt = USE_STMT (use_p);
basic_block bb = gimple_bb (stmt);
if (gimple_code (stmt) == GIMPLE_PHI)
{
int ix = PHI_ARG_INDEX_FROM_USE (use_p);
- edge e = gimple_phi_arg_edge (stmt, ix);
+ edge e = gimple_phi_arg_edge (as_a <gphi *> (stmt), ix);
mark_use_interesting (name, stmt, e->src, insert_phi_p);
}
else
static void
prepare_def_site_for (tree name, bool insert_phi_p)
{
- gimple stmt;
+ gimple *stmt;
basic_block bb;
- gcc_assert (names_to_release == NULL
- || !bitmap_bit_p (names_to_release, SSA_NAME_VERSION (name)));
+ gcc_checking_assert (names_to_release == NULL
+ || !bitmap_bit_p (names_to_release,
+ SSA_NAME_VERSION (name)));
+
+ /* If we rename virtual operands do not update them. */
+ if (virtual_operand_p (name)
+ && cfun->gimple_df->rename_vops)
+ return;
stmt = SSA_NAME_DEF_STMT (name);
bb = gimple_bb (stmt);
if (bb)
{
- gcc_assert (bb->index < last_basic_block);
+ gcc_checking_assert (bb->index < last_basic_block_for_fn (cfun));
mark_block_for_update (bb);
mark_def_interesting (name, stmt, bb, insert_phi_p);
}
want to replace existing instances. */
if (names_to_release)
EXECUTE_IF_SET_IN_BITMAP (names_to_release, 0, i, bi)
- RESET_BIT (new_ssa_names, i);
+ bitmap_clear_bit (new_ssa_names, i);
/* First process names in NEW_SSA_NAMES. Otherwise, uses of old
names may be considered to be live-in on blocks that contain
definitions for their replacements. */
- EXECUTE_IF_SET_IN_SBITMAP (new_ssa_names, 0, i, sbi)
+ EXECUTE_IF_SET_IN_BITMAP (new_ssa_names, 0, i, sbi)
prepare_def_site_for (ssa_name (i), insert_phi_p);
/* If an old name is in NAMES_TO_RELEASE, we cannot remove it from
OLD_SSA_NAMES, but we have to ignore its definition site. */
- EXECUTE_IF_SET_IN_SBITMAP (old_ssa_names, 0, i, sbi)
+ EXECUTE_IF_SET_IN_BITMAP (old_ssa_names, 0, i, sbi)
{
if (names_to_release == NULL || !bitmap_bit_p (names_to_release, i))
prepare_def_site_for (ssa_name (i), insert_phi_p);
bitmap old_set;
bitmap_iterator bi;
- print_generic_expr (file, name, 0);
+ print_generic_expr (file, name);
fprintf (file, " -> { ");
old_set = names_replaced_by (name);
EXECUTE_IF_SET_IN_BITMAP (old_set, 0, i, bi)
{
- print_generic_expr (file, ssa_name (i), 0);
+ print_generic_expr (file, ssa_name (i));
fprintf (file, " ");
}
if (!need_ssa_update_p (cfun))
return;
- if (new_ssa_names && sbitmap_first_set_bit (new_ssa_names) >= 0)
+ if (new_ssa_names && bitmap_first_set_bit (new_ssa_names) >= 0)
{
sbitmap_iterator sbi;
fprintf (file, "N_i -> { O_1 ... O_j } means that N_i replaces "
"O_1, ..., O_j\n\n");
- EXECUTE_IF_SET_IN_SBITMAP (new_ssa_names, 0, i, sbi)
+ EXECUTE_IF_SET_IN_BITMAP (new_ssa_names, 0, i, sbi)
dump_names_replaced_by (file, ssa_name (i));
}
fprintf (file, "\nSSA names to release after updating the SSA web\n\n");
EXECUTE_IF_SET_IN_BITMAP (names_to_release, 0, i, bi)
{
- print_generic_expr (file, ssa_name (i), 0);
+ print_generic_expr (file, ssa_name (i));
fprintf (file, " ");
}
fprintf (file, "\n");
add_new_name_mapping are typically done after creating new SSA
names, so we'll need to reallocate these arrays. */
old_ssa_names = sbitmap_alloc (num_ssa_names + NAME_SETS_GROWTH_FACTOR);
- sbitmap_zero (old_ssa_names);
+ bitmap_clear (old_ssa_names);
new_ssa_names = sbitmap_alloc (num_ssa_names + NAME_SETS_GROWTH_FACTOR);
- sbitmap_zero (new_ssa_names);
+ bitmap_clear (new_ssa_names);
bitmap_obstack_initialize (&update_ssa_obstack);
BITMAP_FREE (symbols_to_rename_set);
symbols_to_rename_set = NULL;
- VEC_free (tree, heap, symbols_to_rename);
+ symbols_to_rename.release ();
if (names_to_release)
{
if (blocks_with_phis_to_rewrite)
EXECUTE_IF_SET_IN_BITMAP (blocks_with_phis_to_rewrite, 0, i, bi)
- {
- gimple_vec phis = VEC_index (gimple_vec, phis_to_rewrite, i);
-
- VEC_free (gimple, heap, phis);
- VEC_replace (gimple_vec, phis_to_rewrite, i, NULL);
- }
+ phis_to_rewrite[i].release ();
BITMAP_FREE (blocks_with_phis_to_rewrite);
BITMAP_FREE (blocks_to_update);
/* Create a new name for OLD_NAME in statement STMT and replace the
- operand pointed to by DEF_P with the newly created name. Return
- the new name and register the replacement mapping <NEW, OLD> in
+ operand pointed to by DEF_P with the newly created name. If DEF_P
+ is NULL then STMT should be a GIMPLE assignment.
+ Return the new name and register the replacement mapping <NEW, OLD> in
update_ssa's tables. */
tree
-create_new_def_for (tree old_name, gimple stmt, def_operand_p def)
+create_new_def_for (tree old_name, gimple *stmt, def_operand_p def)
{
- tree new_name = duplicate_ssa_name (old_name, stmt);
+ tree new_name;
- SET_DEF (def, new_name);
+ timevar_push (TV_TREE_SSA_INCREMENTAL);
+
+ if (!update_ssa_initialized_fn)
+ init_update_ssa (cfun);
+
+ gcc_assert (update_ssa_initialized_fn == cfun);
+
+ new_name = duplicate_ssa_name (old_name, stmt);
+ if (def)
+ SET_DEF (def, new_name);
+ else
+ gimple_assign_set_lhs (stmt, new_name);
if (gimple_code (stmt) == GIMPLE_PHI)
{
SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_name) = bb_has_abnormal_pred (bb);
}
- register_new_name_mapping (new_name, old_name);
+ add_new_name_mapping (new_name, old_name);
/* For the benefit of passes that will be updating the SSA form on
their own, set the current reaching definition of OLD_NAME to be
NEW_NAME. */
get_ssa_name_ann (old_name)->info.current_def = new_name;
+ timevar_pop (TV_TREE_SSA_INCREMENTAL);
+
return new_name;
}
-/* Register name NEW to be a replacement for name OLD. This function
- must be called for every replacement that should be performed by
- update_ssa. */
+/* Mark virtual operands of FN for renaming by update_ssa. */
void
-register_new_name_mapping (tree new_tree, tree old)
+mark_virtual_operands_for_renaming (struct function *fn)
{
- if (!update_ssa_initialized_fn)
- init_update_ssa (cfun);
+ fn->gimple_df->ssa_renaming_needed = 1;
+ fn->gimple_df->rename_vops = 1;
+}
- gcc_assert (update_ssa_initialized_fn == cfun);
+/* Replace all uses of NAME by underlying variable and mark it
+ for renaming. This assumes the defining statement of NAME is
+ going to be removed. */
- add_new_name_mapping (new_tree, old);
-}
+void
+mark_virtual_operand_for_renaming (tree name)
+{
+ tree name_var = SSA_NAME_VAR (name);
+ bool used = false;
+ imm_use_iterator iter;
+ use_operand_p use_p;
+ gimple *stmt;
+ gcc_assert (VAR_DECL_IS_VIRTUAL_OPERAND (name_var));
+ FOR_EACH_IMM_USE_STMT (stmt, iter, name)
+ {
+ FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+ SET_USE (use_p, name_var);
+ used = true;
+ }
+ if (used)
+ mark_virtual_operands_for_renaming (cfun);
+}
-/* Mark virtual operands of FN for renaming by update_ssa. */
+/* Replace all uses of the virtual PHI result by its underlying variable
+ and mark it for renaming. This assumes the PHI node is going to be
+ removed. */
void
-mark_virtual_operands_for_renaming (struct function *fn)
+mark_virtual_phi_result_for_renaming (gphi *phi)
{
- fn->gimple_df->ssa_renaming_needed = 1;
- fn->gimple_df->rename_vops = 1;
-}
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Marking result for renaming : ");
+ print_gimple_stmt (dump_file, phi, 0, TDF_SLIM);
+ fprintf (dump_file, "\n");
+ }
+ mark_virtual_operand_for_renaming (gimple_phi_result (phi));
+}
/* Return true if there is any work to be done by update_ssa
for function FN. */
unsigned update_flags)
{
basic_block entry;
- struct def_blocks_d *db;
+ def_blocks *db;
bitmap idf, pruned_idf;
bitmap_iterator bi;
unsigned i;
common dominator of all the definition blocks. */
entry = nearest_common_dominator_for_set (CDI_DOMINATORS,
db->def_blocks);
- if (entry != ENTRY_BLOCK_PTR)
+ if (entry != ENTRY_BLOCK_PTR_FOR_FN (cfun))
EXECUTE_IF_SET_IN_BITMAP (idf, 0, i, bi)
- if (BASIC_BLOCK (i) != entry
- && dominated_by_p (CDI_DOMINATORS, BASIC_BLOCK (i), entry))
+ if (BASIC_BLOCK_FOR_FN (cfun, i) != entry
+ && dominated_by_p (CDI_DOMINATORS,
+ BASIC_BLOCK_FOR_FN (cfun, i), entry))
bitmap_set_bit (pruned_idf, i);
}
else
{
/* Otherwise, do not prune the IDF for VAR. */
- gcc_assert (update_flags == TODO_update_ssa_full_phi);
+ gcc_checking_assert (update_flags == TODO_update_ssa_full_phi);
bitmap_copy (pruned_idf, idf);
}
}
{
edge e;
edge_iterator ei;
- basic_block bb = BASIC_BLOCK (i);
+ basic_block bb = BASIC_BLOCK_FOR_FN (cfun, i);
FOR_EACH_EDGE (e, ei, bb->preds)
if (e->src->index >= 0)
BITMAP_FREE (idf);
}
+/* Sort symbols_to_rename after their DECL_UID. */
+
+static int
+insert_updated_phi_nodes_compare_uids (const void *a, const void *b)
+{
+ const_tree syma = *(const const_tree *)a;
+ const_tree symb = *(const const_tree *)b;
+ if (DECL_UID (syma) == DECL_UID (symb))
+ return 0;
+ return DECL_UID (syma) < DECL_UID (symb) ? -1 : 1;
+}
/* Given a set of newly created SSA names (NEW_SSA_NAMES) and a set of
existing SSA names (OLD_SSA_NAMES), update the SSA form so that:
frontier of the blocks where each of NEW_SSA_NAMES are defined.
The mapping between OLD_SSA_NAMES and NEW_SSA_NAMES is setup by
- calling register_new_name_mapping for every pair of names that the
+ calling create_new_def_for to create new defs for names that the
caller wants to replace.
- The caller identifies the new names that have been inserted and the
- names that need to be replaced by calling register_new_name_mapping
- for every pair <NEW, OLD>. Note that the function assumes that the
- new names have already been inserted in the IL.
+ The caller cretaes the new names to be inserted and the names that need
+ to be replaced by calling create_new_def_for for each old definition
+ to be replaced. Note that the function assumes that the
+ new defining statement has already been inserted in the IL.
For instance, given the following code:
if (!need_ssa_update_p (cfun))
return;
+ if (flag_checking)
+ {
+ timevar_push (TV_TREE_STMT_VERIFY);
+
+ bool err = false;
+
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ gimple_stmt_iterator gsi;
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+
+ ssa_op_iter i;
+ use_operand_p use_p;
+ FOR_EACH_SSA_USE_OPERAND (use_p, stmt, i, SSA_OP_ALL_USES)
+ {
+ tree use = USE_FROM_PTR (use_p);
+ if (TREE_CODE (use) != SSA_NAME)
+ continue;
+
+ if (SSA_NAME_IN_FREE_LIST (use))
+ {
+ error ("statement uses released SSA name");
+ debug_gimple_stmt (stmt);
+ fprintf (stderr, "The use of ");
+ print_generic_expr (stderr, use);
+ fprintf (stderr," should have been replaced\n");
+ err = true;
+ }
+ }
+ }
+ }
+
+ if (err)
+ internal_error ("cannot update SSA form");
+
+ timevar_pop (TV_TREE_STMT_VERIFY);
+ }
+
timevar_push (TV_TREE_SSA_INCREMENTAL);
if (dump_file && (dump_flags & TDF_DETAILS))
/* If we only need to update virtuals, remove all the mappings for
real names before proceeding. The caller is responsible for
having dealt with the name mappings before calling update_ssa. */
- sbitmap_zero (old_ssa_names);
- sbitmap_zero (new_ssa_names);
+ bitmap_clear (old_ssa_names);
+ bitmap_clear (new_ssa_names);
}
gcc_assert (update_ssa_initialized_fn == cfun);
blocks_with_phis_to_rewrite = BITMAP_ALLOC (NULL);
- if (!phis_to_rewrite)
- phis_to_rewrite = VEC_alloc (gimple_vec, heap, last_basic_block + 1);
+ if (!phis_to_rewrite.exists ())
+ phis_to_rewrite.create (last_basic_block_for_fn (cfun) + 1);
blocks_to_update = BITMAP_ALLOC (NULL);
/* Ensure that the dominance information is up-to-date. */
/* If there are names defined in the replacement table, prepare
definition and use sites for all the names in NEW_SSA_NAMES and
OLD_SSA_NAMES. */
- if (sbitmap_first_set_bit (new_ssa_names) >= 0)
+ if (bitmap_first_set_bit (new_ssa_names) >= 0)
{
+ statistics_counter_event (cfun, "Incremental SSA update", 1);
+
prepare_names_to_update (insert_phi_p);
/* If all the names in NEW_SSA_NAMES had been marked for
removal, and there are no symbols to rename, then there's
nothing else to do. */
- if (sbitmap_first_set_bit (new_ssa_names) < 0
+ if (bitmap_first_set_bit (new_ssa_names) < 0
&& !cfun->gimple_df->ssa_renaming_needed)
goto done;
}
/* Next, determine the block at which to start the renaming process. */
if (cfun->gimple_df->ssa_renaming_needed)
{
+ statistics_counter_event (cfun, "Symbol to SSA rewrite", 1);
+
/* If we rename bare symbols initialize the mapping to
auxiliar info we need to keep track of. */
- var_infos = htab_create (47, var_info_hash, var_info_eq, NULL);
+ var_infos = new hash_table<var_info_hasher> (47);
/* If we have to rename some symbols from scratch, we need to
start the process at the root of the CFG. FIXME, it should
be possible to determine the nearest block that had a
definition for each of the symbols that are marked for
updating. For now this seems more work than it's worth. */
- start_bb = ENTRY_BLOCK_PTR;
+ start_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun);
/* Traverse the CFG looking for existing definitions and uses of
symbols in SSA operands. Mark interesting blocks and
placement heuristics. */
prepare_block_for_update (start_bb, insert_phi_p);
-#ifdef ENABLE_CHECKING
- for (i = 1; i < num_ssa_names; ++i)
- {
- tree name = ssa_name (i);
- if (!name
- || virtual_operand_p (name))
- continue;
+ tree name;
- /* For all but virtual operands, which do not have SSA names
- with overlapping life ranges, ensure that symbols marked
- for renaming do not have existing SSA names associated with
- them as we do not re-write them out-of-SSA before going
- into SSA for the remaining symbol uses. */
- if (marked_for_renaming (SSA_NAME_VAR (name)))
- {
- fprintf (stderr, "Existing SSA name for symbol marked for "
- "renaming: ");
- print_generic_expr (stderr, name, TDF_SLIM);
- fprintf (stderr, "\n");
- internal_error ("SSA corruption");
- }
- }
-#endif
+ if (flag_checking)
+ FOR_EACH_SSA_NAME (i, name, cfun)
+ {
+ if (virtual_operand_p (name))
+ continue;
+
+ /* For all but virtual operands, which do not have SSA names
+ with overlapping life ranges, ensure that symbols marked
+ for renaming do not have existing SSA names associated with
+ them as we do not re-write them out-of-SSA before going
+ into SSA for the remaining symbol uses. */
+ if (marked_for_renaming (SSA_NAME_VAR (name)))
+ {
+ fprintf (stderr, "Existing SSA name for symbol marked for "
+ "renaming: ");
+ print_generic_expr (stderr, name, TDF_SLIM);
+ fprintf (stderr, "\n");
+ internal_error ("SSA corruption");
+ }
+ }
}
else
{
/* If the caller requested PHI nodes to be added, compute
dominance frontiers. */
- dfs = XNEWVEC (bitmap_head, last_basic_block);
- FOR_EACH_BB (bb)
+ dfs = XNEWVEC (bitmap_head, last_basic_block_for_fn (cfun));
+ FOR_EACH_BB_FN (bb, cfun)
bitmap_initialize (&dfs[bb->index], &bitmap_default_obstack);
compute_dominance_frontiers (dfs);
- if (sbitmap_first_set_bit (old_ssa_names) >= 0)
+ if (bitmap_first_set_bit (old_ssa_names) >= 0)
{
sbitmap_iterator sbi;
will grow while we are traversing it (but it will not
gain any new members). Copy OLD_SSA_NAMES to a temporary
for traversal. */
- sbitmap tmp = sbitmap_alloc (SBITMAP_SIZE (old_ssa_names));
- sbitmap_copy (tmp, old_ssa_names);
- EXECUTE_IF_SET_IN_SBITMAP (tmp, 0, i, sbi)
+ auto_sbitmap tmp (SBITMAP_SIZE (old_ssa_names));
+ bitmap_copy (tmp, old_ssa_names);
+ EXECUTE_IF_SET_IN_BITMAP (tmp, 0, i, sbi)
insert_updated_phi_nodes_for (ssa_name (i), dfs, blocks_to_update,
update_flags);
- sbitmap_free (tmp);
}
- FOR_EACH_VEC_ELT (tree, symbols_to_rename, i, sym)
+ symbols_to_rename.qsort (insert_updated_phi_nodes_compare_uids);
+ FOR_EACH_VEC_ELT (symbols_to_rename, i, sym)
insert_updated_phi_nodes_for (sym, dfs, blocks_to_update,
update_flags);
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
bitmap_clear (&dfs[bb->index]);
free (dfs);
/* Insertion of PHI nodes may have added blocks to the region.
We need to re-compute START_BB to include the newly added
blocks. */
- if (start_bb != ENTRY_BLOCK_PTR)
+ if (start_bb != ENTRY_BLOCK_PTR_FOR_FN (cfun))
start_bb = nearest_common_dominator_for_set (CDI_DOMINATORS,
blocks_to_update);
}
/* Reset the current definition for name and symbol before renaming
the sub-graph. */
- EXECUTE_IF_SET_IN_SBITMAP (old_ssa_names, 0, i, sbi)
+ EXECUTE_IF_SET_IN_BITMAP (old_ssa_names, 0, i, sbi)
get_ssa_name_ann (ssa_name (i))->info.current_def = NULL_TREE;
- FOR_EACH_VEC_ELT (tree, symbols_to_rename, i, sym)
+ FOR_EACH_VEC_ELT (symbols_to_rename, i, sym)
get_var_info (sym)->info.current_def = NULL_TREE;
/* Now start the renaming process at START_BB. */
- interesting_blocks = sbitmap_alloc (last_basic_block);
- sbitmap_zero (interesting_blocks);
+ interesting_blocks = sbitmap_alloc (last_basic_block_for_fn (cfun));
+ bitmap_clear (interesting_blocks);
EXECUTE_IF_SET_IN_BITMAP (blocks_to_update, 0, i, bi)
- SET_BIT (interesting_blocks, i);
+ bitmap_set_bit (interesting_blocks, i);
rewrite_blocks (start_bb, REWRITE_UPDATE);
c = 0;
EXECUTE_IF_SET_IN_BITMAP (blocks_to_update, 0, i, bi)
c++;
- fprintf (dump_file, "Number of blocks in CFG: %d\n", last_basic_block);
+ fprintf (dump_file, "Number of blocks in CFG: %d\n",
+ last_basic_block_for_fn (cfun));
fprintf (dump_file, "Number of blocks to update: %d (%3.0f%%)\n",
- c, PERCENT (c, last_basic_block));
+ c, PERCENT (c, last_basic_block_for_fn (cfun)));
if (dump_flags & TDF_DETAILS)
{