aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.4.3/gcc/tree-ssa-structalias.c
diff options
context:
space:
mode:
authorJing Yu <jingyu@google.com>2010-07-22 14:03:48 -0700
committerJing Yu <jingyu@google.com>2010-07-22 14:03:48 -0700
commitb094d6c4bf572654a031ecc4afe675154c886dc5 (patch)
tree89394c56b05e13a5413ee60237d65b0214fd98e2 /gcc-4.4.3/gcc/tree-ssa-structalias.c
parentdc34721ac3bf7e3c406fba8cfe9d139393345ec5 (diff)
downloadtoolchain_gcc-b094d6c4bf572654a031ecc4afe675154c886dc5.tar.gz
toolchain_gcc-b094d6c4bf572654a031ecc4afe675154c886dc5.tar.bz2
toolchain_gcc-b094d6c4bf572654a031ecc4afe675154c886dc5.zip
commit gcc-4.4.3 which is used to build gcc-4.4.3 Android toolchain in master.
The source is based on fsf gcc-4.4.3 and contains local patches which are recorded in gcc-4.4.3/README.google. Change-Id: Id8c6d6927df274ae9749196a1cc24dbd9abc9887
Diffstat (limited to 'gcc-4.4.3/gcc/tree-ssa-structalias.c')
-rw-r--r--gcc-4.4.3/gcc/tree-ssa-structalias.c5810
1 files changed, 5810 insertions, 0 deletions
diff --git a/gcc-4.4.3/gcc/tree-ssa-structalias.c b/gcc-4.4.3/gcc/tree-ssa-structalias.c
new file mode 100644
index 000000000..9d30528ca
--- /dev/null
+++ b/gcc-4.4.3/gcc/tree-ssa-structalias.c
@@ -0,0 +1,5810 @@
+/* Tree based points-to analysis
+ Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ Contributed by Daniel Berlin <dberlin@dberlin.org>
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ GCC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "ggc.h"
+#include "obstack.h"
+#include "bitmap.h"
+#include "flags.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "output.h"
+#include "tree.h"
+#include "c-common.h"
+#include "tree-flow.h"
+#include "tree-inline.h"
+#include "varray.h"
+#include "c-tree.h"
+#include "diagnostic.h"
+#include "toplev.h"
+#include "gimple.h"
+#include "hashtab.h"
+#include "function.h"
+#include "cgraph.h"
+#include "tree-pass.h"
+#include "timevar.h"
+#include "alloc-pool.h"
+#include "splay-tree.h"
+#include "params.h"
+#include "tree-ssa-structalias.h"
+#include "cgraph.h"
+#include "alias.h"
+#include "pointer-set.h"
+
+/* The idea behind this analyzer is to generate set constraints from the
+ program, then solve the resulting constraints in order to generate the
+ points-to sets.
+
+ Set constraints are a way of modeling program analysis problems that
+ involve sets. They consist of an inclusion constraint language,
+ describing the variables (each variable is a set) and operations that
+ are involved on the variables, and a set of rules that derive facts
+ from these operations. To solve a system of set constraints, you derive
+ all possible facts under the rules, which gives you the correct sets
+ as a consequence.
+
+ See "Efficient Field-sensitive pointer analysis for C" by "David
+ J. Pearce and Paul H. J. Kelly and Chris Hankin, at
+ http://citeseer.ist.psu.edu/pearce04efficient.html
+
+ Also see "Ultra-fast Aliasing Analysis using CLA: A Million Lines
+ of C Code in a Second" by ""Nevin Heintze and Olivier Tardieu" at
+ http://citeseer.ist.psu.edu/heintze01ultrafast.html
+
+ There are three types of real constraint expressions, DEREF,
+ ADDRESSOF, and SCALAR. Each constraint expression consists
+ of a constraint type, a variable, and an offset.
+
+ SCALAR is a constraint expression type used to represent x, whether
+ it appears on the LHS or the RHS of a statement.
+ DEREF is a constraint expression type used to represent *x, whether
+ it appears on the LHS or the RHS of a statement.
+ ADDRESSOF is a constraint expression used to represent &x, whether
+ it appears on the LHS or the RHS of a statement.
+
+ Each pointer variable in the program is assigned an integer id, and
+ each field of a structure variable is assigned an integer id as well.
+
+ Structure variables are linked to their list of fields through a "next
+ field" in each variable that points to the next field in offset
+ order.
+ Each variable for a structure field has
+
+ 1. "size", that tells the size in bits of that field.
+ 2. "fullsize, that tells the size in bits of the entire structure.
+ 3. "offset", that tells the offset in bits from the beginning of the
+ structure to this field.
+
+ Thus,
+ struct f
+ {
+ int a;
+ int b;
+ } foo;
+ int *bar;
+
+ looks like
+
+ foo.a -> id 1, size 32, offset 0, fullsize 64, next foo.b
+ foo.b -> id 2, size 32, offset 32, fullsize 64, next NULL
+ bar -> id 3, size 32, offset 0, fullsize 32, next NULL
+
+
+ In order to solve the system of set constraints, the following is
+ done:
+
+ 1. Each constraint variable x has a solution set associated with it,
+ Sol(x).
+
+ 2. Constraints are separated into direct, copy, and complex.
+ Direct constraints are ADDRESSOF constraints that require no extra
+ processing, such as P = &Q
+ Copy constraints are those of the form P = Q.
+ Complex constraints are all the constraints involving dereferences
+ and offsets (including offsetted copies).
+
+ 3. All direct constraints of the form P = &Q are processed, such
+ that Q is added to Sol(P)
+
+ 4. All complex constraints for a given constraint variable are stored in a
+ linked list attached to that variable's node.
+
+ 5. A directed graph is built out of the copy constraints. Each
+ constraint variable is a node in the graph, and an edge from
+ Q to P is added for each copy constraint of the form P = Q
+
+ 6. The graph is then walked, and solution sets are
+ propagated along the copy edges, such that an edge from Q to P
+ causes Sol(P) <- Sol(P) union Sol(Q).
+
+ 7. As we visit each node, all complex constraints associated with
+ that node are processed by adding appropriate copy edges to the graph, or the
+ appropriate variables to the solution set.
+
+ 8. The process of walking the graph is iterated until no solution
+ sets change.
+
+ Prior to walking the graph in steps 6 and 7, We perform static
+ cycle elimination on the constraint graph, as well
+ as off-line variable substitution.
+
+ TODO: Adding offsets to pointer-to-structures can be handled (IE not punted
+ on and turned into anything), but isn't. You can just see what offset
+ inside the pointed-to struct it's going to access.
+
+ TODO: Constant bounded arrays can be handled as if they were structs of the
+ same number of elements.
+
+ TODO: Modeling heap and incoming pointers becomes much better if we
+ add fields to them as we discover them, which we could do.
+
+ TODO: We could handle unions, but to be honest, it's probably not
+ worth the pain or slowdown. */
+
+static GTY ((if_marked ("tree_map_marked_p"), param_is (struct tree_map)))
+htab_t heapvar_for_stmt;
+
+static bool use_field_sensitive = true;
+static int in_ipa_mode = 0;
+
+/* Used for predecessor bitmaps. */
+static bitmap_obstack predbitmap_obstack;
+
+/* Used for points-to sets. */
+static bitmap_obstack pta_obstack;
+
+/* Used for oldsolution members of variables. */
+static bitmap_obstack oldpta_obstack;
+
+/* Used for per-solver-iteration bitmaps. */
+static bitmap_obstack iteration_obstack;
+
+static unsigned int create_variable_info_for (tree, const char *);
+typedef struct constraint_graph *constraint_graph_t;
+static void unify_nodes (constraint_graph_t, unsigned int, unsigned int, bool);
+
+DEF_VEC_P(constraint_t);
+DEF_VEC_ALLOC_P(constraint_t,heap);
+
+#define EXECUTE_IF_IN_NONNULL_BITMAP(a, b, c, d) \
+ if (a) \
+ EXECUTE_IF_SET_IN_BITMAP (a, b, c, d)
+
+static struct constraint_stats
+{
+ unsigned int total_vars;
+ unsigned int nonpointer_vars;
+ unsigned int unified_vars_static;
+ unsigned int unified_vars_dynamic;
+ unsigned int iterations;
+ unsigned int num_edges;
+ unsigned int num_implicit_edges;
+ unsigned int points_to_sets_created;
+} stats;
+
+struct variable_info
+{
+ /* ID of this variable */
+ unsigned int id;
+
+ /* True if this is a variable created by the constraint analysis, such as
+ heap variables and constraints we had to break up. */
+ unsigned int is_artificial_var:1;
+
+ /* True if this is a special variable whose solution set should not be
+ changed. */
+ unsigned int is_special_var:1;
+
+ /* True for variables whose size is not known or variable. */
+ unsigned int is_unknown_size_var:1;
+
+ /* True for (sub-)fields that represent a whole variable. */
+ unsigned int is_full_var : 1;
+
+ /* True if this is a heap variable. */
+ unsigned int is_heap_var:1;
+
+ /* True if we may not use TBAA to prune references to this
+ variable. This is used for C++ placement new. */
+ unsigned int no_tbaa_pruning : 1;
+
+ /* True if this field may contain pointers. */
+ unsigned int may_have_pointers : 1;
+
+ /* Variable id this was collapsed to due to type unsafety. Zero if
+ this variable was not collapsed. This should be unused completely
+ after build_succ_graph, or something is broken. */
+ unsigned int collapsed_to;
+
+ /* A link to the variable for the next field in this structure. */
+ struct variable_info *next;
+
+ /* Offset of this variable, in bits, from the base variable */
+ unsigned HOST_WIDE_INT offset;
+
+ /* Size of the variable, in bits. */
+ unsigned HOST_WIDE_INT size;
+
+ /* Full size of the base variable, in bits. */
+ unsigned HOST_WIDE_INT fullsize;
+
+ /* Name of this variable */
+ const char *name;
+
+ /* Tree that this variable is associated with. */
+ tree decl;
+
+ /* Points-to set for this variable. */
+ bitmap solution;
+
+ /* Old points-to set for this variable. */
+ bitmap oldsolution;
+};
+typedef struct variable_info *varinfo_t;
+
+static varinfo_t first_vi_for_offset (varinfo_t, unsigned HOST_WIDE_INT);
+static varinfo_t lookup_vi_for_tree (tree);
+
+/* Pool of variable info structures. */
+static alloc_pool variable_info_pool;
+
+DEF_VEC_P(varinfo_t);
+
+DEF_VEC_ALLOC_P(varinfo_t, heap);
+
+/* Table of variable info structures for constraint variables.
+ Indexed directly by variable info id. */
+static VEC(varinfo_t,heap) *varmap;
+
+/* Return the varmap element N */
+
+static inline varinfo_t
+get_varinfo (unsigned int n)
+{
+ return VEC_index (varinfo_t, varmap, n);
+}
+
+/* Return the varmap element N, following the collapsed_to link. */
+
+static inline varinfo_t
+get_varinfo_fc (unsigned int n)
+{
+ varinfo_t v = VEC_index (varinfo_t, varmap, n);
+
+ if (v->collapsed_to != 0)
+ return get_varinfo (v->collapsed_to);
+ return v;
+}
+
+/* Static IDs for the special variables. */
+enum { nothing_id = 0, anything_id = 1, readonly_id = 2,
+ escaped_id = 3, nonlocal_id = 4, callused_id = 5,
+ storedanything_id = 6, integer_id = 7 };
+
+/* Variable that represents the unknown pointer. */
+static varinfo_t var_anything;
+static tree anything_tree;
+
+/* Variable that represents the NULL pointer. */
+static varinfo_t var_nothing;
+static tree nothing_tree;
+
+/* Variable that represents read only memory. */
+static varinfo_t var_readonly;
+static tree readonly_tree;
+
+/* Variable that represents escaped memory. */
+static varinfo_t var_escaped;
+static tree escaped_tree;
+
+/* Variable that represents nonlocal memory. */
+static varinfo_t var_nonlocal;
+static tree nonlocal_tree;
+
+/* Variable that represents call-used memory. */
+static varinfo_t var_callused;
+static tree callused_tree;
+
+/* Variable that represents variables that are stored to anything. */
+static varinfo_t var_storedanything;
+static tree storedanything_tree;
+
+/* Variable that represents integers. This is used for when people do things
+ like &0->a.b. */
+static varinfo_t var_integer;
+static tree integer_tree;
+
+/* Lookup a heap var for FROM, and return it if we find one. */
+
+static tree
+heapvar_lookup (tree from)
+{
+ struct tree_map *h, in;
+ in.base.from = from;
+
+ h = (struct tree_map *) htab_find_with_hash (heapvar_for_stmt, &in,
+ htab_hash_pointer (from));
+ if (h)
+ return h->to;
+ return NULL_TREE;
+}
+
+/* Insert a mapping FROM->TO in the heap var for statement
+ hashtable. */
+
+static void
+heapvar_insert (tree from, tree to)
+{
+ struct tree_map *h;
+ void **loc;
+
+ h = GGC_NEW (struct tree_map);
+ h->hash = htab_hash_pointer (from);
+ h->base.from = from;
+ h->to = to;
+ loc = htab_find_slot_with_hash (heapvar_for_stmt, h, h->hash, INSERT);
+ *(struct tree_map **) loc = h;
+}
+
+/* Return a new variable info structure consisting for a variable
+ named NAME, and using constraint graph node NODE. */
+
+static varinfo_t
+new_var_info (tree t, unsigned int id, const char *name)
+{
+ varinfo_t ret = (varinfo_t) pool_alloc (variable_info_pool);
+ tree var;
+
+ ret->id = id;
+ ret->name = name;
+ ret->decl = t;
+ ret->is_artificial_var = false;
+ ret->is_heap_var = false;
+ ret->is_special_var = false;
+ ret->is_unknown_size_var = false;
+ ret->is_full_var = false;
+ ret->may_have_pointers = true;
+ var = t;
+ if (TREE_CODE (var) == SSA_NAME)
+ var = SSA_NAME_VAR (var);
+ ret->no_tbaa_pruning = (DECL_P (var)
+ && POINTER_TYPE_P (TREE_TYPE (var))
+ && DECL_NO_TBAA_P (var));
+ ret->solution = BITMAP_ALLOC (&pta_obstack);
+ ret->oldsolution = BITMAP_ALLOC (&oldpta_obstack);
+ ret->next = NULL;
+ ret->collapsed_to = 0;
+ return ret;
+}
+
+typedef enum {SCALAR, DEREF, ADDRESSOF} constraint_expr_type;
+
+/* An expression that appears in a constraint. */
+
+struct constraint_expr
+{
+ /* Constraint type. */
+ constraint_expr_type type;
+
+ /* Variable we are referring to in the constraint. */
+ unsigned int var;
+
+ /* Offset, in bits, of this constraint from the beginning of
+ variables it ends up referring to.
+
+ IOW, in a deref constraint, we would deref, get the result set,
+ then add OFFSET to each member. */
+ unsigned HOST_WIDE_INT offset;
+};
+
+typedef struct constraint_expr ce_s;
+DEF_VEC_O(ce_s);
+DEF_VEC_ALLOC_O(ce_s, heap);
+static void get_constraint_for_1 (tree, VEC(ce_s, heap) **, bool);
+static void get_constraint_for (tree, VEC(ce_s, heap) **);
+static void do_deref (VEC (ce_s, heap) **);
+
+/* Our set constraints are made up of two constraint expressions, one
+ LHS, and one RHS.
+
+ As described in the introduction, our set constraints each represent an
+ operation between set valued variables.
+*/
+struct constraint
+{
+ struct constraint_expr lhs;
+ struct constraint_expr rhs;
+};
+
+/* List of constraints that we use to build the constraint graph from. */
+
+static VEC(constraint_t,heap) *constraints;
+static alloc_pool constraint_pool;
+
+
+DEF_VEC_I(int);
+DEF_VEC_ALLOC_I(int, heap);
+
+/* The constraint graph is represented as an array of bitmaps
+ containing successor nodes. */
+
+struct constraint_graph
+{
+ /* Size of this graph, which may be different than the number of
+ nodes in the variable map. */
+ unsigned int size;
+
+ /* Explicit successors of each node. */
+ bitmap *succs;
+
+ /* Implicit predecessors of each node (Used for variable
+ substitution). */
+ bitmap *implicit_preds;
+
+ /* Explicit predecessors of each node (Used for variable substitution). */
+ bitmap *preds;
+
+ /* Indirect cycle representatives, or -1 if the node has no indirect
+ cycles. */
+ int *indirect_cycles;
+
+ /* Representative node for a node. rep[a] == a unless the node has
+ been unified. */
+ unsigned int *rep;
+
+ /* Equivalence class representative for a label. This is used for
+ variable substitution. */
+ int *eq_rep;
+
+ /* Pointer equivalence label for a node. All nodes with the same
+ pointer equivalence label can be unified together at some point
+ (either during constraint optimization or after the constraint
+ graph is built). */
+ unsigned int *pe;
+
+ /* Pointer equivalence representative for a label. This is used to
+ handle nodes that are pointer equivalent but not location
+ equivalent. We can unite these once the addressof constraints
+ are transformed into initial points-to sets. */
+ int *pe_rep;
+
+ /* Pointer equivalence label for each node, used during variable
+ substitution. */
+ unsigned int *pointer_label;
+
+ /* Location equivalence label for each node, used during location
+ equivalence finding. */
+ unsigned int *loc_label;
+
+ /* Pointed-by set for each node, used during location equivalence
+ finding. This is pointed-by rather than pointed-to, because it
+ is constructed using the predecessor graph. */
+ bitmap *pointed_by;
+
+ /* Points to sets for pointer equivalence. This is *not* the actual
+ points-to sets for nodes. */
+ bitmap *points_to;
+
+ /* Bitmap of nodes where the bit is set if the node is a direct
+ node. Used for variable substitution. */
+ sbitmap direct_nodes;
+
+ /* Bitmap of nodes where the bit is set if the node is address
+ taken. Used for variable substitution. */
+ bitmap address_taken;
+
+ /* Vector of complex constraints for each graph node. Complex
+ constraints are those involving dereferences or offsets that are
+ not 0. */
+ VEC(constraint_t,heap) **complex;
+};
+
+static constraint_graph_t graph;
+
+/* During variable substitution and the offline version of indirect
+ cycle finding, we create nodes to represent dereferences and
+ address taken constraints. These represent where these start and
+ end. */
+#define FIRST_REF_NODE (VEC_length (varinfo_t, varmap))
+#define LAST_REF_NODE (FIRST_REF_NODE + (FIRST_REF_NODE - 1))
+
+/* Return the representative node for NODE, if NODE has been unioned
+ with another NODE.
+ This function performs path compression along the way to finding
+ the representative. */
+
+static unsigned int
+find (unsigned int node)
+{
+ gcc_assert (node < graph->size);
+ if (graph->rep[node] != node)
+ return graph->rep[node] = find (graph->rep[node]);
+ return node;
+}
+
+/* Union the TO and FROM nodes to the TO nodes.
+ Note that at some point in the future, we may want to do
+ union-by-rank, in which case we are going to have to return the
+ node we unified to. */
+
+static bool
+unite (unsigned int to, unsigned int from)
+{
+ gcc_assert (to < graph->size && from < graph->size);
+ if (to != from && graph->rep[from] != to)
+ {
+ graph->rep[from] = to;
+ return true;
+ }
+ return false;
+}
+
+/* Create a new constraint consisting of LHS and RHS expressions. */
+
+static constraint_t
+new_constraint (const struct constraint_expr lhs,
+ const struct constraint_expr rhs)
+{
+ constraint_t ret = (constraint_t) pool_alloc (constraint_pool);
+ ret->lhs = lhs;
+ ret->rhs = rhs;
+ return ret;
+}
+
+/* Print out constraint C to FILE. */
+
+void
+dump_constraint (FILE *file, constraint_t c)
+{
+ if (c->lhs.type == ADDRESSOF)
+ fprintf (file, "&");
+ else if (c->lhs.type == DEREF)
+ fprintf (file, "*");
+ fprintf (file, "%s", get_varinfo_fc (c->lhs.var)->name);
+ if (c->lhs.offset != 0)
+ fprintf (file, " + " HOST_WIDE_INT_PRINT_DEC, c->lhs.offset);
+ fprintf (file, " = ");
+ if (c->rhs.type == ADDRESSOF)
+ fprintf (file, "&");
+ else if (c->rhs.type == DEREF)
+ fprintf (file, "*");
+ fprintf (file, "%s", get_varinfo_fc (c->rhs.var)->name);
+ if (c->rhs.offset != 0)
+ fprintf (file, " + " HOST_WIDE_INT_PRINT_DEC, c->rhs.offset);
+ fprintf (file, "\n");
+}
+
+/* Print out constraint C to stderr. */
+
+void
+debug_constraint (constraint_t c)
+{
+ dump_constraint (stderr, c);
+}
+
+/* Print out all constraints to FILE */
+
+void
+dump_constraints (FILE *file)
+{
+ int i;
+ constraint_t c;
+ for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
+ dump_constraint (file, c);
+}
+
+/* Print out all constraints to stderr. */
+
+void
+debug_constraints (void)
+{
+ dump_constraints (stderr);
+}
+
+/* Print out to FILE the edge in the constraint graph that is created by
+ constraint c. The edge may have a label, depending on the type of
+ constraint that it represents. If complex1, e.g: a = *b, then the label
+ is "=*", if complex2, e.g: *a = b, then the label is "*=", if
+ complex with an offset, e.g: a = b + 8, then the label is "+".
+ Otherwise the edge has no label. */
+
+void
+dump_constraint_edge (FILE *file, constraint_t c)
+{
+ if (c->rhs.type != ADDRESSOF)
+ {
+ const char *src = get_varinfo_fc (c->rhs.var)->name;
+ const char *dst = get_varinfo_fc (c->lhs.var)->name;
+ fprintf (file, " \"%s\" -> \"%s\" ", src, dst);
+ /* Due to preprocessing of constraints, instructions like *a = *b are
+ illegal; thus, we do not have to handle such cases. */
+ if (c->lhs.type == DEREF)
+ fprintf (file, " [ label=\"*=\" ] ;\n");
+ else if (c->rhs.type == DEREF)
+ fprintf (file, " [ label=\"=*\" ] ;\n");
+ else
+ {
+ /* We must check the case where the constraint is an offset.
+ In this case, it is treated as a complex constraint. */
+ if (c->rhs.offset != c->lhs.offset)
+ fprintf (file, " [ label=\"+\" ] ;\n");
+ else
+ fprintf (file, " ;\n");
+ }
+ }
+}
+
+/* Print the constraint graph in dot format. */
+
+void
+dump_constraint_graph (FILE *file)
+{
+ unsigned int i=0, size;
+ constraint_t c;
+
+ /* Only print the graph if it has already been initialized: */
+ if (!graph)
+ return;
+
+ /* Print the constraints used to produce the constraint graph. The
+ constraints will be printed as comments in the dot file: */
+ fprintf (file, "\n\n/* Constraints used in the constraint graph:\n");
+ dump_constraints (file);
+ fprintf (file, "*/\n");
+
+ /* Prints the header of the dot file: */
+ fprintf (file, "\n\n// The constraint graph in dot format:\n");
+ fprintf (file, "strict digraph {\n");
+ fprintf (file, " node [\n shape = box\n ]\n");
+ fprintf (file, " edge [\n fontsize = \"12\"\n ]\n");
+ fprintf (file, "\n // List of nodes in the constraint graph:\n");
+
+ /* The next lines print the nodes in the graph. In order to get the
+ number of nodes in the graph, we must choose the minimum between the
+ vector VEC (varinfo_t, varmap) and graph->size. If the graph has not
+ yet been initialized, then graph->size == 0, otherwise we must only
+ read nodes that have an entry in VEC (varinfo_t, varmap). */
+ size = VEC_length (varinfo_t, varmap);
+ size = size < graph->size ? size : graph->size;
+ for (i = 0; i < size; i++)
+ {
+ const char *name = get_varinfo_fc (graph->rep[i])->name;
+ fprintf (file, " \"%s\" ;\n", name);
+ }
+
+ /* Go over the list of constraints printing the edges in the constraint
+ graph. */
+ fprintf (file, "\n // The constraint edges:\n");
+ for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
+ if (c)
+ dump_constraint_edge (file, c);
+
+ /* Prints the tail of the dot file. By now, only the closing bracket. */
+ fprintf (file, "}\n\n\n");
+}
+
+/* Print out the constraint graph to stderr. */
+
+void
+debug_constraint_graph (void)
+{
+ dump_constraint_graph (stderr);
+}
+
+/* SOLVER FUNCTIONS
+
+ The solver is a simple worklist solver, that works on the following
+ algorithm:
+
+ sbitmap changed_nodes = all zeroes;
+ changed_count = 0;
+ For each node that is not already collapsed:
+ changed_count++;
+ set bit in changed nodes
+
+ while (changed_count > 0)
+ {
+ compute topological ordering for constraint graph
+
+ find and collapse cycles in the constraint graph (updating
+ changed if necessary)
+
+ for each node (n) in the graph in topological order:
+ changed_count--;
+
+ Process each complex constraint associated with the node,
+ updating changed if necessary.
+
+ For each outgoing edge from n, propagate the solution from n to
+ the destination of the edge, updating changed as necessary.
+
+ } */
+
+/* Return true if two constraint expressions A and B are equal. */
+
+static bool
+constraint_expr_equal (struct constraint_expr a, struct constraint_expr b)
+{
+ return a.type == b.type && a.var == b.var && a.offset == b.offset;
+}
+
+/* Return true if constraint expression A is less than constraint expression
+ B. This is just arbitrary, but consistent, in order to give them an
+ ordering. */
+
+static bool
+constraint_expr_less (struct constraint_expr a, struct constraint_expr b)
+{
+ if (a.type == b.type)
+ {
+ if (a.var == b.var)
+ return a.offset < b.offset;
+ else
+ return a.var < b.var;
+ }
+ else
+ return a.type < b.type;
+}
+
+/* Return true if constraint A is less than constraint B. This is just
+ arbitrary, but consistent, in order to give them an ordering. */
+
+static bool
+constraint_less (const constraint_t a, const constraint_t b)
+{
+ if (constraint_expr_less (a->lhs, b->lhs))
+ return true;
+ else if (constraint_expr_less (b->lhs, a->lhs))
+ return false;
+ else
+ return constraint_expr_less (a->rhs, b->rhs);
+}
+
+/* Return true if two constraints A and B are equal. */
+
+static bool
+constraint_equal (struct constraint a, struct constraint b)
+{
+ return constraint_expr_equal (a.lhs, b.lhs)
+ && constraint_expr_equal (a.rhs, b.rhs);
+}
+
+
+/* Find a constraint LOOKFOR in the sorted constraint vector VEC */
+
+static constraint_t
+constraint_vec_find (VEC(constraint_t,heap) *vec,
+ struct constraint lookfor)
+{
+ unsigned int place;
+ constraint_t found;
+
+ if (vec == NULL)
+ return NULL;
+
+ place = VEC_lower_bound (constraint_t, vec, &lookfor, constraint_less);
+ if (place >= VEC_length (constraint_t, vec))
+ return NULL;
+ found = VEC_index (constraint_t, vec, place);
+ if (!constraint_equal (*found, lookfor))
+ return NULL;
+ return found;
+}
+
+/* Union two constraint vectors, TO and FROM. Put the result in TO. */
+
+static void
+constraint_set_union (VEC(constraint_t,heap) **to,
+ VEC(constraint_t,heap) **from)
+{
+ int i;
+ constraint_t c;
+
+ for (i = 0; VEC_iterate (constraint_t, *from, i, c); i++)
+ {
+ if (constraint_vec_find (*to, *c) == NULL)
+ {
+ unsigned int place = VEC_lower_bound (constraint_t, *to, c,
+ constraint_less);
+ VEC_safe_insert (constraint_t, heap, *to, place, c);
+ }
+ }
+}
+
+/* Take a solution set SET, add OFFSET to each member of the set, and
+ overwrite SET with the result when done. */
+
+static void
+solution_set_add (bitmap set, unsigned HOST_WIDE_INT offset)
+{
+ bitmap result = BITMAP_ALLOC (&iteration_obstack);
+ unsigned int i;
+ bitmap_iterator bi;
+
+ EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
+ {
+ varinfo_t vi = get_varinfo (i);
+
+ /* If this is a variable with just one field just set its bit
+ in the result. */
+ if (vi->is_artificial_var
+ || vi->is_unknown_size_var
+ || vi->is_full_var)
+ bitmap_set_bit (result, i);
+ else
+ {
+ unsigned HOST_WIDE_INT fieldoffset = vi->offset + offset;
+ varinfo_t v = first_vi_for_offset (vi, fieldoffset);
+ /* If the result is outside of the variable use the last field. */
+ if (!v)
+ {
+ v = vi;
+ while (v->next != NULL)
+ v = v->next;
+ }
+ bitmap_set_bit (result, v->id);
+ /* If the result is not exactly at fieldoffset include the next
+ field as well. See get_constraint_for_ptr_offset for more
+ rationale. */
+ if (v->offset != fieldoffset
+ && v->next != NULL)
+ bitmap_set_bit (result, v->next->id);
+ }
+ }
+
+ bitmap_copy (set, result);
+ BITMAP_FREE (result);
+}
+
+/* Union solution sets TO and FROM, and add INC to each member of FROM in the
+ process. */
+
+static bool
+set_union_with_increment (bitmap to, bitmap from, unsigned HOST_WIDE_INT inc)
+{
+ if (inc == 0)
+ return bitmap_ior_into (to, from);
+ else
+ {
+ bitmap tmp;
+ bool res;
+
+ tmp = BITMAP_ALLOC (&iteration_obstack);
+ bitmap_copy (tmp, from);
+ solution_set_add (tmp, inc);
+ res = bitmap_ior_into (to, tmp);
+ BITMAP_FREE (tmp);
+ return res;
+ }
+}
+
+/* Insert constraint C into the list of complex constraints for graph
+ node VAR. */
+
+static void
+insert_into_complex (constraint_graph_t graph,
+ unsigned int var, constraint_t c)
+{
+ VEC (constraint_t, heap) *complex = graph->complex[var];
+ unsigned int place = VEC_lower_bound (constraint_t, complex, c,
+ constraint_less);
+
+ /* Only insert constraints that do not already exist. */
+ if (place >= VEC_length (constraint_t, complex)
+ || !constraint_equal (*c, *VEC_index (constraint_t, complex, place)))
+ VEC_safe_insert (constraint_t, heap, graph->complex[var], place, c);
+}
+
+
+/* Condense two variable nodes into a single variable node, by moving
+ all associated info from SRC to TO. */
+
+static void
+merge_node_constraints (constraint_graph_t graph, unsigned int to,
+ unsigned int from)
+{
+ unsigned int i;
+ constraint_t c;
+
+ gcc_assert (find (from) == to);
+
+ /* Move all complex constraints from src node into to node */
+ for (i = 0; VEC_iterate (constraint_t, graph->complex[from], i, c); i++)
+ {
+ /* In complex constraints for node src, we may have either
+ a = *src, and *src = a, or an offseted constraint which are
+ always added to the rhs node's constraints. */
+
+ if (c->rhs.type == DEREF)
+ c->rhs.var = to;
+ else if (c->lhs.type == DEREF)
+ c->lhs.var = to;
+ else
+ c->rhs.var = to;
+ }
+ constraint_set_union (&graph->complex[to], &graph->complex[from]);
+ VEC_free (constraint_t, heap, graph->complex[from]);
+ graph->complex[from] = NULL;
+}
+
+
+/* Remove edges involving NODE from GRAPH. */
+
+static void
+clear_edges_for_node (constraint_graph_t graph, unsigned int node)
+{
+ if (graph->succs[node])
+ BITMAP_FREE (graph->succs[node]);
+}
+
+/* Merge GRAPH nodes FROM and TO into node TO. */
+
+static void
+merge_graph_nodes (constraint_graph_t graph, unsigned int to,
+ unsigned int from)
+{
+ if (graph->indirect_cycles[from] != -1)
+ {
+ /* If we have indirect cycles with the from node, and we have
+ none on the to node, the to node has indirect cycles from the
+ from node now that they are unified.
+ If indirect cycles exist on both, unify the nodes that they
+ are in a cycle with, since we know they are in a cycle with
+ each other. */
+ if (graph->indirect_cycles[to] == -1)
+ graph->indirect_cycles[to] = graph->indirect_cycles[from];
+ }
+
+ /* Merge all the successor edges. */
+ if (graph->succs[from])
+ {
+ if (!graph->succs[to])
+ graph->succs[to] = BITMAP_ALLOC (&pta_obstack);
+ bitmap_ior_into (graph->succs[to],
+ graph->succs[from]);
+ }
+
+ clear_edges_for_node (graph, from);
+}
+
+
+/* Add an indirect graph edge to GRAPH, going from TO to FROM if
+ it doesn't exist in the graph already. */
+
+static void
+add_implicit_graph_edge (constraint_graph_t graph, unsigned int to,
+ unsigned int from)
+{
+ if (to == from)
+ return;
+
+ if (!graph->implicit_preds[to])
+ graph->implicit_preds[to] = BITMAP_ALLOC (&predbitmap_obstack);
+
+ if (bitmap_set_bit (graph->implicit_preds[to], from))
+ stats.num_implicit_edges++;
+}
+
+/* Add a predecessor graph edge to GRAPH, going from TO to FROM if
+ it doesn't exist in the graph already.
+ Return false if the edge already existed, true otherwise. */
+
+static void
+add_pred_graph_edge (constraint_graph_t graph, unsigned int to,
+ unsigned int from)
+{
+ if (!graph->preds[to])
+ graph->preds[to] = BITMAP_ALLOC (&predbitmap_obstack);
+ bitmap_set_bit (graph->preds[to], from);
+}
+
+/* Add a graph edge to GRAPH, going from FROM to TO if
+ it doesn't exist in the graph already.
+ Return false if the edge already existed, true otherwise. */
+
+static bool
+add_graph_edge (constraint_graph_t graph, unsigned int to,
+ unsigned int from)
+{
+ if (to == from)
+ {
+ return false;
+ }
+ else
+ {
+ bool r = false;
+
+ if (!graph->succs[from])
+ graph->succs[from] = BITMAP_ALLOC (&pta_obstack);
+ if (bitmap_set_bit (graph->succs[from], to))
+ {
+ r = true;
+ if (to < FIRST_REF_NODE && from < FIRST_REF_NODE)
+ stats.num_edges++;
+ }
+ return r;
+ }
+}
+
+
+/* Return true if {DEST.SRC} is an existing graph edge in GRAPH. */
+
+static bool
+valid_graph_edge (constraint_graph_t graph, unsigned int src,
+ unsigned int dest)
+{
+ return (graph->succs[dest]
+ && bitmap_bit_p (graph->succs[dest], src));
+}
+
+/* Initialize the constraint graph structure to contain SIZE nodes. */
+
+static void
+init_graph (unsigned int size)
+{
+ unsigned int j;
+
+ graph = XCNEW (struct constraint_graph);
+ graph->size = size;
+ graph->succs = XCNEWVEC (bitmap, graph->size);
+ graph->indirect_cycles = XNEWVEC (int, graph->size);
+ graph->rep = XNEWVEC (unsigned int, graph->size);
+ graph->complex = XCNEWVEC (VEC(constraint_t, heap) *, size);
+ graph->pe = XCNEWVEC (unsigned int, graph->size);
+ graph->pe_rep = XNEWVEC (int, graph->size);
+
+ for (j = 0; j < graph->size; j++)
+ {
+ graph->rep[j] = j;
+ graph->pe_rep[j] = -1;
+ graph->indirect_cycles[j] = -1;
+ }
+}
+
+/* Build the constraint graph, adding only predecessor edges right now. */
+
+static void
+build_pred_graph (void)
+{
+ int i;
+ constraint_t c;
+ unsigned int j;
+
+ graph->implicit_preds = XCNEWVEC (bitmap, graph->size);
+ graph->preds = XCNEWVEC (bitmap, graph->size);
+ graph->pointer_label = XCNEWVEC (unsigned int, graph->size);
+ graph->loc_label = XCNEWVEC (unsigned int, graph->size);
+ graph->pointed_by = XCNEWVEC (bitmap, graph->size);
+ graph->points_to = XCNEWVEC (bitmap, graph->size);
+ graph->eq_rep = XNEWVEC (int, graph->size);
+ graph->direct_nodes = sbitmap_alloc (graph->size);
+ graph->address_taken = BITMAP_ALLOC (&predbitmap_obstack);
+ sbitmap_zero (graph->direct_nodes);
+
+ for (j = 0; j < FIRST_REF_NODE; j++)
+ {
+ if (!get_varinfo (j)->is_special_var)
+ SET_BIT (graph->direct_nodes, j);
+ }
+
+ for (j = 0; j < graph->size; j++)
+ graph->eq_rep[j] = -1;
+
+ for (j = 0; j < VEC_length (varinfo_t, varmap); j++)
+ graph->indirect_cycles[j] = -1;
+
+ for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
+ {
+ struct constraint_expr lhs = c->lhs;
+ struct constraint_expr rhs = c->rhs;
+ unsigned int lhsvar = get_varinfo_fc (lhs.var)->id;
+ unsigned int rhsvar = get_varinfo_fc (rhs.var)->id;
+
+ if (lhs.type == DEREF)
+ {
+ /* *x = y. */
+ if (rhs.offset == 0 && lhs.offset == 0 && rhs.type == SCALAR)
+ add_pred_graph_edge (graph, FIRST_REF_NODE + lhsvar, rhsvar);
+ }
+ else if (rhs.type == DEREF)
+ {
+ /* x = *y */
+ if (rhs.offset == 0 && lhs.offset == 0 && lhs.type == SCALAR)
+ add_pred_graph_edge (graph, lhsvar, FIRST_REF_NODE + rhsvar);
+ else
+ RESET_BIT (graph->direct_nodes, lhsvar);
+ }
+ else if (rhs.type == ADDRESSOF)
+ {
+ varinfo_t v;
+
+ /* x = &y */
+ if (graph->points_to[lhsvar] == NULL)
+ graph->points_to[lhsvar] = BITMAP_ALLOC (&predbitmap_obstack);
+ bitmap_set_bit (graph->points_to[lhsvar], rhsvar);
+
+ if (graph->pointed_by[rhsvar] == NULL)
+ graph->pointed_by[rhsvar] = BITMAP_ALLOC (&predbitmap_obstack);
+ bitmap_set_bit (graph->pointed_by[rhsvar], lhsvar);
+
+ /* Implicitly, *x = y */
+ add_implicit_graph_edge (graph, FIRST_REF_NODE + lhsvar, rhsvar);
+
+ /* All related variables are no longer direct nodes. */
+ RESET_BIT (graph->direct_nodes, rhsvar);
+ v = get_varinfo (rhsvar);
+ if (!v->is_full_var)
+ {
+ v = lookup_vi_for_tree (v->decl);
+ do
+ {
+ RESET_BIT (graph->direct_nodes, v->id);
+ v = v->next;
+ }
+ while (v != NULL);
+ }
+ bitmap_set_bit (graph->address_taken, rhsvar);
+ }
+ else if (lhsvar > anything_id
+ && lhsvar != rhsvar && lhs.offset == 0 && rhs.offset == 0)
+ {
+ /* x = y */
+ add_pred_graph_edge (graph, lhsvar, rhsvar);
+ /* Implicitly, *x = *y */
+ add_implicit_graph_edge (graph, FIRST_REF_NODE + lhsvar,
+ FIRST_REF_NODE + rhsvar);
+ }
+ else if (lhs.offset != 0 || rhs.offset != 0)
+ {
+ if (rhs.offset != 0)
+ RESET_BIT (graph->direct_nodes, lhs.var);
+ else if (lhs.offset != 0)
+ RESET_BIT (graph->direct_nodes, rhs.var);
+ }
+ }
+}
+
+/* Build the constraint graph, adding successor edges. */
+
+static void
+build_succ_graph (void)
+{
+ unsigned i, t;
+ constraint_t c;
+
+ for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
+ {
+ struct constraint_expr lhs;
+ struct constraint_expr rhs;
+ unsigned int lhsvar;
+ unsigned int rhsvar;
+
+ if (!c)
+ continue;
+
+ lhs = c->lhs;
+ rhs = c->rhs;
+ lhsvar = find (get_varinfo_fc (lhs.var)->id);
+ rhsvar = find (get_varinfo_fc (rhs.var)->id);
+
+ if (lhs.type == DEREF)
+ {
+ if (rhs.offset == 0 && lhs.offset == 0 && rhs.type == SCALAR)
+ add_graph_edge (graph, FIRST_REF_NODE + lhsvar, rhsvar);
+ }
+ else if (rhs.type == DEREF)
+ {
+ if (rhs.offset == 0 && lhs.offset == 0 && lhs.type == SCALAR)
+ add_graph_edge (graph, lhsvar, FIRST_REF_NODE + rhsvar);
+ }
+ else if (rhs.type == ADDRESSOF)
+ {
+ /* x = &y */
+ gcc_assert (find (get_varinfo_fc (rhs.var)->id)
+ == get_varinfo_fc (rhs.var)->id);
+ bitmap_set_bit (get_varinfo (lhsvar)->solution, rhsvar);
+ }
+ else if (lhsvar > anything_id
+ && lhsvar != rhsvar && lhs.offset == 0 && rhs.offset == 0)
+ {
+ add_graph_edge (graph, lhsvar, rhsvar);
+ }
+ }
+
+ /* Add edges from STOREDANYTHING to all non-direct nodes. */
+ t = find (storedanything_id);
+ for (i = integer_id + 1; i < FIRST_REF_NODE; ++i)
+ {
+ if (!TEST_BIT (graph->direct_nodes, i))
+ add_graph_edge (graph, find (i), t);
+ }
+}
+
+
+/* Changed variables on the last iteration. */
+static unsigned int changed_count;
+static sbitmap changed;
+
+DEF_VEC_I(unsigned);
+DEF_VEC_ALLOC_I(unsigned,heap);
+
+
+/* Strongly Connected Component visitation info. */
+
+struct scc_info
+{
+ sbitmap visited;
+ sbitmap deleted;
+ unsigned int *dfs;
+ unsigned int *node_mapping;
+ int current_index;
+ VEC(unsigned,heap) *scc_stack;
+};
+
+
+/* Recursive routine to find strongly connected components in GRAPH.
+ SI is the SCC info to store the information in, and N is the id of current
+ graph node we are processing.
+
+ This is Tarjan's strongly connected component finding algorithm, as
+ modified by Nuutila to keep only non-root nodes on the stack.
+ The algorithm can be found in "On finding the strongly connected
+ connected components in a directed graph" by Esko Nuutila and Eljas
+ Soisalon-Soininen, in Information Processing Letters volume 49,
+ number 1, pages 9-14. */
+
+static void
+scc_visit (constraint_graph_t graph, struct scc_info *si, unsigned int n)
+{
+ unsigned int i;
+ bitmap_iterator bi;
+ unsigned int my_dfs;
+
+ SET_BIT (si->visited, n);
+ si->dfs[n] = si->current_index ++;
+ my_dfs = si->dfs[n];
+
+ /* Visit all the successors. */
+ EXECUTE_IF_IN_NONNULL_BITMAP (graph->succs[n], 0, i, bi)
+ {
+ unsigned int w;
+
+ if (i > LAST_REF_NODE)
+ break;
+
+ w = find (i);
+ if (TEST_BIT (si->deleted, w))
+ continue;
+
+ if (!TEST_BIT (si->visited, w))
+ scc_visit (graph, si, w);
+ {
+ unsigned int t = find (w);
+ unsigned int nnode = find (n);
+ gcc_assert (nnode == n);
+
+ if (si->dfs[t] < si->dfs[nnode])
+ si->dfs[n] = si->dfs[t];
+ }
+ }
+
+ /* See if any components have been identified. */
+ if (si->dfs[n] == my_dfs)
+ {
+ if (VEC_length (unsigned, si->scc_stack) > 0
+ && si->dfs[VEC_last (unsigned, si->scc_stack)] >= my_dfs)
+ {
+ bitmap scc = BITMAP_ALLOC (NULL);
+ bool have_ref_node = n >= FIRST_REF_NODE;
+ unsigned int lowest_node;
+ bitmap_iterator bi;
+
+ bitmap_set_bit (scc, n);
+
+ while (VEC_length (unsigned, si->scc_stack) != 0
+ && si->dfs[VEC_last (unsigned, si->scc_stack)] >= my_dfs)
+ {
+ unsigned int w = VEC_pop (unsigned, si->scc_stack);
+
+ bitmap_set_bit (scc, w);
+ if (w >= FIRST_REF_NODE)
+ have_ref_node = true;
+ }
+
+ lowest_node = bitmap_first_set_bit (scc);
+ gcc_assert (lowest_node < FIRST_REF_NODE);
+
+ /* Collapse the SCC nodes into a single node, and mark the
+ indirect cycles. */
+ EXECUTE_IF_SET_IN_BITMAP (scc, 0, i, bi)
+ {
+ if (i < FIRST_REF_NODE)
+ {
+ if (unite (lowest_node, i))
+ unify_nodes (graph, lowest_node, i, false);
+ }
+ else
+ {
+ unite (lowest_node, i);
+ graph->indirect_cycles[i - FIRST_REF_NODE] = lowest_node;
+ }
+ }
+ }
+ SET_BIT (si->deleted, n);
+ }
+ else
+ VEC_safe_push (unsigned, heap, si->scc_stack, n);
+}
+
+/* Unify node FROM into node TO, updating the changed count if
+ necessary when UPDATE_CHANGED is true. */
+
+static void
+unify_nodes (constraint_graph_t graph, unsigned int to, unsigned int from,
+ bool update_changed)
+{
+
+ gcc_assert (to != from && find (to) == to);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Unifying %s to %s\n",
+ get_varinfo (from)->name,
+ get_varinfo (to)->name);
+
+ if (update_changed)
+ stats.unified_vars_dynamic++;
+ else
+ stats.unified_vars_static++;
+
+ merge_graph_nodes (graph, to, from);
+ merge_node_constraints (graph, to, from);
+
+ if (get_varinfo (from)->no_tbaa_pruning)
+ get_varinfo (to)->no_tbaa_pruning = true;
+
+ /* Mark TO as changed if FROM was changed. If TO was already marked
+ as changed, decrease the changed count. */
+
+ if (update_changed && TEST_BIT (changed, from))
+ {
+ RESET_BIT (changed, from);
+ if (!TEST_BIT (changed, to))
+ SET_BIT (changed, to);
+ else
+ {
+ gcc_assert (changed_count > 0);
+ changed_count--;
+ }
+ }
+ if (get_varinfo (from)->solution)
+ {
+ /* If the solution changes because of the merging, we need to mark
+ the variable as changed. */
+ if (bitmap_ior_into (get_varinfo (to)->solution,
+ get_varinfo (from)->solution))
+ {
+ if (update_changed && !TEST_BIT (changed, to))
+ {
+ SET_BIT (changed, to);
+ changed_count++;
+ }
+ }
+
+ BITMAP_FREE (get_varinfo (from)->solution);
+ BITMAP_FREE (get_varinfo (from)->oldsolution);
+
+ if (stats.iterations > 0)
+ {
+ BITMAP_FREE (get_varinfo (to)->oldsolution);
+ get_varinfo (to)->oldsolution = BITMAP_ALLOC (&oldpta_obstack);
+ }
+ }
+ if (valid_graph_edge (graph, to, to))
+ {
+ if (graph->succs[to])
+ bitmap_clear_bit (graph->succs[to], to);
+ }
+}
+
+/* Information needed to compute the topological ordering of a graph. */
+
+struct topo_info
+{
+ /* sbitmap of visited nodes. */
+ sbitmap visited;
+ /* Array that stores the topological order of the graph, *in
+ reverse*. */
+ VEC(unsigned,heap) *topo_order;
+};
+
+
+/* Initialize and return a topological info structure. */
+
+static struct topo_info *
+init_topo_info (void)
+{
+ size_t size = graph->size;
+ struct topo_info *ti = XNEW (struct topo_info);
+ ti->visited = sbitmap_alloc (size);
+ sbitmap_zero (ti->visited);
+ ti->topo_order = VEC_alloc (unsigned, heap, 1);
+ return ti;
+}
+
+
+/* Free the topological sort info pointed to by TI. */
+
+static void
+free_topo_info (struct topo_info *ti)
+{
+ sbitmap_free (ti->visited);
+ VEC_free (unsigned, heap, ti->topo_order);
+ free (ti);
+}
+
+/* Visit the graph in topological order, and store the order in the
+ topo_info structure. */
+
+static void
+topo_visit (constraint_graph_t graph, struct topo_info *ti,
+ unsigned int n)
+{
+ bitmap_iterator bi;
+ unsigned int j;
+
+ SET_BIT (ti->visited, n);
+
+ if (graph->succs[n])
+ EXECUTE_IF_SET_IN_BITMAP (graph->succs[n], 0, j, bi)
+ {
+ if (!TEST_BIT (ti->visited, j))
+ topo_visit (graph, ti, j);
+ }
+
+ VEC_safe_push (unsigned, heap, ti->topo_order, n);
+}
+
+/* Return true if variable N + OFFSET is a legal field of N. */
+
+static bool
+type_safe (unsigned int n, unsigned HOST_WIDE_INT *offset)
+{
+ varinfo_t ninfo = get_varinfo (n);
+
+ /* For things we've globbed to single variables, any offset into the
+ variable acts like the entire variable, so that it becomes offset
+ 0. */
+ if (ninfo->is_special_var
+ || ninfo->is_artificial_var
+ || ninfo->is_unknown_size_var
+ || ninfo->is_full_var)
+ {
+ *offset = 0;
+ return true;
+ }
+ return (get_varinfo (n)->offset + *offset) < get_varinfo (n)->fullsize;
+}
+
+/* Process a constraint C that represents x = *y, using DELTA as the
+ starting solution. */
+
+static void
+do_sd_constraint (constraint_graph_t graph, constraint_t c,
+ bitmap delta)
+{
+ unsigned int lhs = c->lhs.var;
+ bool flag = false;
+ bitmap sol = get_varinfo (lhs)->solution;
+ unsigned int j;
+ bitmap_iterator bi;
+
+ /* For x = *ESCAPED and x = *CALLUSED we want to compute the
+ reachability set of the rhs var. As a pointer to a sub-field
+ of a variable can also reach all other fields of the variable
+ we simply have to expand the solution to contain all sub-fields
+ if one sub-field is contained. */
+ if (c->rhs.var == find (escaped_id)
+ || c->rhs.var == find (callused_id))
+ {
+ bitmap vars = NULL;
+ /* In a first pass record all variables we need to add all
+ sub-fields off. This avoids quadratic behavior. */
+ EXECUTE_IF_SET_IN_BITMAP (delta, 0, j, bi)
+ {
+ varinfo_t v = get_varinfo (j);
+ if (v->is_full_var)
+ continue;
+
+ v = lookup_vi_for_tree (v->decl);
+ if (v->next != NULL)
+ {
+ if (vars == NULL)
+ vars = BITMAP_ALLOC (NULL);
+ bitmap_set_bit (vars, v->id);
+ }
+ }
+ /* In the second pass now do the addition to the solution and
+ to speed up solving add it to the delta as well. */
+ if (vars != NULL)
+ {
+ EXECUTE_IF_SET_IN_BITMAP (vars, 0, j, bi)
+ {
+ varinfo_t v = get_varinfo (j);
+ for (; v != NULL; v = v->next)
+ {
+ if (bitmap_set_bit (sol, v->id))
+ {
+ flag = true;
+ bitmap_set_bit (delta, v->id);
+ }
+ }
+ }
+ BITMAP_FREE (vars);
+ }
+ }
+
+ if (bitmap_bit_p (delta, anything_id))
+ {
+ flag |= bitmap_set_bit (sol, anything_id);
+ goto done;
+ }
+
+ /* For each variable j in delta (Sol(y)), add
+ an edge in the graph from j to x, and union Sol(j) into Sol(x). */
+ EXECUTE_IF_SET_IN_BITMAP (delta, 0, j, bi)
+ {
+ unsigned HOST_WIDE_INT roffset = c->rhs.offset;
+ if (type_safe (j, &roffset))
+ {
+ varinfo_t v;
+ unsigned HOST_WIDE_INT fieldoffset = get_varinfo (j)->offset + roffset;
+ unsigned int t;
+
+ v = first_vi_for_offset (get_varinfo (j), fieldoffset);
+ /* If the access is outside of the variable we can ignore it. */
+ if (!v)
+ continue;
+ t = find (v->id);
+
+ /* Adding edges from the special vars is pointless.
+ They don't have sets that can change. */
+ if (get_varinfo (t)->is_special_var)
+ flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
+ /* Merging the solution from ESCAPED needlessly increases
+ the set. Use ESCAPED as representative instead.
+ Same for CALLUSED. */
+ else if (get_varinfo (t)->id == find (escaped_id))
+ flag |= bitmap_set_bit (sol, escaped_id);
+ else if (get_varinfo (t)->id == find (callused_id))
+ flag |= bitmap_set_bit (sol, callused_id);
+ else if (add_graph_edge (graph, lhs, t))
+ flag |= bitmap_ior_into (sol, get_varinfo (t)->solution);
+ }
+ }
+
+done:
+ /* If the LHS solution changed, mark the var as changed. */
+ if (flag)
+ {
+ get_varinfo (lhs)->solution = sol;
+ if (!TEST_BIT (changed, lhs))
+ {
+ SET_BIT (changed, lhs);
+ changed_count++;
+ }
+ }
+}
+
+/* Process a constraint C that represents *x = y. */
+
+static void
+do_ds_constraint (constraint_t c, bitmap delta)
+{
+ unsigned int rhs = c->rhs.var;
+ bitmap sol = get_varinfo (rhs)->solution;
+ unsigned int j;
+ bitmap_iterator bi;
+
+ /* Our IL does not allow this. */
+ gcc_assert (c->rhs.offset == 0);
+
+ /* If the solution of y contains ANYTHING simply use the ANYTHING
+ solution. This avoids needlessly increasing the points-to sets. */
+ if (bitmap_bit_p (sol, anything_id))
+ sol = get_varinfo (find (anything_id))->solution;
+
+ /* If the solution for x contains ANYTHING we have to merge the
+ solution of y into all pointer variables which we do via
+ STOREDANYTHING. */
+ if (bitmap_bit_p (delta, anything_id))
+ {
+ unsigned t = find (storedanything_id);
+ if (add_graph_edge (graph, t, rhs))
+ {
+ if (bitmap_ior_into (get_varinfo (t)->solution, sol))
+ {
+ if (!TEST_BIT (changed, t))
+ {
+ SET_BIT (changed, t);
+ changed_count++;
+ }
+ }
+ }
+ return;
+ }
+
+ /* For each member j of delta (Sol(x)), add an edge from y to j and
+ union Sol(y) into Sol(j) */
+ EXECUTE_IF_SET_IN_BITMAP (delta, 0, j, bi)
+ {
+ unsigned HOST_WIDE_INT loff = c->lhs.offset;
+ if (type_safe (j, &loff) && !(get_varinfo (j)->is_special_var))
+ {
+ varinfo_t v;
+ unsigned int t;
+ unsigned HOST_WIDE_INT fieldoffset = get_varinfo (j)->offset + loff;
+
+ v = first_vi_for_offset (get_varinfo (j), fieldoffset);
+ /* If the access is outside of the variable we can ignore it. */
+ if (!v)
+ continue;
+
+ if (v->may_have_pointers)
+ {
+ t = find (v->id);
+ if (add_graph_edge (graph, t, rhs))
+ {
+ if (bitmap_ior_into (get_varinfo (t)->solution, sol))
+ {
+ if (t == rhs)
+ sol = get_varinfo (rhs)->solution;
+ if (!TEST_BIT (changed, t))
+ {
+ SET_BIT (changed, t);
+ changed_count++;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Handle a non-simple (simple meaning requires no iteration),
+ constraint (IE *x = &y, x = *y, *x = y, and x = y with offsets involved). */
+
+static void
+do_complex_constraint (constraint_graph_t graph, constraint_t c, bitmap delta)
+{
+ if (c->lhs.type == DEREF)
+ {
+ if (c->rhs.type == ADDRESSOF)
+ {
+ gcc_unreachable();
+ }
+ else
+ {
+ /* *x = y */
+ do_ds_constraint (c, delta);
+ }
+ }
+ else if (c->rhs.type == DEREF)
+ {
+ /* x = *y */
+ if (!(get_varinfo (c->lhs.var)->is_special_var))
+ do_sd_constraint (graph, c, delta);
+ }
+ else
+ {
+ bitmap tmp;
+ bitmap solution;
+ bool flag = false;
+
+ gcc_assert (c->rhs.type == SCALAR && c->lhs.type == SCALAR);
+ solution = get_varinfo (c->rhs.var)->solution;
+ tmp = get_varinfo (c->lhs.var)->solution;
+
+ flag = set_union_with_increment (tmp, solution, c->rhs.offset);
+
+ if (flag)
+ {
+ get_varinfo (c->lhs.var)->solution = tmp;
+ if (!TEST_BIT (changed, c->lhs.var))
+ {
+ SET_BIT (changed, c->lhs.var);
+ changed_count++;
+ }
+ }
+ }
+}
+
+/* Initialize and return a new SCC info structure. */
+
+static struct scc_info *
+init_scc_info (size_t size)
+{
+ struct scc_info *si = XNEW (struct scc_info);
+ size_t i;
+
+ si->current_index = 0;
+ si->visited = sbitmap_alloc (size);
+ sbitmap_zero (si->visited);
+ si->deleted = sbitmap_alloc (size);
+ sbitmap_zero (si->deleted);
+ si->node_mapping = XNEWVEC (unsigned int, size);
+ si->dfs = XCNEWVEC (unsigned int, size);
+
+ for (i = 0; i < size; i++)
+ si->node_mapping[i] = i;
+
+ si->scc_stack = VEC_alloc (unsigned, heap, 1);
+ return si;
+}
+
+/* Free an SCC info structure pointed to by SI */
+
+static void
+free_scc_info (struct scc_info *si)
+{
+ sbitmap_free (si->visited);
+ sbitmap_free (si->deleted);
+ free (si->node_mapping);
+ free (si->dfs);
+ VEC_free (unsigned, heap, si->scc_stack);
+ free (si);
+}
+
+
+/* Find indirect cycles in GRAPH that occur, using strongly connected
+ components, and note them in the indirect cycles map.
+
+ This technique comes from Ben Hardekopf and Calvin Lin,
+ "It Pays to be Lazy: Fast and Accurate Pointer Analysis for Millions of
+ Lines of Code", submitted to PLDI 2007. */
+
+static void
+find_indirect_cycles (constraint_graph_t graph)
+{
+ unsigned int i;
+ unsigned int size = graph->size;
+ struct scc_info *si = init_scc_info (size);
+
+ for (i = 0; i < MIN (LAST_REF_NODE, size); i ++ )
+ if (!TEST_BIT (si->visited, i) && find (i) == i)
+ scc_visit (graph, si, i);
+
+ free_scc_info (si);
+}
+
+/* Compute a topological ordering for GRAPH, and store the result in the
+ topo_info structure TI. */
+
+static void
+compute_topo_order (constraint_graph_t graph,
+ struct topo_info *ti)
+{
+ unsigned int i;
+ unsigned int size = graph->size;
+
+ for (i = 0; i != size; ++i)
+ if (!TEST_BIT (ti->visited, i) && find (i) == i)
+ topo_visit (graph, ti, i);
+}
+
+/* Structure used to for hash value numbering of pointer equivalence
+ classes. */
+
+typedef struct equiv_class_label
+{
+ hashval_t hashcode;
+ unsigned int equivalence_class;
+ bitmap labels;
+} *equiv_class_label_t;
+typedef const struct equiv_class_label *const_equiv_class_label_t;
+
+/* A hashtable for mapping a bitmap of labels->pointer equivalence
+ classes. */
+static htab_t pointer_equiv_class_table;
+
+/* A hashtable for mapping a bitmap of labels->location equivalence
+ classes. */
+static htab_t location_equiv_class_table;
+
+/* Hash function for a equiv_class_label_t */
+
+static hashval_t
+equiv_class_label_hash (const void *p)
+{
+ const_equiv_class_label_t const ecl = (const_equiv_class_label_t) p;
+ return ecl->hashcode;
+}
+
+/* Equality function for two equiv_class_label_t's. */
+
+static int
+equiv_class_label_eq (const void *p1, const void *p2)
+{
+ const_equiv_class_label_t const eql1 = (const_equiv_class_label_t) p1;
+ const_equiv_class_label_t const eql2 = (const_equiv_class_label_t) p2;
+ return bitmap_equal_p (eql1->labels, eql2->labels);
+}
+
+/* Lookup a equivalence class in TABLE by the bitmap of LABELS it
+ contains. */
+
+static unsigned int
+equiv_class_lookup (htab_t table, bitmap labels)
+{
+ void **slot;
+ struct equiv_class_label ecl;
+
+ ecl.labels = labels;
+ ecl.hashcode = bitmap_hash (labels);
+
+ slot = htab_find_slot_with_hash (table, &ecl,
+ ecl.hashcode, NO_INSERT);
+ if (!slot)
+ return 0;
+ else
+ return ((equiv_class_label_t) *slot)->equivalence_class;
+}
+
+
+/* Add an equivalence class named EQUIVALENCE_CLASS with labels LABELS
+ to TABLE. */
+
+static void
+equiv_class_add (htab_t table, unsigned int equivalence_class,
+ bitmap labels)
+{
+ void **slot;
+ equiv_class_label_t ecl = XNEW (struct equiv_class_label);
+
+ ecl->labels = labels;
+ ecl->equivalence_class = equivalence_class;
+ ecl->hashcode = bitmap_hash (labels);
+
+ slot = htab_find_slot_with_hash (table, ecl,
+ ecl->hashcode, INSERT);
+ gcc_assert (!*slot);
+ *slot = (void *) ecl;
+}
+
+/* Perform offline variable substitution.
+
+ This is a worst case quadratic time way of identifying variables
+ that must have equivalent points-to sets, including those caused by
+ static cycles, and single entry subgraphs, in the constraint graph.
+
+ The technique is described in "Exploiting Pointer and Location
+ Equivalence to Optimize Pointer Analysis. In the 14th International
+ Static Analysis Symposium (SAS), August 2007." It is known as the
+ "HU" algorithm, and is equivalent to value numbering the collapsed
+ constraint graph including evaluating unions.
+
+ The general method of finding equivalence classes is as follows:
+ Add fake nodes (REF nodes) and edges for *a = b and a = *b constraints.
+ Initialize all non-REF nodes to be direct nodes.
+ For each constraint a = a U {b}, we set pts(a) = pts(a) u {fresh
+ variable}
+ For each constraint containing the dereference, we also do the same
+ thing.
+
+ We then compute SCC's in the graph and unify nodes in the same SCC,
+ including pts sets.
+
+ For each non-collapsed node x:
+ Visit all unvisited explicit incoming edges.
+ Ignoring all non-pointers, set pts(x) = Union of pts(a) for y
+ where y->x.
+ Lookup the equivalence class for pts(x).
+ If we found one, equivalence_class(x) = found class.
+ Otherwise, equivalence_class(x) = new class, and new_class is
+ added to the lookup table.
+
+ All direct nodes with the same equivalence class can be replaced
+ with a single representative node.
+ All unlabeled nodes (label == 0) are not pointers and all edges
+ involving them can be eliminated.
+ We perform these optimizations during rewrite_constraints
+
+ In addition to pointer equivalence class finding, we also perform
+ location equivalence class finding. This is the set of variables
+ that always appear together in points-to sets. We use this to
+ compress the size of the points-to sets. */
+
+/* Current maximum pointer equivalence class id. */
+static int pointer_equiv_class;
+
+/* Current maximum location equivalence class id. */
+static int location_equiv_class;
+
+/* Recursive routine to find strongly connected components in GRAPH,
+ and label it's nodes with DFS numbers. */
+
+static void
+condense_visit (constraint_graph_t graph, struct scc_info *si, unsigned int n)
+{
+ unsigned int i;
+ bitmap_iterator bi;
+ unsigned int my_dfs;
+
+ gcc_assert (si->node_mapping[n] == n);
+ SET_BIT (si->visited, n);
+ si->dfs[n] = si->current_index ++;
+ my_dfs = si->dfs[n];
+
+ /* Visit all the successors. */
+ EXECUTE_IF_IN_NONNULL_BITMAP (graph->preds[n], 0, i, bi)
+ {
+ unsigned int w = si->node_mapping[i];
+
+ if (TEST_BIT (si->deleted, w))
+ continue;
+
+ if (!TEST_BIT (si->visited, w))
+ condense_visit (graph, si, w);
+ {
+ unsigned int t = si->node_mapping[w];
+ unsigned int nnode = si->node_mapping[n];
+ gcc_assert (nnode == n);
+
+ if (si->dfs[t] < si->dfs[nnode])
+ si->dfs[n] = si->dfs[t];
+ }
+ }
+
+ /* Visit all the implicit predecessors. */
+ EXECUTE_IF_IN_NONNULL_BITMAP (graph->implicit_preds[n], 0, i, bi)
+ {
+ unsigned int w = si->node_mapping[i];
+
+ if (TEST_BIT (si->deleted, w))
+ continue;
+
+ if (!TEST_BIT (si->visited, w))
+ condense_visit (graph, si, w);
+ {
+ unsigned int t = si->node_mapping[w];
+ unsigned int nnode = si->node_mapping[n];
+ gcc_assert (nnode == n);
+
+ if (si->dfs[t] < si->dfs[nnode])
+ si->dfs[n] = si->dfs[t];
+ }
+ }
+
+ /* See if any components have been identified. */
+ if (si->dfs[n] == my_dfs)
+ {
+ while (VEC_length (unsigned, si->scc_stack) != 0
+ && si->dfs[VEC_last (unsigned, si->scc_stack)] >= my_dfs)
+ {
+ unsigned int w = VEC_pop (unsigned, si->scc_stack);
+ si->node_mapping[w] = n;
+
+ if (!TEST_BIT (graph->direct_nodes, w))
+ RESET_BIT (graph->direct_nodes, n);
+
+ /* Unify our nodes. */
+ if (graph->preds[w])
+ {
+ if (!graph->preds[n])
+ graph->preds[n] = BITMAP_ALLOC (&predbitmap_obstack);
+ bitmap_ior_into (graph->preds[n], graph->preds[w]);
+ }
+ if (graph->implicit_preds[w])
+ {
+ if (!graph->implicit_preds[n])
+ graph->implicit_preds[n] = BITMAP_ALLOC (&predbitmap_obstack);
+ bitmap_ior_into (graph->implicit_preds[n],
+ graph->implicit_preds[w]);
+ }
+ if (graph->points_to[w])
+ {
+ if (!graph->points_to[n])
+ graph->points_to[n] = BITMAP_ALLOC (&predbitmap_obstack);
+ bitmap_ior_into (graph->points_to[n],
+ graph->points_to[w]);
+ }
+ }
+ SET_BIT (si->deleted, n);
+ }
+ else
+ VEC_safe_push (unsigned, heap, si->scc_stack, n);
+}
+
+/* Label pointer equivalences. */
+
+static void
+label_visit (constraint_graph_t graph, struct scc_info *si, unsigned int n)
+{
+ unsigned int i;
+ bitmap_iterator bi;
+ SET_BIT (si->visited, n);
+
+ if (!graph->points_to[n])
+ graph->points_to[n] = BITMAP_ALLOC (&predbitmap_obstack);
+
+ /* Label and union our incoming edges's points to sets. */
+ EXECUTE_IF_IN_NONNULL_BITMAP (graph->preds[n], 0, i, bi)
+ {
+ unsigned int w = si->node_mapping[i];
+ if (!TEST_BIT (si->visited, w))
+ label_visit (graph, si, w);
+
+ /* Skip unused edges */
+ if (w == n || graph->pointer_label[w] == 0)
+ continue;
+
+ if (graph->points_to[w])
+ bitmap_ior_into(graph->points_to[n], graph->points_to[w]);
+ }
+ /* Indirect nodes get fresh variables. */
+ if (!TEST_BIT (graph->direct_nodes, n))
+ bitmap_set_bit (graph->points_to[n], FIRST_REF_NODE + n);
+
+ if (!bitmap_empty_p (graph->points_to[n]))
+ {
+ unsigned int label = equiv_class_lookup (pointer_equiv_class_table,
+ graph->points_to[n]);
+ if (!label)
+ {
+ label = pointer_equiv_class++;
+ equiv_class_add (pointer_equiv_class_table,
+ label, graph->points_to[n]);
+ }
+ graph->pointer_label[n] = label;
+ }
+}
+
+/* Perform offline variable substitution, discovering equivalence
+ classes, and eliminating non-pointer variables. */
+
+static struct scc_info *
+perform_var_substitution (constraint_graph_t graph)
+{
+ unsigned int i;
+ unsigned int size = graph->size;
+ struct scc_info *si = init_scc_info (size);
+
+ bitmap_obstack_initialize (&iteration_obstack);
+ pointer_equiv_class_table = htab_create (511, equiv_class_label_hash,
+ equiv_class_label_eq, free);
+ location_equiv_class_table = htab_create (511, equiv_class_label_hash,
+ equiv_class_label_eq, free);
+ pointer_equiv_class = 1;
+ location_equiv_class = 1;
+
+ /* Condense the nodes, which means to find SCC's, count incoming
+ predecessors, and unite nodes in SCC's. */
+ for (i = 0; i < FIRST_REF_NODE; i++)
+ if (!TEST_BIT (si->visited, si->node_mapping[i]))
+ condense_visit (graph, si, si->node_mapping[i]);
+
+ sbitmap_zero (si->visited);
+ /* Actually the label the nodes for pointer equivalences */
+ for (i = 0; i < FIRST_REF_NODE; i++)
+ if (!TEST_BIT (si->visited, si->node_mapping[i]))
+ label_visit (graph, si, si->node_mapping[i]);
+
+ /* Calculate location equivalence labels. */
+ for (i = 0; i < FIRST_REF_NODE; i++)
+ {
+ bitmap pointed_by;
+ bitmap_iterator bi;
+ unsigned int j;
+ unsigned int label;
+
+ if (!graph->pointed_by[i])
+ continue;
+ pointed_by = BITMAP_ALLOC (&iteration_obstack);
+
+ /* Translate the pointed-by mapping for pointer equivalence
+ labels. */
+ EXECUTE_IF_SET_IN_BITMAP (graph->pointed_by[i], 0, j, bi)
+ {
+ bitmap_set_bit (pointed_by,
+ graph->pointer_label[si->node_mapping[j]]);
+ }
+ /* The original pointed_by is now dead. */
+ BITMAP_FREE (graph->pointed_by[i]);
+
+ /* Look up the location equivalence label if one exists, or make
+ one otherwise. */
+ label = equiv_class_lookup (location_equiv_class_table,
+ pointed_by);
+ if (label == 0)
+ {
+ label = location_equiv_class++;
+ equiv_class_add (location_equiv_class_table,
+ label, pointed_by);
+ }
+ else
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Found location equivalence for node %s\n",
+ get_varinfo (i)->name);
+ BITMAP_FREE (pointed_by);
+ }
+ graph->loc_label[i] = label;
+
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ for (i = 0; i < FIRST_REF_NODE; i++)
+ {
+ bool direct_node = TEST_BIT (graph->direct_nodes, i);
+ fprintf (dump_file,
+ "Equivalence classes for %s node id %d:%s are pointer: %d"
+ ", location:%d\n",
+ direct_node ? "Direct node" : "Indirect node", i,
+ get_varinfo (i)->name,
+ graph->pointer_label[si->node_mapping[i]],
+ graph->loc_label[si->node_mapping[i]]);
+ }
+
+ /* Quickly eliminate our non-pointer variables. */
+
+ for (i = 0; i < FIRST_REF_NODE; i++)
+ {
+ unsigned int node = si->node_mapping[i];
+
+ if (graph->pointer_label[node] == 0)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "%s is a non-pointer variable, eliminating edges.\n",
+ get_varinfo (node)->name);
+ stats.nonpointer_vars++;
+ clear_edges_for_node (graph, node);
+ }
+ }
+
+ return si;
+}
+
+/* Free information that was only necessary for variable
+ substitution. */
+
+static void
+free_var_substitution_info (struct scc_info *si)
+{
+ free_scc_info (si);
+ free (graph->pointer_label);
+ free (graph->loc_label);
+ free (graph->pointed_by);
+ free (graph->points_to);
+ free (graph->eq_rep);
+ sbitmap_free (graph->direct_nodes);
+ htab_delete (pointer_equiv_class_table);
+ htab_delete (location_equiv_class_table);
+ bitmap_obstack_release (&iteration_obstack);
+}
+
+/* Return an existing node that is equivalent to NODE, which has
+ equivalence class LABEL, if one exists. Return NODE otherwise. */
+
+static unsigned int
+find_equivalent_node (constraint_graph_t graph,
+ unsigned int node, unsigned int label)
+{
+ /* If the address version of this variable is unused, we can
+ substitute it for anything else with the same label.
+ Otherwise, we know the pointers are equivalent, but not the
+ locations, and we can unite them later. */
+
+ if (!bitmap_bit_p (graph->address_taken, node))
+ {
+ gcc_assert (label < graph->size);
+
+ if (graph->eq_rep[label] != -1)
+ {
+ /* Unify the two variables since we know they are equivalent. */
+ if (unite (graph->eq_rep[label], node))
+ unify_nodes (graph, graph->eq_rep[label], node, false);
+ return graph->eq_rep[label];
+ }
+ else
+ {
+ graph->eq_rep[label] = node;
+ graph->pe_rep[label] = node;
+ }
+ }
+ else
+ {
+ gcc_assert (label < graph->size);
+ graph->pe[node] = label;
+ if (graph->pe_rep[label] == -1)
+ graph->pe_rep[label] = node;
+ }
+
+ return node;
+}
+
+/* Unite pointer equivalent but not location equivalent nodes in
+ GRAPH. This may only be performed once variable substitution is
+ finished. */
+
+static void
+unite_pointer_equivalences (constraint_graph_t graph)
+{
+ unsigned int i;
+
+ /* Go through the pointer equivalences and unite them to their
+ representative, if they aren't already. */
+ for (i = 0; i < FIRST_REF_NODE; i++)
+ {
+ unsigned int label = graph->pe[i];
+ if (label)
+ {
+ int label_rep = graph->pe_rep[label];
+
+ if (label_rep == -1)
+ continue;
+
+ label_rep = find (label_rep);
+ if (label_rep >= 0 && unite (label_rep, find (i)))
+ unify_nodes (graph, label_rep, i, false);
+ }
+ }
+}
+
+/* Move complex constraints to the GRAPH nodes they belong to. */
+
+static void
+move_complex_constraints (constraint_graph_t graph)
+{
+ int i;
+ constraint_t c;
+
+ for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
+ {
+ if (c)
+ {
+ struct constraint_expr lhs = c->lhs;
+ struct constraint_expr rhs = c->rhs;
+
+ if (lhs.type == DEREF)
+ {
+ insert_into_complex (graph, lhs.var, c);
+ }
+ else if (rhs.type == DEREF)
+ {
+ if (!(get_varinfo (lhs.var)->is_special_var))
+ insert_into_complex (graph, rhs.var, c);
+ }
+ else if (rhs.type != ADDRESSOF && lhs.var > anything_id
+ && (lhs.offset != 0 || rhs.offset != 0))
+ {
+ insert_into_complex (graph, rhs.var, c);
+ }
+ }
+ }
+}
+
+
+/* Optimize and rewrite complex constraints while performing
+ collapsing of equivalent nodes. SI is the SCC_INFO that is the
+ result of perform_variable_substitution. */
+
+static void
+rewrite_constraints (constraint_graph_t graph,
+ struct scc_info *si)
+{
+ int i;
+ unsigned int j;
+ constraint_t c;
+
+ for (j = 0; j < graph->size; j++)
+ gcc_assert (find (j) == j);
+
+ for (i = 0; VEC_iterate (constraint_t, constraints, i, c); i++)
+ {
+ struct constraint_expr lhs = c->lhs;
+ struct constraint_expr rhs = c->rhs;
+ unsigned int lhsvar = find (get_varinfo_fc (lhs.var)->id);
+ unsigned int rhsvar = find (get_varinfo_fc (rhs.var)->id);
+ unsigned int lhsnode, rhsnode;
+ unsigned int lhslabel, rhslabel;
+
+ lhsnode = si->node_mapping[lhsvar];
+ rhsnode = si->node_mapping[rhsvar];
+ lhslabel = graph->pointer_label[lhsnode];
+ rhslabel = graph->pointer_label[rhsnode];
+
+ /* See if it is really a non-pointer variable, and if so, ignore
+ the constraint. */
+ if (lhslabel == 0)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+
+ fprintf (dump_file, "%s is a non-pointer variable,"
+ "ignoring constraint:",
+ get_varinfo (lhs.var)->name);
+ dump_constraint (dump_file, c);
+ }
+ VEC_replace (constraint_t, constraints, i, NULL);
+ continue;
+ }
+
+ if (rhslabel == 0)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+
+ fprintf (dump_file, "%s is a non-pointer variable,"
+ "ignoring constraint:",
+ get_varinfo (rhs.var)->name);
+ dump_constraint (dump_file, c);
+ }
+ VEC_replace (constraint_t, constraints, i, NULL);
+ continue;
+ }
+
+ lhsvar = find_equivalent_node (graph, lhsvar, lhslabel);
+ rhsvar = find_equivalent_node (graph, rhsvar, rhslabel);
+ c->lhs.var = lhsvar;
+ c->rhs.var = rhsvar;
+
+ }
+}
+
+/* Eliminate indirect cycles involving NODE. Return true if NODE was
+ part of an SCC, false otherwise. */
+
+static bool
+eliminate_indirect_cycles (unsigned int node)
+{
+ if (graph->indirect_cycles[node] != -1
+ && !bitmap_empty_p (get_varinfo (node)->solution))
+ {
+ unsigned int i;
+ VEC(unsigned,heap) *queue = NULL;
+ int queuepos;
+ unsigned int to = find (graph->indirect_cycles[node]);
+ bitmap_iterator bi;
+
+ /* We can't touch the solution set and call unify_nodes
+ at the same time, because unify_nodes is going to do
+ bitmap unions into it. */
+
+ EXECUTE_IF_SET_IN_BITMAP (get_varinfo (node)->solution, 0, i, bi)
+ {
+ if (find (i) == i && i != to)
+ {
+ if (unite (to, i))
+ VEC_safe_push (unsigned, heap, queue, i);
+ }
+ }
+
+ for (queuepos = 0;
+ VEC_iterate (unsigned, queue, queuepos, i);
+ queuepos++)
+ {
+ unify_nodes (graph, to, i, true);
+ }
+ VEC_free (unsigned, heap, queue);
+ return true;
+ }
+ return false;
+}
+
+/* Solve the constraint graph GRAPH using our worklist solver.
+ This is based on the PW* family of solvers from the "Efficient Field
+ Sensitive Pointer Analysis for C" paper.
+ It works by iterating over all the graph nodes, processing the complex
+ constraints and propagating the copy constraints, until everything stops
+ changed. This corresponds to steps 6-8 in the solving list given above. */
+
+static void
+solve_graph (constraint_graph_t graph)
+{
+ unsigned int size = graph->size;
+ unsigned int i;
+ bitmap pts;
+
+ changed_count = 0;
+ changed = sbitmap_alloc (size);
+ sbitmap_zero (changed);
+
+ /* Mark all initial non-collapsed nodes as changed. */
+ for (i = 0; i < size; i++)
+ {
+ varinfo_t ivi = get_varinfo (i);
+ if (find (i) == i && !bitmap_empty_p (ivi->solution)
+ && ((graph->succs[i] && !bitmap_empty_p (graph->succs[i]))
+ || VEC_length (constraint_t, graph->complex[i]) > 0))
+ {
+ SET_BIT (changed, i);
+ changed_count++;
+ }
+ }
+
+ /* Allocate a bitmap to be used to store the changed bits. */
+ pts = BITMAP_ALLOC (&pta_obstack);
+
+ while (changed_count > 0)
+ {
+ unsigned int i;
+ struct topo_info *ti = init_topo_info ();
+ stats.iterations++;
+
+ bitmap_obstack_initialize (&iteration_obstack);
+
+ compute_topo_order (graph, ti);
+
+ while (VEC_length (unsigned, ti->topo_order) != 0)
+ {
+
+ i = VEC_pop (unsigned, ti->topo_order);
+
+ /* If this variable is not a representative, skip it. */
+ if (find (i) != i)
+ continue;
+
+ /* In certain indirect cycle cases, we may merge this
+ variable to another. */
+ if (eliminate_indirect_cycles (i) && find (i) != i)
+ continue;
+
+ /* If the node has changed, we need to process the
+ complex constraints and outgoing edges again. */
+ if (TEST_BIT (changed, i))
+ {
+ unsigned int j;
+ constraint_t c;
+ bitmap solution;
+ VEC(constraint_t,heap) *complex = graph->complex[i];
+ bool solution_empty;
+
+ RESET_BIT (changed, i);
+ changed_count--;
+
+ /* Compute the changed set of solution bits. */
+ bitmap_and_compl (pts, get_varinfo (i)->solution,
+ get_varinfo (i)->oldsolution);
+
+ if (bitmap_empty_p (pts))
+ continue;
+
+ bitmap_ior_into (get_varinfo (i)->oldsolution, pts);
+
+ solution = get_varinfo (i)->solution;
+ solution_empty = bitmap_empty_p (solution);
+
+ /* Process the complex constraints */
+ for (j = 0; VEC_iterate (constraint_t, complex, j, c); j++)
+ {
+ /* XXX: This is going to unsort the constraints in
+ some cases, which will occasionally add duplicate
+ constraints during unification. This does not
+ affect correctness. */
+ c->lhs.var = find (c->lhs.var);
+ c->rhs.var = find (c->rhs.var);
+
+ /* The only complex constraint that can change our
+ solution to non-empty, given an empty solution,
+ is a constraint where the lhs side is receiving
+ some set from elsewhere. */
+ if (!solution_empty || c->lhs.type != DEREF)
+ do_complex_constraint (graph, c, pts);
+ }
+
+ solution_empty = bitmap_empty_p (solution);
+
+ if (!solution_empty
+ /* Do not propagate the ESCAPED/CALLUSED solutions. */
+ && i != find (escaped_id)
+ && i != find (callused_id))
+ {
+ bitmap_iterator bi;
+
+ /* Propagate solution to all successors. */
+ EXECUTE_IF_IN_NONNULL_BITMAP (graph->succs[i],
+ 0, j, bi)
+ {
+ bitmap tmp;
+ bool flag;
+
+ unsigned int to = find (j);
+ tmp = get_varinfo (to)->solution;
+ flag = false;
+
+ /* Don't try to propagate to ourselves. */
+ if (to == i)
+ continue;
+
+ flag = set_union_with_increment (tmp, pts, 0);
+
+ if (flag)
+ {
+ get_varinfo (to)->solution = tmp;
+ if (!TEST_BIT (changed, to))
+ {
+ SET_BIT (changed, to);
+ changed_count++;
+ }
+ }
+ }
+ }
+ }
+ }
+ free_topo_info (ti);
+ bitmap_obstack_release (&iteration_obstack);
+ }
+
+ BITMAP_FREE (pts);
+ sbitmap_free (changed);
+ bitmap_obstack_release (&oldpta_obstack);
+}
+
+/* Map from trees to variable infos. */
+static struct pointer_map_t *vi_for_tree;
+
+
+/* Insert ID as the variable id for tree T in the vi_for_tree map. */
+
+static void
+insert_vi_for_tree (tree t, varinfo_t vi)
+{
+ void **slot = pointer_map_insert (vi_for_tree, t);
+ gcc_assert (vi);
+ gcc_assert (*slot == NULL);
+ *slot = vi;
+}
+
+/* Find the variable info for tree T in VI_FOR_TREE. If T does not
+ exist in the map, return NULL, otherwise, return the varinfo we found. */
+
+static varinfo_t
+lookup_vi_for_tree (tree t)
+{
+ void **slot = pointer_map_contains (vi_for_tree, t);
+ if (slot == NULL)
+ return NULL;
+
+ return (varinfo_t) *slot;
+}
+
+/* Return a printable name for DECL */
+
+static const char *
+alias_get_name (tree decl)
+{
+ const char *res = get_name (decl);
+ char *temp;
+ int num_printed = 0;
+
+ if (res != NULL)
+ return res;
+
+ res = "NULL";
+ if (!dump_file)
+ return res;
+
+ if (TREE_CODE (decl) == SSA_NAME)
+ {
+ num_printed = asprintf (&temp, "%s_%u",
+ alias_get_name (SSA_NAME_VAR (decl)),
+ SSA_NAME_VERSION (decl));
+ }
+ else if (DECL_P (decl))
+ {
+ num_printed = asprintf (&temp, "D.%u", DECL_UID (decl));
+ }
+ if (num_printed > 0)
+ {
+ res = ggc_strdup (temp);
+ free (temp);
+ }
+ return res;
+}
+
+/* Find the variable id for tree T in the map.
+ If T doesn't exist in the map, create an entry for it and return it. */
+
+static varinfo_t
+get_vi_for_tree (tree t)
+{
+ void **slot = pointer_map_contains (vi_for_tree, t);
+ if (slot == NULL)
+ return get_varinfo (create_variable_info_for (t, alias_get_name (t)));
+
+ return (varinfo_t) *slot;
+}
+
+/* Get a constraint expression for a new temporary variable. */
+
+static struct constraint_expr
+get_constraint_exp_for_temp (tree t)
+{
+ struct constraint_expr cexpr;
+
+ gcc_assert (SSA_VAR_P (t));
+
+ cexpr.type = SCALAR;
+ cexpr.var = get_vi_for_tree (t)->id;
+ cexpr.offset = 0;
+
+ return cexpr;
+}
+
+/* Get a constraint expression vector from an SSA_VAR_P node.
+ If address_p is true, the result will be taken its address of. */
+
+static void
+get_constraint_for_ssa_var (tree t, VEC(ce_s, heap) **results, bool address_p)
+{
+ struct constraint_expr cexpr;
+ varinfo_t vi;
+
+ /* We allow FUNCTION_DECLs here even though it doesn't make much sense. */
+ gcc_assert (SSA_VAR_P (t) || DECL_P (t));
+
+ /* For parameters, get at the points-to set for the actual parm
+ decl. */
+ if (TREE_CODE (t) == SSA_NAME
+ && TREE_CODE (SSA_NAME_VAR (t)) == PARM_DECL
+ && SSA_NAME_IS_DEFAULT_DEF (t))
+ {
+ get_constraint_for_ssa_var (SSA_NAME_VAR (t), results, address_p);
+ return;
+ }
+
+ vi = get_vi_for_tree (t);
+ cexpr.var = vi->id;
+ cexpr.type = SCALAR;
+ cexpr.offset = 0;
+ /* If we determine the result is "anything", and we know this is readonly,
+ say it points to readonly memory instead. */
+ if (cexpr.var == anything_id && TREE_READONLY (t))
+ {
+ gcc_unreachable ();
+ cexpr.type = ADDRESSOF;
+ cexpr.var = readonly_id;
+ }
+
+ /* If we are not taking the address of the constraint expr, add all
+ sub-fiels of the variable as well. */
+ if (!address_p)
+ {
+ for (; vi; vi = vi->next)
+ {
+ cexpr.var = vi->id;
+ VEC_safe_push (ce_s, heap, *results, &cexpr);
+ }
+ return;
+ }
+
+ VEC_safe_push (ce_s, heap, *results, &cexpr);
+}
+
+/* Process constraint T, performing various simplifications and then
+ adding it to our list of overall constraints. */
+
+static void
+process_constraint (constraint_t t)
+{
+ struct constraint_expr rhs = t->rhs;
+ struct constraint_expr lhs = t->lhs;
+
+ gcc_assert (rhs.var < VEC_length (varinfo_t, varmap));
+ gcc_assert (lhs.var < VEC_length (varinfo_t, varmap));
+
+ /* ANYTHING == ANYTHING is pointless. */
+ if (lhs.var == anything_id && rhs.var == anything_id)
+ return;
+
+ /* If we have &ANYTHING = something, convert to SOMETHING = &ANYTHING) */
+ else if (lhs.var == anything_id && lhs.type == ADDRESSOF)
+ {
+ rhs = t->lhs;
+ t->lhs = t->rhs;
+ t->rhs = rhs;
+ process_constraint (t);
+ }
+ /* This can happen in our IR with things like n->a = *p */
+ else if (rhs.type == DEREF && lhs.type == DEREF && rhs.var != anything_id)
+ {
+ /* Split into tmp = *rhs, *lhs = tmp */
+ tree rhsdecl = get_varinfo (rhs.var)->decl;
+ tree pointertype = TREE_TYPE (rhsdecl);
+ tree pointedtotype = TREE_TYPE (pointertype);
+ tree tmpvar = create_tmp_var_raw (pointedtotype, "doubledereftmp");
+ struct constraint_expr tmplhs = get_constraint_exp_for_temp (tmpvar);
+
+ process_constraint (new_constraint (tmplhs, rhs));
+ process_constraint (new_constraint (lhs, tmplhs));
+ }
+ else if (rhs.type == ADDRESSOF && lhs.type == DEREF)
+ {
+ /* Split into tmp = &rhs, *lhs = tmp */
+ tree rhsdecl = get_varinfo (rhs.var)->decl;
+ tree pointertype = TREE_TYPE (rhsdecl);
+ tree tmpvar = create_tmp_var_raw (pointertype, "derefaddrtmp");
+ struct constraint_expr tmplhs = get_constraint_exp_for_temp (tmpvar);
+
+ process_constraint (new_constraint (tmplhs, rhs));
+ process_constraint (new_constraint (lhs, tmplhs));
+ }
+ else
+ {
+ gcc_assert (rhs.type != ADDRESSOF || rhs.offset == 0);
+ VEC_safe_push (constraint_t, heap, constraints, t);
+ }
+}
+
+/* Return true if T is a type that could contain pointers. */
+
+static bool
+type_could_have_pointers (tree type)
+{
+ if (POINTER_TYPE_P (type))
+ return true;
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ return type_could_have_pointers (TREE_TYPE (type));
+
+ return AGGREGATE_TYPE_P (type);
+}
+
+/* Return true if T is a variable of a type that could contain
+ pointers. */
+
+static bool
+could_have_pointers (tree t)
+{
+ return type_could_have_pointers (TREE_TYPE (t));
+}
+
+/* Return the position, in bits, of FIELD_DECL from the beginning of its
+ structure. */
+
+static HOST_WIDE_INT
+bitpos_of_field (const tree fdecl)
+{
+
+ if (!host_integerp (DECL_FIELD_OFFSET (fdecl), 0)
+ || !host_integerp (DECL_FIELD_BIT_OFFSET (fdecl), 0))
+ return -1;
+
+ return (TREE_INT_CST_LOW (DECL_FIELD_OFFSET (fdecl)) * 8
+ + TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (fdecl)));
+}
+
+
+/* Get constraint expressions for offsetting PTR by OFFSET. Stores the
+ resulting constraint expressions in *RESULTS. */
+
+static void
+get_constraint_for_ptr_offset (tree ptr, tree offset,
+ VEC (ce_s, heap) **results)
+{
+ struct constraint_expr c;
+ unsigned int j, n;
+ unsigned HOST_WIDE_INT rhsunitoffset, rhsoffset;
+
+ /* If we do not do field-sensitive PTA adding offsets to pointers
+ does not change the points-to solution. */
+ if (!use_field_sensitive)
+ {
+ get_constraint_for (ptr, results);
+ return;
+ }
+
+ /* If the offset is not a non-negative integer constant that fits
+ in a HOST_WIDE_INT, we have to fall back to a conservative
+ solution which includes all sub-fields of all pointed-to
+ variables of ptr.
+ ??? As we do not have the ability to express this, fall back
+ to anything. */
+ if (!host_integerp (offset, 1))
+ {
+ struct constraint_expr temp;
+ temp.var = anything_id;
+ temp.type = SCALAR;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &temp);
+ return;
+ }
+
+ /* Make sure the bit-offset also fits. */
+ rhsunitoffset = TREE_INT_CST_LOW (offset);
+ rhsoffset = rhsunitoffset * BITS_PER_UNIT;
+ if (rhsunitoffset != rhsoffset / BITS_PER_UNIT)
+ {
+ struct constraint_expr temp;
+ temp.var = anything_id;
+ temp.type = SCALAR;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &temp);
+ return;
+ }
+
+ get_constraint_for (ptr, results);
+ if (rhsoffset == 0)
+ return;
+
+ /* As we are eventually appending to the solution do not use
+ VEC_iterate here. */
+ n = VEC_length (ce_s, *results);
+ for (j = 0; j < n; j++)
+ {
+ varinfo_t curr;
+ c = *VEC_index (ce_s, *results, j);
+ curr = get_varinfo (c.var);
+
+ if (c.type == ADDRESSOF
+ && !curr->is_full_var)
+ {
+ varinfo_t temp, curr = get_varinfo (c.var);
+
+ /* Search the sub-field which overlaps with the
+ pointed-to offset. As we deal with positive offsets
+ only, we can start the search from the current variable. */
+ temp = first_vi_for_offset (curr, curr->offset + rhsoffset);
+
+ /* If the result is outside of the variable we have to provide
+ a conservative result, as the variable is still reachable
+ from the resulting pointer (even though it technically
+ cannot point to anything). The last sub-field is such
+ a conservative result.
+ ??? If we always had a sub-field for &object + 1 then
+ we could represent this in a more precise way. */
+ if (temp == NULL)
+ {
+ temp = curr;
+ while (temp->next != NULL)
+ temp = temp->next;
+ continue;
+ }
+
+ /* If the found variable is not exactly at the pointed to
+ result, we have to include the next variable in the
+ solution as well. Otherwise two increments by offset / 2
+ do not result in the same or a conservative superset
+ solution. */
+ if (temp->offset != curr->offset + rhsoffset
+ && temp->next != NULL)
+ {
+ struct constraint_expr c2;
+ c2.var = temp->next->id;
+ c2.type = ADDRESSOF;
+ c2.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &c2);
+ }
+ c.var = temp->id;
+ c.offset = 0;
+ }
+ else if (c.type == ADDRESSOF
+ /* If this varinfo represents a full variable just use it. */
+ && curr->is_full_var)
+ c.offset = 0;
+ else
+ c.offset = rhsoffset;
+
+ VEC_replace (ce_s, *results, j, &c);
+ }
+}
+
+
+/* Given a COMPONENT_REF T, return the constraint_expr vector for it.
+ If address_p is true the result will be taken its address of. */
+
+static void
+get_constraint_for_component_ref (tree t, VEC(ce_s, heap) **results,
+ bool address_p)
+{
+ tree orig_t = t;
+ HOST_WIDE_INT bitsize = -1;
+ HOST_WIDE_INT bitmaxsize = -1;
+ HOST_WIDE_INT bitpos;
+ tree forzero;
+ struct constraint_expr *result;
+
+ /* Some people like to do cute things like take the address of
+ &0->a.b */
+ forzero = t;
+ while (!SSA_VAR_P (forzero) && !CONSTANT_CLASS_P (forzero))
+ forzero = TREE_OPERAND (forzero, 0);
+
+ if (CONSTANT_CLASS_P (forzero) && integer_zerop (forzero))
+ {
+ struct constraint_expr temp;
+
+ temp.offset = 0;
+ temp.var = integer_id;
+ temp.type = SCALAR;
+ VEC_safe_push (ce_s, heap, *results, &temp);
+ return;
+ }
+
+ t = get_ref_base_and_extent (t, &bitpos, &bitsize, &bitmaxsize);
+
+ /* Pretend to take the address of the base, we'll take care of
+ adding the required subset of sub-fields below. */
+ get_constraint_for_1 (t, results, true);
+ gcc_assert (VEC_length (ce_s, *results) == 1);
+ result = VEC_last (ce_s, *results);
+
+ /* This can also happen due to weird offsetof type macros. */
+ if (TREE_CODE (t) != ADDR_EXPR && result->type == ADDRESSOF)
+ result->type = SCALAR;
+
+ if (result->type == SCALAR
+ && get_varinfo (result->var)->is_full_var)
+ /* For single-field vars do not bother about the offset. */
+ result->offset = 0;
+ else if (result->type == SCALAR)
+ {
+ /* In languages like C, you can access one past the end of an
+ array. You aren't allowed to dereference it, so we can
+ ignore this constraint. When we handle pointer subtraction,
+ we may have to do something cute here. */
+
+ if ((unsigned HOST_WIDE_INT)bitpos < get_varinfo (result->var)->fullsize
+ && bitmaxsize != 0)
+ {
+ /* It's also not true that the constraint will actually start at the
+ right offset, it may start in some padding. We only care about
+ setting the constraint to the first actual field it touches, so
+ walk to find it. */
+ struct constraint_expr cexpr = *result;
+ varinfo_t curr;
+ VEC_pop (ce_s, *results);
+ cexpr.offset = 0;
+ for (curr = get_varinfo (cexpr.var); curr; curr = curr->next)
+ {
+ if (ranges_overlap_p (curr->offset, curr->size,
+ bitpos, bitmaxsize))
+ {
+ cexpr.var = curr->id;
+ VEC_safe_push (ce_s, heap, *results, &cexpr);
+ if (address_p)
+ break;
+ }
+ }
+ /* If we are going to take the address of this field then
+ to be able to compute reachability correctly add at least
+ the last field of the variable. */
+ if (address_p
+ && VEC_length (ce_s, *results) == 0)
+ {
+ curr = get_varinfo (cexpr.var);
+ while (curr->next != NULL)
+ curr = curr->next;
+ cexpr.var = curr->id;
+ VEC_safe_push (ce_s, heap, *results, &cexpr);
+ }
+ else
+ /* Assert that we found *some* field there. The user couldn't be
+ accessing *only* padding. */
+ /* Still the user could access one past the end of an array
+ embedded in a struct resulting in accessing *only* padding. */
+ gcc_assert (VEC_length (ce_s, *results) >= 1
+ || ref_contains_array_ref (orig_t));
+ }
+ else if (bitmaxsize == 0)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Access to zero-sized part of variable,"
+ "ignoring\n");
+ }
+ else
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Access to past the end of variable, ignoring\n");
+ }
+ else if (bitmaxsize == -1)
+ {
+ /* We can't handle DEREF constraints with unknown size, we'll
+ get the wrong answer. Punt and return anything. */
+ result->var = anything_id;
+ result->offset = 0;
+ }
+ else
+ result->offset = bitpos;
+}
+
+
+/* Dereference the constraint expression CONS, and return the result.
+ DEREF (ADDRESSOF) = SCALAR
+ DEREF (SCALAR) = DEREF
+ DEREF (DEREF) = (temp = DEREF1; result = DEREF(temp))
+ This is needed so that we can handle dereferencing DEREF constraints. */
+
+static void
+do_deref (VEC (ce_s, heap) **constraints)
+{
+ struct constraint_expr *c;
+ unsigned int i = 0;
+
+ for (i = 0; VEC_iterate (ce_s, *constraints, i, c); i++)
+ {
+ if (c->type == SCALAR)
+ c->type = DEREF;
+ else if (c->type == ADDRESSOF)
+ c->type = SCALAR;
+ else if (c->type == DEREF)
+ {
+ tree tmpvar = create_tmp_var_raw (ptr_type_node, "dereftmp");
+ struct constraint_expr tmplhs = get_constraint_exp_for_temp (tmpvar);
+ process_constraint (new_constraint (tmplhs, *c));
+ c->var = tmplhs.var;
+ }
+ else
+ gcc_unreachable ();
+ }
+}
+
+/* Given a tree T, return the constraint expression for it. */
+
+static void
+get_constraint_for_1 (tree t, VEC (ce_s, heap) **results, bool address_p)
+{
+ struct constraint_expr temp;
+
+ /* x = integer is all glommed to a single variable, which doesn't
+ point to anything by itself. That is, of course, unless it is an
+ integer constant being treated as a pointer, in which case, we
+ will return that this is really the addressof anything. This
+ happens below, since it will fall into the default case. The only
+ case we know something about an integer treated like a pointer is
+ when it is the NULL pointer, and then we just say it points to
+ NULL.
+
+ Do not do that if -fno-delete-null-pointer-checks though, because
+ in that case *NULL does not fail, so it _should_ alias *anything.
+ It is not worth adding a new option or renaming the existing one,
+ since this case is relatively obscure. */
+ if (flag_delete_null_pointer_checks
+ && TREE_CODE (t) == INTEGER_CST
+ && integer_zerop (t))
+ {
+ temp.var = nothing_id;
+ temp.type = ADDRESSOF;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &temp);
+ return;
+ }
+
+ /* String constants are read-only. */
+ if (TREE_CODE (t) == STRING_CST)
+ {
+ temp.var = readonly_id;
+ temp.type = SCALAR;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &temp);
+ return;
+ }
+
+ switch (TREE_CODE_CLASS (TREE_CODE (t)))
+ {
+ case tcc_expression:
+ {
+ switch (TREE_CODE (t))
+ {
+ case ADDR_EXPR:
+ {
+ struct constraint_expr *c;
+ unsigned int i;
+ tree exp = TREE_OPERAND (t, 0);
+
+ get_constraint_for_1 (exp, results, true);
+
+ for (i = 0; VEC_iterate (ce_s, *results, i, c); i++)
+ {
+ if (c->type == DEREF)
+ c->type = SCALAR;
+ else
+ c->type = ADDRESSOF;
+ }
+ return;
+ }
+ break;
+ default:;
+ }
+ break;
+ }
+ case tcc_reference:
+ {
+ switch (TREE_CODE (t))
+ {
+ case INDIRECT_REF:
+ {
+ get_constraint_for_1 (TREE_OPERAND (t, 0), results, address_p);
+ do_deref (results);
+ return;
+ }
+ case ARRAY_REF:
+ case ARRAY_RANGE_REF:
+ case COMPONENT_REF:
+ get_constraint_for_component_ref (t, results, address_p);
+ return;
+ default:;
+ }
+ break;
+ }
+ case tcc_exceptional:
+ {
+ switch (TREE_CODE (t))
+ {
+ case SSA_NAME:
+ {
+ get_constraint_for_ssa_var (t, results, address_p);
+ return;
+ }
+ default:;
+ }
+ break;
+ }
+ case tcc_declaration:
+ {
+ get_constraint_for_ssa_var (t, results, address_p);
+ return;
+ }
+ default:;
+ }
+
+ /* The default fallback is a constraint from anything. */
+ temp.type = ADDRESSOF;
+ temp.var = anything_id;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, *results, &temp);
+}
+
+/* Given a gimple tree T, return the constraint expression vector for it. */
+
+static void
+get_constraint_for (tree t, VEC (ce_s, heap) **results)
+{
+ gcc_assert (VEC_length (ce_s, *results) == 0);
+
+ get_constraint_for_1 (t, results, false);
+}
+
+/* Handle the structure copy case where we have a simple structure copy
+ between LHS and RHS that is of SIZE (in bits)
+
+ For each field of the lhs variable (lhsfield)
+ For each field of the rhs variable at lhsfield.offset (rhsfield)
+ add the constraint lhsfield = rhsfield
+
+ If we fail due to some kind of type unsafety or other thing we
+ can't handle, return false. We expect the caller to collapse the
+ variable in that case. */
+
+static bool
+do_simple_structure_copy (const struct constraint_expr lhs,
+ const struct constraint_expr rhs,
+ const unsigned HOST_WIDE_INT size)
+{
+ varinfo_t p = get_varinfo (lhs.var);
+ unsigned HOST_WIDE_INT pstart, last;
+ pstart = p->offset;
+ last = p->offset + size;
+ for (; p && p->offset < last; p = p->next)
+ {
+ varinfo_t q;
+ struct constraint_expr templhs = lhs;
+ struct constraint_expr temprhs = rhs;
+ unsigned HOST_WIDE_INT fieldoffset;
+
+ templhs.var = p->id;
+ q = get_varinfo (temprhs.var);
+ fieldoffset = p->offset - pstart;
+ q = first_vi_for_offset (q, q->offset + fieldoffset);
+ if (!q)
+ return false;
+ temprhs.var = q->id;
+ process_constraint (new_constraint (templhs, temprhs));
+ }
+ return true;
+}
+
+
+/* Handle the structure copy case where we have a structure copy between a
+ aggregate on the LHS and a dereference of a pointer on the RHS
+ that is of SIZE (in bits)
+
+ For each field of the lhs variable (lhsfield)
+ rhs.offset = lhsfield->offset
+ add the constraint lhsfield = rhs
+*/
+
+static void
+do_rhs_deref_structure_copy (const struct constraint_expr lhs,
+ const struct constraint_expr rhs,
+ const unsigned HOST_WIDE_INT size)
+{
+ varinfo_t p = get_varinfo (lhs.var);
+ unsigned HOST_WIDE_INT pstart,last;
+ pstart = p->offset;
+ last = p->offset + size;
+
+ for (; p && p->offset < last; p = p->next)
+ {
+ varinfo_t q;
+ struct constraint_expr templhs = lhs;
+ struct constraint_expr temprhs = rhs;
+ unsigned HOST_WIDE_INT fieldoffset;
+
+
+ if (templhs.type == SCALAR)
+ templhs.var = p->id;
+ else
+ templhs.offset = p->offset;
+
+ q = get_varinfo (temprhs.var);
+ fieldoffset = p->offset - pstart;
+ temprhs.offset += fieldoffset;
+ process_constraint (new_constraint (templhs, temprhs));
+ }
+}
+
+/* Handle the structure copy case where we have a structure copy
+ between an aggregate on the RHS and a dereference of a pointer on
+ the LHS that is of SIZE (in bits)
+
+ For each field of the rhs variable (rhsfield)
+ lhs.offset = rhsfield->offset
+ add the constraint lhs = rhsfield
+*/
+
+static void
+do_lhs_deref_structure_copy (const struct constraint_expr lhs,
+ const struct constraint_expr rhs,
+ const unsigned HOST_WIDE_INT size)
+{
+ varinfo_t p = get_varinfo (rhs.var);
+ unsigned HOST_WIDE_INT pstart,last;
+ pstart = p->offset;
+ last = p->offset + size;
+
+ for (; p && p->offset < last; p = p->next)
+ {
+ varinfo_t q;
+ struct constraint_expr templhs = lhs;
+ struct constraint_expr temprhs = rhs;
+ unsigned HOST_WIDE_INT fieldoffset;
+
+
+ if (temprhs.type == SCALAR)
+ temprhs.var = p->id;
+ else
+ temprhs.offset = p->offset;
+
+ q = get_varinfo (templhs.var);
+ fieldoffset = p->offset - pstart;
+ templhs.offset += fieldoffset;
+ process_constraint (new_constraint (templhs, temprhs));
+ }
+}
+
+/* Sometimes, frontends like to give us bad type information. This
+ function will collapse all the fields from VAR to the end of VAR,
+ into VAR, so that we treat those fields as a single variable.
+ We return the variable they were collapsed into. */
+
+static unsigned int
+collapse_rest_of_var (unsigned int var)
+{
+ varinfo_t currvar = get_varinfo (var);
+ varinfo_t field;
+
+ for (field = currvar->next; field; field = field->next)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Type safety: Collapsing var %s into %s\n",
+ field->name, currvar->name);
+
+ gcc_assert (field->collapsed_to == 0);
+ field->collapsed_to = currvar->id;
+ }
+
+ currvar->next = NULL;
+ currvar->size = currvar->fullsize - currvar->offset;
+
+ return currvar->id;
+}
+
+/* Handle aggregate copies by expanding into copies of the respective
+ fields of the structures. */
+
+static void
+do_structure_copy (tree lhsop, tree rhsop)
+{
+ struct constraint_expr lhs, rhs, tmp;
+ VEC (ce_s, heap) *lhsc = NULL, *rhsc = NULL;
+ varinfo_t p;
+ unsigned HOST_WIDE_INT lhssize;
+ unsigned HOST_WIDE_INT rhssize;
+
+ /* Pretend we are taking the address of the constraint exprs.
+ We deal with walking the sub-fields ourselves. */
+ get_constraint_for_1 (lhsop, &lhsc, true);
+ get_constraint_for_1 (rhsop, &rhsc, true);
+ gcc_assert (VEC_length (ce_s, lhsc) == 1);
+ gcc_assert (VEC_length (ce_s, rhsc) == 1);
+ lhs = *(VEC_last (ce_s, lhsc));
+ rhs = *(VEC_last (ce_s, rhsc));
+
+ VEC_free (ce_s, heap, lhsc);
+ VEC_free (ce_s, heap, rhsc);
+
+ /* If we have special var = x, swap it around. */
+ if (lhs.var <= integer_id && !(get_varinfo (rhs.var)->is_special_var))
+ {
+ tmp = lhs;
+ lhs = rhs;
+ rhs = tmp;
+ }
+
+ /* This is fairly conservative for the RHS == ADDRESSOF case, in that it's
+ possible it's something we could handle. However, most cases falling
+ into this are dealing with transparent unions, which are slightly
+ weird. */
+ if (rhs.type == ADDRESSOF && !(get_varinfo (rhs.var)->is_special_var))
+ {
+ rhs.type = ADDRESSOF;
+ rhs.var = anything_id;
+ }
+
+ /* If the RHS is a special var, or an addressof, set all the LHS fields to
+ that special var. */
+ if (rhs.var <= integer_id)
+ {
+ for (p = get_varinfo (lhs.var); p; p = p->next)
+ {
+ struct constraint_expr templhs = lhs;
+ struct constraint_expr temprhs = rhs;
+
+ if (templhs.type == SCALAR )
+ templhs.var = p->id;
+ else
+ templhs.offset += p->offset;
+ process_constraint (new_constraint (templhs, temprhs));
+ }
+ }
+ else
+ {
+ tree rhstype = TREE_TYPE (rhsop);
+ tree lhstype = TREE_TYPE (lhsop);
+ tree rhstypesize;
+ tree lhstypesize;
+
+ lhstypesize = DECL_P (lhsop) ? DECL_SIZE (lhsop) : TYPE_SIZE (lhstype);
+ rhstypesize = DECL_P (rhsop) ? DECL_SIZE (rhsop) : TYPE_SIZE (rhstype);
+
+ /* If we have a variably sized types on the rhs or lhs, and a deref
+ constraint, add the constraint, lhsconstraint = &ANYTHING.
+ This is conservatively correct because either the lhs is an unknown
+ sized var (if the constraint is SCALAR), or the lhs is a DEREF
+ constraint, and every variable it can point to must be unknown sized
+ anyway, so we don't need to worry about fields at all. */
+ if ((rhs.type == DEREF && TREE_CODE (rhstypesize) != INTEGER_CST)
+ || (lhs.type == DEREF && TREE_CODE (lhstypesize) != INTEGER_CST))
+ {
+ rhs.var = anything_id;
+ rhs.type = ADDRESSOF;
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+ return;
+ }
+
+ /* The size only really matters insofar as we don't set more or less of
+ the variable. If we hit an unknown size var, the size should be the
+ whole darn thing. */
+ if (get_varinfo (rhs.var)->is_unknown_size_var)
+ rhssize = ~0;
+ else
+ rhssize = TREE_INT_CST_LOW (rhstypesize);
+
+ if (get_varinfo (lhs.var)->is_unknown_size_var)
+ lhssize = ~0;
+ else
+ lhssize = TREE_INT_CST_LOW (lhstypesize);
+
+
+ if (rhs.type == SCALAR && lhs.type == SCALAR)
+ {
+ if (!do_simple_structure_copy (lhs, rhs, MIN (lhssize, rhssize)))
+ {
+ lhs.var = collapse_rest_of_var (get_varinfo_fc (lhs.var)->id);
+ rhs.var = collapse_rest_of_var (get_varinfo_fc (rhs.var)->id);
+ lhs.offset = 0;
+ rhs.offset = 0;
+ lhs.type = SCALAR;
+ rhs.type = SCALAR;
+ process_constraint (new_constraint (lhs, rhs));
+ }
+ }
+ else if (lhs.type != DEREF && rhs.type == DEREF)
+ do_rhs_deref_structure_copy (lhs, rhs, MIN (lhssize, rhssize));
+ else if (lhs.type == DEREF && rhs.type != DEREF)
+ do_lhs_deref_structure_copy (lhs, rhs, MIN (lhssize, rhssize));
+ else
+ {
+ tree pointedtotype = lhstype;
+ tree tmpvar;
+
+ gcc_assert (rhs.type == DEREF && lhs.type == DEREF);
+ tmpvar = create_tmp_var_raw (pointedtotype, "structcopydereftmp");
+ do_structure_copy (tmpvar, rhsop);
+ do_structure_copy (lhsop, tmpvar);
+ }
+ }
+}
+
+/* Create a constraint ID = OP. */
+
+static void
+make_constraint_to (unsigned id, tree op)
+{
+ VEC(ce_s, heap) *rhsc = NULL;
+ struct constraint_expr *c;
+ struct constraint_expr includes;
+ unsigned int j;
+
+ includes.var = id;
+ includes.offset = 0;
+ includes.type = SCALAR;
+
+ get_constraint_for (op, &rhsc);
+ for (j = 0; VEC_iterate (ce_s, rhsc, j, c); j++)
+ process_constraint (new_constraint (includes, *c));
+ VEC_free (ce_s, heap, rhsc);
+}
+
+/* Make constraints necessary to make OP escape. */
+
+static void
+make_escape_constraint (tree op)
+{
+ make_constraint_to (escaped_id, op);
+}
+
+/* For non-IPA mode, generate constraints necessary for a call on the
+ RHS. */
+
+static void
+handle_rhs_call (gimple stmt)
+{
+ unsigned i;
+
+ for (i = 0; i < gimple_call_num_args (stmt); ++i)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+
+ /* Find those pointers being passed, and make sure they end up
+ pointing to anything. */
+ if (could_have_pointers (arg))
+ make_escape_constraint (arg);
+ }
+
+ /* The static chain escapes as well. */
+ if (gimple_call_chain (stmt))
+ make_escape_constraint (gimple_call_chain (stmt));
+}
+
+/* For non-IPA mode, generate constraints necessary for a call
+ that returns a pointer and assigns it to LHS. This simply makes
+ the LHS point to global and escaped variables. */
+
+static void
+handle_lhs_call (tree lhs, int flags)
+{
+ VEC(ce_s, heap) *lhsc = NULL;
+ struct constraint_expr rhsc;
+ unsigned int j;
+ struct constraint_expr *lhsp;
+
+ get_constraint_for (lhs, &lhsc);
+
+ if (flags & ECF_MALLOC)
+ {
+ tree heapvar = heapvar_lookup (lhs);
+ varinfo_t vi;
+
+ if (heapvar == NULL)
+ {
+ heapvar = create_tmp_var_raw (ptr_type_node, "HEAP");
+ DECL_EXTERNAL (heapvar) = 1;
+ get_var_ann (heapvar)->is_heapvar = 1;
+ if (gimple_referenced_vars (cfun))
+ add_referenced_var (heapvar);
+ heapvar_insert (lhs, heapvar);
+ }
+
+ rhsc.var = create_variable_info_for (heapvar,
+ alias_get_name (heapvar));
+ vi = get_varinfo (rhsc.var);
+ vi->is_artificial_var = 1;
+ vi->is_heap_var = 1;
+ vi->is_unknown_size_var = true;
+ vi->fullsize = ~0;
+ vi->size = ~0;
+ rhsc.type = ADDRESSOF;
+ rhsc.offset = 0;
+ }
+ else
+ {
+ rhsc.var = escaped_id;
+ rhsc.offset = 0;
+ rhsc.type = ADDRESSOF;
+ }
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ process_constraint (new_constraint (*lhsp, rhsc));
+ VEC_free (ce_s, heap, lhsc);
+}
+
+/* For non-IPA mode, generate constraints necessary for a call of a
+ const function that returns a pointer in the statement STMT. */
+
+static void
+handle_const_call (gimple stmt)
+{
+ tree lhs = gimple_call_lhs (stmt);
+ VEC(ce_s, heap) *lhsc = NULL;
+ struct constraint_expr rhsc;
+ unsigned int j, k;
+ struct constraint_expr *lhsp;
+ tree tmpvar;
+ struct constraint_expr tmpc;
+
+ get_constraint_for (lhs, &lhsc);
+
+ /* If this is a nested function then it can return anything. */
+ if (gimple_call_chain (stmt))
+ {
+ rhsc.var = anything_id;
+ rhsc.offset = 0;
+ rhsc.type = ADDRESSOF;
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ process_constraint (new_constraint (*lhsp, rhsc));
+ VEC_free (ce_s, heap, lhsc);
+ return;
+ }
+
+ /* We always use a temporary here, otherwise we end up with a quadratic
+ amount of constraints for
+ large_struct = const_call (large_struct);
+ in field-sensitive PTA. */
+ tmpvar = create_tmp_var_raw (ptr_type_node, "consttmp");
+ tmpc = get_constraint_exp_for_temp (tmpvar);
+
+ /* May return addresses of globals. */
+ rhsc.var = nonlocal_id;
+ rhsc.offset = 0;
+ rhsc.type = ADDRESSOF;
+ process_constraint (new_constraint (tmpc, rhsc));
+
+ /* May return arguments. */
+ for (k = 0; k < gimple_call_num_args (stmt); ++k)
+ {
+ tree arg = gimple_call_arg (stmt, k);
+
+ if (could_have_pointers (arg))
+ {
+ VEC(ce_s, heap) *argc = NULL;
+ struct constraint_expr *argp;
+ int i;
+
+ get_constraint_for (arg, &argc);
+ for (i = 0; VEC_iterate (ce_s, argc, i, argp); i++)
+ process_constraint (new_constraint (tmpc, *argp));
+ VEC_free (ce_s, heap, argc);
+ }
+ }
+
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ process_constraint (new_constraint (*lhsp, tmpc));
+
+ VEC_free (ce_s, heap, lhsc);
+}
+
+/* For non-IPA mode, generate constraints necessary for a call to a
+ pure function in statement STMT. */
+
+static void
+handle_pure_call (gimple stmt)
+{
+ unsigned i;
+
+ /* Memory reached from pointer arguments is call-used. */
+ for (i = 0; i < gimple_call_num_args (stmt); ++i)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+
+ if (could_have_pointers (arg))
+ make_constraint_to (callused_id, arg);
+ }
+
+ /* The static chain is used as well. */
+ if (gimple_call_chain (stmt))
+ make_constraint_to (callused_id, gimple_call_chain (stmt));
+
+ /* If the call returns a pointer it may point to reachable memory
+ from the arguments. Not so for malloc functions though. */
+ if (gimple_call_lhs (stmt)
+ && could_have_pointers (gimple_call_lhs (stmt))
+ && !(gimple_call_flags (stmt) & ECF_MALLOC))
+ {
+ tree lhs = gimple_call_lhs (stmt);
+ VEC(ce_s, heap) *lhsc = NULL;
+ struct constraint_expr rhsc;
+ struct constraint_expr *lhsp;
+ unsigned j;
+
+ get_constraint_for (lhs, &lhsc);
+
+ /* If this is a nested function then it can return anything. */
+ if (gimple_call_chain (stmt))
+ {
+ rhsc.var = anything_id;
+ rhsc.offset = 0;
+ rhsc.type = ADDRESSOF;
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ process_constraint (new_constraint (*lhsp, rhsc));
+ VEC_free (ce_s, heap, lhsc);
+ return;
+ }
+
+ /* Else just add the call-used memory here. Escaped variables
+ and globals will be dealt with in handle_lhs_call. */
+ rhsc.var = callused_id;
+ rhsc.offset = 0;
+ rhsc.type = ADDRESSOF;
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ process_constraint (new_constraint (*lhsp, rhsc));
+ VEC_free (ce_s, heap, lhsc);
+ }
+}
+
+/* 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 (gimple origt)
+{
+ gimple t = origt;
+ VEC(ce_s, heap) *lhsc = NULL;
+ VEC(ce_s, heap) *rhsc = NULL;
+ struct constraint_expr *c;
+ enum escape_type stmt_escape_type;
+
+ /* Now build constraints expressions. */
+ if (gimple_code (t) == GIMPLE_PHI)
+ {
+ gcc_assert (!AGGREGATE_TYPE_P (TREE_TYPE (gimple_phi_result (t))));
+
+ /* Only care about pointers and structures containing
+ pointers. */
+ if (could_have_pointers (gimple_phi_result (t)))
+ {
+ size_t i;
+ unsigned int j;
+
+ /* For a phi node, assign all the arguments to
+ the result. */
+ get_constraint_for (gimple_phi_result (t), &lhsc);
+ for (i = 0; i < gimple_phi_num_args (t); i++)
+ {
+ tree rhstype;
+ tree strippedrhs = PHI_ARG_DEF (t, i);
+
+ STRIP_NOPS (strippedrhs);
+ rhstype = TREE_TYPE (strippedrhs);
+ get_constraint_for (gimple_phi_arg_def (t, i), &rhsc);
+
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
+ {
+ struct constraint_expr *c2;
+ while (VEC_length (ce_s, rhsc) > 0)
+ {
+ c2 = VEC_last (ce_s, rhsc);
+ process_constraint (new_constraint (*c, *c2));
+ VEC_pop (ce_s, rhsc);
+ }
+ }
+ }
+ }
+ }
+ /* In IPA mode, we need to generate constraints to pass call
+ arguments through their calls. There are two cases,
+ either a GIMPLE_CALL returning a value, or just a plain
+ GIMPLE_CALL when we are not.
+
+ In non-ipa mode, we need to generate constraints for each
+ pointer passed by address. */
+ else if (is_gimple_call (t))
+ {
+ if (!in_ipa_mode)
+ {
+ int flags = gimple_call_flags (t);
+
+ /* Const functions can return their arguments and addresses
+ of global memory but not of escaped memory. */
+ if (flags & ECF_CONST)
+ {
+ if (gimple_call_lhs (t)
+ && could_have_pointers (gimple_call_lhs (t)))
+ handle_const_call (t);
+ }
+ /* Pure functions can return addresses in and of memory
+ reachable from their arguments, but they are not an escape
+ point for reachable memory of their arguments. */
+ else if (flags & ECF_PURE)
+ {
+ handle_pure_call (t);
+ if (gimple_call_lhs (t)
+ && could_have_pointers (gimple_call_lhs (t)))
+ handle_lhs_call (gimple_call_lhs (t), flags);
+ }
+ else
+ {
+ handle_rhs_call (t);
+ if (gimple_call_lhs (t)
+ && could_have_pointers (gimple_call_lhs (t)))
+ handle_lhs_call (gimple_call_lhs (t), flags);
+ }
+ }
+ else
+ {
+ tree lhsop;
+ varinfo_t fi;
+ int i = 1;
+ size_t j;
+ tree decl;
+
+ lhsop = gimple_call_lhs (t);
+ decl = gimple_call_fndecl (t);
+
+ /* If we can directly resolve the function being called, do so.
+ Otherwise, it must be some sort of indirect expression that
+ we should still be able to handle. */
+ if (decl)
+ fi = get_vi_for_tree (decl);
+ else
+ {
+ decl = gimple_call_fn (t);
+ fi = get_vi_for_tree (decl);
+ }
+
+ /* Assign all the passed arguments to the appropriate incoming
+ parameters of the function. */
+ for (j = 0; j < gimple_call_num_args (t); j++)
+ {
+ struct constraint_expr lhs ;
+ struct constraint_expr *rhsp;
+ tree arg = gimple_call_arg (t, j);
+
+ get_constraint_for (arg, &rhsc);
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ {
+ lhs.type = DEREF;
+ lhs.var = fi->id;
+ lhs.offset = i;
+ }
+ else
+ {
+ lhs.type = SCALAR;
+ lhs.var = first_vi_for_offset (fi, i)->id;
+ lhs.offset = 0;
+ }
+ while (VEC_length (ce_s, rhsc) != 0)
+ {
+ rhsp = VEC_last (ce_s, rhsc);
+ process_constraint (new_constraint (lhs, *rhsp));
+ VEC_pop (ce_s, rhsc);
+ }
+ i++;
+ }
+
+ /* If we are returning a value, assign it to the result. */
+ if (lhsop)
+ {
+ struct constraint_expr rhs;
+ struct constraint_expr *lhsp;
+ unsigned int j = 0;
+
+ get_constraint_for (lhsop, &lhsc);
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ {
+ rhs.type = DEREF;
+ rhs.var = fi->id;
+ rhs.offset = i;
+ }
+ else
+ {
+ rhs.type = SCALAR;
+ rhs.var = first_vi_for_offset (fi, i)->id;
+ rhs.offset = 0;
+ }
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, lhsp); j++)
+ process_constraint (new_constraint (*lhsp, rhs));
+ }
+ }
+ }
+ /* Otherwise, just a regular assignment statement. Only care about
+ operations with pointer result, others are dealt with as escape
+ points if they have pointer operands. */
+ else if (is_gimple_assign (t)
+ && could_have_pointers (gimple_assign_lhs (t)))
+ {
+ /* Otherwise, just a regular assignment statement. */
+ tree lhsop = gimple_assign_lhs (t);
+ tree rhsop = (gimple_num_ops (t) == 2) ? gimple_assign_rhs1 (t) : NULL;
+
+ if (rhsop && AGGREGATE_TYPE_P (TREE_TYPE (lhsop)))
+ do_structure_copy (lhsop, rhsop);
+ else
+ {
+ unsigned int j;
+ struct constraint_expr temp;
+ get_constraint_for (lhsop, &lhsc);
+
+ if (gimple_assign_rhs_code (t) == POINTER_PLUS_EXPR)
+ get_constraint_for_ptr_offset (gimple_assign_rhs1 (t),
+ gimple_assign_rhs2 (t), &rhsc);
+ else if ((CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (t))
+ && !(POINTER_TYPE_P (gimple_expr_type (t))
+ && !POINTER_TYPE_P (TREE_TYPE (rhsop))))
+ || gimple_assign_single_p (t))
+ get_constraint_for (rhsop, &rhsc);
+ else
+ {
+ temp.type = ADDRESSOF;
+ temp.var = anything_id;
+ temp.offset = 0;
+ VEC_safe_push (ce_s, heap, rhsc, &temp);
+ }
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
+ {
+ struct constraint_expr *c2;
+ unsigned int k;
+
+ for (k = 0; VEC_iterate (ce_s, rhsc, k, c2); k++)
+ process_constraint (new_constraint (*c, *c2));
+ }
+ }
+ }
+ else if (gimple_code (t) == GIMPLE_CHANGE_DYNAMIC_TYPE)
+ {
+ unsigned int j;
+
+ get_constraint_for (gimple_cdt_location (t), &lhsc);
+ for (j = 0; VEC_iterate (ce_s, lhsc, j, c); ++j)
+ get_varinfo (c->var)->no_tbaa_pruning = true;
+ }
+
+ stmt_escape_type = is_escape_site (t);
+ if (stmt_escape_type == ESCAPE_STORED_IN_GLOBAL)
+ {
+ gcc_assert (is_gimple_assign (t));
+ if (gimple_assign_rhs_code (t) == ADDR_EXPR)
+ {
+ tree rhs = gimple_assign_rhs1 (t);
+ tree base = get_base_address (TREE_OPERAND (rhs, 0));
+ if (base
+ && (!DECL_P (base)
+ || !is_global_var (base)))
+ make_escape_constraint (rhs);
+ }
+ else if (get_gimple_rhs_class (gimple_assign_rhs_code (t))
+ == GIMPLE_SINGLE_RHS)
+ {
+ if (could_have_pointers (gimple_assign_rhs1 (t)))
+ make_escape_constraint (gimple_assign_rhs1 (t));
+ }
+ else
+ gcc_unreachable ();
+ }
+ else if (stmt_escape_type == ESCAPE_BAD_CAST)
+ {
+ gcc_assert (is_gimple_assign (t));
+ gcc_assert (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (t))
+ || gimple_assign_rhs_code (t) == VIEW_CONVERT_EXPR);
+ make_escape_constraint (gimple_assign_rhs1 (t));
+ }
+ else if (stmt_escape_type == ESCAPE_TO_ASM)
+ {
+ unsigned i;
+ for (i = 0; i < gimple_asm_noutputs (t); ++i)
+ {
+ tree op = TREE_VALUE (gimple_asm_output_op (t, i));
+ if (op && could_have_pointers (op))
+ /* Strictly we'd only need the constraints from ESCAPED and
+ NONLOCAL. */
+ make_escape_constraint (op);
+ }
+ for (i = 0; i < gimple_asm_ninputs (t); ++i)
+ {
+ tree op = TREE_VALUE (gimple_asm_input_op (t, i));
+ if (op && could_have_pointers (op))
+ /* Strictly we'd only need the constraint to ESCAPED. */
+ make_escape_constraint (op);
+ }
+ }
+
+ /* 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. */
+ if (!in_ipa_mode)
+ gimple_set_modified (origt, true);
+ VEC_free (ce_s, heap, rhsc);
+ VEC_free (ce_s, heap, lhsc);
+}
+
+
+/* Find the first varinfo in the same variable as START that overlaps with
+ OFFSET.
+ Effectively, walk the chain of fields for the variable START to find the
+ first field that overlaps with OFFSET.
+ Return NULL if we can't find one. */
+
+static varinfo_t
+first_vi_for_offset (varinfo_t start, unsigned HOST_WIDE_INT offset)
+{
+ varinfo_t curr = start;
+ while (curr)
+ {
+ /* We may not find a variable in the field list with the actual
+ offset when when we have glommed a structure to a variable.
+ In that case, however, offset should still be within the size
+ of the variable. */
+ if (offset >= curr->offset && offset < (curr->offset + curr->size))
+ return curr;
+ curr = curr->next;
+ }
+ return NULL;
+}
+
+
+/* Insert the varinfo FIELD into the field list for BASE, at the front
+ of the list. */
+
+static void
+insert_into_field_list (varinfo_t base, varinfo_t field)
+{
+ varinfo_t prev = base;
+ varinfo_t curr = base->next;
+
+ field->next = curr;
+ prev->next = field;
+}
+
+/* Insert the varinfo FIELD into the field list for BASE, ordered by
+ offset. */
+
+static void
+insert_into_field_list_sorted (varinfo_t base, varinfo_t field)
+{
+ varinfo_t prev = base;
+ varinfo_t curr = base->next;
+
+ if (curr == NULL)
+ {
+ prev->next = field;
+ field->next = NULL;
+ }
+ else
+ {
+ while (curr)
+ {
+ if (field->offset <= curr->offset)
+ break;
+ prev = curr;
+ curr = curr->next;
+ }
+ field->next = prev->next;
+ prev->next = field;
+ }
+}
+
+/* This structure is used during pushing fields onto the fieldstack
+ to track the offset of the field, since bitpos_of_field gives it
+ relative to its immediate containing type, and we want it relative
+ to the ultimate containing object. */
+
+struct fieldoff
+{
+ /* Offset from the base of the base containing object to this field. */
+ HOST_WIDE_INT offset;
+
+ /* Size, in bits, of the field. */
+ unsigned HOST_WIDE_INT size;
+
+ unsigned has_unknown_size : 1;
+
+ unsigned may_have_pointers : 1;
+};
+typedef struct fieldoff fieldoff_s;
+
+DEF_VEC_O(fieldoff_s);
+DEF_VEC_ALLOC_O(fieldoff_s,heap);
+
+/* qsort comparison function for two fieldoff's PA and PB */
+
+static int
+fieldoff_compare (const void *pa, const void *pb)
+{
+ const fieldoff_s *foa = (const fieldoff_s *)pa;
+ const fieldoff_s *fob = (const fieldoff_s *)pb;
+ unsigned HOST_WIDE_INT foasize, fobsize;
+
+ if (foa->offset < fob->offset)
+ return -1;
+ else if (foa->offset > fob->offset)
+ return 1;
+
+ foasize = foa->size;
+ fobsize = fob->size;
+ if (foasize < fobsize)
+ return -1;
+ else if (foasize > fobsize)
+ return 1;
+ return 0;
+}
+
+/* Sort a fieldstack according to the field offset and sizes. */
+static void
+sort_fieldstack (VEC(fieldoff_s,heap) *fieldstack)
+{
+ qsort (VEC_address (fieldoff_s, fieldstack),
+ VEC_length (fieldoff_s, fieldstack),
+ sizeof (fieldoff_s),
+ fieldoff_compare);
+}
+
+/* Return true if V is a tree that we can have subvars for.
+ Normally, this is any aggregate type. Also complex
+ types which are not gimple registers can have subvars. */
+
+static inline bool
+var_can_have_subvars (const_tree v)
+{
+ /* Volatile variables should never have subvars. */
+ if (TREE_THIS_VOLATILE (v))
+ return false;
+
+ /* Non decls or memory tags can never have subvars. */
+ if (!DECL_P (v) || MTAG_P (v))
+ return false;
+
+ /* Aggregates without overlapping fields can have subvars. */
+ if (TREE_CODE (TREE_TYPE (v)) == RECORD_TYPE)
+ return true;
+
+ return false;
+}
+
+/* Given a TYPE, and a vector of field offsets FIELDSTACK, push all
+ the fields of TYPE onto fieldstack, recording their offsets along
+ the way.
+
+ OFFSET is used to keep track of the offset in this entire
+ structure, rather than just the immediately containing structure.
+ Returns the number of fields pushed. */
+
+static int
+push_fields_onto_fieldstack (tree type, VEC(fieldoff_s,heap) **fieldstack,
+ HOST_WIDE_INT offset)
+{
+ tree field;
+ int count = 0;
+
+ if (TREE_CODE (type) != RECORD_TYPE)
+ return 0;
+
+ /* If the vector of fields is growing too big, bail out early.
+ Callers check for VEC_length <= MAX_FIELDS_FOR_FIELD_SENSITIVE, make
+ sure this fails. */
+ if (VEC_length (fieldoff_s, *fieldstack) > MAX_FIELDS_FOR_FIELD_SENSITIVE)
+ return 0;
+
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL)
+ {
+ bool push = false;
+ int pushed = 0;
+ HOST_WIDE_INT foff = bitpos_of_field (field);
+
+ if (!var_can_have_subvars (field)
+ || TREE_CODE (TREE_TYPE (field)) == QUAL_UNION_TYPE
+ || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
+ push = true;
+ else if (!(pushed = push_fields_onto_fieldstack
+ (TREE_TYPE (field), fieldstack, offset + foff))
+ && (DECL_SIZE (field)
+ && !integer_zerop (DECL_SIZE (field))))
+ /* Empty structures may have actual size, like in C++. So
+ see if we didn't push any subfields and the size is
+ nonzero, push the field onto the stack. */
+ push = true;
+
+ if (push)
+ {
+ fieldoff_s *pair = NULL;
+ bool has_unknown_size = false;
+
+ if (!VEC_empty (fieldoff_s, *fieldstack))
+ pair = VEC_last (fieldoff_s, *fieldstack);
+
+ if (!DECL_SIZE (field)
+ || !host_integerp (DECL_SIZE (field), 1))
+ has_unknown_size = true;
+
+ /* If adjacent fields do not contain pointers merge them. */
+ if (pair
+ && !pair->may_have_pointers
+ && !could_have_pointers (field)
+ && !pair->has_unknown_size
+ && !has_unknown_size
+ && pair->offset + (HOST_WIDE_INT)pair->size == offset + foff)
+ {
+ pair = VEC_last (fieldoff_s, *fieldstack);
+ pair->size += TREE_INT_CST_LOW (DECL_SIZE (field));
+ }
+ else
+ {
+ pair = VEC_safe_push (fieldoff_s, heap, *fieldstack, NULL);
+ pair->offset = offset + foff;
+ pair->has_unknown_size = has_unknown_size;
+ if (!has_unknown_size)
+ pair->size = TREE_INT_CST_LOW (DECL_SIZE (field));
+ else
+ pair->size = -1;
+ pair->may_have_pointers = could_have_pointers (field);
+ count++;
+ }
+ }
+ else
+ count += pushed;
+ }
+
+ return count;
+}
+
+/* Create a constraint ID = &FROM. */
+
+static void
+make_constraint_from (varinfo_t vi, int from)
+{
+ struct constraint_expr lhs, rhs;
+
+ lhs.var = vi->id;
+ lhs.offset = 0;
+ lhs.type = SCALAR;
+
+ rhs.var = from;
+ rhs.offset = 0;
+ rhs.type = ADDRESSOF;
+ process_constraint (new_constraint (lhs, rhs));
+}
+
+/* Count the number of arguments DECL has, and set IS_VARARGS to true
+ if it is a varargs function. */
+
+static unsigned int
+count_num_arguments (tree decl, bool *is_varargs)
+{
+ unsigned int i = 0;
+ tree t;
+
+ for (t = TYPE_ARG_TYPES (TREE_TYPE (decl));
+ t;
+ t = TREE_CHAIN (t))
+ {
+ if (TREE_VALUE (t) == void_type_node)
+ break;
+ i++;
+ }
+
+ if (!t)
+ *is_varargs = true;
+ return i;
+}
+
+/* Creation function node for DECL, using NAME, and return the index
+ of the variable we've created for the function. */
+
+static unsigned int
+create_function_info_for (tree decl, const char *name)
+{
+ unsigned int index = VEC_length (varinfo_t, varmap);
+ varinfo_t vi;
+ tree arg;
+ unsigned int i;
+ bool is_varargs = false;
+
+ /* Create the variable info. */
+
+ vi = new_var_info (decl, index, name);
+ vi->decl = decl;
+ vi->offset = 0;
+ vi->size = 1;
+ vi->fullsize = count_num_arguments (decl, &is_varargs) + 1;
+ insert_vi_for_tree (vi->decl, vi);
+ VEC_safe_push (varinfo_t, heap, varmap, vi);
+
+ stats.total_vars++;
+
+ /* If it's varargs, we don't know how many arguments it has, so we
+ can't do much. */
+ if (is_varargs)
+ {
+ vi->fullsize = ~0;
+ vi->size = ~0;
+ vi->is_unknown_size_var = true;
+ return index;
+ }
+
+
+ arg = DECL_ARGUMENTS (decl);
+
+ /* Set up variables for each argument. */
+ for (i = 1; i < vi->fullsize; i++)
+ {
+ varinfo_t argvi;
+ const char *newname;
+ char *tempname;
+ unsigned int newindex;
+ tree argdecl = decl;
+
+ if (arg)
+ argdecl = arg;
+
+ newindex = VEC_length (varinfo_t, varmap);
+ asprintf (&tempname, "%s.arg%d", name, i-1);
+ newname = ggc_strdup (tempname);
+ free (tempname);
+
+ argvi = new_var_info (argdecl, newindex, newname);
+ argvi->decl = argdecl;
+ VEC_safe_push (varinfo_t, heap, varmap, argvi);
+ argvi->offset = i;
+ argvi->size = 1;
+ argvi->is_full_var = true;
+ argvi->fullsize = vi->fullsize;
+ insert_into_field_list_sorted (vi, argvi);
+ stats.total_vars ++;
+ if (arg)
+ {
+ insert_vi_for_tree (arg, argvi);
+ arg = TREE_CHAIN (arg);
+ }
+ }
+
+ /* Create a variable for the return var. */
+ if (DECL_RESULT (decl) != NULL
+ || !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl))))
+ {
+ varinfo_t resultvi;
+ const char *newname;
+ char *tempname;
+ unsigned int newindex;
+ tree resultdecl = decl;
+
+ vi->fullsize ++;
+
+ if (DECL_RESULT (decl))
+ resultdecl = DECL_RESULT (decl);
+
+ newindex = VEC_length (varinfo_t, varmap);
+ asprintf (&tempname, "%s.result", name);
+ newname = ggc_strdup (tempname);
+ free (tempname);
+
+ resultvi = new_var_info (resultdecl, newindex, newname);
+ resultvi->decl = resultdecl;
+ VEC_safe_push (varinfo_t, heap, varmap, resultvi);
+ resultvi->offset = i;
+ resultvi->size = 1;
+ resultvi->fullsize = vi->fullsize;
+ resultvi->is_full_var = true;
+ insert_into_field_list_sorted (vi, resultvi);
+ stats.total_vars ++;
+ if (DECL_RESULT (decl))
+ insert_vi_for_tree (DECL_RESULT (decl), resultvi);
+ }
+ return index;
+}
+
+
+/* Return true if FIELDSTACK contains fields that overlap.
+ FIELDSTACK is assumed to be sorted by offset. */
+
+static bool
+check_for_overlaps (VEC (fieldoff_s,heap) *fieldstack)
+{
+ fieldoff_s *fo = NULL;
+ unsigned int i;
+ HOST_WIDE_INT lastoffset = -1;
+
+ for (i = 0; VEC_iterate (fieldoff_s, fieldstack, i, fo); i++)
+ {
+ if (fo->offset == lastoffset)
+ return true;
+ lastoffset = fo->offset;
+ }
+ return false;
+}
+
+/* Create a varinfo structure for NAME and DECL, and add it to VARMAP.
+ This will also create any varinfo structures necessary for fields
+ of DECL. */
+
+static unsigned int
+create_variable_info_for (tree decl, const char *name)
+{
+ unsigned int index = VEC_length (varinfo_t, varmap);
+ varinfo_t vi;
+ tree decl_type = TREE_TYPE (decl);
+ tree declsize = DECL_P (decl) ? DECL_SIZE (decl) : TYPE_SIZE (decl_type);
+ bool is_global = DECL_P (decl) ? is_global_var (decl) : false;
+ VEC (fieldoff_s,heap) *fieldstack = NULL;
+
+ if (TREE_CODE (decl) == FUNCTION_DECL && in_ipa_mode)
+ return create_function_info_for (decl, name);
+
+ if (var_can_have_subvars (decl) && use_field_sensitive
+ && (!var_ann (decl)
+ || var_ann (decl)->noalias_state == 0)
+ && (!var_ann (decl)
+ || !var_ann (decl)->is_heapvar))
+ push_fields_onto_fieldstack (decl_type, &fieldstack, 0);
+
+ /* If the variable doesn't have subvars, we may end up needing to
+ sort the field list and create fake variables for all the
+ fields. */
+ vi = new_var_info (decl, index, name);
+ vi->decl = decl;
+ vi->offset = 0;
+ vi->may_have_pointers = could_have_pointers (decl);
+ if (!declsize
+ || !host_integerp (declsize, 1))
+ {
+ vi->is_unknown_size_var = true;
+ vi->fullsize = ~0;
+ vi->size = ~0;
+ }
+ else
+ {
+ vi->fullsize = TREE_INT_CST_LOW (declsize);
+ vi->size = vi->fullsize;
+ }
+
+ insert_vi_for_tree (vi->decl, vi);
+ VEC_safe_push (varinfo_t, heap, varmap, vi);
+ if (is_global && (!flag_whole_program || !in_ipa_mode)
+ && vi->may_have_pointers)
+ {
+ if (var_ann (decl)
+ && var_ann (decl)->noalias_state == NO_ALIAS_ANYTHING)
+ make_constraint_from (vi, vi->id);
+ else
+ make_constraint_from (vi, escaped_id);
+ }
+
+ stats.total_vars++;
+ if (use_field_sensitive
+ && !vi->is_unknown_size_var
+ && var_can_have_subvars (decl)
+ && VEC_length (fieldoff_s, fieldstack) > 1
+ && VEC_length (fieldoff_s, fieldstack) <= MAX_FIELDS_FOR_FIELD_SENSITIVE)
+ {
+ unsigned int newindex = VEC_length (varinfo_t, varmap);
+ fieldoff_s *fo = NULL;
+ bool notokay = false;
+ unsigned int i;
+
+ for (i = 0; !notokay && VEC_iterate (fieldoff_s, fieldstack, i, fo); i++)
+ {
+ if (fo->has_unknown_size
+ || fo->offset < 0)
+ {
+ notokay = true;
+ break;
+ }
+ }
+
+ /* We can't sort them if we have a field with a variable sized type,
+ which will make notokay = true. In that case, we are going to return
+ without creating varinfos for the fields anyway, so sorting them is a
+ waste to boot. */
+ if (!notokay)
+ {
+ sort_fieldstack (fieldstack);
+ /* Due to some C++ FE issues, like PR 22488, we might end up
+ what appear to be overlapping fields even though they,
+ in reality, do not overlap. Until the C++ FE is fixed,
+ we will simply disable field-sensitivity for these cases. */
+ notokay = check_for_overlaps (fieldstack);
+ }
+
+
+ if (VEC_length (fieldoff_s, fieldstack) != 0)
+ fo = VEC_index (fieldoff_s, fieldstack, 0);
+
+ if (fo == NULL || notokay)
+ {
+ vi->is_unknown_size_var = 1;
+ vi->fullsize = ~0;
+ vi->size = ~0;
+ vi->is_full_var = true;
+ VEC_free (fieldoff_s, heap, fieldstack);
+ return index;
+ }
+
+ vi->size = fo->size;
+ vi->offset = fo->offset;
+ vi->may_have_pointers = fo->may_have_pointers;
+ for (i = VEC_length (fieldoff_s, fieldstack) - 1;
+ i >= 1 && VEC_iterate (fieldoff_s, fieldstack, i, fo);
+ i--)
+ {
+ varinfo_t newvi;
+ const char *newname = "NULL";
+ char *tempname;
+
+ newindex = VEC_length (varinfo_t, varmap);
+ if (dump_file)
+ {
+ asprintf (&tempname, "%s." HOST_WIDE_INT_PRINT_DEC
+ "+" HOST_WIDE_INT_PRINT_DEC,
+ vi->name, fo->offset, fo->size);
+ newname = ggc_strdup (tempname);
+ free (tempname);
+ }
+ newvi = new_var_info (decl, newindex, newname);
+ newvi->offset = fo->offset;
+ newvi->size = fo->size;
+ newvi->fullsize = vi->fullsize;
+ newvi->may_have_pointers = fo->may_have_pointers;
+ insert_into_field_list (vi, newvi);
+ VEC_safe_push (varinfo_t, heap, varmap, newvi);
+ if (is_global && (!flag_whole_program || !in_ipa_mode)
+ && newvi->may_have_pointers)
+ make_constraint_from (newvi, escaped_id);
+
+ stats.total_vars++;
+ }
+ }
+ else
+ vi->is_full_var = true;
+
+ VEC_free (fieldoff_s, heap, fieldstack);
+
+ return index;
+}
+
+/* Print out the points-to solution for VAR to FILE. */
+
+void
+dump_solution_for_var (FILE *file, unsigned int var)
+{
+ varinfo_t vi = get_varinfo (var);
+ unsigned int i;
+ bitmap_iterator bi;
+
+ if (find (var) != var)
+ {
+ varinfo_t vipt = get_varinfo (find (var));
+ fprintf (file, "%s = same as %s\n", vi->name, vipt->name);
+ }
+ else
+ {
+ fprintf (file, "%s = { ", vi->name);
+ EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
+ {
+ fprintf (file, "%s ", get_varinfo (i)->name);
+ }
+ fprintf (file, "}");
+ if (vi->no_tbaa_pruning)
+ fprintf (file, " no-tbaa-pruning");
+ fprintf (file, "\n");
+ }
+}
+
+/* Print the points-to solution for VAR to stdout. */
+
+void
+debug_solution_for_var (unsigned int var)
+{
+ dump_solution_for_var (stdout, var);
+}
+
+/* Create varinfo structures for all of the variables in the
+ function for intraprocedural mode. */
+
+static void
+intra_create_variable_infos (void)
+{
+ tree t;
+ struct constraint_expr lhs, rhs;
+
+ /* For each incoming pointer argument arg, create the constraint ARG
+ = NONLOCAL or a dummy variable if flag_argument_noalias is set. */
+ for (t = DECL_ARGUMENTS (current_function_decl); t; t = TREE_CHAIN (t))
+ {
+ varinfo_t p;
+
+ if (!could_have_pointers (t))
+ continue;
+
+ /* If flag_argument_noalias is set, then function pointer
+ arguments are guaranteed not to point to each other. In that
+ case, create an artificial variable PARM_NOALIAS and the
+ constraint ARG = &PARM_NOALIAS. */
+ if (POINTER_TYPE_P (TREE_TYPE (t)) && flag_argument_noalias > 0)
+ {
+ varinfo_t vi;
+ tree heapvar = heapvar_lookup (t);
+
+ lhs.offset = 0;
+ lhs.type = SCALAR;
+ lhs.var = get_vi_for_tree (t)->id;
+
+ if (heapvar == NULL_TREE)
+ {
+ var_ann_t ann;
+ heapvar = create_tmp_var_raw (ptr_type_node,
+ "PARM_NOALIAS");
+ DECL_EXTERNAL (heapvar) = 1;
+ if (gimple_referenced_vars (cfun))
+ add_referenced_var (heapvar);
+
+ heapvar_insert (t, heapvar);
+
+ ann = get_var_ann (heapvar);
+ ann->is_heapvar = 1;
+ if (flag_argument_noalias == 1)
+ ann->noalias_state = NO_ALIAS;
+ else if (flag_argument_noalias == 2)
+ ann->noalias_state = NO_ALIAS_GLOBAL;
+ else if (flag_argument_noalias == 3)
+ ann->noalias_state = NO_ALIAS_ANYTHING;
+ else
+ gcc_unreachable ();
+ }
+
+ vi = get_vi_for_tree (heapvar);
+ vi->is_artificial_var = 1;
+ vi->is_heap_var = 1;
+ vi->is_unknown_size_var = true;
+ vi->fullsize = ~0;
+ vi->size = ~0;
+ rhs.var = vi->id;
+ rhs.type = ADDRESSOF;
+ rhs.offset = 0;
+ for (p = get_varinfo (lhs.var); p; p = p->next)
+ {
+ struct constraint_expr temp = lhs;
+ temp.var = p->id;
+ process_constraint (new_constraint (temp, rhs));
+ }
+ }
+ else
+ {
+ varinfo_t arg_vi = get_vi_for_tree (t);
+
+ for (p = arg_vi; p; p = p->next)
+ make_constraint_from (p, nonlocal_id);
+ }
+ }
+
+ /* Add a constraint for a result decl that is passed by reference. */
+ if (DECL_RESULT (cfun->decl)
+ && DECL_BY_REFERENCE (DECL_RESULT (cfun->decl)))
+ {
+ varinfo_t p, result_vi = get_vi_for_tree (DECL_RESULT (cfun->decl));
+
+ for (p = result_vi; p; p = p->next)
+ make_constraint_from (p, nonlocal_id);
+ }
+
+ /* Add a constraint for the incoming static chain parameter. */
+ if (cfun->static_chain_decl != NULL_TREE)
+ {
+ varinfo_t p, chain_vi = get_vi_for_tree (cfun->static_chain_decl);
+
+ for (p = chain_vi; p; p = p->next)
+ make_constraint_from (p, nonlocal_id);
+ }
+}
+
+/* Structure used to put solution bitmaps in a hashtable so they can
+ be shared among variables with the same points-to set. */
+
+typedef struct shared_bitmap_info
+{
+ bitmap pt_vars;
+ hashval_t hashcode;
+} *shared_bitmap_info_t;
+typedef const struct shared_bitmap_info *const_shared_bitmap_info_t;
+
+static htab_t shared_bitmap_table;
+
+/* Hash function for a shared_bitmap_info_t */
+
+static hashval_t
+shared_bitmap_hash (const void *p)
+{
+ const_shared_bitmap_info_t const bi = (const_shared_bitmap_info_t) p;
+ return bi->hashcode;
+}
+
+/* Equality function for two shared_bitmap_info_t's. */
+
+static int
+shared_bitmap_eq (const void *p1, const void *p2)
+{
+ const_shared_bitmap_info_t const sbi1 = (const_shared_bitmap_info_t) p1;
+ const_shared_bitmap_info_t const sbi2 = (const_shared_bitmap_info_t) p2;
+ return bitmap_equal_p (sbi1->pt_vars, sbi2->pt_vars);
+}
+
+/* Lookup a bitmap in the shared bitmap hashtable, and return an already
+ existing instance if there is one, NULL otherwise. */
+
+static bitmap
+shared_bitmap_lookup (bitmap pt_vars)
+{
+ void **slot;
+ struct shared_bitmap_info sbi;
+
+ sbi.pt_vars = pt_vars;
+ sbi.hashcode = bitmap_hash (pt_vars);
+
+ slot = htab_find_slot_with_hash (shared_bitmap_table, &sbi,
+ sbi.hashcode, NO_INSERT);
+ if (!slot)
+ return NULL;
+ else
+ return ((shared_bitmap_info_t) *slot)->pt_vars;
+}
+
+
+/* Add a bitmap to the shared bitmap hashtable. */
+
+static void
+shared_bitmap_add (bitmap pt_vars)
+{
+ void **slot;
+ shared_bitmap_info_t sbi = XNEW (struct shared_bitmap_info);
+
+ sbi->pt_vars = pt_vars;
+ sbi->hashcode = bitmap_hash (pt_vars);
+
+ slot = htab_find_slot_with_hash (shared_bitmap_table, sbi,
+ sbi->hashcode, INSERT);
+ gcc_assert (!*slot);
+ *slot = (void *) sbi;
+}
+
+
+/* Set bits in INTO corresponding to the variable uids in solution set
+ FROM, which came from variable PTR.
+ For variables that are actually dereferenced, we also use type
+ based alias analysis to prune the points-to sets.
+ IS_DEREFED is true if PTR was directly dereferenced, which we use to
+ help determine whether we are we are allowed to prune using TBAA.
+ If NO_TBAA_PRUNING is true, we do not perform any TBAA pruning of
+ the from set. Returns the number of pruned variables. */
+
+static unsigned
+set_uids_in_ptset (tree ptr, bitmap into, bitmap from, bool is_derefed,
+ bool no_tbaa_pruning)
+{
+ unsigned int i;
+ bitmap_iterator bi;
+ unsigned pruned = 0;
+
+ gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr)));
+
+ EXECUTE_IF_SET_IN_BITMAP (from, 0, i, bi)
+ {
+ varinfo_t vi = get_varinfo (i);
+
+ /* 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;
+
+ if (TREE_CODE (vi->decl) == VAR_DECL
+ || TREE_CODE (vi->decl) == PARM_DECL
+ || TREE_CODE (vi->decl) == RESULT_DECL)
+ {
+ /* Just add VI->DECL to the alias set.
+ Don't type prune artificial vars or points-to sets
+ for pointers that have not been dereferenced or with
+ type-based pruning disabled. */
+ if (vi->is_artificial_var
+ || !is_derefed
+ || no_tbaa_pruning
+ || vi->no_tbaa_pruning)
+ bitmap_set_bit (into, DECL_UID (vi->decl));
+ else
+ {
+ alias_set_type var_alias_set, mem_alias_set;
+ var_alias_set = get_alias_set (vi->decl);
+ mem_alias_set = get_alias_set (TREE_TYPE (TREE_TYPE (ptr)));
+ if (may_alias_p (SSA_NAME_VAR (ptr), mem_alias_set,
+ vi->decl, var_alias_set, true))
+ bitmap_set_bit (into, DECL_UID (vi->decl));
+ else
+ ++pruned;
+ }
+ }
+ }
+
+ return pruned;
+}
+
+
+static bool have_alias_info = false;
+
+/* Emit a note for the pointer initialization point DEF. */
+
+static void
+emit_pointer_definition (tree ptr, bitmap visited)
+{
+ gimple def = SSA_NAME_DEF_STMT (ptr);
+ if (gimple_code (def) == GIMPLE_PHI)
+ {
+ use_operand_p argp;
+ ssa_op_iter oi;
+
+ FOR_EACH_PHI_ARG (argp, def, oi, SSA_OP_USE)
+ {
+ tree arg = USE_FROM_PTR (argp);
+ if (TREE_CODE (arg) == SSA_NAME)
+ {
+ if (bitmap_set_bit (visited, SSA_NAME_VERSION (arg)))
+ emit_pointer_definition (arg, visited);
+ }
+ else
+ inform (0, "initialized from %qE", arg);
+ }
+ }
+ else if (!gimple_nop_p (def))
+ inform (gimple_location (def), "initialized from here");
+}
+
+/* Emit a strict aliasing warning for dereferencing the pointer PTR. */
+
+static void
+emit_alias_warning (tree ptr)
+{
+ gimple use;
+ imm_use_iterator ui;
+ bool warned = false;
+
+ FOR_EACH_IMM_USE_STMT (use, ui, ptr)
+ {
+ tree deref = NULL_TREE;
+
+ if (gimple_has_lhs (use))
+ {
+ tree lhs = get_base_address (gimple_get_lhs (use));
+ if (lhs
+ && INDIRECT_REF_P (lhs)
+ && TREE_OPERAND (lhs, 0) == ptr)
+ deref = lhs;
+ }
+ if (gimple_assign_single_p (use))
+ {
+ tree rhs = get_base_address (gimple_assign_rhs1 (use));
+ if (rhs
+ && INDIRECT_REF_P (rhs)
+ && TREE_OPERAND (rhs, 0) == ptr)
+ deref = rhs;
+ }
+ else if (is_gimple_call (use))
+ {
+ unsigned i;
+ for (i = 0; i < gimple_call_num_args (use); ++i)
+ {
+ tree op = get_base_address (gimple_call_arg (use, i));
+ if (op
+ && INDIRECT_REF_P (op)
+ && TREE_OPERAND (op, 0) == ptr)
+ deref = op;
+ }
+ }
+ if (deref
+ && !TREE_NO_WARNING (deref))
+ {
+ TREE_NO_WARNING (deref) = 1;
+ warned |= warning_at (gimple_location (use), OPT_Wstrict_aliasing,
+ "dereferencing pointer %qD does break "
+ "strict-aliasing rules", SSA_NAME_VAR (ptr));
+ }
+ }
+ if (warned)
+ {
+ bitmap visited = BITMAP_ALLOC (NULL);
+ emit_pointer_definition (ptr, visited);
+ BITMAP_FREE (visited);
+ }
+}
+
+/* Given a pointer variable P, fill in its points-to set, or return
+ false if we can't.
+ Rather than return false for variables that point-to anything, we
+ instead find the corresponding SMT, and merge in its aliases. In
+ addition to these aliases, we also set the bits for the SMT's
+ themselves and their subsets, as SMT's are still in use by
+ non-SSA_NAME's, and pruning may eliminate every one of their
+ aliases. In such a case, if we did not include the right set of
+ SMT's in the points-to set of the variable, we'd end up with
+ statements that do not conflict but should. */
+
+bool
+find_what_p_points_to (tree p)
+{
+ tree lookup_p = p;
+ varinfo_t vi;
+
+ if (!have_alias_info)
+ return false;
+
+ /* For parameters, get at the points-to set for the actual parm
+ decl. */
+ if (TREE_CODE (p) == SSA_NAME
+ && TREE_CODE (SSA_NAME_VAR (p)) == PARM_DECL
+ && SSA_NAME_IS_DEFAULT_DEF (p))
+ lookup_p = SSA_NAME_VAR (p);
+
+ vi = lookup_vi_for_tree (lookup_p);
+ if (vi)
+ {
+ if (vi->is_artificial_var)
+ return false;
+
+ /* 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. */
+ return false;
+ }
+ else
+ {
+ struct ptr_info_def *pi = get_ptr_info (p);
+ unsigned int i, pruned;
+ bitmap_iterator bi;
+ bool was_pt_anything = false;
+ bitmap finished_solution;
+ bitmap result;
+
+ if (!pi->memory_tag_needed)
+ return false;
+
+ /* This variable may have been collapsed, let's get the real
+ variable. */
+ vi = get_varinfo (find (vi->id));
+
+ /* Translate artificial variables into SSA_NAME_PTR_INFO
+ attributes. */
+ EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
+ {
+ varinfo_t vi = get_varinfo (i);
+
+ if (vi->is_artificial_var)
+ {
+ /* FIXME. READONLY should be handled better so that
+ flow insensitive aliasing can disregard writable
+ aliases. */
+ if (vi->id == nothing_id)
+ pi->pt_null = 1;
+ else if (vi->id == anything_id
+ || vi->id == nonlocal_id
+ || vi->id == escaped_id
+ || vi->id == callused_id)
+ was_pt_anything = 1;
+ else if (vi->id == readonly_id)
+ was_pt_anything = 1;
+ else if (vi->id == integer_id)
+ was_pt_anything = 1;
+ else if (vi->is_heap_var)
+ pi->pt_global_mem = 1;
+ }
+ }
+
+ /* Instead of doing extra work, simply do not create
+ points-to information for pt_anything pointers. This
+ will cause the operand scanner to fall back to the
+ type-based SMT and its aliases. Which is the best
+ we could do here for the points-to set as well. */
+ if (was_pt_anything)
+ return false;
+
+ /* Share the final set of variables when possible. */
+ finished_solution = BITMAP_GGC_ALLOC ();
+ stats.points_to_sets_created++;
+
+ pruned = set_uids_in_ptset (p, finished_solution, vi->solution,
+ pi->is_dereferenced,
+ vi->no_tbaa_pruning);
+ result = shared_bitmap_lookup (finished_solution);
+
+ if (!result)
+ {
+ shared_bitmap_add (finished_solution);
+ pi->pt_vars = finished_solution;
+ }
+ else
+ {
+ pi->pt_vars = result;
+ bitmap_clear (finished_solution);
+ }
+
+ if (bitmap_empty_p (pi->pt_vars))
+ {
+ pi->pt_vars = NULL;
+ if (pruned > 0
+ && !pi->pt_null
+ && pi->is_dereferenced
+ && warn_strict_aliasing > 0
+ && !SSA_NAME_IS_DEFAULT_DEF (p))
+ {
+ if (dump_file && dump_flags & TDF_DETAILS)
+ {
+ fprintf (dump_file, "alias warning for ");
+ print_generic_expr (dump_file, p, 0);
+ fprintf (dump_file, "\n");
+ }
+ emit_alias_warning (p);
+ }
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Mark the ESCAPED solution as call clobbered. Returns false if
+ pt_anything escaped which needs all locals that have their address
+ taken marked call clobbered as well. */
+
+bool
+clobber_what_escaped (void)
+{
+ varinfo_t vi;
+ unsigned int i;
+ bitmap_iterator bi;
+
+ if (!have_alias_info)
+ return false;
+
+ /* This variable may have been collapsed, let's get the real
+ variable for escaped_id. */
+ vi = get_varinfo (find (escaped_id));
+
+ /* If call-used memory escapes we need to include it in the
+ set of escaped variables. This can happen if a pure
+ function returns a pointer and this pointer escapes. */
+ if (bitmap_bit_p (vi->solution, callused_id))
+ {
+ varinfo_t cu_vi = get_varinfo (find (callused_id));
+ bitmap_ior_into (vi->solution, cu_vi->solution);
+ }
+
+ /* Mark variables in the solution call-clobbered. */
+ EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
+ {
+ varinfo_t vi = get_varinfo (i);
+
+ if (vi->is_artificial_var)
+ {
+ /* nothing_id and readonly_id do not cause any
+ call clobber ops. For anything_id and integer_id
+ we need to clobber all addressable vars. */
+ if (vi->id == anything_id
+ || vi->id == integer_id)
+ return false;
+ }
+
+ /* Only artificial heap-vars are further interesting. */
+ if (vi->is_artificial_var && !vi->is_heap_var)
+ continue;
+
+ if ((TREE_CODE (vi->decl) == VAR_DECL
+ || TREE_CODE (vi->decl) == PARM_DECL
+ || TREE_CODE (vi->decl) == RESULT_DECL)
+ && !unmodifiable_var_p (vi->decl))
+ mark_call_clobbered (vi->decl, ESCAPE_TO_CALL);
+ }
+
+ return true;
+}
+
+/* Compute the call-used variables. */
+
+void
+compute_call_used_vars (void)
+{
+ varinfo_t vi;
+ unsigned int i;
+ bitmap_iterator bi;
+ bool has_anything_id = false;
+
+ if (!have_alias_info)
+ return;
+
+ /* This variable may have been collapsed, let's get the real
+ variable for escaped_id. */
+ vi = get_varinfo (find (callused_id));
+
+ /* Mark variables in the solution call-clobbered. */
+ EXECUTE_IF_SET_IN_BITMAP (vi->solution, 0, i, bi)
+ {
+ varinfo_t vi = get_varinfo (i);
+
+ if (vi->is_artificial_var)
+ {
+ /* For anything_id and integer_id we need to make
+ all local addressable vars call-used. */
+ if (vi->id == anything_id
+ || vi->id == integer_id)
+ has_anything_id = true;
+ }
+
+ /* Only artificial heap-vars are further interesting. */
+ if (vi->is_artificial_var && !vi->is_heap_var)
+ continue;
+
+ if ((TREE_CODE (vi->decl) == VAR_DECL
+ || TREE_CODE (vi->decl) == PARM_DECL
+ || TREE_CODE (vi->decl) == RESULT_DECL)
+ && !unmodifiable_var_p (vi->decl))
+ bitmap_set_bit (gimple_call_used_vars (cfun), DECL_UID (vi->decl));
+ }
+
+ /* If anything is call-used, add all addressable locals to the set. */
+ if (has_anything_id)
+ bitmap_ior_into (gimple_call_used_vars (cfun),
+ gimple_addressable_vars (cfun));
+}
+
+
+/* Dump points-to information to OUTFILE. */
+
+void
+dump_sa_points_to_info (FILE *outfile)
+{
+ unsigned int i;
+
+ fprintf (outfile, "\nPoints-to sets\n\n");
+
+ if (dump_flags & TDF_STATS)
+ {
+ fprintf (outfile, "Stats:\n");
+ fprintf (outfile, "Total vars: %d\n", stats.total_vars);
+ fprintf (outfile, "Non-pointer vars: %d\n",
+ stats.nonpointer_vars);
+ fprintf (outfile, "Statically unified vars: %d\n",
+ stats.unified_vars_static);
+ fprintf (outfile, "Dynamically unified vars: %d\n",
+ stats.unified_vars_dynamic);
+ fprintf (outfile, "Iterations: %d\n", stats.iterations);
+ fprintf (outfile, "Number of edges: %d\n", stats.num_edges);
+ fprintf (outfile, "Number of implicit edges: %d\n",
+ stats.num_implicit_edges);
+ }
+
+ for (i = 0; i < VEC_length (varinfo_t, varmap); i++)
+ dump_solution_for_var (outfile, i);
+}
+
+
+/* Debug points-to information to stderr. */
+
+void
+debug_sa_points_to_info (void)
+{
+ dump_sa_points_to_info (stderr);
+}
+
+
+/* Initialize the always-existing constraint variables for NULL
+ ANYTHING, READONLY, and INTEGER */
+
+static void
+init_base_vars (void)
+{
+ struct constraint_expr lhs, rhs;
+
+ /* Create the NULL variable, used to represent that a variable points
+ to NULL. */
+ nothing_tree = create_tmp_var_raw (void_type_node, "NULL");
+ var_nothing = new_var_info (nothing_tree, nothing_id, "NULL");
+ insert_vi_for_tree (nothing_tree, var_nothing);
+ var_nothing->is_artificial_var = 1;
+ var_nothing->offset = 0;
+ var_nothing->size = ~0;
+ var_nothing->fullsize = ~0;
+ var_nothing->is_special_var = 1;
+ VEC_safe_push (varinfo_t, heap, varmap, var_nothing);
+
+ /* Create the ANYTHING variable, used to represent that a variable
+ points to some unknown piece of memory. */
+ anything_tree = create_tmp_var_raw (void_type_node, "ANYTHING");
+ var_anything = new_var_info (anything_tree, anything_id, "ANYTHING");
+ insert_vi_for_tree (anything_tree, var_anything);
+ var_anything->is_artificial_var = 1;
+ var_anything->size = ~0;
+ var_anything->offset = 0;
+ var_anything->next = NULL;
+ var_anything->fullsize = ~0;
+ var_anything->is_special_var = 1;
+
+ /* Anything points to anything. This makes deref constraints just
+ work in the presence of linked list and other p = *p type loops,
+ by saying that *ANYTHING = ANYTHING. */
+ VEC_safe_push (varinfo_t, heap, varmap, var_anything);
+ lhs.type = SCALAR;
+ lhs.var = anything_id;
+ lhs.offset = 0;
+ rhs.type = ADDRESSOF;
+ rhs.var = anything_id;
+ rhs.offset = 0;
+
+ /* This specifically does not use process_constraint because
+ process_constraint ignores all anything = anything constraints, since all
+ but this one are redundant. */
+ VEC_safe_push (constraint_t, heap, constraints, new_constraint (lhs, rhs));
+
+ /* Create the READONLY variable, used to represent that a variable
+ points to readonly memory. */
+ readonly_tree = create_tmp_var_raw (void_type_node, "READONLY");
+ var_readonly = new_var_info (readonly_tree, readonly_id, "READONLY");
+ var_readonly->is_artificial_var = 1;
+ var_readonly->offset = 0;
+ var_readonly->size = ~0;
+ var_readonly->fullsize = ~0;
+ var_readonly->next = NULL;
+ var_readonly->is_special_var = 1;
+ insert_vi_for_tree (readonly_tree, var_readonly);
+ VEC_safe_push (varinfo_t, heap, varmap, var_readonly);
+
+ /* readonly memory points to anything, in order to make deref
+ easier. In reality, it points to anything the particular
+ readonly variable can point to, but we don't track this
+ separately. */
+ lhs.type = SCALAR;
+ lhs.var = readonly_id;
+ lhs.offset = 0;
+ rhs.type = ADDRESSOF;
+ rhs.var = readonly_id; /* FIXME */
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+
+ /* Create the ESCAPED variable, used to represent the set of escaped
+ memory. */
+ escaped_tree = create_tmp_var_raw (void_type_node, "ESCAPED");
+ var_escaped = new_var_info (escaped_tree, escaped_id, "ESCAPED");
+ insert_vi_for_tree (escaped_tree, var_escaped);
+ var_escaped->is_artificial_var = 1;
+ var_escaped->offset = 0;
+ var_escaped->size = ~0;
+ var_escaped->fullsize = ~0;
+ var_escaped->is_special_var = 0;
+ VEC_safe_push (varinfo_t, heap, varmap, var_escaped);
+ gcc_assert (VEC_index (varinfo_t, varmap, 3) == var_escaped);
+
+ /* ESCAPED = *ESCAPED, because escaped is may-deref'd at calls, etc. */
+ lhs.type = SCALAR;
+ lhs.var = escaped_id;
+ lhs.offset = 0;
+ rhs.type = DEREF;
+ rhs.var = escaped_id;
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+
+ /* Create the NONLOCAL variable, used to represent the set of nonlocal
+ memory. */
+ nonlocal_tree = create_tmp_var_raw (void_type_node, "NONLOCAL");
+ var_nonlocal = new_var_info (nonlocal_tree, nonlocal_id, "NONLOCAL");
+ insert_vi_for_tree (nonlocal_tree, var_nonlocal);
+ var_nonlocal->is_artificial_var = 1;
+ var_nonlocal->offset = 0;
+ var_nonlocal->size = ~0;
+ var_nonlocal->fullsize = ~0;
+ var_nonlocal->is_special_var = 1;
+ VEC_safe_push (varinfo_t, heap, varmap, var_nonlocal);
+
+ /* Nonlocal memory points to escaped (which includes nonlocal),
+ in order to make deref easier. */
+ lhs.type = SCALAR;
+ lhs.var = nonlocal_id;
+ lhs.offset = 0;
+ rhs.type = ADDRESSOF;
+ rhs.var = escaped_id;
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+
+ /* Create the CALLUSED variable, used to represent the set of call-used
+ memory. */
+ callused_tree = create_tmp_var_raw (void_type_node, "CALLUSED");
+ var_callused = new_var_info (callused_tree, callused_id, "CALLUSED");
+ insert_vi_for_tree (callused_tree, var_callused);
+ var_callused->is_artificial_var = 1;
+ var_callused->offset = 0;
+ var_callused->size = ~0;
+ var_callused->fullsize = ~0;
+ var_callused->is_special_var = 0;
+ VEC_safe_push (varinfo_t, heap, varmap, var_callused);
+
+ /* CALLUSED = *CALLUSED, because call-used is may-deref'd at calls, etc. */
+ lhs.type = SCALAR;
+ lhs.var = callused_id;
+ lhs.offset = 0;
+ rhs.type = DEREF;
+ rhs.var = callused_id;
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+
+ /* Create the STOREDANYTHING variable, used to represent the set of
+ variables stored to *ANYTHING. */
+ storedanything_tree = create_tmp_var_raw (ptr_type_node, "STOREDANYTHING");
+ var_storedanything = new_var_info (storedanything_tree, storedanything_id,
+ "STOREDANYTHING");
+ insert_vi_for_tree (storedanything_tree, var_storedanything);
+ var_storedanything->is_artificial_var = 1;
+ var_storedanything->offset = 0;
+ var_storedanything->size = ~0;
+ var_storedanything->fullsize = ~0;
+ var_storedanything->is_special_var = 0;
+ VEC_safe_push (varinfo_t, heap, varmap, var_storedanything);
+
+ /* Create the INTEGER variable, used to represent that a variable points
+ to an INTEGER. */
+ integer_tree = create_tmp_var_raw (void_type_node, "INTEGER");
+ var_integer = new_var_info (integer_tree, integer_id, "INTEGER");
+ insert_vi_for_tree (integer_tree, var_integer);
+ var_integer->is_artificial_var = 1;
+ var_integer->size = ~0;
+ var_integer->fullsize = ~0;
+ var_integer->offset = 0;
+ var_integer->next = NULL;
+ var_integer->is_special_var = 1;
+ VEC_safe_push (varinfo_t, heap, varmap, var_integer);
+
+ /* INTEGER = ANYTHING, because we don't know where a dereference of
+ a random integer will point to. */
+ lhs.type = SCALAR;
+ lhs.var = integer_id;
+ lhs.offset = 0;
+ rhs.type = ADDRESSOF;
+ rhs.var = anything_id;
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+
+ /* *ESCAPED = &ESCAPED. This is true because we have to assume
+ everything pointed to by escaped can also point to escaped. */
+ lhs.type = DEREF;
+ lhs.var = escaped_id;
+ lhs.offset = 0;
+ rhs.type = ADDRESSOF;
+ rhs.var = escaped_id;
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+
+ /* *ESCAPED = &NONLOCAL. This is true because we have to assume
+ everything pointed to by escaped can also point to nonlocal. */
+ lhs.type = DEREF;
+ lhs.var = escaped_id;
+ lhs.offset = 0;
+ rhs.type = ADDRESSOF;
+ rhs.var = nonlocal_id;
+ rhs.offset = 0;
+ process_constraint (new_constraint (lhs, rhs));
+}
+
+/* Initialize things necessary to perform PTA */
+
+static void
+init_alias_vars (void)
+{
+ use_field_sensitive = (MAX_FIELDS_FOR_FIELD_SENSITIVE > 1);
+
+ bitmap_obstack_initialize (&pta_obstack);
+ bitmap_obstack_initialize (&oldpta_obstack);
+ bitmap_obstack_initialize (&predbitmap_obstack);
+
+ constraint_pool = create_alloc_pool ("Constraint pool",
+ sizeof (struct constraint), 30);
+ variable_info_pool = create_alloc_pool ("Variable info pool",
+ sizeof (struct variable_info), 30);
+ constraints = VEC_alloc (constraint_t, heap, 8);
+ varmap = VEC_alloc (varinfo_t, heap, 8);
+ vi_for_tree = pointer_map_create ();
+
+ memset (&stats, 0, sizeof (stats));
+ shared_bitmap_table = htab_create (511, shared_bitmap_hash,
+ shared_bitmap_eq, free);
+ init_base_vars ();
+}
+
+/* Remove the REF and ADDRESS edges from GRAPH, as well as all the
+ predecessor edges. */
+
+static void
+remove_preds_and_fake_succs (constraint_graph_t graph)
+{
+ unsigned int i;
+
+ /* Clear the implicit ref and address nodes from the successor
+ lists. */
+ for (i = 0; i < FIRST_REF_NODE; i++)
+ {
+ if (graph->succs[i])
+ bitmap_clear_range (graph->succs[i], FIRST_REF_NODE,
+ FIRST_REF_NODE * 2);
+ }
+
+ /* Free the successor list for the non-ref nodes. */
+ for (i = FIRST_REF_NODE; i < graph->size; i++)
+ {
+ if (graph->succs[i])
+ BITMAP_FREE (graph->succs[i]);
+ }
+
+ /* Now reallocate the size of the successor list as, and blow away
+ the predecessor bitmaps. */
+ graph->size = VEC_length (varinfo_t, varmap);
+ graph->succs = XRESIZEVEC (bitmap, graph->succs, graph->size);
+
+ free (graph->implicit_preds);
+ graph->implicit_preds = NULL;
+ free (graph->preds);
+ graph->preds = NULL;
+ bitmap_obstack_release (&predbitmap_obstack);
+}
+
+/* Compute the set of variables we can't TBAA prune. */
+
+static void
+compute_tbaa_pruning (void)
+{
+ unsigned int size = VEC_length (varinfo_t, varmap);
+ unsigned int i;
+ bool any;
+
+ changed_count = 0;
+ changed = sbitmap_alloc (size);
+ sbitmap_zero (changed);
+
+ /* Mark all initial no_tbaa_pruning nodes as changed. */
+ any = false;
+ for (i = 0; i < size; ++i)
+ {
+ varinfo_t ivi = get_varinfo (i);
+
+ if (find (i) == i && ivi->no_tbaa_pruning)
+ {
+ any = true;
+ if ((graph->succs[i] && !bitmap_empty_p (graph->succs[i]))
+ || VEC_length (constraint_t, graph->complex[i]) > 0)
+ {
+ SET_BIT (changed, i);
+ ++changed_count;
+ }
+ }
+ }
+
+ while (changed_count > 0)
+ {
+ struct topo_info *ti = init_topo_info ();
+ ++stats.iterations;
+
+ compute_topo_order (graph, ti);
+
+ while (VEC_length (unsigned, ti->topo_order) != 0)
+ {
+ bitmap_iterator bi;
+
+ i = VEC_pop (unsigned, ti->topo_order);
+
+ /* If this variable is not a representative, skip it. */
+ if (find (i) != i)
+ continue;
+
+ /* If the node has changed, we need to process the complex
+ constraints and outgoing edges again. */
+ if (TEST_BIT (changed, i))
+ {
+ unsigned int j;
+ constraint_t c;
+ VEC(constraint_t,heap) *complex = graph->complex[i];
+
+ RESET_BIT (changed, i);
+ --changed_count;
+
+ /* Process the complex copy constraints. */
+ for (j = 0; VEC_iterate (constraint_t, complex, j, c); ++j)
+ {
+ if (c->lhs.type == SCALAR && c->rhs.type == SCALAR)
+ {
+ varinfo_t lhsvi = get_varinfo (find (c->lhs.var));
+
+ if (!lhsvi->no_tbaa_pruning)
+ {
+ lhsvi->no_tbaa_pruning = true;
+ if (!TEST_BIT (changed, lhsvi->id))
+ {
+ SET_BIT (changed, lhsvi->id);
+ ++changed_count;
+ }
+ }
+ }
+ }
+
+ /* Propagate to all successors. */
+ EXECUTE_IF_IN_NONNULL_BITMAP (graph->succs[i], 0, j, bi)
+ {
+ unsigned int to = find (j);
+ varinfo_t tovi = get_varinfo (to);
+
+ /* Don't propagate to ourselves. */
+ if (to == i)
+ continue;
+
+ if (!tovi->no_tbaa_pruning)
+ {
+ tovi->no_tbaa_pruning = true;
+ if (!TEST_BIT (changed, to))
+ {
+ SET_BIT (changed, to);
+ ++changed_count;
+ }
+ }
+ }
+ }
+ }
+
+ free_topo_info (ti);
+ }
+
+ sbitmap_free (changed);
+
+ if (any)
+ {
+ for (i = 0; i < size; ++i)
+ {
+ varinfo_t ivi = get_varinfo (i);
+ varinfo_t ivip = get_varinfo (find (i));
+
+ if (ivip->no_tbaa_pruning)
+ {
+ tree var = ivi->decl;
+
+ if (TREE_CODE (var) == SSA_NAME)
+ var = SSA_NAME_VAR (var);
+
+ if (POINTER_TYPE_P (TREE_TYPE (var)))
+ {
+ DECL_NO_TBAA_P (var) = 1;
+
+ /* Tell the RTL layer that this pointer can alias
+ anything. */
+ DECL_POINTER_ALIAS_SET (var) = 0;
+ }
+ }
+ }
+ }
+}
+
+/* Create points-to sets for the current function. See the comments
+ at the start of the file for an algorithmic overview. */
+
+void
+compute_points_to_sets (void)
+{
+ struct scc_info *si;
+ basic_block bb;
+
+ timevar_push (TV_TREE_PTA);
+
+ init_alias_vars ();
+ init_alias_heapvars ();
+
+ intra_create_variable_infos ();
+
+ /* Now walk all statements and derive aliases. */
+ FOR_EACH_BB (bb)
+ {
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple phi = gsi_stmt (gsi);
+
+ if (is_gimple_reg (gimple_phi_result (phi)))
+ find_func_aliases (phi);
+ }
+
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ find_func_aliases (gsi_stmt (gsi));
+ }
+
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Points-to analysis\n\nConstraints:\n\n");
+ dump_constraints (dump_file);
+ }
+
+ if (dump_file)
+ fprintf (dump_file,
+ "\nCollapsing static cycles and doing variable "
+ "substitution\n");
+
+ init_graph (VEC_length (varinfo_t, varmap) * 2);
+
+ if (dump_file)
+ fprintf (dump_file, "Building predecessor graph\n");
+ build_pred_graph ();
+
+ if (dump_file)
+ fprintf (dump_file, "Detecting pointer and location "
+ "equivalences\n");
+ si = perform_var_substitution (graph);
+
+ if (dump_file)
+ fprintf (dump_file, "Rewriting constraints and unifying "
+ "variables\n");
+ rewrite_constraints (graph, si);
+
+ build_succ_graph ();
+ free_var_substitution_info (si);
+
+ if (dump_file && (dump_flags & TDF_GRAPH))
+ dump_constraint_graph (dump_file);
+
+ move_complex_constraints (graph);
+
+ if (dump_file)
+ fprintf (dump_file, "Uniting pointer but not location equivalent "
+ "variables\n");
+ unite_pointer_equivalences (graph);
+
+ if (dump_file)
+ fprintf (dump_file, "Finding indirect cycles\n");
+ find_indirect_cycles (graph);
+
+ /* Implicit nodes and predecessors are no longer necessary at this
+ point. */
+ remove_preds_and_fake_succs (graph);
+
+ if (dump_file)
+ fprintf (dump_file, "Solving graph\n");
+
+ solve_graph (graph);
+
+ compute_tbaa_pruning ();
+
+ if (dump_file)
+ dump_sa_points_to_info (dump_file);
+
+ have_alias_info = true;
+
+ timevar_pop (TV_TREE_PTA);
+}
+
+
+/* Delete created points-to sets. */
+
+void
+delete_points_to_sets (void)
+{
+ unsigned int i;
+
+ htab_delete (shared_bitmap_table);
+ if (dump_file && (dump_flags & TDF_STATS))
+ fprintf (dump_file, "Points to sets created:%d\n",
+ stats.points_to_sets_created);
+
+ pointer_map_destroy (vi_for_tree);
+ bitmap_obstack_release (&pta_obstack);
+ VEC_free (constraint_t, heap, constraints);
+
+ for (i = 0; i < graph->size; i++)
+ VEC_free (constraint_t, heap, graph->complex[i]);
+ free (graph->complex);
+
+ free (graph->rep);
+ free (graph->succs);
+ free (graph->pe);
+ free (graph->pe_rep);
+ free (graph->indirect_cycles);
+ free (graph);
+
+ VEC_free (varinfo_t, heap, varmap);
+ free_alloc_pool (variable_info_pool);
+ free_alloc_pool (constraint_pool);
+ have_alias_info = false;
+}
+
+/* Return true if we should execute IPA PTA. */
+static bool
+gate_ipa_pta (void)
+{
+ return (flag_ipa_pta
+ /* Don't bother doing anything if the program has errors. */
+ && !(errorcount || sorrycount));
+}
+
+/* Execute the driver for IPA PTA. */
+static unsigned int
+ipa_pta_execute (void)
+{
+ struct cgraph_node *node;
+ struct scc_info *si;
+
+ in_ipa_mode = 1;
+ init_alias_heapvars ();
+ init_alias_vars ();
+
+ for (node = cgraph_nodes; node; node = node->next)
+ {
+ if (!node->analyzed || cgraph_is_master_clone (node))
+ {
+ unsigned int varid;
+
+ varid = create_function_info_for (node->decl,
+ cgraph_node_name (node));
+ if (node->local.externally_visible)
+ {
+ varinfo_t fi = get_varinfo (varid);
+ for (; fi; fi = fi->next)
+ make_constraint_from (fi, anything_id);
+ }
+ }
+ }
+ for (node = cgraph_nodes; node; node = node->next)
+ {
+ if (node->analyzed && cgraph_is_master_clone (node))
+ {
+ struct function *func = DECL_STRUCT_FUNCTION (node->decl);
+ basic_block bb;
+ tree old_func_decl = current_function_decl;
+ if (dump_file)
+ fprintf (dump_file,
+ "Generating constraints for %s\n",
+ cgraph_node_name (node));
+ push_cfun (func);
+ current_function_decl = node->decl;
+
+ FOR_EACH_BB_FN (bb, func)
+ {
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ {
+ gimple phi = gsi_stmt (gsi);
+
+ if (is_gimple_reg (gimple_phi_result (phi)))
+ find_func_aliases (phi);
+ }
+
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ find_func_aliases (gsi_stmt (gsi));
+ }
+ current_function_decl = old_func_decl;
+ pop_cfun ();
+ }
+ else
+ {
+ /* Make point to anything. */
+ }
+ }
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "Points-to analysis\n\nConstraints:\n\n");
+ dump_constraints (dump_file);
+ }
+
+ if (dump_file)
+ fprintf (dump_file,
+ "\nCollapsing static cycles and doing variable "
+ "substitution:\n");
+
+ init_graph (VEC_length (varinfo_t, varmap) * 2);
+ build_pred_graph ();
+ si = perform_var_substitution (graph);
+ rewrite_constraints (graph, si);
+
+ build_succ_graph ();
+ free_var_substitution_info (si);
+ move_complex_constraints (graph);
+ unite_pointer_equivalences (graph);
+ find_indirect_cycles (graph);
+
+ /* Implicit nodes and predecessors are no longer necessary at this
+ point. */
+ remove_preds_and_fake_succs (graph);
+
+ if (dump_file)
+ fprintf (dump_file, "\nSolving graph\n");
+
+ solve_graph (graph);
+
+ if (dump_file)
+ dump_sa_points_to_info (dump_file);
+
+ in_ipa_mode = 0;
+ delete_alias_heapvars ();
+ delete_points_to_sets ();
+ return 0;
+}
+
+struct simple_ipa_opt_pass pass_ipa_pta =
+{
+ {
+ SIMPLE_IPA_PASS,
+ "pta", /* name */
+ gate_ipa_pta, /* gate */
+ ipa_pta_execute, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_IPA_PTA, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_update_ssa /* todo_flags_finish */
+ }
+};
+
+/* Initialize the heapvar for statement mapping. */
+void
+init_alias_heapvars (void)
+{
+ if (!heapvar_for_stmt)
+ heapvar_for_stmt = htab_create_ggc (11, tree_map_hash, tree_map_eq,
+ NULL);
+}
+
+void
+delete_alias_heapvars (void)
+{
+ htab_delete (heapvar_for_stmt);
+ heapvar_for_stmt = NULL;
+}
+
+#include "gt-tree-ssa-structalias.h"