/* Callgraph based analysis of static variables.
- Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
This file is part of GCC.
#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
static struct pointer_set_t *visited_nodes;
-static bitmap_obstack ipa_obstack;
+/* 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
{
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_from_cgraph (struct cgraph_node * node)
+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)
{
- return get_function_ann (node->decl)->reference_vars_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 (tree fn)
+get_local_reference_vars_info (struct cgraph_node *fn)
{
- ipa_reference_vars_info_t info = get_function_ann (fn)->reference_vars_info;
+ ipa_reference_vars_info_t info = get_reference_vars_info (fn);
if (info)
return info->local;
variables for function FN. */
static ipa_reference_global_vars_info_t
-get_global_reference_vars_info (tree fn)
+get_global_reference_vars_info (struct cgraph_node *fn)
{
- ipa_reference_vars_info_t info = get_function_ann (fn)->reference_vars_info;
+ ipa_reference_vars_info_t info = get_reference_vars_info (fn);
if (info)
return info->global;
}
/* Return a bitmap indexed by VAR_DECL uid for the static variables
- that may be read locally by the execution of the function fn.
- Returns NULL if no data is available. */
-
-bitmap
-ipa_reference_get_read_local (tree fn)
-{
- ipa_reference_local_vars_info_t l = get_local_reference_vars_info (fn);
- if (l)
- return l->statics_read;
- else
- return NULL;
-}
-
-/* Return a bitmap indexed by VAR_DECL uid for the static variables
- that may be written locally by the execution of the function fn.
- Returns NULL if no data is available. */
-
-bitmap
-ipa_reference_get_written_local (tree fn)
-{
- ipa_reference_local_vars_info_t l = get_local_reference_vars_info (fn);
- if (l)
- return l->statics_written;
- else
- 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 (tree fn)
+ipa_reference_get_read_global (struct cgraph_node *fn)
{
ipa_reference_global_vars_info_t g = get_global_reference_vars_info (fn);
if (g)
call. Returns NULL if no data is available. */
bitmap
-ipa_reference_get_written_global (tree fn)
+ipa_reference_get_written_global (struct cgraph_node *fn)
{
ipa_reference_global_vars_info_t g = get_global_reference_vars_info (fn);
if (g)
NULL if no data is available. */
bitmap
-ipa_reference_get_not_read_global (tree fn)
+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)
call. Returns NULL if no data is available. */
bitmap
-ipa_reference_get_not_written_global (tree fn)
+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)
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,
return true;
}
-/* If T is a VAR_DECL for a static that we are interested in, add the
- uid to the bitmap. */
+/* Mark tree T as having address taken. */
static void
-check_operand (ipa_reference_local_vars_info_t local,
- tree t, bool checking_write)
+mark_address_taken (tree x)
{
- if (!t) return;
-
- if ((TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == FUNCTION_DECL)
- && (has_proper_scope_for_analysis (t)))
- {
- if (checking_write)
- {
- if (local)
- bitmap_set_bit (local->statics_written, DECL_UID (t));
- /* Mark the write so we can tell which statics are
- readonly. */
- bitmap_set_bit (module_statics_written, DECL_UID (t));
- }
- else if (local)
- bitmap_set_bit (local->statics_read, DECL_UID (t));
- }
+ if (TREE_CODE (x) == VAR_DECL
+ && module_statics_escape && has_proper_scope_for_analysis (x))
+ bitmap_set_bit (module_statics_escape, DECL_UID (x));
}
-/* Examine tree T for references to static variables. All internal
- references like array references or indirect references are added
- to the READ_BM. Direct references are added to either READ_BM or
- WRITE_BM depending on the value of CHECKING_WRITE. */
+/* Mark load of T. */
static void
-check_tree (ipa_reference_local_vars_info_t local, tree t, bool checking_write)
+mark_load (ipa_reference_local_vars_info_t local,
+ tree t)
{
- if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR))
- return;
-
- while (TREE_CODE (t) == REALPART_EXPR
- || TREE_CODE (t) == IMAGPART_EXPR
- || handled_component_p (t))
- {
- if (TREE_CODE (t) == ARRAY_REF)
- check_operand (local, TREE_OPERAND (t, 1), false);
- t = TREE_OPERAND (t, 0);
- }
-
- /* The bottom of an indirect reference can only be read, not
- written. So just recurse and whatever we find, check it against
- the read bitmaps. */
-
- /* if (INDIRECT_REF_P (t) || TREE_CODE (t) == MEM_REF) */
- /* FIXME when we have array_ref's of pointers. */
- if (INDIRECT_REF_P (t))
- check_tree (local, TREE_OPERAND (t, 0), false);
-
- if (SSA_VAR_P (t))
- check_operand (local, t, checking_write);
+ if (TREE_CODE (t) == VAR_DECL
+ && has_proper_scope_for_analysis (t))
+ bitmap_set_bit (local->statics_read, DECL_UID (t));
}
-/* Scan tree T to see if there are any addresses taken in within T. */
+/* Mark store of T. */
-static void
-look_for_address_of (tree t)
+static void
+mark_store (ipa_reference_local_vars_info_t local,
+ tree t)
{
- if (TREE_CODE (t) == ADDR_EXPR)
+ if (TREE_CODE (t) == VAR_DECL
+ && has_proper_scope_for_analysis (t))
{
- tree x = get_base_var (t);
- if (TREE_CODE (x) == VAR_DECL || TREE_CODE (x) == FUNCTION_DECL)
- if (has_proper_scope_for_analysis (x))
- bitmap_set_bit (module_statics_escape, DECL_UID (x));
+ 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));
}
}
-/* Check to see if T is a read or address of operation on a static var
- we are interested in analyzing. LOCAL is passed in to get access
- to its bit vectors. Local is NULL if this is called from a static
- initializer. */
-
-static void
-check_rhs_var (ipa_reference_local_vars_info_t local, tree t)
-{
- look_for_address_of (t);
-
- if (local == NULL)
- return;
-
- check_tree(local, t, false);
-}
-
-/* Check to see if T is an assignment to a static var we are
- interested in analyzing. LOCAL is passed in to get access to its bit
- vectors. */
-
-static void
-check_lhs_var (ipa_reference_local_vars_info_t local, tree t)
-{
- if (local == NULL)
- return;
-
- check_tree(local, t, true);
-}
-
-/* This is a scaled down version of get_asm_expr_operands from
- tree_ssa_operands.c. The version there runs much later and assumes
- that aliasing information is already available. Here we are just
- trying to find if the set of inputs and outputs contain references
- or address of operations to local static variables. FN is the
- function being analyzed and STMT is the actual asm statement. */
+/* Look for memory clobber and set read_all/write_all if present. */
static void
-get_asm_stmt_operands (ipa_reference_local_vars_info_t local, gimple stmt)
+check_asm_memory_clobber (ipa_reference_local_vars_info_t local, gimple stmt)
{
- size_t noutputs = gimple_asm_noutputs (stmt);
- const char **oconstraints
- = (const char **) alloca ((noutputs) * sizeof (const char *));
size_t i;
tree op;
- const char *constraint;
- bool allows_mem, allows_reg, is_inout;
-
- for (i = 0; i < noutputs; i++)
- {
- op = gimple_asm_output_op (stmt, i);
- oconstraints[i] = constraint
- = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
- parse_output_constraint (&constraint, i, 0, 0,
- &allows_mem, &allows_reg, &is_inout);
-
- check_lhs_var (local, TREE_VALUE (op));
- }
-
- for (i = 0; i < gimple_asm_ninputs (stmt); i++)
- {
- op = gimple_asm_input_op (stmt, i);
- constraint
- = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
- parse_input_constraint (&constraint, 0, 0, noutputs, 0,
- oconstraints, &allows_mem, &allows_reg);
-
- check_rhs_var (local, TREE_VALUE (op));
- }
for (i = 0; i < gimple_asm_nclobbers (stmt); i++)
{
}
}
-/* Check the parameters of a function call from CALLER to CALL_EXPR to
- see if any of them are static vars. Also check to see if this is
- either an indirect call, a call outside the compilation unit, or
- has special attributes that effect the clobbers. The caller
- parameter is the tree node for the caller and the second operand is
- the tree node for the entire call expression. */
+/* 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 operand;
tree callee_t = gimple_call_fndecl (stmt);
enum availability avail = AVAIL_NOT_AVAILABLE;
- size_t i;
-
- if ((operand = gimple_call_lhs (stmt)) != NULL)
- check_lhs_var (local, operand);
-
- for (i = 0; i < gimple_call_num_args (stmt); i++)
- check_rhs_var (local, gimple_call_arg (stmt, i));
if (callee_t)
{
avail = cgraph_function_body_availability (callee);
}
- if (avail == AVAIL_NOT_AVAILABLE || avail == AVAIL_OVERWRITABLE)
+ if (avail <= AVAIL_OVERWRITABLE)
if (local)
{
- if (flags & ECF_PURE)
+ if (flags & ECF_CONST)
+ ;
+ else if (flags & ECF_PURE)
local->calls_read_all = true;
else
{
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. */
-/* FIXME: When this is converted to run over SSA form, this code
- should be converted to use the operand scanner. */
-
static tree
-scan_stmt_for_static_refs (gimple_stmt_iterator *gsip, bool *handled_ops_p,
- struct walk_stmt_info *data)
+scan_stmt_for_static_refs (gimple_stmt_iterator *gsip,
+ struct cgraph_node *fn)
{
- struct cgraph_node *fn = (struct cgraph_node *) data->info;
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_from_cgraph (fn)->local;
+ 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_ASSIGN:
- {
- /* First look on the lhs and see what variable is stored to */
- tree lhs = gimple_assign_lhs (stmt);
- tree rhs1 = gimple_assign_rhs1 (stmt);
- tree rhs2 = gimple_assign_rhs2 (stmt);
- enum tree_code code = gimple_assign_rhs_code (stmt);
-
- check_lhs_var (local, lhs);
-
- /* For the purposes of figuring out what the cast affects */
-
- /* Next check the operands on the rhs to see if they are ok. */
- switch (TREE_CODE_CLASS (code))
- {
- case tcc_binary:
- case tcc_comparison:
- check_rhs_var (local, rhs1);
- check_rhs_var (local, rhs2);
- break;
-
- case tcc_unary:
- case tcc_reference:
- case tcc_declaration:
- check_rhs_var (local, rhs1);
- break;
-
- case tcc_expression:
- switch (code)
- {
- case ADDR_EXPR:
- check_rhs_var (local, rhs1);
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- *handled_ops_p = true;
- }
- break;
-
- case GIMPLE_LABEL:
- if (DECL_NONLOCAL (gimple_label_label (stmt)))
- {
- /* Target of long jump. */
- local->calls_read_all = true;
- local->calls_write_all = true;
- }
- break;
-
case GIMPLE_CALL:
check_call (local, stmt);
- *handled_ops_p = true;
break;
case GIMPLE_ASM:
- get_asm_stmt_operands (local, stmt);
- *handled_ops_p = true;
+ 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 GIMPLE operands for static references. This is supposed
- to work with scan_stmt_for_static_refs so the real call-back data is stored
- inside a walk_stmt_info struct. Callers using the walk_tree interface must
- also wrap the call-back data in a walk_stmt_info struct. */
+/* Call-back to scan variable initializers for static references.
+ Called using walk_tree. */
static tree
-scan_op_for_static_refs (tree *tp, int *walk_subtrees, void *data)
+scan_initializer_for_static_refs (tree *tp, int *walk_subtrees,
+ void *data ATTRIBUTE_UNUSED)
{
- struct walk_stmt_info *wi = (struct walk_stmt_info*) data;
- struct cgraph_node *fn = (struct cgraph_node *) wi->info;
tree t = *tp;
- ipa_reference_local_vars_info_t local = NULL;
- if (fn)
- local = get_reference_vars_info_from_cgraph (fn)->local;
- switch (TREE_CODE (t))
+ if (TREE_CODE (t) == ADDR_EXPR)
{
- case VAR_DECL:
- if (DECL_INITIAL (t))
- walk_tree (&DECL_INITIAL (t), scan_op_for_static_refs, data,
- wi->pset);
- *walk_subtrees = 0;
- break;
-
- case ADDR_EXPR:
- /* This case is here to find addresses on rhs of constructors in
- decl_initial of static variables. */
- check_rhs_var (local, t);
+ mark_address_taken (get_base_var (t));
*walk_subtrees = 0;
- break;
-
- default:
- break;
}
+ /* 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;
}
return NULL;
}
-/* Or in all of the bits from every callee into X, the caller's, bit
- vector. There are several cases to check to avoid the sparse
+/* 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 (struct cgraph_node *x)
+propagate_bits (ipa_reference_global_vars_info_t x_global, struct cgraph_node *x)
{
- ipa_reference_vars_info_t x_info = get_reference_vars_info_from_cgraph (x);
- ipa_reference_global_vars_info_t x_global = x_info->global;
-
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. */
- y = cgraph_master_clone (y);
- if (y)
+ if (cgraph_function_body_availability (e->callee) > AVAIL_OVERWRITABLE)
{
- if (get_reference_vars_info_from_cgraph (y))
+ if (get_reference_vars_info (y))
{
- ipa_reference_vars_info_t y_info = get_reference_vars_info_from_cgraph (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)
}
}
else
- {
- gcc_unreachable ();
- }
- }
- }
-}
-
-/* Look at all of the callees of X to see which ones represent inlined
- calls. For each of these callees, merge their local info into
- TARGET and check their children recursively.
-
- This function goes away when Jan changes the inliner and IPA
- analysis so that this is not run between the time when inlining
- decisions are made and when the inlining actually occurs. */
-
-static void
-merge_callee_local_info (struct cgraph_node *target,
- struct cgraph_node *x)
-{
- struct cgraph_edge *e;
- ipa_reference_local_vars_info_t x_l =
- get_reference_vars_info_from_cgraph (target)->local;
-
- /* Make the world safe for tail recursion. */
- struct ipa_dfs_info *node_info = (struct ipa_dfs_info *) x->aux;
-
- if (node_info->aux)
- return;
-
- node_info->aux = x;
-
- for (e = x->callees; e; e = e->next_callee)
- {
- struct cgraph_node *y = e->callee;
- if (y->global.inlined_to)
- {
- ipa_reference_vars_info_t y_info;
- ipa_reference_local_vars_info_t y_l;
- struct cgraph_node* orig_y = y;
-
- y = cgraph_master_clone (y);
- if (y)
- {
- y_info = get_reference_vars_info_from_cgraph (y);
- y_l = y_info->local;
- if (x_l != y_l)
- {
- bitmap_ior_into (x_l->statics_read,
- y_l->statics_read);
- bitmap_ior_into (x_l->statics_written,
- y_l->statics_written);
- }
- x_l->calls_read_all |= y_l->calls_read_all;
- x_l->calls_write_all |= y_l->calls_write_all;
- merge_callee_local_info (target, y);
- }
- else
- {
- fprintf(stderr, "suspect inlining of ");
- dump_cgraph_node (stderr, orig_y);
- fprintf(stderr, "\ninto ");
- dump_cgraph_node (stderr, target);
- dump_cgraph (stderr);
- gcc_assert(false);
- }
+ gcc_unreachable ();
}
}
-
- node_info->aux = NULL;
}
/* The init routine for analyzing global static variable usage. See
static void
ipa_init (void)
{
- struct cgraph_node *node;
memory_identifier_string = build_string(7, "memory");
reference_vars_to_consider =
splay_tree_new_ggc (splay_tree_compare_ints);
- bitmap_obstack_initialize (&ipa_obstack);
- module_statics_escape = BITMAP_ALLOC (&ipa_obstack);
- module_statics_written = BITMAP_ALLOC (&ipa_obstack);
- all_module_statics = BITMAP_ALLOC (&ipa_obstack);
-
- /* This will add NODE->DECL to the splay trees. */
- for (node = cgraph_nodes; node; node = node->next)
- has_proper_scope_for_analysis (node->decl);
+ 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
memset (&wi, 0, sizeof (wi));
wi.pset = visited_nodes;
- walk_tree (&DECL_INITIAL (global), scan_op_for_static_refs,
+ walk_tree (&DECL_INITIAL (global), scan_initializer_for_static_refs,
&wi, wi.pset);
}
-/* This is the main routine for finding the reference patterns for
- global variables within a function FN. */
+/* Set up the persistent info for FN. */
-static void
-analyze_function (struct cgraph_node *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);
- tree decl = fn->decl;
- struct walk_stmt_info wi;
/* Add the info to the tree's annotation. */
- get_function_ann (fn->decl)->reference_vars_info = info;
+ set_reference_vars_info (fn, info);
info->local = l;
- l->statics_read = BITMAP_ALLOC (&ipa_obstack);
- l->statics_written = BITMAP_ALLOC (&ipa_obstack);
+ l->statics_read = BITMAP_ALLOC (&local_info_obstack);
+ l->statics_written = BITMAP_ALLOC (&local_info_obstack);
- if (dump_file)
- fprintf (dump_file, "\n local analysis of %s\n", cgraph_node_name (fn));
-
- {
- struct function *this_cfun = DECL_STRUCT_FUNCTION (decl);
- basic_block this_block;
+ return l;
+}
- 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)
- check_rhs_var (l, op);
- }
- }
+/* 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
- memset (&wi, 0, sizeof (wi));
- wi.info = fn;
- wi.pset = visited_nodes;
- for (gsi = gsi_start_bb (this_block); !gsi_end_p (gsi); gsi_next (&gsi))
- walk_gimple_stmt (&gsi, scan_stmt_for_static_refs,
- scan_op_for_static_refs, &wi);
- }
- }
+ if (dump_file)
+ fprintf (dump_file, "\n local analysis of %s\n", cgraph_node_name (fn));
- /* There may be const decls with interesting right hand sides. */
- if (DECL_STRUCT_FUNCTION (decl))
+ push_cfun (DECL_STRUCT_FUNCTION (decl));
+ current_function_decl = decl;
+
+ init_function_info (fn);
+ FOR_EACH_BB_FN (this_block, this_cfun)
{
- tree step;
- for (step = DECL_STRUCT_FUNCTION (decl)->local_decls;
- step;
- step = TREE_CHAIN (step))
+ 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))
{
- tree var = TREE_VALUE (step);
- if (TREE_CODE (var) == VAR_DECL
- && DECL_INITIAL (var)
- && !TREE_STATIC (var))
+ phi = gsi_stmt (gsi);
+ FOR_EACH_PHI_ARG (use, phi, iter, SSA_OP_USE)
{
- memset (&wi, 0, sizeof (wi));
- wi.info = fn;
- wi.pset = visited_nodes;
- walk_tree (&DECL_INITIAL (var), scan_op_for_static_refs,
- &wi, wi.pset);
+ 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);
}
-}
-/* If FN is avail == AVAIL_OVERWRITABLE, replace the effects bit
- vectors with worst case bit vectors. We had to analyze it above to
- find out if it took the address of any statics. However, now that
- we know that, we can get rid of all of the other side effects. */
+#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 (struct cgraph_node *fn)
+clean_function_local_data (struct cgraph_node *fn)
{
- ipa_reference_vars_info_t info = get_reference_vars_info_from_cgraph (fn);
+ ipa_reference_vars_info_t info = get_reference_vars_info (fn);
ipa_reference_local_vars_info_t l = info->local;
- ipa_reference_global_vars_info_t g = info->global;
-
if (l)
{
if (l->statics_read
&&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_not_written != all_module_statics)
BITMAP_FREE (g->statics_not_written);
free (g);
+ info->global = NULL;
}
-
- free (get_function_ann (fn->decl)->reference_vars_info);
- get_function_ann (fn->decl)->reference_vars_info = NULL;
+ free (get_reference_vars_info (fn));
+ set_reference_vars_info (fn, NULL);
}
-\f
-/* Produce the global information by preforming a transitive closure
- on the local information that was produced by ipa_analyze_function
- and ipa_analyze_variable. */
+/* 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 unsigned int
-static_execute (void)
+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;
- 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;
-
+ 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)
replaced with worst case info.
*/
for (node = cgraph_nodes; node; node = node->next)
- if (node->analyzed
- && (cgraph_is_master_clone (node)
- || (cgraph_function_body_availability (node)
- == AVAIL_OVERWRITABLE)))
+ if (cgraph_function_body_availability (node) >= AVAIL_OVERWRITABLE)
analyze_function (node);
pointer_set_destroy (visited_nodes);
visited_nodes = NULL;
- if (dump_file)
- dump_cgraph (dump_file);
/* Prune out the variables that were found to behave badly
(i.e. have their address taken). */
- {
- unsigned int index;
- bitmap_iterator bi;
- bitmap module_statics_readonly = BITMAP_ALLOC (&ipa_obstack);
- bitmap bm_temp = BITMAP_ALLOC (&ipa_obstack);
-
- 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 (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)
{
- 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);
-
- /* Readonly on a function decl is very different from the
- variable. */
- if (TREE_CODE (var) == FUNCTION_DECL)
- continue;
-
- /* 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));
- }
+ fprintf (dump_file, "\nPromotable global:%s",
+ get_static_name (index));
}
-
- BITMAP_FREE(module_statics_escape);
- BITMAP_FREE(module_statics_written);
-
- 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 (i = 0; i < order_pos; i++ )
+
+ for (node = cgraph_nodes; node; node = node->next)
+ if (cgraph_function_body_availability (node) >= AVAIL_OVERWRITABLE)
{
ipa_reference_local_vars_info_t l;
- node = order[i];
- l = get_reference_vars_info_from_cgraph (node)->local;
-
+ 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);
+ all_module_statics);
bitmap_and_into (l->statics_written,
- all_module_statics);
+ all_module_statics);
}
-
- BITMAP_FREE(module_statics_readonly);
- BITMAP_FREE(bm_temp);
- }
-
+
+ BITMAP_FREE(module_statics_readonly);
+ BITMAP_FREE(bm_temp);
+
if (dump_file)
- {
- for (i = 0; i < order_pos; i++ )
+ for (node = cgraph_nodes; node; node = node->next)
+ if (cgraph_function_body_availability (node) >= AVAIL_OVERWRITABLE)
{
- unsigned int index;
ipa_reference_local_vars_info_t l;
+ unsigned int index;
bitmap_iterator bi;
-
- node = order[i];
- l = get_reference_vars_info_from_cgraph (node)->local;
+
+ l = get_reference_vars_info (node)->local;
fprintf (dump_file,
"\nFunction name:%s/%i:",
cgraph_node_name (node), node->uid);
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: ");
}
- }
+}
+\f
+/* 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
struct ipa_dfs_info * w_info;
node = order[i];
- node_info = get_reference_vars_info_from_cgraph (node);
+ node_info = get_reference_vars_info (node);
if (!node_info)
{
dump_cgraph_node (stderr, node);
gcc_unreachable ();
}
- node_info->global = node_g;
+ gcc_assert (!node_info->global);
node_l = node_info->local;
read_all = node_l->calls_read_all;
while (w)
{
ipa_reference_local_vars_info_t w_l =
- get_reference_vars_info_from_cgraph (w)->local;
+ get_reference_vars_info (w)->local;
read_all |= w_l->calls_read_all;
write_all |= w_l->calls_write_all;
node_g->statics_read = all_module_statics;
else
{
- node_g->statics_read = BITMAP_ALLOC (&ipa_obstack);
+ node_g->statics_read = BITMAP_ALLOC (&global_info_obstack);
bitmap_copy (node_g->statics_read,
node_l->statics_read);
}
node_g->statics_written = all_module_statics;
else
{
- node_g->statics_written = BITMAP_ALLOC (&ipa_obstack);
+ 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_from_cgraph (w);
+ get_reference_vars_info (w);
ipa_reference_local_vars_info_t w_l = w_ri->local;
-
- /* All nodes within a cycle share the same global info bitmaps. */
- w_ri->global = node_g;
/* These global bitmaps are initialized from the local info
of all of the nodes in the region. However there is no
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;
}
- w = node;
- while (w)
- {
- propagate_bits (w);
- w_info = (struct ipa_dfs_info *) w->aux;
- w = w_info->next_cycle;
- }
- }
-
- /* Need to fix up the local information sets. The information that
- has been gathered so far is preinlining. However, the
- compilation will progress post inlining so the local sets for the
- inlined calls need to be merged into the callers. Note that the
- local sets are not shared between all of the nodes in a cycle so
- those nodes in the cycle must be processed explicitly. */
- for (i = 0; i < order_pos; i++ )
- {
- struct ipa_dfs_info * w_info;
- node = order[i];
- merge_callee_local_info (node, node);
-
+ /* 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)
{
- merge_callee_local_info (w, 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;
}
struct ipa_dfs_info * w_info;
node = order[i];
- node_info = get_reference_vars_info_from_cgraph (node);
+ node_info = get_reference_vars_info (node);
node_g = node_info->global;
node_l = node_info->local;
fprintf (dump_file,
while (w)
{
ipa_reference_vars_info_t w_ri =
- get_reference_vars_info_from_cgraph (w);
+ 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);
ipa_reference_vars_info_t node_info;
ipa_reference_global_vars_info_t node_g;
node = order[i];
- node_info = get_reference_vars_info_from_cgraph (node);
+ 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 (&ipa_obstack);
- node_g->statics_not_written = BITMAP_ALLOC (&ipa_obstack);
+ 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);
- }
+ bitmap_and_compl (node_g->statics_not_read,
+ all_module_statics,
+ node_g->statics_read);
if (node_g->statics_written
!= all_module_statics)
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)
node->aux = NULL;
}
- if (node->analyzed
- && (cgraph_function_body_availability (node) == AVAIL_OVERWRITABLE))
+ 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;
}
&& !(errorcount || sorrycount));
}
-struct simple_ipa_opt_pass pass_ipa_reference =
+struct ipa_opt_pass pass_ipa_reference =
{
{
- SIMPLE_IPA_PASS,
+ IPA_PASS,
"static-var", /* name */
gate_reference, /* gate */
- static_execute, /* execute */
+ propagate, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
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"