-
-/* Create name tags for all the pointers that have been dereferenced.
- We only create a name tag for a pointer P if P is found to point to
- a set of variables (so that we can alias them to *P) or if it is
- the result of a call to malloc (which means that P cannot point to
- anything else nor alias any other variable).
-
- If two pointers P and Q point to the same set of variables, they
- are assigned the same name tag. */
-
-static void
-create_name_tags (void)
-{
- size_t i;
- VEC (tree, heap) *with_ptvars = NULL;
- tree ptr;
- htab_t ptr_hash;
-
- /* Collect the list of pointers with a non-empty points to set. */
- for (i = 1; i < num_ssa_names; i++)
- {
- tree ptr = ssa_name (i);
- struct ptr_info_def *pi;
-
- if (!ptr
- || !POINTER_TYPE_P (TREE_TYPE (ptr))
- || !SSA_NAME_PTR_INFO (ptr))
- continue;
-
- pi = SSA_NAME_PTR_INFO (ptr);
-
- if (pi->pt_anything || !pi->is_dereferenced)
- {
- /* No name tags for pointers that have not been
- dereferenced or point to an arbitrary location. */
- pi->name_mem_tag = NULL_TREE;
- continue;
- }
-
- /* Set pt_anything on the pointers without pt_vars filled in so
- that they are assigned a symbol tag. */
- if (pi->pt_vars && !bitmap_empty_p (pi->pt_vars))
- VEC_safe_push (tree, heap, with_ptvars, ptr);
- else
- set_pt_anything (ptr);
- }
-
- /* If we didn't find any pointers with pt_vars set, we're done. */
- if (!with_ptvars)
- return;
-
- ptr_hash = htab_create (10, ptr_info_hash, eq_ptr_info, NULL);
-
- /* Now go through the pointers with pt_vars, and find a name tag
- with the same pt_vars as this pointer, or create one if one
- doesn't exist. */
- for (i = 0; VEC_iterate (tree, with_ptvars, i, ptr); i++)
- {
- struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
- tree old_name_tag = pi->name_mem_tag;
- struct ptr_info_def **slot;
-
- /* If PTR points to a set of variables, check if we don't
- have another pointer Q with the same points-to set before
- creating a tag. If so, use Q's tag instead of creating a
- new one.
-
- This is important for not creating unnecessary symbols
- and also for copy propagation. If we ever need to
- propagate PTR into Q or vice-versa, we would run into
- problems if they both had different name tags because
- they would have different SSA version numbers (which
- would force us to take the name tags in and out of SSA). */
- slot = (struct ptr_info_def **) htab_find_slot (ptr_hash, pi, INSERT);
- if (*slot)
- pi->name_mem_tag = (*slot)->name_mem_tag;
- else
- {
- *slot = pi;
-
- /* If we didn't find a pointer with the same points-to set
- as PTR, create a new name tag if needed. */
- if (pi->name_mem_tag == NULL_TREE)
- pi->name_mem_tag = get_nmt_for (ptr);
- }
-
- /* If the new name tag computed for PTR is different than
- the old name tag that it used to have, then the old tag
- needs to be removed from the IL, so we mark it for
- renaming. */
- if (old_name_tag && old_name_tag != pi->name_mem_tag)
- mark_sym_for_renaming (old_name_tag);
-
- /* Inherit volatility from the pointed-to type. */
- TREE_THIS_VOLATILE (pi->name_mem_tag)
- |= TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (ptr)));
-
- /* Mark the new name tag for renaming. */
- mark_sym_for_renaming (pi->name_mem_tag);
- }
-
- htab_delete (ptr_hash);
-
- VEC_free (tree, heap, with_ptvars);
-}
-
-
-/* Union the alias set SET into the may-aliases for TAG. */
-
-static void
-union_alias_set_into (tree tag, bitmap set)
-{
- bitmap ma = MTAG_ALIASES (tag);
-
- if (bitmap_empty_p (set))
- return;
-
- if (!ma)
- ma = MTAG_ALIASES (tag) = BITMAP_ALLOC (&alias_bitmap_obstack);
- bitmap_ior_into (ma, set);
-}
-
-
-/* For every pointer P_i in AI->PROCESSED_PTRS, create may-alias sets for
- the name memory tag (NMT) associated with P_i. If P_i escapes, then its
- name tag and the variables it points-to are call-clobbered. Finally, if
- P_i escapes and we could not determine where it points to, then all the
- variables in the same alias set as *P_i are marked call-clobbered. This
- is necessary because we must assume that P_i may take the address of any
- variable in the same alias set. */
-
-static void
-compute_flow_sensitive_aliasing (struct alias_info *ai)
-{
- size_t i;
- tree ptr;
-
- timevar_push (TV_FLOW_SENSITIVE);
- set_used_smts ();
-
- for (i = 0; VEC_iterate (tree, ai->processed_ptrs, i, ptr); i++)
- {
- if (!find_what_p_points_to (ptr))
- set_pt_anything (ptr);
- }
-
- create_name_tags ();
-
- for (i = 0; VEC_iterate (tree, ai->processed_ptrs, i, ptr); i++)
- {
- struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
- tree tag = symbol_mem_tag (SSA_NAME_VAR (ptr));
-
- /* Set up aliasing information for PTR's name memory tag (if it has
- one). Note that only pointers that have been dereferenced will
- have a name memory tag. */
- if (pi->name_mem_tag && pi->pt_vars)
- {
- if (!bitmap_empty_p (pi->pt_vars))
- {
- union_alias_set_into (pi->name_mem_tag, pi->pt_vars);
- union_alias_set_into (tag, pi->pt_vars);
- bitmap_clear_bit (MTAG_ALIASES (tag), DECL_UID (tag));
-
- /* It may be the case that this the tag uid was the only
- bit we had set in the aliases list, and in this case,
- we don't want to keep an empty bitmap, as this
- asserts in tree-ssa-operands.c . */
- if (bitmap_empty_p (MTAG_ALIASES (tag)))
- BITMAP_FREE (MTAG_ALIASES (tag));
- }
- }
- }
- timevar_pop (TV_FLOW_SENSITIVE);
-}
-
-
-/* Return TRUE if at least one symbol in TAG2's alias set is also
- present in TAG1's alias set. */
-
-static bool
-have_common_aliases_p (bitmap tag1aliases, bitmap tag2aliases)
-{
-
- /* This is the old behavior of have_common_aliases_p, which is to
- return false if both sets are empty, or one set is and the other
- isn't. */
- if ((tag1aliases == NULL && tag2aliases != NULL)
- || (tag2aliases == NULL && tag1aliases != NULL)
- || (tag1aliases == NULL && tag2aliases == NULL))
- return false;
-
- return bitmap_intersect_p (tag1aliases, tag2aliases);
-}
-
-/* Compute type-based alias sets. Traverse all the pointers and
- addressable variables found in setup_pointers_and_addressables.
-
- For every pointer P in AI->POINTERS and addressable variable V in
- AI->ADDRESSABLE_VARS, add V to the may-alias sets of P's symbol
- memory tag (SMT) if their alias sets conflict. V is then marked as
- an aliased symbol so that the operand scanner knows that statements
- containing V have aliased operands. */
-
-static void
-compute_flow_insensitive_aliasing (struct alias_info *ai)
-{
- size_t i;
-
- timevar_push (TV_FLOW_INSENSITIVE);
- /* For every pointer P, determine which addressable variables may alias
- with P's symbol memory tag. */
- for (i = 0; i < ai->num_pointers; i++)
- {
- size_t j;
- struct alias_map_d *p_map = ai->pointers[i];
- tree tag = symbol_mem_tag (p_map->var);
- tree var;
-
- /* Call-clobbering information is not finalized yet at this point. */
- if (PTR_IS_REF_ALL (p_map->var))
- continue;
-
- for (j = 0; j < ai->num_addressable_vars; j++)
- {
- struct alias_map_d *v_map;
- var_ann_t v_ann;
- bool tag_stored_p, var_stored_p;
-
- v_map = ai->addressable_vars[j];
- var = v_map->var;
- v_ann = var_ann (var);
-
- /* Skip memory tags and variables that have never been
- written to. We also need to check if the variables are
- call-clobbered because they may be overwritten by
- function calls. */
- tag_stored_p = pointer_set_contains (ai->written_vars, tag)
- || is_call_clobbered (tag);
- var_stored_p = pointer_set_contains (ai->written_vars, var)
- || is_call_clobbered (var);
- if (!tag_stored_p && !var_stored_p)
- continue;
-
- if (may_alias_p (p_map->var, p_map->set, var, v_map->set, false))
- {
- /* We should never have a var with subvars here, because
- they shouldn't get into the set of addressable vars */
- gcc_assert (!var_can_have_subvars (var)
- || get_subvars_for_var (var) == NULL);
-
- /* Add VAR to TAG's may-aliases set. */
- add_may_alias (tag, var);
- }
- }
- }
-
- /* Since this analysis is based exclusively on symbols, it fails to
- handle cases where two pointers P and Q have different memory
- tags with conflicting alias set numbers but no aliased symbols in
- common.
-
- For example, suppose that we have two memory tags SMT.1 and SMT.2
- such that
-
- may-aliases (SMT.1) = { a }
- may-aliases (SMT.2) = { b }
-
- and the alias set number of SMT.1 conflicts with that of SMT.2.
- Since they don't have symbols in common, loads and stores from
- SMT.1 and SMT.2 will seem independent of each other, which will
- lead to the optimizers making invalid transformations (see
- testsuite/gcc.c-torture/execute/pr15262-[12].c).
-
- To avoid this problem, we do a final traversal of AI->POINTERS
- looking for pairs of pointers that have no aliased symbols in
- common and yet have conflicting alias set numbers. */
- for (i = 0; i < ai->num_pointers; i++)
- {
- size_t j;
- struct alias_map_d *p_map1 = ai->pointers[i];
- tree tag1 = symbol_mem_tag (p_map1->var);
- bitmap may_aliases1 = MTAG_ALIASES (tag1);
-
- if (PTR_IS_REF_ALL (p_map1->var))
- continue;
-
- for (j = i + 1; j < ai->num_pointers; j++)
- {
- struct alias_map_d *p_map2 = ai->pointers[j];
- tree tag2 = symbol_mem_tag (p_map2->var);
- bitmap may_aliases2 = may_aliases (tag2);
-
- if (PTR_IS_REF_ALL (p_map2->var))
- continue;
-
- /* If the pointers may not point to each other, do nothing. */
- if (!may_alias_p (p_map1->var, p_map1->set, tag2, p_map2->set, true))
- continue;
-
- /* The two pointers may alias each other. If they already have
- symbols in common, do nothing. */
- if (have_common_aliases_p (may_aliases1, may_aliases2))
- continue;
-
- if (may_aliases2 && !bitmap_empty_p (may_aliases2))
- {
- union_alias_set_into (tag1, may_aliases2);
- }
- else
- {
- /* Since TAG2 does not have any aliases of its own, add
- TAG2 itself to the alias set of TAG1. */
- add_may_alias (tag1, tag2);
- }
- }
-
- }
- timevar_pop (TV_FLOW_INSENSITIVE);
-}
-
-
-/* Finalize may-alias information for ref-all pointers. Traverse all
- the addressable variables found in setup_pointers_and_addressables.
-
- If flow-sensitive alias analysis has attached a name memory tag to
- a ref-all pointer, we will use it for the dereferences because that
- will have more precise aliasing information. But if there is no
- name tag, we will use a special symbol tag that aliases all the
- call-clobbered addressable variables. */
-
-static void
-finalize_ref_all_pointers (struct alias_info *ai)
-{
- size_t i;
-
- /* First add the real call-clobbered variables. */
- for (i = 0; i < ai->num_addressable_vars; i++)
- {
- tree var = ai->addressable_vars[i]->var;
- if (is_call_clobbered (var))
- add_may_alias (ai->ref_all_symbol_mem_tag, var);
- }
-
- /* Then add the call-clobbered pointer memory tags. See
- compute_flow_insensitive_aliasing for the rationale. */
- for (i = 0; i < ai->num_pointers; i++)
- {
- tree ptr = ai->pointers[i]->var, tag;
- /* Avoid adding to self and clean up. */
- if (PTR_IS_REF_ALL (ptr))
- {
- struct ptr_info_def *pi = get_ptr_info (ptr);
- if (pi->is_dereferenced)
- pi->pt_anything = 0;
- continue;
- }
- tag = symbol_mem_tag (ptr);
- if (is_call_clobbered (tag))
- add_may_alias (ai->ref_all_symbol_mem_tag, tag);
- }
-
-}
-
-
-/* Create a new alias set entry for VAR in AI->ADDRESSABLE_VARS. */
-
-static void
-create_alias_map_for (tree var, struct alias_info *ai)
-{
- struct alias_map_d *alias_map;
- alias_map = XCNEW (struct alias_map_d);
- alias_map->var = var;
- alias_map->set = get_alias_set (var);
- ai->addressable_vars[ai->num_addressable_vars++] = alias_map;
-}
-
-
-/* Create memory tags for all the dereferenced pointers and build the
- ADDRESSABLE_VARS and POINTERS arrays used for building the may-alias
- sets. Based on the address escape and points-to information collected
- earlier, this pass will also clear the TREE_ADDRESSABLE flag from those
- variables whose address is not needed anymore. */
-
-static void
-setup_pointers_and_addressables (struct alias_info *ai)
-{
- size_t num_addressable_vars, num_pointers;
- referenced_var_iterator rvi;
- tree var;
- VEC (tree, heap) *varvec = NULL;
- safe_referenced_var_iterator srvi;
-
- /* Size up the arrays ADDRESSABLE_VARS and POINTERS. */
- num_addressable_vars = num_pointers = 0;
-
- FOR_EACH_REFERENCED_VAR (var, rvi)
- {
- if (may_be_aliased (var))
- num_addressable_vars++;
-
- if (POINTER_TYPE_P (TREE_TYPE (var)))
- {
- /* Since we don't keep track of volatile variables, assume that
- these pointers are used in indirect store operations. */
- if (TREE_THIS_VOLATILE (var))
- pointer_set_insert (ai->dereferenced_ptrs_store, var);
-
- num_pointers++;
- }
- }
-
- /* Create ADDRESSABLE_VARS and POINTERS. Note that these arrays are
- always going to be slightly bigger than we actually need them
- because some TREE_ADDRESSABLE variables will be marked
- non-addressable below and only pointers with unique symbol tags are
- going to be added to POINTERS. */
- ai->addressable_vars = XCNEWVEC (struct alias_map_d *, num_addressable_vars);
- ai->pointers = XCNEWVEC (struct alias_map_d *, num_pointers);
- ai->num_addressable_vars = 0;
- ai->num_pointers = 0;
-
- FOR_EACH_REFERENCED_VAR_SAFE (var, varvec, srvi)
- {
- subvar_t svars;
-
- /* Name memory tags already have flow-sensitive aliasing
- information, so they need not be processed by
- compute_flow_insensitive_aliasing. Similarly, symbol memory
- tags are already accounted for when we process their
- associated pointer.
-
- Structure fields, on the other hand, have to have some of this
- information processed for them, but it's pointless to mark them
- non-addressable (since they are fake variables anyway). */
- if (MTAG_P (var) && TREE_CODE (var) != STRUCT_FIELD_TAG)
- continue;
-
- /* Remove the ADDRESSABLE flag from every addressable variable whose
- address is not needed anymore. This is caused by the propagation
- of ADDR_EXPR constants into INDIRECT_REF expressions and the
- removal of dead pointer assignments done by the early scalar
- cleanup passes. */
- if (TREE_ADDRESSABLE (var))
- {
- if (!bitmap_bit_p (gimple_addressable_vars (cfun), DECL_UID (var))
- && TREE_CODE (var) != RESULT_DECL
- && !is_global_var (var))
- {
- bool okay_to_mark = true;
-
- /* Since VAR is now a regular GIMPLE register, we will need
- to rename VAR into SSA afterwards. */
- mark_sym_for_renaming (var);
-
- /* If VAR can have sub-variables, and any of its
- sub-variables has its address taken, then we cannot
- remove the addressable flag from VAR. */
- if (var_can_have_subvars (var)
- && (svars = get_subvars_for_var (var)))
- {
- unsigned int i;
- tree subvar;
-
- for (i = 0; VEC_iterate (tree, svars, i, subvar); ++i)
- {
- if (bitmap_bit_p (gimple_addressable_vars (cfun),
- DECL_UID (subvar)))
- okay_to_mark = false;
- mark_sym_for_renaming (subvar);
- }
- }
-
- /* The address of VAR is not needed, remove the
- addressable bit, so that it can be optimized as a
- regular variable. */
- if (okay_to_mark)
- {
- /* The memory partition holding VAR will no longer
- contain VAR, and statements referencing it will need
- to be updated. */
- if (memory_partition (var))
- mark_sym_for_renaming (memory_partition (var));
-
- mark_non_addressable (var);
- }
- }
- }
-
- /* Global variables and addressable locals may be aliased. Create an
- entry in ADDRESSABLE_VARS for VAR. */
- if (may_be_aliased (var))
- {
- if (!var_can_have_subvars (var)
- || get_subvars_for_var (var) == NULL)
- create_alias_map_for (var, ai);
-
- mark_sym_for_renaming (var);
- }
-
- /* Add pointer variables that have been dereferenced to the POINTERS
- array and create a symbol memory tag for them. */
- if (POINTER_TYPE_P (TREE_TYPE (var)))
- {
- if ((pointer_set_contains (ai->dereferenced_ptrs_store, var)
- || pointer_set_contains (ai->dereferenced_ptrs_load, var)))
- {
- tree tag, old_tag;
- var_ann_t t_ann;
-
- /* If pointer VAR still doesn't have a memory tag
- associated with it, create it now or re-use an
- existing one. */
- tag = get_smt_for (var, ai);
- t_ann = var_ann (tag);
-
- /* The symbol tag will need to be renamed into SSA
- afterwards. Note that we cannot do this inside
- get_smt_for because aliasing may run multiple times
- and we only create symbol tags the first time. */
- mark_sym_for_renaming (tag);
-
- /* Similarly, if pointer VAR used to have another type
- tag, we will need to process it in the renamer to
- remove the stale virtual operands. */
- old_tag = symbol_mem_tag (var);
- if (old_tag)
- mark_sym_for_renaming (old_tag);
-
- /* Associate the tag with pointer VAR. */
- set_symbol_mem_tag (var, tag);
-
- /* If pointer VAR has been used in a store operation,
- then its memory tag must be marked as written-to. */
- if (pointer_set_contains (ai->dereferenced_ptrs_store, var))
- pointer_set_insert (ai->written_vars, tag);
- }
- else
- {
- /* The pointer has not been dereferenced. If it had a
- symbol memory tag, remove it and mark the old tag for
- renaming to remove it out of the IL. */
- tree tag = symbol_mem_tag (var);
- if (tag)
- {
- mark_sym_for_renaming (tag);
- set_symbol_mem_tag (var, NULL_TREE);
- }
- }
- }
- }
-
- VEC_free (tree, heap, varvec);
-}
-
-
-/* Determine whether to use .GLOBAL_VAR to model call clobbering
- semantics. If the function makes no references to global
- variables and contains at least one call to a non-pure function,
- then we need to mark the side-effects of the call using .GLOBAL_VAR
- to represent all possible global memory referenced by the callee. */
-
-static void
-maybe_create_global_var (void)
-{
- /* No need to create it, if we have one already. */
- if (gimple_global_var (cfun) == NULL_TREE)
- {
- struct mem_ref_stats_d *stats = gimple_mem_ref_stats (cfun);
-
- /* Create .GLOBAL_VAR if there are no call-clobbered
- variables and the program contains a mixture of pure/const
- and regular function calls. This is to avoid the problem
- described in PR 20115:
-
- int X;
- int func_pure (void) { return X; }
- int func_non_pure (int a) { X += a; }
- int foo ()
- {
- int a = func_pure ();
- func_non_pure (a);
- a = func_pure ();
- return a;
- }
-
- Since foo() has no call-clobbered variables, there is
- no relationship between the calls to func_pure and
- func_non_pure. Since func_pure has no side-effects, value
- numbering optimizations elide the second call to func_pure.
- So, if we have some pure/const and some regular calls in the
- program we create .GLOBAL_VAR to avoid missing these
- relations. */
- if (bitmap_empty_p (gimple_call_clobbered_vars (cfun))
- && stats->num_call_sites > 0
- && stats->num_pure_const_call_sites > 0
- && stats->num_call_sites > stats->num_pure_const_call_sites)
- create_global_var ();
- }
-}
-
-
-/* Return TRUE if pointer PTR may point to variable VAR.
-
- MEM_ALIAS_SET is the alias set for the memory location pointed-to by PTR
- This is needed because when checking for type conflicts we are
- interested in the alias set of the memory location pointed-to by
- PTR. The alias set of PTR itself is irrelevant.
-
- VAR_ALIAS_SET is the alias set for VAR. */
-
-static bool
-may_alias_p (tree ptr, alias_set_type mem_alias_set,
- tree var, alias_set_type var_alias_set,
- bool alias_set_only)
-{
- tree mem;
-
- alias_stats.alias_queries++;
- alias_stats.simple_queries++;
-
- /* By convention, a variable cannot alias itself. */
- mem = symbol_mem_tag (ptr);
- if (mem == var)
- {
- alias_stats.alias_noalias++;
- alias_stats.simple_resolved++;
- return false;
- }
-
- /* If -fargument-noalias-global is > 2, pointer arguments may
- not point to anything else. */
- if (flag_argument_noalias > 2 && TREE_CODE (ptr) == PARM_DECL)
- {
- alias_stats.alias_noalias++;
- alias_stats.simple_resolved++;
- return false;
- }
-
- /* If -fargument-noalias-global is > 1, pointer arguments may
- not point to global variables. */
- if (flag_argument_noalias > 1 && is_global_var (var)
- && TREE_CODE (ptr) == PARM_DECL)
- {
- alias_stats.alias_noalias++;
- alias_stats.simple_resolved++;
- return false;
- }
-
- /* If either MEM or VAR is a read-only global and the other one
- isn't, then PTR cannot point to VAR. */
- if ((unmodifiable_var_p (mem) && !unmodifiable_var_p (var))
- || (unmodifiable_var_p (var) && !unmodifiable_var_p (mem)))
- {
- alias_stats.alias_noalias++;
- alias_stats.simple_resolved++;
- return false;
- }
-
- gcc_assert (TREE_CODE (mem) == SYMBOL_MEMORY_TAG);
-
- if (!DECL_NO_TBAA_P (ptr))
- {
- alias_stats.tbaa_queries++;
-
- /* If the alias sets don't conflict then MEM cannot alias VAR. */
- if (!alias_sets_conflict_p (mem_alias_set, var_alias_set))
- {
- alias_stats.alias_noalias++;
- alias_stats.tbaa_resolved++;
- return false;
- }
-
- /* If VAR is a record or union type, PTR cannot point into VAR
- unless there is some explicit address operation in the
- program that can reference a field of the type pointed-to by
- PTR. This also assumes that the types of both VAR and PTR
- are contained within the compilation unit, and that there is
- no fancy addressing arithmetic associated with any of the
- types involved. */
- if (mem_alias_set != 0 && var_alias_set != 0)
- {
- tree ptr_type = TREE_TYPE (ptr);
- tree var_type = TREE_TYPE (var);
-
- /* The star count is -1 if the type at the end of the
- pointer_to chain is not a record or union type. */
- if ((!alias_set_only) &&
- ipa_type_escape_star_count_of_interesting_type (var_type) >= 0)
- {
- int ptr_star_count = 0;
-
- /* ipa_type_escape_star_count_of_interesting_type is a
- little too restrictive for the pointer type, need to
- allow pointers to primitive types as long as those
- types cannot be pointers to everything. */
- while (POINTER_TYPE_P (ptr_type))
- {
- /* Strip the *s off. */
- ptr_type = TREE_TYPE (ptr_type);
- ptr_star_count++;
- }
-
- /* There does not appear to be a better test to see if
- the pointer type was one of the pointer to everything
- types. */
- if (ptr_star_count > 0)
- {
- alias_stats.structnoaddress_queries++;
- if (ipa_type_escape_field_does_not_clobber_p (var_type,
- TREE_TYPE (ptr)))
- {
- alias_stats.structnoaddress_resolved++;
- alias_stats.alias_noalias++;
- return false;
- }
- }
- else if (ptr_star_count == 0)
- {
- /* If PTR_TYPE was not really a pointer to type, it cannot
- alias. */
- alias_stats.structnoaddress_queries++;
- alias_stats.structnoaddress_resolved++;
- alias_stats.alias_noalias++;
- return false;
- }
- }
- }
- }
-
- alias_stats.alias_mayalias++;
- return true;
-}
-
-
-/* Add ALIAS to the set of variables that may alias VAR. */
-
-static void
-add_may_alias (tree var, tree alias)
-{
- /* Don't allow self-referential aliases. */
- gcc_assert (var != alias);
-
- /* ALIAS must be addressable if it's being added to an alias set. */
-#if 1
- TREE_ADDRESSABLE (alias) = 1;
-#else
- gcc_assert (may_be_aliased (alias));
-#endif
-
- /* VAR must be a symbol or a name tag. */
- gcc_assert (TREE_CODE (var) == SYMBOL_MEMORY_TAG
- || TREE_CODE (var) == NAME_MEMORY_TAG);
-
- if (MTAG_ALIASES (var) == NULL)
- MTAG_ALIASES (var) = BITMAP_ALLOC (&alias_bitmap_obstack);
-
- bitmap_set_bit (MTAG_ALIASES (var), DECL_UID (alias));
-}
-
-
-/* Mark pointer PTR as pointing to an arbitrary memory location. */
-
-static void
-set_pt_anything (tree ptr)
-{
- struct ptr_info_def *pi = get_ptr_info (ptr);
-
- pi->pt_anything = 1;
- pi->pt_vars = NULL;
-
- /* The pointer used to have a name tag, but we now found it pointing
- to an arbitrary location. The name tag needs to be renamed and
- disassociated from PTR. */
- if (pi->name_mem_tag)
- {
- mark_sym_for_renaming (pi->name_mem_tag);
- pi->name_mem_tag = NULL_TREE;
- }
-}
-
-
-/* Return true if STMT is an "escape" site from the current function. Escape
- sites those statements which might expose the address of a variable
- outside the current function. STMT is an escape site iff:
-
- 1- STMT is a function call, or
- 2- STMT is an __asm__ expression, or
- 3- STMT is an assignment to a non-local variable, or
- 4- STMT is a return statement.
-
- Return the type of escape site found, if we found one, or NO_ESCAPE
- if none. */
-
-enum escape_type
-is_escape_site (tree stmt)
-{
- tree call = get_call_expr_in (stmt);
- if (call != NULL_TREE)
- {
- if (!TREE_SIDE_EFFECTS (call))
- return ESCAPE_TO_PURE_CONST;
-
- return ESCAPE_TO_CALL;
- }
- else if (TREE_CODE (stmt) == ASM_EXPR)
- return ESCAPE_TO_ASM;
- else if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT)
- {
- tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
-
- /* Get to the base of _REF nodes. */
- if (TREE_CODE (lhs) != SSA_NAME)
- lhs = get_base_address (lhs);
-
- /* If we couldn't recognize the LHS of the assignment, assume that it
- is a non-local store. */
- if (lhs == NULL_TREE)
- return ESCAPE_UNKNOWN;
-
- if (TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == NOP_EXPR
- || TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CONVERT_EXPR
- || TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == VIEW_CONVERT_EXPR)
- {
- tree from
- = TREE_TYPE (TREE_OPERAND (GIMPLE_STMT_OPERAND (stmt, 1), 0));
- tree to = TREE_TYPE (GIMPLE_STMT_OPERAND (stmt, 1));
-
- /* If the RHS is a conversion between a pointer and an integer, the
- pointer escapes since we can't track the integer. */
- if (POINTER_TYPE_P (from) && !POINTER_TYPE_P (to))
- return ESCAPE_BAD_CAST;
-
- /* Same if the RHS is a conversion between a regular pointer and a
- ref-all pointer since we can't track the SMT of the former. */
- if (POINTER_TYPE_P (from) && !TYPE_REF_CAN_ALIAS_ALL (from)
- && POINTER_TYPE_P (to) && TYPE_REF_CAN_ALIAS_ALL (to))
- 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 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
- don't have information about objects not in SSA form. Need to
- implement something along the lines of
-
- J.-D. Choi, M. Gupta, M. J. Serrano, V. C. Sreedhar, and S. P.
- Midkiff, ``Escape analysis for java,'' in Proceedings of the
- Conference on Object-Oriented Programming Systems, Languages, and
- Applications (OOPSLA), pp. 1-19, 1999. */
- return ESCAPE_STORED_IN_GLOBAL;
- }
- else if (TREE_CODE (stmt) == RETURN_EXPR)
- return ESCAPE_TO_RETURN;
-
- return NO_ESCAPE;
-}
-
-/* Create a new memory tag of type TYPE.
- Does NOT push it into the current binding. */
-
-tree
-create_tag_raw (enum tree_code code, tree type, const char *prefix)
-{
- tree tmp_var;
-
- tmp_var = build_decl (code, create_tmp_var_name (prefix), type);
-
- /* Make the variable writable. */
- TREE_READONLY (tmp_var) = 0;
-
- /* It doesn't start out global. */
- MTAG_GLOBAL (tmp_var) = 0;
- TREE_STATIC (tmp_var) = 0;
- TREE_USED (tmp_var) = 1;
-
- return tmp_var;
-}
-
-/* Create a new memory tag of type TYPE. If IS_TYPE_TAG is true, the tag
- is considered to represent all the pointers whose pointed-to types are
- in the same alias set class. Otherwise, the tag represents a single
- SSA_NAME pointer variable. */
-
-static tree
-create_memory_tag (tree type, bool is_type_tag)
-{
- tree tag = create_tag_raw (is_type_tag ? SYMBOL_MEMORY_TAG : NAME_MEMORY_TAG,
- type, (is_type_tag) ? "SMT" : "NMT");
-
- /* By default, memory tags are local variables. Alias analysis will
- determine whether they should be considered globals. */
- DECL_CONTEXT (tag) = current_function_decl;
-
- /* Memory tags are by definition addressable. */
- TREE_ADDRESSABLE (tag) = 1;
-
- set_symbol_mem_tag (tag, NULL_TREE);
-
- /* Add the tag to the symbol table. */
- add_referenced_var (tag);
-
- return tag;
-}
-
-
-/* Create a name memory tag to represent a specific SSA_NAME pointer P_i.
- This is used if P_i has been found to point to a specific set of
- variables or to a non-aliased memory location like the address returned
- by malloc functions. */
-
-static tree
-get_nmt_for (tree ptr)
-{
- struct ptr_info_def *pi = get_ptr_info (ptr);
- tree tag = pi->name_mem_tag;
-
- if (tag == NULL_TREE)
- tag = create_memory_tag (TREE_TYPE (TREE_TYPE (ptr)), false);
- return tag;
-}
-
-
-/* Return the symbol memory tag associated to pointer PTR. A memory
- tag is an artificial variable that represents the memory location
- pointed-to by PTR. It is used to model the effects of pointer
- de-references on addressable variables.
-
- AI points to the data gathered during alias analysis. This
- function populates the array AI->POINTERS. */
-
-static tree
-get_smt_for (tree ptr, struct alias_info *ai)
-{
- size_t i;
- tree tag;
- tree tag_type = TREE_TYPE (TREE_TYPE (ptr));
- alias_set_type tag_set = get_alias_set (tag_type);
-
- /* We use a unique memory tag for all the ref-all pointers. */
- if (PTR_IS_REF_ALL (ptr))
- {
- if (!ai->ref_all_symbol_mem_tag)
- ai->ref_all_symbol_mem_tag = create_memory_tag (void_type_node, true);
- return ai->ref_all_symbol_mem_tag;
- }
-
- /* To avoid creating unnecessary memory tags, only create one memory tag
- per alias set class. Note that it may be tempting to group
- memory tags based on conflicting alias sets instead of
- equivalence. That would be wrong because alias sets are not
- necessarily transitive (as demonstrated by the libstdc++ test
- 23_containers/vector/cons/4.cc). Given three alias sets A, B, C
- such that conflicts (A, B) == true and conflicts (A, C) == true,
- it does not necessarily follow that conflicts (B, C) == true. */
- for (i = 0, tag = NULL_TREE; i < ai->num_pointers; i++)
- {
- struct alias_map_d *curr = ai->pointers[i];
- tree curr_tag = symbol_mem_tag (curr->var);
- if (tag_set == curr->set)
- {
- tag = curr_tag;
- break;
- }
- }
-
- /* If VAR cannot alias with any of the existing memory tags, create a new
- tag for PTR and add it to the POINTERS array. */
- if (tag == NULL_TREE)
- {
- struct alias_map_d *alias_map;
-
- /* If PTR did not have a symbol tag already, create a new SMT.*
- artificial variable representing the memory location
- pointed-to by PTR. */
- tag = symbol_mem_tag (ptr);
- if (tag == NULL_TREE)
- tag = create_memory_tag (tag_type, true);
-
- /* Add PTR to the POINTERS array. Note that we are not interested in
- PTR's alias set. Instead, we cache the alias set for the memory that
- PTR points to. */
- alias_map = XCNEW (struct alias_map_d);
- alias_map->var = ptr;
- alias_map->set = tag_set;
- ai->pointers[ai->num_pointers++] = alias_map;
- }
-
- /* If the pointed-to type is volatile, so is the tag. */
- TREE_THIS_VOLATILE (tag) |= TREE_THIS_VOLATILE (tag_type);
-
- /* Make sure that the symbol tag has the same alias set as the
- pointed-to type. */
- gcc_assert (tag_set == get_alias_set (tag));
-
- return tag;
-}
-
-
-/* Create GLOBAL_VAR, an artificial global variable to act as a
- representative of all the variables that may be clobbered by function
- calls. */
-
-static void
-create_global_var (void)
-{
- tree global_var = build_decl (VAR_DECL, get_identifier (".GLOBAL_VAR"),
- void_type_node);
- DECL_ARTIFICIAL (global_var) = 1;
- TREE_READONLY (global_var) = 0;
- DECL_EXTERNAL (global_var) = 1;
- TREE_STATIC (global_var) = 1;
- TREE_USED (global_var) = 1;
- DECL_CONTEXT (global_var) = NULL_TREE;
- 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_var (global_var);
- mark_sym_for_renaming (global_var);
- cfun->gimple_df->global_var = global_var;
-}
-
-
-/* Dump alias statistics on FILE. */
-
-static void
-dump_alias_stats (FILE *file)
-{
- const char *funcname
- = lang_hooks.decl_printable_name (current_function_decl, 2);
- fprintf (file, "\nAlias statistics for %s\n\n", funcname);
- fprintf (file, "Total alias queries:\t%u\n", alias_stats.alias_queries);
- fprintf (file, "Total alias mayalias results:\t%u\n",
- alias_stats.alias_mayalias);
- fprintf (file, "Total alias noalias results:\t%u\n",
- alias_stats.alias_noalias);
- fprintf (file, "Total simple queries:\t%u\n",
- alias_stats.simple_queries);
- fprintf (file, "Total simple resolved:\t%u\n",
- alias_stats.simple_resolved);
- fprintf (file, "Total TBAA queries:\t%u\n",
- alias_stats.tbaa_queries);
- fprintf (file, "Total TBAA resolved:\t%u\n",
- alias_stats.tbaa_resolved);
- fprintf (file, "Total non-addressable structure type queries:\t%u\n",
- alias_stats.structnoaddress_queries);
- fprintf (file, "Total non-addressable structure type resolved:\t%u\n",
- alias_stats.structnoaddress_resolved);
-}
-
-
-/* Dump alias information on FILE. */
-
-void
-dump_alias_info (FILE *file)
-{
- size_t i;
- const char *funcname
- = lang_hooks.decl_printable_name (current_function_decl, 2);
- referenced_var_iterator rvi;
- tree var;
-
- fprintf (file, "\nAlias information for %s\n\n", funcname);
-
- dump_memory_partitions (file);
-
- fprintf (file, "\nFlow-insensitive alias information for %s\n\n", funcname);
-
- fprintf (file, "Aliased symbols\n\n");
-
- FOR_EACH_REFERENCED_VAR (var, rvi)
- {
- if (may_be_aliased (var))
- dump_variable (file, var);
- }
-
- fprintf (file, "\nDereferenced pointers\n\n");
-
- FOR_EACH_REFERENCED_VAR (var, rvi)
- if (symbol_mem_tag (var))
- dump_variable (file, var);
-
- fprintf (file, "\nSymbol memory tags\n\n");
-
- FOR_EACH_REFERENCED_VAR (var, rvi)
- {
- if (TREE_CODE (var) == SYMBOL_MEMORY_TAG)
- dump_variable (file, var);
- }
-
- fprintf (file, "\n\nFlow-sensitive alias information for %s\n\n", funcname);
-
- fprintf (file, "SSA_NAME pointers\n\n");
- for (i = 1; i < num_ssa_names; i++)
- {
- tree ptr = ssa_name (i);
- struct ptr_info_def *pi;
-
- if (ptr == NULL_TREE)
- continue;
-
- pi = SSA_NAME_PTR_INFO (ptr);
- if (!SSA_NAME_IN_FREE_LIST (ptr)
- && pi
- && pi->name_mem_tag)
- dump_points_to_info_for (file, ptr);
- }
-
- fprintf (file, "\nName memory tags\n\n");
-
- FOR_EACH_REFERENCED_VAR (var, rvi)
- {
- if (TREE_CODE (var) == NAME_MEMORY_TAG)
- dump_variable (file, var);
- }
-
- fprintf (file, "\n");
-}
-
-
-/* Dump alias information on stderr. */
-
-void
-debug_alias_info (void)
-{
- dump_alias_info (stderr);
-}
-
-
-/* Return the alias information associated with pointer T. It creates a
- new instance if none existed. */
-
-struct ptr_info_def *
-get_ptr_info (tree t)
-{
- struct ptr_info_def *pi;
-
- gcc_assert (POINTER_TYPE_P (TREE_TYPE (t)));
-
- pi = SSA_NAME_PTR_INFO (t);
- if (pi == NULL)
- {
- pi = GGC_CNEW (struct ptr_info_def);
- SSA_NAME_PTR_INFO (t) = pi;
- }
-
- return pi;
-}
-
-
-/* Dump points-to information for SSA_NAME PTR into FILE. */
-
-void
-dump_points_to_info_for (FILE *file, tree ptr)
-{
- struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
-
- print_generic_expr (file, ptr, dump_flags);
-
- if (pi)
- {
- if (pi->name_mem_tag)
- {
- fprintf (file, ", name memory tag: ");
- print_generic_expr (file, pi->name_mem_tag, dump_flags);
- }
-
- if (pi->is_dereferenced)
- fprintf (file, ", is dereferenced");
-
- if (pi->value_escapes_p)
- fprintf (file, ", its value escapes");
-
- if (pi->pt_anything)
- fprintf (file, ", points-to anything");
-
- if (pi->pt_null)
- fprintf (file, ", points-to NULL");
-
- if (pi->pt_vars)
- {
- fprintf (file, ", points-to vars: ");
- dump_decl_set (file, pi->pt_vars);
- }
- }
-
- fprintf (file, "\n");
-}
-
-
-/* Dump points-to information for VAR into stderr. */
-
-void
-debug_points_to_info_for (tree var)
-{
- dump_points_to_info_for (stderr, var);
-}
-
-
-/* Dump points-to information into FILE. NOTE: This function is slow, as
- it needs to traverse the whole CFG looking for pointer SSA_NAMEs. */
-
-void
-dump_points_to_info (FILE *file)
-{
- basic_block bb;
- block_stmt_iterator si;
- ssa_op_iter iter;
- const char *fname =
- lang_hooks.decl_printable_name (current_function_decl, 2);
- referenced_var_iterator rvi;
- tree var;
-
- fprintf (file, "\n\nPointed-to sets for pointers in %s\n\n", fname);
-
- /* First dump points-to information for the default definitions of
- pointer variables. This is necessary because default definitions are
- not part of the code. */
- FOR_EACH_REFERENCED_VAR (var, rvi)
- {
- if (POINTER_TYPE_P (TREE_TYPE (var)))
- {
- tree def = gimple_default_def (cfun, var);
- if (def)
- dump_points_to_info_for (file, def);
- }
- }
-
- /* Dump points-to information for every pointer defined in the program. */
- FOR_EACH_BB (bb)
- {
- tree phi;
-
- for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
- {
- tree ptr = PHI_RESULT (phi);
- if (POINTER_TYPE_P (TREE_TYPE (ptr)))
- dump_points_to_info_for (file, ptr);
- }
-
- for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
- {
- tree stmt = bsi_stmt (si);
- tree def;
- FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_DEF)
- if (TREE_CODE (def) == SSA_NAME
- && POINTER_TYPE_P (TREE_TYPE (def)))
- dump_points_to_info_for (file, def);
- }
- }
-
- fprintf (file, "\n");
-}
-
-
-/* Dump points-to info pointed to by PTO into STDERR. */
-
-void
-debug_points_to_info (void)
-{
- dump_points_to_info (stderr);
-}
-
-/* Dump to FILE the list of variables that may be aliasing VAR. */
-
-void
-dump_may_aliases_for (FILE *file, tree var)
-{
- bitmap aliases;
-
- aliases = MTAG_ALIASES (var);
- if (aliases)
- {
- bitmap_iterator bi;
- unsigned int i;
- tree al;
-
- fprintf (file, "{ ");
- EXECUTE_IF_SET_IN_BITMAP (aliases, 0, i, bi)
- {
- al = referenced_var (i);
- print_generic_expr (file, al, dump_flags);
- fprintf (file, " ");
- }
- fprintf (file, "}");
- }
-}
-
-
-/* Dump to stderr the list of variables that may be aliasing VAR. */
-
-void
-debug_may_aliases_for (tree var)
-{
- dump_may_aliases_for (stderr, var);
-}
-
-
-/* Return true if VAR may be aliased. */
-
-bool
-may_be_aliased (tree var)
-{
- /* Obviously. */
- if (TREE_ADDRESSABLE (var))
- return true;
-
- /* Globally visible variables can have their addresses taken by other
- translation units. */
- if (MTAG_P (var)
- && (MTAG_GLOBAL (var) || TREE_PUBLIC (var)))
- return true;
- else if (!MTAG_P (var)
- && (DECL_EXTERNAL (var) || TREE_PUBLIC (var)))
- return true;
-
- /* Automatic variables can't have their addresses escape any other
- way. This must be after the check for global variables, as
- extern declarations do not have TREE_STATIC set. */
- if (!TREE_STATIC (var))
- return false;
-
- /* If we're in unit-at-a-time mode, then we must have seen all
- occurrences of address-of operators, and so we can trust
- TREE_ADDRESSABLE. Otherwise we can only be sure the variable
- isn't addressable if it's local to the current function. */
- if (flag_unit_at_a_time)
- return false;
-
- if (decl_function_context (var) == current_function_decl)
- return false;
-
- return true;
-}
-
-/* The following is based on code in add_stmt_operand to ensure that the
- same defs/uses/vdefs/vuses will be found after replacing a reference
- to var (or ARRAY_REF to var) with an INDIRECT_REF to ptr whose value
- is the address of var. Return a memtag for the ptr, after adding the
- proper may_aliases to it (which are the aliases of var, if it has any,
- or var itself). */
-
-static tree
-add_may_alias_for_new_tag (tree tag, tree var)
-{
- bitmap aliases = NULL;
-
- if (MTAG_P (var))
- aliases = may_aliases (var);
-
- /* Case 1: |aliases| == 1 */
- if (aliases
- && bitmap_single_bit_set_p (aliases))
- {
- tree ali = referenced_var (bitmap_first_set_bit (aliases));
- if (TREE_CODE (ali) == SYMBOL_MEMORY_TAG)
- return ali;
- }
-
- /* Case 2: |aliases| == 0 */
- if (aliases == NULL)
- add_may_alias (tag, var);
- else
- {
- /* Case 3: |aliases| > 1 */
- union_alias_set_into (tag, aliases);
- }
- return tag;
-}
-
-/* Create a new symbol tag for PTR. Construct the may-alias list of this type
- tag so that it has the aliasing of VAR, or of the relevant subvars of VAR
- according to the location accessed by EXPR.
-
- Note, the set of aliases represented by the new symbol tag are not marked
- for renaming. */
-
-void
-new_type_alias (tree ptr, tree var, tree expr)
-{
- tree tag_type = TREE_TYPE (TREE_TYPE (ptr));
- tree tag;
- subvar_t svars;
- tree ali = NULL_TREE;
- HOST_WIDE_INT offset, size, maxsize;
- tree ref;
- VEC (tree, heap) *overlaps = NULL;
- unsigned int len, i;
- tree subvar;
-
-
- gcc_assert (symbol_mem_tag (ptr) == NULL_TREE);
- gcc_assert (!MTAG_P (var));
-
- ref = get_ref_base_and_extent (expr, &offset, &size, &maxsize);
- gcc_assert (ref);
-
- tag = create_memory_tag (tag_type, true);
- set_symbol_mem_tag (ptr, tag);
-
- /* Add VAR to the may-alias set of PTR's new symbol tag. If VAR has
- subvars, add the subvars to the tag instead of the actual var. */
- if (var_can_have_subvars (ref)
- && (svars = get_subvars_for_var (ref)))
- {
- for (i = 0; VEC_iterate (tree, svars, i, subvar); ++i)
- {
- bool exact;
-
- if (overlap_subvar (offset, maxsize, subvar, &exact))
- VEC_safe_push (tree, heap, overlaps, subvar);
- }
- gcc_assert (overlaps != NULL);
- }
- else if (var_can_have_subvars (var)
- && (svars = get_subvars_for_var (var)))
- {
- /* If the REF is not a direct access to VAR (e.g., it is a dereference
- of a pointer), we should scan the virtual operands of REF the same
- way as tree-ssa-operands do. At the moment, this is somewhat
- difficult, so we just give up and add all the subvars of VAR.
- On mem-ssa branch, the scanning for virtual operands have been
- split from the rest of tree-ssa-operands, so it should be much
- easier to fix this problem correctly once mem-ssa is merged. */
- for (i = 0; VEC_iterate (tree, svars, i, subvar); ++i)
- VEC_safe_push (tree, heap, overlaps, subvar);
-
- gcc_assert (overlaps != NULL);
- }
- else
- ali = add_may_alias_for_new_tag (tag, var);
-
- len = VEC_length (tree, overlaps);
- if (len > 0)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "\nnumber of overlapping subvars = %u\n", len);
-
- if (len == 1)
- ali = add_may_alias_for_new_tag (tag, VEC_index (tree, overlaps, 0));
- else if (len > 1)
- {
- unsigned int k;
- tree sv_var;
-
- for (k = 0; VEC_iterate (tree, overlaps, k, sv_var); k++)
- {
- ali = add_may_alias_for_new_tag (tag, sv_var);
-
- if (ali != tag)
- {
- /* Can happen only if 'Case 1' of add_may_alias_for_new_tag
- took place. Since more than one svar was found, we add
- 'ali' as one of the may_aliases of the new tag. */
- add_may_alias (tag, ali);
- ali = tag;
- }
- }
- }
- VEC_free (tree, heap, overlaps);
- }
-
- set_symbol_mem_tag (ptr, ali);
- TREE_READONLY (tag) = TREE_READONLY (var);
- MTAG_GLOBAL (tag) = is_global_var (var);
-}
-
-/* This represents the used range of a variable. */
-
-typedef struct used_part
-{
- HOST_WIDE_INT minused;
- HOST_WIDE_INT maxused;
- /* True if we have an explicit use/def of some portion of this variable,
- even if it is all of it. i.e. a.b = 5 or temp = a.b. */
- bool explicit_uses;
- /* True if we have an implicit use/def of some portion of this
- variable. Implicit uses occur when we can't tell what part we
- are referencing, and have to make conservative assumptions. */
- bool implicit_uses;
- /* True if the structure is only written to or taken its address. */
- bool write_only;
-} *used_part_t;
-
-/* An array of used_part structures, indexed by variable uid. */
-
-static htab_t used_portions;
-
-struct used_part_map
-{
- unsigned int uid;
- used_part_t to;
-};
-
-/* Return true if the uid in the two used part maps are equal. */
-
-static int
-used_part_map_eq (const void *va, const void *vb)
-{
- const struct used_part_map *a = (const struct used_part_map *) va;
- const struct used_part_map *b = (const struct used_part_map *) vb;
- return (a->uid == b->uid);
-}
-
-/* Hash a from uid in a used_part_map. */
-
-static unsigned int
-used_part_map_hash (const void *item)
-{
- return ((const struct used_part_map *)item)->uid;
-}
-
-/* Free a used part map element. */
-
-static void
-free_used_part_map (void *item)
-{
- free (((struct used_part_map *)item)->to);
- free (item);
-}
-
-/* Lookup a used_part structure for a UID. */
-
-static used_part_t
-up_lookup (unsigned int uid)
-{
- struct used_part_map *h, in;
- in.uid = uid;
- h = (struct used_part_map *) htab_find_with_hash (used_portions, &in, uid);
- if (!h)
- return NULL;
- return h->to;
-}
-
-/* Insert the pair UID, TO into the used part hashtable. */
-
-static void
-up_insert (unsigned int uid, used_part_t to)
-{
- struct used_part_map *h;
- void **loc;
-
- h = XNEW (struct used_part_map);
- h->uid = uid;
- h->to = to;
- loc = htab_find_slot_with_hash (used_portions, h,
- uid, INSERT);
- if (*loc != NULL)
- free (*loc);
- *(struct used_part_map **) loc = h;
-}
-
-
-/* Given a variable uid, UID, get or create the entry in the used portions
- table for the variable. */
-
-static used_part_t
-get_or_create_used_part_for (size_t uid)
-{
- used_part_t up;
- if ((up = up_lookup (uid)) == NULL)
- {
- up = XCNEW (struct used_part);
- up->minused = INT_MAX;
- up->maxused = 0;
- up->explicit_uses = false;
- up->implicit_uses = false;
- up->write_only = true;
- }
-
- return up;
-}
-
-
-/* Create and return a structure sub-variable for field type FIELD at
- offset OFFSET, with size SIZE, of variable VAR. If ALIAS_SET not
- -1 this field is non-addressable and we should use this alias set
- with this field. */
-
-static tree
-create_sft (tree var, tree field, unsigned HOST_WIDE_INT offset,
- unsigned HOST_WIDE_INT size, alias_set_type alias_set,
- unsigned nesting_level)
-{
- tree subvar = create_tag_raw (STRUCT_FIELD_TAG, field, "SFT");
-
- /* We need to copy the various flags from VAR to SUBVAR, so that
- they are is_global_var iff the original variable was. */
- DECL_CONTEXT (subvar) = DECL_CONTEXT (var);
- MTAG_GLOBAL (subvar) = DECL_EXTERNAL (var);
- TREE_PUBLIC (subvar) = TREE_PUBLIC (var);
- TREE_STATIC (subvar) = TREE_STATIC (var);
- TREE_READONLY (subvar) = TREE_READONLY (var);
- TREE_ADDRESSABLE (subvar) = TREE_ADDRESSABLE (var);
-
- /* Add the new variable to REFERENCED_VARS. */
- set_symbol_mem_tag (subvar, NULL);
- add_referenced_var (subvar);
- SFT_PARENT_VAR (subvar) = var;
- SFT_OFFSET (subvar) = offset;
- SFT_SIZE (subvar) = size;
- SFT_ALIAS_SET (subvar) = alias_set;
- SFT_NESTING_LEVEL (subvar) = nesting_level;
-
- return subvar;
-}
-
-
-/* Given an aggregate VAR, create the subvariables that represent its
- fields. */
-
-static void
-create_overlap_variables_for (tree var)
-{
- VEC(fieldoff_s,heap) *fieldstack = NULL;
- used_part_t up;
- size_t uid = DECL_UID (var);
-
- up = up_lookup (uid);
- if (!up
- || up->write_only)
- return;
-
- push_fields_onto_fieldstack (TREE_TYPE (var), &fieldstack, 0, NULL,
- TREE_TYPE (var), 0);
- if (VEC_length (fieldoff_s, fieldstack) != 0)
- {
- subvar_t *subvars;
- fieldoff_s *fo;
- bool notokay = false;
- int fieldcount = 0;
- int i;
- HOST_WIDE_INT lastfooffset = -1;
- HOST_WIDE_INT lastfosize = -1;
- tree lastfotype = NULL_TREE;
-
- /* Not all fields have DECL_SIZE set, and those that don't, we don't
- know their size, and thus, can't handle.
- The same is true of fields with DECL_SIZE that is not an integer
- constant (such as variable sized fields).
- Fields with offsets which are not constant will have an offset < 0
- We *could* handle fields that are constant sized arrays, but
- currently don't. Doing so would require some extra changes to
- tree-ssa-operands.c. */
-
- for (i = 0; VEC_iterate (fieldoff_s, fieldstack, i, fo); i++)
- {
- if (!fo->size
- || TREE_CODE (fo->size) != INTEGER_CST
- || fo->offset < 0)
- {
- notokay = true;
- break;
- }
- fieldcount++;
- }
-
- /* The current heuristic we use is as follows:
- If the variable has no used portions in this function, no
- structure vars are created for it.
- Otherwise,
- If the variable has less than SALIAS_MAX_IMPLICIT_FIELDS,
- we always create structure vars for them.
- If the variable has more than SALIAS_MAX_IMPLICIT_FIELDS, and
- some explicit uses, we create structure vars for them.
- If the variable has more than SALIAS_MAX_IMPLICIT_FIELDS, and
- no explicit uses, we do not create structure vars for them.
- */
-
- if (fieldcount >= SALIAS_MAX_IMPLICIT_FIELDS
- && !up->explicit_uses)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Variable ");
- print_generic_expr (dump_file, var, 0);
- fprintf (dump_file, " has no explicit uses in this function, and is > SALIAS_MAX_IMPLICIT_FIELDS, so skipping\n");
- }
- notokay = true;
- }
-
- /* Bail out, if we can't create overlap variables. */
- if (notokay)
- {
- VEC_free (fieldoff_s, heap, fieldstack);
- return;
- }
-
- /* Otherwise, create the variables. */
- subvars = lookup_subvars_for_var (var);
- *subvars = VEC_alloc (tree, gc, VEC_length (fieldoff_s, fieldstack));
-
- sort_fieldstack (fieldstack);
-
- for (i = 0; VEC_iterate (fieldoff_s, fieldstack, i, fo); ++i)
- {
- HOST_WIDE_INT fosize;
- tree currfotype, subvar;
-
- fosize = TREE_INT_CST_LOW (fo->size);
- currfotype = fo->type;
-
- /* If this field isn't in the used portion,
- or it has the exact same offset and size as the last
- field, skip it. Note that we always need the field at
- offset 0 so we can properly handle pointers to the
- structure. */
- if ((fo->offset != 0
- && ((fo->offset <= up->minused
- && fo->offset + fosize <= up->minused)
- || fo->offset >= up->maxused))
- || (fo->offset == lastfooffset
- && fosize == lastfosize
- && currfotype == lastfotype))
- continue;
-
- subvar = create_sft (var, fo->type, fo->offset, fosize,
- fo->alias_set, fo->nesting_level);
- VEC_quick_push (tree, *subvars, subvar);
-
- if (dump_file)
- {
- fprintf (dump_file, "structure field tag %s created for var %s",
- get_name (subvar), get_name (var));
- fprintf (dump_file, " offset " HOST_WIDE_INT_PRINT_DEC,
- SFT_OFFSET (subvar));
- fprintf (dump_file, " size " HOST_WIDE_INT_PRINT_DEC,
- SFT_SIZE (subvar));
- fprintf (dump_file, " nesting level %d\n",
- SFT_NESTING_LEVEL (subvar));
- }
-
- lastfotype = currfotype;
- lastfooffset = fo->offset;
- lastfosize = fosize;
- }
-
- /* Once we have created subvars, the original is no longer call
- clobbered on its own. Its call clobbered status depends
- completely on the call clobbered status of the subvars.
-
- add_referenced_var in the above loop will take care of
- marking subvars of global variables as call clobbered for us
- to start, since they are global as well. */
- clear_call_clobbered (var);
- }
-
- VEC_free (fieldoff_s, heap, fieldstack);
-}
-
-
-/* Find the conservative answer to the question of what portions of what
- structures are used by this statement. We assume that if we have a
- component ref with a known size + offset, that we only need that part
- of the structure. For unknown cases, or cases where we do something
- to the whole structure, we assume we need to create fields for the
- entire structure. */
-
-static tree
-find_used_portions (tree *tp, int *walk_subtrees, void *lhs_p)
-{
- switch (TREE_CODE (*tp))
- {
- case GIMPLE_MODIFY_STMT:
- /* Recurse manually here to track whether the use is in the
- LHS of an assignment. */
- find_used_portions (&GIMPLE_STMT_OPERAND (*tp, 0), walk_subtrees, tp);
- return find_used_portions (&GIMPLE_STMT_OPERAND (*tp, 1),
- walk_subtrees, NULL);
- case REALPART_EXPR:
- case IMAGPART_EXPR:
- case COMPONENT_REF:
- case ARRAY_REF:
- {
- HOST_WIDE_INT bitsize;
- HOST_WIDE_INT bitmaxsize;
- HOST_WIDE_INT bitpos;
- tree ref;
- ref = get_ref_base_and_extent (*tp, &bitpos, &bitsize, &bitmaxsize);
- if (DECL_P (ref)
- && var_can_have_subvars (ref)
- && bitmaxsize != -1)
- {
- size_t uid = DECL_UID (ref);
- used_part_t up;
-
- up = get_or_create_used_part_for (uid);
-
- if (bitpos <= up->minused)
- up->minused = bitpos;
- if ((bitpos + bitmaxsize >= up->maxused))
- up->maxused = bitpos + bitmaxsize;
-
- if (bitsize == bitmaxsize)
- up->explicit_uses = true;
- else
- up->implicit_uses = true;
- if (!lhs_p)
- up->write_only = false;
- up_insert (uid, up);
-
- *walk_subtrees = 0;
- return NULL_TREE;
- }
- }
- break;
- /* This is here to make sure we mark the entire base variable as used
- when you take its address. Because our used portion analysis is
- simple, we aren't looking at casts or pointer arithmetic to see what
- happens when you take the address. */
- case ADDR_EXPR:
- {
- tree var = get_base_address (TREE_OPERAND (*tp, 0));
-
- if (var
- && DECL_P (var)
- && DECL_SIZE (var)
- && var_can_have_subvars (var)
- && TREE_CODE (DECL_SIZE (var)) == INTEGER_CST)
- {
- used_part_t up;
- size_t uid = DECL_UID (var);
-
- up = get_or_create_used_part_for (uid);
-
- up->minused = 0;
- up->maxused = TREE_INT_CST_LOW (DECL_SIZE (var));
- up->implicit_uses = true;
- if (!lhs_p)
- up->write_only = false;
-
- up_insert (uid, up);
- *walk_subtrees = 0;
- return NULL_TREE;
- }
- }
- break;
- case CALL_EXPR:
- {
- int i;
- int nargs = call_expr_nargs (*tp);
- for (i = 0; i < nargs; i++)
- {
- tree *arg = &CALL_EXPR_ARG (*tp, i);
- if (TREE_CODE (*arg) == ADDR_EXPR)
- find_used_portions (arg, walk_subtrees, NULL);
- }
- *walk_subtrees = 0;
- return NULL_TREE;
- }
- case VAR_DECL:
- case PARM_DECL:
- case RESULT_DECL:
- {
- tree var = *tp;
- if (DECL_SIZE (var)
- && var_can_have_subvars (var)
- && TREE_CODE (DECL_SIZE (var)) == INTEGER_CST)
- {
- used_part_t up;
- size_t uid = DECL_UID (var);
-
- up = get_or_create_used_part_for (uid);
-
- up->minused = 0;
- up->maxused = TREE_INT_CST_LOW (DECL_SIZE (var));
- up->implicit_uses = true;
-
- up_insert (uid, up);
- *walk_subtrees = 0;
- return NULL_TREE;
- }
- }
- break;
-
- default:
- break;
-
- }
- return NULL_TREE;
-}
-
-/* Create structure field variables for structures used in this function. */
-
-static unsigned int
-create_structure_vars (void)
-{
- basic_block bb;
- safe_referenced_var_iterator rvi;
- VEC (tree, heap) *varvec = NULL;
- tree var;
-
- used_portions = htab_create (10, used_part_map_hash, used_part_map_eq,
- free_used_part_map);
-
- FOR_EACH_BB (bb)
- {
- block_stmt_iterator bsi;
- tree phi;
-
- for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
- {
- use_operand_p use;
- ssa_op_iter iter;
-
- FOR_EACH_PHI_ARG (use, phi, iter, SSA_OP_USE)
- {
- tree op = USE_FROM_PTR (use);
- walk_tree_without_duplicates (&op, find_used_portions,
- NULL);
- }
- }
-
- for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
- {
- walk_tree_without_duplicates (bsi_stmt_ptr (bsi),
- find_used_portions,
- NULL);
- }
- }
- FOR_EACH_REFERENCED_VAR_SAFE (var, varvec, rvi)
- {
- /* The C++ FE creates vars without DECL_SIZE set, for some reason. */
- if (var
- && DECL_SIZE (var)
- && var_can_have_subvars (var)
- && !MTAG_P (var)
- && TREE_CODE (DECL_SIZE (var)) == INTEGER_CST)
- create_overlap_variables_for (var);
- }
- htab_delete (used_portions);
- VEC_free (tree, heap, varvec);
-
- /* Update SSA operands of statements mentioning variables we split. */
- if (gimple_in_ssa_p (cfun))
- FOR_EACH_BB (bb)
- {
- block_stmt_iterator bsi;
- for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
- {
- tree stmt = bsi_stmt (bsi);
- bool update = false;
- unsigned int i;
- bitmap_iterator bi;
-
- if (STORED_SYMS (stmt))
- EXECUTE_IF_SET_IN_BITMAP (STORED_SYMS (stmt), 0, i, bi)
- {
- tree sym = referenced_var_lookup (i);
- if (get_subvars_for_var (sym))
- {
- update = true;
- break;
- }
- }
-
- if (LOADED_SYMS (stmt) && !update)
- EXECUTE_IF_SET_IN_BITMAP (LOADED_SYMS (stmt), 0, i, bi)
- {
- tree sym = referenced_var_lookup (i);
- if (get_subvars_for_var (sym))
- {
- update = true;
- break;
- }
- }
-
- if (stmt_ann (stmt)->addresses_taken && !update)
- EXECUTE_IF_SET_IN_BITMAP (stmt_ann (stmt)->addresses_taken,
- 0, i, bi)
- {
- tree sym = referenced_var_lookup (i);
- if (get_subvars_for_var (sym))
- {
- update = true;
- break;
- }
- }
-
- if (update)
- update_stmt (stmt);
- }
- }
-
- return TODO_rebuild_alias;
-}
-
-static bool
-gate_structure_vars (void)
-{
- return flag_tree_salias != 0;
-}
-
-struct tree_opt_pass pass_create_structure_vars =
-{
- "salias", /* name */
- gate_structure_vars, /* gate */
- create_structure_vars, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- 0, /* tv_id */
- PROP_cfg, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_dump_func, /* todo_flags_finish */
- 0 /* letter */
-};
-
-/* Reset the call_clobbered flags on our referenced vars. In
- theory, this only needs to be done for globals. */
-
-static unsigned int
-reset_cc_flags (void)
-{
- tree var;
- referenced_var_iterator rvi;
-
- FOR_EACH_REFERENCED_VAR (var, rvi)
- var_ann (var)->call_clobbered = false;
- return 0;
-}
-
-struct tree_opt_pass pass_reset_cc_flags =
-{
- NULL, /* name */
- NULL, /* gate */
- reset_cc_flags, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- 0, /* tv_id */
- PROP_referenced_vars |PROP_cfg, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- 0, /* todo_flags_finish */
- 0 /* letter */
-};