diff options
author | dberlin <dberlin@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-01-19 01:42:48 +0000 |
---|---|---|
committer | dberlin <dberlin@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-01-19 01:42:48 +0000 |
commit | 7bbb6ff8a2c6a4b9be50769f8b7bce25ef3f75c9 (patch) | |
tree | 1bea89da9c401e1b8e235d514d1d027b81c0eece /gcc | |
parent | bd4dc62d8cc4f1bbfadf82a378e78a20630c597f (diff) | |
download | gcc-7bbb6ff8a2c6a4b9be50769f8b7bce25ef3f75c9.tar.gz |
2006-01-16 Daniel Berlin <dberlin@dberlin.org>
* tree-ssa-operands.h (ssa_call_clobbered_cache_valid): Remove.
(ssa_ro_call_cache_valid): Ditto.
* tree-ssa-alias.c (sort_tags_by_id): New function.
(init_transitive_clobber_worklist): Ditto.
(add_to_worklist): Ditto.
(mark_aliases_call_clobbered): Ditto.
(compute_tag_properties): Ditto.
(set_initial_properties): Ditto.
(compute_call_clobbered): Ditto.
(compute_may_aliases): Call compute_call_clobbered and grouping.
(compute_flow_sensitive_aliasing): Remove clobbering related code.
(compute_flow_insensitive_aliasing): Grouping now happens in our
caller.
(setup_pointers_and_addressables): Remove clobbering related code.
(add_may_alias): Ditto.
(replace_may_alias): Ditto.
(get_nmt_for): Ditto.
(create_global_var):
(is_escape_site): Return an escape_type enumeration.
* tree-flow-inline.h (is_call_clobbered): Global var does not
imply call clobbered.
(mark_call_clobbered): Take a reason for marking this. Remove
marking of globalness, and cache invalidation.
(clear_call_clobbered): Remove cache invalidation code.
* tree-dfa.c (dump_variable): If details is on, dump the reason
for escaping.
* tree-outof-ssa.c (create_temp): Copy escape mask from original
variable.
* tree-flow.h (struct ptr_info_def): Add escape mask member.
(struct var_ann_d): Ditto.
(enum escape_type): New.
(mark_call_clobbered): Adjust prototype.
* tree-ssa-structalias.c (update_alias_info): Unmodifiable vars
are never call clobbered.
Record reasons for escaping.
* tree-ssa-structalias.h (is_escape_site): Update prototype.
* tree-ssa-operands.c (ssa_call_clobbered_cache_valid): Remove.
(ssa_ro_call_cache_valid): Ditto.
(clobbered_v_may_defs): Ditto.
(clobbered_vuses): Ditto.
(ro_call_vuses): Ditto.
(clobber_stats): New.
(init_ssa_operands): Zero out clobber stats.
(fini_ssa_operands): Print out clobber stats.
(get_call_expr_operands): Pass callee fndecl to
add_call_read_ops).
(add_call_clobber_ops): Remove use of cache.
Add use of PURE_CONST information.
(add_call_read_ops): Remove use of cache.
Add use of static not_read information.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@109938 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 53 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/pr24287.c | 25 | ||||
-rw-r--r-- | gcc/tree-dfa.c | 38 | ||||
-rw-r--r-- | gcc/tree-flow-inline.h | 20 | ||||
-rw-r--r-- | gcc/tree-flow.h | 27 | ||||
-rw-r--r-- | gcc/tree-outof-ssa.c | 2 | ||||
-rw-r--r-- | gcc/tree-ssa-alias.c | 376 | ||||
-rw-r--r-- | gcc/tree-ssa-operands.c | 231 | ||||
-rw-r--r-- | gcc/tree-ssa-operands.h | 3 | ||||
-rw-r--r-- | gcc/tree-ssa-structalias.c | 13 | ||||
-rw-r--r-- | gcc/tree-ssa-structalias.h | 2 |
12 files changed, 571 insertions, 223 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9511cdba041..d8586406628 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,56 @@ +2006-01-16 Daniel Berlin <dberlin@dberlin.org> + + * tree-ssa-operands.h (ssa_call_clobbered_cache_valid): Remove. + (ssa_ro_call_cache_valid): Ditto. + * tree-ssa-alias.c (sort_tags_by_id): New function. + (init_transitive_clobber_worklist): Ditto. + (add_to_worklist): Ditto. + (mark_aliases_call_clobbered): Ditto. + (compute_tag_properties): Ditto. + (set_initial_properties): Ditto. + (compute_call_clobbered): Ditto. + (compute_may_aliases): Call compute_call_clobbered and grouping. + (compute_flow_sensitive_aliasing): Remove clobbering related code. + (compute_flow_insensitive_aliasing): Grouping now happens in our + caller. + (setup_pointers_and_addressables): Remove clobbering related code. + (add_may_alias): Ditto. + (replace_may_alias): Ditto. + (get_nmt_for): Ditto. + (create_global_var): + (is_escape_site): Return an escape_type enumeration. + * tree-flow-inline.h (is_call_clobbered): Global var does not + imply call clobbered. + (mark_call_clobbered): Take a reason for marking this. Remove + marking of globalness, and cache invalidation. + (clear_call_clobbered): Remove cache invalidation code. + * tree-dfa.c (dump_variable): If details is on, dump the reason + for escaping. + * tree-outof-ssa.c (create_temp): Copy escape mask from original + variable. + * tree-flow.h (struct ptr_info_def): Add escape mask member. + (struct var_ann_d): Ditto. + (enum escape_type): New. + (mark_call_clobbered): Adjust prototype. + * tree-ssa-structalias.c (update_alias_info): Unmodifiable vars + are never call clobbered. + Record reasons for escaping. + * tree-ssa-structalias.h (is_escape_site): Update prototype. + * tree-ssa-operands.c (ssa_call_clobbered_cache_valid): Remove. + (ssa_ro_call_cache_valid): Ditto. + (clobbered_v_may_defs): Ditto. + (clobbered_vuses): Ditto. + (ro_call_vuses): Ditto. + (clobber_stats): New. + (init_ssa_operands): Zero out clobber stats. + (fini_ssa_operands): Print out clobber stats. + (get_call_expr_operands): Pass callee fndecl to + add_call_read_ops). + (add_call_clobber_ops): Remove use of cache. + Add use of PURE_CONST information. + (add_call_read_ops): Remove use of cache. + Add use of static not_read information. + 2006-01-18 Alexandre Oliva <aoliva@redhat.com> Introduce TLS descriptors for i386 and x86_64. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6a64ab9a5d8..d901d414768 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2006-01-18 Daniel Berlin <dberlin@dberlin.org> + + * gcc.dg/tree-ssa/pr24287.c: New test + 2006-01-18 Eric Christopher <echristo@apple.com> * g++.dg/eh/table.C: New. diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c b/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c new file mode 100644 index 00000000000..8e7f18691dc --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr24287.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +int g1(int); +int h(int *a, int *b)__attribute__((pure)); +void link_error(); + +/* The calls to link_error should be eliminated, since nothing escapes to + non-pure functions. */ +int g(void) +{ + int t = 0, t1 = 2; + int t2 = h(&t, &t1); + if (t != 0) + link_error (); + if (t1 != 2) + link_error (); + g1(t2); + if (t != 0) + link_error (); + if (t1 != 2) + link_error (); + return t2 == 2; +} +/* { dg-final { scan-tree-dump-times "link_error" 0 "optimized"} } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c index 36b4a5bf953..19453780d42 100644 --- a/gcc/tree-dfa.c +++ b/gcc/tree-dfa.c @@ -363,7 +363,35 @@ dump_variable (FILE *file, tree var) fprintf (file, ", is volatile"); if (is_call_clobbered (var)) - fprintf (file, ", call clobbered"); + { + fprintf (file, ", call clobbered"); + if (dump_flags & TDF_DETAILS) + { + var_ann_t va = var_ann (var); + unsigned int escape_mask = va->escape_mask; + + fprintf (file, " ("); + if (escape_mask & ESCAPE_STORED_IN_GLOBAL) + fprintf (file, ", stored in global"); + if (escape_mask & ESCAPE_TO_ASM) + fprintf (file, ", goes through ASM"); + if (escape_mask & ESCAPE_TO_CALL) + fprintf (file, ", passed to call"); + if (escape_mask & ESCAPE_BAD_CAST) + fprintf (file, ", bad cast"); + if (escape_mask & ESCAPE_TO_RETURN) + fprintf (file, ", returned from func"); + if (escape_mask & ESCAPE_TO_PURE_CONST) + fprintf (file, ", passed to pure/const"); + if (escape_mask & ESCAPE_IS_GLOBAL) + fprintf (file, ", is global var"); + if (escape_mask & ESCAPE_IS_PARM) + fprintf (file, ", is incoming pointer"); + if (escape_mask & ESCAPE_UNKNOWN) + fprintf (file, ", unknown escape"); + fprintf (file, " )"); + } + } if (default_def (var)) { @@ -719,15 +747,11 @@ add_referenced_var (tree var, struct walk_state *walk_state) *slot = (void *) var; referenced_var_insert (DECL_UID (var), var); - - /* Global variables are always call-clobbered. */ - if (is_global_var (var)) - mark_call_clobbered (var); - + /* Tag's don't have DECL_INITIAL. */ if (MTAG_P (var)) return; - + /* Scan DECL_INITIAL for pointer variables as they may contain address arithmetic referencing the address of other variables. */ diff --git a/gcc/tree-flow-inline.h b/gcc/tree-flow-inline.h index 7e36ccc0e2a..69bef68f9ac 100644 --- a/gcc/tree-flow-inline.h +++ b/gcc/tree-flow-inline.h @@ -843,34 +843,26 @@ loop_containing_stmt (tree stmt) static inline bool is_call_clobbered (tree var) { - return is_global_var (var) - || bitmap_bit_p (call_clobbered_vars, DECL_UID (var)); + return bitmap_bit_p (call_clobbered_vars, DECL_UID (var)); } /* Mark variable VAR as being clobbered by function calls. */ static inline void -mark_call_clobbered (tree var) +mark_call_clobbered (tree var, unsigned int escape_type) { - /* If VAR is a memory tag, then we need to consider it a global - variable. This is because the pointer that VAR represents has - been found to point to either an arbitrary location or to a known - location in global memory. */ - if (MTAG_P (var) && TREE_CODE (var) != STRUCT_FIELD_TAG) - MTAG_GLOBAL (var) = 1; + var_ann (var)->escape_mask |= escape_type; bitmap_set_bit (call_clobbered_vars, DECL_UID (var)); - ssa_call_clobbered_cache_valid = false; - ssa_ro_call_cache_valid = false; } /* Clear the call-clobbered attribute from variable VAR. */ static inline void clear_call_clobbered (tree var) { + var_ann_t ann = var_ann (var); + ann->escape_mask = 0; if (MTAG_P (var) && TREE_CODE (var) != STRUCT_FIELD_TAG) MTAG_GLOBAL (var) = 0; bitmap_clear_bit (call_clobbered_vars, DECL_UID (var)); - ssa_call_clobbered_cache_valid = false; - ssa_ro_call_cache_valid = false; } /* Mark variable VAR as being non-addressable. */ @@ -879,8 +871,6 @@ mark_non_addressable (tree var) { bitmap_clear_bit (call_clobbered_vars, DECL_UID (var)); TREE_ADDRESSABLE (var) = 0; - ssa_call_clobbered_cache_valid = false; - ssa_ro_call_cache_valid = false; } /* Return the common annotation for T. Return NULL if the annotation diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index 92a6035c6a9..a766e391b17 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -92,6 +92,9 @@ struct ptr_info_def GTY(()) pointer will be represented by this memory tag, instead of the type tag computed by TBAA. */ tree name_mem_tag; + + /* Mask of reasons this pointer's value escapes the function */ + unsigned int escape_mask; }; @@ -213,6 +216,10 @@ struct var_ann_d GTY(()) /* If this variable is a structure, this fields holds a list of symbols representing each of the fields of the structure. */ subvar_t subvars; + + /* Mask of values saying the reasons why this variable has escaped + the function. */ + unsigned int escape_mask; }; struct function_ann_d GTY(()) @@ -751,9 +758,27 @@ enum move_pos }; extern enum move_pos movement_possibility (tree); +/* The reasons a variable may escape a function. */ +enum escape_type + { + NO_ESCAPE = 0, /* Doesn't escape. */ + ESCAPE_STORED_IN_GLOBAL = 1 << 1, + ESCAPE_TO_ASM = 1 << 2, /* Passed by address to an assembly + statement. */ + ESCAPE_TO_CALL = 1 << 3, /* Escapes to a function call. */ + ESCAPE_BAD_CAST = 1 << 4, /* Cast from pointer to integer */ + ESCAPE_TO_RETURN = 1 << 5, /* Returned from function. */ + ESCAPE_TO_PURE_CONST = 1 << 6, /* Escapes to a pure or constant + function call. */ + ESCAPE_IS_GLOBAL = 1 << 7, /* Is a global variable. */ + ESCAPE_IS_PARM = 1 << 8, /* Is an incoming function parameter. */ + ESCAPE_UNKNOWN = 1 << 9 /* We believe it escapes for some reason + not enumerated above. */ + }; + /* In tree-flow-inline.h */ static inline bool is_call_clobbered (tree); -static inline void mark_call_clobbered (tree); +static inline void mark_call_clobbered (tree, unsigned int); static inline void set_is_used (tree); static inline bool unmodifiable_var_p (tree); diff --git a/gcc/tree-outof-ssa.c b/gcc/tree-outof-ssa.c index 553f7a1f1fc..2f36cc6cc81 100644 --- a/gcc/tree-outof-ssa.c +++ b/gcc/tree-outof-ssa.c @@ -177,7 +177,7 @@ create_temp (tree t) 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); + mark_call_clobbered (tmp, var_ann (t)->escape_mask); return tmp; } diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index 5370747fc62..a890e11ce75 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -135,6 +135,287 @@ bitmap addressable_vars; having to keep track of too many V_MAY_DEF expressions at call sites. */ tree global_var; +DEF_VEC_I(int); +DEF_VEC_ALLOC_I(int,heap); + +/* qsort comparison function to sort type/name tags by DECL_UID. */ + +static int +sort_tags_by_id (const void *pa, const void *pb) +{ + tree a = *(tree *)pa; + tree b = *(tree *)pb; + + return DECL_UID (a) - DECL_UID (b); +} + +/* Initialize WORKLIST to contain those memory tags that are marked call + clobbered. Initialized WORKLIST2 to contain the reasons these + memory tags escaped. */ + +static void +init_transitive_clobber_worklist (VEC (tree, heap) **worklist, + VEC (int, heap) **worklist2) +{ + referenced_var_iterator rvi; + tree curr; + + FOR_EACH_REFERENCED_VAR (curr, rvi) + { + if (MTAG_P (curr) && is_call_clobbered (curr)) + { + VEC_safe_push (tree, heap, *worklist, curr); + VEC_safe_push (int, heap, *worklist2, var_ann (curr)->escape_mask); + } + } +} + +/* Add ALIAS to WORKLIST (and the reason for escaping REASON to WORKLIST2) if + ALIAS is not already marked call clobbered, and is a memory + tag. */ + +static void +add_to_worklist (tree alias, VEC (tree, heap) **worklist, + VEC (int, heap) **worklist2, + int reason) +{ + if (MTAG_P (alias) && !is_call_clobbered (alias)) + { + VEC_safe_push (tree, heap, *worklist, alias); + VEC_safe_push (int, heap, *worklist2, reason); + } +} + +/* Mark aliases of TAG as call clobbered, and place any tags on the + alias list that were not already call clobbered on WORKLIST. */ + +static void +mark_aliases_call_clobbered (tree tag, VEC (tree, heap) **worklist, + VEC (int, heap) **worklist2) +{ + unsigned int i; + VEC (tree, gc) *ma; + tree entry; + var_ann_t ta = var_ann (tag); + + if (!MTAG_P (tag)) + return; + ma = may_aliases (tag); + if (!ma) + return; + + for (i = 0; VEC_iterate (tree, ma, i, entry); i++) + { + if (!unmodifiable_var_p (entry)) + { + add_to_worklist (entry, worklist, worklist2, ta->escape_mask); + mark_call_clobbered (entry, ta->escape_mask); + } + } +} + +/* Tags containing global vars need to be marked as global. + Tags containing call clobbered vars need to be marked as call + clobbered. */ + +static void +compute_tag_properties (void) +{ + referenced_var_iterator rvi; + tree tag; + bool changed = true; + VEC (tree, heap) *taglist = NULL; + + FOR_EACH_REFERENCED_VAR (tag, rvi) + { + if (!MTAG_P (tag) || TREE_CODE (tag) == STRUCT_FIELD_TAG) + continue; + VEC_safe_push (tree, heap, taglist, tag); + } + + /* We sort the taglist by DECL_UID, for two reasons. + 1. To get a sequential ordering to make the bitmap accesses + faster. + 2. Because of the way we compute aliases, it's more likely that + an earlier tag is included in a later tag, and this will reduce + the number of iterations. + + If we had a real tag graph, we would just topo-order it and be + done with it. */ + qsort (VEC_address (tree, taglist), + VEC_length (tree, taglist), + sizeof (tree), + sort_tags_by_id); + + /* Go through each tag not marked as global, and if it aliases + global vars, mark it global. + + If the tag contains call clobbered vars, mark it call + clobbered. + + This loop iterates because tags may appear in the may-aliases + list of other tags when we group. */ + + while (changed) + { + unsigned int k; + + changed = false; + for (k = 0; VEC_iterate (tree, taglist, k, tag); k++) + { + VEC (tree, gc) *ma; + unsigned int i; + tree entry; + bool tagcc = is_call_clobbered (tag); + bool tagglobal = MTAG_GLOBAL (tag); + + if (tagcc && tagglobal) + continue; + + ma = may_aliases (tag); + if (!ma) + continue; + + for (i = 0; VEC_iterate (tree, ma, i, entry); i++) + { + /* Call clobbered entries cause the tag to be marked + call clobbered. */ + if (!tagcc && is_call_clobbered (entry)) + { + mark_call_clobbered (tag, var_ann (entry)->escape_mask); + tagcc = true; + changed = true; + } + + /* Global vars cause the tag to be marked global. */ + if (!tagglobal && is_global_var (entry)) + { + MTAG_GLOBAL (tag) = true; + changed = true; + tagglobal = true; + } + + /* Early exit once both global and cc are set, since the + loop can't do any more than that. */ + if (tagcc && tagglobal) + break; + } + } + } + VEC_free (tree, heap, taglist); +} + +/* Set up the initial variable clobbers and globalness. + When this function completes, only tags whose aliases need to be + clobbered will be set clobbered. Tags clobbered because they + contain call clobbered vars are handled in compute_tag_properties. */ + +static void +set_initial_properties (struct alias_info *ai) +{ + unsigned int i; + referenced_var_iterator rvi; + tree var; + + FOR_EACH_REFERENCED_VAR (var, rvi) + { + if (is_global_var (var) + && (!var_can_have_subvars (var) + || get_subvars_for_var (var) == NULL)) + { + if (!unmodifiable_var_p (var)) + mark_call_clobbered (var, ESCAPE_IS_GLOBAL); + } + else if (TREE_CODE (var) == PARM_DECL + && default_def (var) + && POINTER_TYPE_P (TREE_TYPE (var))) + { + tree def = default_def (var); + get_ptr_info (def)->value_escapes_p = 1; + get_ptr_info (def)->escape_mask |= ESCAPE_IS_PARM; + } + } + + for (i = 0; i < VARRAY_ACTIVE_SIZE (ai->processed_ptrs); i++) + { + tree ptr = VARRAY_TREE (ai->processed_ptrs, i); + struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr); + var_ann_t v_ann = var_ann (SSA_NAME_VAR (ptr)); + + if (pi->value_escapes_p) + { + /* If PTR escapes then its associated memory tags and + pointed-to variables are call-clobbered. */ + if (pi->name_mem_tag) + mark_call_clobbered (pi->name_mem_tag, pi->escape_mask); + + if (v_ann->type_mem_tag) + mark_call_clobbered (v_ann->type_mem_tag, pi->escape_mask); + + if (pi->pt_vars) + { + bitmap_iterator bi; + unsigned int j; + EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, j, bi) + if (!unmodifiable_var_p (referenced_var (j))) + mark_call_clobbered (referenced_var (j), pi->escape_mask); + } + } + /* If the name tag is call clobbered, so is the type tag + associated with the base VAR_DECL. */ + if (pi->name_mem_tag + && v_ann->type_mem_tag + && is_call_clobbered (pi->name_mem_tag)) + mark_call_clobbered (v_ann->type_mem_tag, pi->escape_mask); + + /* Name tags and type tags that we don't know where they point + to, might point to global memory, and thus, are clobbered. + + FIXME: This is not quite right. They should only be + clobbered if value_escapes_p is true, regardless of whether + they point to global memory or not. + So removing this code and fixing all the bugs would be nice. + It is the cause of a bunch of clobbering. */ + if ((pi->pt_global_mem || pi->pt_anything) + && pi->is_dereferenced && pi->name_mem_tag) + { + mark_call_clobbered (pi->name_mem_tag, ESCAPE_IS_GLOBAL); + MTAG_GLOBAL (pi->name_mem_tag) = true; + } + + if ((pi->pt_global_mem || pi->pt_anything) + && pi->is_dereferenced && v_ann->type_mem_tag) + { + mark_call_clobbered (v_ann->type_mem_tag, ESCAPE_IS_GLOBAL); + MTAG_GLOBAL (v_ann->type_mem_tag) = true; + } + } +} + +/* Compute which variables need to be marked call clobbered because + their tag is call clobbered, and which tags need to be marked + global because they contain global variables. */ + +static void +compute_call_clobbered (struct alias_info *ai) +{ + VEC (tree, heap) *worklist = NULL; + VEC(int,heap) *worklist2 = NULL; + + set_initial_properties (ai); + init_transitive_clobber_worklist (&worklist, &worklist2); + while (VEC_length (tree, worklist) != 0) + { + tree curr = VEC_pop (tree, worklist); + int reason = VEC_pop (int, worklist2); + + mark_call_clobbered (curr, reason); + mark_aliases_call_clobbered (curr, &worklist, &worklist2); + } + VEC_free (tree, heap, worklist); + VEC_free (int, heap, worklist2); + compute_tag_properties (); +} /* Compute may-alias information for every variable referenced in function FNDECL. @@ -277,6 +558,13 @@ compute_may_aliases (void) memory tags. */ compute_flow_insensitive_aliasing (ai); + /* Determine if we need to enable alias grouping. */ + if (ai->total_alias_vops >= MAX_ALIASED_VOPS) + group_aliases (ai); + + /* Compute call clobbering information. */ + compute_call_clobbered (ai); + /* If the program has too many call-clobbered variables and/or function calls, create .GLOBAL_VAR and use it to model call-clobbering semantics at call sites. This reduces the number of virtual operands @@ -703,20 +991,6 @@ compute_flow_sensitive_aliasing (struct alias_info *ai) var_ann_t v_ann = var_ann (SSA_NAME_VAR (ptr)); bitmap_iterator bi; - if (pi->value_escapes_p || pi->pt_anything) - { - /* If PTR escapes or may point to anything, then its associated - memory tags and pointed-to variables are call-clobbered. */ - if (pi->name_mem_tag) - mark_call_clobbered (pi->name_mem_tag); - - if (v_ann->type_mem_tag) - mark_call_clobbered (v_ann->type_mem_tag); - - if (pi->pt_vars) - EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, j, bi) - mark_call_clobbered (referenced_var (j)); - } /* Set up aliasing information for PTR's name memory tag (if it has one). Note that only pointers that have been dereferenced will @@ -727,13 +1001,6 @@ compute_flow_sensitive_aliasing (struct alias_info *ai) add_may_alias (pi->name_mem_tag, referenced_var (j)); add_may_alias (v_ann->type_mem_tag, referenced_var (j)); } - - /* If the name tag is call clobbered, so is the type tag - associated with the base VAR_DECL. */ - if (pi->name_mem_tag - && v_ann->type_mem_tag - && is_call_clobbered (pi->name_mem_tag)) - mark_call_clobbered (v_ann->type_mem_tag); } } @@ -897,10 +1164,6 @@ compute_flow_insensitive_aliasing (struct alias_info *ai) fprintf (dump_file, "\n%s: Total number of aliased vops: %ld\n", get_name (current_function_decl), ai->total_alias_vops); - - /* Determine if we need to enable alias grouping. */ - if (ai->total_alias_vops >= MAX_ALIASED_VOPS) - group_aliases (ai); } @@ -1308,12 +1571,6 @@ setup_pointers_and_addressables (struct alias_info *ai) if (bitmap_bit_p (ai->dereferenced_ptrs_store, DECL_UID (var))) bitmap_set_bit (ai->written_vars, DECL_UID (tag)); - /* If pointer VAR is a global variable or a PARM_DECL, - then its memory tag should be considered a global - variable. */ - if (TREE_CODE (var) == PARM_DECL || is_global_var (var)) - mark_call_clobbered (tag); - /* All the dereferences of pointer VAR count as references of TAG. Since TAG can be associated with several pointers, add the dereferences of VAR to the @@ -1598,16 +1855,6 @@ add_may_alias (tree var, tree alias) if (alias == al) return; - /* If VAR is a call-clobbered variable, so is its new ALIAS. - FIXME, call-clobbering should only depend on whether an address - escapes. It should be independent of aliasing. */ - if (is_call_clobbered (var)) - mark_call_clobbered (alias); - - /* Likewise. If ALIAS is call-clobbered, so is VAR. */ - else if (is_call_clobbered (alias)) - mark_call_clobbered (var); - VEC_safe_push (tree, gc, v_ann->may_aliases, alias); a_ann->is_alias_tag = 1; } @@ -1620,16 +1867,6 @@ replace_may_alias (tree var, size_t i, tree new_alias) { var_ann_t v_ann = var_ann (var); VEC_replace (tree, v_ann->may_aliases, i, new_alias); - - /* If VAR is a call-clobbered variable, so is NEW_ALIAS. - FIXME, call-clobbering should only depend on whether an address - escapes. It should be independent of aliasing. */ - if (is_call_clobbered (var)) - mark_call_clobbered (new_alias); - - /* Likewise. If NEW_ALIAS is call-clobbered, so is VAR. */ - else if (is_call_clobbered (new_alias)) - mark_call_clobbered (var); } @@ -1663,9 +1900,12 @@ set_pt_anything (tree ptr) 3- STMT is an assignment to a non-local variable, or 4- STMT is a return statement. - AI points to the alias information collected so far. */ + AI points to the alias information collected so far. -bool + Return the type of escape site found, if we found one, or NO_ESCAPE + if none. */ + +enum escape_type is_escape_site (tree stmt, struct alias_info *ai) { tree call = get_call_expr_in (stmt); @@ -1674,12 +1914,15 @@ is_escape_site (tree stmt, struct alias_info *ai) ai->num_calls_found++; if (!TREE_SIDE_EFFECTS (call)) - ai->num_pure_const_calls_found++; + { + ai->num_pure_const_calls_found++; + return ESCAPE_TO_PURE_CONST; + } - return true; + return ESCAPE_TO_CALL; } else if (TREE_CODE (stmt) == ASM_EXPR) - return true; + return ESCAPE_TO_ASM; else if (TREE_CODE (stmt) == MODIFY_EXPR) { tree lhs = TREE_OPERAND (stmt, 0); @@ -1691,7 +1934,7 @@ is_escape_site (tree stmt, struct alias_info *ai) /* If we couldn't recognize the LHS of the assignment, assume that it is a non-local store. */ if (lhs == NULL_TREE) - return true; + return ESCAPE_UNKNOWN; /* If the RHS is a conversion between a pointer and an integer, the pointer escapes since we can't track the integer. */ @@ -1701,12 +1944,12 @@ is_escape_site (tree stmt, struct alias_info *ai) && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (stmt, 1), 0))) && !POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (stmt, 1)))) - return true; + return ESCAPE_BAD_CAST; /* If the LHS is an SSA name, it can't possibly represent a non-local memory store. */ if (TREE_CODE (lhs) == SSA_NAME) - return false; + return NO_ESCAPE; /* FIXME: LHS is not an SSA_NAME. Even if it's an assignment to a local variables we cannot be sure if it will escape, because we @@ -1717,12 +1960,12 @@ is_escape_site (tree stmt, struct alias_info *ai) Midkiff, ``Escape analysis for java,'' in Proceedings of the Conference on Object-Oriented Programming Systems, Languages, and Applications (OOPSLA), pp. 1-19, 1999. */ - return true; + return ESCAPE_STORED_IN_GLOBAL; } else if (TREE_CODE (stmt) == RETURN_EXPR) - return true; + return ESCAPE_TO_RETURN; - return false; + return NO_ESCAPE; } /* Create a new memory tag of type TYPE. @@ -1793,13 +2036,6 @@ get_nmt_for (tree ptr) if (tag == NULL_TREE) tag = create_memory_tag (TREE_TYPE (TREE_TYPE (ptr)), false); - - /* If PTR is a PARM_DECL, it points to a global variable or malloc, - then its name tag should be considered a global variable. */ - if (TREE_CODE (SSA_NAME_VAR (ptr)) == PARM_DECL - || pi->pt_global_mem) - mark_call_clobbered (tag); - return tag; } @@ -1896,6 +2132,8 @@ create_global_var (void) TREE_THIS_VOLATILE (global_var) = 0; TREE_ADDRESSABLE (global_var) = 0; + create_var_ann (global_var); + mark_call_clobbered (global_var, ESCAPE_UNKNOWN); add_referenced_tmp_var (global_var); mark_sym_for_renaming (global_var); } diff --git a/gcc/tree-ssa-operands.c b/gcc/tree-ssa-operands.c index 87a1fc6eb1d..57cfedc0202 100644 --- a/gcc/tree-ssa-operands.c +++ b/gcc/tree-ssa-operands.c @@ -119,14 +119,8 @@ static VEC(tree,heap) *build_vuses; /* Array for building all the v_must_def operands. */ static VEC(tree,heap) *build_v_must_defs; -/* True if the operands for call clobbered vars are cached and valid. */ -bool ssa_call_clobbered_cache_valid; -bool ssa_ro_call_cache_valid; /* These arrays are the cached operand vectors for call clobbered calls. */ -static VEC(tree,heap) *clobbered_v_may_defs; -static VEC(tree,heap) *clobbered_vuses; -static VEC(tree,heap) *ro_call_vuses; static bool ops_active = false; static GTY (()) struct ssa_operand_memory_d *operand_memory = NULL; @@ -142,7 +136,7 @@ static inline void append_use (tree *); static void append_v_may_def (tree); static void append_v_must_def (tree); static void add_call_clobber_ops (tree, tree); -static void add_call_read_ops (tree); +static void add_call_read_ops (tree, tree); static void add_stmt_operand (tree *, stmt_ann_t, int); static void build_ssa_operands (tree stmt); @@ -220,7 +214,34 @@ ssa_operands_active (void) return ops_active; } +/* Structure storing statistics on how many call clobbers we have, and + how many where avoided. */ +static struct +{ + /* Number of call-clobbered ops we attempt to add to calls in + add_call_clobber_ops. */ + unsigned int clobbered_vars; + + /* Number of write-clobbers (v_may_defs) avoided by using + not_written information. */ + unsigned int static_write_clobbers_avoided; + + /* Number of reads (vuses) avoided by using not_read + information. */ + unsigned int static_read_clobbers_avoided; + + /* Number of write-clobbers avoided because the variable can't escape to + this call. */ + unsigned int unescapable_clobbers_avoided; + /* Number of readonly uses we attempt to add to calls in + add_call_read_ops. */ + unsigned int readonly_clobbers; + + /* Number of readonly uses we avoid using not_read information. */ + unsigned int static_readonly_clobbers_avoided; +} clobber_stats; + /* Initialize the operand cache routines. */ void @@ -235,6 +256,8 @@ init_ssa_operands (void) gcc_assert (operand_memory == NULL); operand_memory_index = SSA_OPERAND_MEMORY_SIZE; ops_active = true; + memset (&clobber_stats, 0, sizeof (clobber_stats)); + } @@ -260,10 +283,17 @@ fini_ssa_operands (void) ggc_free (ptr); } - VEC_free (tree, heap, clobbered_v_may_defs); - VEC_free (tree, heap, clobbered_vuses); - VEC_free (tree, heap, ro_call_vuses); ops_active = false; + + if (dump_file && (dump_flags & TDF_STATS)) + { + fprintf (dump_file, "Original clobbered vars:%d\n", clobber_stats.clobbered_vars); + fprintf (dump_file, "Static write clobbers avoided:%d\n", clobber_stats.static_write_clobbers_avoided); + fprintf (dump_file, "Static read clobbers avoided:%d\n", clobber_stats.static_read_clobbers_avoided); + fprintf (dump_file, "Unescapable clobbers avoided:%d\n", clobber_stats.unescapable_clobbers_avoided); + fprintf (dump_file, "Original readonly clobbers:%d\n", clobber_stats.readonly_clobbers); + fprintf (dump_file, "Static readonly clobbers avoided:%d\n", clobber_stats.static_readonly_clobbers_avoided); + } } @@ -1528,7 +1558,7 @@ get_call_expr_operands (tree stmt, tree expr) && !(call_flags & (ECF_PURE | ECF_CONST | ECF_NORETURN))) add_call_clobber_ops (stmt, get_callee_fndecl (expr)); else if (!(call_flags & ECF_CONST)) - add_call_read_ops (stmt); + add_call_read_ops (stmt, get_callee_fndecl (expr)); } /* Find uses in the called function. */ @@ -1715,7 +1745,6 @@ add_to_addressable_set (tree ref, bitmap *addresses_taken) } } - /* Add clobbering definitions for .GLOBAL_VAR or for each of the call clobbered variables in the function. */ @@ -1723,12 +1752,10 @@ static void add_call_clobber_ops (tree stmt, tree callee) { unsigned u; - tree t; bitmap_iterator bi; stmt_ann_t s_ann = stmt_ann (stmt); - struct stmt_ann_d empty_ann; bitmap not_read_b, not_written_b; - + /* Functions that are not const, pure or never return may clobber call-clobbered variables. */ if (s_ann) @@ -1742,100 +1769,67 @@ add_call_clobber_ops (tree stmt, tree callee) return; } - /* FIXME - if we have better information from the static vars - analysis, we need to make the cache call site specific. This way - we can have the performance benefits even if we are doing good - optimization. */ - /* Get info for local and module level statics. There is a bit set for each static if the call being processed does not read or write that variable. */ not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL; not_written_b = callee ? ipa_reference_get_not_written_global (callee) : NULL; - - /* If cache is valid, copy the elements into the build vectors. */ - if (ssa_call_clobbered_cache_valid - && (!not_read_b || bitmap_empty_p (not_read_b)) - && (!not_written_b || bitmap_empty_p (not_written_b))) - { - for (u = 0 ; u < VEC_length (tree, clobbered_vuses); u++) - { - t = VEC_index (tree, clobbered_vuses, u); - gcc_assert (TREE_CODE (t) != SSA_NAME); - var_ann (t)->in_vuse_list = 1; - VEC_safe_push (tree, heap, build_vuses, (tree)t); - } - for (u = 0; u < VEC_length (tree, clobbered_v_may_defs); u++) - { - t = VEC_index (tree, clobbered_v_may_defs, u); - gcc_assert (TREE_CODE (t) != SSA_NAME); - var_ann (t)->in_v_may_def_list = 1; - VEC_safe_push (tree, heap, build_v_may_defs, (tree)t); - } - return; - } - - memset (&empty_ann, 0, sizeof (struct stmt_ann_d)); - /* Add a V_MAY_DEF operand for every call clobbered variable. */ EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, u, bi) { - tree var = referenced_var (u); - unsigned int uid = u; + tree var = referenced_var_lookup (u); + unsigned int escape_mask = var_ann (var)->escape_mask; + tree real_var = var; + bool not_read; + bool not_written; + + /* Not read and not written are computed on regular vars, not + subvars, so look at the parent var if this is an SFT. */ - if (unmodifiable_var_p (var)) - add_stmt_operand (&var, &empty_ann, opf_none); - else + if (TREE_CODE (var) == STRUCT_FIELD_TAG) + real_var = SFT_PARENT_VAR (var); + + not_read = not_read_b ? bitmap_bit_p (not_read_b, + DECL_UID (real_var)) : false; + not_written = not_written_b ? bitmap_bit_p (not_written_b, + DECL_UID (real_var)) : false; + gcc_assert (!unmodifiable_var_p (var)); + + clobber_stats.clobbered_vars++; + + /* See if this variable is really clobbered by this function. */ + + /* Trivial case: Things escaping only to pure/const are not + clobbered by non-pure-const, and only read by pure/const. */ + if ((escape_mask & ~(ESCAPE_TO_PURE_CONST)) == 0) { - bool not_read; - bool not_written; - - /* Not read and not written are computed on regular vars, not - subvars, so look at the parent var if this is an SFT. */ - - if (TREE_CODE (var) == STRUCT_FIELD_TAG) - uid = DECL_UID (SFT_PARENT_VAR (var)); - - not_read = - not_read_b ? bitmap_bit_p (not_read_b, uid) : false; - not_written = - not_written_b ? bitmap_bit_p (not_written_b, uid) : false; - - if (not_written) + tree call = get_call_expr_in (stmt); + if (call_expr_flags (call) & (ECF_CONST | ECF_PURE)) { - if (!not_read) - add_stmt_operand (&var, &empty_ann, opf_none); + add_stmt_operand (&var, s_ann, opf_none); + clobber_stats.unescapable_clobbers_avoided++; + continue; } else - add_stmt_operand (&var, &empty_ann, opf_is_def); + { + clobber_stats.unescapable_clobbers_avoided++; + continue; + } } + + if (not_written) + { + clobber_stats.static_write_clobbers_avoided++; + if (!not_read) + add_stmt_operand (&var, s_ann, opf_none); + else + clobber_stats.static_read_clobbers_avoided++; + } + else + add_stmt_operand (&var, s_ann, opf_is_def); } - - if ((!not_read_b || bitmap_empty_p (not_read_b)) - && (!not_written_b || bitmap_empty_p (not_written_b))) - { - /* Prepare empty cache vectors. */ - VEC_truncate (tree, clobbered_vuses, 0); - VEC_truncate (tree, clobbered_v_may_defs, 0); - - /* Now fill the clobbered cache with the values that have been found. */ - for (u = 0; u < VEC_length (tree, build_vuses); u++) - VEC_safe_push (tree, heap, clobbered_vuses, - VEC_index (tree, build_vuses, u)); - - gcc_assert (VEC_length (tree, build_vuses) - == VEC_length (tree, clobbered_vuses)); - - for (u = 0; u < VEC_length (tree, build_v_may_defs); u++) - VEC_safe_push (tree, heap, clobbered_v_may_defs, - VEC_index (tree, build_v_may_defs, u)); - - gcc_assert (VEC_length (tree, build_v_may_defs) - == VEC_length (tree, clobbered_v_may_defs)); - - ssa_call_clobbered_cache_valid = true; - } + } @@ -1843,13 +1837,12 @@ add_call_clobber_ops (tree stmt, tree callee) function. */ static void -add_call_read_ops (tree stmt) +add_call_read_ops (tree stmt, tree callee) { unsigned u; - tree t; bitmap_iterator bi; stmt_ann_t s_ann = stmt_ann (stmt); - struct stmt_ann_d empty_ann; + bitmap not_read_b; /* if the function is not pure, it may reference memory. Add a VUSE for .GLOBAL_VAR if it has been created. See add_referenced_var @@ -1860,40 +1853,34 @@ add_call_read_ops (tree stmt) return; } - /* If cache is valid, copy the elements into the build vector. */ - if (ssa_ro_call_cache_valid) - { - for (u = 0; u < VEC_length (tree, ro_call_vuses); u++) - { - t = VEC_index (tree, ro_call_vuses, u); - gcc_assert (TREE_CODE (t) != SSA_NAME); - var_ann (t)->in_vuse_list = 1; - VEC_safe_push (tree, heap, build_vuses, (tree)t); - } - return; - } - - memset (&empty_ann, 0, sizeof (struct stmt_ann_d)); + not_read_b = callee ? ipa_reference_get_not_read_global (callee) : NULL; /* Add a VUSE for each call-clobbered variable. */ EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, u, bi) { tree var = referenced_var (u); - add_stmt_operand (&var, &empty_ann, opf_none | opf_non_specific); - } - - /* Prepare empty cache vectors. */ - VEC_truncate (tree, ro_call_vuses, 0); + tree real_var = var; + bool not_read; + + clobber_stats.readonly_clobbers++; - /* Now fill the clobbered cache with the values that have been found. */ - for (u = 0; u < VEC_length (tree, build_vuses); u++) - VEC_safe_push (tree, heap, ro_call_vuses, - VEC_index (tree, build_vuses, u)); + /* Not read and not written are computed on regular vars, not + subvars, so look at the parent var if this is an SFT. */ - gcc_assert (VEC_length (tree, build_vuses) - == VEC_length (tree, ro_call_vuses)); + if (TREE_CODE (var) == STRUCT_FIELD_TAG) + real_var = SFT_PARENT_VAR (var); - ssa_ro_call_cache_valid = true; + not_read = not_read_b ? bitmap_bit_p (not_read_b, + DECL_UID (real_var)) : false; + + if (not_read) + { + clobber_stats.static_readonly_clobbers_avoided++; + continue; + } + + add_stmt_operand (&var, s_ann, opf_none | opf_non_specific); + } } diff --git a/gcc/tree-ssa-operands.h b/gcc/tree-ssa-operands.h index c1ec3650ee1..daf2dce05f7 100644 --- a/gcc/tree-ssa-operands.h +++ b/gcc/tree-ssa-operands.h @@ -165,9 +165,6 @@ extern void dump_immediate_uses_for (FILE *file, tree var); extern void debug_immediate_uses (void); extern void debug_immediate_uses_for (tree var); -extern bool ssa_call_clobbered_cache_valid; -extern bool ssa_ro_call_cache_valid; - extern bool ssa_operands_active (void); extern void add_to_addressable_set (tree, bitmap *); diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c index 90bd037e313..923bdb77b3c 100644 --- a/gcc/tree-ssa-structalias.c +++ b/gcc/tree-ssa-structalias.c @@ -2953,7 +2953,7 @@ update_alias_info (tree stmt, struct alias_info *ai) bitmap addr_taken; use_operand_p use_p; ssa_op_iter iter; - bool stmt_escapes_p = is_escape_site (stmt, ai); + enum escape_type stmt_escape_type = is_escape_site (stmt, ai); tree op; /* Mark all the variables whose address are taken by the statement. */ @@ -2964,13 +2964,17 @@ update_alias_info (tree stmt, struct alias_info *ai) /* If STMT is an escape point, all the addresses taken by it are call-clobbered. */ - if (stmt_escapes_p) + if (stmt_escape_type != NO_ESCAPE) { bitmap_iterator bi; unsigned i; EXECUTE_IF_SET_IN_BITMAP (addr_taken, 0, i, bi) - mark_call_clobbered (referenced_var (i)); + { + tree rvar = referenced_var (i); + if (!unmodifiable_var_p (rvar)) + mark_call_clobbered (rvar, stmt_escape_type); + } } } @@ -3094,13 +3098,14 @@ update_alias_info (tree stmt, struct alias_info *ai) bitmap_set_bit (ai->dereferenced_ptrs_load, DECL_UID (var)); } - if (stmt_escapes_p && num_derefs < num_uses) + if (stmt_escape_type != NO_ESCAPE && num_derefs < num_uses) { /* If STMT is an escape point and STMT contains at least one direct use of OP, then the value of OP escapes and so the pointed-to variables need to be marked call-clobbered. */ pi->value_escapes_p = 1; + pi->escape_mask |= stmt_escape_type; /* If the statement makes a function call, assume that pointer OP will be dereferenced in a store diff --git a/gcc/tree-ssa-structalias.h b/gcc/tree-ssa-structalias.h index ddabd6d1799..bc129dde134 100644 --- a/gcc/tree-ssa-structalias.h +++ b/gcc/tree-ssa-structalias.h @@ -80,7 +80,7 @@ struct alias_info #define NUM_REFERENCES_SET(ANN, VAL) (ANN)->common.aux = (void*) ((void *)(VAL)) /* In tree-ssa-alias.c. */ -bool is_escape_site (tree, struct alias_info *); +enum escape_type is_escape_site (tree, struct alias_info *); /* In tree-ssa-structalias.c. */ extern void compute_points_to_sets (struct alias_info *); |