Makefile.in (tree-ssa-alias.o): Depend on tree-ssa-structalias.h
* Makefile.in (tree-ssa-alias.o): Depend on tree-ssa-structalias.h * tree-cfg.c (CHECK_OP): Only test for is_gimple_val. * tree-dfa.c (dump_subvars_for): New. (debug_subvars_for): New. (dump_variable): Show subvariables if VAR has them. * tree-flow-inline.h (get_subvar_at): New. (overlap_subvar): Change offset and size to unsigned HOST_WIDE_INT. * tree-flow.h (struct ptr_info_def): Remove field pt_malloc. Update all users. (struct subvar): Change fields offset and size to unsigned HOST_WIDE_INT. (dump_subvars_for): Declare. (debug_subvars_for): Declare. (get_subvar_at): Declare. (okay_component_ref_for_subvars): Change 2nd and 3rd argument to unsigned HOST_WIDE_INT *. (overlap_subvar): Likewise. * tree-gimple.c (is_gimple_reg): Always return false for SFTs and memory tags. * tree-pass.h (pass_build_pta, pass_del_pta): Remove. Update all callers. * tree-ssa-alias.c: Include tree-ssa-structalias.h. (compute_may_aliases): Call compute_points_to_sets. (collect_points_to_info_for): Remove. (compute_points_to_and_addr_escape): Remove. (delete_alias_info): Call delete_points_to_sets. (compute_flow_sensitive_aliasing): If the call to find_what_p_points_to returns false, call set_pt_anything. (add_may_alias): Set TREE_ADDRESSABLE when adding a new alias. (set_pt_anything): Clear pi->pt_vars. (set_pt_malloc): Remove. (merge_pointed_to_info): Remove. (add_pointed_to_expr): Remove. (add_pointed_to_var): Remove. (collect_points_to_info_r): Remove. (is_escape_site): Make extern. (create_sft): New. (create_overlap_variables_for): Call it. * tree-ssa-copy.c (merge_alias_info): Never merge flow-sensitive alias information. * tree-ssa-operands.c (get_expr_operands): Adjust variables offset and size to be unsigned HOST_WIDE_INT. (add_to_addressable_set): Rename from note_addressable. Set TREE_ADDRESSABLE as the variables are added to the set. Update all users. (add_stmt_operand): Do not try to micro-optimize unmodifiable operands into VUSEs when adding V_MAY_DEFs for members in an alias set. * tree-ssa-operands.h (add_to_addressable_set): Declare. * tree-ssa-structalias.c: Include tree-ssa-structalias.h last. (struct variable_info): Add bitfield is_heap_var. (var_anyoffset, anyoffset_tree, anyoffset_id): Declare. (new_var_info): Initialize is_heap_var. (get_constraint_for): Add HEAP variables to the symbol table. Mark them with is_heap_var. (update_alias_info): New. Taken mostly from the old compute_points_to_and_addr_escape. (handle_ptr_arith): New. (find_func_aliases): Call update_alias_info. Call handle_ptr_info for tcc_binary expressions. Call mark_stmt_modified. (create_variable_info_for): If DECL has subvars, do not create variables for its subvars. Always add all the fields. (set_uids_in_ptset): If the solution includes ANYOFFSET and SFTs, then add all the SFTs of the structure. If VI->DECL is an aggregate with subvariables, add the SFT at VI->OFFSET. (find_what_p_points_to): If VI is an artificial variable, translate to bitfields in SSA_NAME_PTR_INFO. If the solution is empty, set pi->pt_vars to NULL (init_base_vars): Create ANYOFFSET. (compute_points_to_sets): Rename from create_alias_vars. Make extern. (pass_build_pta): Remove. (delete_points_to_sets): Rename from delete_alias_vars. (pass_del_pta): Remove. * tree-ssa-structalias.h (struct alias_info): Move from tree-ssa-alias.h. (NUM_REFERENCES, NUM_REFERENCES_CLEAR, NUM_REFERENCES_INC, NUM_REFERENCES_SET): Likewise. (compute_points_to_sets, delete_points_to_sets): Declare. testsuite/ChangeLog * gcc.dg/tree-ssa/pta-fp.c: Use -fdump-tree-alias1. From-SVN: r101841
This commit is contained in:
parent
87f8dcd06e
commit
e8ca415995
@ -1,3 +1,87 @@
|
||||
2005-07-09 Diego Novillo <dnovillo@redhat.com>
|
||||
|
||||
* Makefile.in (tree-ssa-alias.o): Depend on tree-ssa-structalias.h
|
||||
* tree-cfg.c (CHECK_OP): Only test for is_gimple_val.
|
||||
* tree-dfa.c (dump_subvars_for): New.
|
||||
(debug_subvars_for): New.
|
||||
(dump_variable): Show subvariables if VAR has them.
|
||||
* tree-flow-inline.h (get_subvar_at): New.
|
||||
(overlap_subvar): Change offset and size to unsigned HOST_WIDE_INT.
|
||||
* tree-flow.h (struct ptr_info_def): Remove field pt_malloc.
|
||||
Update all users.
|
||||
(struct subvar): Change fields offset and size to unsigned
|
||||
HOST_WIDE_INT.
|
||||
(dump_subvars_for): Declare.
|
||||
(debug_subvars_for): Declare.
|
||||
(get_subvar_at): Declare.
|
||||
(okay_component_ref_for_subvars): Change 2nd and 3rd argument
|
||||
to unsigned HOST_WIDE_INT *.
|
||||
(overlap_subvar): Likewise.
|
||||
* tree-gimple.c (is_gimple_reg): Always return false for
|
||||
SFTs and memory tags.
|
||||
* tree-pass.h (pass_build_pta, pass_del_pta): Remove.
|
||||
Update all callers.
|
||||
* tree-ssa-alias.c: Include tree-ssa-structalias.h.
|
||||
(compute_may_aliases): Call compute_points_to_sets.
|
||||
(collect_points_to_info_for): Remove.
|
||||
(compute_points_to_and_addr_escape): Remove.
|
||||
(delete_alias_info): Call delete_points_to_sets.
|
||||
(compute_flow_sensitive_aliasing): If the call to
|
||||
find_what_p_points_to returns false, call set_pt_anything.
|
||||
(add_may_alias): Set TREE_ADDRESSABLE when adding a new alias.
|
||||
(set_pt_anything): Clear pi->pt_vars.
|
||||
(set_pt_malloc): Remove.
|
||||
(merge_pointed_to_info): Remove.
|
||||
(add_pointed_to_expr): Remove.
|
||||
(add_pointed_to_var): Remove.
|
||||
(collect_points_to_info_r): Remove.
|
||||
(is_escape_site): Make extern.
|
||||
(create_sft): New.
|
||||
(create_overlap_variables_for): Call it.
|
||||
* tree-ssa-copy.c (merge_alias_info): Never merge
|
||||
flow-sensitive alias information.
|
||||
* tree-ssa-operands.c (get_expr_operands): Adjust variables
|
||||
offset and size to be unsigned HOST_WIDE_INT.
|
||||
(add_to_addressable_set): Rename from note_addressable.
|
||||
Set TREE_ADDRESSABLE as the variables are added to the set.
|
||||
Update all users.
|
||||
(add_stmt_operand): Do not try to micro-optimize unmodifiable
|
||||
operands into VUSEs when adding V_MAY_DEFs for members in an
|
||||
alias set.
|
||||
* tree-ssa-operands.h (add_to_addressable_set): Declare.
|
||||
* tree-ssa-structalias.c: Include tree-ssa-structalias.h last.
|
||||
(struct variable_info): Add bitfield is_heap_var.
|
||||
(var_anyoffset, anyoffset_tree, anyoffset_id): Declare.
|
||||
(new_var_info): Initialize is_heap_var.
|
||||
(get_constraint_for): Add HEAP variables to the symbol table.
|
||||
Mark them with is_heap_var.
|
||||
(update_alias_info): New. Taken mostly from the old
|
||||
compute_points_to_and_addr_escape.
|
||||
(handle_ptr_arith): New.
|
||||
(find_func_aliases): Call update_alias_info.
|
||||
Call handle_ptr_info for tcc_binary expressions.
|
||||
Call mark_stmt_modified.
|
||||
(create_variable_info_for): If DECL has subvars, do not create
|
||||
variables for its subvars. Always add all the fields.
|
||||
(set_uids_in_ptset): If the solution includes ANYOFFSET and
|
||||
SFTs, then add all the SFTs of the structure.
|
||||
If VI->DECL is an aggregate with subvariables, add the SFT at
|
||||
VI->OFFSET.
|
||||
(find_what_p_points_to): If VI is an artificial variable,
|
||||
translate to bitfields in SSA_NAME_PTR_INFO.
|
||||
If the solution is empty, set pi->pt_vars to NULL
|
||||
(init_base_vars): Create ANYOFFSET.
|
||||
(compute_points_to_sets): Rename from create_alias_vars.
|
||||
Make extern.
|
||||
(pass_build_pta): Remove.
|
||||
(delete_points_to_sets): Rename from delete_alias_vars.
|
||||
(pass_del_pta): Remove.
|
||||
* tree-ssa-structalias.h (struct alias_info): Move from
|
||||
tree-ssa-alias.h.
|
||||
(NUM_REFERENCES, NUM_REFERENCES_CLEAR, NUM_REFERENCES_INC,
|
||||
NUM_REFERENCES_SET): Likewise.
|
||||
(compute_points_to_sets, delete_points_to_sets): Declare.
|
||||
|
||||
2005-07-09 Richard Henderson <rth@redhat.com>
|
||||
|
||||
* config/alpha/alpha.c (emit_insxl, alpha_expand_compare_and_swap_12,
|
||||
|
@ -1895,7 +1895,7 @@ tree-ssa-alias.o : tree-ssa-alias.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
|
||||
$(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) tree-inline.h $(FLAGS_H) \
|
||||
function.h $(TIMEVAR_H) convert.h $(TM_H) coretypes.h langhooks.h \
|
||||
$(TREE_DUMP_H) tree-pass.h $(PARAMS_H) $(BASIC_BLOCK_H) $(DIAGNOSTIC_H) \
|
||||
hard-reg-set.h $(TREE_GIMPLE_H) vec.h
|
||||
hard-reg-set.h $(TREE_GIMPLE_H) vec.h tree-ssa-structalias.h
|
||||
tree-ssa-reassoc.o : tree-ssa-reassoc.c $(TREE_FLOW_H) $(CONFIG_H) \
|
||||
$(SYSTEM_H) $(TREE_H) $(GGC_H) $(DIAGNOSTIC_H) errors.h $(TIMEVAR_H) \
|
||||
$(TM_H) coretypes.h $(TREE_DUMP_H) tree-pass.h $(FLAGS_H) tree-iterator.h\
|
||||
|
@ -471,10 +471,8 @@ init_optimization_passes (void)
|
||||
NEXT_PASS (pass_referenced_vars);
|
||||
NEXT_PASS (pass_create_structure_vars);
|
||||
NEXT_PASS (pass_build_ssa);
|
||||
NEXT_PASS (pass_build_pta);
|
||||
NEXT_PASS (pass_may_alias);
|
||||
NEXT_PASS (pass_return_slot);
|
||||
NEXT_PASS (pass_del_pta);
|
||||
NEXT_PASS (pass_rename_ssa_copies);
|
||||
NEXT_PASS (pass_early_warn_uninitialized);
|
||||
|
||||
@ -490,9 +488,7 @@ init_optimization_passes (void)
|
||||
NEXT_PASS (pass_dominator);
|
||||
|
||||
NEXT_PASS (pass_phiopt);
|
||||
NEXT_PASS (pass_build_pta);
|
||||
NEXT_PASS (pass_may_alias);
|
||||
NEXT_PASS (pass_del_pta);
|
||||
NEXT_PASS (pass_tail_recursion);
|
||||
NEXT_PASS (pass_profile);
|
||||
NEXT_PASS (pass_ch);
|
||||
|
@ -1,3 +1,7 @@
|
||||
2005-07-09 Diego Novillo <dnovillo@redhat.com>
|
||||
|
||||
* gcc.dg/tree-ssa/pta-fp.c: Use -fdump-tree-alias1.
|
||||
|
||||
2005-07-09 Richard Henderson <rth@redhat.com>
|
||||
|
||||
* lib/target-supports.exp (check_effective_target_sync_char_short):
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-tree-pta1" } */
|
||||
/* { dg-options "-O2 -fdump-tree-alias1" } */
|
||||
extern double cos (double);
|
||||
extern double sin (double);
|
||||
double f(double a)
|
||||
@ -22,5 +22,5 @@ double f(double a)
|
||||
}
|
||||
/* The points-to set of the final function pointer should be "sin cos" */
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "sin cos" 1 "pta1"} } */
|
||||
/* { dg-final { cleanup-tree-dump "pta1" } } */
|
||||
/* { dg-final { scan-tree-dump-times "sin cos" 1 "alias1"} } */
|
||||
/* { dg-final { cleanup-tree-dump "alias1" } } */
|
||||
|
@ -3083,12 +3083,9 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
|
||||
if (TYPE_P (t))
|
||||
*walk_subtrees = 0;
|
||||
|
||||
/* Check operand N for being valid GIMPLE and give error MSG if not.
|
||||
We check for constants explicitly since they are not considered
|
||||
gimple invariants if they overflowed. */
|
||||
/* Check operand N for being valid GIMPLE and give error MSG if not. */
|
||||
#define CHECK_OP(N, MSG) \
|
||||
do { if (!CONSTANT_CLASS_P (TREE_OPERAND (t, N)) \
|
||||
&& !is_gimple_val (TREE_OPERAND (t, N))) \
|
||||
do { if (!is_gimple_val (TREE_OPERAND (t, N))) \
|
||||
{ error (MSG); return TREE_OPERAND (t, N); }} while (0)
|
||||
|
||||
switch (TREE_CODE (t))
|
||||
|
@ -254,6 +254,37 @@ debug_referenced_vars (void)
|
||||
}
|
||||
|
||||
|
||||
/* Dump sub-variables for VAR to FILE. */
|
||||
|
||||
void
|
||||
dump_subvars_for (FILE *file, tree var)
|
||||
{
|
||||
subvar_t sv = get_subvars_for_var (var);
|
||||
|
||||
if (!sv)
|
||||
return;
|
||||
|
||||
fprintf (file, "{ ");
|
||||
|
||||
for (; sv; sv = sv->next)
|
||||
{
|
||||
print_generic_expr (file, sv->var, dump_flags);
|
||||
fprintf (file, " ");
|
||||
}
|
||||
|
||||
fprintf (file, "}");
|
||||
}
|
||||
|
||||
|
||||
/* Dumb sub-variables for VAR to stderr. */
|
||||
|
||||
void
|
||||
debug_subvars_for (tree var)
|
||||
{
|
||||
dump_subvars_for (stderr, var);
|
||||
}
|
||||
|
||||
|
||||
/* Dump variable VAR and its may-aliases to FILE. */
|
||||
|
||||
void
|
||||
@ -316,6 +347,12 @@ dump_variable (FILE *file, tree var)
|
||||
dump_may_aliases_for (file, var);
|
||||
}
|
||||
|
||||
if (get_subvars_for_var (var))
|
||||
{
|
||||
fprintf (file, ", sub-vars: ");
|
||||
dump_subvars_for (file, var);
|
||||
}
|
||||
|
||||
fprintf (file, "\n");
|
||||
}
|
||||
|
||||
@ -741,8 +778,8 @@ find_new_referenced_vars (tree *stmt_p)
|
||||
size, in bits, of REF inside the return value. */
|
||||
|
||||
tree
|
||||
okay_component_ref_for_subvars (tree ref, HOST_WIDE_INT *poffset,
|
||||
HOST_WIDE_INT *psize)
|
||||
okay_component_ref_for_subvars (tree ref, unsigned HOST_WIDE_INT *poffset,
|
||||
unsigned HOST_WIDE_INT *psize)
|
||||
{
|
||||
tree result = NULL;
|
||||
HOST_WIDE_INT bitsize;
|
||||
|
@ -1444,6 +1444,20 @@ get_subvars_for_var (tree var)
|
||||
return subvars;
|
||||
}
|
||||
|
||||
/* Return the subvariable of VAR at offset OFFSET. */
|
||||
|
||||
static inline tree
|
||||
get_subvar_at (tree var, unsigned HOST_WIDE_INT offset)
|
||||
{
|
||||
subvar_t sv;
|
||||
|
||||
for (sv = get_subvars_for_var (var); sv; sv = sv->next)
|
||||
if (sv->offset == offset)
|
||||
return sv->var;
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Return true if V is a tree that we can have subvars for.
|
||||
Normally, this is any aggregate type, however, due to implementation
|
||||
limitations ATM, we exclude array types as well. */
|
||||
@ -1461,7 +1475,7 @@ var_can_have_subvars (tree v)
|
||||
*EXACT will be set to true upon return. */
|
||||
|
||||
static inline bool
|
||||
overlap_subvar (HOST_WIDE_INT offset, HOST_WIDE_INT size,
|
||||
overlap_subvar (unsigned HOST_WIDE_INT offset, unsigned HOST_WIDE_INT size,
|
||||
subvar_t sv, bool *exact)
|
||||
{
|
||||
/* There are three possible cases of overlap.
|
||||
|
@ -71,9 +71,6 @@ struct ptr_info_def GTY(())
|
||||
is pointing to. */
|
||||
unsigned int pt_anything : 1;
|
||||
|
||||
/* Nonzero if this pointer is the result of a call to malloc. */
|
||||
unsigned int pt_malloc : 1;
|
||||
|
||||
/* Nonzero if the value of this pointer escapes the current function. */
|
||||
unsigned int value_escapes_p : 1;
|
||||
|
||||
@ -160,19 +157,22 @@ enum mem_tag_kind {
|
||||
/* This variable represents a structure field. */
|
||||
STRUCT_FIELD
|
||||
};
|
||||
|
||||
struct subvar;
|
||||
typedef struct subvar *subvar_t;
|
||||
|
||||
/* This structure represents a fake sub-variable for a structure field. */
|
||||
|
||||
struct subvar GTY(())
|
||||
{
|
||||
/* Fake variable name */
|
||||
/* Fake variable. */
|
||||
tree var;
|
||||
|
||||
/* Offset inside structure. */
|
||||
HOST_WIDE_INT offset;
|
||||
/* Size of field. */
|
||||
HOST_WIDE_INT size;
|
||||
unsigned HOST_WIDE_INT offset;
|
||||
|
||||
/* Size of the field. */
|
||||
unsigned HOST_WIDE_INT size;
|
||||
|
||||
/* Next subvar for this structure. */
|
||||
subvar_t next;
|
||||
};
|
||||
@ -552,6 +552,8 @@ extern void debug_referenced_vars (void);
|
||||
extern void dump_referenced_vars (FILE *);
|
||||
extern void dump_variable (FILE *, tree);
|
||||
extern void debug_variable (tree);
|
||||
extern void dump_subvars_for (FILE *, tree);
|
||||
extern void debug_subvars_for (tree);
|
||||
extern tree get_virtual_var (tree);
|
||||
extern void add_referenced_tmp_var (tree);
|
||||
extern void mark_new_vars_to_rename (tree);
|
||||
@ -578,11 +580,13 @@ extern void add_type_alias (tree, tree);
|
||||
extern void new_type_alias (tree, tree);
|
||||
extern void count_uses_and_derefs (tree, tree, unsigned *, unsigned *, bool *);
|
||||
static inline subvar_t get_subvars_for_var (tree);
|
||||
static inline tree get_subvar_at (tree, unsigned HOST_WIDE_INT);
|
||||
static inline bool ref_contains_array_ref (tree);
|
||||
extern tree okay_component_ref_for_subvars (tree, HOST_WIDE_INT *,
|
||||
HOST_WIDE_INT *);
|
||||
extern tree okay_component_ref_for_subvars (tree, unsigned HOST_WIDE_INT *,
|
||||
unsigned HOST_WIDE_INT *);
|
||||
static inline bool var_can_have_subvars (tree);
|
||||
static inline bool overlap_subvar (HOST_WIDE_INT, HOST_WIDE_INT,
|
||||
static inline bool overlap_subvar (unsigned HOST_WIDE_INT,
|
||||
unsigned HOST_WIDE_INT,
|
||||
subvar_t, bool *);
|
||||
|
||||
/* Call-back function for walk_use_def_chains(). At each reaching
|
||||
|
@ -268,6 +268,8 @@ is_gimple_reg_type (tree type)
|
||||
bool
|
||||
is_gimple_reg (tree t)
|
||||
{
|
||||
var_ann_t ann;
|
||||
|
||||
if (TREE_CODE (t) == SSA_NAME)
|
||||
t = SSA_NAME_VAR (t);
|
||||
|
||||
@ -305,9 +307,16 @@ is_gimple_reg (tree t)
|
||||
if (TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE)
|
||||
return DECL_COMPLEX_GIMPLE_REG_P (t);
|
||||
|
||||
/* Some compiler temporaries are created to be used exclusively in
|
||||
virtual operands (currently memory tags and sub-variables).
|
||||
These variables should never be considered GIMPLE registers. */
|
||||
if (DECL_ARTIFICIAL (t) && (ann = var_ann (t)) != NULL)
|
||||
return ann->mem_tag_kind == NOT_A_TAG;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Returns true if T is a GIMPLE formal temporary variable. */
|
||||
|
||||
bool
|
||||
|
@ -276,8 +276,6 @@ extern struct tree_opt_pass pass_store_ccp;
|
||||
extern struct tree_opt_pass pass_store_copy_prop;
|
||||
extern struct tree_opt_pass pass_vrp;
|
||||
extern struct tree_opt_pass pass_create_structure_vars;
|
||||
extern struct tree_opt_pass pass_build_pta;
|
||||
extern struct tree_opt_pass pass_del_pta;
|
||||
extern struct tree_opt_pass pass_uncprop;
|
||||
extern struct tree_opt_pass pass_return_slot;
|
||||
extern struct tree_opt_pass pass_reassoc;
|
||||
|
@ -40,19 +40,12 @@ Boston, MA 02110-1301, USA. */
|
||||
#include "tree-flow.h"
|
||||
#include "tree-inline.h"
|
||||
#include "tree-pass.h"
|
||||
#include "tree-ssa-structalias.h"
|
||||
#include "convert.h"
|
||||
#include "params.h"
|
||||
#include "vec.h"
|
||||
#include "bitmap.h"
|
||||
|
||||
/* Keep track of how many times each pointer has been dereferenced in
|
||||
the program using the aux variable. This is used by the alias
|
||||
grouping heuristic in compute_flow_insensitive_aliasing. */
|
||||
#define NUM_REFERENCES(ANN) ((size_t)((ANN)->common.aux))
|
||||
#define NUM_REFERENCES_CLEAR(ANN) ((ANN)->common.aux) = 0
|
||||
#define NUM_REFERENCES_INC(ANN) (ANN)->common.aux = (void*) (((size_t)((ANN)->common.aux)) + 1)
|
||||
#define NUM_REFERENCES_SET(ANN, VAL) (ANN)->common.aux = (void*) ((void *)(VAL))
|
||||
|
||||
/* Obstack used to hold grouping bitmaps and other temporary bitmaps used by
|
||||
aliasing */
|
||||
static bitmap_obstack alias_obstack;
|
||||
@ -83,51 +76,6 @@ struct alias_map_d
|
||||
};
|
||||
|
||||
|
||||
/* Alias information used by compute_may_aliases and its helpers. */
|
||||
struct alias_info
|
||||
{
|
||||
/* SSA names visited while collecting points-to information. If bit I
|
||||
is set, it means that SSA variable with version I has already been
|
||||
visited. */
|
||||
sbitmap ssa_names_visited;
|
||||
|
||||
/* Array of SSA_NAME pointers processed by the points-to collector. */
|
||||
varray_type processed_ptrs;
|
||||
|
||||
/* Variables whose address is still needed. */
|
||||
bitmap addresses_needed;
|
||||
|
||||
/* ADDRESSABLE_VARS contains all the global variables and locals that
|
||||
have had their address taken. */
|
||||
struct alias_map_d **addressable_vars;
|
||||
size_t num_addressable_vars;
|
||||
|
||||
/* POINTERS contains all the _DECL pointers with unique memory tags
|
||||
that have been referenced in the program. */
|
||||
struct alias_map_d **pointers;
|
||||
size_t num_pointers;
|
||||
|
||||
/* Number of function calls found in the program. */
|
||||
size_t num_calls_found;
|
||||
|
||||
/* Number of const/pure function calls found in the program. */
|
||||
size_t num_pure_const_calls_found;
|
||||
|
||||
/* Total number of virtual operands that will be needed to represent
|
||||
all the aliases of all the pointers found in the program. */
|
||||
long total_alias_vops;
|
||||
|
||||
/* Variables that have been written to. */
|
||||
bitmap written_vars;
|
||||
|
||||
/* Pointers that have been used in an indirect store operation. */
|
||||
bitmap dereferenced_ptrs_store;
|
||||
|
||||
/* Pointers that have been used in an indirect load operation. */
|
||||
bitmap dereferenced_ptrs_load;
|
||||
};
|
||||
|
||||
|
||||
/* Counters used to display statistics on alias analysis. */
|
||||
struct alias_stats_d
|
||||
{
|
||||
@ -155,18 +103,12 @@ static void add_may_alias (tree, tree);
|
||||
static void replace_may_alias (tree, size_t, tree);
|
||||
static struct alias_info *init_alias_info (void);
|
||||
static void delete_alias_info (struct alias_info *);
|
||||
static void compute_points_to_and_addr_escape (struct alias_info *);
|
||||
static void compute_flow_sensitive_aliasing (struct alias_info *);
|
||||
static void setup_pointers_and_addressables (struct alias_info *);
|
||||
static bool collect_points_to_info_r (tree, tree, void *);
|
||||
static bool is_escape_site (tree, struct alias_info *);
|
||||
static void add_pointed_to_var (struct alias_info *, tree, tree);
|
||||
static void create_global_var (void);
|
||||
static void collect_points_to_info_for (struct alias_info *, tree);
|
||||
static void maybe_create_global_var (struct alias_info *ai);
|
||||
static void group_aliases (struct alias_info *);
|
||||
static void set_pt_anything (tree ptr);
|
||||
static void set_pt_malloc (tree ptr);
|
||||
|
||||
/* Global declarations. */
|
||||
|
||||
@ -315,7 +257,7 @@ compute_may_aliases (void)
|
||||
address of V escapes the current function, making V call-clobbered
|
||||
(i.e., whether &V is stored in a global variable or if its passed as a
|
||||
function call argument). */
|
||||
compute_points_to_and_addr_escape (ai);
|
||||
compute_points_to_sets (ai);
|
||||
|
||||
/* Collect all pointers and addressable variables, compute alias sets,
|
||||
create memory tags for pointers and promote variables whose address is
|
||||
@ -506,7 +448,6 @@ init_alias_info (void)
|
||||
ai->ssa_names_visited = sbitmap_alloc (num_ssa_names);
|
||||
sbitmap_zero (ai->ssa_names_visited);
|
||||
VARRAY_TREE_INIT (ai->processed_ptrs, 50, "processed_ptrs");
|
||||
ai->addresses_needed = BITMAP_ALLOC (&alias_obstack);
|
||||
ai->written_vars = BITMAP_ALLOC (&alias_obstack);
|
||||
ai->dereferenced_ptrs_store = BITMAP_ALLOC (&alias_obstack);
|
||||
ai->dereferenced_ptrs_load = BITMAP_ALLOC (&alias_obstack);
|
||||
@ -564,7 +505,6 @@ init_alias_info (void)
|
||||
superset of its former points-to set, then a new
|
||||
tag will need to be created in create_name_tags. */
|
||||
pi->pt_anything = 0;
|
||||
pi->pt_malloc = 0;
|
||||
pi->pt_null = 0;
|
||||
pi->value_escapes_p = 0;
|
||||
pi->is_dereferenced = 0;
|
||||
@ -592,7 +532,6 @@ delete_alias_info (struct alias_info *ai)
|
||||
|
||||
sbitmap_free (ai->ssa_names_visited);
|
||||
ai->processed_ptrs = NULL;
|
||||
BITMAP_FREE (ai->addresses_needed);
|
||||
|
||||
for (i = 0; i < ai->num_addressable_vars; i++)
|
||||
free (ai->addressable_vars[i]);
|
||||
@ -613,171 +552,9 @@ delete_alias_info (struct alias_info *ai)
|
||||
BITMAP_FREE (ai->dereferenced_ptrs_store);
|
||||
BITMAP_FREE (ai->dereferenced_ptrs_load);
|
||||
bitmap_obstack_release (&alias_obstack);
|
||||
|
||||
free (ai);
|
||||
}
|
||||
|
||||
|
||||
/* Walk use-def chains for pointer PTR to determine what variables is PTR
|
||||
pointing to. */
|
||||
|
||||
static void
|
||||
collect_points_to_info_for (struct alias_info *ai, tree ptr)
|
||||
{
|
||||
gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr)));
|
||||
|
||||
if (!TEST_BIT (ai->ssa_names_visited, SSA_NAME_VERSION (ptr)))
|
||||
{
|
||||
SET_BIT (ai->ssa_names_visited, SSA_NAME_VERSION (ptr));
|
||||
walk_use_def_chains (ptr, collect_points_to_info_r, ai, true);
|
||||
VARRAY_PUSH_TREE (ai->processed_ptrs, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Traverse use-def links for all the pointers in the program to collect
|
||||
address escape and points-to information.
|
||||
|
||||
This is loosely based on the same idea described in R. Hasti and S.
|
||||
Horwitz, ``Using static single assignment form to improve
|
||||
flow-insensitive pointer analysis,'' in SIGPLAN Conference on
|
||||
Programming Language Design and Implementation, pp. 97-105, 1998. */
|
||||
|
||||
static void
|
||||
compute_points_to_and_addr_escape (struct alias_info *ai)
|
||||
{
|
||||
basic_block bb;
|
||||
unsigned i;
|
||||
tree op;
|
||||
ssa_op_iter iter;
|
||||
|
||||
timevar_push (TV_TREE_PTA);
|
||||
|
||||
FOR_EACH_BB (bb)
|
||||
{
|
||||
block_stmt_iterator si;
|
||||
|
||||
for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
|
||||
{
|
||||
bitmap addr_taken;
|
||||
tree stmt = bsi_stmt (si);
|
||||
bool stmt_escapes_p = is_escape_site (stmt, ai);
|
||||
bitmap_iterator bi;
|
||||
|
||||
/* Mark all the variables whose address are taken by the
|
||||
statement. Note that this will miss all the addresses taken
|
||||
in PHI nodes (those are discovered while following the use-def
|
||||
chains). */
|
||||
addr_taken = addresses_taken (stmt);
|
||||
if (addr_taken)
|
||||
EXECUTE_IF_SET_IN_BITMAP (addr_taken, 0, i, bi)
|
||||
{
|
||||
tree var = referenced_var (i);
|
||||
bitmap_set_bit (ai->addresses_needed, DECL_UID (var));
|
||||
if (stmt_escapes_p)
|
||||
mark_call_clobbered (var);
|
||||
}
|
||||
|
||||
FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
|
||||
{
|
||||
tree var = SSA_NAME_VAR (op);
|
||||
var_ann_t v_ann = var_ann (var);
|
||||
struct ptr_info_def *pi;
|
||||
bool is_store;
|
||||
unsigned num_uses, num_derefs;
|
||||
|
||||
/* If the operand's variable may be aliased, keep track
|
||||
of how many times we've referenced it. This is used
|
||||
for alias grouping in compute_flow_sensitive_aliasing.
|
||||
Note that we don't need to grow AI->NUM_REFERENCES
|
||||
because we are processing regular variables, not
|
||||
memory tags (the array's initial size is set to
|
||||
NUM_REFERENCED_VARS). */
|
||||
if (may_be_aliased (var))
|
||||
NUM_REFERENCES_INC (v_ann);
|
||||
|
||||
if (!POINTER_TYPE_P (TREE_TYPE (op)))
|
||||
continue;
|
||||
|
||||
collect_points_to_info_for (ai, op);
|
||||
|
||||
pi = SSA_NAME_PTR_INFO (op);
|
||||
count_uses_and_derefs (op, stmt, &num_uses, &num_derefs,
|
||||
&is_store);
|
||||
|
||||
if (num_derefs > 0)
|
||||
{
|
||||
/* Mark OP as dereferenced. In a subsequent pass,
|
||||
dereferenced pointers that point to a set of
|
||||
variables will be assigned a name tag to alias
|
||||
all the variables OP points to. */
|
||||
pi->is_dereferenced = 1;
|
||||
|
||||
/* Keep track of how many time we've dereferenced each
|
||||
pointer. */
|
||||
NUM_REFERENCES_INC (v_ann);
|
||||
|
||||
/* If this is a store operation, mark OP as being
|
||||
dereferenced to store, otherwise mark it as being
|
||||
dereferenced to load. */
|
||||
if (is_store)
|
||||
bitmap_set_bit (ai->dereferenced_ptrs_store,
|
||||
DECL_UID (var));
|
||||
else
|
||||
bitmap_set_bit (ai->dereferenced_ptrs_load,
|
||||
DECL_UID (var));
|
||||
}
|
||||
|
||||
if (stmt_escapes_p && 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;
|
||||
|
||||
/* If the statement makes a function call, assume
|
||||
that pointer OP will be dereferenced in a store
|
||||
operation inside the called function. */
|
||||
if (get_call_expr_in (stmt))
|
||||
{
|
||||
bitmap_set_bit (ai->dereferenced_ptrs_store,
|
||||
DECL_UID (var));
|
||||
pi->is_dereferenced = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update reference counter for definitions to any
|
||||
potentially aliased variable. This is used in the alias
|
||||
grouping heuristics. */
|
||||
FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_DEF)
|
||||
{
|
||||
tree var = SSA_NAME_VAR (op);
|
||||
var_ann_t ann = var_ann (var);
|
||||
bitmap_set_bit (ai->written_vars, DECL_UID (var));
|
||||
if (may_be_aliased (var))
|
||||
NUM_REFERENCES_INC (ann);
|
||||
|
||||
if (POINTER_TYPE_P (TREE_TYPE (op)))
|
||||
collect_points_to_info_for (ai, op);
|
||||
}
|
||||
|
||||
/* Mark variables in V_MAY_DEF operands as being written to. */
|
||||
FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_VIRTUAL_DEFS)
|
||||
{
|
||||
tree var = DECL_P (op) ? op : SSA_NAME_VAR (op);
|
||||
bitmap_set_bit (ai->written_vars, DECL_UID (var));
|
||||
}
|
||||
|
||||
/* After promoting variables and computing aliasing we will
|
||||
need to re-scan most statements. FIXME: Try to minimize the
|
||||
number of statements re-scanned. It's not really necessary to
|
||||
re-scan *all* statements. */
|
||||
mark_stmt_modified (stmt);
|
||||
}
|
||||
}
|
||||
|
||||
timevar_pop (TV_TREE_PTA);
|
||||
delete_points_to_sets ();
|
||||
}
|
||||
|
||||
|
||||
@ -863,16 +640,10 @@ create_name_tags (void)
|
||||
if (old_name_tag && old_name_tag != pi->name_mem_tag)
|
||||
mark_sym_for_renaming (old_name_tag);
|
||||
}
|
||||
else if (pi->pt_malloc)
|
||||
{
|
||||
/* Otherwise, create a unique name tag for this pointer. */
|
||||
pi->name_mem_tag = get_nmt_for (ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Only pointers that may point to malloc or other variables
|
||||
may receive a name tag. If the pointer does not point to
|
||||
a known spot, we should use type tags. */
|
||||
/* If the pointer does not point to a known spot, we should
|
||||
use type tags. */
|
||||
set_pt_anything (ptr);
|
||||
continue;
|
||||
}
|
||||
@ -902,11 +673,8 @@ compute_flow_sensitive_aliasing (struct alias_info *ai)
|
||||
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);
|
||||
if (pi->pt_anything || pi->pt_vars == NULL)
|
||||
{
|
||||
find_what_p_points_to (ptr);
|
||||
}
|
||||
if (!find_what_p_points_to (ptr))
|
||||
set_pt_anything (ptr);
|
||||
}
|
||||
|
||||
create_name_tags ();
|
||||
@ -931,9 +699,7 @@ compute_flow_sensitive_aliasing (struct alias_info *ai)
|
||||
|
||||
if (pi->pt_vars)
|
||||
EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, j, bi)
|
||||
{
|
||||
mark_call_clobbered (referenced_var (j));
|
||||
}
|
||||
mark_call_clobbered (referenced_var (j));
|
||||
}
|
||||
|
||||
/* Set up aliasing information for PTR's name memory tag (if it has
|
||||
@ -1009,9 +775,9 @@ compute_flow_insensitive_aliasing (struct alias_info *ai)
|
||||
So we first check the call_clobbered status of the
|
||||
tag and variable before querying the bitmap. */
|
||||
tag_stored_p = is_call_clobbered (tag)
|
||||
|| bitmap_bit_p (ai->written_vars, DECL_UID (tag));
|
||||
|| bitmap_bit_p (ai->written_vars, DECL_UID (tag));
|
||||
var_stored_p = is_call_clobbered (var)
|
||||
|| bitmap_bit_p (ai->written_vars, DECL_UID (var));
|
||||
|| bitmap_bit_p (ai->written_vars, DECL_UID (var));
|
||||
if (!tag_stored_p && !var_stored_p)
|
||||
continue;
|
||||
|
||||
@ -1126,7 +892,7 @@ compute_flow_insensitive_aliasing (struct alias_info *ai)
|
||||
}
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "%s: Total number of aliased vops: %ld\n",
|
||||
fprintf (dump_file, "\n%s: Total number of aliased vops: %ld\n",
|
||||
get_name (current_function_decl),
|
||||
ai->total_alias_vops);
|
||||
|
||||
@ -1458,9 +1224,9 @@ setup_pointers_and_addressables (struct alias_info *ai)
|
||||
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) && v_ann->mem_tag_kind != STRUCT_FIELD)
|
||||
if (TREE_ADDRESSABLE (var))
|
||||
{
|
||||
if (!bitmap_bit_p (ai->addresses_needed, DECL_UID (var))
|
||||
if (!bitmap_bit_p (addressable_vars, DECL_UID (var))
|
||||
&& TREE_CODE (var) != RESULT_DECL
|
||||
&& !is_global_var (var))
|
||||
{
|
||||
@ -1470,6 +1236,9 @@ setup_pointers_and_addressables (struct alias_info *ai)
|
||||
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)))
|
||||
{
|
||||
@ -1477,8 +1246,7 @@ setup_pointers_and_addressables (struct alias_info *ai)
|
||||
|
||||
for (sv = svars; sv; sv = sv->next)
|
||||
{
|
||||
if (bitmap_bit_p (ai->addresses_needed,
|
||||
DECL_UID (sv->var)))
|
||||
if (bitmap_bit_p (addressable_vars, DECL_UID (sv->var)))
|
||||
okay_to_mark = false;
|
||||
mark_sym_for_renaming (sv->var);
|
||||
}
|
||||
@ -1490,22 +1258,6 @@ setup_pointers_and_addressables (struct alias_info *ai)
|
||||
if (okay_to_mark)
|
||||
mark_non_addressable (var);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add the variable to the set of addressables. Mostly
|
||||
used when scanning operands for ASM_EXPRs that
|
||||
clobber memory. In those cases, we need to clobber
|
||||
all call-clobbered variables and all addressables. */
|
||||
bitmap_set_bit (addressable_vars, DECL_UID (var));
|
||||
if (var_can_have_subvars (var)
|
||||
&& (svars = get_subvars_for_var (var)))
|
||||
{
|
||||
subvar_t sv;
|
||||
for (sv = svars; sv; sv = sv->next)
|
||||
bitmap_set_bit (addressable_vars, DECL_UID (sv->var));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Global variables and addressable locals may be aliased. Create an
|
||||
@ -1562,10 +1314,9 @@ setup_pointers_and_addressables (struct alias_info *ai)
|
||||
references of TAG. Since TAG can be associated with
|
||||
several pointers, add the dereferences of VAR to the
|
||||
TAG. */
|
||||
|
||||
NUM_REFERENCES_SET (t_ann,
|
||||
NUM_REFERENCES (t_ann) +
|
||||
NUM_REFERENCES (v_ann));
|
||||
NUM_REFERENCES (t_ann)
|
||||
+ NUM_REFERENCES (v_ann));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1783,8 +1534,16 @@ add_may_alias (tree var, tree alias)
|
||||
var_ann_t v_ann = get_var_ann (var);
|
||||
var_ann_t a_ann = get_var_ann (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
|
||||
|
||||
if (v_ann->may_aliases == NULL)
|
||||
VARRAY_TREE_INIT (v_ann->may_aliases, 2, "aliases");
|
||||
|
||||
@ -1836,7 +1595,7 @@ set_pt_anything (tree ptr)
|
||||
struct ptr_info_def *pi = get_ptr_info (ptr);
|
||||
|
||||
pi->pt_anything = 1;
|
||||
pi->pt_malloc = 0;
|
||||
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
|
||||
@ -1849,341 +1608,6 @@ set_pt_anything (tree ptr)
|
||||
}
|
||||
|
||||
|
||||
/* Mark pointer PTR as pointing to a malloc'd memory area. */
|
||||
|
||||
static void
|
||||
set_pt_malloc (tree ptr)
|
||||
{
|
||||
struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
|
||||
|
||||
/* If the pointer has already been found to point to arbitrary
|
||||
memory locations, it is unsafe to mark it as pointing to malloc. */
|
||||
if (pi->pt_anything)
|
||||
return;
|
||||
|
||||
pi->pt_malloc = 1;
|
||||
}
|
||||
|
||||
|
||||
/* Given two different pointers DEST and ORIG. Merge the points-to
|
||||
information in ORIG into DEST. AI contains all the alias
|
||||
information collected up to this point. */
|
||||
|
||||
static void
|
||||
merge_pointed_to_info (struct alias_info *ai, tree dest, tree orig)
|
||||
{
|
||||
struct ptr_info_def *dest_pi, *orig_pi;
|
||||
|
||||
gcc_assert (dest != orig);
|
||||
|
||||
/* Make sure we have points-to information for ORIG. */
|
||||
collect_points_to_info_for (ai, orig);
|
||||
|
||||
dest_pi = get_ptr_info (dest);
|
||||
orig_pi = SSA_NAME_PTR_INFO (orig);
|
||||
|
||||
if (orig_pi)
|
||||
{
|
||||
gcc_assert (orig_pi != dest_pi);
|
||||
|
||||
/* Notice that we never merge PT_MALLOC. This attribute is only
|
||||
true if the pointer is the result of a malloc() call.
|
||||
Otherwise, we can end up in this situation:
|
||||
|
||||
P_i = malloc ();
|
||||
...
|
||||
P_j = P_i + X;
|
||||
|
||||
P_j would be marked as PT_MALLOC, however we currently do not
|
||||
handle cases of more than one pointer pointing to the same
|
||||
malloc'd area.
|
||||
|
||||
FIXME: If the merging comes from an expression that preserves
|
||||
the PT_MALLOC attribute (copy assignment, address
|
||||
arithmetic), we ought to merge PT_MALLOC, but then both
|
||||
pointers would end up getting different name tags because
|
||||
create_name_tags is not smart enough to determine that the
|
||||
two come from the same malloc call. Copy propagation before
|
||||
aliasing should cure this. */
|
||||
dest_pi->pt_malloc = 0;
|
||||
if (orig_pi->pt_malloc || orig_pi->pt_anything)
|
||||
set_pt_anything (dest);
|
||||
|
||||
dest_pi->pt_null |= orig_pi->pt_null;
|
||||
|
||||
if (!dest_pi->pt_anything
|
||||
&& orig_pi->pt_vars
|
||||
&& !bitmap_empty_p (orig_pi->pt_vars))
|
||||
{
|
||||
if (dest_pi->pt_vars == NULL)
|
||||
{
|
||||
dest_pi->pt_vars = BITMAP_GGC_ALLOC ();
|
||||
bitmap_copy (dest_pi->pt_vars, orig_pi->pt_vars);
|
||||
}
|
||||
else
|
||||
bitmap_ior_into (dest_pi->pt_vars, orig_pi->pt_vars);
|
||||
}
|
||||
}
|
||||
else
|
||||
set_pt_anything (dest);
|
||||
}
|
||||
|
||||
|
||||
/* Add EXPR to the list of expressions pointed-to by PTR. */
|
||||
|
||||
static void
|
||||
add_pointed_to_expr (struct alias_info *ai, tree ptr, tree expr)
|
||||
{
|
||||
if (TREE_CODE (expr) == WITH_SIZE_EXPR)
|
||||
expr = TREE_OPERAND (expr, 0);
|
||||
|
||||
get_ptr_info (ptr);
|
||||
|
||||
if (TREE_CODE (expr) == CALL_EXPR
|
||||
&& (call_expr_flags (expr) & (ECF_MALLOC | ECF_MAY_BE_ALLOCA)))
|
||||
{
|
||||
/* If EXPR is a malloc-like call, then the area pointed to PTR
|
||||
is guaranteed to not alias with anything else. */
|
||||
set_pt_malloc (ptr);
|
||||
}
|
||||
else if (TREE_CODE (expr) == ADDR_EXPR)
|
||||
{
|
||||
/* Found P_i = ADDR_EXPR */
|
||||
add_pointed_to_var (ai, ptr, expr);
|
||||
}
|
||||
else if (TREE_CODE (expr) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (expr)))
|
||||
{
|
||||
/* Found P_i = Q_j. */
|
||||
merge_pointed_to_info (ai, ptr, expr);
|
||||
}
|
||||
else if (TREE_CODE (expr) == PLUS_EXPR || TREE_CODE (expr) == MINUS_EXPR)
|
||||
{
|
||||
/* Found P_i = PLUS_EXPR or P_i = MINUS_EXPR */
|
||||
tree op0 = TREE_OPERAND (expr, 0);
|
||||
tree op1 = TREE_OPERAND (expr, 1);
|
||||
|
||||
/* Both operands may be of pointer type. FIXME: Shouldn't
|
||||
we just expect PTR + OFFSET always? */
|
||||
if (POINTER_TYPE_P (TREE_TYPE (op0))
|
||||
&& TREE_CODE (op0) != INTEGER_CST)
|
||||
{
|
||||
if (TREE_CODE (op0) == SSA_NAME)
|
||||
merge_pointed_to_info (ai, ptr, op0);
|
||||
else if (TREE_CODE (op0) == ADDR_EXPR)
|
||||
add_pointed_to_var (ai, ptr, op0);
|
||||
else
|
||||
set_pt_anything (ptr);
|
||||
}
|
||||
|
||||
if (POINTER_TYPE_P (TREE_TYPE (op1))
|
||||
&& TREE_CODE (op1) != INTEGER_CST)
|
||||
{
|
||||
if (TREE_CODE (op1) == SSA_NAME)
|
||||
merge_pointed_to_info (ai, ptr, op1);
|
||||
else if (TREE_CODE (op1) == ADDR_EXPR)
|
||||
add_pointed_to_var (ai, ptr, op1);
|
||||
else
|
||||
set_pt_anything (ptr);
|
||||
}
|
||||
|
||||
/* Neither operand is a pointer? VAR can be pointing anywhere.
|
||||
FIXME: Shouldn't we asserting here? If we get here, we found
|
||||
PTR = INT_CST + INT_CST, which should not be a valid pointer
|
||||
expression. */
|
||||
if (!(POINTER_TYPE_P (TREE_TYPE (op0))
|
||||
&& TREE_CODE (op0) != INTEGER_CST)
|
||||
&& !(POINTER_TYPE_P (TREE_TYPE (op1))
|
||||
&& TREE_CODE (op1) != INTEGER_CST))
|
||||
set_pt_anything (ptr);
|
||||
}
|
||||
else if (integer_zerop (expr))
|
||||
{
|
||||
/* EXPR is the NULL pointer. Mark PTR as pointing to NULL. */
|
||||
SSA_NAME_PTR_INFO (ptr)->pt_null = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we can't recognize the expression, assume that PTR may
|
||||
point anywhere. */
|
||||
set_pt_anything (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* If VALUE is of the form &DECL, add DECL to the set of variables
|
||||
pointed-to by PTR. Otherwise, add VALUE as a pointed-to expression by
|
||||
PTR. AI points to the collected alias information. */
|
||||
|
||||
static void
|
||||
add_pointed_to_var (struct alias_info *ai, tree ptr, tree value)
|
||||
{
|
||||
struct ptr_info_def *pi = get_ptr_info (ptr);
|
||||
tree pt_var = NULL_TREE;
|
||||
HOST_WIDE_INT offset, size;
|
||||
tree addrop;
|
||||
size_t uid;
|
||||
tree ref;
|
||||
subvar_t svars;
|
||||
|
||||
gcc_assert (TREE_CODE (value) == ADDR_EXPR);
|
||||
|
||||
addrop = TREE_OPERAND (value, 0);
|
||||
if (REFERENCE_CLASS_P (addrop))
|
||||
pt_var = get_base_address (addrop);
|
||||
else
|
||||
pt_var = addrop;
|
||||
|
||||
/* If this is a component_ref, see if we can get a smaller number of
|
||||
variables to take the address of. */
|
||||
if (TREE_CODE (addrop) == COMPONENT_REF
|
||||
&& (ref = okay_component_ref_for_subvars (addrop, &offset ,&size)))
|
||||
{
|
||||
subvar_t sv;
|
||||
svars = get_subvars_for_var (ref);
|
||||
|
||||
uid = DECL_UID (pt_var);
|
||||
|
||||
if (pi->pt_vars == NULL)
|
||||
pi->pt_vars = BITMAP_GGC_ALLOC ();
|
||||
/* If the variable is a global, mark the pointer as pointing to
|
||||
global memory (which will make its tag a global variable). */
|
||||
if (is_global_var (pt_var))
|
||||
pi->pt_global_mem = 1;
|
||||
|
||||
for (sv = svars; sv; sv = sv->next)
|
||||
{
|
||||
if (overlap_subvar (offset, size, sv, NULL))
|
||||
{
|
||||
bitmap_set_bit (pi->pt_vars, DECL_UID (sv->var));
|
||||
bitmap_set_bit (ai->addresses_needed, DECL_UID (sv->var));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pt_var && SSA_VAR_P (pt_var))
|
||||
{
|
||||
|
||||
uid = DECL_UID (pt_var);
|
||||
|
||||
if (pi->pt_vars == NULL)
|
||||
pi->pt_vars = BITMAP_GGC_ALLOC ();
|
||||
|
||||
/* If this is an aggregate, we may have subvariables for it that need
|
||||
to be pointed to. */
|
||||
if (var_can_have_subvars (pt_var)
|
||||
&& (svars = get_subvars_for_var (pt_var)))
|
||||
{
|
||||
subvar_t sv;
|
||||
for (sv = svars; sv; sv = sv->next)
|
||||
{
|
||||
uid = DECL_UID (sv->var);
|
||||
bitmap_set_bit (ai->addresses_needed, uid);
|
||||
bitmap_set_bit (pi->pt_vars, uid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap_set_bit (ai->addresses_needed, uid);
|
||||
bitmap_set_bit (pi->pt_vars, uid);
|
||||
}
|
||||
|
||||
/* If the variable is a global, mark the pointer as pointing to
|
||||
global memory (which will make its tag a global variable). */
|
||||
if (is_global_var (pt_var))
|
||||
pi->pt_global_mem = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Callback for walk_use_def_chains to gather points-to information from the
|
||||
SSA web.
|
||||
|
||||
VAR is an SSA variable or a GIMPLE expression.
|
||||
|
||||
STMT is the statement that generates the SSA variable or, if STMT is a
|
||||
PHI_NODE, VAR is one of the PHI arguments.
|
||||
|
||||
DATA is a pointer to a structure of type ALIAS_INFO. */
|
||||
|
||||
static bool
|
||||
collect_points_to_info_r (tree var, tree stmt, void *data)
|
||||
{
|
||||
struct alias_info *ai = (struct alias_info *) data;
|
||||
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file, "Visiting use-def links for ");
|
||||
print_generic_expr (dump_file, var, dump_flags);
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
|
||||
switch (TREE_CODE (stmt))
|
||||
{
|
||||
case RETURN_EXPR:
|
||||
gcc_assert (TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR);
|
||||
stmt = TREE_OPERAND (stmt, 0);
|
||||
/* FALLTHRU */
|
||||
|
||||
case MODIFY_EXPR:
|
||||
{
|
||||
tree rhs = TREE_OPERAND (stmt, 1);
|
||||
STRIP_NOPS (rhs);
|
||||
add_pointed_to_expr (ai, var, rhs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ASM_EXPR:
|
||||
/* Pointers defined by __asm__ statements can point anywhere. */
|
||||
set_pt_anything (var);
|
||||
break;
|
||||
|
||||
case NOP_EXPR:
|
||||
if (IS_EMPTY_STMT (stmt))
|
||||
{
|
||||
tree decl = SSA_NAME_VAR (var);
|
||||
|
||||
if (TREE_CODE (decl) == PARM_DECL)
|
||||
add_pointed_to_expr (ai, var, decl);
|
||||
else if (DECL_INITIAL (decl))
|
||||
add_pointed_to_expr (ai, var, DECL_INITIAL (decl));
|
||||
else
|
||||
add_pointed_to_expr (ai, var, decl);
|
||||
}
|
||||
break;
|
||||
|
||||
case PHI_NODE:
|
||||
{
|
||||
/* It STMT is a PHI node, then VAR is one of its arguments. The
|
||||
variable that we are analyzing is the LHS of the PHI node. */
|
||||
tree lhs = PHI_RESULT (stmt);
|
||||
|
||||
switch (TREE_CODE (var))
|
||||
{
|
||||
case ADDR_EXPR:
|
||||
add_pointed_to_var (ai, lhs, var);
|
||||
break;
|
||||
|
||||
case SSA_NAME:
|
||||
/* Avoid unnecessary merges. */
|
||||
if (lhs != var)
|
||||
merge_pointed_to_info (ai, lhs, var);
|
||||
break;
|
||||
|
||||
default:
|
||||
gcc_assert (is_gimple_min_invariant (var));
|
||||
add_pointed_to_expr (ai, lhs, var);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* 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:
|
||||
@ -2195,7 +1619,7 @@ collect_points_to_info_r (tree var, tree stmt, void *data)
|
||||
|
||||
AI points to the alias information collected so far. */
|
||||
|
||||
static bool
|
||||
bool
|
||||
is_escape_site (tree stmt, struct alias_info *ai)
|
||||
{
|
||||
tree call = get_call_expr_in (stmt);
|
||||
@ -2271,9 +1695,7 @@ create_memory_tag (tree type, bool is_type_tag)
|
||||
determine whether they should be considered globals. */
|
||||
DECL_CONTEXT (tag) = current_function_decl;
|
||||
|
||||
/* Memory tags are by definition addressable. This also prevents
|
||||
is_gimple_ref frome confusing memory tags with optimizable
|
||||
variables. */
|
||||
/* Memory tags are by definition addressable. */
|
||||
TREE_ADDRESSABLE (tag) = 1;
|
||||
|
||||
ann = get_var_ann (tag);
|
||||
@ -2304,7 +1726,6 @@ get_nmt_for (tree ptr)
|
||||
/* 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_malloc
|
||||
|| pi->pt_global_mem)
|
||||
mark_call_clobbered (tag);
|
||||
|
||||
@ -2560,9 +1981,6 @@ dump_points_to_info_for (FILE *file, tree ptr)
|
||||
if (pi->pt_anything)
|
||||
fprintf (file, ", points-to anything");
|
||||
|
||||
if (pi->pt_malloc)
|
||||
fprintf (file, ", points-to malloc");
|
||||
|
||||
if (pi->pt_null)
|
||||
fprintf (file, ", points-to NULL");
|
||||
|
||||
@ -2978,6 +2396,33 @@ get_or_create_used_part_for (size_t uid)
|
||||
}
|
||||
|
||||
|
||||
/* Create and return a structure sub-variable for field FIELD of
|
||||
variable VAR. */
|
||||
|
||||
static tree
|
||||
create_sft (tree var, tree field)
|
||||
{
|
||||
var_ann_t ann;
|
||||
tree subvar = create_tmp_var_raw (TREE_TYPE (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);
|
||||
DECL_EXTERNAL (subvar) = DECL_EXTERNAL (var);
|
||||
TREE_PUBLIC (subvar) = TREE_PUBLIC (var);
|
||||
TREE_STATIC (subvar) = TREE_STATIC (var);
|
||||
TREE_READONLY (subvar) = TREE_READONLY (var);
|
||||
|
||||
/* Add the new variable to REFERENCED_VARS. */
|
||||
ann = get_var_ann (subvar);
|
||||
ann->mem_tag_kind = STRUCT_FIELD;
|
||||
ann->type_mem_tag = NULL;
|
||||
add_referenced_tmp_var (subvar);
|
||||
|
||||
return subvar;
|
||||
}
|
||||
|
||||
|
||||
/* Given an aggregate VAR, create the subvariables that represent its
|
||||
fields. */
|
||||
|
||||
@ -3067,7 +2512,6 @@ create_overlap_variables_for (tree var)
|
||||
{
|
||||
subvar_t sv;
|
||||
HOST_WIDE_INT fosize;
|
||||
var_ann_t ann;
|
||||
tree currfotype;
|
||||
|
||||
fosize = TREE_INT_CST_LOW (DECL_SIZE (fo->field));
|
||||
@ -3088,7 +2532,8 @@ create_overlap_variables_for (tree var)
|
||||
sv->offset = fo->offset;
|
||||
sv->size = fosize;
|
||||
sv->next = *subvars;
|
||||
sv->var = create_tmp_var_raw (TREE_TYPE (fo->field), "SFT");
|
||||
sv->var = create_sft (var, fo->field);
|
||||
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, "structure field tag %s created for var %s",
|
||||
@ -3100,25 +2545,6 @@ create_overlap_variables_for (tree var)
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
|
||||
/* We need to copy the various flags from var to sv->var, so that
|
||||
they are is_global_var iff the original variable was. */
|
||||
|
||||
DECL_EXTERNAL (sv->var) = DECL_EXTERNAL (var);
|
||||
TREE_PUBLIC (sv->var) = TREE_PUBLIC (var);
|
||||
TREE_STATIC (sv->var) = TREE_STATIC (var);
|
||||
TREE_READONLY (sv->var) = TREE_READONLY (var);
|
||||
|
||||
/* Like other memory tags, these need to be marked addressable to
|
||||
keep is_gimple_reg from thinking they are real. */
|
||||
TREE_ADDRESSABLE (sv->var) = 1;
|
||||
|
||||
DECL_CONTEXT (sv->var) = DECL_CONTEXT (var);
|
||||
|
||||
ann = get_var_ann (sv->var);
|
||||
ann->mem_tag_kind = STRUCT_FIELD;
|
||||
ann->type_mem_tag = NULL;
|
||||
add_referenced_tmp_var (sv->var);
|
||||
|
||||
lastfotype = currfotype;
|
||||
lastfooffset = fo->offset;
|
||||
lastfosize = fosize;
|
||||
|
@ -188,7 +188,9 @@ merge_alias_info (tree orig, tree new)
|
||||
#endif
|
||||
|
||||
/* Synchronize the type tags. If both pointers had a tag and they
|
||||
are different, then something has gone wrong. */
|
||||
are different, then something has gone wrong. Type tags can
|
||||
always be merged because they are flow insensitive, all the SSA
|
||||
names of the same base DECL share the same type tag. */
|
||||
if (new_ann->type_mem_tag == NULL_TREE)
|
||||
new_ann->type_mem_tag = orig_ann->type_mem_tag;
|
||||
else if (orig_ann->type_mem_tag == NULL_TREE)
|
||||
@ -196,32 +198,41 @@ merge_alias_info (tree orig, tree new)
|
||||
else
|
||||
gcc_assert (new_ann->type_mem_tag == orig_ann->type_mem_tag);
|
||||
|
||||
/* Synchronize the name tags. If NEW did not have a name tag, get
|
||||
it from ORIG. This happens when NEW is a compiler generated
|
||||
temporary which still hasn't had its points-to information filled
|
||||
in. */
|
||||
if (SSA_NAME_PTR_INFO (orig))
|
||||
/* Check that flow-sensitive information is compatible. Notice that
|
||||
we may not merge flow-sensitive information here. This function
|
||||
is called when propagating equivalences dictated by the IL, like
|
||||
a copy operation P_i = Q_j, and from equivalences dictated by
|
||||
control-flow, like if (P_i == Q_j).
|
||||
|
||||
In the former case, P_i and Q_j are equivalent in every block
|
||||
dominated by the assignment, so their flow-sensitive information
|
||||
is always the same. However, in the latter case, the pointers
|
||||
P_i and Q_j are only equivalent in one of the sub-graphs out of
|
||||
the predicate, so their flow-sensitive information is not the
|
||||
same in every block dominated by the predicate.
|
||||
|
||||
Since we cannot distinguish one case from another in this
|
||||
function, we can only make sure that if P_i and Q_j have
|
||||
flow-sensitive information, they should be compatible. */
|
||||
if (SSA_NAME_PTR_INFO (orig) && SSA_NAME_PTR_INFO (new))
|
||||
{
|
||||
struct ptr_info_def *orig_ptr_info = SSA_NAME_PTR_INFO (orig);
|
||||
struct ptr_info_def *new_ptr_info = SSA_NAME_PTR_INFO (new);
|
||||
|
||||
if (new_ptr_info == NULL)
|
||||
duplicate_ssa_name_ptr_info (new, orig_ptr_info);
|
||||
else if (orig_ptr_info->name_mem_tag
|
||||
&& new_ptr_info->name_mem_tag
|
||||
&& orig_ptr_info->pt_vars
|
||||
&& new_ptr_info->pt_vars)
|
||||
{
|
||||
/* Note that pointer NEW may actually have a different set
|
||||
of pointed-to variables. However, since NEW is being
|
||||
copy-propagated into ORIG, it must always be true that
|
||||
the pointed-to set for pointer NEW is the same, or a
|
||||
subset, of the pointed-to set for pointer ORIG. If this
|
||||
isn't the case, we shouldn't have been able to do the
|
||||
propagation of NEW into ORIG. */
|
||||
gcc_assert (bitmap_intersect_p (new_ptr_info->pt_vars,
|
||||
orig_ptr_info->pt_vars));
|
||||
}
|
||||
/* Note that pointer NEW and ORIG may actually have different
|
||||
pointed-to variables (e.g., PR 18291 represented in
|
||||
testsuite/gcc.c-torture/compile/pr18291.c). However, since
|
||||
NEW is being copy-propagated into ORIG, it must always be
|
||||
true that the pointed-to set for pointer NEW is the same, or
|
||||
a subset, of the pointed-to set for pointer ORIG. If this
|
||||
isn't the case, we shouldn't have been able to do the
|
||||
propagation of NEW into ORIG. */
|
||||
if (orig_ptr_info->name_mem_tag
|
||||
&& new_ptr_info->name_mem_tag
|
||||
&& orig_ptr_info->pt_vars
|
||||
&& new_ptr_info->pt_vars)
|
||||
gcc_assert (bitmap_intersect_p (new_ptr_info->pt_vars,
|
||||
orig_ptr_info->pt_vars));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,6 @@ Boston, MA 02110-1301, USA. */
|
||||
#include "ggc.h"
|
||||
#include "timevar.h"
|
||||
#include "toplev.h"
|
||||
|
||||
#include "langhooks.h"
|
||||
|
||||
/* This file contains the code required to manage the operands cache of the
|
||||
@ -148,7 +147,6 @@ static bool ops_active = false;
|
||||
static GTY (()) struct ssa_operand_memory_d *operand_memory = NULL;
|
||||
static unsigned operand_memory_index;
|
||||
|
||||
static void note_addressable (tree, stmt_ann_t);
|
||||
static void get_expr_operands (tree, tree *, int);
|
||||
static void get_asm_expr_operands (tree);
|
||||
static void get_indirect_ref_operands (tree, tree, int);
|
||||
@ -1310,7 +1308,7 @@ get_expr_operands (tree stmt, tree *expr_p, int flags)
|
||||
case IMAGPART_EXPR:
|
||||
{
|
||||
tree ref;
|
||||
HOST_WIDE_INT offset, size;
|
||||
unsigned HOST_WIDE_INT offset, size;
|
||||
/* This component ref becomes an access to all of the subvariables
|
||||
it can touch, if we can determine that, but *NOT* the real one.
|
||||
If we can't determine which fields we could touch, the recursion
|
||||
@ -1515,8 +1513,8 @@ get_asm_expr_operands (tree stmt)
|
||||
if (!allows_reg && allows_mem)
|
||||
{
|
||||
tree t = get_base_address (TREE_VALUE (link));
|
||||
if (t && DECL_P (t))
|
||||
note_addressable (t, s_ann);
|
||||
if (t && DECL_P (t) && s_ann)
|
||||
add_to_addressable_set (t, &s_ann->addresses_taken);
|
||||
}
|
||||
|
||||
get_expr_operands (stmt, &TREE_VALUE (link), opf_is_def);
|
||||
@ -1534,8 +1532,8 @@ get_asm_expr_operands (tree stmt)
|
||||
if (!allows_reg && allows_mem)
|
||||
{
|
||||
tree t = get_base_address (TREE_VALUE (link));
|
||||
if (t && DECL_P (t))
|
||||
note_addressable (t, s_ann);
|
||||
if (t && DECL_P (t) && s_ann)
|
||||
add_to_addressable_set (t, &s_ann->addresses_taken);
|
||||
}
|
||||
|
||||
get_expr_operands (stmt, &TREE_VALUE (link), 0);
|
||||
@ -1688,7 +1686,10 @@ get_tmr_operands (tree stmt, tree expr, int flags)
|
||||
flags &= ~opf_kill_def;
|
||||
|
||||
if (TMR_SYMBOL (expr))
|
||||
note_addressable (TMR_SYMBOL (expr), stmt_ann (stmt));
|
||||
{
|
||||
stmt_ann_t ann = stmt_ann (stmt);
|
||||
add_to_addressable_set (TMR_SYMBOL (expr), &ann->addresses_taken);
|
||||
}
|
||||
|
||||
if (tag)
|
||||
add_stmt_operand (&tag, stmt_ann (stmt), flags);
|
||||
@ -1757,9 +1758,9 @@ add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags)
|
||||
|
||||
/* If the operand is an ADDR_EXPR, add its operand to the list of
|
||||
variables that have had their address taken in this statement. */
|
||||
if (TREE_CODE (var) == ADDR_EXPR)
|
||||
if (TREE_CODE (var) == ADDR_EXPR && s_ann)
|
||||
{
|
||||
note_addressable (TREE_OPERAND (var, 0), s_ann);
|
||||
add_to_addressable_set (TREE_OPERAND (var, 0), &s_ann->addresses_taken);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1861,35 +1862,17 @@ add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags)
|
||||
|
||||
if (flags & opf_is_def)
|
||||
{
|
||||
bool added_may_defs_p = false;
|
||||
|
||||
/* If the variable is also an alias tag, add a virtual
|
||||
operand for it, otherwise we will miss representing
|
||||
references to the members of the variable's alias set.
|
||||
This fixes the bug in gcc.c-torture/execute/20020503-1.c. */
|
||||
if (v_ann->is_alias_tag)
|
||||
{
|
||||
added_may_defs_p = true;
|
||||
append_v_may_def (var);
|
||||
}
|
||||
append_v_may_def (var);
|
||||
|
||||
for (i = 0; i < VARRAY_ACTIVE_SIZE (aliases); i++)
|
||||
{
|
||||
/* While VAR may be modifiable, some of its aliases
|
||||
may not be. If that's the case, we don't really
|
||||
need to add them a V_MAY_DEF for them. */
|
||||
tree alias = VARRAY_TREE (aliases, i);
|
||||
append_v_may_def (VARRAY_TREE (aliases, i));
|
||||
|
||||
if (unmodifiable_var_p (alias))
|
||||
append_vuse (alias);
|
||||
else
|
||||
{
|
||||
append_v_may_def (alias);
|
||||
added_may_defs_p = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_ann && added_may_defs_p)
|
||||
if (s_ann)
|
||||
s_ann->makes_aliased_stores = 1;
|
||||
}
|
||||
else
|
||||
@ -1910,40 +1893,51 @@ add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags)
|
||||
}
|
||||
|
||||
|
||||
/* Record that VAR had its address taken in the statement with annotations
|
||||
S_ANN. */
|
||||
/* Add the base address of REF to the set *ADDRESSES_TAKEN. If
|
||||
*ADDRESSES_TAKEN is NULL, a new set is created. REF may be
|
||||
a single variable whose address has been taken or any other valid
|
||||
GIMPLE memory reference (structure reference, array, etc). If the
|
||||
base address of REF is a decl that has sub-variables, also add all
|
||||
of its sub-variables. */
|
||||
|
||||
static void
|
||||
note_addressable (tree var, stmt_ann_t s_ann)
|
||||
void
|
||||
add_to_addressable_set (tree ref, bitmap *addresses_taken)
|
||||
{
|
||||
tree var;
|
||||
subvar_t svars;
|
||||
|
||||
if (!s_ann)
|
||||
return;
|
||||
|
||||
gcc_assert (addresses_taken);
|
||||
|
||||
/* Note that it is *NOT OKAY* to use the target of a COMPONENT_REF
|
||||
as the only thing we take the address of.
|
||||
See PR 21407 and the ensuing mailing list discussion. */
|
||||
|
||||
var = get_base_address (var);
|
||||
as the only thing we take the address of. If VAR is a structure,
|
||||
taking the address of a field means that the whole structure may
|
||||
be referenced using pointer arithmetic. See PR 21407 and the
|
||||
ensuing mailing list discussion. */
|
||||
var = get_base_address (ref);
|
||||
if (var && SSA_VAR_P (var))
|
||||
{
|
||||
if (s_ann->addresses_taken == NULL)
|
||||
s_ann->addresses_taken = BITMAP_GGC_ALLOC ();
|
||||
if (*addresses_taken == NULL)
|
||||
*addresses_taken = BITMAP_GGC_ALLOC ();
|
||||
|
||||
|
||||
if (var_can_have_subvars (var)
|
||||
&& (svars = get_subvars_for_var (var)))
|
||||
{
|
||||
subvar_t sv;
|
||||
for (sv = svars; sv; sv = sv->next)
|
||||
bitmap_set_bit (s_ann->addresses_taken, DECL_UID (sv->var));
|
||||
{
|
||||
bitmap_set_bit (*addresses_taken, DECL_UID (sv->var));
|
||||
TREE_ADDRESSABLE (sv->var) = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
bitmap_set_bit (s_ann->addresses_taken, DECL_UID (var));
|
||||
{
|
||||
bitmap_set_bit (*addresses_taken, DECL_UID (var));
|
||||
TREE_ADDRESSABLE (var) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Add clobbering definitions for .GLOBAL_VAR or for each of the call
|
||||
clobbered variables in the function. */
|
||||
|
||||
|
@ -170,6 +170,8 @@ extern bool ssa_ro_call_cache_valid;
|
||||
|
||||
extern bool ssa_operands_active (void);
|
||||
|
||||
extern void add_to_addressable_set (tree, bitmap *);
|
||||
|
||||
enum ssa_op_iter_type {
|
||||
ssa_op_iter_none = 0,
|
||||
ssa_op_iter_tree,
|
||||
|
@ -26,7 +26,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include "ggc.h"
|
||||
#include "obstack.h"
|
||||
#include "bitmap.h"
|
||||
#include "tree-ssa-structalias.h"
|
||||
#include "flags.h"
|
||||
#include "rtl.h"
|
||||
#include "tm_p.h"
|
||||
@ -49,6 +48,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include "timevar.h"
|
||||
#include "alloc-pool.h"
|
||||
#include "splay-tree.h"
|
||||
#include "tree-ssa-structalias.h"
|
||||
|
||||
/* The idea behind this analyzer is to generate set constraints from the
|
||||
program, then solve the resulting constraints in order to generate the
|
||||
@ -223,6 +223,9 @@ struct variable_info
|
||||
/* True for variables that have unions somewhere in them. */
|
||||
unsigned int has_union:1;
|
||||
|
||||
/* True if this is a heap variable. */
|
||||
unsigned int is_heap_var:1;
|
||||
|
||||
/* Points-to set for this variable. */
|
||||
bitmap solution;
|
||||
|
||||
@ -270,6 +273,12 @@ static varinfo_t var_integer;
|
||||
static tree integer_tree;
|
||||
static unsigned int integer_id;
|
||||
|
||||
/* Variable that represents arbitrary offsets into an object. Used to
|
||||
represent pointer arithmetic, which may not legally escape the
|
||||
bounds of an object. */
|
||||
static varinfo_t var_anyoffset;
|
||||
static tree anyoffset_tree;
|
||||
static unsigned int anyoffset_id;
|
||||
|
||||
/* Return a new variable info structure consisting for a variable
|
||||
named NAME, and using constraint graph node NODE. */
|
||||
@ -286,6 +295,7 @@ new_var_info (tree t, unsigned int id, const char *name, unsigned int node)
|
||||
ret->address_taken = false;
|
||||
ret->indirect_target = false;
|
||||
ret->is_artificial_var = false;
|
||||
ret->is_heap_var = false;
|
||||
ret->is_unknown_size_var = false;
|
||||
ret->solution = BITMAP_ALLOC (&ptabitmap_obstack);
|
||||
bitmap_clear (ret->solution);
|
||||
@ -941,8 +951,11 @@ build_constraint_graph (void)
|
||||
constraint_t c;
|
||||
|
||||
graph = ggc_alloc (sizeof (struct constraint_graph));
|
||||
graph->succs = ggc_alloc_cleared (VEC_length (varinfo_t, varmap) * sizeof (*graph->succs));
|
||||
graph->preds = ggc_alloc_cleared (VEC_length (varinfo_t, varmap) * sizeof (*graph->preds));
|
||||
graph->succs = ggc_alloc_cleared (VEC_length (varinfo_t, varmap)
|
||||
* sizeof (*graph->succs));
|
||||
graph->preds = ggc_alloc_cleared (VEC_length (varinfo_t, varmap)
|
||||
* sizeof (*graph->preds));
|
||||
|
||||
for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
|
||||
{
|
||||
struct constraint_expr lhs = c->lhs;
|
||||
@ -983,6 +996,8 @@ build_constraint_graph (void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Changed variables on the last iteration. */
|
||||
static unsigned int changed_count;
|
||||
static sbitmap changed;
|
||||
@ -2137,11 +2152,18 @@ get_constraint_for (tree t)
|
||||
&ANYTHING added. */
|
||||
if (call_expr_flags (t) & (ECF_MALLOC | ECF_MAY_BE_ALLOCA))
|
||||
{
|
||||
tree heapvar = create_tmp_var_raw (ptr_type_node, "HEAP");
|
||||
varinfo_t vi;
|
||||
tree heapvar;
|
||||
|
||||
heapvar = create_tmp_var_raw (ptr_type_node, "HEAP");
|
||||
DECL_EXTERNAL (heapvar) = 1;
|
||||
add_referenced_tmp_var (heapvar);
|
||||
temp.var = create_variable_info_for (heapvar,
|
||||
alias_get_name (heapvar));
|
||||
|
||||
get_varinfo (temp.var)->is_artificial_var = 1;
|
||||
vi = get_varinfo (temp.var);
|
||||
vi->is_artificial_var = 1;
|
||||
vi->is_heap_var = 1;
|
||||
temp.type = ADDRESSOF;
|
||||
temp.offset = 0;
|
||||
return temp;
|
||||
@ -2191,9 +2213,11 @@ get_constraint_for (tree t)
|
||||
|
||||
/* Cast from non-pointer to pointers are bad news for us.
|
||||
Anything else, we see through */
|
||||
if (!(POINTER_TYPE_P (TREE_TYPE (t)) &&
|
||||
! POINTER_TYPE_P (TREE_TYPE (op))))
|
||||
if (!(POINTER_TYPE_P (TREE_TYPE (t))
|
||||
&& ! POINTER_TYPE_P (TREE_TYPE (op))))
|
||||
return get_constraint_for (op);
|
||||
|
||||
/* FALLTHRU */
|
||||
}
|
||||
default:
|
||||
{
|
||||
@ -2467,81 +2491,300 @@ ref_contains_indirect_ref (tree ref)
|
||||
}
|
||||
|
||||
|
||||
/* Tree walker that is the heart of the aliasing infrastructure.
|
||||
TP is a pointer to the current tree.
|
||||
WALK_SUBTREES specifies whether to continue traversing subtrees or
|
||||
not.
|
||||
Returns NULL_TREE when we should stop.
|
||||
|
||||
This function is the main part of the constraint builder. It
|
||||
walks the trees, calling the appropriate building functions
|
||||
to process various statements. */
|
||||
/* Update related alias information kept in AI. This is used when
|
||||
building name tags, alias sets and deciding grouping heuristics.
|
||||
STMT is the statement to process. This function also updates
|
||||
ADDRESSABLE_VARS. */
|
||||
|
||||
static void
|
||||
find_func_aliases (tree t)
|
||||
update_alias_info (tree stmt, struct alias_info *ai)
|
||||
{
|
||||
bitmap addr_taken;
|
||||
use_operand_p use_p;
|
||||
def_operand_p def_p;
|
||||
ssa_op_iter iter;
|
||||
bool stmt_escapes_p = is_escape_site (stmt, ai);
|
||||
|
||||
/* Mark all the variables whose address are taken by the statement. */
|
||||
addr_taken = addresses_taken (stmt);
|
||||
if (addr_taken)
|
||||
{
|
||||
bitmap_ior_into (addressable_vars, addr_taken);
|
||||
|
||||
/* If STMT is an escape point, all the addresses taken by it are
|
||||
call-clobbered. */
|
||||
if (stmt_escapes_p)
|
||||
{
|
||||
bitmap_iterator bi;
|
||||
unsigned i;
|
||||
|
||||
EXECUTE_IF_SET_IN_BITMAP (addr_taken, 0, i, bi)
|
||||
mark_call_clobbered (referenced_var (i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Process each operand use. If an operand may be aliased, keep
|
||||
track of how many times it's being used. For pointers, determine
|
||||
whether they are dereferenced by the statement, or whether their
|
||||
value escapes, etc. */
|
||||
FOR_EACH_PHI_OR_STMT_USE (use_p, stmt, iter, SSA_OP_USE)
|
||||
{
|
||||
tree op, var;
|
||||
var_ann_t v_ann;
|
||||
struct ptr_info_def *pi;
|
||||
bool is_store;
|
||||
unsigned num_uses, num_derefs;
|
||||
|
||||
op = USE_FROM_PTR (use_p);
|
||||
|
||||
/* If STMT is a PHI node, OP may be an ADDR_EXPR. If so, add it
|
||||
to the set of addressable variables. */
|
||||
if (TREE_CODE (op) == ADDR_EXPR)
|
||||
{
|
||||
gcc_assert (TREE_CODE (stmt) == PHI_NODE);
|
||||
|
||||
/* PHI nodes don't have annotations for pinning the set
|
||||
of addresses taken, so we collect them here.
|
||||
|
||||
FIXME, should we allow PHI nodes to have annotations
|
||||
so that they can be treated like regular statements?
|
||||
Currently, they are treated as second-class
|
||||
statements. */
|
||||
add_to_addressable_set (TREE_OPERAND (op, 0), &addressable_vars);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ignore constants. */
|
||||
if (TREE_CODE (op) != SSA_NAME)
|
||||
continue;
|
||||
|
||||
var = SSA_NAME_VAR (op);
|
||||
v_ann = var_ann (var);
|
||||
|
||||
/* If the operand's variable may be aliased, keep track of how
|
||||
many times we've referenced it. This is used for alias
|
||||
grouping in compute_flow_insensitive_aliasing. */
|
||||
if (may_be_aliased (var))
|
||||
NUM_REFERENCES_INC (v_ann);
|
||||
|
||||
/* We are only interested in pointers. */
|
||||
if (!POINTER_TYPE_P (TREE_TYPE (op)))
|
||||
continue;
|
||||
|
||||
pi = get_ptr_info (op);
|
||||
|
||||
/* Add OP to AI->PROCESSED_PTRS, if it's not there already. */
|
||||
if (!TEST_BIT (ai->ssa_names_visited, SSA_NAME_VERSION (op)))
|
||||
{
|
||||
SET_BIT (ai->ssa_names_visited, SSA_NAME_VERSION (op));
|
||||
VARRAY_PUSH_TREE (ai->processed_ptrs, op);
|
||||
}
|
||||
|
||||
/* If STMT is a PHI node, then it will not have pointer
|
||||
dereferences and it will not be an escape point. */
|
||||
if (TREE_CODE (stmt) == PHI_NODE)
|
||||
continue;
|
||||
|
||||
/* Determine whether OP is a dereferenced pointer, and if STMT
|
||||
is an escape point, whether OP escapes. */
|
||||
count_uses_and_derefs (op, stmt, &num_uses, &num_derefs, &is_store);
|
||||
|
||||
if (num_derefs > 0)
|
||||
{
|
||||
/* Mark OP as dereferenced. In a subsequent pass,
|
||||
dereferenced pointers that point to a set of
|
||||
variables will be assigned a name tag to alias
|
||||
all the variables OP points to. */
|
||||
pi->is_dereferenced = 1;
|
||||
|
||||
/* Keep track of how many time we've dereferenced each
|
||||
pointer. */
|
||||
NUM_REFERENCES_INC (v_ann);
|
||||
|
||||
/* If this is a store operation, mark OP as being
|
||||
dereferenced to store, otherwise mark it as being
|
||||
dereferenced to load. */
|
||||
if (is_store)
|
||||
bitmap_set_bit (ai->dereferenced_ptrs_store, DECL_UID (var));
|
||||
else
|
||||
bitmap_set_bit (ai->dereferenced_ptrs_load, DECL_UID (var));
|
||||
}
|
||||
|
||||
if (stmt_escapes_p && 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;
|
||||
|
||||
/* If the statement makes a function call, assume
|
||||
that pointer OP will be dereferenced in a store
|
||||
operation inside the called function. */
|
||||
if (get_call_expr_in (stmt))
|
||||
{
|
||||
bitmap_set_bit (ai->dereferenced_ptrs_store, DECL_UID (var));
|
||||
pi->is_dereferenced = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update reference counter for definitions to any potentially
|
||||
aliased variable. This is used in the alias grouping heuristics. */
|
||||
FOR_EACH_PHI_OR_STMT_DEF (def_p, stmt, iter, SSA_OP_ALL_DEFS)
|
||||
{
|
||||
tree op = DEF_FROM_PTR (def_p);
|
||||
tree var = SSA_NAME_VAR (op);
|
||||
var_ann_t ann = var_ann (var);
|
||||
bitmap_set_bit (ai->written_vars, DECL_UID (var));
|
||||
if (may_be_aliased (var))
|
||||
NUM_REFERENCES_INC (ann);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Handle pointer arithmetic EXPR when creating aliasing constraints.
|
||||
Expressions of the type PTR + CST can be handled in two ways:
|
||||
|
||||
1- If the constraint for PTR is ADDRESSOF for a non-structure
|
||||
variable, then we can use it directly because adding or
|
||||
subtracting a constant may not alter the original ADDRESSOF
|
||||
constraing (i.e., pointer arithmetic may not legally go outside
|
||||
an object's boundaries).
|
||||
|
||||
2- If the constraint for PTR is ADDRESSOF for a structure variable,
|
||||
then if CST is a compile-time constant that can be used as an
|
||||
offset, we can determine which sub-variable will be pointed-to
|
||||
by the expression.
|
||||
|
||||
Return true if the expression is handled. For any other kind of
|
||||
expression, return false so that each operand can be added as a
|
||||
separate constraint by the caller. */
|
||||
|
||||
static bool
|
||||
handle_ptr_arith (struct constraint_expr lhs, tree expr)
|
||||
{
|
||||
tree op0, op1;
|
||||
struct constraint_expr base, offset;
|
||||
|
||||
if (TREE_CODE (expr) != PLUS_EXPR)
|
||||
return false;
|
||||
|
||||
op0 = TREE_OPERAND (expr, 0);
|
||||
op1 = TREE_OPERAND (expr, 1);
|
||||
|
||||
base = get_constraint_for (op0);
|
||||
|
||||
offset.var = anyoffset_id;
|
||||
offset.type = ADDRESSOF;
|
||||
offset.offset = 0;
|
||||
|
||||
process_constraint (new_constraint (lhs, base));
|
||||
process_constraint (new_constraint (lhs, offset));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Walk statement T setting up aliasing constraints according to the
|
||||
references found in T. This function is the main part of the
|
||||
constraint builder. AI points to auxiliary alias information used
|
||||
when building alias sets and computing alias grouping heuristics. */
|
||||
|
||||
static void
|
||||
find_func_aliases (tree t, struct alias_info *ai)
|
||||
{
|
||||
struct constraint_expr lhs, rhs;
|
||||
switch (TREE_CODE (t))
|
||||
{
|
||||
case PHI_NODE:
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Only care about pointers and structures containing
|
||||
pointers. */
|
||||
if (POINTER_TYPE_P (TREE_TYPE (PHI_RESULT (t)))
|
||||
|| AGGREGATE_TYPE_P (TREE_TYPE (PHI_RESULT (t))))
|
||||
{
|
||||
lhs = get_constraint_for (PHI_RESULT (t));
|
||||
for (i = 0; i < PHI_NUM_ARGS (t); i++)
|
||||
{
|
||||
rhs = get_constraint_for (PHI_ARG_DEF (t, i));
|
||||
process_constraint (new_constraint (lhs, rhs));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* Update various related attributes like escaped addresses, pointer
|
||||
dereferences for loads and stores. This is used when creating
|
||||
name tags and alias sets. */
|
||||
update_alias_info (t, ai);
|
||||
|
||||
case MODIFY_EXPR:
|
||||
{
|
||||
tree lhsop = TREE_OPERAND (t, 0);
|
||||
tree rhsop = TREE_OPERAND (t, 1);
|
||||
int i;
|
||||
/* Now build constraints expressions. */
|
||||
if (TREE_CODE (t) == PHI_NODE)
|
||||
{
|
||||
/* Only care about pointers and structures containing
|
||||
pointers. */
|
||||
if (POINTER_TYPE_P (TREE_TYPE (PHI_RESULT (t)))
|
||||
|| AGGREGATE_TYPE_P (TREE_TYPE (PHI_RESULT (t))))
|
||||
{
|
||||
int i;
|
||||
|
||||
if (AGGREGATE_TYPE_P (TREE_TYPE (lhsop))
|
||||
&& AGGREGATE_TYPE_P (TREE_TYPE (rhsop)))
|
||||
{
|
||||
do_structure_copy (lhsop, rhsop);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Only care about operations with pointers, structures
|
||||
containing pointers, dereferences, and call
|
||||
expressions. */
|
||||
if (POINTER_TYPE_P (TREE_TYPE (lhsop))
|
||||
|| AGGREGATE_TYPE_P (TREE_TYPE (lhsop))
|
||||
|| ref_contains_indirect_ref (lhsop)
|
||||
|| TREE_CODE (rhsop) == CALL_EXPR)
|
||||
{
|
||||
lhs = get_constraint_for (lhsop);
|
||||
switch (TREE_CODE_CLASS (TREE_CODE (rhsop)))
|
||||
{
|
||||
/* RHS that consist of unary operations,
|
||||
exceptional types, or bare decls/constants, get
|
||||
handled directly by get_constraint_for. */
|
||||
lhs = get_constraint_for (PHI_RESULT (t));
|
||||
for (i = 0; i < PHI_NUM_ARGS (t); i++)
|
||||
{
|
||||
rhs = get_constraint_for (PHI_ARG_DEF (t, i));
|
||||
process_constraint (new_constraint (lhs, rhs));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (TREE_CODE (t) == MODIFY_EXPR)
|
||||
{
|
||||
tree lhsop = TREE_OPERAND (t, 0);
|
||||
tree rhsop = TREE_OPERAND (t, 1);
|
||||
int i;
|
||||
|
||||
if (AGGREGATE_TYPE_P (TREE_TYPE (lhsop))
|
||||
&& AGGREGATE_TYPE_P (TREE_TYPE (rhsop)))
|
||||
{
|
||||
do_structure_copy (lhsop, rhsop);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Only care about operations with pointers, structures
|
||||
containing pointers, dereferences, and call expressions. */
|
||||
if (POINTER_TYPE_P (TREE_TYPE (lhsop))
|
||||
|| AGGREGATE_TYPE_P (TREE_TYPE (lhsop))
|
||||
|| ref_contains_indirect_ref (lhsop)
|
||||
|| TREE_CODE (rhsop) == CALL_EXPR)
|
||||
{
|
||||
lhs = get_constraint_for (lhsop);
|
||||
switch (TREE_CODE_CLASS (TREE_CODE (rhsop)))
|
||||
{
|
||||
/* RHS that consist of unary operations,
|
||||
exceptional types, or bare decls/constants, get
|
||||
handled directly by get_constraint_for. */
|
||||
case tcc_reference:
|
||||
case tcc_declaration:
|
||||
case tcc_constant:
|
||||
case tcc_exceptional:
|
||||
case tcc_expression:
|
||||
case tcc_unary:
|
||||
{
|
||||
rhs = get_constraint_for (rhsop);
|
||||
process_constraint (new_constraint (lhs, rhs));
|
||||
}
|
||||
{
|
||||
rhs = get_constraint_for (rhsop);
|
||||
process_constraint (new_constraint (lhs, rhs));
|
||||
|
||||
/* When taking the address of an aggregate
|
||||
type, from the LHS we can access any field
|
||||
of the RHS. */
|
||||
if (rhs.type == ADDRESSOF
|
||||
&& rhs.var > anything_id
|
||||
&& AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (rhsop))))
|
||||
{
|
||||
rhs.var = anyoffset_id;
|
||||
rhs.type = ADDRESSOF;
|
||||
rhs.offset = 0;
|
||||
process_constraint (new_constraint (lhs, rhs));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Otherwise, walk each operand. */
|
||||
case tcc_binary:
|
||||
{
|
||||
/* For pointer arithmetic of the form
|
||||
PTR + CST, we can simply use PTR's
|
||||
constraint because pointer arithmetic is
|
||||
not allowed to go out of bounds. */
|
||||
if (handle_ptr_arith (lhs, rhsop))
|
||||
break;
|
||||
}
|
||||
/* FALLTHRU */
|
||||
|
||||
/* Otherwise, walk each operand. Notice that we
|
||||
can't use the operand interface because we need
|
||||
to process expressions other than simple operands
|
||||
(e.g. INDIRECT_REF, ADDR_EXPR, CALL_EXPR). */
|
||||
default:
|
||||
for (i = 0; i < TREE_CODE_LENGTH (TREE_CODE (rhsop)); i++)
|
||||
{
|
||||
@ -2549,15 +2792,16 @@ find_func_aliases (tree t)
|
||||
rhs = get_constraint_for (op);
|
||||
process_constraint (new_constraint (lhs, rhs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* After promoting variables and computing aliasing we will
|
||||
need to re-scan most statements. FIXME: Try to minimize the
|
||||
number of statements re-scanned. It's not really necessary to
|
||||
re-scan *all* statements. */
|
||||
mark_stmt_modified (t);
|
||||
}
|
||||
|
||||
|
||||
@ -2718,12 +2962,12 @@ create_variable_info_for (tree decl, const char *name)
|
||||
tree decltype = TREE_TYPE (decl);
|
||||
bool notokay = false;
|
||||
bool hasunion;
|
||||
subvar_t svars;
|
||||
bool is_global = DECL_P (decl) ? is_global_var (decl) : false;
|
||||
VEC (fieldoff_s,heap) *fieldstack = NULL;
|
||||
|
||||
|
||||
hasunion = TREE_CODE (decltype) == UNION_TYPE || TREE_CODE (decltype) == QUAL_UNION_TYPE;
|
||||
hasunion = TREE_CODE (decltype) == UNION_TYPE
|
||||
|| TREE_CODE (decltype) == QUAL_UNION_TYPE;
|
||||
if (var_can_have_subvars (decl) && use_field_sensitive && !hasunion)
|
||||
{
|
||||
push_fields_onto_fieldstack (decltype, &fieldstack, 0, &hasunion);
|
||||
@ -2733,62 +2977,6 @@ create_variable_info_for (tree decl, const char *name)
|
||||
notokay = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this variable already has subvars, just create the variables for the
|
||||
subvars and we are done.
|
||||
NOTE: This assumes things haven't generated uses of previously
|
||||
unused structure fields. */
|
||||
if (use_field_sensitive
|
||||
&& !notokay
|
||||
&& var_can_have_subvars (decl)
|
||||
&& var_ann (decl)
|
||||
&& (svars = get_subvars_for_var (decl)))
|
||||
{
|
||||
subvar_t sv;
|
||||
varinfo_t base = NULL;
|
||||
unsigned int firstindex = index;
|
||||
|
||||
for (sv = svars; sv; sv = sv->next)
|
||||
{
|
||||
/* For debugging purposes, this will print the names of the
|
||||
fields as "<var>.<offset>.<size>"
|
||||
This is only for debugging purposes. */
|
||||
#define PRINT_LONG_NAMES
|
||||
#ifdef PRINT_LONG_NAMES
|
||||
char *tempname;
|
||||
const char *newname;
|
||||
|
||||
asprintf (&tempname,
|
||||
"%s." HOST_WIDE_INT_PRINT_DEC "." HOST_WIDE_INT_PRINT_DEC,
|
||||
alias_get_name (decl), sv->offset, sv->size);
|
||||
newname = ggc_strdup (tempname);
|
||||
free (tempname);
|
||||
vi = new_var_info (sv->var, index, newname, index);
|
||||
#else
|
||||
vi = new_var_info (sv->var, index, alias_get_name (sv->var), index);
|
||||
#endif
|
||||
vi->decl = sv->var;
|
||||
vi->fullsize = TREE_INT_CST_LOW (TYPE_SIZE (decltype));
|
||||
vi->size = sv->size;
|
||||
vi->offset = sv->offset;
|
||||
if (!base)
|
||||
{
|
||||
base = vi;
|
||||
insert_id_for_tree (decl, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
insert_into_field_list (base, vi);
|
||||
}
|
||||
insert_id_for_tree (sv->var, index);
|
||||
VEC_safe_push (varinfo_t, gc, varmap, vi);
|
||||
if (is_global)
|
||||
make_constraint_to_anything (vi);
|
||||
index++;
|
||||
|
||||
}
|
||||
return firstindex;
|
||||
}
|
||||
|
||||
|
||||
/* If the variable doesn't have subvars, we may end up needing to
|
||||
@ -2935,7 +3123,6 @@ intra_create_variable_infos (void)
|
||||
lhs.type = SCALAR;
|
||||
lhs.var = create_variable_info_for (t, alias_get_name (t));
|
||||
|
||||
get_varinfo (lhs.var)->is_artificial_var = true;
|
||||
rhs.var = anything_id;
|
||||
rhs.type = ADDRESSOF;
|
||||
rhs.offset = 0;
|
||||
@ -2958,30 +3145,67 @@ set_uids_in_ptset (bitmap into, bitmap from)
|
||||
{
|
||||
unsigned int i;
|
||||
bitmap_iterator bi;
|
||||
bool found_anyoffset = false;
|
||||
subvar_t sv;
|
||||
|
||||
EXECUTE_IF_SET_IN_BITMAP (from, 0, i, bi)
|
||||
{
|
||||
varinfo_t vi = get_varinfo (i);
|
||||
|
||||
/* If we find ANYOFFSET in the solution and the solution
|
||||
includes SFTs for some structure, then all the SFTs in that
|
||||
structure will need to be added to the alias set. */
|
||||
if (vi->id == anyoffset_id)
|
||||
{
|
||||
found_anyoffset = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The only artificial variables that are allowed in a may-alias
|
||||
set are heap variables. */
|
||||
if (vi->is_artificial_var && !vi->is_heap_var)
|
||||
continue;
|
||||
|
||||
/* Variables containing unions may need to be converted to their
|
||||
SFT's, because SFT's can have unions and we cannot. */
|
||||
if (vi->has_union && get_subvars_for_var (vi->decl) != NULL)
|
||||
{
|
||||
subvar_t svars = get_subvars_for_var (vi->decl);
|
||||
subvar_t sv;
|
||||
for (sv = svars; sv; sv = sv->next)
|
||||
/* Variables containing unions may need to be converted to
|
||||
their SFT's, because SFT's can have unions and we cannot. */
|
||||
for (sv = get_subvars_for_var (vi->decl); sv; sv = sv->next)
|
||||
bitmap_set_bit (into, DECL_UID (sv->var));
|
||||
}
|
||||
/* We may end up with labels in the points-to set because people
|
||||
take their address, and they are _DECL's. */
|
||||
else if (TREE_CODE (vi->decl) == VAR_DECL
|
||||
|| TREE_CODE (vi->decl) == PARM_DECL)
|
||||
bitmap_set_bit (into, DECL_UID (vi->decl));
|
||||
|
||||
|
||||
|| TREE_CODE (vi->decl) == PARM_DECL)
|
||||
{
|
||||
if (found_anyoffset
|
||||
&& var_can_have_subvars (vi->decl)
|
||||
&& get_subvars_for_var (vi->decl))
|
||||
{
|
||||
/* If ANYOFFSET is in the solution set and VI->DECL is
|
||||
an aggregate variable with sub-variables, then any of
|
||||
the SFTs inside VI->DECL may have been accessed. Add
|
||||
all the sub-vars for VI->DECL. */
|
||||
for (sv = get_subvars_for_var (vi->decl); sv; sv = sv->next)
|
||||
bitmap_set_bit (into, DECL_UID (sv->var));
|
||||
}
|
||||
else if (var_can_have_subvars (vi->decl)
|
||||
&& get_subvars_for_var (vi->decl))
|
||||
{
|
||||
/* If VI->DECL is an aggregate for which we created
|
||||
SFTs, add the SFT corresponding to VI->OFFSET. */
|
||||
tree sft = get_subvar_at (vi->decl, vi->offset);
|
||||
bitmap_set_bit (into, DECL_UID (sft));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, just add VI->DECL to the alias set. */
|
||||
bitmap_set_bit (into, DECL_UID (vi->decl));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static int have_alias_info = false;
|
||||
|
||||
|
||||
static bool have_alias_info = false;
|
||||
|
||||
/* Given a pointer variable P, fill in its points-to set, or return
|
||||
false if we can't. */
|
||||
@ -2990,8 +3214,10 @@ bool
|
||||
find_what_p_points_to (tree p)
|
||||
{
|
||||
unsigned int id = 0;
|
||||
|
||||
if (!have_alias_info)
|
||||
return false;
|
||||
|
||||
if (lookup_id_for_tree (p, &id))
|
||||
{
|
||||
varinfo_t vi = get_varinfo (id);
|
||||
@ -2999,14 +3225,15 @@ find_what_p_points_to (tree p)
|
||||
if (vi->is_artificial_var)
|
||||
return false;
|
||||
|
||||
/* See if this is a field or a structure */
|
||||
/* See if this is a field or a structure. */
|
||||
if (vi->size != vi->fullsize)
|
||||
{
|
||||
/* Nothing currently asks about structure fields directly,
|
||||
but when they do, we need code here to hand back the
|
||||
points-to set. */
|
||||
if (!var_can_have_subvars (vi->decl)
|
||||
|| get_subvars_for_var (vi->decl) == NULL)
|
||||
return false;
|
||||
/* Nothing currently asks about structure fields directly, but when
|
||||
they do, we need code here to hand back the points-to set. */
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3018,21 +3245,45 @@ find_what_p_points_to (tree p)
|
||||
variable. */
|
||||
vi = get_varinfo (vi->node);
|
||||
|
||||
/* Make sure there aren't any artificial vars in the points to set.
|
||||
XXX: Note that we need to translate our heap variables to
|
||||
something. */
|
||||
/* Translate artificial variables into SSA_NAME_PTR_INFO
|
||||
attributes. */
|
||||
EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
|
||||
{
|
||||
if (get_varinfo (i)->is_artificial_var)
|
||||
return false;
|
||||
varinfo_t vi = get_varinfo (i);
|
||||
|
||||
if (vi->is_artificial_var)
|
||||
{
|
||||
/* FIXME. READONLY should be handled better so that
|
||||
flow insensitive aliasing can disregard writeable
|
||||
aliases. */
|
||||
if (vi->id == nothing_id)
|
||||
pi->pt_null = 1;
|
||||
else if (vi->id == anything_id)
|
||||
pi->pt_anything = 1;
|
||||
else if (vi->id == readonly_id)
|
||||
pi->pt_anything = 1;
|
||||
else if (vi->id == integer_id)
|
||||
pi->pt_anything = 1;
|
||||
else if (vi->is_heap_var)
|
||||
pi->pt_global_mem = 1;
|
||||
}
|
||||
}
|
||||
pi->pt_anything = false;
|
||||
|
||||
if (pi->pt_anything)
|
||||
return false;
|
||||
|
||||
if (!pi->pt_vars)
|
||||
pi->pt_vars = BITMAP_GGC_ALLOC ();
|
||||
|
||||
set_uids_in_ptset (pi->pt_vars, vi->solution);
|
||||
|
||||
if (bitmap_empty_p (pi->pt_vars))
|
||||
pi->pt_vars = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3053,7 +3304,7 @@ dump_sa_points_to_info (FILE *outfile)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
fprintf (outfile, "\nPoints-to information\n\n");
|
||||
fprintf (outfile, "\nPoints-to sets\n\n");
|
||||
|
||||
if (dump_flags & TDF_STATS)
|
||||
{
|
||||
@ -3124,6 +3375,7 @@ init_base_vars (void)
|
||||
rhs.var = anything_id;
|
||||
rhs.offset = 0;
|
||||
var_anything->address_taken = true;
|
||||
|
||||
/* This specifically does not use process_constraint because
|
||||
process_constraint ignores all anything = anything constraints, since all
|
||||
but this one are redundant. */
|
||||
@ -3177,17 +3429,44 @@ init_base_vars (void)
|
||||
rhs.var = anything_id;
|
||||
rhs.offset = 0;
|
||||
process_constraint (new_constraint (lhs, rhs));
|
||||
|
||||
/* Create the ANYOFFSET variable, used to represent an arbitrary offset
|
||||
inside an object. This is similar to ANYTHING, but less drastic.
|
||||
It means that the pointer can point anywhere inside an object,
|
||||
but not outside of it. */
|
||||
anyoffset_tree = create_tmp_var_raw (void_type_node, "ANYOFFSET");
|
||||
anyoffset_id = 4;
|
||||
var_anyoffset = new_var_info (anyoffset_tree, anyoffset_id, "ANYOFFSET",
|
||||
anyoffset_id);
|
||||
insert_id_for_tree (anyoffset_tree, anyoffset_id);
|
||||
var_anyoffset->is_artificial_var = 1;
|
||||
var_anyoffset->size = ~0;
|
||||
var_anyoffset->offset = 0;
|
||||
var_anyoffset->next = NULL;
|
||||
var_anyoffset->fullsize = ~0;
|
||||
VEC_safe_push (varinfo_t, gc, varmap, var_anyoffset);
|
||||
|
||||
/* ANYOFFSET points to ANYOFFSET. */
|
||||
lhs.type = SCALAR;
|
||||
lhs.var = anyoffset_id;
|
||||
lhs.offset = 0;
|
||||
rhs.type = ADDRESSOF;
|
||||
rhs.var = anyoffset_id;
|
||||
rhs.offset = 0;
|
||||
process_constraint (new_constraint (lhs, rhs));
|
||||
}
|
||||
|
||||
|
||||
/* Create points-to sets for the current function. See the comments
|
||||
at the start of the file for an algorithmic overview. */
|
||||
|
||||
static void
|
||||
create_alias_vars (void)
|
||||
void
|
||||
compute_points_to_sets (struct alias_info *ai)
|
||||
{
|
||||
basic_block bb;
|
||||
|
||||
|
||||
timevar_push (TV_TREE_PTA);
|
||||
|
||||
init_alias_vars ();
|
||||
|
||||
constraint_pool = create_alloc_pool ("Constraint pool",
|
||||
@ -3201,7 +3480,7 @@ create_alias_vars (void)
|
||||
varmap = VEC_alloc (varinfo_t, gc, 8);
|
||||
id_for_tree = htab_create (10, tree_id_hash, tree_id_eq, free);
|
||||
memset (&stats, 0, sizeof (stats));
|
||||
|
||||
|
||||
init_base_vars ();
|
||||
|
||||
intra_create_variable_infos ();
|
||||
@ -3214,28 +3493,29 @@ create_alias_vars (void)
|
||||
|
||||
for (phi = phi_nodes (bb); phi; phi = TREE_CHAIN (phi))
|
||||
if (is_gimple_reg (PHI_RESULT (phi)))
|
||||
find_func_aliases (phi);
|
||||
find_func_aliases (phi, ai);
|
||||
|
||||
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
|
||||
find_func_aliases (bsi_stmt (bsi));
|
||||
find_func_aliases (bsi_stmt (bsi), ai);
|
||||
}
|
||||
|
||||
build_constraint_graph ();
|
||||
|
||||
if (dump_file)
|
||||
{
|
||||
fprintf (dump_file, "Constraints:\n");
|
||||
fprintf (dump_file, "Points-to analysis\n\nConstraints:\n\n");
|
||||
dump_constraints (dump_file);
|
||||
}
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Collapsing static cycles and doing variable substitution:\n");
|
||||
fprintf (dump_file, "\nCollapsing static cycles and doing variable "
|
||||
"substitution:\n");
|
||||
|
||||
find_and_collapse_graph_cycles (graph, false);
|
||||
perform_var_substitution (graph);
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Solving graph:\n");
|
||||
fprintf (dump_file, "\nSolving graph:\n");
|
||||
|
||||
solve_graph (graph);
|
||||
|
||||
@ -3243,30 +3523,15 @@ create_alias_vars (void)
|
||||
dump_sa_points_to_info (dump_file);
|
||||
|
||||
have_alias_info = true;
|
||||
|
||||
timevar_pop (TV_TREE_PTA);
|
||||
}
|
||||
|
||||
struct tree_opt_pass pass_build_pta =
|
||||
{
|
||||
"pta", /* name */
|
||||
NULL, /* gate */
|
||||
create_alias_vars, /* execute */
|
||||
NULL, /* sub */
|
||||
NULL, /* next */
|
||||
0, /* static_pass_number */
|
||||
TV_TREE_PTA, /* tv_id */
|
||||
PROP_cfg, /* properties_required */
|
||||
PROP_pta, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
0, /* todo_flags_finish */
|
||||
0 /* letter */
|
||||
};
|
||||
|
||||
|
||||
/* Delete created points-to sets. */
|
||||
|
||||
static void
|
||||
delete_alias_vars (void)
|
||||
void
|
||||
delete_points_to_sets (void)
|
||||
{
|
||||
htab_delete (id_for_tree);
|
||||
free_alloc_pool (variable_info_pool);
|
||||
@ -3275,20 +3540,3 @@ delete_alias_vars (void)
|
||||
bitmap_obstack_release (&ptabitmap_obstack);
|
||||
have_alias_info = false;
|
||||
}
|
||||
|
||||
struct tree_opt_pass pass_del_pta =
|
||||
{
|
||||
NULL, /* name */
|
||||
NULL, /* gate */
|
||||
delete_alias_vars, /* execute */
|
||||
NULL, /* sub */
|
||||
NULL, /* next */
|
||||
0, /* static_pass_number */
|
||||
TV_TREE_PTA, /* tv_id */
|
||||
PROP_pta, /* properties_required */
|
||||
0, /* properties_provided */
|
||||
PROP_pta, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
0, /* todo_flags_finish */
|
||||
0 /* letter */
|
||||
};
|
||||
|
@ -25,6 +25,66 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
struct constraint;
|
||||
typedef struct constraint *constraint_t;
|
||||
|
||||
/* Alias information used by compute_may_aliases and its helpers. */
|
||||
struct alias_info
|
||||
{
|
||||
/* SSA names visited while collecting points-to information. If bit I
|
||||
is set, it means that SSA variable with version I has already been
|
||||
visited. */
|
||||
sbitmap ssa_names_visited;
|
||||
|
||||
/* Array of SSA_NAME pointers processed by the points-to collector. */
|
||||
varray_type processed_ptrs;
|
||||
|
||||
/* ADDRESSABLE_VARS contains all the global variables and locals that
|
||||
have had their address taken. */
|
||||
struct alias_map_d **addressable_vars;
|
||||
size_t num_addressable_vars;
|
||||
|
||||
/* POINTERS contains all the _DECL pointers with unique memory tags
|
||||
that have been referenced in the program. */
|
||||
struct alias_map_d **pointers;
|
||||
size_t num_pointers;
|
||||
|
||||
/* Number of function calls found in the program. */
|
||||
size_t num_calls_found;
|
||||
|
||||
/* Number of const/pure function calls found in the program. */
|
||||
size_t num_pure_const_calls_found;
|
||||
|
||||
/* Array of counters to keep track of how many times each pointer has
|
||||
been dereferenced in the program. This is used by the alias grouping
|
||||
heuristic in compute_flow_insensitive_aliasing. */
|
||||
varray_type num_references;
|
||||
|
||||
/* Total number of virtual operands that will be needed to represent
|
||||
all the aliases of all the pointers found in the program. */
|
||||
long total_alias_vops;
|
||||
|
||||
/* Variables that have been written to. */
|
||||
bitmap written_vars;
|
||||
|
||||
/* Pointers that have been used in an indirect store operation. */
|
||||
bitmap dereferenced_ptrs_store;
|
||||
|
||||
/* Pointers that have been used in an indirect load operation. */
|
||||
bitmap dereferenced_ptrs_load;
|
||||
};
|
||||
|
||||
/* Keep track of how many times each pointer has been dereferenced in
|
||||
the program using the aux variable. This is used by the alias
|
||||
grouping heuristic in compute_flow_insensitive_aliasing. */
|
||||
#define NUM_REFERENCES(ANN) ((size_t)((ANN)->common.aux))
|
||||
#define NUM_REFERENCES_CLEAR(ANN) ((ANN)->common.aux) = 0
|
||||
#define NUM_REFERENCES_INC(ANN) (ANN)->common.aux = (void*) (((size_t)((ANN)->common.aux)) + 1)
|
||||
#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 *);
|
||||
|
||||
/* In tree-ssa-structalias.c. */
|
||||
extern void compute_points_to_sets (struct alias_info *);
|
||||
extern void delete_points_to_sets (void);
|
||||
extern void dump_constraint (FILE *, constraint_t);
|
||||
extern void dump_constraints (FILE *);
|
||||
extern void debug_constraint (constraint_t);
|
||||
|
@ -467,10 +467,9 @@ verify_flow_sensitive_alias_info (void)
|
||||
}
|
||||
|
||||
if (pi->name_mem_tag
|
||||
&& !pi->pt_malloc
|
||||
&& (pi->pt_vars == NULL || bitmap_empty_p (pi->pt_vars)))
|
||||
{
|
||||
error ("pointers with a memory tag, should have points-to sets or point to malloc");
|
||||
error ("pointers with a memory tag, should have points-to sets");
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user