Mercurial > hg > CbC > CbC_gcc
diff gcc/ipa-reference.c @ 0:a06113de4d67
first commit
author | kent <kent@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 17 Jul 2009 14:47:48 +0900 |
parents | |
children | 77e2b8dfacca |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcc/ipa-reference.c Fri Jul 17 14:47:48 2009 +0900 @@ -0,0 +1,1286 @@ +/* Callgraph based analysis of static variables. + Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. + Contributed by Kenneth Zadeck <zadeck@naturalbridge.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 3, 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 COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* This file gathers information about how variables whose scope is + confined to the compilation unit are used. + + There are two categories of information produced by this pass: + + 1) The addressable (TREE_ADDRESSABLE) bit and readonly + (TREE_READONLY) bit associated with these variables is properly set + based on scanning all of the code withing the compilation unit. + + 2) The transitive call site specific clobber effects are computed + for the variables whose scope is contained within this compilation + unit. + + First each function and static variable initialization is analyzed + to determine which local static variables are either read, written, + or have their address taken. Any local static that has its address + taken is removed from consideration. Once the local read and + writes are determined, a transitive closure of this information is + performed over the call graph to determine the worst case set of + side effects of each call. In later parts of the compiler, these + local and global sets are examined to make the call clobbering less + traumatic, promote some statics to registers, and improve aliasing + information. + + Currently must be run after inlining decisions have been made since + otherwise, the local sets will not contain information that is + consistent with post inlined state. The global sets are not prone + to this problem since they are by definition transitive. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "tree-flow.h" +#include "tree-inline.h" +#include "tree-pass.h" +#include "langhooks.h" +#include "pointer-set.h" +#include "ggc.h" +#include "ipa-utils.h" +#include "ipa-reference.h" +#include "c-common.h" +#include "gimple.h" +#include "cgraph.h" +#include "output.h" +#include "flags.h" +#include "timevar.h" +#include "diagnostic.h" +#include "langhooks.h" + +/* The static variables defined within the compilation unit that are + loaded or stored directly by function that owns this structure. */ + +struct ipa_reference_local_vars_info_d +{ + bitmap statics_read; + bitmap statics_written; + + /* Set when this function calls another function external to the + compilation unit or if the function has a asm clobber of memory. + In general, such calls are modeled as reading and writing all + variables (both bits on) but sometime there are attributes on the + called function so we can do better. */ + bool calls_read_all; + bool calls_write_all; +}; + +/* Statics that are read and written by some set of functions. The + local ones are based on the loads and stores local to the function. + The global ones are based on the local info as well as the + transitive closure of the functions that are called. The + structures are separated to allow the global structures to be + shared between several functions since every function within a + strongly connected component will have the same information. This + sharing saves both time and space in the computation of the vectors + as well as their translation from decl_uid form to ann_uid + form. */ + +struct ipa_reference_global_vars_info_d +{ + bitmap statics_read; + bitmap statics_written; + bitmap statics_not_read; + bitmap statics_not_written; +}; + +typedef struct ipa_reference_local_vars_info_d *ipa_reference_local_vars_info_t; +typedef struct ipa_reference_global_vars_info_d *ipa_reference_global_vars_info_t; +struct ipa_reference_vars_info_d +{ + ipa_reference_local_vars_info_t local; + ipa_reference_global_vars_info_t global; +}; + +typedef struct ipa_reference_vars_info_d *ipa_reference_vars_info_t; + +/* This splay tree contains all of the static variables that are + being considered by the compilation level alias analysis. For + module_at_a_time compilation, this is the set of static but not + public variables. Any variables that either have their address + taken or participate in otherwise unsavory operations are deleted + from this list. */ +static GTY((param1_is(int), param2_is(tree))) + splay_tree reference_vars_to_consider; + +/* This bitmap is used to knock out the module static variables whose + addresses have been taken and passed around. */ +static bitmap module_statics_escape; + +/* This bitmap is used to knock out the module static variables that + are not readonly. */ +static bitmap module_statics_written; + +/* A bit is set for every module static we are considering. This is + ored into the local info when asm code is found that clobbers all + memory. */ +static bitmap all_module_statics; + +static struct pointer_set_t *visited_nodes; + +/* Obstack holding bitmaps of local analysis (live from analysis to + propagation) */ +static bitmap_obstack local_info_obstack; +/* Obstack holding global analysis live forever. */ +static bitmap_obstack global_info_obstack; + +/* Holders of ipa cgraph hooks: */ +static struct cgraph_node_hook_list *function_insertion_hook_holder; +static struct cgraph_2node_hook_list *node_duplication_hook_holder; +static struct cgraph_node_hook_list *node_removal_hook_holder; + +enum initialization_status_t +{ + UNINITIALIZED, + RUNNING, + FINISHED +}; + +tree memory_identifier_string; + +/* Vector where the reference var infos are actually stored. */ +DEF_VEC_P (ipa_reference_vars_info_t); +DEF_VEC_ALLOC_P (ipa_reference_vars_info_t, heap); +static VEC (ipa_reference_vars_info_t, heap) *ipa_reference_vars_vector; + +/* Return the ipa_reference_vars structure starting from the cgraph NODE. */ +static inline ipa_reference_vars_info_t +get_reference_vars_info (struct cgraph_node *node) +{ + if (!ipa_reference_vars_vector + || VEC_length (ipa_reference_vars_info_t, ipa_reference_vars_vector) <= (unsigned int)node->uid) + return NULL; + return VEC_index (ipa_reference_vars_info_t, ipa_reference_vars_vector, node->uid); +} + +/* Return the ipa_reference_vars structure starting from the cgraph NODE. */ +static inline void +set_reference_vars_info (struct cgraph_node *node, ipa_reference_vars_info_t info) +{ + if (!ipa_reference_vars_vector + || VEC_length (ipa_reference_vars_info_t, ipa_reference_vars_vector) <= (unsigned int)node->uid) + VEC_safe_grow_cleared (ipa_reference_vars_info_t, heap, ipa_reference_vars_vector, node->uid + 1); + VEC_replace (ipa_reference_vars_info_t, ipa_reference_vars_vector, node->uid, info); +} + +/* Get a bitmap that contains all of the locally referenced static + variables for function FN. */ +static ipa_reference_local_vars_info_t +get_local_reference_vars_info (struct cgraph_node *fn) +{ + ipa_reference_vars_info_t info = get_reference_vars_info (fn); + + if (info) + return info->local; + else + /* This phase was not run. */ + return NULL; +} + +/* Get a bitmap that contains all of the globally referenced static + variables for function FN. */ + +static ipa_reference_global_vars_info_t +get_global_reference_vars_info (struct cgraph_node *fn) +{ + ipa_reference_vars_info_t info = get_reference_vars_info (fn); + + if (info) + return info->global; + else + /* This phase was not run. */ + return NULL; +} + +/* Return a bitmap indexed by VAR_DECL uid for the static variables + that are read during the execution of the function FN. Returns + NULL if no data is available. */ + +bitmap +ipa_reference_get_read_global (struct cgraph_node *fn) +{ + ipa_reference_global_vars_info_t g = get_global_reference_vars_info (fn); + if (g) + return g->statics_read; + else + return NULL; +} + +/* Return a bitmap indexed by VAR_DECL uid for the static variables + that are written during the execution of the function FN. Note + that variables written may or may not be read during the function + call. Returns NULL if no data is available. */ + +bitmap +ipa_reference_get_written_global (struct cgraph_node *fn) +{ + ipa_reference_global_vars_info_t g = get_global_reference_vars_info (fn); + if (g) + return g->statics_written; + else + return NULL; +} + +/* Return a bitmap indexed by_DECL_UID uid for the static variables + that are not read during the execution of the function FN. Returns + NULL if no data is available. */ + +bitmap +ipa_reference_get_not_read_global (struct cgraph_node *fn) +{ + ipa_reference_global_vars_info_t g = get_global_reference_vars_info (fn); + if (g) + return g->statics_not_read; + else + return NULL; +} + +/* Return a bitmap indexed by DECL_UID uid for the static variables + that are not written during the execution of the function FN. Note + that variables written may or may not be read during the function + call. Returns NULL if no data is available. */ + +bitmap +ipa_reference_get_not_written_global (struct cgraph_node *fn) +{ + ipa_reference_global_vars_info_t g = get_global_reference_vars_info (fn); + if (g) + return g->statics_not_written; + else + return NULL; +} + + + +/* Add VAR to all_module_statics and the two + reference_vars_to_consider* sets. */ + +static inline void +add_static_var (tree var) +{ + int uid = DECL_UID (var); + gcc_assert (TREE_CODE (var) == VAR_DECL); + if (!bitmap_bit_p (all_module_statics, uid)) + { + splay_tree_insert (reference_vars_to_consider, + uid, (splay_tree_value)var); + bitmap_set_bit (all_module_statics, uid); + } +} + +/* Return true if the variable T is the right kind of static variable to + perform compilation unit scope escape analysis. */ + +static inline bool +has_proper_scope_for_analysis (tree t) +{ + /* If the variable has the "used" attribute, treat it as if it had a + been touched by the devil. */ + if (lookup_attribute ("used", DECL_ATTRIBUTES (t))) + return false; + + /* Do not want to do anything with volatile except mark any + function that uses one to be not const or pure. */ + if (TREE_THIS_VOLATILE (t)) + return false; + + /* Do not care about a local automatic that is not static. */ + if (!TREE_STATIC (t) && !DECL_EXTERNAL (t)) + return false; + + if (DECL_EXTERNAL (t) || TREE_PUBLIC (t)) + return false; + + /* We cannot touch decls where the type needs constructing. */ + if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (t))) + return false; + + /* This is a variable we care about. Check if we have seen it + before, and if not add it the set of variables we care about. */ + if (!bitmap_bit_p (all_module_statics, DECL_UID (t))) + add_static_var (t); + + return true; +} + +/* Mark tree T as having address taken. */ + +static void +mark_address_taken (tree x) +{ + if (TREE_CODE (x) == VAR_DECL + && module_statics_escape && has_proper_scope_for_analysis (x)) + bitmap_set_bit (module_statics_escape, DECL_UID (x)); +} + +/* Mark load of T. */ + +static void +mark_load (ipa_reference_local_vars_info_t local, + tree t) +{ + if (TREE_CODE (t) == VAR_DECL + && has_proper_scope_for_analysis (t)) + bitmap_set_bit (local->statics_read, DECL_UID (t)); +} + +/* Mark store of T. */ + +static void +mark_store (ipa_reference_local_vars_info_t local, + tree t) +{ + if (TREE_CODE (t) == VAR_DECL + && has_proper_scope_for_analysis (t)) + { + if (local) + bitmap_set_bit (local->statics_written, DECL_UID (t)); + /* Mark the write so we can tell which statics are + readonly. */ + if (module_statics_written) + bitmap_set_bit (module_statics_written, DECL_UID (t)); + } +} + +/* Look for memory clobber and set read_all/write_all if present. */ + +static void +check_asm_memory_clobber (ipa_reference_local_vars_info_t local, gimple stmt) +{ + size_t i; + tree op; + + for (i = 0; i < gimple_asm_nclobbers (stmt); i++) + { + op = gimple_asm_clobber_op (stmt, i); + if (simple_cst_equal(TREE_VALUE (op), memory_identifier_string) == 1) + { + /* Abandon all hope, ye who enter here. */ + local->calls_read_all = true; + local->calls_write_all = true; + } + } +} + +/* Look for external calls and set read_all/write_all correspondingly. */ + +static void +check_call (ipa_reference_local_vars_info_t local, gimple stmt) +{ + int flags = gimple_call_flags (stmt); + tree callee_t = gimple_call_fndecl (stmt); + enum availability avail = AVAIL_NOT_AVAILABLE; + + if (callee_t) + { + struct cgraph_node* callee = cgraph_node(callee_t); + avail = cgraph_function_body_availability (callee); + } + + if (avail <= AVAIL_OVERWRITABLE) + if (local) + { + if (flags & ECF_CONST) + ; + else if (flags & ECF_PURE) + local->calls_read_all = true; + else + { + local->calls_read_all = true; + local->calls_write_all = true; + } + } + /* TODO: To be able to produce sane results, we should also handle + common builtins, in particular throw. + Indirect calls hsould be only counted and as inliner is replacing them + by direct calls, we can conclude if any indirect calls are left in body */ +} + +/* TP is the part of the tree currently under the microscope. + WALK_SUBTREES is part of the walk_tree api but is unused here. + DATA is cgraph_node of the function being walked. */ + +static tree +scan_stmt_for_static_refs (gimple_stmt_iterator *gsip, + struct cgraph_node *fn) +{ + gimple stmt = gsi_stmt (*gsip); + ipa_reference_local_vars_info_t local = NULL; + unsigned int i; + bitmap_iterator bi; + + if (fn) + local = get_reference_vars_info (fn)->local; + + if (gimple_loaded_syms (stmt)) + EXECUTE_IF_SET_IN_BITMAP (gimple_loaded_syms (stmt), 0, i, bi) + mark_load (local, referenced_var_lookup (i)); + if (gimple_stored_syms (stmt)) + EXECUTE_IF_SET_IN_BITMAP (gimple_stored_syms (stmt), 0, i, bi) + mark_store (local, referenced_var_lookup (i)); + if (gimple_addresses_taken (stmt)) + EXECUTE_IF_SET_IN_BITMAP (gimple_addresses_taken (stmt), 0, i, bi) + mark_address_taken (referenced_var_lookup (i)); + + switch (gimple_code (stmt)) + { + case GIMPLE_CALL: + check_call (local, stmt); + break; + + case GIMPLE_ASM: + check_asm_memory_clobber (local, stmt); + break; + + /* We used to check nonlocal labels here and set them as potentially modifying + everything. This is not needed, since we can get to nonlocal label only + from callee and thus we will get info propagated. */ + + default: + break; + } + + return NULL; +} + +/* Call-back to scan variable initializers for static references. + Called using walk_tree. */ + +static tree +scan_initializer_for_static_refs (tree *tp, int *walk_subtrees, + void *data ATTRIBUTE_UNUSED) +{ + tree t = *tp; + + if (TREE_CODE (t) == ADDR_EXPR) + { + mark_address_taken (get_base_var (t)); + *walk_subtrees = 0; + } + /* Save some cycles by not walking types and declaration as we + won't find anything useful there anyway. */ + else if (IS_TYPE_OR_DECL_P (*tp)) + *walk_subtrees = 0; + + return NULL; +} + +/* Lookup the tree node for the static variable that has UID. */ +static tree +get_static_decl (int index) +{ + splay_tree_node stn = + splay_tree_lookup (reference_vars_to_consider, index); + if (stn) + return (tree)stn->value; + return NULL; +} + +/* Lookup the tree node for the static variable that has UID and + convert the name to a string for debugging. */ + +static const char * +get_static_name (int index) +{ + splay_tree_node stn = + splay_tree_lookup (reference_vars_to_consider, index); + if (stn) + return lang_hooks.decl_printable_name ((tree)(stn->value), 2); + return NULL; +} + +/* Or in all of the bits from every callee of X into X_GLOBAL, the caller's cycle, + bit vector. There are several cases to check to avoid the sparse + bitmap oring. */ + +static void +propagate_bits (ipa_reference_global_vars_info_t x_global, struct cgraph_node *x) +{ + struct cgraph_edge *e; + for (e = x->callees; e; e = e->next_callee) + { + struct cgraph_node *y = e->callee; + + /* Only look at the master nodes and skip external nodes. */ + if (cgraph_function_body_availability (e->callee) > AVAIL_OVERWRITABLE) + { + if (get_reference_vars_info (y)) + { + ipa_reference_vars_info_t y_info + = get_reference_vars_info (y); + ipa_reference_global_vars_info_t y_global = y_info->global; + + /* Calls in current cycle do not have global computed yet. */ + if (!y_info->global) + continue; + + if (x_global->statics_read + != all_module_statics) + { + if (y_global->statics_read + == all_module_statics) + { + BITMAP_FREE (x_global->statics_read); + x_global->statics_read + = all_module_statics; + } + /* Skip bitmaps that are pointer equal to node's bitmap + (no reason to spin within the cycle). */ + else if (x_global->statics_read + != y_global->statics_read) + bitmap_ior_into (x_global->statics_read, + y_global->statics_read); + } + + if (x_global->statics_written + != all_module_statics) + { + if (y_global->statics_written + == all_module_statics) + { + BITMAP_FREE (x_global->statics_written); + x_global->statics_written + = all_module_statics; + } + /* Skip bitmaps that are pointer equal to node's bitmap + (no reason to spin within the cycle). */ + else if (x_global->statics_written + != y_global->statics_written) + bitmap_ior_into (x_global->statics_written, + y_global->statics_written); + } + } + else + gcc_unreachable (); + } + } +} + +/* The init routine for analyzing global static variable usage. See + comments at top for description. */ +static void +ipa_init (void) +{ + memory_identifier_string = build_string(7, "memory"); + + reference_vars_to_consider = + splay_tree_new_ggc (splay_tree_compare_ints); + + bitmap_obstack_initialize (&local_info_obstack); + bitmap_obstack_initialize (&global_info_obstack); + module_statics_escape = BITMAP_ALLOC (&local_info_obstack); + module_statics_written = BITMAP_ALLOC (&local_info_obstack); + all_module_statics = BITMAP_ALLOC (&global_info_obstack); + + /* There are some shared nodes, in particular the initializers on + static declarations. We do not need to scan them more than once + since all we would be interested in are the addressof + operations. */ + visited_nodes = pointer_set_create (); +} + +/* Check out the rhs of a static or global initialization VNODE to see + if any of them contain addressof operations. Note that some of + these variables may not even be referenced in the code in this + compilation unit but their right hand sides may contain references + to variables defined within this unit. */ + +static void +analyze_variable (struct varpool_node *vnode) +{ + struct walk_stmt_info wi; + tree global = vnode->decl; + + memset (&wi, 0, sizeof (wi)); + wi.pset = visited_nodes; + walk_tree (&DECL_INITIAL (global), scan_initializer_for_static_refs, + &wi, wi.pset); +} + +/* Set up the persistent info for FN. */ + +static ipa_reference_local_vars_info_t +init_function_info (struct cgraph_node *fn) +{ + ipa_reference_vars_info_t info + = XCNEW (struct ipa_reference_vars_info_d); + ipa_reference_local_vars_info_t l + = XCNEW (struct ipa_reference_local_vars_info_d); + + /* Add the info to the tree's annotation. */ + set_reference_vars_info (fn, info); + + info->local = l; + l->statics_read = BITMAP_ALLOC (&local_info_obstack); + l->statics_written = BITMAP_ALLOC (&local_info_obstack); + + return l; +} + +/* This is the main routine for finding the reference patterns for + global variables within a function FN. */ + +static void +analyze_function (struct cgraph_node *fn) +{ + tree decl = fn->decl; + struct function *this_cfun = DECL_STRUCT_FUNCTION (decl); + basic_block this_block; +#ifdef ENABLE_CHECKING + tree step; +#endif + + if (dump_file) + fprintf (dump_file, "\n local analysis of %s\n", cgraph_node_name (fn)); + + push_cfun (DECL_STRUCT_FUNCTION (decl)); + current_function_decl = decl; + + init_function_info (fn); + FOR_EACH_BB_FN (this_block, this_cfun) + { + gimple_stmt_iterator gsi; + gimple phi; + tree op; + use_operand_p use; + ssa_op_iter iter; + + /* Find the addresses taken in phi node arguments. */ + for (gsi = gsi_start_phis (this_block); + !gsi_end_p (gsi); + gsi_next (&gsi)) + { + phi = gsi_stmt (gsi); + FOR_EACH_PHI_ARG (use, phi, iter, SSA_OP_USE) + { + op = USE_FROM_PTR (use); + if (TREE_CODE (op) == ADDR_EXPR) + mark_address_taken (get_base_var (op)); + } + } + + for (gsi = gsi_start_bb (this_block); !gsi_end_p (gsi); gsi_next (&gsi)) + scan_stmt_for_static_refs (&gsi, fn); + } + +#ifdef ENABLE_CHECKING + /* Verify that all local initializers was expanded by gimplifier. */ + for (step = DECL_STRUCT_FUNCTION (decl)->local_decls; + step; + step = TREE_CHAIN (step)) + { + tree var = TREE_VALUE (step); + if (TREE_CODE (var) == VAR_DECL + && DECL_INITIAL (var) + && !TREE_STATIC (var)) + gcc_unreachable (); + } +#endif + pop_cfun (); + current_function_decl = NULL; +} + +/* Remove local data associated with function FN. */ +static void +clean_function_local_data (struct cgraph_node *fn) +{ + ipa_reference_vars_info_t info = get_reference_vars_info (fn); + ipa_reference_local_vars_info_t l = info->local; + if (l) + { + if (l->statics_read + && l->statics_read != all_module_statics) + BITMAP_FREE (l->statics_read); + if (l->statics_written + &&l->statics_written != all_module_statics) + BITMAP_FREE (l->statics_written); + free (l); + info->local = NULL; + } +} + +/* Remove all data associated with function FN. */ + +static void +clean_function (struct cgraph_node *fn) +{ + ipa_reference_vars_info_t info = get_reference_vars_info (fn); + ipa_reference_global_vars_info_t g = info->global; + + clean_function_local_data (fn); + if (g) + { + if (g->statics_read + && g->statics_read != all_module_statics) + BITMAP_FREE (g->statics_read); + + if (g->statics_written + && g->statics_written != all_module_statics) + BITMAP_FREE (g->statics_written); + + if (g->statics_not_read + && g->statics_not_read != all_module_statics) + BITMAP_FREE (g->statics_not_read); + + if (g->statics_not_written + && g->statics_not_written != all_module_statics) + BITMAP_FREE (g->statics_not_written); + free (g); + info->global = NULL; + } + + free (get_reference_vars_info (fn)); + set_reference_vars_info (fn, NULL); +} + +/* Called when new function is inserted to callgraph late. */ +static void +add_new_function (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) +{ + /* There are some shared nodes, in particular the initializers on + static declarations. We do not need to scan them more than once + since all we would be interested in are the addressof + operations. */ + analyze_function (node); + visited_nodes = NULL; +} + +static bitmap +copy_local_bitmap (bitmap src) +{ + bitmap dst; + if (!src) + return NULL; + if (src == all_module_statics) + return all_module_statics; + dst = BITMAP_ALLOC (&local_info_obstack); + bitmap_copy (dst, src); + return dst; +} + +static bitmap +copy_global_bitmap (bitmap src) +{ + bitmap dst; + if (!src) + return NULL; + if (src == all_module_statics) + return all_module_statics; + dst = BITMAP_ALLOC (&global_info_obstack); + bitmap_copy (dst, src); + return dst; +} + +/* Called when new clone is inserted to callgraph late. */ + +static void +duplicate_node_data (struct cgraph_node *src, struct cgraph_node *dst, + void *data ATTRIBUTE_UNUSED) +{ + ipa_reference_global_vars_info_t ginfo; + ipa_reference_local_vars_info_t linfo; + ipa_reference_global_vars_info_t dst_ginfo; + ipa_reference_local_vars_info_t dst_linfo; + + ginfo = get_global_reference_vars_info (src); + linfo = get_local_reference_vars_info (src); + if (!linfo && !ginfo) + return; + init_function_info (dst); + if (linfo) + { + dst_linfo = get_local_reference_vars_info (dst); + dst_linfo->statics_read = copy_local_bitmap (linfo->statics_read); + dst_linfo->statics_written = copy_local_bitmap (linfo->statics_written); + dst_linfo->calls_read_all = linfo->calls_read_all; + dst_linfo->calls_write_all = linfo->calls_write_all; + } + if (ginfo) + { + get_reference_vars_info (dst)->global = XCNEW (struct ipa_reference_global_vars_info_d); + dst_ginfo = get_global_reference_vars_info (dst); + dst_ginfo->statics_read = copy_global_bitmap (ginfo->statics_read); + dst_ginfo->statics_written = copy_global_bitmap (ginfo->statics_written); + dst_ginfo->statics_not_read = copy_global_bitmap (ginfo->statics_not_read); + dst_ginfo->statics_not_written = copy_global_bitmap (ginfo->statics_not_written); + } +} + +/* Called when node is removed. */ + +static void +remove_node_data (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) +{ + if (get_reference_vars_info (node)) + clean_function (node); +} + +/* Analyze each function in the cgraph to see which global or statics + are read or written. */ + +static void +generate_summary (void) +{ + struct cgraph_node *node; + struct varpool_node *vnode; + unsigned int index; + bitmap_iterator bi; + bitmap module_statics_readonly; + bitmap bm_temp; + + function_insertion_hook_holder = + cgraph_add_function_insertion_hook (&add_new_function, NULL); + node_removal_hook_holder = + cgraph_add_node_removal_hook (&remove_node_data, NULL); + node_duplication_hook_holder = + cgraph_add_node_duplication_hook (&duplicate_node_data, NULL); + ipa_init (); + module_statics_readonly = BITMAP_ALLOC (&local_info_obstack); + bm_temp = BITMAP_ALLOC (&local_info_obstack); + + /* Process all of the variables first. */ + FOR_EACH_STATIC_INITIALIZER (vnode) + analyze_variable (vnode); + + /* Process all of the functions next. + + We do not want to process any of the clones so we check that this + is a master clone. However, we do need to process any + AVAIL_OVERWRITABLE functions (these are never clones) because + they may cause a static variable to escape. The code that can + overwrite such a function cannot access the statics because it + would not be in the same compilation unit. When the analysis is + finished, the computed information of these AVAIL_OVERWRITABLE is + replaced with worst case info. + */ + for (node = cgraph_nodes; node; node = node->next) + if (cgraph_function_body_availability (node) >= AVAIL_OVERWRITABLE) + analyze_function (node); + + pointer_set_destroy (visited_nodes); + visited_nodes = NULL; + + /* Prune out the variables that were found to behave badly + (i.e. have their address taken). */ + EXECUTE_IF_SET_IN_BITMAP (module_statics_escape, 0, index, bi) + { + splay_tree_remove (reference_vars_to_consider, index); + } + + bitmap_and_compl_into (all_module_statics, + module_statics_escape); + + bitmap_and_compl (module_statics_readonly, all_module_statics, + module_statics_written); + + /* If the address is not taken, we can unset the addressable bit + on this variable. */ + EXECUTE_IF_SET_IN_BITMAP (all_module_statics, 0, index, bi) + { + tree var = get_static_decl (index); + TREE_ADDRESSABLE (var) = 0; + if (dump_file) + fprintf (dump_file, "Not TREE_ADDRESSABLE var %s\n", + get_static_name (index)); + } + + /* If the variable is never written, we can set the TREE_READONLY + flag. Additionally if it has a DECL_INITIAL that is made up of + constants we can treat the entire global as a constant. */ + + bitmap_and_compl (module_statics_readonly, all_module_statics, + module_statics_written); + EXECUTE_IF_SET_IN_BITMAP (module_statics_readonly, 0, index, bi) + { + tree var = get_static_decl (index); + + /* Ignore variables in named sections - changing TREE_READONLY + changes the section flags, potentially causing conflicts with + other variables in the same named section. */ + if (DECL_SECTION_NAME (var) == NULL_TREE) + { + TREE_READONLY (var) = 1; + if (dump_file) + fprintf (dump_file, "read-only var %s\n", + get_static_name (index)); + } + } + + BITMAP_FREE(module_statics_escape); + BITMAP_FREE(module_statics_written); + module_statics_escape = NULL; + module_statics_written = NULL; + + if (dump_file) + EXECUTE_IF_SET_IN_BITMAP (all_module_statics, 0, index, bi) + { + fprintf (dump_file, "\nPromotable global:%s", + get_static_name (index)); + } + + for (node = cgraph_nodes; node; node = node->next) + if (cgraph_function_body_availability (node) >= AVAIL_OVERWRITABLE) + { + ipa_reference_local_vars_info_t l; + l = get_reference_vars_info (node)->local; + + /* Any variables that are not in all_module_statics are + removed from the local maps. This will include all of the + variables that were found to escape in the function + scanning. */ + bitmap_and_into (l->statics_read, + all_module_statics); + bitmap_and_into (l->statics_written, + all_module_statics); + } + + BITMAP_FREE(module_statics_readonly); + BITMAP_FREE(bm_temp); + + if (dump_file) + for (node = cgraph_nodes; node; node = node->next) + if (cgraph_function_body_availability (node) >= AVAIL_OVERWRITABLE) + { + ipa_reference_local_vars_info_t l; + unsigned int index; + bitmap_iterator bi; + + l = get_reference_vars_info (node)->local; + fprintf (dump_file, + "\nFunction name:%s/%i:", + cgraph_node_name (node), node->uid); + fprintf (dump_file, "\n locals read: "); + EXECUTE_IF_SET_IN_BITMAP (l->statics_read, + 0, index, bi) + { + fprintf (dump_file, "%s ", + get_static_name (index)); + } + fprintf (dump_file, "\n locals written: "); + EXECUTE_IF_SET_IN_BITMAP (l->statics_written, + 0, index, bi) + { + fprintf(dump_file, "%s ", + get_static_name (index)); + } + if (l->calls_read_all) + fprintf (dump_file, "\n calls read all: "); + if (l->calls_write_all) + fprintf (dump_file, "\n calls read all: "); + } +} + +/* Produce the global information by preforming a transitive closure + on the local information that was produced by ipa_analyze_function + and ipa_analyze_variable. */ + +static unsigned int +propagate (void) +{ + struct cgraph_node *node; + struct cgraph_node *w; + struct cgraph_node **order = + XCNEWVEC (struct cgraph_node *, cgraph_n_nodes); + int order_pos = ipa_utils_reduced_inorder (order, false, true); + int i; + + cgraph_remove_function_insertion_hook (function_insertion_hook_holder); + if (dump_file) + dump_cgraph (dump_file); + + /* Propagate the local information thru the call graph to produce + the global information. All the nodes within a cycle will have + the same info so we collapse cycles first. Then we can do the + propagation in one pass from the leaves to the roots. */ + order_pos = ipa_utils_reduced_inorder (order, true, true); + if (dump_file) + ipa_utils_print_order(dump_file, "reduced", order, order_pos); + + for (i = 0; i < order_pos; i++ ) + { + ipa_reference_vars_info_t node_info; + ipa_reference_global_vars_info_t node_g = + XCNEW (struct ipa_reference_global_vars_info_d); + ipa_reference_local_vars_info_t node_l; + + bool read_all; + bool write_all; + struct ipa_dfs_info * w_info; + + node = order[i]; + node_info = get_reference_vars_info (node); + if (!node_info) + { + dump_cgraph_node (stderr, node); + dump_cgraph (stderr); + gcc_unreachable (); + } + + gcc_assert (!node_info->global); + node_l = node_info->local; + + read_all = node_l->calls_read_all; + write_all = node_l->calls_write_all; + + /* If any node in a cycle is calls_read_all or calls_write_all + they all are. */ + w_info = (struct ipa_dfs_info *) node->aux; + w = w_info->next_cycle; + while (w) + { + ipa_reference_local_vars_info_t w_l = + get_reference_vars_info (w)->local; + read_all |= w_l->calls_read_all; + write_all |= w_l->calls_write_all; + + w_info = (struct ipa_dfs_info *) w->aux; + w = w_info->next_cycle; + } + + /* Initialized the bitmaps for the reduced nodes */ + if (read_all) + node_g->statics_read = all_module_statics; + else + { + node_g->statics_read = BITMAP_ALLOC (&global_info_obstack); + bitmap_copy (node_g->statics_read, + node_l->statics_read); + } + + if (write_all) + node_g->statics_written = all_module_statics; + else + { + node_g->statics_written = BITMAP_ALLOC (&global_info_obstack); + bitmap_copy (node_g->statics_written, + node_l->statics_written); + } + + propagate_bits (node_g, node); + w_info = (struct ipa_dfs_info *) node->aux; + w = w_info->next_cycle; + while (w) + { + ipa_reference_vars_info_t w_ri = + get_reference_vars_info (w); + ipa_reference_local_vars_info_t w_l = w_ri->local; + + /* These global bitmaps are initialized from the local info + of all of the nodes in the region. However there is no + need to do any work if the bitmaps were set to + all_module_statics. */ + if (!read_all) + bitmap_ior_into (node_g->statics_read, + w_l->statics_read); + if (!write_all) + bitmap_ior_into (node_g->statics_written, + w_l->statics_written); + propagate_bits (node_g, w); + w_info = (struct ipa_dfs_info *) w->aux; + w = w_info->next_cycle; + } + + /* All nodes within a cycle have the same global info bitmaps. */ + node_info->global = node_g; + w_info = (struct ipa_dfs_info *) node->aux; + w = w_info->next_cycle; + while (w) + { + ipa_reference_vars_info_t w_ri = + get_reference_vars_info (w); + + gcc_assert (!w_ri->global); + w_ri->global = XCNEW (struct ipa_reference_global_vars_info_d); + w_ri->global->statics_read = copy_global_bitmap (node_g->statics_read); + w_ri->global->statics_written = copy_global_bitmap (node_g->statics_written); + + w_info = (struct ipa_dfs_info *) w->aux; + w = w_info->next_cycle; + } + } + + if (dump_file) + { + for (i = 0; i < order_pos; i++ ) + { + ipa_reference_vars_info_t node_info; + ipa_reference_global_vars_info_t node_g; + ipa_reference_local_vars_info_t node_l; + unsigned int index; + bitmap_iterator bi; + struct ipa_dfs_info * w_info; + + node = order[i]; + node_info = get_reference_vars_info (node); + node_g = node_info->global; + node_l = node_info->local; + fprintf (dump_file, + "\nFunction name:%s/%i:", + cgraph_node_name (node), node->uid); + fprintf (dump_file, "\n locals read: "); + EXECUTE_IF_SET_IN_BITMAP (node_l->statics_read, + 0, index, bi) + { + fprintf (dump_file, "%s ", + get_static_name (index)); + } + fprintf (dump_file, "\n locals written: "); + EXECUTE_IF_SET_IN_BITMAP (node_l->statics_written, + 0, index, bi) + { + fprintf(dump_file, "%s ", + get_static_name (index)); + } + + w_info = (struct ipa_dfs_info *) node->aux; + w = w_info->next_cycle; + while (w) + { + ipa_reference_vars_info_t w_ri = + get_reference_vars_info (w); + ipa_reference_local_vars_info_t w_l = w_ri->local; + fprintf (dump_file, "\n next cycle: %s/%i ", + cgraph_node_name (w), w->uid); + fprintf (dump_file, "\n locals read: "); + EXECUTE_IF_SET_IN_BITMAP (w_l->statics_read, + 0, index, bi) + { + fprintf (dump_file, "%s ", + get_static_name (index)); + } + + fprintf (dump_file, "\n locals written: "); + EXECUTE_IF_SET_IN_BITMAP (w_l->statics_written, + 0, index, bi) + { + fprintf(dump_file, "%s ", + get_static_name (index)); + } + + + w_info = (struct ipa_dfs_info *) w->aux; + w = w_info->next_cycle; + } + fprintf (dump_file, "\n globals read: "); + EXECUTE_IF_SET_IN_BITMAP (node_g->statics_read, + 0, index, bi) + { + fprintf (dump_file, "%s ", + get_static_name (index)); + } + fprintf (dump_file, "\n globals written: "); + EXECUTE_IF_SET_IN_BITMAP (node_g->statics_written, + 0, index, bi) + { + fprintf (dump_file, "%s ", + get_static_name (index)); + } + } + } + + /* Cleanup. */ + for (i = 0; i < order_pos; i++ ) + { + ipa_reference_vars_info_t node_info; + ipa_reference_global_vars_info_t node_g; + node = order[i]; + node_info = get_reference_vars_info (node); + node_g = node_info->global; + + /* Create the complimentary sets. These are more useful for + certain apis. */ + node_g->statics_not_read = BITMAP_ALLOC (&global_info_obstack); + node_g->statics_not_written = BITMAP_ALLOC (&global_info_obstack); + + if (node_g->statics_read != all_module_statics) + bitmap_and_compl (node_g->statics_not_read, + all_module_statics, + node_g->statics_read); + + if (node_g->statics_written + != all_module_statics) + bitmap_and_compl (node_g->statics_not_written, + all_module_statics, + node_g->statics_written); + } + + free (order); + + for (node = cgraph_nodes; node; node = node->next) + { + ipa_reference_vars_info_t node_info; + node_info = get_reference_vars_info (node); + /* Get rid of the aux information. */ + + if (node->aux) + { + free (node->aux); + node->aux = NULL; + } + + if (cgraph_function_body_availability (node) == AVAIL_OVERWRITABLE) + clean_function (node); + else if (node_info) + clean_function_local_data (node); + } + bitmap_obstack_release (&local_info_obstack); + return 0; +} + + +static bool +gate_reference (void) +{ + return (flag_ipa_reference + /* Don't bother doing anything if the program has errors. */ + && !(errorcount || sorrycount)); +} + +struct ipa_opt_pass pass_ipa_reference = +{ + { + IPA_PASS, + "static-var", /* name */ + gate_reference, /* gate */ + propagate, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_IPA_REFERENCE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0 /* todo_flags_finish */ + }, + generate_summary, /* generate_summary */ + NULL, /* write_summary */ + NULL, /* read_summary */ + NULL, /* function_read_summary */ + 0, /* TODOs */ + NULL, /* function_transform */ + NULL /* variable_transform */ +}; + +#include "gt-ipa-reference.h"