diff options
author | Diego Novillo <dnovillo@redhat.com> | 2004-03-19 02:07:25 +0000 |
---|---|---|
committer | Diego Novillo <dnovillo@redhat.com> | 2004-03-19 02:07:25 +0000 |
commit | 05b3c5d398c4861a5a275dc1fbdf7fc8274769a1 (patch) | |
tree | a7c38831784f899cc51185e656b74145105a0672 | |
parent | b81dd463696bfa1c7bc935311d7aaac75baa571b (diff) |
* Makefile.in (TREE_FLOW_H): Reformat.
(OBJS-common): Add tree-into-ssa.o and tree-outof-ssa.o.
(tree-ssa.o): Remove dependency on domwalk.h and tree-ssa-live.h
(tree-into-ssa.o): New.
(tree-outof-ssa.o): New.
(GTFILES): Remove tree-ssa.c.
(gt-tree-ssa.h): Remove.
* tree-into-ssa.c: New file.
Move all the functions used to rename into SSA from tree-ssa.c.
Update/add comments.
Remove unused variables and structures.
Don't use GGC for memory allocation.
* tree-outof-ssa.c: New file.
Move all the functions used to rename out of SSA from
tree-ssa.c.
Update/add comments.
* tree-ssa-alias.c (compute_points_to_and_addr_escape): Add
bibliographic reference.
git-svn-id: https://gcc.gnu.org/svn/gcc/branches/tree-ssa-20020619-branch@79666 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r-- | gcc/ChangeLog.tree-ssa | 21 | ||||
-rw-r--r-- | gcc/Makefile.in | 33 | ||||
-rw-r--r-- | gcc/tree-flow.h | 4 | ||||
-rw-r--r-- | gcc/tree-into-ssa.c | 1167 | ||||
-rw-r--r-- | gcc/tree-outof-ssa.c | 2025 | ||||
-rw-r--r-- | gcc/tree-ssa-alias.c | 7 | ||||
-rw-r--r-- | gcc/tree-ssa.c | 3158 |
7 files changed, 3247 insertions, 3168 deletions
diff --git a/gcc/ChangeLog.tree-ssa b/gcc/ChangeLog.tree-ssa index a1c70ab897a..a91a9190733 100644 --- a/gcc/ChangeLog.tree-ssa +++ b/gcc/ChangeLog.tree-ssa @@ -1,3 +1,24 @@ +2004-03-18 Diego Novillo <dnovillo@redhat.com> + + * Makefile.in (TREE_FLOW_H): Reformat. + (OBJS-common): Add tree-into-ssa.o and tree-outof-ssa.o. + (tree-ssa.o): Remove dependency on domwalk.h and tree-ssa-live.h + (tree-into-ssa.o): New. + (tree-outof-ssa.o): New. + (GTFILES): Remove tree-ssa.c. + (gt-tree-ssa.h): Remove. + * tree-into-ssa.c: New file. + Move all the functions used to rename into SSA from tree-ssa.c. + Update/add comments. + Remove unused variables and structures. + Don't use GGC for memory allocation. + * tree-outof-ssa.c: New file. + Move all the functions used to rename out of SSA from + tree-ssa.c. + Update/add comments. + * tree-ssa-alias.c (compute_points_to_and_addr_escape): Add + bibliographic reference. + 2003-03-18 Jeff Law <law@redhat.com> * Makefile.in (tree-tailcall.o): Depend on langhooks.h. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index a10a7ce4aea..adc538e9a34 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -702,8 +702,9 @@ PREDICT_H = predict.h predict.def CPPLIB_H = cpplib.h line-map.h TREE_DUMP_H = tree-dump.h $(SPLAY_TREE_H) TREE_SIMPLE_H = tree-simple.h tree-iterator.h -TREE_FLOW_H = tree-flow.h tree-flow-inline.h tree-ssa-operands.h bitmap.h $(BASIC_BLOCK_H) \ - hard-reg-set.h $(TREE_SIMPLE_H) $(HASHTAB_H) +TREE_FLOW_H = tree-flow.h tree-flow-inline.h tree-ssa-operands.h \ + bitmap.h $(BASIC_BLOCK_H) hard-reg-set.h $(TREE_SIMPLE_H) \ + $(HASHTAB_H) PRETTY_PRINT_H = pretty-print.h input.h $(OBSTACK_H) DIAGNOSTIC_H = diagnostic.h diagnostic.def $(PRETTY_PRINT_H) C_PRETTY_PRINT_H = $(PRETTY_PRINT_H) $(C_COMMON_H) $(TREE_H) @@ -866,9 +867,9 @@ C_OBJS = c-parse.o c-lang.o stub-objc.o $(C_AND_OBJC_OBJS) OBJS-common = \ tree-cfg.o tree-dfa.o tree-eh.o tree-ssa.o tree-optimize.o tree-simple.o \ - tree-alias-type.o gimplify.o tree-pretty-print.o \ - tree-alias-common.o tree-ssa-ccp.o tree-browser.o @ANDER@ tree-ssa-dce.o \ - tree-ssa-copy.o tree-nrv.o tree-ssa-copyrename.o \ + tree-alias-type.o gimplify.o tree-pretty-print.o tree-into-ssa.o \ + tree-outof-ssa.o tree-alias-common.o tree-ssa-ccp.o tree-browser.o \ + @ANDER@ tree-ssa-dce.o tree-ssa-copy.o tree-nrv.o tree-ssa-copyrename.o \ tree-ssa-pre.o tree-ssa-live.o tree-ssa-operands.o tree-ssa-alias.o \ tree-ssa-phiopt.o tree-ssa-forwprop.o tree-nested.o tree-ssa-dse.o \ tree-ssa-dom.o domwalk.o tree-tailcall.o gimple-low.o tree-iterator.o \ @@ -1554,10 +1555,19 @@ tree-alias-common.o: tree-alias-common.c tree-alias-common.h $(SYSTEM_H) \ $(CONFIG_H) $(GGC_H) $(TREE_H) gt-tree-alias-common.h $(TREE_FLOW_H) \ $(TM_H) coretypes.h cgraph.h tree-pass.h $(TIMEVAR_H) tree-ssa.o : tree-ssa.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ - $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) output.h diagnostic.h \ + $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) output.h diagnostic.h \ + errors.h toplev.h function.h $(TIMEVAR_H) tree-alias-common.h \ + $(TM_H) coretypes.h $(TREE_DUMP_H) langhooks.h cfgloop.h \ + tree-pass.h +tree-into-ssa.o : tree-into-ssa.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ + $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) output.h diagnostic.h \ errors.h toplev.h function.h $(TIMEVAR_H) tree-alias-common.h \ - $(TM_H) coretypes.h $(TREE_DUMP_H) tree-ssa-live.h langhooks.h \ - cfgloop.h domwalk.h tree-pass.h + $(TM_H) coretypes.h $(TREE_DUMP_H) langhooks.h domwalk.h tree-pass.h +tree-outof-ssa.o : tree-outof-ssa.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ + $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) output.h diagnostic.h \ + errors.h toplev.h function.h $(TIMEVAR_H) tree-alias-common.h \ + $(TM_H) coretypes.h $(TREE_DUMP_H) langhooks.h domwalk.h \ + tree-pass.h tree-ssa-live.h tree-ssa-dse.o : tree-ssa-dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) errors.h $(GGC_H) $(TREE_H) $(RTL_H) $(TM_P_H) $(BASIC_BLOCK_H) \ $(TREE_FLOW_H) tree-pass.h $(TREE_DUMP_H) domwalk.h flags.h @@ -1569,7 +1579,8 @@ tree-ssa-phiopt.o : tree-ssa-phiopt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TREE_FLOW_H) tree-pass.h $(TREE_DUMP_H) langhooks.h tree-nrv.o : tree-nrv.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(TREE_H) $(RTL_H) function.h $(BASIC_BLOCK_H) $(EXPR_H) \ - diagnostic.h $(TREE_FLOW_H) $(TIMEVAR_H) $(TREE_DUMP_H) tree-pass.h langhooks.h + diagnostic.h $(TREE_FLOW_H) $(TIMEVAR_H) $(TREE_DUMP_H) tree-pass.h \ + langhooks.h tree-ssa-copy.o : tree-ssa-copy.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \ $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) output.h diagnostic.h \ errors.h function.h $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) \ @@ -2265,7 +2276,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/coretypes.h $(srcdir)/cpplib.h \ $(srcdir)/c-objc-common.c $(srcdir)/c-common.c $(srcdir)/c-parse.in \ $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c \ $(srcdir)/tree-phinodes.c $(srcdir)/tree-cfg.c \ - $(srcdir)/tree-ssa.c $(srcdir)/tree-dfa.c $(srcdir)/tree-ssa-ccp.c \ + $(srcdir)/tree-dfa.c $(srcdir)/tree-ssa-ccp.c \ $(srcdir)/tree-iterator.c $(srcdir)/gimplify.c \ $(srcdir)/tree-alias-type.h $(srcdir)/tree-alias-common.h \ $(srcdir)/tree-alias-type.c $(srcdir)/tree-alias-common.c \ @@ -2288,7 +2299,7 @@ gt-dwarf2out.h gt-ra-build.h gt-reg-stack.h gt-dwarf2asm.h \ gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parse.h \ gt-c-pragma.h gtype-c.h gt-input.h gt-cfglayout.h \ gt-tree-alias-common.h gt-tree-mudflap.h \ -gt-tree-ssa.h gt-tree-ssa-ccp.h gt-tree-eh.h \ +gt-tree-ssa-ccp.h gt-tree-eh.h \ gt-tree-ssanames.h gt-tree-iterator.h gt-gimplify.h \ gt-tree-phinodes.h gt-tree-cfg.h gt-tree-nested.h \ gt-stringpool.h gt-langhooks.h : s-gtype ; @true diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index 95eb1f5417f..61eeff517aa 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -535,7 +535,6 @@ typedef bool (*walk_use_def_chains_fn) (tree, tree, void *); /* In tree-ssa.c */ extern void init_tree_ssa (void); -extern void rewrite_into_ssa (void); extern void rewrite_vars_out_of_ssa (bitmap); extern void dump_reaching_defs (FILE *); extern void debug_reaching_defs (void); @@ -554,6 +553,9 @@ extern void delete_tree_ssa (void); extern void register_new_def (tree, tree, varray_type *, varray_type); extern void walk_use_def_chains (tree, walk_use_def_chains_fn, void *); +/* In tree-into-ssa.c */ +extern void rewrite_into_ssa (void); + extern unsigned int highest_ssa_version; /* In tree-ssa-pre.c */ diff --git a/gcc/tree-into-ssa.c b/gcc/tree-into-ssa.c new file mode 100644 index 00000000000..d8949de2b2a --- /dev/null +++ b/gcc/tree-into-ssa.c @@ -0,0 +1,1167 @@ +/* Rewrite a program in Normal form into SSA. + 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 "tree.h" +#include "flags.h" +#include "rtl.h" +#include "tm_p.h" +#include "langhooks.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "output.h" +#include "errors.h" +#include "expr.h" +#include "function.h" +#include "diagnostic.h" +#include "bitmap.h" +#include "tree-flow.h" +#include "tree-simple.h" +#include "tree-inline.h" +#include "varray.h" +#include "timevar.h" +#include "tree-alias-common.h" +#include "hashtab.h" +#include "tree-dump.h" +#include "tree-pass.h" +#include "cfgloop.h" +#include "domwalk.h" + +/* This file builds the SSA form for a function as described in: + R. Cytron, J. Ferrante, B. Rosen, M. Wegman, and K. Zadeck. Efficiently + Computing Static Single Assignment Form and the Control Dependence + Graph. ACM Transactions on Programming Languages and Systems, + 13(4):451-490, October 1991. */ + + +/* Structure to map a variable VAR to the set of blocks that contain + definitions for VAR. */ +struct def_blocks_d +{ + /* The variable. */ + tree var; + + /* Blocks that contain definitions of VAR. Bit I will be set if the + Ith block contains a definition of VAR. */ + bitmap def_blocks; + + /* Blocks where VAR is live-on-entry. Similar semantics as + DEF_BLOCKS. */ + bitmap livein_blocks; +}; + +/* Each entry in DEF_BLOCKS contains an element of type STRUCT + DEF_BLOCKS_D, mapping a variable VAR to a bitmap describing all the + basic blocks where VAR is defined (assigned a new value). It also + contains a bitmap of all the blocks where VAR is live-on-entry + (i.e., there is a use of VAR in block B without a preceding + definition in B). The live-on-entry information is used when + computing PHI pruning heuristics. */ +static htab_t def_blocks; + +/* Global data to attach to the main dominator walk structure. */ +struct mark_def_sites_global_data +{ + /* This sbitmap contains the variables which are set before they + are used in a basic block. We keep it as a global variable + solely to avoid the overhead of allocating and deallocating + the bitmap. */ + sbitmap kills; +}; + +/* Table to store the current reaching definition for every variable in + the function. Given a variable V, its entry will be its immediately + reaching SSA_NAME node. */ +static varray_type currdefs; + +struct rewrite_block_data +{ + varray_type block_defs; +}; + + +/* Local functions. */ +static void rewrite_finalize_block (struct dom_walk_data *, basic_block); +static void rewrite_initialize_block_local_data (struct dom_walk_data *, + basic_block, bool); +static void rewrite_initialize_block (struct dom_walk_data *, basic_block); +static void rewrite_add_phi_arguments (struct dom_walk_data *, basic_block); +static void mark_def_sites (struct dom_walk_data *walk_data, + basic_block bb, block_stmt_iterator); +static void mark_def_sites_initialize_block (struct dom_walk_data *walk_data, + basic_block bb); +static void compute_global_livein (bitmap, bitmap); +static void set_def_block (tree, basic_block); +static void set_livein_block (tree, basic_block); +static bool prepare_operand_for_rename (tree *op_p, size_t *uid_p); +static void insert_phi_nodes (bitmap *); +static void rewrite_stmt (struct dom_walk_data *, basic_block, + block_stmt_iterator); +static inline void rewrite_operand (tree *); +static void insert_phi_nodes_for (tree, bitmap *); +static tree get_reaching_def (tree); +static tree get_value_for (tree, varray_type); +static void set_value_for (tree, tree, varray_type); +static hashval_t def_blocks_hash (const void *); +static int def_blocks_eq (const void *, const void *); +static void def_blocks_free (void *); +static int debug_def_blocks_r (void **, void *); +static inline struct def_blocks_d *get_def_blocks_for (tree); +static inline struct def_blocks_d *find_def_blocks_for (tree); +static void htab_statistics (FILE *, htab_t); + +/* Return the value associated with variable VAR in TABLE. */ + +static inline tree +get_value_for (tree var, varray_type table) +{ + return VARRAY_TREE (table, var_ann (var)->uid); +} + + +/* Associate VALUE to variable VAR in TABLE. */ + +static inline void +set_value_for (tree var, tree value, varray_type table) +{ + VARRAY_TREE (table, var_ann (var)->uid) = value; +} + + +/* Compute global livein information given the set of blockx where + an object is locally live at the start of the block (LIVEIN) + and the set of blocks where the object is defined (DEF_BLOCKS). + + Note: This routine augments the existing local livein information + to include global livein (i.e., it modifies the underlying bitmap + for LIVEIN). */ + +static void +compute_global_livein (bitmap livein, bitmap def_blocks) +{ + basic_block bb, *worklist, *tos; + + tos = worklist + = (basic_block *) xmalloc (sizeof (basic_block) * (last_basic_block + 1)); + + /* Initialize the worklist. */ + FOR_EACH_BB (bb) + { + if (bitmap_bit_p (livein, bb->index)) + *tos++ = bb; + } + + /* Iterate until the worklist is empty. */ + while (tos != worklist) + { + edge e; + + /* Pull a block off the worklist. */ + bb = *--tos; + + /* For each predecessor block. */ + for (e = bb->pred; e; e = e->pred_next) + { + basic_block pred = e->src; + int pred_index = pred->index; + + /* None of this is necessary for the entry block. */ + if (pred != ENTRY_BLOCK_PTR + && ! bitmap_bit_p (livein, pred_index) + && ! bitmap_bit_p (def_blocks, pred_index)) + { + *tos++ = pred; + bitmap_set_bit (livein, pred_index); + } + } + } + + free (worklist); +} + + +/* Block initialization routine for mark_def_sites. Clear the + KILLS bitmap at the start of each block. */ + +static void +mark_def_sites_initialize_block (struct dom_walk_data *walk_data, + basic_block bb ATTRIBUTE_UNUSED) +{ + struct mark_def_sites_global_data *gd = walk_data->global_data; + sbitmap kills = gd->kills; + + sbitmap_zero (kills); +} + + +/* Call back for walk_dominator_tree used to collect definition sites + for every variable in the function. For every statement S in block + BB: + + 1- Variables defined by S in DEF_OPS(S) are marked in the bitmap + WALK_DATA->GLOBAL_DATA->KILLS. + + 2- If S uses a variable VAR and there is no preceding kill of VAR, + then it is marked in marked in the LIVEIN_BLOCKS bitmap + associated with VAR. + + This information is used to determine which variables are live + across block boundaries to reduce the number of PHI nodes + we create. */ + +static void +mark_def_sites (struct dom_walk_data *walk_data, + basic_block bb, + block_stmt_iterator bsi) +{ + struct mark_def_sites_global_data *gd = walk_data->global_data; + sbitmap kills = gd->kills; + vdef_optype vdefs; + vuse_optype vuses; + def_optype defs; + use_optype uses; + size_t i, uid; + tree stmt; + stmt_ann_t ann; + + /* Mark all the blocks that have definitions for each variable in the + VARS_TO_RENAME bitmap. */ + stmt = bsi_stmt (bsi); + get_stmt_operands (stmt); + ann = stmt_ann (stmt); + + /* If a variable is used before being set, then the variable is live + across a block boundary, so mark it live-on-entry to BB. */ + uses = USE_OPS (ann); + for (i = 0; i < NUM_USES (uses); i++) + { + tree *use_p = USE_OP_PTR (uses, i); + + if (prepare_operand_for_rename (use_p, &uid) + && !TEST_BIT (kills, uid)) + set_livein_block (*use_p, bb); + } + + /* Similarly for virtual uses. */ + vuses = VUSE_OPS (ann); + for (i = 0; i < NUM_VUSES (vuses); i++) + { + tree *use_p = VUSE_OP_PTR (vuses, i); + + if (prepare_operand_for_rename (use_p, &uid) + && !TEST_BIT (kills, uid)) + set_livein_block (*use_p, bb); + } + + /* Note that virtual definitions are irrelevant for computing KILLS + because a VDEF does not constitute a killing definition of the + variable. However, the operand of a virtual definitions is a use + of the variable, so it may cause the variable to be considered + live-on-entry. */ + vdefs = VDEF_OPS (ann); + for (i = 0; i < NUM_VDEFS (vdefs); i++) + { + size_t dummy; + + if (prepare_operand_for_rename (VDEF_OP_PTR (vdefs, i), &uid) + && prepare_operand_for_rename (VDEF_RESULT_PTR (vdefs, i), &dummy)) + { + VDEF_RESULT (vdefs, i) = VDEF_OP (vdefs, i); + + if (!TEST_BIT (kills, uid)) + set_livein_block (VDEF_OP (vdefs, i), bb); + set_def_block (VDEF_RESULT (vdefs, i), bb); + } + } + + /* Now process the definition made by this statement. Mark the + variables in KILLS. */ + defs = DEF_OPS (ann); + for (i = 0; i < NUM_DEFS (defs); i++) + { + tree *def_p = DEF_OP_PTR (defs, i); + + if (prepare_operand_for_rename (def_p, &uid)) + { + set_def_block (*def_p, bb); + SET_BIT (kills, uid); + } + } +} + + +/* Mark block BB as the definition site for variable VAR. */ + +static void +set_def_block (tree var, basic_block bb) +{ + struct def_blocks_d *db_p; + enum need_phi_state state = var_ann (var)->need_phi_state; + + db_p = get_def_blocks_for (var); + + /* Set the bit corresponding to the block where VAR is defined. */ + bitmap_set_bit (db_p->def_blocks, bb->index); + + /* Keep track of whether or not we may need to insert phi nodes. + + If we are in the UNKNOWN state, then this is the first definition + of VAR. Additionally, we have not seen any uses of VAR yet, so + we do not need a phi node for this variable at this time (i.e., + transition to NEED_PHI_STATE_NO). + + If we are in any other state, then we either have multiple definitions + of this variable occurring in different blocks or we saw a use of the + variable which was not dominated by the block containing the + definition(s). In this case we may need a PHI node, so enter + state NEED_PHI_STATE_MAYBE. */ + if (state == NEED_PHI_STATE_UNKNOWN) + var_ann (var)->need_phi_state = NEED_PHI_STATE_NO; + else + var_ann (var)->need_phi_state = NEED_PHI_STATE_MAYBE; +} + + +/* Mark block BB as having VAR live at the entry to BB. */ + +static void +set_livein_block (tree var, basic_block bb) +{ + struct def_blocks_d *db_p; + enum need_phi_state state = var_ann (var)->need_phi_state; + + db_p = get_def_blocks_for (var); + + /* Set the bit corresponding to the block where VAR is live in. */ + bitmap_set_bit (db_p->livein_blocks, bb->index); + + /* Keep track of whether or not we may need to insert phi nodes. + + If we reach here in NEED_PHI_STATE_NO, see if this use is dominated + by the single block containing the definition(s) of this variable. If + it is, then we remain in NEED_PHI_STATE_NO, otherwise we transition to + NEED_PHI_STATE_MAYBE. */ + if (state == NEED_PHI_STATE_NO) + { + int def_block_index = bitmap_first_set_bit (db_p->def_blocks); + + if (def_block_index == -1 + || ! dominated_by_p (CDI_DOMINATORS, bb, + BASIC_BLOCK (def_block_index))) + var_ann (var)->need_phi_state = NEED_PHI_STATE_MAYBE; + } + else + var_ann (var)->need_phi_state = NEED_PHI_STATE_MAYBE; +} + + +/* If the operand pointed by OP_P needs to be renamed, strip away SSA_NAME + wrappers (if needed) and return true. The unique ID for the operand's + variable will be stored in *UID_P. */ + +static bool +prepare_operand_for_rename (tree *op_p, size_t *uid_p) +{ + tree var = (TREE_CODE (*op_p) != SSA_NAME) ? *op_p : SSA_NAME_VAR (*op_p); + *uid_p = var_ann (var)->uid; + + /* Ignore variables that don't need to be renamed. */ + if (vars_to_rename && !bitmap_bit_p (vars_to_rename, *uid_p)) + return false; + + /* The variable needs to be renamed. If it already had an + SSA_NAME, strip it off. This way, the SSA rename pass + doesn't need to deal with existing SSA names. */ + if (TREE_CODE (*op_p) == SSA_NAME) + { + if (default_def (SSA_NAME_VAR (*op_p)) != *op_p) + release_ssa_name (*op_p); + *op_p = var; + } + + return true; +} + + +/* Helper for insert_phi_nodes. If VAR needs PHI nodes, insert them + at the dominance frontier (DFS) of blocks defining VAR. */ + +static inline +void insert_phi_nodes_1 (tree var, bitmap *dfs) +{ + var_ann_t ann = var_ann (var); + if (ann->need_phi_state != NEED_PHI_STATE_NO) + insert_phi_nodes_for (var, dfs); +} + + +/* Insert PHI nodes at the dominance frontier of blocks with variable + definitions. DFS contains the dominance frontier information for + the flowgraph. PHI nodes will only be inserted at the dominance + frontier of definition blocks for variables whose NEED_PHI_STATE + annotation is marked as ``maybe'' or ``unknown'' (computed by + mark_def_sites). */ + +static void +insert_phi_nodes (bitmap *dfs) +{ + size_t i; + + timevar_push (TV_TREE_INSERT_PHI_NODES); + + /* Iterate over all variables in VARS_TO_RENAME. For each variable, add + to the work list all the blocks that have a definition for the + variable. PHI nodes will be added to the dominance frontier blocks of + each definition block. */ + if (vars_to_rename) + EXECUTE_IF_SET_IN_BITMAP (vars_to_rename, 0, i, + insert_phi_nodes_1 (referenced_var (i), dfs)); + else + for (i = 0; i < num_referenced_vars; i++) + insert_phi_nodes_1 (referenced_var (i), dfs); + + timevar_pop (TV_TREE_INSERT_PHI_NODES); +} + + +/* Perform a depth-first traversal of the dominator tree looking for + variables to rename. BB is the block where to start searching. + Renaming is a five step process: + + 1- Every definition made by PHI nodes at the start of the blocks is + registered as the current definition for the corresponding variable. + + 2- Every statement in BB is rewritten. USE and VUSE operands are + rewritten with their corresponding reaching definition. DEF and + VDEF targets are registered as new definitions. + + 3- All the PHI nodes in successor blocks of BB are visited. The + argument corresponding to BB is replaced with its current reaching + definition. + + 4- Recursively rewrite every dominator child block of BB. + + 5- Restore (in reverse order) the current reaching definition for every + new definition introduced in this block. This is done so that when + we return from the recursive call, all the current reaching + definitions are restored to the names that were valid in the + dominator parent of BB. */ + +/* Initialize the local stacks. + + BLOCK_DEFS is used to save all the existing reaching definitions for + the new SSA names introduced in this block. Before registering a + new definition for a variable, the existing reaching definition is + pushed into this stack so that we can restore it in Step 5. */ + +static void +rewrite_initialize_block_local_data (struct dom_walk_data *walk_data, + basic_block bb ATTRIBUTE_UNUSED, + bool recycled) +{ + struct rewrite_block_data *bd + = (struct rewrite_block_data *)VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack); + + /* We get cleared memory from the allocator, so if the memory is + not cleared, then we are re-using a previously allocated entry. In + that case, we can also re-use the underlying virtal arrays. Just + make sure we clear them before using them! */ + if (recycled && bd->block_defs) + VARRAY_CLEAR (bd->block_defs); +} + + +/* 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_initialize_block (struct dom_walk_data *walk_data, basic_block bb) +{ + tree phi; + struct rewrite_block_data *bd + = (struct rewrite_block_data *)VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\n\nRenaming block #%d\n\n", bb->index); + + /* 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 (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) + { + tree result = PHI_RESULT (phi); + + register_new_def (SSA_NAME_VAR (result), result, + &bd->block_defs, currdefs); + } +} + + +/* SSA Rewriting Step 3. Visit all the successor blocks of BB looking for + PHI nodes. For every PHI node found, add a new argument containing the + current reaching definition for the variable and the edge through which + that definition is reaching the PHI node. */ + +static void +rewrite_add_phi_arguments (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED, + basic_block bb) +{ + edge e; + + for (e = bb->succ; e; e = e->succ_next) + { + tree phi; + + for (phi = phi_nodes (e->dest); phi; phi = TREE_CHAIN (phi)) + { + tree currdef; + + /* If this PHI node has already been rewritten, then there is + nothing to do for this PHI or any following PHIs since we + always add new PHI nodes at the start of the PHI chain. */ + if (PHI_REWRITTEN (phi)) + break; + + currdef = get_reaching_def (SSA_NAME_VAR (PHI_RESULT (phi))); + add_phi_arg (&phi, currdef, e); + } + } +} + +/* SSA Rewriting Step 5. Restore the current reaching definition for each + variable referenced in the block (in reverse order). */ + +static void +rewrite_finalize_block (struct dom_walk_data *walk_data, + basic_block bb ATTRIBUTE_UNUSED) +{ + struct rewrite_block_data *bd + = (struct rewrite_block_data *)VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack); + + /* Step 5. Restore the current reaching definition for each variable + referenced in the block (in reverse order). */ + while (bd->block_defs && VARRAY_ACTIVE_SIZE (bd->block_defs) > 0) + { + tree var; + tree saved_def = VARRAY_TOP_TREE (bd->block_defs); + VARRAY_POP (bd->block_defs); + + /* If SAVED_DEF is NULL, then the next slot in the stack contains the + variable associated with SAVED_DEF. */ + if (saved_def == NULL_TREE) + { + var = VARRAY_TOP_TREE (bd->block_defs); + VARRAY_POP (bd->block_defs); + } + else + var = SSA_NAME_VAR (saved_def); + + set_value_for (var, saved_def, currdefs); + } +} + + +/* Dump SSA information to FILE. */ + +void +dump_tree_ssa (FILE *file) +{ + basic_block bb; + const char *funcname + = (*lang_hooks.decl_printable_name) (current_function_decl, 2); + + fprintf (file, "SSA information for %s\n\n", funcname); + + FOR_EACH_BB (bb) + { + dump_bb (bb, file, 0); + fputs (" ", file); + print_generic_stmt (file, phi_nodes (bb), dump_flags); + fputs ("\n\n", file); + } +} + + +/* Dump SSA information to stderr. */ + +void +debug_tree_ssa (void) +{ + dump_tree_ssa (stderr); +} + + +/* Dump SSA statistics on FILE. */ + +void +dump_tree_ssa_stats (FILE *file) +{ + fprintf (file, "\nHash table statistics:\n"); + + fprintf (file, " def_blocks: "); + htab_statistics (file, def_blocks); + + fprintf (file, "\n"); +} + + +/* Dump SSA statistics on stderr. */ + +void +debug_tree_ssa_stats (void) +{ + dump_tree_ssa_stats (stderr); +} + + +/* Dump statistics for the hash table HTAB. */ + +static void +htab_statistics (FILE *file, htab_t htab) +{ + fprintf (file, "size %ld, %ld elements, %f collision/search ratio\n", + (long) htab_size (htab), + (long) htab_elements (htab), + htab_collisions (htab)); +} + + +/* Insert PHI nodes for variable VAR using the dominance frontier + information given in DFS. */ + +static void +insert_phi_nodes_for (tree var, bitmap *dfs) +{ + struct def_blocks_d *def_map; + bitmap phi_insertion_points; + int bb_index; + varray_type work_stack; + + def_map = find_def_blocks_for (var); + if (def_map == NULL) + return; + + /* Array WORK_STACK is a stack of CFG blocks. Each block that contains + an assignment or PHI node will be pushed to this stack. */ + VARRAY_BB_INIT (work_stack, last_basic_block, "work_stack"); + + phi_insertion_points = BITMAP_XMALLOC (); + + EXECUTE_IF_SET_IN_BITMAP (def_map->def_blocks, 0, bb_index, + { + VARRAY_PUSH_BB (work_stack, BASIC_BLOCK (bb_index)); + }); + + /* Pop a block off the worklist, add every block that appears in + the original block's dfs that we have not already processed to + the worklist. Iterate until the worklist is empty. Blocks + which are added to the worklist are potential sites for + PHI nodes. + + The iteration step could be done during PHI insertion just as + easily. We do it here for historical reasons -- we used to have + a heuristic which used the potential PHI insertion points to + determine if fully pruned or semi pruned SSA form was appropriate. + + We now always use fully pruned SSA form. */ + while (VARRAY_ACTIVE_SIZE (work_stack) > 0) + { + basic_block bb = VARRAY_TOP_BB (work_stack); + int bb_index = bb->index; + int dfs_index; + + VARRAY_POP (work_stack); + + EXECUTE_IF_AND_COMPL_IN_BITMAP (dfs[bb_index], + phi_insertion_points, + 0, dfs_index, + { + basic_block bb = BASIC_BLOCK (dfs_index); + + VARRAY_PUSH_BB (work_stack, bb); + bitmap_set_bit (phi_insertion_points, dfs_index); + }); + } + + /* Now compute global livein for this variable. Note this modifies + def_map->livein_blocks. */ + compute_global_livein (def_map->livein_blocks, def_map->def_blocks); + + /* And insert the PHI nodes. */ + EXECUTE_IF_AND_IN_BITMAP (phi_insertion_points, def_map->livein_blocks, + 0, bb_index, + { + create_phi_node (var, BASIC_BLOCK (bb_index)); + }); + + BITMAP_FREE (phi_insertion_points); +} + +/* SSA Rewriting 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. */ + +static void +rewrite_stmt (struct dom_walk_data *walk_data, + basic_block bb ATTRIBUTE_UNUSED, + block_stmt_iterator si) +{ + size_t i; + stmt_ann_t ann; + tree stmt; + vuse_optype vuses; + vdef_optype vdefs; + def_optype defs; + use_optype uses; + struct rewrite_block_data *bd; + + bd = VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack); + + stmt = bsi_stmt (si); + ann = stmt_ann (stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Renaming statement "); + print_generic_stmt (dump_file, stmt, TDF_SLIM); + fprintf (dump_file, "\n"); + } + +#if defined ENABLE_CHECKING + /* We have just scanned the code for operands. No statement should + be modified. */ + if (ann->modified) + abort (); +#endif + + defs = DEF_OPS (ann); + uses = USE_OPS (ann); + vuses = VUSE_OPS (ann); + vdefs = VDEF_OPS (ann); + + /* Step 1. Rewrite USES and VUSES in the statement. */ + for (i = 0; i < NUM_USES (uses); i++) + rewrite_operand (USE_OP_PTR (uses, i)); + + /* Rewrite virtual uses in the statement. */ + for (i = 0; i < NUM_VUSES (vuses); i++) + rewrite_operand (VUSE_OP_PTR (vuses, i)); + + /* Step 2. Register the statement's DEF and VDEF operands. */ + for (i = 0; i < NUM_DEFS (defs); i++) + { + tree *def_p = DEF_OP_PTR (defs, i); + + if (TREE_CODE (*def_p) != SSA_NAME) + *def_p = make_ssa_name (*def_p, stmt); + + /* FIXME: We shouldn't be registering new defs if the variable + doesn't need to be renamed. */ + register_new_def (SSA_NAME_VAR (*def_p), *def_p, + &bd->block_defs, currdefs); + } + + /* Register new virtual definitions made by the statement. */ + for (i = 0; i < NUM_VDEFS (vdefs); i++) + { + rewrite_operand (VDEF_OP_PTR (vdefs, i)); + + if (TREE_CODE (VDEF_RESULT (vdefs, i)) != SSA_NAME) + *VDEF_RESULT_PTR (vdefs, i) + = make_ssa_name (VDEF_RESULT (vdefs, i), stmt); + + /* FIXME: We shouldn't be registering new defs if the variable + doesn't need to be renamed. */ + register_new_def (SSA_NAME_VAR (VDEF_RESULT (vdefs, i)), + VDEF_RESULT (vdefs, i), &bd->block_defs, currdefs); + } +} + + +/* Replace the operand pointed by OP_P with its immediate reaching + definition. */ + +static inline void +rewrite_operand (tree *op_p) +{ + if (TREE_CODE (*op_p) != SSA_NAME) + *op_p = get_reaching_def (*op_p); +} + + +/* Register DEF to be a new definition for variable VAR and push VAR's + current reaching definition into the stack pointed by BLOCK_DEFS_P. + IS_REAL_OPERAND is true when DEF is a real definition. */ + +void +register_new_def (tree var, tree def, + varray_type *block_defs_p, varray_type table) +{ + tree currdef = get_value_for (var, table); + + if (! *block_defs_p) + VARRAY_TREE_INIT (*block_defs_p, 20, "block_defs"); + + /* If the current reaching definition is NULL, push the variable itself + so that the dominator tree callbacks know what variable is associated + with this NULL reaching def when unwinding the *BLOCK_DEFS_P stack. */ + if (currdef == NULL_TREE) + VARRAY_PUSH_TREE (*block_defs_p, var); + + /* Push the current reaching definition into *BLOCK_DEFS_P. This stack is + later used by the dominator tree callbacks to restore the reaching + definitions for all the variables defined in the block after a recursive + visit to all its immediately dominated blocks. */ + VARRAY_PUSH_TREE (*block_defs_p, currdef); + + /* Set the current reaching definition for VAR to be DEF. */ + set_value_for (var, def, table); +} + + +/* Return the current definition for variable VAR. If none is found, + create a new SSA name to act as the zeroth definition for VAR. If VAR + is call clobbered and there exists a more recent definition of + GLOBAL_VAR, return the definition for GLOBAL_VAR. This means that VAR + has been clobbered by a function call since its last assignment. */ + +static tree +get_reaching_def (tree var) +{ + tree default_d, currdef_var; + + /* Lookup the current reaching definition for VAR. */ + default_d = NULL_TREE; + currdef_var = get_value_for (var, currdefs); + + /* If there is no reaching definition for VAR, create and register a + default definition for it (if needed). */ + if (currdef_var == NULL_TREE) + { + default_d = default_def (var); + if (default_d == NULL_TREE) + { + default_d = make_ssa_name (var, build_empty_stmt ()); + set_default_def (var, default_d); + } + set_value_for (var, default_d, currdefs); + } + + /* Return the current reaching definition for VAR, or the default + definition, if we had to create one. */ + return (currdef_var) ? currdef_var : default_d; +} + + +/* Hashing and equality functions for DEF_BLOCKS. */ + +static hashval_t +def_blocks_hash (const void *p) +{ + return htab_hash_pointer + ((const void *)((const struct def_blocks_d *)p)->var); +} + +static int +def_blocks_eq (const void *p1, const void *p2) +{ + return ((const struct def_blocks_d *)p1)->var + == ((const struct def_blocks_d *)p2)->var; +} + +/* Free memory allocated by one entry in DEF_BLOCKS. */ + +static void +def_blocks_free (void *p) +{ + struct def_blocks_d *entry = p; + BITMAP_FREE (entry->def_blocks); + BITMAP_FREE (entry->livein_blocks); + free (entry); +} + + +/* Dump the DEF_BLOCKS hash table on stderr. */ + +void +debug_def_blocks (void) +{ + htab_traverse (def_blocks, debug_def_blocks_r, NULL); +} + +/* Callback for htab_traverse to dump the DEF_BLOCKS hash table. */ + +static int +debug_def_blocks_r (void **slot, void *data ATTRIBUTE_UNUSED) +{ + unsigned long i; + struct def_blocks_d *db_p = (struct def_blocks_d *) *slot; + + fprintf (stderr, "VAR: "); + print_generic_expr (stderr, db_p->var, dump_flags); + fprintf (stderr, ", DEF_BLOCKS: { "); + EXECUTE_IF_SET_IN_BITMAP (db_p->def_blocks, 0, i, + fprintf (stderr, "%ld ", i)); + fprintf (stderr, "}"); + fprintf (stderr, ", LIVEIN_BLOCKS: { "); + EXECUTE_IF_SET_IN_BITMAP (db_p->livein_blocks, 0, i, + fprintf (stderr, "%ld ", i)); + fprintf (stderr, "}\n"); + + return 1; +} + + +/* Return the set of blocks where variable VAR is defined and the blocks + where VAR is live on entry (livein). Return NULL, if no entry is + found in DEF_BLOCKS. */ + +static inline struct def_blocks_d * +find_def_blocks_for (tree var) +{ + struct def_blocks_d dm; + dm.var = var; + return (struct def_blocks_d *) htab_find (def_blocks, &dm); +} + + +/* Return the set of blocks where variable VAR is defined and the blocks + 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 (tree var) +{ + struct def_blocks_d db, *db_p; + void **slot; + + db.var = var; + slot = htab_find_slot (def_blocks, (void *) &db, INSERT); + if (*slot == NULL) + { + db_p = xmalloc (sizeof (*db_p)); + db_p->var = var; + db_p->def_blocks = BITMAP_XMALLOC (); + db_p->livein_blocks = BITMAP_XMALLOC (); + *slot = (void *) db_p; + } + else + db_p = (struct def_blocks_d *) *slot; + + return db_p; +} + + +/* Main entry point into the SSA builder. The renaming process + proceeds in five main phases: + + 1- If VARS_TO_RENAME has any entries, any existing PHI nodes for + those variables are removed from the flow graph so that they can + be computed again. + + 2- Compute dominance frontier and immediate dominators, needed to + insert PHI nodes and rename the function in dominator tree + order. + + 3- Find and mark all the blocks that define variables + (mark_def_sites). + + 4- Insert PHI nodes at dominance frontiers (insert_phi_nodes). + + 5- Rename all the blocks (rewrite_initialize_block, + rewrite_add_phi_arguments) and statements in the program + (rewrite_stmt). + + Steps 3 and 5 are done using the dominator tree walker + (walk_dominator_tree). */ + +void +rewrite_into_ssa (void) +{ + bitmap *dfs; + basic_block bb; + struct dom_walk_data walk_data; + struct mark_def_sites_global_data mark_def_sites_global_data; + + timevar_push (TV_TREE_SSA_OTHER); + + /* Initialize the array of variables to rename. */ + if (vars_to_rename != NULL) + { + size_t i; + bool rename_name_tags_p; + + /* If any of the variables in VARS_TO_RENAME is a pointer, we need to + invalidate all the name memory tags associated with the variables + that we are about to rename. FIXME: Currently we just invalidate + *all* the NMTs. Make this more selective. */ + rename_name_tags_p = false; + EXECUTE_IF_SET_IN_BITMAP (vars_to_rename, 0, i, + if (POINTER_TYPE_P (TREE_TYPE (referenced_var (i)))) + { + rename_name_tags_p = true; + break; + }); + + if (rename_name_tags_p) + for (i = 0; i < num_referenced_vars; i++) + { + var_ann_t ann = var_ann (referenced_var (i)); + + if (ann->mem_tag_kind == NAME_TAG) + bitmap_set_bit (vars_to_rename, ann->uid); + } + + /* Now remove all the existing PHI nodes (if any) for the variables + that we are about to rename into SSA. */ + remove_all_phi_nodes_for (vars_to_rename); + } + + /* Allocate memory for the DEF_BLOCKS hash table. */ + def_blocks = htab_create (VARRAY_ACTIVE_SIZE (referenced_vars), + def_blocks_hash, def_blocks_eq, def_blocks_free); + + VARRAY_TREE_INIT (currdefs, num_referenced_vars, "currdefs"); + + /* Initialize dominance frontier and immediate dominator bitmaps. + Also count the number of predecessors for each block. Doing so + can save significant time during PHI insertion for large graphs. */ + dfs = (bitmap *) xmalloc (last_basic_block * sizeof (bitmap *)); + FOR_EACH_BB (bb) + { + edge e; + int count = 0; + + for (e = bb->pred; e; e = e->pred_next) + count++; + + bb_ann (bb)->num_preds = count; + dfs[bb->index] = BITMAP_XMALLOC (); + } + + /* Ensure that the dominance information is OK. */ + calculate_dominance_info (CDI_DOMINATORS); + + /* Compute dominance frontiers. */ + compute_dominance_frontiers (dfs); + + /* Setup callbacks for the generic dominator tree walker to find and + mark definition sites. */ + walk_data.walk_stmts_backward = false; + walk_data.dom_direction = CDI_DOMINATORS; + walk_data.initialize_block_local_data = NULL; + walk_data.before_dom_children_before_stmts = mark_def_sites_initialize_block; + walk_data.before_dom_children_walk_stmts = mark_def_sites; + walk_data.before_dom_children_after_stmts = NULL; + walk_data.after_dom_children_before_stmts = NULL; + walk_data.after_dom_children_walk_stmts = NULL; + walk_data.after_dom_children_after_stmts = NULL; + + /* 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 = sbitmap_alloc (num_referenced_vars); + walk_data.global_data = &mark_def_sites_global_data; + + /* We do not have any local data. */ + walk_data.block_local_data_size = 0; + + /* Initialize the dominator walker. */ + init_walk_dominator_tree (&walk_data); + + /* Recursively walk the dominator tree. */ + walk_dominator_tree (&walk_data, ENTRY_BLOCK_PTR); + + /* Finalize the dominator walker. */ + fini_walk_dominator_tree (&walk_data); + + /* We no longer need this bitmap, clear and free it. */ + sbitmap_free (mark_def_sites_global_data.kills); + + /* Insert PHI nodes at dominance frontiers of definition blocks. */ + insert_phi_nodes (dfs); + + /* Rewrite all the basic blocks in the program. */ + timevar_push (TV_TREE_SSA_REWRITE_BLOCKS); + + /* Setup callbacks for the generic dominator tree walker. */ + walk_data.walk_stmts_backward = false; + walk_data.dom_direction = CDI_DOMINATORS; + walk_data.initialize_block_local_data = rewrite_initialize_block_local_data; + walk_data.before_dom_children_before_stmts = rewrite_initialize_block; + walk_data.before_dom_children_walk_stmts = rewrite_stmt; + walk_data.before_dom_children_after_stmts = rewrite_add_phi_arguments; + walk_data.after_dom_children_before_stmts = NULL; + walk_data.after_dom_children_walk_stmts = NULL; + walk_data.after_dom_children_after_stmts = rewrite_finalize_block; + walk_data.global_data = NULL; + walk_data.block_local_data_size = sizeof (struct rewrite_block_data); + + /* 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_BLOCK_PTR); + + /* Finalize the dominator walker. */ + fini_walk_dominator_tree (&walk_data); + + timevar_pop (TV_TREE_SSA_REWRITE_BLOCKS); + + /* Debugging dumps. */ + if (dump_file && (dump_flags & TDF_STATS)) + { + dump_dfa_stats (dump_file); + dump_tree_ssa_stats (dump_file); + } + + /* Free allocated memory. */ + FOR_EACH_BB (bb) + BITMAP_XFREE (dfs[bb->index]); + free (dfs); + + htab_delete (def_blocks); + VARRAY_CLEAR (currdefs); + + timevar_pop (TV_TREE_SSA_OTHER); +} + +struct tree_opt_pass pass_build_ssa = +{ + "ssa", /* name */ + NULL, /* gate */ + rewrite_into_ssa, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + PROP_cfg | PROP_referenced_vars, /* properties_required */ + PROP_ssa, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func | TODO_verify_ssa /* todo_flags_finish */ +}; diff --git a/gcc/tree-outof-ssa.c b/gcc/tree-outof-ssa.c new file mode 100644 index 00000000000..dc77fe2fbbb --- /dev/null +++ b/gcc/tree-outof-ssa.c @@ -0,0 +1,2025 @@ +/* Convert a program in SSA form into Normal form. + Copyright (C) 2004 Free Software Foundation, Inc. + Contributed by Andrew Macleod <amacleod@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 "tree.h" +#include "flags.h" +#include "rtl.h" +#include "tm_p.h" +#include "ggc.h" +#include "langhooks.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "output.h" +#include "errors.h" +#include "expr.h" +#include "function.h" +#include "diagnostic.h" +#include "bitmap.h" +#include "tree-flow.h" +#include "tree-simple.h" +#include "tree-inline.h" +#include "varray.h" +#include "timevar.h" +#include "tree-alias-common.h" +#include "hashtab.h" +#include "tree-dump.h" +#include "tree-ssa-live.h" +#include "tree-pass.h" + +/* Used to hold all the components required to do SSA PHI elimination. + The node and pred/succ list is a simple linear list of nodes and + edges represented as pairs of nodes. + + The predecessor and successor list: Nodes are entered in pairs, where + [0] ->PRED, [1]->SUCC. All the even indexes in the array represent + predecessors, all the odd elements are successors. + + Rationale: + When implemented as bitmaps, very large programs SSA->Normal times were + being dominated by clearing the interference graph. + + Typically this list of edges is extremely small since it only includes + PHI results and uses from a single edge which have not coalesced with + each other. This means that no virtual PHI nodes are included, and + empirical evidence suggests that the number of edges rarely exceed + 3, and in a bootstrap of GCC, the maximum size encountered was 7. + This also limits the number of possible nodes that are involved to + rarely more than 6, and in the bootstrap of gcc, the maximum number + of nodes encountered was 12. */ + +typedef struct _elim_graph { + /* Size of the elimination vectors. */ + int size; + + /* List of nodes in the elimination graph. */ + varray_type nodes; + + /* The predecessor and successor edge list. */ + varray_type edge_list; + + /* Visited vector. */ + sbitmap visited; + + /* Stack for visited nodes. */ + varray_type stack; + + /* The variable partition map. */ + var_map map; + + /* Edge being eliminated by this graph. */ + edge e; + + /* List of constant copies to emit. These are pushed on in pairs. */ + varray_type const_copies; +} *elim_graph; + + +/* Local functions. */ +static tree create_temp (tree); +static void insert_copy_on_edge (edge, tree, tree); +static elim_graph new_elim_graph (int); +static inline void delete_elim_graph (elim_graph); +static inline void clear_elim_graph (elim_graph); +static inline int elim_graph_size (elim_graph); +static inline void elim_graph_add_node (elim_graph, tree); +static inline void elim_graph_add_edge (elim_graph, int, int); +static inline int elim_graph_remove_succ_edge (elim_graph, int); + +static inline void eliminate_name (elim_graph, tree); +static void eliminate_build (elim_graph, basic_block, int); +static void elim_forward (elim_graph, int); +static int elim_unvisited_predecessor (elim_graph, int); +static void elim_backward (elim_graph, int); +static void elim_create (elim_graph, int); +static void eliminate_phi (edge, int, elim_graph); +static tree_live_info_p coalesce_ssa_name (var_map, int); +static void assign_vars (var_map); +static bool replace_variable (var_map, tree *, tree *); +static void eliminate_virtual_phis (void); +static void coalesce_abnormal_edges (var_map, conflict_graph, root_var_p); +static void print_exprs (FILE *, const char *, tree, const char *, tree, + const char *); +static void print_exprs_edge (FILE *, edge, const char *, tree, const char *, + tree); + + +/* Create a temporary for a partition based on the type of variable T, + which already represents a partition. */ + +static tree +create_temp (tree t) +{ + tree tmp; + const char *name = NULL; + tree type; + + if (TREE_CODE (t) == SSA_NAME) + t = SSA_NAME_VAR (t); + + if (TREE_CODE (t) != VAR_DECL + && TREE_CODE (t) != PARM_DECL) + abort (); + + type = TREE_TYPE (t); + tmp = DECL_NAME (t); + if (tmp) + name = IDENTIFIER_POINTER (tmp); + + if (name == NULL) + name = "temp"; + tmp = create_tmp_var (type, name); + DECL_ARTIFICIAL (tmp) = DECL_ARTIFICIAL (t); + add_referenced_tmp_var (tmp); + + /* add_referenced_tmp_var will create the annotation and set up some + of the flags in the annotation. However, some flags we need to + inherit from our original variable. */ + var_ann (tmp)->type_mem_tag = var_ann (t)->type_mem_tag; + if (is_call_clobbered (t)) + mark_call_clobbered (tmp); + + return tmp; +} + + +/* This helper function fill insert a copy from a constant or a variable to + a variable on the specified edge. */ + +static void +insert_copy_on_edge (edge e, tree dest, tree src) +{ + tree copy; + + copy = build (MODIFY_EXPR, TREE_TYPE (dest), dest, src); + set_is_used (dest); + + if (TREE_CODE (src) == ADDR_EXPR) + src = TREE_OPERAND (src, 0); + if (TREE_CODE (src) == VAR_DECL || TREE_CODE (src) == PARM_DECL) + set_is_used (src); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, + "Inserting a copy on edge BB%d->BB%d :", + e->src->index, + e->dest->index); + print_generic_expr (dump_file, copy, dump_flags); + fprintf (dump_file, "\n"); + } + + bsi_insert_on_edge (e, copy); +} + +/* --------------------------------------------------------------------- */ +/* Create an elimination graph and associated data structures. */ + +static elim_graph +new_elim_graph (int size) +{ + elim_graph g = (elim_graph) xmalloc (sizeof (struct _elim_graph)); + + VARRAY_TREE_INIT (g->nodes, 30, "Elimination Node List"); + VARRAY_TREE_INIT (g->const_copies, 20, "Elimination Constant Copies"); + VARRAY_INT_INIT (g->edge_list, 20, "Elimination Edge List"); + VARRAY_INT_INIT (g->stack, 30, " Elimination Stack"); + + g->visited = sbitmap_alloc (size); + + return g; +} + + +/* Empty the elimination graph. */ +static inline void +clear_elim_graph (elim_graph g) +{ + VARRAY_POP_ALL (g->nodes); + VARRAY_POP_ALL (g->edge_list); +} + + +/* Delete an elimination graph. */ +static inline void +delete_elim_graph (elim_graph g) +{ + sbitmap_free (g->visited); + free (g); +} + + +/* Return the number of nodes in the graph. */ +static inline int +elim_graph_size (elim_graph g) +{ + return VARRAY_ACTIVE_SIZE (g->nodes); +} + + +/* Add a node to the graph, if it doesn't exist already. */ +static inline void +elim_graph_add_node (elim_graph g, tree node) +{ + int x; + for (x = 0; x < elim_graph_size (g); x++) + if (VARRAY_TREE (g->nodes, x) == node) + return; + VARRAY_PUSH_TREE (g->nodes, node); +} + + +/* Add an edge to the graph. */ +static inline void +elim_graph_add_edge (elim_graph g, int pred, int succ) +{ + VARRAY_PUSH_INT (g->edge_list, pred); + VARRAY_PUSH_INT (g->edge_list, succ); +} + + +/* Remove an edge from the graph for which node is the predecessor, and + return the successor node. -1 is returned if there is no such edge. */ +static inline int +elim_graph_remove_succ_edge (elim_graph g, int node) +{ + int y; + unsigned x; + for (x = 0; x < VARRAY_ACTIVE_SIZE (g->edge_list); x += 2) + if (VARRAY_INT (g->edge_list, x) == node) + { + VARRAY_INT (g->edge_list, x) = -1; + y = VARRAY_INT (g->edge_list, x + 1); + VARRAY_INT (g->edge_list, x + 1) = -1; + return y; + } + return -1; +} + +/* Find all the nodes which are successors to NODE in the edge list. */ +#define FOR_EACH_ELIM_GRAPH_SUCC(GRAPH, NODE, VAR, CODE) \ +do { \ + unsigned x_; \ + int y_; \ + for (x_ = 0; x_ < VARRAY_ACTIVE_SIZE ((GRAPH)->edge_list); x_ += 2) \ + { \ + y_ = VARRAY_INT ((GRAPH)->edge_list, x_); \ + if (y_ != (NODE)) \ + continue; \ + (VAR) = VARRAY_INT ((GRAPH)->edge_list, x_ + 1); \ + CODE; \ + } \ +} while (0) + +/* Find all the nodes which are predecessors of NODE in the edge list. */ +#define FOR_EACH_ELIM_GRAPH_PRED(GRAPH, NODE, VAR, CODE) \ +do { \ + unsigned x_; \ + int y_; \ + for (x_ = 0; x_ < VARRAY_ACTIVE_SIZE ((GRAPH)->edge_list); x_ += 2) \ + { \ + y_ = VARRAY_INT ((GRAPH)->edge_list, x_ + 1); \ + if (y_ != (NODE)) \ + continue; \ + (VAR) = VARRAY_INT ((GRAPH)->edge_list, x_); \ + CODE; \ + } \ +} while (0) + + +/* Add T to the elimination graph. */ + +static inline void +eliminate_name (elim_graph g, tree T) +{ + elim_graph_add_node (g, T); +} + +/* Build the auxiliary graph. */ + +static void +eliminate_build (elim_graph g, basic_block B, int i) +{ + tree phi; + tree T0, Ti; + int p0, pi; + + clear_elim_graph (g); + + for (phi = phi_nodes (B); phi; phi = TREE_CHAIN (phi)) + { + T0 = var_to_partition_to_var (g->map, PHI_RESULT (phi)); + /* Ignore results which are not in partitions. */ + if (T0 == NULL_TREE) + { +#ifdef ENABLE_CHECKING + /* There should be no arguments of this PHI which are in + the partition list, or we get incorrect results. */ + for (pi = 0; pi < PHI_NUM_ARGS (phi); pi++) + { + tree arg = PHI_ARG_DEF (phi, pi); + if (TREE_CODE (arg) == SSA_NAME + && var_to_partition (g->map, arg) != NO_PARTITION) + { + fprintf (stderr, "Argument of PHI is in a partition :("); + print_generic_expr (stderr, arg, TDF_SLIM); + fprintf (stderr, "), but the result is not :"); + print_generic_stmt (stderr, phi, TDF_SLIM); + abort(); + } + } +#endif + continue; + } + if (PHI_ARG_EDGE (phi, i) == g->e) + Ti = PHI_ARG_DEF (phi, i); + else + { + /* On rare occasions, a PHI node may not have the arguments + in the same order as all of the other PHI nodes. If they don't + match, find the appropriate index here. */ + pi = phi_arg_from_edge (phi, g->e); + if (pi == -1) + abort(); + Ti = PHI_ARG_DEF (phi, pi); + } + + /* If this argument is a constant, or a SSA_NAME which is being + left in SSA form, just queue a copy to be emitted on this + edge. */ + if (!phi_ssa_name_p (Ti) + || (TREE_CODE (Ti) == SSA_NAME + && var_to_partition (g->map, Ti) == NO_PARTITION)) + { + /* Save constant copies until all other copies have been emitted + on this edge. */ + VARRAY_PUSH_TREE (g->const_copies, T0); + VARRAY_PUSH_TREE (g->const_copies, Ti); + } + else + { + Ti = var_to_partition_to_var (g->map, Ti); + if (T0 != Ti) + { + eliminate_name (g, T0); + eliminate_name (g, Ti); + p0 = var_to_partition (g->map, T0); + pi = var_to_partition (g->map, Ti); + elim_graph_add_edge (g, p0, pi); + } + } + } +} + +/* Push successors onto the stack depth first. */ + +static void +elim_forward (elim_graph g, int T) +{ + int S; + SET_BIT (g->visited, T); + FOR_EACH_ELIM_GRAPH_SUCC (g, T, S, + { + if (!TEST_BIT (g->visited, S)) + elim_forward (g, S); + }); + VARRAY_PUSH_INT (g->stack, T); +} + + +/* Are there unvisited predecessors? */ + +static int +elim_unvisited_predecessor (elim_graph g, int T) +{ + int P; + FOR_EACH_ELIM_GRAPH_PRED (g, T, P, + { + if (!TEST_BIT (g->visited, P)) + return 1; + }); + return 0; +} + +/* Process predecessors first, and insert a copy. */ + +static void +elim_backward (elim_graph g, int T) +{ + int P; + SET_BIT (g->visited, T); + FOR_EACH_ELIM_GRAPH_PRED (g, T, P, + { + if (!TEST_BIT (g->visited, P)) + { + elim_backward (g, P); + insert_copy_on_edge (g->e, + partition_to_var (g->map, P), + partition_to_var (g->map, T)); + } + }); +} + +/* Check for a SCR, and create a temporary if there is one, and break + the cycle. Then issue the copies. Otherwise, simply insert the + required copies. */ + +static void +elim_create (elim_graph g, int T) +{ + tree U; + int P, S; + + if (elim_unvisited_predecessor (g, T)) + { + U = create_temp (partition_to_var (g->map, T)); + insert_copy_on_edge (g->e, U, partition_to_var (g->map, T)); + FOR_EACH_ELIM_GRAPH_PRED (g, T, P, + { + if (!TEST_BIT (g->visited, P)) + { + elim_backward (g, P); + insert_copy_on_edge (g->e, partition_to_var (g->map, P), U); + } + }); + } + else + { + S = elim_graph_remove_succ_edge (g, T); + if (S != -1) + { + SET_BIT (g->visited, T); + insert_copy_on_edge (g->e, + partition_to_var (g->map, T), + partition_to_var (g->map, S)); + } + } + +} + +/* Eliminate all the phi nodes on this edge. */ + +static void +eliminate_phi (edge e, int i, elim_graph g) +{ + int num_nodes = 0; + int x; + basic_block B = e->dest; + +#if defined ENABLE_CHECKING + if (i == -1) + abort (); + if (VARRAY_ACTIVE_SIZE (g->const_copies) != 0) + abort (); +#endif + + /* Abnormal edges already have everything coalesced, or the coalescer + would have aborted. */ + if (e->flags & EDGE_ABNORMAL) + return; + + num_nodes = num_var_partitions (g->map); + g->e = e; + + eliminate_build (g, B, i); + + if (elim_graph_size (g) != 0) + { + sbitmap_zero (g->visited); + VARRAY_POP_ALL (g->stack); + + for (x = 0; x < elim_graph_size (g); x++) + { + tree var = VARRAY_TREE (g->nodes, x); + int p = var_to_partition (g->map, var); + if (!TEST_BIT (g->visited, p)) + elim_forward (g, p); + } + + sbitmap_zero (g->visited); + while (VARRAY_ACTIVE_SIZE (g->stack) > 0) + { + x = VARRAY_TOP_INT (g->stack); + VARRAY_POP (g->stack); + if (!TEST_BIT (g->visited, x)) + elim_create (g, x); + } + } + + /* If there are any pending constant copies, issue them now. */ + while (VARRAY_ACTIVE_SIZE (g->const_copies) > 0) + { + tree src, dest; + src = VARRAY_TOP_TREE (g->const_copies); + VARRAY_POP (g->const_copies); + dest = VARRAY_TOP_TREE (g->const_copies); + VARRAY_POP (g->const_copies); + insert_copy_on_edge (e, dest, src); + } +} + + +/* Shortcut routine to print messages of the form: "str expr str expr str." */ + +static void +print_exprs (FILE *f, const char *str1, tree expr1, const char *str2, + tree expr2, const char *str3) +{ + fprintf (f, "%s", str1); + print_generic_expr (f, expr1, TDF_SLIM); + fprintf (f, "%s", str2); + print_generic_expr (f, expr2, TDF_SLIM); + fprintf (f, "%s", str3); +} + +static void +print_exprs_edge (FILE *f, edge e, const char *str1, tree expr1, + const char *str2, tree expr2) +{ + print_exprs (f, str1, expr1, str2, expr2, " across an abnormal edge"); + fprintf (f, " from BB%d->BB%d\n", e->src->index, + e->dest->index); +} + +/* Coalesce partitions which are live across abnormal edges. Since code + cannot be inserted on these edges, failure to coalesce something across + an abnormal edge is a non-compilable situation. */ + +static void +coalesce_abnormal_edges (var_map map, conflict_graph graph, root_var_p rv) +{ + basic_block bb; + edge e; + tree phi, var, tmp; + int x, y; + + /* Code cannot be inserted on abnormal edges. Look for all abnormal + edges, and coalesce any PHI results with their arguments across + that edge. */ + + FOR_EACH_BB (bb) + for (e = bb->succ; e; e = e->succ_next) + if (e->dest != EXIT_BLOCK_PTR && e->flags & EDGE_ABNORMAL) + for (phi = phi_nodes (e->dest); phi; phi = TREE_CHAIN (phi)) + { + /* Visit each PHI on the destination side of this abnormal + edge, and attempt to coalesce the argument with the result. */ + var = PHI_RESULT (phi); + x = var_to_partition (map, var); + + /* Ignore results which are not relevant. */ + if (x == NO_PARTITION) + continue; + + y = phi_arg_from_edge (phi, e); + if (y == -1) + abort (); + + tmp = PHI_ARG_DEF (phi, y); + if (!phi_ssa_name_p (tmp)) + { + print_exprs_edge (stderr, e, + "\nConstant argument in PHI. Can't insert :", + var, " = ", tmp); + abort (); + } + y = var_to_partition (map, tmp); + if (x == NO_PARTITION || y == NO_PARTITION) + abort (); + if (root_var_find (rv, x) != root_var_find (rv, y)) + { + print_exprs_edge (stderr, e, "\nDifferent root vars: ", + root_var (rv, root_var_find (rv, x)), + " and ", root_var (rv, root_var_find (rv, y))); + abort (); + } + + if (x != y) + { + if (!conflict_graph_conflict_p (graph, x, y)) + { + /* Now map the partitions back to their real variables. */ + var = partition_to_var (map, x); + tmp = partition_to_var (map, y); + if (dump_file + && (dump_flags & TDF_DETAILS)) + { + print_exprs_edge (dump_file, e, "ABNORMAL: Coalescing ", + var, " and ", tmp); + } + if (var_union (map, var, tmp) == NO_PARTITION) + { + print_exprs_edge (stderr, e, "\nUnable to coalesce", + partition_to_var (map, x), " and ", + partition_to_var (map, y)); + abort (); + } + conflict_graph_merge_regs (graph, x, y); + } + else + { + print_exprs_edge (stderr, e, "\n Conflict ", + partition_to_var (map, x), + " and ", partition_to_var (map, y)); + abort (); + } + } + } +} + + +/* Reduce the number of live ranges in the var_map. The only partitions + which are associated with actual variables at this point are those which + are forced to be coalesced for various reason. (live on entry, live + across abnormal edges, etc.). + Live range information is returned if FLAGS indicates that we are + combining temporaries, otherwise NULL is returned. */ + +static tree_live_info_p +coalesce_ssa_name (var_map map, int flags) +{ + int num, x, i; + sbitmap live; + tree var, phi; + root_var_p rv; + tree_live_info_p liveinfo; + var_ann_t ann; + conflict_graph graph; + basic_block bb; + coalesce_list_p cl = NULL; + + if (num_var_partitions (map) <= 1) + return NULL; + + /* If no preference given, use cheap coalescing of all partitions. */ + if ((flags & (SSANORM_COALESCE_PARTITIONS | SSANORM_USE_COALESCE_LIST)) == 0) + flags |= SSANORM_COALESCE_PARTITIONS; + + liveinfo = calculate_live_on_entry (map); + calculate_live_on_exit (liveinfo); + rv = root_var_init (map); + + /* Remove single element variable from the list. */ + root_var_compact (rv); + + if (flags & SSANORM_USE_COALESCE_LIST) + { + cl = create_coalesce_list (map); + + /* Add all potential copies via PHI arguments to the list. */ + FOR_EACH_BB (bb) + { + for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) + { + tree res = PHI_RESULT (phi); + int p = var_to_partition (map, res); + if (p == NO_PARTITION) + continue; + for (x = 0; x < PHI_NUM_ARGS (phi); x++) + { + tree arg = PHI_ARG_DEF (phi, x); + int p2; + + if (TREE_CODE (arg) != SSA_NAME) + continue; + if (SSA_NAME_VAR (res) != SSA_NAME_VAR (arg)) + continue; + p2 = var_to_partition (map, PHI_ARG_DEF (phi, x)); + if (p2 != NO_PARTITION) + add_coalesce (cl, p, p2, 1); + } + } + } + + /* Coalesce all the result decls together. */ + var = NULL_TREE; + i = 0; + for (x = 0; x < num_var_partitions (map); x++) + { + tree p = partition_to_var (map, x); + if (TREE_CODE (SSA_NAME_VAR(p)) == RESULT_DECL) + { + if (var == NULL_TREE) + { + var = p; + i = x; + } + else + add_coalesce (cl, i, x, 1); + } + } + } + + /* Build a conflict graph. */ + graph = build_tree_conflict_graph (liveinfo, rv, cl); + + if (cl) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Before sorting:\n"); + dump_coalesce_list (dump_file, cl); + } + + sort_coalesce_list (cl); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nAfter sorting:\n"); + dump_coalesce_list (dump_file, cl); + } + } + + /* Put the single element variables back in. */ + root_var_decompact (rv); + + /* First, coalesce all live on entry variables to their root variable. + This will ensure the first use is coming from the correct location. */ + + live = sbitmap_alloc (num_var_partitions (map)); + sbitmap_zero (live); + + /* Set 'live' vector to indicate live on entry partitions. */ + num = num_var_partitions (map); + for (x = 0 ; x < num; x++) + { + var = partition_to_var (map, x); + if (default_def (SSA_NAME_VAR (var)) == var) + SET_BIT (live, x); + } + + if ((flags & SSANORM_COMBINE_TEMPS) == 0) + { + delete_tree_live_info (liveinfo); + liveinfo = NULL; + } + + /* Assign root variable as partition representative for each live on entry + partition. */ + EXECUTE_IF_SET_IN_SBITMAP (live, 0, x, + { + var = root_var (rv, root_var_find (rv, x)); + ann = var_ann (var); + /* If these aren't already coalesced... */ + if (partition_to_var (map, x) != var) + { + if (ann->out_of_ssa_tag) + { + /* This root variable has already been assigned to another + partition which is not coalesced with this one. */ + abort (); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_exprs (dump_file, "Must coalesce ", + partition_to_var (map, x), + " with the root variable ", var, ".\n"); + } + + change_partition_var (map, var, x); + } + }); + + sbitmap_free (live); + + /* Coalesce partitions live across abnormal edges. */ + coalesce_abnormal_edges (map, graph, rv); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + dump_var_map (dump_file, map); + } + + /* Coalesce partitions. */ + if (flags & SSANORM_USE_COALESCE_LIST) + coalesce_tpa_members (rv, graph, map, cl, + ((dump_flags & TDF_DETAILS) ? dump_file + : NULL)); + + + if (flags & SSANORM_COALESCE_PARTITIONS) + coalesce_tpa_members (rv, graph, map, NULL, + ((dump_flags & TDF_DETAILS) ? dump_file + : NULL)); + if (cl) + delete_coalesce_list (cl); + root_var_delete (rv); + conflict_graph_delete (graph); + + return liveinfo; +} + +/* Take the ssa-name var_map, and assign real variables to each partition. */ + +static void +assign_vars (var_map map) +{ + int x, i, num, rep; + tree t, var; + var_ann_t ann; + root_var_p rv; + + rv = root_var_init (map); + if (!rv) + return; + + /* Coalescing may already have forced some partitions to their root + variable. Find these and tag them. */ + + num = num_var_partitions (map); + for (x = 0; x < num; x++) + { + var = partition_to_var (map, x); + if (TREE_CODE (var) != SSA_NAME) + { + /* Coalescing will already have verified that more than one + partition doesn't have the same root variable. Simply marked + the variable as assigned. */ + ann = var_ann (var); + ann->out_of_ssa_tag = 1; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "partition %d has variable ", x); + print_generic_expr (dump_file, var, TDF_SLIM); + fprintf (dump_file, " assigned to it.\n"); + } + + } + } + + num = root_var_num (rv); + for (x = 0; x < num; x++) + { + var = root_var (rv, x); + ann = var_ann (var); + for (i = root_var_first_partition (rv, x); + i != ROOT_VAR_NONE; + i = root_var_next_partition (rv, i)) + { + t = partition_to_var (map, i); + + if (t == var || TREE_CODE (t) != SSA_NAME) + continue; + + rep = var_to_partition (map, t); + + if (!ann->out_of_ssa_tag) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + print_exprs (dump_file, "", t, " --> ", var, "\n"); + change_partition_var (map, var, rep); + continue; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + print_exprs (dump_file, "", t, " not coalesced with ", var, + ""); + + var = create_temp (t); + change_partition_var (map, var, rep); + ann = var_ann (var); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " --> New temp: '"); + print_generic_expr (dump_file, var, TDF_SLIM); + fprintf (dump_file, "'\n"); + } + } + } + + root_var_delete (rv); +} + +/* Replace *p with whatever variable it has been rewritten to. If it changes + the stmt, return true. */ + +static inline bool +replace_variable (var_map map, tree *p, tree *expr) +{ + tree new_var; + tree var = *p; + + /* Check if we are replacing this variable with an expression. */ + if (expr) + { + int version = SSA_NAME_VERSION (*p); + if (expr[version]) + { + tree new_expr = TREE_OPERAND (expr[version], 1); + *p = new_expr; + /* Clear the stmt's RHS, or GC might bite us. */ + TREE_OPERAND (expr[version], 1) = NULL_TREE; + return true; + } + } + + new_var = var_to_partition_to_var (map, var); + if (new_var) + { + *p = new_var; + set_is_used (new_var); + return true; + } + return false; +} + + +/* Remove any PHI node which is virtual PHI. */ +static void +eliminate_virtual_phis (void) +{ + basic_block bb; + tree phi, next; + + FOR_EACH_BB (bb) + { + for (phi = phi_nodes (bb); phi; phi = next) + { + next = TREE_CHAIN (phi); + if (!is_gimple_reg (SSA_NAME_VAR (PHI_RESULT (phi)))) + { +#ifdef ENABLE_CHECKING + int i; + /* There should be no arguments of this PHI which are in + the partition list, or we get incorrect results. */ + for (i = 0; i < PHI_NUM_ARGS (phi); i++) + { + tree arg = PHI_ARG_DEF (phi, i); + if (TREE_CODE (arg) == SSA_NAME + && is_gimple_reg (SSA_NAME_VAR (arg))) + { + fprintf (stderr, "Argument of PHI is not virtual ("); + print_generic_expr (stderr, arg, TDF_SLIM); + fprintf (stderr, "), but the result is :"); + print_generic_stmt (stderr, phi, TDF_SLIM); + abort(); + } + } +#endif + remove_phi_node (phi, NULL_TREE, bb); + } + } + } +} + + +/* This routine will coalesce variables of the same type which do not + interfere with each other. This will both reduce the memory footprint of + the stack, and allow us to coalesce together local copies of globals and + scalarized component refs. */ + +static void +coalesce_vars (var_map map, tree_live_info_p liveinfo) +{ + basic_block bb; + type_var_p tv; + tree var; + int x, p, p2; + coalesce_list_p cl; + conflict_graph graph; + + cl = create_coalesce_list (map); + + /* Merge all the live on entry vectors for coalesced partitions. */ + for (x = 0; x < num_var_partitions (map); x++) + { + var = partition_to_var (map, x); + p = var_to_partition (map, var); + if (p != x) + live_merge_and_clear (liveinfo, p, x); + } + + /* When PHI nodes are turned into copies, the result of each PHI node + becomes live on entry to the block. Mark these now. */ + FOR_EACH_BB (bb) + { + tree phi, arg; + int p; + for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) + { + p = var_to_partition (map, PHI_RESULT (phi)); + + /* Skip virtual PHI nodes. */ + if (p == NO_PARTITION) + continue; + + make_live_on_entry (liveinfo, bb, p); + + /* Each argument is a potential copy operation. Add any arguments + which are not coalesced to the result to the coalesce list. */ + for (x = 0; x < PHI_NUM_ARGS (phi); x++) + { + arg = PHI_ARG_DEF (phi, x); + if (!phi_ssa_name_p (arg)) + continue; + p2 = var_to_partition (map, arg); + if (p2 == NO_PARTITION) + continue; + if (p != p2) + add_coalesce (cl, p, p2, 1); + } + } + } + + + /* Re-calculate live on exit info. */ + calculate_live_on_exit (liveinfo); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Live range info for variable memory coalescing.\n"); + dump_live_info (dump_file, liveinfo, LIVEDUMP_ALL); + + fprintf (dump_file, "Coalesce list from phi nodes:\n"); + dump_coalesce_list (dump_file, cl); + } + + + tv = type_var_init (map); + if (dump_file) + type_var_dump (dump_file, tv); + type_var_compact (tv); + if (dump_file) + type_var_dump (dump_file, tv); + + graph = build_tree_conflict_graph (liveinfo, tv, cl); + + type_var_decompact (tv); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "type var list now looks like:n"); + type_var_dump (dump_file, tv); + + fprintf (dump_file, "Coalesce list after conflict graph build:\n"); + dump_coalesce_list (dump_file, cl); + } + + sort_coalesce_list (cl); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Coalesce list after sorting:\n"); + dump_coalesce_list (dump_file, cl); + } + + coalesce_tpa_members (tv, graph, map, cl, + ((dump_flags & TDF_DETAILS) ? dump_file : NULL)); + + type_var_delete (tv); + delete_coalesce_list (cl); +} + + +/* Temporary Expression Replacement (TER) + + Replace SSA version variables during out-of-ssa with their defining + expression if there is only one use of the variable. + + A pass is made through the function, one block at a time. No cross block + information is tracked. + + Variables which only have one use, and whose defining stmt is considered + a replaceable expression (see check_replaceable) are entered into + consideration by adding a list of dependent partitions to the version_info + vector for that ssa_name_version. This information comes from the partition + mapping for each USE. At the same time, the partition_dep_list vector for + these partitions have this version number entered into their lists. + + When the use of a replaceable ssa_variable is encountered, the dependence + list in version_info[] is moved to the "pending_dependence" list in case + the current expression is also replaceable. (To be determined later in + processing this stmt.) version_info[] for the version is then updated to + point to the defining stmt and the 'replaceable' bit is set. + + Any partition which is defined by a statement 'kills' any expression which + is dependent on this partition. Every ssa version in the partitions' + dependence list is removed from future consideration. + + All virtual references are lumped together. Any expression which is + dependent on any virtual variable (via a VUSE) has a dependence added + to the special partition defined by VIRTUAL_PARTITION. + + Whenever a VDEF is seen, all expressions dependent this VIRTUAL_PARTITION + are removed from consideration. + + At the end of a basic block, all expression are removed from consideration + in preparation for the next block. + + The end result is a vector over SSA_NAME_VERSION which is passed back to + rewrite_out_of_ssa. As the SSA variables are being rewritten, instead of + replacing the SSA_NAME tree element with the partition it was assigned, + it is replaced with the RHS of the defining expression. */ + +/* Dependancy list element. This can contain either a partition index or a + version number, depending on which list it is in. */ +typedef struct value_expr_d +{ + int value; + struct value_expr_d *next; +} *value_expr_p; + +/* Temporary Expression Replacement (TER) table information. */ +typedef struct temp_expr_table_d +{ + var_map map; + void **version_info; + value_expr_p *partition_dep_list; + bitmap replaceable; + bool saw_replaceable; + int virtual_partition; + bitmap partition_in_use; + value_expr_p free_list; + value_expr_p pending_dependence; +} *temp_expr_table_p; + +/* Used to indicate a dependancy on VDEFs. */ +#define VIRTUAL_PARTITION(table) (table->virtual_partition) + +static temp_expr_table_p new_temp_expr_table (var_map); +static tree *free_temp_expr_table (temp_expr_table_p); +static inline value_expr_p new_value_expr (temp_expr_table_p); +static inline void free_value_expr (temp_expr_table_p, value_expr_p); +static inline value_expr_p find_value_in_list (value_expr_p, int, + value_expr_p *); +static inline void add_value_to_list (temp_expr_table_p, value_expr_p *, int); +static inline void add_info_to_list (temp_expr_table_p, value_expr_p *, + value_expr_p); +static value_expr_p remove_value_from_list (value_expr_p *, int); +static void add_dependance (temp_expr_table_p, int, tree); +static bool check_replaceable (temp_expr_table_p, tree); +static void finish_expr (temp_expr_table_p, int, bool); +static void mark_replaceable (temp_expr_table_p, tree); +static inline void kill_expr (temp_expr_table_p, int, bool); +static inline void kill_virtual_exprs (temp_expr_table_p, bool); +static void find_replaceable_in_bb (temp_expr_table_p, basic_block); +static tree *find_replaceable_exprs (var_map); +static void dump_replaceable_exprs (FILE *, tree *); + +/* Create a new TER table. */ +static temp_expr_table_p +new_temp_expr_table (var_map map) +{ + temp_expr_table_p t; + + t = (temp_expr_table_p) xmalloc (sizeof (struct temp_expr_table_d)); + t->map = map; + + t->version_info = xcalloc (highest_ssa_version + 1, sizeof (void *)); + t->partition_dep_list = xcalloc (num_var_partitions (map) + 1, + sizeof (value_expr_p)); + + t->replaceable = BITMAP_XMALLOC (); + t->partition_in_use = BITMAP_XMALLOC (); + + t->saw_replaceable = false; + t->virtual_partition = num_var_partitions (map); + t->free_list = NULL; + t->pending_dependence = NULL; + + return t; +} + + +/* Free a TER table. If there are valid replacements, return the expression + vector. */ +static tree * +free_temp_expr_table (temp_expr_table_p t) +{ + value_expr_p p; + tree *ret = NULL; + +#ifdef ENABLE_CHECKING + int x; + for (x = 0; x <= num_var_partitions (t->map); x++) + if (t->partition_dep_list[x] != NULL) + abort(); +#endif + + while ((p = t->free_list)) + { + t->free_list = p->next; + free (p); + } + + BITMAP_XFREE (t->partition_in_use); + BITMAP_XFREE (t->replaceable); + + free (t->partition_dep_list); + if (t->saw_replaceable) + ret = (tree *)t->version_info; + else + free (t->version_info); + + free (t); + return ret; +} + + +/* Allocate a new value list node. Take it from the free list if possible. */ +static inline value_expr_p +new_value_expr (temp_expr_table_p table) +{ + value_expr_p p; + if (table->free_list) + { + p = table->free_list; + table->free_list = p->next; + } + else + p = (value_expr_p) xmalloc (sizeof (struct value_expr_d)); + + return p; +} + + +/* Add a value list node to the free list. */ +static inline void +free_value_expr (temp_expr_table_p table, value_expr_p p) +{ + p->next = table->free_list; + table->free_list = p; +} + + +/* Find a specific value if its in a list. Return a pointer to the list + object if found. Return NULL if it isn't. If last_ptr is provided, + it will point to the previous item upon return, or NULL if this is the + first item in the list. */ +static inline value_expr_p +find_value_in_list (value_expr_p list, int value, value_expr_p *last_ptr) +{ + value_expr_p curr; + value_expr_p last = NULL; + + for (curr = list; curr; last = curr, curr = curr->next) + { + if (curr->value == value) + break; + } + if (last_ptr) + *last_ptr = last; + return curr; +} + + +/* Add a value to a list, if it isn't already present. */ +static inline void +add_value_to_list (temp_expr_table_p tab, value_expr_p *list, int value) +{ + value_expr_p info; + + if (!find_value_in_list (*list, value, NULL)) + { + info = new_value_expr (tab); + info->value = value; + info->next = *list; + *list = info; + } +} + + +/* Add a value node if it's value isn't already in the list. Free this node if + it is already in the list. */ +static inline void +add_info_to_list (temp_expr_table_p tab, value_expr_p *list, value_expr_p info) +{ + if (find_value_in_list (*list, info->value, NULL)) + free_value_expr (tab, info); + else + { + info->next = *list; + *list = info; + } +} + + +/* Look for a value in a list. If found, remove it from the list and return + it's pointer. */ +static value_expr_p +remove_value_from_list (value_expr_p *list, int value) +{ + value_expr_p info, last; + + info = find_value_in_list (*list, value, &last); + if (!info) + return NULL; + if (!last) + *list = info->next; + else + last->next = info->next; + + return info; +} + + +/* Add a dependancy between the def of an SSA version and the partitions each + use in the expression represent. */ +static void +add_dependance (temp_expr_table_p tab, int version, tree var) +{ + int i, x; + value_expr_p info; + + i = SSA_NAME_VERSION (var); + if (bitmap_bit_p (tab->replaceable, i)) + { + /* This variable is being substituted, so use whatever dependences + were queued up when we marked this as replaceable earlier. */ + while ((info = tab->pending_dependence)) + { + tab->pending_dependence = info->next; + /* Get the partition this variable was dependent on. Reuse this + object to represent the current expression instead. */ + x = info->value; + info->value = version; + add_info_to_list (tab, &(tab->partition_dep_list[x]), info); + add_value_to_list (tab, + (value_expr_p *)&(tab->version_info[version]), x); + bitmap_set_bit (tab->partition_in_use, x); + } + } + else + { + i = var_to_partition (tab->map, var); +#ifdef ENABLE_CHECKING + if (i== NO_PARTITION) + abort (); +#endif + add_value_to_list (tab, &(tab->partition_dep_list[i]), version); + add_value_to_list (tab, (value_expr_p *)&(tab->version_info[version]), i); + bitmap_set_bit (tab->partition_in_use, i); + } +} + + +/* Check if an expression is suitable for replacement. If so, create an + expression entry. Return true if this stmt is replaceable. */ + +static bool +check_replaceable (temp_expr_table_p tab, tree stmt) +{ + stmt_ann_t ann; + vuse_optype vuseops; + def_optype defs; + use_optype uses; + tree var, def; + int num_use_ops, version, i; + var_map map = tab->map; + + if (TREE_CODE (stmt) != MODIFY_EXPR) + return false; + + ann = stmt_ann (stmt); + defs = DEF_OPS (ann); + + /* Punt if there is more than 1 def, or more than 1 use. */ + if (NUM_DEFS (defs) != 1) + return false; + def = DEF_OP (defs, 0); + if (version_ref_count (map, def) != 1) + return false; + + /* Assignments to variables assigned to hard registers are not + replaceable. */ + if (DECL_HARD_REGISTER (SSA_NAME_VAR (def))) + return false; + + /* There must be no VDEFS. */ + if (NUM_VDEFS (VDEF_OPS (ann)) != 0) + return false; + + /* Float expressions must go through memory if float-store is on. */ + if (flag_float_store && FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (stmt, 1)))) + return false; + + uses = USE_OPS (ann); + num_use_ops = NUM_USES (uses); + vuseops = VUSE_OPS (ann); + + /* Any expression which has no virtual operands and no real operands + should have been propagated if it's possible to do anything with them. + If this happens here, it probably exists that way for a reason, so we + won't touch it. An example is: + b_4 = &tab + There are no virtual uses nor any real uses, so we just leave this + alone to be safe. */ + + if (num_use_ops == 0 && NUM_VUSES (vuseops) == 0) + return false; + + version = SSA_NAME_VERSION (def); + + /* Add this expression to the dependancy list for each use partition. */ + for (i = 0; i < num_use_ops; i++) + { + var = USE_OP (uses, i); + add_dependance (tab, version, var); + } + + /* If there are VUSES, add a dependence on virtual defs. */ + if (NUM_VUSES (vuseops) != 0) + { + add_value_to_list (tab, (value_expr_p *)&(tab->version_info[version]), + VIRTUAL_PARTITION (tab)); + add_value_to_list (tab, + &(tab->partition_dep_list[VIRTUAL_PARTITION (tab)]), + version); + bitmap_set_bit (tab->partition_in_use, VIRTUAL_PARTITION (tab)); + } + + return true; +} + + +/* This function will remove an expression from replacement consideration. If + 'replace' is true, it is marked as replaceable, otherwise not. */ +static void +finish_expr (temp_expr_table_p tab, int version, bool replace) +{ + value_expr_p info, tmp; + int partition; + + /* Remove this expression from its dependent lists. The partition dependance + list is retained and transfered later to whomever uses this version. */ + for (info = (value_expr_p) tab->version_info[version]; info; info = tmp) + { + partition = info->value; +#ifdef ENABLE_CHECKING + if (tab->partition_dep_list[partition] == NULL) + abort (); +#endif + tmp = remove_value_from_list (&(tab->partition_dep_list[partition]), + version); +#ifdef ENABLE_CHECKING + if (!tmp) + abort (); +#endif + free_value_expr (tab, tmp); + /* Only clear the bit when the dependancy list is emptied via + a replacement. Otherwise kill_expr will take care of it. */ + if (!(tab->partition_dep_list[partition]) && replace) + bitmap_clear_bit (tab->partition_in_use, partition); + tmp = info->next; + if (!replace) + free_value_expr (tab, info); + } + + if (replace) + { + tab->saw_replaceable = true; + bitmap_set_bit (tab->replaceable, version); + } + else + { +#ifdef ENABLE_CHECKING + if (bitmap_bit_p (tab->replaceable, version)) + abort (); +#endif + tab->version_info[version] = NULL; + } +} + + +/* Mark the expression associated with a variable as replaceable, and enter + the defining stmt into the version_info table. */ +static void +mark_replaceable (temp_expr_table_p tab, tree var) +{ + value_expr_p info; + int version = SSA_NAME_VERSION (var); + finish_expr (tab, version, true); + + /* Move the dependence list to the pending list. */ + if (tab->version_info[version]) + { + info = (value_expr_p) tab->version_info[version]; + for ( ; info->next; info = info->next) + continue; + info->next = tab->pending_dependence; + tab->pending_dependence = (value_expr_p)tab->version_info[version]; + } + + tab->version_info[version] = SSA_NAME_DEF_STMT (var); +} + + +/* This function finishes any expression which is dependent on this partition + as NOT replaceable. clear_bit is used to determine whether partition_in_use + should have iuts bit cleared. Since this can be called within an + EXECUTE_IF_SET_IN_BITMAP, the bit can't always be cleared. */ +static inline void +kill_expr (temp_expr_table_p tab, int partition, bool clear_bit) +{ + value_expr_p ptr; + + /* Mark every active expr dependant on this var as not replaceable. */ + while ((ptr = tab->partition_dep_list[partition]) != NULL) + finish_expr (tab, ptr->value, false); + + if (clear_bit) + bitmap_clear_bit (tab->partition_in_use, partition); +} + + +/* This function kills all expressions which are dependant on virtual DEFs. */ +static inline void +kill_virtual_exprs (temp_expr_table_p tab, bool clear_bit) +{ + kill_expr (tab, VIRTUAL_PARTITION (tab), clear_bit); +} + + +/* This function processes a basic block, and looks for variables which can + be replaced by their expressions. */ +static void +find_replaceable_in_bb (temp_expr_table_p tab, basic_block bb) +{ + block_stmt_iterator bsi; + tree stmt, def; + stmt_ann_t ann; + int partition, num, i; + use_optype uses; + def_optype defs; + var_map map = tab->map; + value_expr_p p; + + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + stmt = bsi_stmt (bsi); + ann = stmt_ann (stmt); + + /* Determine if this stmt finishes an existing expression. */ + uses = USE_OPS (ann); + num = NUM_USES (uses); + for (i = 0; i < num; i++) + { + def = USE_OP (uses, i); + if (tab->version_info[SSA_NAME_VERSION (def)]) + { + /* Mark expression as replaceable unless stmt is volatile. */ + if (!ann->has_volatile_ops) + mark_replaceable (tab, def); + else + finish_expr (tab, SSA_NAME_VERSION (def), false); + } + } + + /* Next, see if this stmt kills off an active expression. */ + defs = DEF_OPS (ann); + num = NUM_DEFS (defs); + for (i = 0; i < num; i++) + { + def = DEF_OP (defs, i); + partition = var_to_partition (map, def); + if (partition != NO_PARTITION && tab->partition_dep_list[partition]) + kill_expr (tab, partition, true); + } + + /* Now see if we are creating a new expression or not. */ + if (!ann->has_volatile_ops) + check_replaceable (tab, stmt); + + /* Free any unused dependancy lists. */ + while ((p = tab->pending_dependence)) + { + tab->pending_dependence = p->next; + free_value_expr (tab, p); + } + + /* A VDEF kills any expression using a virtual operand. */ + if (NUM_VDEFS (VDEF_OPS (ann)) > 0) + kill_virtual_exprs (tab, true); + } +} + + +/* This function is the driver routine for replacement of temporary expressions + in the SSA->normal phase. If there are replaceable expressions, a table is + returned which maps SSA versions to the expressions they should be replaced + with. A NULL_TREE indicates no replacement should take place. + If there are no replacements at all, NULL is returned by the function. */ +static tree * +find_replaceable_exprs (var_map map) +{ + basic_block bb; + int i; + temp_expr_table_p table; + tree *ret; + + table = new_temp_expr_table (map); + FOR_EACH_BB (bb) + { + find_replaceable_in_bb (table, bb); + EXECUTE_IF_SET_IN_BITMAP ((table->partition_in_use), 0, i, + { + kill_expr (table, i, false); + }); + } + + ret = free_temp_expr_table (table); + return ret; +} + + +/* Dump the TER expression table. */ +static void +dump_replaceable_exprs (FILE *f, tree *expr) +{ + tree stmt, var; + int x; + fprintf (f, "\nReplacing Expressions\n"); + for (x = 0; x < (int)highest_ssa_version + 1; x++) + if (expr[x]) + { + stmt = expr[x]; + var = DEF_OP (STMT_DEF_OPS (stmt), 0); + print_generic_expr (f, var, TDF_SLIM); + fprintf (f, " replace with --> "); + print_generic_expr (f, TREE_OPERAND (stmt, 1), TDF_SLIM); + fprintf (f, "\n"); + } + fprintf (f, "\n"); +} + + +/* This function will rewrite the current program using the variable mapping + found in 'map'. If the replacement vector 'values' is provided, any + occurrences of partitions with non-null entries in the vector will be + replaced with the expression in the vector instead of its mapped + variable. */ + +static void +rewrite_trees (var_map map, tree *values) +{ + elim_graph g; + basic_block bb; + block_stmt_iterator si; + edge e; + tree phi; + bool changed; + + /* Replace PHI nodes with any required copies. */ + g = new_elim_graph (map->num_partitions); + g->map = map; + FOR_EACH_BB (bb) + { + for (si = bsi_start (bb); !bsi_end_p (si); ) + { + size_t i, num_uses, num_defs; + use_optype uses; + def_optype defs; + tree stmt = bsi_stmt (si); + tree *use_p = NULL; + int remove = 0, is_copy = 0; + stmt_ann_t ann; + + get_stmt_operands (stmt); + ann = stmt_ann (stmt); + changed = false; + + if (TREE_CODE (stmt) == MODIFY_EXPR + && (TREE_CODE (TREE_OPERAND (stmt, 1)) == SSA_NAME)) + is_copy = 1; + + uses = USE_OPS (ann); + num_uses = NUM_USES (uses); + + for (i = 0; i < num_uses; i++) + { + use_p = USE_OP_PTR (uses, i); + if (replace_variable (map, use_p, values)) + changed = true; + } + + defs = DEF_OPS (ann); + num_defs = NUM_DEFS (defs); + + if (values && num_defs == 1) + { + tree def = DEF_OP (defs, 0); + tree val; + val = values[SSA_NAME_VERSION (def)]; + if (val) + remove = 1; + } + if (!remove) + { + for (i = 0; i < num_defs; i++) + { + tree *def_p = DEF_OP_PTR (defs, i); + + if (replace_variable (map, def_p, NULL)) + changed = true; + + if (is_copy + && num_uses == 1 + && use_p + && def_p + && (*def_p == *use_p)) + remove = 1; + } + if (changed) + modify_stmt (stmt); + } + + /* Remove copies of the form 'var = var'. */ + if (remove) + bsi_remove (&si); + else + bsi_next (&si); + } + + phi = phi_nodes (bb); + if (phi) + { + for (e = bb->pred; e; e = e->pred_next) + eliminate_phi (e, phi_arg_from_edge (phi, e), g); + } + } + + delete_elim_graph (g); + + /* If any copies were inserted on edges, actually insert them now. */ + bsi_commit_edge_inserts (NULL); +} + +/* Remove the variables specified in a var map from SSA form. */ +void +remove_ssa_form (FILE *dump, var_map map, int flags) +{ + tree_live_info_p liveinfo; + basic_block bb; + tree phi, next; + FILE *save; + tree *values = NULL; + + save = dump_file; + dump_file = dump; + + /* If we are not combining temps, dont calculate live ranges fo variables + with only one SSA version. */ + if ((flags & SSANORM_COMBINE_TEMPS) == 0) + compact_var_map (map, VARMAP_NO_SINGLE_DEFS); + else + compact_var_map (map, VARMAP_NORMAL); + + if (dump_file && (dump_flags & TDF_DETAILS)) + dump_var_map (dump_file, map); + + liveinfo = coalesce_ssa_name (map, flags); + + /* Make sure even single occurrence variables are in the list now. */ + if ((flags & SSANORM_COMBINE_TEMPS) == 0) + compact_var_map (map, VARMAP_NORMAL); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "After Coalescing:\n"); + dump_var_map (dump_file, map); + } + + if (flags & SSANORM_PERFORM_TER) + { + values = find_replaceable_exprs (map); + if (values && dump_file && (dump_flags & TDF_DETAILS)) + dump_replaceable_exprs (dump_file, values); + } + + /* Assign real variables to the partitions now. */ + assign_vars (map); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "After Root variable replacement:\n"); + dump_var_map (dump_file, map); + } + + if ((flags & SSANORM_COMBINE_TEMPS) && liveinfo) + { + coalesce_vars (map, liveinfo); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "After variable memory coalescing:\n"); + dump_var_map (dump_file, map); + } + } + + if (liveinfo) + delete_tree_live_info (liveinfo); + + rewrite_trees (map, values); + + if (values) + free (values); + + /* Remove phi nodes which have been translated back to real variables. */ + FOR_EACH_BB (bb) + { + for (phi = phi_nodes (bb); phi; phi = next) + { + next = TREE_CHAIN (phi); + if ((flags & SSANORM_REMOVE_ALL_PHIS) + || var_to_partition (map, PHI_RESULT (phi)) != NO_PARTITION) + remove_phi_node (phi, NULL_TREE, bb); + } + } + + dump_file = save; +} + +/* Take a subset of the variables (VARS) in the current function out of SSA + form. */ + +void +rewrite_vars_out_of_ssa (bitmap vars) +{ + if (bitmap_first_set_bit (vars) >= 0) + { + var_map map; + basic_block bb; + tree phi; + int i; + int ssa_flags; + + /* Search for PHIs in which one of the PHI arguments is marked for + translation out of SSA form, but for which the PHI result is not + marked for translation out of SSA form. + + Our per-variable out of SSA translation can not handle that case; + however we can easily handle it here by creating a new instance + of the PHI result's underlying variable and initializing it to + the offending PHI argument on the edge associated with the + PHI argument. We then change the PHI argument to use our new + instead of the PHI's underlying variable. + + You might think we could register partitions for the out-of-ssa + translation here and avoid a second walk of the PHI nodes. No + such luck since the size of the var map will change if we have + to manually take variables out of SSA form here. */ + FOR_EACH_BB (bb) + { + for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) + { + tree result = SSA_NAME_VAR (PHI_RESULT (phi)); + + /* If the definition is marked for renaming, then we need + to do nothing more for this PHI node. */ + if (bitmap_bit_p (vars, var_ann (result)->uid)) + continue; + + /* Look at all the arguments and see if any of them are + marked for renaming. If so, we need to handle them + specially. */ + for (i = 0; i < PHI_NUM_ARGS (phi); i++) + { + tree arg = PHI_ARG_DEF (phi, i); + + /* If the argument is not an SSA_NAME, then we can ignore + this argument. */ + if (TREE_CODE (arg) != SSA_NAME) + continue; + + /* If this argument is marked for renaming, then we need + to undo the copy propagation so that we can take + the argument out of SSA form without taking the + result out of SSA form. */ + arg = SSA_NAME_VAR (arg); + if (bitmap_bit_p (vars, var_ann (arg)->uid)) + { + tree new_name, copy; + + /* Get a new SSA_NAME for the copy, it is based on + the result, not the argument! We use the PHI + as the definition since we haven't created the + definition statement yet. */ + new_name = make_ssa_name (result, phi); + + /* Now create the copy statement. */ + copy = build (MODIFY_EXPR, TREE_TYPE (arg), + new_name, PHI_ARG_DEF (phi, i)); + + /* Now update SSA_NAME_DEF_STMT to point to the + newly created statement. */ + SSA_NAME_DEF_STMT (new_name) = copy; + + /* Now make the argument reference our new SSA_NAME. */ + PHI_ARG_DEF (phi, i) = new_name; + + /* Queue the statement for insertion. */ + bsi_insert_on_edge (PHI_ARG_EDGE (phi, i), copy); + modify_stmt (copy); + } + } + } + } + + /* If any copies were inserted on edges, actually insert them now. */ + bsi_commit_edge_inserts (NULL); + + /* Now register partitions for all instances of the variables we + are taking out of SSA form. */ + map = init_var_map (highest_ssa_version + 1); + register_ssa_partitions_for_vars (vars, map); + + /* Now that we have all the partitions registered, translate the + appropriate variables out of SSA form. */ + ssa_flags = SSANORM_COALESCE_PARTITIONS; + if (flag_tree_combine_temps) + ssa_flags |= SSANORM_COMBINE_TEMPS; + remove_ssa_form (dump_file, map, ssa_flags); + + /* And finally, reset the out_of_ssa flag for each of the vars + we just took out of SSA form. */ + EXECUTE_IF_SET_IN_BITMAP (vars, 0, i, + { + var_ann (referenced_var (i))->out_of_ssa_tag = 0; + }); + + } +} + + +/* Take the current function out of SSA form, as described in + R. Morgan, ``Building an Optimizing Compiler'', + Butterworth-Heinemann, Boston, MA, 1998. pp 176-186. */ + +static void +rewrite_out_of_ssa (void) +{ + var_map map; + int var_flags = 0; + int ssa_flags = (SSANORM_REMOVE_ALL_PHIS | SSANORM_USE_COALESCE_LIST + | SSANORM_COALESCE_PARTITIONS); + + eliminate_virtual_phis (); + + if (dump_file && (dump_flags & TDF_DETAILS)) + dump_tree_cfg (dump_file, dump_flags & ~TDF_DETAILS); + + /* We cannot allow unssa to un-gimplify trees before we instrument them. */ + if (flag_tree_ter && !flag_mudflap) + var_flags = SSA_VAR_MAP_REF_COUNT; + + map = create_ssa_var_map (var_flags); + + if (flag_tree_combine_temps) + ssa_flags |= SSANORM_COMBINE_TEMPS; + if (flag_tree_ter && !flag_mudflap) + ssa_flags |= SSANORM_PERFORM_TER; + + remove_ssa_form (dump_file, map, ssa_flags); + + if (dump_file && (dump_flags & TDF_DETAILS)) + dump_tree_cfg (dump_file, dump_flags & ~TDF_DETAILS); + + /* Do some cleanups which reduce the amount of data the + tree->rtl expanders deal with. */ + cfg_remove_useless_stmts (); + + /* Flush out flow graph and SSA data. */ + delete_var_map (map); + + /* Mark arrays indexed with non-constant indices with TREE_ADDRESSABLE. */ + discover_nonconstant_array_refs (); +} + + +struct tree_opt_pass pass_del_ssa = +{ + "optimized", /* name */ + NULL, /* gate */ + rewrite_out_of_ssa, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_TREE_SSA_TO_NORMAL, /* tv_id */ + PROP_cfg | PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + /* ??? If TER is enabled, we also kill gimple. */ + PROP_ssa, /* properties_destroyed */ + TODO_verify_ssa | TODO_verify_flow + | TODO_verify_stmts, /* todo_flags_start */ + TODO_dump_func | TODO_ggc_collect /* todo_flags_finish */ +}; diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index ff7ef9a6c9a..2696256b1df 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -515,7 +515,12 @@ ptr_is_dereferenced_by (tree ptr, tree stmt, bool *is_store) /* Traverse use-def links for all the pointers in the program to collect - address escape and points-to information. */ + address escape and points-to information. + + This is loosely based on the same idea described in R. Hasti and S. + Horwitz, ``Using static single assignment form to improve + flow-insensitive pointer analysis,'' in SIGPLAN Conference on + Programming Language Design and Implementation, pp. 97-105, 1998. */ static void compute_points_to_and_addr_escape (struct alias_info *ai) diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c index 303e0b20458..a5026e8ce44 100644 --- a/gcc/tree-ssa.c +++ b/gcc/tree-ssa.c @@ -1,6 +1,5 @@ -/* SSA for trees. +/* Miscellaneous SSA utility functions. Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. - Contributed by Diego Novillo <dnovillo@redhat.com> This file is part of GCC. @@ -45,2803 +44,8 @@ Boston, MA 02111-1307, USA. */ #include "tree-alias-common.h" #include "hashtab.h" #include "tree-dump.h" -#include "tree-ssa-live.h" #include "tree-pass.h" -#include "cfgloop.h" -#include "domwalk.h" -/* This file builds the SSA form for a function as described in: - - R. Cytron, J. Ferrante, B. Rosen, M. Wegman, and K. Zadeck. Efficiently - Computing Static Single Assignment Form and the Control Dependence - Graph. ACM Transactions on Programming Languages and Systems, - 13(4):451-490, October 1991. */ - - -/* Workstack for computing PHI node insertion points. */ -static GTY (()) varray_type work_stack = NULL; - -/* Each entry in DEF_BLOCKS is a bitmap B for a variable VAR. If B(J) is - set, then there is a definition for variable VAR in basic block J. - This hashtable is created by the variable reference finder and used by - the SSA builder to add PHI nodes. It is destroyed after the program is - converted to SSA form. */ -static htab_t def_blocks; - -/* Structure to map a variable VAR to the set of blocks that contain - definitions for VAR. */ -struct GTY(()) def_blocks_d -{ - tree var; - bitmap def_blocks; - bitmap livein_blocks; -}; - -/* Global data to attach to the main dominator walk structure. */ -struct mark_def_sites_global_data -{ - /* This sbitmap contains the variables which are set before they - are used in a basic block. We keep it as a global variable - solely to avoid the overhead of allocating and deallocating - the bitmap. */ - sbitmap kills; -}; - -/* Table to store the current reaching definition for every variable in - the function. Given a variable V, its entry will be its immediately - reaching SSA_NAME node. */ -static varray_type currdefs; - -/* Structure to map variables to values. It's used to keep track of the - current reaching definition, constant values and variable copies while - renaming. */ -struct GTY(()) var_value_d -{ - tree var; - tree value; -}; - -/* Used to hold all the components requires to do SSA PHI elimination. - The implementation of the node list and pred/succ list has been changed - to a simple linear list of nodes and edges represented as pairs of nodes. - - The predecessor and successor list. Nodes are entered in pairs, where - [0] ->PRED, [1]->SUCC. All the even indexes in the array represent - predecessors, all the odd elements are successors. - - Rationale: - When implemented as bitmaps, very large programs SSA->Normal times were - being dominated by clearing the interference graph. - - Typically this list of edges is extremely small since it only includes - PHI results and uses from a single edge which have not coalesced with - each other. This means no virtual PHI nodes are included, and empirical - evidence suggests that the number of edges rarely exceed 3, and in a - bootstrap of GCC, the maximum size encountered was 7. THis also limits - the number of possible nodes that are involved to rarely more than 6, - and in the bootstrap of gcc, the maximum number of nodes encountered - was 12. */ - -typedef struct _elim_graph -{ - /* Size of the elimination vectors. */ - int size; - - /* List of nodes in the elimination graph. */ - varray_type nodes; - /* The predecessor and successor edge list. */ - varray_type edge_list; - - /* Visited vector. */ - sbitmap visited; - - /* Stack for visited nodes. */ - varray_type stack; - - /* The variable partition map. */ - var_map map; - /* edge being eliminated by this graph. */ - edge e; - /* List of constant copies to emit. These are pushed on in pairs. */ - varray_type const_copies; -} *elim_graph; - -/* Statistics for dominator optimizations. */ -struct ssa_stats_d -{ - long num_stmts; -}; - -struct rewrite_block_data -{ - varray_type block_defs; -}; - -static struct ssa_stats_d ssa_stats; - -/* Local functions. */ -static void rewrite_finalize_block (struct dom_walk_data *, basic_block); -static void rewrite_initialize_block_local_data (struct dom_walk_data *, - basic_block, bool); -static void rewrite_initialize_block (struct dom_walk_data *, basic_block); -static void rewrite_add_phi_arguments (struct dom_walk_data *, basic_block); -static void mark_def_sites (struct dom_walk_data *walk_data, - basic_block bb, block_stmt_iterator); -static void mark_def_sites_initialize_block (struct dom_walk_data *walk_data, - basic_block bb); -static void compute_global_livein (bitmap, bitmap); -static void set_def_block (tree, basic_block); -static void set_livein_block (tree, basic_block); -static bool prepare_operand_for_rename (tree *op_p, size_t *uid_p); -static void insert_phi_nodes (bitmap *); -static void rewrite_stmt (struct dom_walk_data *, - basic_block, - block_stmt_iterator); -static inline void rewrite_operand (tree *); -static void insert_phi_nodes_for (tree, bitmap *); -static tree get_reaching_def (tree); -static tree get_value_for (tree, varray_type); -static void set_value_for (tree, tree, varray_type); -static hashval_t def_blocks_hash (const void *); -static int def_blocks_eq (const void *, const void *); -static int debug_def_blocks_r (void **, void *); -static struct def_blocks_d *get_def_blocks_for (tree); -static void htab_statistics (FILE *, htab_t); - -static tree create_temp (tree); -static void insert_copy_on_edge (edge, tree, tree); -static elim_graph new_elim_graph (int); -static inline void delete_elim_graph (elim_graph); -static inline void clear_elim_graph (elim_graph); -static inline int elim_graph_size (elim_graph); -static inline void elim_graph_add_node (elim_graph, tree); -static inline void elim_graph_add_edge (elim_graph, int, int); -static inline int elim_graph_remove_succ_edge (elim_graph, int); - -static inline void eliminate_name (elim_graph, tree); -static void eliminate_build (elim_graph, basic_block, int); -static void elim_forward (elim_graph, int); -static int elim_unvisited_predecessor (elim_graph, int); -static void elim_backward (elim_graph, int); -static void elim_create (elim_graph, int); -static void eliminate_phi (edge, int, elim_graph); -static tree_live_info_p coalesce_ssa_name (var_map, int); -static void assign_vars (var_map); -static bool replace_variable (var_map, tree *, tree *); -static void eliminate_virtual_phis (void); -static void coalesce_abnormal_edges (var_map, conflict_graph, root_var_p); -static void print_exprs (FILE *, const char *, tree, const char *, tree, - const char *); -static void print_exprs_edge (FILE *, edge, const char *, tree, const char *, - tree); -static bool verify_def (basic_block, basic_block *, tree, tree); -static bool verify_use (basic_block, basic_block, tree, tree, bool); -static bool verify_phi_args (tree, basic_block, basic_block *); - -/* Return the value associated with variable VAR in TABLE. */ - -static inline tree -get_value_for (tree var, varray_type table) -{ - return VARRAY_TREE (table, var_ann (var)->uid); -} - - -/* Associate VALUE to variable VAR in TABLE. */ - -static inline void -set_value_for (tree var, tree value, varray_type table) -{ - VARRAY_TREE (table, var_ann (var)->uid) = value; -} - -/* Main entry point to the SSA builder. FNDECL is the gimplified function - to convert. VARS is an sbitmap representing variables that need to be - renamed into SSA form. A variable in REFERENCED_VARS will be renamed - into SSA if the element corresponding to the variable's UID is set in - VARS. If VARS is NULL, all variables in REFERENCED_VARS will be - processed. - - PHASE indicates which dump file from the DUMP_FILES array to use when - dumping debugging information. - - Every statement has two different sets of operands: real and virtual. - Real operands are those that use scalar, non-aliased variables. Virtual - operands are everything else (pointers, arrays, compound structures). - In addition, virtual operands are used to model side effects like - call-clobbered variables. - - The conversion process works in four phases: - - 1- Compute aliasing information (compute_may_aliases). - 2- Mark blocks that contain variable definitions (mark_def_sites). - 3- Insert PHI nodes (insert_phi_nodes). - 4- Rewrite all the basic blocks into SSA form (walk_dominator_tree). - - Both operands (OPS) and virtual operands (VOPS) are rewritten by the - conversion process. When new definitions are found, a new SSA_NAME - wrapper is created for the variable. SSA_NAMEs are akin to VAR_DECLs, - but contain additional information (defining statement and SSA version - number). - - The pretty printer renders SSA_NAME nodes using brackets and underscores - to highlight the variable and its version number. Given a variable VAR, - a new SSA_NAME for VAR with version number N is rendered VAR_N. For - instance, - - a = 4; a_1 = 4; - *p = a; *p_3 = a_1; - - - Dealing with aliases - -------------------- - - Assume that in the following code fragment, pointer p may point to b: - - 1 b = 4; - 2 *p = 5; - 3 ... = b; - - When converting this code to SSA form, we need to model the fact that - the use of 'b' at line 3 may be reached by the definition of *p at line - 2. We cannot just rewrite the use of 'b' to be a use of '*p': - - 1 b_1 = 4; - 2 *p_2 = 5; - 3 ... = *p_2; - - The above transformation is wrong, because p and b MAY alias, the - compiler doesn't know whether they will always alias at runtime. On the - other hand, ignoring the may alias relation also leads to an incorrect - transformation: - - 1 b_1 = 4; - 2 *p_2 = 5; - 3 ... = b_1; - - With this version, the optimizers will not notice that the assignment to - *p at line 2 may in fact reach the use of b at line 3. To deal with - this problem, the rewriting pass will move aliased operands into the - virtual operands and then re-write the virtual operand, while leaving - the original operand untouched. So, the above code fragment is converted - into: - - # MT.1_2 = VDEF <MT.1_1>; - 1 b = 4; - # MT.1_3 = VDEF <MT.1_2>; - 2 *p_3 = 5; - # VUSE <MT.1_3> - 3 ... = b; - - Notice how variable 'b' is not rewritten anymore. Instead, references - to it are moved to the virtual operands which are all rewritten using - MT.1, which is an artificial variable representing all the variables - aliased by 'p'. - - Virtual operands provide safe information about potential references to - the operands in a statement. But they are imprecise by nature. - Optimizations may want to take them into account, at the expense of - increased compilation time. */ - -void -rewrite_into_ssa (void) -{ - bitmap *dfs; - basic_block bb; - struct dom_walk_data walk_data; - struct mark_def_sites_global_data mark_def_sites_global_data; - - timevar_push (TV_TREE_SSA_OTHER); - - /* Initialize the array of variables to rename. */ - if (vars_to_rename != NULL) - { - size_t i; - bool rename_name_tags_p; - - /* If any of the variables in VARS_TO_RENAME is a pointer, we need to - invalidate all the name memory tags associated with the variables - that we are about to rename. FIXME: Currently we just invalidate - *all* the NMTs. Make this more selective. */ - rename_name_tags_p = false; - EXECUTE_IF_SET_IN_BITMAP (vars_to_rename, 0, i, - if (POINTER_TYPE_P (TREE_TYPE (referenced_var (i)))) - { - rename_name_tags_p = true; - break; - }); - - if (rename_name_tags_p) - for (i = 0; i < num_referenced_vars; i++) - { - var_ann_t ann = var_ann (referenced_var (i)); - - if (ann->mem_tag_kind == NAME_TAG) - bitmap_set_bit (vars_to_rename, ann->uid); - } - - /* Now remove all the existing PHI nodes (if any) for the variables - that we are about to rename into SSA. */ - remove_all_phi_nodes_for (vars_to_rename); - } - - /* Allocate memory for the DEF_BLOCKS hash table. */ - def_blocks = htab_create (VARRAY_ACTIVE_SIZE (referenced_vars), - def_blocks_hash, def_blocks_eq, NULL); - - VARRAY_TREE_INIT (currdefs, num_referenced_vars, "currdefs"); - - /* Initialize dominance frontier and immediate dominator bitmaps. - - Also count the number of predecessors for each block. Doing so - can save significant time during PHI insertion for large graphs. */ - dfs = (bitmap *) xmalloc (last_basic_block * sizeof (bitmap *)); - FOR_EACH_BB (bb) - { - edge e; - int count = 0; - - for (e = bb->pred; e; e = e->pred_next) - count++; - - bb_ann (bb)->num_preds = count; - dfs[bb->index] = BITMAP_XMALLOC (); - } - - /* Ensure that the dominance information is OK. */ - calculate_dominance_info (CDI_DOMINATORS); - - /* Compute dominance frontiers. */ - compute_dominance_frontiers (dfs); - - /* Setup callbacks for the generic dominator tree walker to find and - mark definition sites. */ - walk_data.walk_stmts_backward = false; - walk_data.dom_direction = CDI_DOMINATORS; - walk_data.initialize_block_local_data = NULL; - walk_data.before_dom_children_before_stmts = mark_def_sites_initialize_block; - walk_data.before_dom_children_walk_stmts = mark_def_sites; - walk_data.before_dom_children_after_stmts = NULL; - walk_data.after_dom_children_before_stmts = NULL; - walk_data.after_dom_children_walk_stmts = NULL; - walk_data.after_dom_children_after_stmts = NULL; - - /* 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 = sbitmap_alloc (num_referenced_vars); - walk_data.global_data = &mark_def_sites_global_data; - - /* We do not have any local data. */ - walk_data.block_local_data_size = 0; - - /* Initialize the dominator walker. */ - init_walk_dominator_tree (&walk_data); - - /* Recursively walk the dominator tree. */ - walk_dominator_tree (&walk_data, ENTRY_BLOCK_PTR); - - /* Finalize the dominator walker. */ - fini_walk_dominator_tree (&walk_data); - - /* We no longer need this bitmap, clear and free it. */ - sbitmap_free (mark_def_sites_global_data.kills); - - /* Insert PHI nodes at dominance frontiers of definition blocks. */ - insert_phi_nodes (dfs); - - /* Rewrite all the basic blocks in the program. */ - timevar_push (TV_TREE_SSA_REWRITE_BLOCKS); - - /* Setup callbacks for the generic dominator tree walker. */ - walk_data.walk_stmts_backward = false; - walk_data.dom_direction = CDI_DOMINATORS; - walk_data.initialize_block_local_data = rewrite_initialize_block_local_data; - walk_data.before_dom_children_before_stmts = rewrite_initialize_block; - walk_data.before_dom_children_walk_stmts = rewrite_stmt; - walk_data.before_dom_children_after_stmts = rewrite_add_phi_arguments; - walk_data.after_dom_children_before_stmts = NULL; - walk_data.after_dom_children_walk_stmts = NULL; - walk_data.after_dom_children_after_stmts = rewrite_finalize_block; - walk_data.global_data = NULL; - walk_data.block_local_data_size = sizeof (struct rewrite_block_data); - - /* 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_BLOCK_PTR); - - /* Finalize the dominator walker. */ - fini_walk_dominator_tree (&walk_data); - - timevar_pop (TV_TREE_SSA_REWRITE_BLOCKS); - - /* Debugging dumps. */ - if (dump_file && (dump_flags & TDF_STATS)) - { - dump_dfa_stats (dump_file); - dump_tree_ssa_stats (dump_file); - } - - /* Free allocated memory. */ - FOR_EACH_BB (bb) - BITMAP_XFREE (dfs[bb->index]); - free (dfs); - - htab_delete (def_blocks); - VARRAY_CLEAR (currdefs); - - timevar_pop (TV_TREE_SSA_OTHER); -} - -struct tree_opt_pass pass_build_ssa = -{ - "ssa", /* name */ - NULL, /* gate */ - rewrite_into_ssa, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - 0, /* tv_id */ - PROP_cfg | PROP_referenced_vars, /* properties_required */ - PROP_ssa, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_dump_func | TODO_verify_ssa /* todo_flags_finish */ -}; - - -/* Compute global livein information given the set of blockx where - an object is locally live at the start of the block (LIVEIN) - and the set of blocks where the object is defined (DEF_BLOCKS). - - Note: This routine augments the existing local livein information - to include global livein. Ie, it modifies the underlying bitmap - for LIVEIN. */ - -static void -compute_global_livein (bitmap livein, bitmap def_blocks) -{ - basic_block bb, *worklist, *tos; - - tos = worklist - = (basic_block *) xmalloc (sizeof (basic_block) * (last_basic_block + 1)); - - /* Initialize the worklist. */ - FOR_EACH_BB (bb) - { - if (bitmap_bit_p (livein, bb->index)) - *tos++ = bb; - } - - /* Iterate until the worklist is empty. */ - while (tos != worklist) - { - edge e; - - /* Pull a block off the worklist. */ - bb = *--tos; - - /* For each predecessor block. */ - for (e = bb->pred; e; e = e->pred_next) - { - basic_block pred = e->src; - int pred_index = pred->index; - - /* None of this is necessary for the entry block. */ - if (pred != ENTRY_BLOCK_PTR - && ! bitmap_bit_p (livein, pred_index) - && ! bitmap_bit_p (def_blocks, pred_index)) - { - *tos++ = pred; - bitmap_set_bit (livein, pred_index); - } - } - } - - free (worklist); -} - -/* Block initialization routine for mark_def_sites. Clear the - KILLS bitmap at the start of each block. */ -static void -mark_def_sites_initialize_block (struct dom_walk_data *walk_data, - basic_block bb ATTRIBUTE_UNUSED) -{ - struct mark_def_sites_global_data *gd = walk_data->global_data; - sbitmap kills = gd->kills; - - sbitmap_zero (kills); -} - -/* Collect definition sites for every variable in the function. - Return a bitmap for the set of referenced variables which are - "nonlocal", ie those which are live across block boundaries. - This information is used to reduce the number of PHI nodes - we create. */ - -static void -mark_def_sites (struct dom_walk_data *walk_data, - basic_block bb, - block_stmt_iterator bsi) -{ - struct mark_def_sites_global_data *gd = walk_data->global_data; - sbitmap kills = gd->kills; - vdef_optype vdefs; - vuse_optype vuses; - def_optype defs; - use_optype uses; - size_t i, uid; - tree stmt; - stmt_ann_t ann; - - /* Mark all the blocks that have definitions for each variable in the - VARS_TO_RENAME bitmap. */ - stmt = bsi_stmt (bsi); - get_stmt_operands (stmt); - ann = stmt_ann (stmt); - - /* If a variable is used before being set, then the variable - is live across a block boundary, so add it to NONLOCAL_VARS. */ - uses = USE_OPS (ann); - for (i = 0; i < NUM_USES (uses); i++) - { - tree *use_p = USE_OP_PTR (uses, i); - - if (prepare_operand_for_rename (use_p, &uid) - && !TEST_BIT (kills, uid)) - set_livein_block (*use_p, bb); - } - - /* Similarly for virtual uses. */ - vuses = VUSE_OPS (ann); - for (i = 0; i < NUM_VUSES (vuses); i++) - { - tree *use_p = VUSE_OP_PTR (vuses, i); - - if (prepare_operand_for_rename (use_p, &uid) - && !TEST_BIT (kills, uid)) - set_livein_block (*use_p, bb); - } - - /* Note that virtual definitions are irrelevant for computing - KILLED_VARS because a VDEF does not constitute a killing - definition of the variable. However, the operand of a virtual - definitions is a use of the variable, so it may affect - GLOBALS. */ - vdefs = VDEF_OPS (ann); - for (i = 0; i < NUM_VDEFS (vdefs); i++) - { - size_t dummy; - - if (prepare_operand_for_rename (VDEF_OP_PTR (vdefs, i), &uid) - && prepare_operand_for_rename (VDEF_RESULT_PTR (vdefs, i), - &dummy)) - { - VDEF_RESULT (vdefs, i) = VDEF_OP (vdefs, i); - - if (!TEST_BIT (kills, uid)) - set_livein_block (VDEF_OP (vdefs, i), bb); - set_def_block (VDEF_RESULT (vdefs, i), bb); - } - } - - /* Now process the definition made by this statement. */ - defs = DEF_OPS (ann); - for (i = 0; i < NUM_DEFS (defs); i++) - { - tree *def_p = DEF_OP_PTR (defs, i); - - if (prepare_operand_for_rename (def_p, &uid)) - { - set_def_block (*def_p, bb); - SET_BIT (kills, uid); - } - } -} - -/* Mark block BB as the definition site for variable VAR. */ - -static void -set_def_block (tree var, basic_block bb) -{ - struct def_blocks_d db, *db_p; - void **slot; - enum need_phi_state state = var_ann (var)->need_phi_state; - - /* Find the DEFS bitmap associated with variable VAR. */ - db.var = var; - slot = htab_find_slot (def_blocks, (void *) &db, INSERT); - if (*slot == NULL) - { - db_p = ggc_alloc (sizeof (*db_p)); - db_p->var = var; - db_p->def_blocks = BITMAP_GGC_ALLOC (); - db_p->livein_blocks = BITMAP_GGC_ALLOC (); - *slot = (void *) db_p; - } - else - db_p = (struct def_blocks_d *) *slot; - - /* Set the bit corresponding to the block where VAR is defined. */ - bitmap_set_bit (db_p->def_blocks, bb->index); - - /* Keep track of whether or not we may need to insert phi nodes. - - If we are in the UNKNOWN state, then this is the first definition - of VAR. Additionally, we have not seen any uses of VAR yet, so we - do not need a phi node for this variable at this time (ie, transition - to NEED_PHI_STATE_NO). - - If we are in any other state, then we either have multiple definitions - of this variable occurring in different blocks or we saw a use of the - variable which was not dominated by the block containing the - definition(s). In this case we may need a PHI node, so enter - state NEED_PHI_STATE_MAYBE. */ - if (state == NEED_PHI_STATE_UNKNOWN) - var_ann (var)->need_phi_state = NEED_PHI_STATE_NO; - else - var_ann (var)->need_phi_state = NEED_PHI_STATE_MAYBE; -} - -/* Mark block BB as having VAR live at the entry to BB. */ - -static void -set_livein_block (tree var, basic_block bb) -{ - struct def_blocks_d db, *db_p; - void **slot; - enum need_phi_state state = var_ann (var)->need_phi_state; - - /* Find the DEFS bitmap associated with variable VAR. */ - db.var = var; - slot = htab_find_slot (def_blocks, (void *) &db, INSERT); - if (*slot == NULL) - { - db_p = ggc_alloc (sizeof (*db_p)); - db_p->var = var; - db_p->def_blocks = BITMAP_GGC_ALLOC (); - db_p->livein_blocks = BITMAP_GGC_ALLOC (); - *slot = (void *) db_p; - } - else - db_p = (struct def_blocks_d *) *slot; - - /* Set the bit corresponding to the block where VAR is live in. */ - bitmap_set_bit (db_p->livein_blocks, bb->index); - - /* Keep track of whether or not we may need to insert phi nodes. - - If we reach here in NEED_PHI_STATE_NO, see if this use is dominated - by the single block containing the definition(s) of this variable. If - it is, then we remain in NEED_PHI_STATE_NO, otherwise we transition to - NEED_PHI_STATE_MAYBE. */ - if (state == NEED_PHI_STATE_NO) - { - int def_block_index = bitmap_first_set_bit (db_p->def_blocks); - - if (def_block_index == -1 - || ! dominated_by_p (CDI_DOMINATORS, bb, BASIC_BLOCK (def_block_index))) - var_ann (var)->need_phi_state = NEED_PHI_STATE_MAYBE; - } - else - var_ann (var)->need_phi_state = NEED_PHI_STATE_MAYBE; -} - - -/* If the operand pointed by OP_P needs to be renamed, strip away SSA_NAME - wrappers (if needed) and return true. The unique ID for the operand's - variable will be stored in *UID_P. */ - -static bool -prepare_operand_for_rename (tree *op_p, size_t *uid_p) -{ - tree var = (TREE_CODE (*op_p) != SSA_NAME) ? *op_p : SSA_NAME_VAR (*op_p); - *uid_p = var_ann (var)->uid; - - /* Ignore variables that don't need to be renamed. */ - if (vars_to_rename && !bitmap_bit_p (vars_to_rename, *uid_p)) - return false; - - /* The variable needs to be renamed. If it already had an - SSA_NAME, strip it off. This way, the SSA rename pass - doesn't need to deal with existing SSA names. */ - if (TREE_CODE (*op_p) == SSA_NAME) - { - if (default_def (SSA_NAME_VAR (*op_p)) != *op_p) - release_ssa_name (*op_p); - *op_p = var; - } - - return true; -} - -/* Insert PHI nodes at the dominance frontier of blocks with variable - definitions. DFS contains the dominance frontier information for the - flowgraph. - - GLOBALS is a bitmap representing the set of variables which are live - across basic block boundaries. Only variables in GLOBALS need PHI - nodes. */ - -static void -insert_phi_nodes (bitmap *dfs) -{ - size_t i; - - timevar_push (TV_TREE_INSERT_PHI_NODES); - - /* Array WORK_STACK is a stack of CFG blocks. Each block that contains - an assignment or PHI node will be pushed to this stack. */ - VARRAY_BB_INIT (work_stack, last_basic_block, "work_stack"); - - /* Iterate over all variables in VARS_TO_RENAME. For each variable, add - to the work list all the blocks that have a definition for the - variable. PHI nodes will be added to the dominance frontier blocks of - each definition block. */ - if (vars_to_rename) - EXECUTE_IF_SET_IN_BITMAP (vars_to_rename, 0, i, - { - tree var = referenced_var (i); - var_ann_t ann = var_ann (var); - - if (ann->need_phi_state != NEED_PHI_STATE_NO) - insert_phi_nodes_for (var, dfs); - }); - else - for (i = 0; i < num_referenced_vars; i++) - { - tree var = referenced_var (i); - var_ann_t ann = var_ann (var); - - if (ann->need_phi_state != NEED_PHI_STATE_NO) - insert_phi_nodes_for (var, dfs); - } - - work_stack = NULL; - timevar_pop (TV_TREE_INSERT_PHI_NODES); -} - - -/* Perform a depth-first traversal of the dominator tree looking for - variables to rename. BB is the block where to start searching. - Renaming is a five step process: - - 1- Every definition made by PHI nodes at the start of the blocks is - registered as the current definition for the corresponding variable. - - 2- Every statement in BB is rewritten. USE and VUSE operands are - rewritten with their corresponding reaching definition. DEF and - VDEF targets are registered as new definitions. - - 3- All the PHI nodes in successor blocks of BB are visited. The - argument corresponding to BB is replaced with its current reaching - definition. - - 4- Recursively rewrite every dominator child block of BB. - - 5- Restore (in reverse order) the current reaching definition for every - new definition introduced in this block. This is done so that when - we return from the recursive call, all the current reaching - definitions are restored to the names that were valid in the - dominator parent of BB. */ - -/* Initialize the local stacks. - - BLOCK_DEFS is used to save all the existing reaching definitions for - the new SSA names introduced in this block. Before registering a - new definition for a variable, the existing reaching definition is - pushed into this stack so that we can restore it in Step 5. */ - -static void -rewrite_initialize_block_local_data (struct dom_walk_data *walk_data, - basic_block bb ATTRIBUTE_UNUSED, - bool recycled) -{ - struct rewrite_block_data *bd - = (struct rewrite_block_data *)VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack); - - /* We get cleared memory from the allocator, so if the memory is - not cleared, then we are re-using a previously allocated entry. In - that case, we can also re-use the underlying virtal arrays. Just - make sure we clear them before using them! */ - if (recycled && bd->block_defs) - VARRAY_CLEAR (bd->block_defs); -} - -/* 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_initialize_block (struct dom_walk_data *walk_data, basic_block bb) -{ - tree phi; - struct rewrite_block_data *bd - = (struct rewrite_block_data *)VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack); - - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "\n\nRenaming block #%d\n\n", bb->index); - - /* 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 (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) - { - tree result = PHI_RESULT (phi); - - register_new_def (SSA_NAME_VAR (result), result, - &bd->block_defs, currdefs); - } -} - -/* SSA Rewriting Step 3. Visit all the successor blocks of BB looking for - PHI nodes. For every PHI node found, add a new argument containing the - current reaching definition for the variable and the edge through which - that definition is reaching the PHI node. */ - -static void -rewrite_add_phi_arguments (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED, - basic_block bb) -{ - edge e; - - for (e = bb->succ; e; e = e->succ_next) - { - tree phi; - - for (phi = phi_nodes (e->dest); phi; phi = TREE_CHAIN (phi)) - { - tree currdef; - - /* If this PHI node has already been rewritten, then there is - nothing to do for this PHI or any following PHIs since we - always add new PHI nodes at the start of the PHI chain. */ - if (PHI_REWRITTEN (phi)) - break; - - currdef = get_reaching_def (SSA_NAME_VAR (PHI_RESULT (phi))); - add_phi_arg (&phi, currdef, e); - } - } -} - -/* SSA Rewriting Step 5. Restore the current reaching definition for each - variable referenced in the block (in reverse order). */ - -static void -rewrite_finalize_block (struct dom_walk_data *walk_data, - basic_block bb ATTRIBUTE_UNUSED) -{ - struct rewrite_block_data *bd - = (struct rewrite_block_data *)VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack); - - /* Step 5. Restore the current reaching definition for each variable - referenced in the block (in reverse order). */ - while (bd->block_defs && VARRAY_ACTIVE_SIZE (bd->block_defs) > 0) - { - tree var; - tree saved_def = VARRAY_TOP_TREE (bd->block_defs); - VARRAY_POP (bd->block_defs); - - /* If SAVED_DEF is NULL, then the next slot in the stack contains the - variable associated with SAVED_DEF. */ - if (saved_def == NULL_TREE) - { - var = VARRAY_TOP_TREE (bd->block_defs); - VARRAY_POP (bd->block_defs); - } - else - var = SSA_NAME_VAR (saved_def); - - set_value_for (var, saved_def, currdefs); - } -} - -/* This function will create a temporary for a partition based on the - type of the variable which already represents a partition. */ - -static tree -create_temp (tree t) -{ - tree tmp; - const char *name = NULL; - tree type; - - if (TREE_CODE (t) == SSA_NAME) - t = SSA_NAME_VAR (t); - - if (TREE_CODE (t) != VAR_DECL - && TREE_CODE (t) != PARM_DECL) - abort (); - - type = TREE_TYPE (t); - tmp = DECL_NAME (t); - if (tmp) - name = IDENTIFIER_POINTER (tmp); - - if (name == NULL) - name = "temp"; - tmp = create_tmp_var (type, name); - DECL_ARTIFICIAL (tmp) = DECL_ARTIFICIAL (t); - add_referenced_tmp_var (tmp); - - /* add_referenced_tmp_var will create the annotation and set up some - of the flags in the annotation. However, some flags we need to - inherit from our original variable. */ - var_ann (tmp)->type_mem_tag = var_ann (t)->type_mem_tag; - if (is_call_clobbered (t)) - mark_call_clobbered (tmp); - - return tmp; -} - -/* This helper function fill insert a copy from a constant or a variable to - a variable on the specified edge. */ - -static void -insert_copy_on_edge (edge e, tree dest, tree src) -{ - tree copy; - - copy = build (MODIFY_EXPR, TREE_TYPE (dest), dest, src); - set_is_used (dest); - - if (TREE_CODE (src) == ADDR_EXPR) - src = TREE_OPERAND (src, 0); - if (TREE_CODE (src) == VAR_DECL || TREE_CODE (src) == PARM_DECL) - set_is_used (src); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, - "Inserting a copy on edge BB%d->BB%d :", - e->src->index, - e->dest->index); - print_generic_expr (dump_file, copy, dump_flags); - fprintf (dump_file, "\n"); - } - - bsi_insert_on_edge (e, copy); -} - -/* --------------------------------------------------------------------- */ -/* Create an elimination graph and associated data structures. */ - -static elim_graph -new_elim_graph (int size) -{ - elim_graph g = (elim_graph) xmalloc (sizeof (struct _elim_graph)); - - VARRAY_TREE_INIT (g->nodes, 30, "Elimination Node List"); - VARRAY_TREE_INIT (g->const_copies, 20, "Elimination Constant Copies"); - VARRAY_INT_INIT (g->edge_list, 20, "Elimination Edge List"); - VARRAY_INT_INIT (g->stack, 30, " Elimination Stack"); - - g->visited = sbitmap_alloc (size); - - return g; -} - - -/* Empty the elimination graph. */ -static inline void -clear_elim_graph (elim_graph g) -{ - VARRAY_POP_ALL (g->nodes); - VARRAY_POP_ALL (g->edge_list); -} - - -/* Delete an elimination graph. */ -static inline void -delete_elim_graph (elim_graph g) -{ - sbitmap_free (g->visited); - free (g); -} - - -/* Return the number of nodes in the graph. */ -static inline int -elim_graph_size (elim_graph g) -{ - return VARRAY_ACTIVE_SIZE (g->nodes); -} - - -/* Add a node to the graph, if it doesn't exist already. */ -static inline void -elim_graph_add_node (elim_graph g, tree node) -{ - int x; - for (x = 0; x < elim_graph_size (g); x++) - if (VARRAY_TREE (g->nodes, x) == node) - return; - VARRAY_PUSH_TREE (g->nodes, node); -} - - -/* Add an edge to the graph. */ -static inline void -elim_graph_add_edge (elim_graph g, int pred, int succ) -{ - VARRAY_PUSH_INT (g->edge_list, pred); - VARRAY_PUSH_INT (g->edge_list, succ); -} - - -/* Remove an edge from the graph for which node is the predecessor, and - return the successor node. -1 is returned if there is no such edge. */ -static inline int -elim_graph_remove_succ_edge (elim_graph g, int node) -{ - int y; - unsigned x; - for (x = 0; x < VARRAY_ACTIVE_SIZE (g->edge_list); x += 2) - if (VARRAY_INT (g->edge_list, x) == node) - { - VARRAY_INT (g->edge_list, x) = -1; - y = VARRAY_INT (g->edge_list, x + 1); - VARRAY_INT (g->edge_list, x + 1) = -1; - return y; - } - return -1; -} - -/* Find all the nodes which are successors to NODE in the edge list. */ -#define FOR_EACH_ELIM_GRAPH_SUCC(GRAPH, NODE, VAR, CODE) \ -do { \ - unsigned x_; \ - int y_; \ - for (x_ = 0; x_ < VARRAY_ACTIVE_SIZE ((GRAPH)->edge_list); x_ += 2) \ - { \ - y_ = VARRAY_INT ((GRAPH)->edge_list, x_); \ - if (y_ != (NODE)) \ - continue; \ - (VAR) = VARRAY_INT ((GRAPH)->edge_list, x_ + 1); \ - CODE; \ - } \ -} while (0) - -/* Find all the nodes which are predecessors of NODE in the edge list. */ -#define FOR_EACH_ELIM_GRAPH_PRED(GRAPH, NODE, VAR, CODE) \ -do { \ - unsigned x_; \ - int y_; \ - for (x_ = 0; x_ < VARRAY_ACTIVE_SIZE ((GRAPH)->edge_list); x_ += 2) \ - { \ - y_ = VARRAY_INT ((GRAPH)->edge_list, x_ + 1); \ - if (y_ != (NODE)) \ - continue; \ - (VAR) = VARRAY_INT ((GRAPH)->edge_list, x_); \ - CODE; \ - } \ -} while (0) - -/* The following procedures implement the out of SSA algorithm detailed in - the Morgan Book pages 176-186. */ - - -/* Add T to the elimination graph. */ - -static inline void -eliminate_name (elim_graph g, tree T) -{ - elim_graph_add_node (g, T); -} - -/* Build the auxiliary graph. */ - -static void -eliminate_build (elim_graph g, basic_block B, int i) -{ - tree phi; - tree T0, Ti; - int p0, pi; - - clear_elim_graph (g); - - for (phi = phi_nodes (B); phi; phi = TREE_CHAIN (phi)) - { - T0 = var_to_partition_to_var (g->map, PHI_RESULT (phi)); - /* Ignore results which are not in partitions. */ - if (T0 == NULL_TREE) - { -#ifdef ENABLE_CHECKING - /* There should be no arguments of this PHI which are in - the partition list, or we get incorrect results. */ - for (pi = 0; pi < PHI_NUM_ARGS (phi); pi++) - { - tree arg = PHI_ARG_DEF (phi, pi); - if (TREE_CODE (arg) == SSA_NAME - && var_to_partition (g->map, arg) != NO_PARTITION) - { - fprintf (stderr, "Argument of PHI is in a partition :("); - print_generic_expr (stderr, arg, TDF_SLIM); - fprintf (stderr, "), but the result is not :"); - print_generic_stmt (stderr, phi, TDF_SLIM); - abort(); - } - } -#endif - continue; - } - if (PHI_ARG_EDGE (phi, i) == g->e) - Ti = PHI_ARG_DEF (phi, i); - else - { - /* On rare occasions, a PHI node may not have the arguments - in the same order as all of the other PHI nodes. If they don't - match, find the appropriate index here. */ - pi = phi_arg_from_edge (phi, g->e); - if (pi == -1) - abort(); - Ti = PHI_ARG_DEF (phi, pi); - } - - /* If this argument is a constant, or a SSA_NAME which is being - left in SSA form, just queue a copy to be emitted on this - edge. */ - if (!phi_ssa_name_p (Ti) - || (TREE_CODE (Ti) == SSA_NAME - && var_to_partition (g->map, Ti) == NO_PARTITION)) - { - /* Save constant copies until all other copies have been emitted - on this edge. */ - VARRAY_PUSH_TREE (g->const_copies, T0); - VARRAY_PUSH_TREE (g->const_copies, Ti); - } - else - { - Ti = var_to_partition_to_var (g->map, Ti); - if (T0 != Ti) - { - eliminate_name (g, T0); - eliminate_name (g, Ti); - p0 = var_to_partition (g->map, T0); - pi = var_to_partition (g->map, Ti); - elim_graph_add_edge (g, p0, pi); - } - } - } -} - -/* Push successors onto the stack depth first. */ - -static void -elim_forward (elim_graph g, int T) -{ - int S; - SET_BIT (g->visited, T); - FOR_EACH_ELIM_GRAPH_SUCC (g, T, S, - { - if (!TEST_BIT (g->visited, S)) - elim_forward (g, S); - }); - VARRAY_PUSH_INT (g->stack, T); -} - - -/* Are there unvisited predecessors? */ - -static int -elim_unvisited_predecessor (elim_graph g, int T) -{ - int P; - FOR_EACH_ELIM_GRAPH_PRED (g, T, P, - { - if (!TEST_BIT (g->visited, P)) - return 1; - }); - return 0; -} - -/* Process predecessors first, and insert a copy. */ - -static void -elim_backward (elim_graph g, int T) -{ - int P; - SET_BIT (g->visited, T); - FOR_EACH_ELIM_GRAPH_PRED (g, T, P, - { - if (!TEST_BIT (g->visited, P)) - { - elim_backward (g, P); - insert_copy_on_edge (g->e, - partition_to_var (g->map, P), - partition_to_var (g->map, T)); - } - }); -} - -/* Check for a SCR, and create a temporary if there is one, and break - the cycle. Then issue the copies. Otherwise, simply insert the - required copies. */ - -static void -elim_create (elim_graph g, int T) -{ - tree U; - int P, S; - - if (elim_unvisited_predecessor (g, T)) - { - U = create_temp (partition_to_var (g->map, T)); - insert_copy_on_edge (g->e, U, partition_to_var (g->map, T)); - FOR_EACH_ELIM_GRAPH_PRED (g, T, P, - { - if (!TEST_BIT (g->visited, P)) - { - elim_backward (g, P); - insert_copy_on_edge (g->e, partition_to_var (g->map, P), U); - } - }); - } - else - { - S = elim_graph_remove_succ_edge (g, T); - if (S != -1) - { - SET_BIT (g->visited, T); - insert_copy_on_edge (g->e, - partition_to_var (g->map, T), - partition_to_var (g->map, S)); - } - } - -} - -/* Eliminate all the phi nodes on this edge. */ - -static void -eliminate_phi (edge e, int i, elim_graph g) -{ - int num_nodes = 0; - int x; - basic_block B = e->dest; - -#if defined ENABLE_CHECKING - if (i == -1) - abort (); - if (VARRAY_ACTIVE_SIZE (g->const_copies) != 0) - abort (); -#endif - - /* Abnormal edges already have everything coalesced, or the coalescer - would have aborted. */ - if (e->flags & EDGE_ABNORMAL) - return; - - num_nodes = num_var_partitions (g->map); - g->e = e; - - eliminate_build (g, B, i); - - if (elim_graph_size (g) != 0) - { - sbitmap_zero (g->visited); - VARRAY_POP_ALL (g->stack); - - for (x = 0; x < elim_graph_size (g); x++) - { - tree var = VARRAY_TREE (g->nodes, x); - int p = var_to_partition (g->map, var); - if (!TEST_BIT (g->visited, p)) - elim_forward (g, p); - } - - sbitmap_zero (g->visited); - while (VARRAY_ACTIVE_SIZE (g->stack) > 0) - { - x = VARRAY_TOP_INT (g->stack); - VARRAY_POP (g->stack); - if (!TEST_BIT (g->visited, x)) - elim_create (g, x); - } - } - - /* If there are any pending constant copies, issue them now. */ - while (VARRAY_ACTIVE_SIZE (g->const_copies) > 0) - { - tree src, dest; - src = VARRAY_TOP_TREE (g->const_copies); - VARRAY_POP (g->const_copies); - dest = VARRAY_TOP_TREE (g->const_copies); - VARRAY_POP (g->const_copies); - insert_copy_on_edge (e, dest, src); - } -} - - -/* Shortcut routine to print messages of the form: "str expr str expr str." */ - -static void -print_exprs (FILE *f, const char *str1, tree expr1, const char *str2, - tree expr2, const char *str3) -{ - fprintf (f, "%s", str1); - print_generic_expr (f, expr1, TDF_SLIM); - fprintf (f, "%s", str2); - print_generic_expr (f, expr2, TDF_SLIM); - fprintf (f, "%s", str3); -} - -static void -print_exprs_edge (FILE *f, edge e, const char *str1, tree expr1, - const char *str2, tree expr2) -{ - print_exprs (f, str1, expr1, str2, expr2, " across an abnormal edge"); - fprintf (f, " from BB%d->BB%d\n", e->src->index, - e->dest->index); -} - -/* Coalesce partitions which are live across abnormal edges. Since code - cannot be inserted on these edges, failure to coalesce something across - an abnormal edge is a non-compilable situation. */ - -static void -coalesce_abnormal_edges (var_map map, conflict_graph graph, root_var_p rv) -{ - basic_block bb; - edge e; - tree phi, var, tmp; - int x, y; - - /* Code cannot be inserted on abnormal edges. Look for all abnormal - edges, and coalesce any PHI results with their arguments across - that edge. */ - - FOR_EACH_BB (bb) - for (e = bb->succ; e; e = e->succ_next) - if (e->dest != EXIT_BLOCK_PTR && e->flags & EDGE_ABNORMAL) - for (phi = phi_nodes (e->dest); phi; phi = TREE_CHAIN (phi)) - { - /* Visit each PHI on the destination side of this abnormal - edge, and attempt to coalesce the argument with the result. */ - var = PHI_RESULT (phi); - x = var_to_partition (map, var); - - /* Ignore results which are not relevant. */ - if (x == NO_PARTITION) - continue; - - y = phi_arg_from_edge (phi, e); - if (y == -1) - abort (); - - tmp = PHI_ARG_DEF (phi, y); - if (!phi_ssa_name_p (tmp)) - { - print_exprs_edge (stderr, e, - "\nConstant argument in PHI. Can't insert :", - var, " = ", tmp); - abort (); - } - y = var_to_partition (map, tmp); - if (x == NO_PARTITION || y == NO_PARTITION) - abort (); - if (root_var_find (rv, x) != root_var_find (rv, y)) - { - print_exprs_edge (stderr, e, "\nDifferent root vars: ", - root_var (rv, root_var_find (rv, x)), - " and ", root_var (rv, root_var_find (rv, y))); - abort (); - } - - if (x != y) - { - if (!conflict_graph_conflict_p (graph, x, y)) - { - /* Now map the partitions back to their real variables. */ - var = partition_to_var (map, x); - tmp = partition_to_var (map, y); - if (dump_file - && (dump_flags & TDF_DETAILS)) - { - print_exprs_edge (dump_file, e, "ABNORMAL: Coalescing ", - var, " and ", tmp); - } - if (var_union (map, var, tmp) == NO_PARTITION) - { - print_exprs_edge (stderr, e, "\nUnable to coalesce", - partition_to_var (map, x), " and ", - partition_to_var (map, y)); - abort (); - } - conflict_graph_merge_regs (graph, x, y); - } - else - { - print_exprs_edge (stderr, e, "\n Conflict ", - partition_to_var (map, x), - " and ", partition_to_var (map, y)); - abort (); - } - } - } -} - - -/* Reduce the number of live ranges in the var_map. The only partitions - which are associated with actual variables at this point are those which - are forced to be coalesced for various reason. (live on entry, live - across abnormal edges, etc.). - Live range information is returned if FLAGS indicates that we are - combining temporaries, otherwise NULL is returned. */ - -static tree_live_info_p -coalesce_ssa_name (var_map map, int flags) -{ - int num, x, i; - sbitmap live; - tree var, phi; - root_var_p rv; - tree_live_info_p liveinfo; - var_ann_t ann; - conflict_graph graph; - basic_block bb; - coalesce_list_p cl = NULL; - - if (num_var_partitions (map) <= 1) - return NULL; - - /* If no preference given, use cheap coalescing of all partitions. */ - if ((flags & (SSANORM_COALESCE_PARTITIONS | SSANORM_USE_COALESCE_LIST)) == 0) - flags |= SSANORM_COALESCE_PARTITIONS; - - liveinfo = calculate_live_on_entry (map); - calculate_live_on_exit (liveinfo); - rv = root_var_init (map); - - /* Remove single element variable from the list. */ - root_var_compact (rv); - - if (flags & SSANORM_USE_COALESCE_LIST) - { - cl = create_coalesce_list (map); - - /* Add all potential copies via PHI arguments to the list. */ - FOR_EACH_BB (bb) - { - for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) - { - tree res = PHI_RESULT (phi); - int p = var_to_partition (map, res); - if (p == NO_PARTITION) - continue; - for (x = 0; x < PHI_NUM_ARGS (phi); x++) - { - tree arg = PHI_ARG_DEF (phi, x); - int p2; - - if (TREE_CODE (arg) != SSA_NAME) - continue; - if (SSA_NAME_VAR (res) != SSA_NAME_VAR (arg)) - continue; - p2 = var_to_partition (map, PHI_ARG_DEF (phi, x)); - if (p2 != NO_PARTITION) - add_coalesce (cl, p, p2, 1); - } - } - } - - /* Coalesce all the result decls together. */ - var = NULL_TREE; - i = 0; - for (x = 0; x < num_var_partitions (map); x++) - { - tree p = partition_to_var (map, x); - if (TREE_CODE (SSA_NAME_VAR(p)) == RESULT_DECL) - { - if (var == NULL_TREE) - { - var = p; - i = x; - } - else - add_coalesce (cl, i, x, 1); - } - } - } - - /* Build a conflict graph. */ - graph = build_tree_conflict_graph (liveinfo, rv, cl); - - if (cl) - { - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "Before sorting:\n"); - dump_coalesce_list (dump_file, cl); - } - - sort_coalesce_list (cl); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "\nAfter sorting:\n"); - dump_coalesce_list (dump_file, cl); - } - } - - /* Put the single element variables back in. */ - root_var_decompact (rv); - - /* First, coalesce all live on entry variables to their root variable. - This will ensure the first use is coming from the correct location. */ - - live = sbitmap_alloc (num_var_partitions (map)); - sbitmap_zero (live); - - /* Set 'live' vector to indicate live on entry partitions. */ - num = num_var_partitions (map); - for (x = 0 ; x < num; x++) - { - var = partition_to_var (map, x); - if (default_def (SSA_NAME_VAR (var)) == var) - SET_BIT (live, x); - } - - if ((flags & SSANORM_COMBINE_TEMPS) == 0) - { - delete_tree_live_info (liveinfo); - liveinfo = NULL; - } - - /* Assign root variable as partition representative for each live on entry - partition. */ - EXECUTE_IF_SET_IN_SBITMAP (live, 0, x, - { - var = root_var (rv, root_var_find (rv, x)); - ann = var_ann (var); - /* If these aren't already coalesced... */ - if (partition_to_var (map, x) != var) - { - if (ann->out_of_ssa_tag) - { - /* This root variable has already been assigned to another - partition which is not coalesced with this one. */ - abort (); - } - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - print_exprs (dump_file, "Must coalesce ", - partition_to_var (map, x), - " with the root variable ", var, ".\n"); - } - - change_partition_var (map, var, x); - } - }); - - sbitmap_free (live); - - /* Coalesce partitions live across abnormal edges. */ - coalesce_abnormal_edges (map, graph, rv); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - dump_var_map (dump_file, map); - } - - /* Coalesce partitions. */ - if (flags & SSANORM_USE_COALESCE_LIST) - coalesce_tpa_members (rv, graph, map, cl, - ((dump_flags & TDF_DETAILS) ? dump_file - : NULL)); - - - if (flags & SSANORM_COALESCE_PARTITIONS) - coalesce_tpa_members (rv, graph, map, NULL, - ((dump_flags & TDF_DETAILS) ? dump_file - : NULL)); - if (cl) - delete_coalesce_list (cl); - root_var_delete (rv); - conflict_graph_delete (graph); - - return liveinfo; -} - -/* Take the ssa-name var_map, and assign real variables to each partition. */ - -static void -assign_vars (var_map map) -{ - int x, i, num, rep; - tree t, var; - var_ann_t ann; - root_var_p rv; - - rv = root_var_init (map); - if (!rv) - return; - - /* Coalescing may already have forced some partitions to their root - variable. Find these and tag them. */ - - num = num_var_partitions (map); - for (x = 0; x < num; x++) - { - var = partition_to_var (map, x); - if (TREE_CODE (var) != SSA_NAME) - { - /* Coalescing will already have verified that more than one - partition doesn't have the same root variable. Simply marked - the variable as assigned. */ - ann = var_ann (var); - ann->out_of_ssa_tag = 1; - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "partition %d has variable ", x); - print_generic_expr (dump_file, var, TDF_SLIM); - fprintf (dump_file, " assigned to it.\n"); - } - - } - } - - num = root_var_num (rv); - for (x = 0; x < num; x++) - { - var = root_var (rv, x); - ann = var_ann (var); - for (i = root_var_first_partition (rv, x); - i != ROOT_VAR_NONE; - i = root_var_next_partition (rv, i)) - { - t = partition_to_var (map, i); - - if (t == var || TREE_CODE (t) != SSA_NAME) - continue; - - rep = var_to_partition (map, t); - - if (!ann->out_of_ssa_tag) - { - if (dump_file && (dump_flags & TDF_DETAILS)) - print_exprs (dump_file, "", t, " --> ", var, "\n"); - change_partition_var (map, var, rep); - continue; - } - - if (dump_file && (dump_flags & TDF_DETAILS)) - print_exprs (dump_file, "", t, " not coalesced with ", var, - ""); - - var = create_temp (t); - change_partition_var (map, var, rep); - ann = var_ann (var); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, " --> New temp: '"); - print_generic_expr (dump_file, var, TDF_SLIM); - fprintf (dump_file, "'\n"); - } - } - } - - root_var_delete (rv); -} - -/* Replace *p with whatever variable it has been rewritten to. If it changes - the stmt, return true. */ - -static inline bool -replace_variable (var_map map, tree *p, tree *expr) -{ - tree new_var; - tree var = *p; - - /* Check if we are replacing this variable with an expression. */ - if (expr) - { - int version = SSA_NAME_VERSION (*p); - if (expr[version]) - { - tree new_expr = TREE_OPERAND (expr[version], 1); - *p = new_expr; - /* Clear the stmt's RHS, or GC might bite us. */ - TREE_OPERAND (expr[version], 1) = NULL_TREE; - return true; - } - } - - new_var = var_to_partition_to_var (map, var); - if (new_var) - { - *p = new_var; - set_is_used (new_var); - return true; - } - return false; -} - - -/* Remove any PHI node which is virtual PHI. */ -static void -eliminate_virtual_phis (void) -{ - basic_block bb; - tree phi, next; - - FOR_EACH_BB (bb) - { - for (phi = phi_nodes (bb); phi; phi = next) - { - next = TREE_CHAIN (phi); - if (!is_gimple_reg (SSA_NAME_VAR (PHI_RESULT (phi)))) - { -#ifdef ENABLE_CHECKING - int i; - /* There should be no arguments of this PHI which are in - the partition list, or we get incorrect results. */ - for (i = 0; i < PHI_NUM_ARGS (phi); i++) - { - tree arg = PHI_ARG_DEF (phi, i); - if (TREE_CODE (arg) == SSA_NAME - && is_gimple_reg (SSA_NAME_VAR (arg))) - { - fprintf (stderr, "Argument of PHI is not virtual ("); - print_generic_expr (stderr, arg, TDF_SLIM); - fprintf (stderr, "), but the result is :"); - print_generic_stmt (stderr, phi, TDF_SLIM); - abort(); - } - } -#endif - remove_phi_node (phi, NULL_TREE, bb); - } - } - } -} - - -/* This routine will coalesce variables of the same type which do not - interfere with each other. This will both reduce the memory footprint of - the stack, and allow us to coalesce together local copies of globals and - scalarized component refs. */ - -static void -coalesce_vars (var_map map, tree_live_info_p liveinfo) -{ - basic_block bb; - type_var_p tv; - tree var; - int x, p, p2; - coalesce_list_p cl; - conflict_graph graph; - - cl = create_coalesce_list (map); - - /* Merge all the live on entry vectors for coalesced partitions. */ - for (x = 0; x < num_var_partitions (map); x++) - { - var = partition_to_var (map, x); - p = var_to_partition (map, var); - if (p != x) - live_merge_and_clear (liveinfo, p, x); - } - - /* When PHI nodes are turned into copies, the result of each PHI node - becomes live on entry to the block. Mark these now. */ - FOR_EACH_BB (bb) - { - tree phi, arg; - int p; - for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) - { - p = var_to_partition (map, PHI_RESULT (phi)); - - /* Skip virtual PHI nodes. */ - if (p == NO_PARTITION) - continue; - - make_live_on_entry (liveinfo, bb, p); - - /* Each argument is a potential copy operation. Add any arguments - which are not coalesced to the result to the coalesce list. */ - for (x = 0; x < PHI_NUM_ARGS (phi); x++) - { - arg = PHI_ARG_DEF (phi, x); - if (!phi_ssa_name_p (arg)) - continue; - p2 = var_to_partition (map, arg); - if (p2 == NO_PARTITION) - continue; - if (p != p2) - add_coalesce (cl, p, p2, 1); - } - } - } - - - /* Re-calculate live on exit info. */ - calculate_live_on_exit (liveinfo); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "Live range info for variable memory coalescing.\n"); - dump_live_info (dump_file, liveinfo, LIVEDUMP_ALL); - - fprintf (dump_file, "Coalesce list from phi nodes:\n"); - dump_coalesce_list (dump_file, cl); - } - - - tv = type_var_init (map); - if (dump_file) - type_var_dump (dump_file, tv); - type_var_compact (tv); - if (dump_file) - type_var_dump (dump_file, tv); - - graph = build_tree_conflict_graph (liveinfo, tv, cl); - - type_var_decompact (tv); - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "type var list now looks like:n"); - type_var_dump (dump_file, tv); - - fprintf (dump_file, "Coalesce list after conflict graph build:\n"); - dump_coalesce_list (dump_file, cl); - } - - sort_coalesce_list (cl); - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "Coalesce list after sorting:\n"); - dump_coalesce_list (dump_file, cl); - } - - coalesce_tpa_members (tv, graph, map, cl, - ((dump_flags & TDF_DETAILS) ? dump_file : NULL)); - - type_var_delete (tv); - delete_coalesce_list (cl); -} - - -/* Temporary Expression Replacement (TER) - - Replace SSA version variables during out-of-ssa with their defining - expression if there is only one use of the variable. - - A pass is made through the function, one block at a time. No cross block - information is tracked. - - Variables which only have one use, and whose defining stmt is considered - a replaceable expression (see check_replaceable) are entered into - consideration by adding a list of dependent partitions to the version_info - vector for that ssa_name_version. This information comes from the partition - mapping for each USE. At the same time, the partition_dep_list vector for - these partitions have this version number entered into their lists. - - When the use of a replaceable ssa_variable is encountered, the dependence - list in version_info[] is moved to the "pending_dependence" list in case - the current expression is also replaceable. (To be determined later in - processing this stmt.) version_info[] for the version is then updated to - point to the defining stmt and the 'replaceable' bit is set. - - Any partition which is defined by a statement 'kills' any expression which - is dependent on this partition. Every ssa version in the partitions' - dependence list is removed from future consideration. - - All virtual references are lumped together. Any expression which is - dependent on any virtual variable (via a VUSE) has a dependence added - to the special partition defined by VIRTUAL_PARTITION. - - Whenever a VDEF is seen, all expressions dependent this VIRTUAL_PARTITION - are removed from consideration. - - At the end of a basic block, all expression are removed from consideration - in preparation for the next block. - - The end result is a vector over SSA_NAME_VERSION which is passed back to - rewrite_out_of_ssa. As the SSA variables are being rewritten, instead of - replacing the SSA_NAME tree element with the partition it was assigned, - it is replaced with the RHS of the defining expression. */ - -/* Dependancy list element. This can contain either a partition index or a - version number, depending on which list it is in. */ -typedef struct value_expr_d -{ - int value; - struct value_expr_d *next; -} *value_expr_p; - -/* Temporary Expression Replacement (TER) table information. */ -typedef struct temp_expr_table_d -{ - var_map map; - void **version_info; - value_expr_p *partition_dep_list; - bitmap replaceable; - bool saw_replaceable; - int virtual_partition; - bitmap partition_in_use; - value_expr_p free_list; - value_expr_p pending_dependence; -} *temp_expr_table_p; - -/* Used to indicate a dependancy on VDEFs. */ -#define VIRTUAL_PARTITION(table) (table->virtual_partition) - -static temp_expr_table_p new_temp_expr_table (var_map); -static tree *free_temp_expr_table (temp_expr_table_p); -static inline value_expr_p new_value_expr (temp_expr_table_p); -static inline void free_value_expr (temp_expr_table_p, value_expr_p); -static inline value_expr_p find_value_in_list (value_expr_p, int, - value_expr_p *); -static inline void add_value_to_list (temp_expr_table_p, value_expr_p *, int); -static inline void add_info_to_list (temp_expr_table_p, value_expr_p *, - value_expr_p); -static value_expr_p remove_value_from_list (value_expr_p *, int); -static void add_dependance (temp_expr_table_p, int, tree); -static bool check_replaceable (temp_expr_table_p, tree); -static void finish_expr (temp_expr_table_p, int, bool); -static void mark_replaceable (temp_expr_table_p, tree); -static inline void kill_expr (temp_expr_table_p, int, bool); -static inline void kill_virtual_exprs (temp_expr_table_p, bool); -static void find_replaceable_in_bb (temp_expr_table_p, basic_block); -static tree *find_replaceable_exprs (var_map); -static void dump_replaceable_exprs (FILE *, tree *); - -/* Create a new TER table. */ -static temp_expr_table_p -new_temp_expr_table (var_map map) -{ - temp_expr_table_p t; - - t = (temp_expr_table_p) xmalloc (sizeof (struct temp_expr_table_d)); - t->map = map; - - t->version_info = xcalloc (highest_ssa_version + 1, sizeof (void *)); - t->partition_dep_list = xcalloc (num_var_partitions (map) + 1, - sizeof (value_expr_p)); - - t->replaceable = BITMAP_XMALLOC (); - t->partition_in_use = BITMAP_XMALLOC (); - - t->saw_replaceable = false; - t->virtual_partition = num_var_partitions (map); - t->free_list = NULL; - t->pending_dependence = NULL; - - return t; -} - - -/* Free a TER table. If there are valid replacements, return the expression - vector. */ -static tree * -free_temp_expr_table (temp_expr_table_p t) -{ - value_expr_p p; - tree *ret = NULL; - -#ifdef ENABLE_CHECKING - int x; - for (x = 0; x <= num_var_partitions (t->map); x++) - if (t->partition_dep_list[x] != NULL) - abort(); -#endif - - while ((p = t->free_list)) - { - t->free_list = p->next; - free (p); - } - - BITMAP_XFREE (t->partition_in_use); - BITMAP_XFREE (t->replaceable); - - free (t->partition_dep_list); - if (t->saw_replaceable) - ret = (tree *)t->version_info; - else - free (t->version_info); - - free (t); - return ret; -} - - -/* Allocate a new value list node. Take it from the free list if possible. */ -static inline value_expr_p -new_value_expr (temp_expr_table_p table) -{ - value_expr_p p; - if (table->free_list) - { - p = table->free_list; - table->free_list = p->next; - } - else - p = (value_expr_p) xmalloc (sizeof (struct value_expr_d)); - - return p; -} - - -/* Add a value list node to the free list. */ -static inline void -free_value_expr (temp_expr_table_p table, value_expr_p p) -{ - p->next = table->free_list; - table->free_list = p; -} - - -/* Find a specific value if its in a list. Return a pointer to the list - object if found. Return NULL if it isn't. If last_ptr is provided, - it will point to the previous item upon return, or NULL if this is the - first item in the list. */ -static inline value_expr_p -find_value_in_list (value_expr_p list, int value, value_expr_p *last_ptr) -{ - value_expr_p curr; - value_expr_p last = NULL; - - for (curr = list; curr; last = curr, curr = curr->next) - { - if (curr->value == value) - break; - } - if (last_ptr) - *last_ptr = last; - return curr; -} - - -/* Add a value to a list, if it isn't already present. */ -static inline void -add_value_to_list (temp_expr_table_p tab, value_expr_p *list, int value) -{ - value_expr_p info; - - if (!find_value_in_list (*list, value, NULL)) - { - info = new_value_expr (tab); - info->value = value; - info->next = *list; - *list = info; - } -} - - -/* Add a value node if it's value isn't already in the list. Free this node if - it is already in the list. */ -static inline void -add_info_to_list (temp_expr_table_p tab, value_expr_p *list, value_expr_p info) -{ - if (find_value_in_list (*list, info->value, NULL)) - free_value_expr (tab, info); - else - { - info->next = *list; - *list = info; - } -} - - -/* Look for a value in a list. If found, remove it from the list and return - it's pointer. */ -static value_expr_p -remove_value_from_list (value_expr_p *list, int value) -{ - value_expr_p info, last; - - info = find_value_in_list (*list, value, &last); - if (!info) - return NULL; - if (!last) - *list = info->next; - else - last->next = info->next; - - return info; -} - - -/* Add a dependancy between the def of an SSA version and the partitions each - use in the expression represent. */ -static void -add_dependance (temp_expr_table_p tab, int version, tree var) -{ - int i, x; - value_expr_p info; - - i = SSA_NAME_VERSION (var); - if (bitmap_bit_p (tab->replaceable, i)) - { - /* This variable is being substituted, so use whatever dependences - were queued up when we marked this as replaceable earlier. */ - while ((info = tab->pending_dependence)) - { - tab->pending_dependence = info->next; - /* Get the partition this variable was dependent on. Reuse this - object to represent the current expression instead. */ - x = info->value; - info->value = version; - add_info_to_list (tab, &(tab->partition_dep_list[x]), info); - add_value_to_list (tab, - (value_expr_p *)&(tab->version_info[version]), x); - bitmap_set_bit (tab->partition_in_use, x); - } - } - else - { - i = var_to_partition (tab->map, var); -#ifdef ENABLE_CHECKING - if (i== NO_PARTITION) - abort (); -#endif - add_value_to_list (tab, &(tab->partition_dep_list[i]), version); - add_value_to_list (tab, (value_expr_p *)&(tab->version_info[version]), i); - bitmap_set_bit (tab->partition_in_use, i); - } -} - - -/* Check if an expression is suitable for replacement. If so, create an - expression entry. Return true if this stmt is replaceable. */ - -static bool -check_replaceable (temp_expr_table_p tab, tree stmt) -{ - stmt_ann_t ann; - vuse_optype vuseops; - def_optype defs; - use_optype uses; - tree var, def; - int num_use_ops, version, i; - var_map map = tab->map; - - if (TREE_CODE (stmt) != MODIFY_EXPR) - return false; - - ann = stmt_ann (stmt); - defs = DEF_OPS (ann); - - /* Punt if there is more than 1 def, or more than 1 use. */ - if (NUM_DEFS (defs) != 1) - return false; - def = DEF_OP (defs, 0); - if (version_ref_count (map, def) != 1) - return false; - - /* Assignments to variables assigned to hard registers are not - replaceable. */ - if (DECL_HARD_REGISTER (SSA_NAME_VAR (def))) - return false; - - /* There must be no VDEFS. */ - if (NUM_VDEFS (VDEF_OPS (ann)) != 0) - return false; - - /* Float expressions must go through memory if float-store is on. */ - if (flag_float_store && FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (stmt, 1)))) - return false; - - uses = USE_OPS (ann); - num_use_ops = NUM_USES (uses); - vuseops = VUSE_OPS (ann); - - /* Any expression which has no virtual operands and no real operands - should have been propagated if it's possible to do anything with them. - If this happens here, it probably exists that way for a reason, so we - won't touch it. An example is: - b_4 = &tab - There are no virtual uses nor any real uses, so we just leave this - alone to be safe. */ - - if (num_use_ops == 0 && NUM_VUSES (vuseops) == 0) - return false; - - version = SSA_NAME_VERSION (def); - - /* Add this expression to the dependancy list for each use partition. */ - for (i = 0; i < num_use_ops; i++) - { - var = USE_OP (uses, i); - add_dependance (tab, version, var); - } - - /* If there are VUSES, add a dependence on virtual defs. */ - if (NUM_VUSES (vuseops) != 0) - { - add_value_to_list (tab, (value_expr_p *)&(tab->version_info[version]), - VIRTUAL_PARTITION (tab)); - add_value_to_list (tab, - &(tab->partition_dep_list[VIRTUAL_PARTITION (tab)]), - version); - bitmap_set_bit (tab->partition_in_use, VIRTUAL_PARTITION (tab)); - } - - return true; -} - - -/* This function will remove an expression from replacement consideration. If - 'replace' is true, it is marked as replaceable, otherwise not. */ -static void -finish_expr (temp_expr_table_p tab, int version, bool replace) -{ - value_expr_p info, tmp; - int partition; - - /* Remove this expression from its dependent lists. The partition dependance - list is retained and transfered later to whomever uses this version. */ - for (info = (value_expr_p) tab->version_info[version]; info; info = tmp) - { - partition = info->value; -#ifdef ENABLE_CHECKING - if (tab->partition_dep_list[partition] == NULL) - abort (); -#endif - tmp = remove_value_from_list (&(tab->partition_dep_list[partition]), - version); -#ifdef ENABLE_CHECKING - if (!tmp) - abort (); -#endif - free_value_expr (tab, tmp); - /* Only clear the bit when the dependancy list is emptied via - a replacement. Otherwise kill_expr will take care of it. */ - if (!(tab->partition_dep_list[partition]) && replace) - bitmap_clear_bit (tab->partition_in_use, partition); - tmp = info->next; - if (!replace) - free_value_expr (tab, info); - } - - if (replace) - { - tab->saw_replaceable = true; - bitmap_set_bit (tab->replaceable, version); - } - else - { -#ifdef ENABLE_CHECKING - if (bitmap_bit_p (tab->replaceable, version)) - abort (); -#endif - tab->version_info[version] = NULL; - } -} - - -/* Mark the expression associated with a variable as replaceable, and enter - the defining stmt into the version_info table. */ -static void -mark_replaceable (temp_expr_table_p tab, tree var) -{ - value_expr_p info; - int version = SSA_NAME_VERSION (var); - finish_expr (tab, version, true); - - /* Move the dependence list to the pending list. */ - if (tab->version_info[version]) - { - info = (value_expr_p) tab->version_info[version]; - for ( ; info->next; info = info->next) - continue; - info->next = tab->pending_dependence; - tab->pending_dependence = (value_expr_p)tab->version_info[version]; - } - - tab->version_info[version] = SSA_NAME_DEF_STMT (var); -} - - -/* This function finishes any expression which is dependent on this partition - as NOT replaceable. clear_bit is used to determine whether partition_in_use - should have iuts bit cleared. Since this can be called within an - EXECUTE_IF_SET_IN_BITMAP, the bit can't always be cleared. */ -static inline void -kill_expr (temp_expr_table_p tab, int partition, bool clear_bit) -{ - value_expr_p ptr; - - /* Mark every active expr dependant on this var as not replaceable. */ - while ((ptr = tab->partition_dep_list[partition]) != NULL) - finish_expr (tab, ptr->value, false); - - if (clear_bit) - bitmap_clear_bit (tab->partition_in_use, partition); -} - - -/* This function kills all expressions which are dependant on virtual DEFs. */ -static inline void -kill_virtual_exprs (temp_expr_table_p tab, bool clear_bit) -{ - kill_expr (tab, VIRTUAL_PARTITION (tab), clear_bit); -} - - -/* This function processes a basic block, and looks for variables which can - be replaced by their expressions. */ -static void -find_replaceable_in_bb (temp_expr_table_p tab, basic_block bb) -{ - block_stmt_iterator bsi; - tree stmt, def; - stmt_ann_t ann; - int partition, num, i; - use_optype uses; - def_optype defs; - var_map map = tab->map; - value_expr_p p; - - for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) - { - stmt = bsi_stmt (bsi); - ann = stmt_ann (stmt); - - /* Determine if this stmt finishes an existing expression. */ - uses = USE_OPS (ann); - num = NUM_USES (uses); - for (i = 0; i < num; i++) - { - def = USE_OP (uses, i); - if (tab->version_info[SSA_NAME_VERSION (def)]) - { - /* Mark expression as replaceable unless stmt is volatile. */ - if (!ann->has_volatile_ops) - mark_replaceable (tab, def); - else - finish_expr (tab, SSA_NAME_VERSION (def), false); - } - } - - /* Next, see if this stmt kills off an active expression. */ - defs = DEF_OPS (ann); - num = NUM_DEFS (defs); - for (i = 0; i < num; i++) - { - def = DEF_OP (defs, i); - partition = var_to_partition (map, def); - if (partition != NO_PARTITION && tab->partition_dep_list[partition]) - kill_expr (tab, partition, true); - } - - /* Now see if we are creating a new expression or not. */ - if (!ann->has_volatile_ops) - check_replaceable (tab, stmt); - - /* Free any unused dependancy lists. */ - while ((p = tab->pending_dependence)) - { - tab->pending_dependence = p->next; - free_value_expr (tab, p); - } - - /* A VDEF kills any expression using a virtual operand. */ - if (NUM_VDEFS (VDEF_OPS (ann)) > 0) - kill_virtual_exprs (tab, true); - } -} - - -/* This function is the driver routine for replacement of temporary expressions - in the SSA->normal phase. If there are replaceable expressions, a table is - returned which maps SSA versions to the expressions they should be replaced - with. A NULL_TREE indicates no replacement should take place. - If there are no replacements at all, NULL is returned by the function. */ -static tree * -find_replaceable_exprs (var_map map) -{ - basic_block bb; - int i; - temp_expr_table_p table; - tree *ret; - - table = new_temp_expr_table (map); - FOR_EACH_BB (bb) - { - find_replaceable_in_bb (table, bb); - EXECUTE_IF_SET_IN_BITMAP ((table->partition_in_use), 0, i, - { - kill_expr (table, i, false); - }); - } - - ret = free_temp_expr_table (table); - return ret; -} - - -/* Dump the TER expression table. */ -static void -dump_replaceable_exprs (FILE *f, tree *expr) -{ - tree stmt, var; - int x; - fprintf (f, "\nReplacing Expressions\n"); - for (x = 0; x < (int)highest_ssa_version + 1; x++) - if (expr[x]) - { - stmt = expr[x]; - var = DEF_OP (STMT_DEF_OPS (stmt), 0); - print_generic_expr (f, var, TDF_SLIM); - fprintf (f, " replace with --> "); - print_generic_expr (f, TREE_OPERAND (stmt, 1), TDF_SLIM); - fprintf (f, "\n"); - } - fprintf (f, "\n"); -} - - -/* This function will rewrite the current program using the variable mapping - found in 'map'. If the replacement vector 'values' is provided, any - occurrences of partitions with non-null entries in the vector will be - replaced with the expression in the vector instead of its mapped - variable. */ - -static void -rewrite_trees (var_map map, tree *values) -{ - elim_graph g; - basic_block bb; - block_stmt_iterator si; - edge e; - tree phi; - bool changed; - - /* Replace PHI nodes with any required copies. */ - g = new_elim_graph (map->num_partitions); - g->map = map; - FOR_EACH_BB (bb) - { - for (si = bsi_start (bb); !bsi_end_p (si); ) - { - size_t i, num_uses, num_defs; - use_optype uses; - def_optype defs; - tree stmt = bsi_stmt (si); - tree *use_p = NULL; - int remove = 0, is_copy = 0; - stmt_ann_t ann; - - get_stmt_operands (stmt); - ann = stmt_ann (stmt); - changed = false; - - if (TREE_CODE (stmt) == MODIFY_EXPR - && (TREE_CODE (TREE_OPERAND (stmt, 1)) == SSA_NAME)) - is_copy = 1; - - uses = USE_OPS (ann); - num_uses = NUM_USES (uses); - - for (i = 0; i < num_uses; i++) - { - use_p = USE_OP_PTR (uses, i); - if (replace_variable (map, use_p, values)) - changed = true; - } - - defs = DEF_OPS (ann); - num_defs = NUM_DEFS (defs); - - if (values && num_defs == 1) - { - tree def = DEF_OP (defs, 0); - tree val; - val = values[SSA_NAME_VERSION (def)]; - if (val) - remove = 1; - } - if (!remove) - { - for (i = 0; i < num_defs; i++) - { - tree *def_p = DEF_OP_PTR (defs, i); - - if (replace_variable (map, def_p, NULL)) - changed = true; - - if (is_copy - && num_uses == 1 - && use_p - && def_p - && (*def_p == *use_p)) - remove = 1; - } - if (changed) - modify_stmt (stmt); - } - - /* Remove copies of the form 'var = var'. */ - if (remove) - bsi_remove (&si); - else - bsi_next (&si); - } - - phi = phi_nodes (bb); - if (phi) - { - for (e = bb->pred; e; e = e->pred_next) - eliminate_phi (e, phi_arg_from_edge (phi, e), g); - } - } - - delete_elim_graph (g); - - /* If any copies were inserted on edges, actually insert them now. */ - bsi_commit_edge_inserts (NULL); -} - -/* Remove the variables specified in a var map from SSA form. */ -void -remove_ssa_form (FILE *dump, var_map map, int flags) -{ - tree_live_info_p liveinfo; - basic_block bb; - tree phi, next; - FILE *save; - tree *values = NULL; - - save = dump_file; - dump_file = dump; - - /* If we are not combining temps, dont calculate live ranges fo variables - with only one SSA version. */ - if ((flags & SSANORM_COMBINE_TEMPS) == 0) - compact_var_map (map, VARMAP_NO_SINGLE_DEFS); - else - compact_var_map (map, VARMAP_NORMAL); - - if (dump_file && (dump_flags & TDF_DETAILS)) - dump_var_map (dump_file, map); - - liveinfo = coalesce_ssa_name (map, flags); - - /* Make sure even single occurrence variables are in the list now. */ - if ((flags & SSANORM_COMBINE_TEMPS) == 0) - compact_var_map (map, VARMAP_NORMAL); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "After Coalescing:\n"); - dump_var_map (dump_file, map); - } - - if (flags & SSANORM_PERFORM_TER) - { - values = find_replaceable_exprs (map); - if (values && dump_file && (dump_flags & TDF_DETAILS)) - dump_replaceable_exprs (dump_file, values); - } - - /* Assign real variables to the partitions now. */ - assign_vars (map); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "After Root variable replacement:\n"); - dump_var_map (dump_file, map); - } - - if ((flags & SSANORM_COMBINE_TEMPS) && liveinfo) - { - coalesce_vars (map, liveinfo); - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "After variable memory coalescing:\n"); - dump_var_map (dump_file, map); - } - } - - if (liveinfo) - delete_tree_live_info (liveinfo); - - rewrite_trees (map, values); - - if (values) - free (values); - - /* Remove phi nodes which have been translated back to real variables. */ - FOR_EACH_BB (bb) - { - for (phi = phi_nodes (bb); phi; phi = next) - { - next = TREE_CHAIN (phi); - if ((flags & SSANORM_REMOVE_ALL_PHIS) - || var_to_partition (map, PHI_RESULT (phi)) != NO_PARTITION) - remove_phi_node (phi, NULL_TREE, bb); - } - } - - dump_file = save; -} - -/* Take a subset of the variables (VARS) in the current function out of SSA - form. */ - -void -rewrite_vars_out_of_ssa (bitmap vars) -{ - if (bitmap_first_set_bit (vars) >= 0) - { - var_map map; - basic_block bb; - tree phi; - int i; - int ssa_flags; - - /* Search for PHIs in which one of the PHI arguments is marked for - translation out of SSA form, but for which the PHI result is not - marked for translation out of SSA form. - - Our per-variable out of SSA translation can not handle that case; - however we can easily handle it here by creating a new instance - of the PHI result's underlying variable and initializing it to - the offending PHI argument on the edge associated with the - PHI argument. We then change the PHI argument to use our new - instead of the PHI's underlying variable. - - You might think we could register partitions for the out-of-ssa - translation here and avoid a second walk of the PHI nodes. No - such luck since the size of the var map will change if we have - to manually take variables out of SSA form here. */ - FOR_EACH_BB (bb) - { - for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi)) - { - tree result = SSA_NAME_VAR (PHI_RESULT (phi)); - - /* If the definition is marked for renaming, then we need - to do nothing more for this PHI node. */ - if (bitmap_bit_p (vars, var_ann (result)->uid)) - continue; - - /* Look at all the arguments and see if any of them are - marked for renaming. If so, we need to handle them - specially. */ - for (i = 0; i < PHI_NUM_ARGS (phi); i++) - { - tree arg = PHI_ARG_DEF (phi, i); - - /* If the argument is not an SSA_NAME, then we can ignore - this argument. */ - if (TREE_CODE (arg) != SSA_NAME) - continue; - - /* If this argument is marked for renaming, then we need - to undo the copy propagation so that we can take - the argument out of SSA form without taking the - result out of SSA form. */ - arg = SSA_NAME_VAR (arg); - if (bitmap_bit_p (vars, var_ann (arg)->uid)) - { - tree new_name, copy; - - /* Get a new SSA_NAME for the copy, it is based on - the result, not the argument! We use the PHI - as the definition since we haven't created the - definition statement yet. */ - new_name = make_ssa_name (result, phi); - - /* Now create the copy statement. */ - copy = build (MODIFY_EXPR, TREE_TYPE (arg), - new_name, PHI_ARG_DEF (phi, i)); - - /* Now update SSA_NAME_DEF_STMT to point to the - newly created statement. */ - SSA_NAME_DEF_STMT (new_name) = copy; - - /* Now make the argument reference our new SSA_NAME. */ - PHI_ARG_DEF (phi, i) = new_name; - - /* Queue the statement for insertion. */ - bsi_insert_on_edge (PHI_ARG_EDGE (phi, i), copy); - modify_stmt (copy); - } - } - } - } - - /* If any copies were inserted on edges, actually insert them now. */ - bsi_commit_edge_inserts (NULL); - - /* Now register partitions for all instances of the variables we - are taking out of SSA form. */ - map = init_var_map (highest_ssa_version + 1); - register_ssa_partitions_for_vars (vars, map); - - /* Now that we have all the partitions registered, translate the - appropriate variables out of SSA form. */ - ssa_flags = SSANORM_COALESCE_PARTITIONS; - if (flag_tree_combine_temps) - ssa_flags |= SSANORM_COMBINE_TEMPS; - remove_ssa_form (dump_file, map, ssa_flags); - - /* And finally, reset the out_of_ssa flag for each of the vars - we just took out of SSA form. */ - EXECUTE_IF_SET_IN_BITMAP (vars, 0, i, - { - var_ann (referenced_var (i))->out_of_ssa_tag = 0; - }); - - } -} - -/* Take function FNDECL out of SSA form. - - PHASE indicates which dump file from the DUMP_FILES array to use when - dumping debugging information. */ - -static void -rewrite_out_of_ssa (void) -{ - var_map map; - int var_flags = 0; - int ssa_flags = (SSANORM_REMOVE_ALL_PHIS | SSANORM_USE_COALESCE_LIST - | SSANORM_COALESCE_PARTITIONS); - - eliminate_virtual_phis (); - - if (dump_file && (dump_flags & TDF_DETAILS)) - dump_tree_cfg (dump_file, dump_flags & ~TDF_DETAILS); - - /* We cannot allow unssa to un-gimplify trees before we instrument them. */ - if (flag_tree_ter && !flag_mudflap) - var_flags = SSA_VAR_MAP_REF_COUNT; - - map = create_ssa_var_map (var_flags); - - if (flag_tree_combine_temps) - ssa_flags |= SSANORM_COMBINE_TEMPS; - if (flag_tree_ter && !flag_mudflap) - ssa_flags |= SSANORM_PERFORM_TER; - - remove_ssa_form (dump_file, map, ssa_flags); - - if (dump_file && (dump_flags & TDF_DETAILS)) - dump_tree_cfg (dump_file, dump_flags & ~TDF_DETAILS); - - /* Do some cleanups which reduce the amount of data the - tree->rtl expanders deal with. */ - cfg_remove_useless_stmts (); - - /* Flush out flow graph and SSA data. */ - delete_var_map (map); - - /* Mark arrays indexed with non-constant indices with TREE_ADDRESSABLE. - FIXME: Is this really needed long-term? This is done for the benefit - of the RTL expanders. */ - discover_nonconstant_array_refs (); -} - -struct tree_opt_pass pass_del_ssa = -{ - "optimized", /* name */ - NULL, /* gate */ - rewrite_out_of_ssa, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_TREE_SSA_TO_NORMAL, /* tv_id */ - PROP_cfg | PROP_ssa, /* properties_required */ - 0, /* properties_provided */ - /* ??? If TER is enabled, we also kill gimple. */ - PROP_ssa, /* properties_destroyed */ - TODO_verify_ssa | TODO_verify_flow - | TODO_verify_stmts, /* todo_flags_start */ - TODO_dump_func | TODO_ggc_collect /* todo_flags_finish */ -}; /* Remove edge E and remove the corresponding arguments from the PHI nodes in E's destination block. */ @@ -2892,74 +96,6 @@ ssa_redirect_edge (edge e, basic_block dest) return e; } -/*--------------------------------------------------------------------------- - Debugging support ----------------------------------------------------------------------------*/ -/* Dump SSA information to FILE. */ - -void -dump_tree_ssa (FILE *file) -{ - basic_block bb; - const char *funcname - = (*lang_hooks.decl_printable_name) (current_function_decl, 2); - - fprintf (file, "SSA information for %s\n\n", funcname); - - FOR_EACH_BB (bb) - { - dump_bb (bb, file, 0); - fputs (" ", file); - print_generic_stmt (file, phi_nodes (bb), dump_flags); - fputs ("\n\n", file); - } -} - - -/* Dump SSA information to stderr. */ - -void -debug_tree_ssa (void) -{ - dump_tree_ssa (stderr); -} - - -/* Dump SSA statistics on FILE. */ - -void -dump_tree_ssa_stats (FILE *file) -{ - fprintf (file, "\nHash table statistics:\n"); - - fprintf (file, " def_blocks: "); - htab_statistics (file, def_blocks); - - fprintf (file, "\n"); -} - - -/* Dump SSA statistics on stderr. */ - -void -debug_tree_ssa_stats (void) -{ - dump_tree_ssa_stats (stderr); -} - - -/* Dump statistics for the hash table HTAB. */ - -static void -htab_statistics (FILE *file, htab_t htab) -{ - fprintf (file, "size %ld, %ld elements, %f collision/search ratio\n", - (long) htab_size (htab), - (long) htab_elements (htab), - htab_collisions (htab)); -} - - /* Return true if the definition of SSA_NAME at block BB is malformed. @@ -3283,157 +419,6 @@ verify_ssa (void) } -/*--------------------------------------------------------------------------- - Helpers for the main SSA building functions ----------------------------------------------------------------------------*/ - -/* Insert PHI nodes for variable VAR. */ - -static void -insert_phi_nodes_for (tree var, bitmap *dfs) -{ - struct def_blocks_d *def_map; - bitmap phi_insertion_points; - int bb_index; - - def_map = get_def_blocks_for (var); - if (def_map == NULL) - return; - - phi_insertion_points = BITMAP_GGC_ALLOC (); - - EXECUTE_IF_SET_IN_BITMAP (def_map->def_blocks, 0, bb_index, - { - VARRAY_PUSH_BB (work_stack, BASIC_BLOCK (bb_index)); - }); - - /* Pop a block off the worklist, add every block that appears in - the original block's dfs that we have not already processed to - the worklist. Iterate until the worklist is empty. Blocks - which are added to the worklist are potential sites for - PHI nodes. - - The iteration step could be done during PHI insertion just as - easily. We do it here for historical reasons -- we used to have - a heuristic which used the potential PHI insertion points to - determine if fully pruned or semi pruned SSA form was appropriate. - - We now always use fully pruned SSA form. */ - while (VARRAY_ACTIVE_SIZE (work_stack) > 0) - { - basic_block bb = VARRAY_TOP_BB (work_stack); - int bb_index = bb->index; - int dfs_index; - - VARRAY_POP (work_stack); - - EXECUTE_IF_AND_COMPL_IN_BITMAP (dfs[bb_index], - phi_insertion_points, - 0, dfs_index, - { - basic_block bb = BASIC_BLOCK (dfs_index); - - VARRAY_PUSH_BB (work_stack, bb); - bitmap_set_bit (phi_insertion_points, dfs_index); - }); - } - - /* Now compute global livein for this variable. Note this modifies - def_map->livein_blocks. */ - compute_global_livein (def_map->livein_blocks, def_map->def_blocks); - - /* And insert the PHI nodes. */ - EXECUTE_IF_AND_IN_BITMAP (phi_insertion_points, def_map->livein_blocks, - 0, bb_index, - { - create_phi_node (var, BASIC_BLOCK (bb_index)); - }); - - phi_insertion_points = NULL; -} - -/* SSA Rewriting 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. */ - -static void -rewrite_stmt (struct dom_walk_data *walk_data, - basic_block bb ATTRIBUTE_UNUSED, - block_stmt_iterator si) -{ - size_t i; - stmt_ann_t ann; - tree stmt; - vuse_optype vuses; - vdef_optype vdefs; - def_optype defs; - use_optype uses; - struct rewrite_block_data *bd; - - bd = VARRAY_TOP_GENERIC_PTR (walk_data->block_data_stack); - - stmt = bsi_stmt (si); - ann = stmt_ann (stmt); - ssa_stats.num_stmts++; - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "Renaming statement "); - print_generic_stmt (dump_file, stmt, TDF_SLIM); - fprintf (dump_file, "\n"); - } - -#if defined ENABLE_CHECKING - /* We have just scanned the code for operands. No statement should - be modified. */ - if (ann->modified) - abort (); -#endif - - defs = DEF_OPS (ann); - uses = USE_OPS (ann); - vuses = VUSE_OPS (ann); - vdefs = VDEF_OPS (ann); - - /* Step 1. Rewrite USES and VUSES in the statement. */ - for (i = 0; i < NUM_USES (uses); i++) - rewrite_operand (USE_OP_PTR (uses, i)); - - /* Rewrite virtual uses in the statement. */ - for (i = 0; i < NUM_VUSES (vuses); i++) - rewrite_operand (VUSE_OP_PTR (vuses, i)); - - /* Step 2. Register the statement's DEF and VDEF operands. */ - for (i = 0; i < NUM_DEFS (defs); i++) - { - tree *def_p = DEF_OP_PTR (defs, i); - - if (TREE_CODE (*def_p) != SSA_NAME) - *def_p = make_ssa_name (*def_p, stmt); - - /* FIXME: We shouldn't be registering new defs if the variable - doesn't need to be renamed. */ - register_new_def (SSA_NAME_VAR (*def_p), *def_p, - &bd->block_defs, currdefs); - } - - /* Register new virtual definitions made by the statement. */ - for (i = 0; i < NUM_VDEFS (vdefs); i++) - { - rewrite_operand (VDEF_OP_PTR (vdefs, i)); - - if (TREE_CODE (VDEF_RESULT (vdefs, i)) != SSA_NAME) - *VDEF_RESULT_PTR (vdefs, i) - = make_ssa_name (VDEF_RESULT (vdefs, i), stmt); - - /* FIXME: We shouldn't be registering new defs if the variable - doesn't need to be renamed. */ - register_new_def (SSA_NAME_VAR (VDEF_RESULT (vdefs, i)), - VDEF_RESULT (vdefs, i), &bd->block_defs, currdefs); - } -} - - /* Set the USED bit in the annotation for T. */ void @@ -3466,47 +451,6 @@ set_is_used (tree t) } -/* Replace the operand pointed by OP_P with its immediate reaching - definition. */ - -static inline void -rewrite_operand (tree *op_p) -{ - if (TREE_CODE (*op_p) != SSA_NAME) - *op_p = get_reaching_def (*op_p); -} - - -/* Register DEF to be a new definition for variable VAR and push VAR's - current reaching definition into the stack pointed by BLOCK_DEFS_P. - IS_REAL_OPERAND is true when DEF is a real definition. */ - -void -register_new_def (tree var, tree def, - varray_type *block_defs_p, varray_type table) -{ - tree currdef = get_value_for (var, table); - - if (! *block_defs_p) - VARRAY_TREE_INIT (*block_defs_p, 20, "block_defs"); - - /* If the current reaching definition is NULL, push the variable itself - so that the dominator tree callbacks know what variable is associated - with this NULL reaching def when unwinding the *BLOCK_DEFS_P stack. */ - if (currdef == NULL_TREE) - VARRAY_PUSH_TREE (*block_defs_p, var); - - /* Push the current reaching definition into *BLOCK_DEFS_P. This stack is - later used by the dominator tree callbacks to restore the reaching - definitions for all the variables defined in the block after a recursive - visit to all its immediately dominated blocks. */ - VARRAY_PUSH_TREE (*block_defs_p, currdef); - - /* Set the current reaching definition for VAR to be DEF. */ - set_value_for (var, def, table); -} - - /* Initialize global DFA and SSA structures. */ void @@ -3517,7 +461,6 @@ init_tree_ssa (void) init_ssa_operands (); init_ssanames (); init_phinodes (); - memset (&ssa_stats, 0, sizeof (ssa_stats)); global_var = NULL_TREE; aliases_computed_p = false; } @@ -3555,100 +498,6 @@ delete_tree_ssa (void) aliases_computed_p = false; } -/* Return the current definition for variable VAR. If none is found, - create a new SSA name to act as the zeroth definition for VAR. If VAR - is call clobbered and there exists a more recent definition of - GLOBAL_VAR, return the definition for GLOBAL_VAR. This means that VAR - has been clobbered by a function call since its last assignment. */ - -static tree -get_reaching_def (tree var) -{ - tree default_d, currdef_var; - - /* Lookup the current reaching definition for VAR. */ - default_d = NULL_TREE; - currdef_var = get_value_for (var, currdefs); - - /* If there is no reaching definition for VAR, create and register a - default definition for it (if needed). */ - if (currdef_var == NULL_TREE) - { - default_d = default_def (var); - if (default_d == NULL_TREE) - { - default_d = make_ssa_name (var, build_empty_stmt ()); - set_default_def (var, default_d); - } - set_value_for (var, default_d, currdefs); - } - - /* Return the current reaching definition for VAR, or the default - definition, if we had to create one. */ - return (currdef_var) ? currdef_var : default_d; -} - - -/* Hashing and equality functions for DEF_BLOCKS. */ - -static hashval_t -def_blocks_hash (const void *p) -{ - return htab_hash_pointer - ((const void *)((const struct def_blocks_d *)p)->var); -} - -static int -def_blocks_eq (const void *p1, const void *p2) -{ - return ((const struct def_blocks_d *)p1)->var - == ((const struct def_blocks_d *)p2)->var; -} - - -/* Dump the DEF_BLOCKS hash table on stderr. */ - -void -debug_def_blocks (void) -{ - htab_traverse (def_blocks, debug_def_blocks_r, NULL); -} - -/* Callback for htab_traverse to dump the DEF_BLOCKS hash table. */ - -static int -debug_def_blocks_r (void **slot, void *data ATTRIBUTE_UNUSED) -{ - unsigned long i; - struct def_blocks_d *db_p = (struct def_blocks_d *) *slot; - - fprintf (stderr, "VAR: "); - print_generic_expr (stderr, db_p->var, dump_flags); - fprintf (stderr, ", DEF_BLOCKS: { "); - EXECUTE_IF_SET_IN_BITMAP (db_p->def_blocks, 0, i, - fprintf (stderr, "%ld ", i)); - fprintf (stderr, "}"); - fprintf (stderr, ", LIVEIN_BLOCKS: { "); - EXECUTE_IF_SET_IN_BITMAP (db_p->livein_blocks, 0, i, - fprintf (stderr, "%ld ", i)); - fprintf (stderr, "}\n"); - - return 1; -} - - -/* Return the set of blocks where variable VAR is defined and the blocks - where VAR is live on entry (livein). */ - -static struct def_blocks_d * -get_def_blocks_for (tree var) -{ - struct def_blocks_d dm; - - dm.var = var; - return (struct def_blocks_d *) htab_find (def_blocks, &dm); -} - /* Return true if EXPR is a useless type conversion, otherwise return false. */ @@ -3810,6 +659,7 @@ walk_use_def_chains (tree var, walk_use_def_chains_fn fn, void *data) } } + /* Replaces immediate uses of VAR by REPL. */ static void @@ -3877,7 +727,7 @@ replace_immediate_uses (tree var, tree repl) } /* Raises value of phi node PHI by joining it with VAL. Processes immediate - uses of the phi recursively. */ + uses of PHI recursively. */ static void raise_value (tree phi, tree val, tree *eq_to) @@ -4217,5 +1067,3 @@ struct tree_opt_pass pass_late_warn_uninitialized = 0, /* todo_flags_start */ 0 /* todo_flags_finish */ }; - -#include "gt-tree-ssa.h" |