blob: 0253f056d2ab2d1f61d1164a7c9f612cd5e609cf [file] [log] [blame]
/* Andersen-style solver for tree based points-to analysis
Copyright (C) 2005-2025 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 "backend.h"
#include "tree-ssa-structalias.h"
#include "pta-andersen.h"
/* 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 (varmap).length ()
#define LAST_REF_NODE (FIRST_REF_NODE + (FIRST_REF_NODE - 1))
#define EXECUTE_IF_IN_NONNULL_BITMAP(a, b, c, d) \
if (a) \
EXECUTE_IF_SET_IN_BITMAP (a, b, c, d)
using namespace pointer_analysis;
/* Used for predecessor bitmaps. */
static bitmap_obstack predbitmap_obstack;
/* Used for per-solver-iteration bitmaps. */
static bitmap_obstack iteration_obstack;
typedef struct constraint_graph *constraint_graph_t;
/* 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> *complex;
};
static constraint_graph_t graph;
static void unify_nodes (constraint_graph_t, unsigned int, unsigned int, bool);
/* 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_checking_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_checking_assert (to < graph->size && from < graph->size);
if (to != from && graph->rep[from] != to)
{
graph->rep[from] = to;
return true;
}
return false;
}
/* Perform path compression for all nodes in the node representatives
union-find structure. */
static void
union_find_compress_all (void)
{
unsigned int i;
for (i = 0; i < graph->size; i++)
find (i);
}
/* Print the constraint graph in dot format. */
static void
dump_constraint_graph (FILE *file)
{
unsigned int i;
/* Only print the graph if it has already been initialized: */
if (!graph)
return;
/* Prints the header of the dot file: */
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 and complex constraints in "
"the constraint graph:\n");
/* The next lines print the nodes in the graph together with the
complex constraints attached to them. */
for (i = 1; i < graph->size; i++)
{
if (i == FIRST_REF_NODE)
continue;
if (find (i) != i)
continue;
if (i < FIRST_REF_NODE)
fprintf (file, "\"%s\"", get_varinfo (i)->name);
else
fprintf (file, "\"*%s\"", get_varinfo (i - FIRST_REF_NODE)->name);
if (graph->complex[i].exists ())
{
unsigned j;
constraint_t c;
fprintf (file, " [label=\"\\N\\n");
for (j = 0; graph->complex[i].iterate (j, &c); ++j)
{
dump_constraint (file, c);
fprintf (file, "\\l");
}
fprintf (file, "\"]");
}
fprintf (file, ";\n");
}
/* Go over the edges. */
fprintf (file, "\n // Edges in the constraint graph:\n");
for (i = 1; i < graph->size; i++)
{
unsigned j;
bitmap_iterator bi;
if (find (i) != i)
continue;
EXECUTE_IF_IN_NONNULL_BITMAP (graph->succs[i], 0, j, bi)
{
unsigned to = find (j);
if (i == to)
continue;
if (i < FIRST_REF_NODE)
fprintf (file, "\"%s\"", get_varinfo (i)->name);
else
fprintf (file, "\"*%s\"", get_varinfo (i - FIRST_REF_NODE)->name);
fprintf (file, " -> ");
if (to < FIRST_REF_NODE)
fprintf (file, "\"%s\"", get_varinfo (to)->name);
else
fprintf (file, "\"*%s\"", get_varinfo (to - FIRST_REF_NODE)->name);
fprintf (file, ";\n");
}
}
/* Prints the tail of the dot file. */
fprintf (file, "}\n");
}
/* Print out the constraint graph to stderr. */
DEBUG_FUNCTION 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 (const constraint &a, const 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> vec,
constraint &lookfor)
{
unsigned int place;
constraint_t found;
if (!vec.exists ())
return NULL;
place = vec.lower_bound (&lookfor, constraint_less);
if (place >= vec.length ())
return NULL;
found = vec[place];
if (!constraint_equal (*found, lookfor))
return NULL;
return found;
}
/* Union two constraint vectors, TO and FROM. Put the result in TO.
Returns true of TO set is changed. */
static bool
constraint_set_union (vec<constraint_t> *to,
vec<constraint_t> *from)
{
int i;
constraint_t c;
bool any_change = false;
FOR_EACH_VEC_ELT (*from, i, c)
{
if (constraint_vec_find (*to, *c) == NULL)
{
unsigned int place = to->lower_bound (c, constraint_less);
to->safe_insert (place, c);
any_change = true;
}
}
return any_change;
}
/* Expands the solution in SET to all sub-fields of variables included. */
static bitmap
solution_set_expand (bitmap set, bitmap *expanded)
{
bitmap_iterator bi;
unsigned j;
if (*expanded)
return *expanded;
*expanded = BITMAP_ALLOC (&iteration_obstack);
/* In a first pass expand variables, once for each head to avoid
quadratic behavior, to include all sub-fields. */
unsigned prev_head = 0;
EXECUTE_IF_SET_IN_BITMAP (set, 0, j, bi)
{
varinfo_t v = get_varinfo (j);
if (v->is_artificial_var
|| v->is_full_var)
continue;
if (v->head != prev_head)
{
varinfo_t head = get_varinfo (v->head);
unsigned num = 1;
for (varinfo_t n = vi_next (head); n != NULL; n = vi_next (n))
{
if (n->id != head->id + num)
{
/* Usually sub variables are adjacent but since we
create pointed-to restrict representatives there
can be gaps as well. */
bitmap_set_range (*expanded, head->id, num);
head = n;
num = 1;
}
else
num++;
}
bitmap_set_range (*expanded, head->id, num);
prev_head = v->head;
}
}
/* And finally set the rest of the bits from SET in an efficient way. */
bitmap_ior_into (*expanded, set);
return *expanded;
}
/* Union solution sets TO and DELTA, and add INC to each member of DELTA in the
process. */
static bool
set_union_with_increment (bitmap to, bitmap delta, HOST_WIDE_INT inc,
bitmap *expanded_delta)
{
bool changed = false;
bitmap_iterator bi;
unsigned int i;
/* If the solution of DELTA contains anything it is good enough to transfer
this to TO. */
if (bitmap_bit_p (delta, anything_id))
return bitmap_set_bit (to, anything_id);
/* If the offset is unknown we have to expand the solution to
all subfields. */
if (inc == UNKNOWN_OFFSET)
{
delta = solution_set_expand (delta, expanded_delta);
changed |= bitmap_ior_into (to, delta);
return changed;
}
/* For non-zero offset union the offsetted solution into the destination. */
EXECUTE_IF_SET_IN_BITMAP (delta, 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)
changed |= bitmap_set_bit (to, i);
else
{
HOST_WIDE_INT fieldoffset = vi->offset + inc;
unsigned HOST_WIDE_INT size = vi->size;
/* If the offset makes the pointer point to before the
variable use offset zero for the field lookup. */
if (fieldoffset < 0)
vi = get_varinfo (vi->head);
else
vi = first_or_preceding_vi_for_offset (vi, fieldoffset);
do
{
changed |= bitmap_set_bit (to, vi->id);
if (vi->is_full_var
|| vi->next == 0)
break;
/* We have to include all fields that overlap the current field
shifted by inc. */
vi = vi_next (vi);
}
while (vi->offset < fieldoffset + size);
}
}
return changed;
}
/* 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> complex = graph->complex[var];
unsigned int place = complex.lower_bound (c, constraint_less);
/* Only insert constraints that do not already exist. */
if (place >= complex.length ()
|| !constraint_equal (*c, *complex[place]))
graph->complex[var].safe_insert (place, c);
}
/* Condense two variable nodes into a single variable node, by moving
all associated info from FROM to TO. Returns true if TO node's
constraint set changes after the merge. */
static bool
merge_node_constraints (constraint_graph_t graph, unsigned int to,
unsigned int from)
{
unsigned int i;
constraint_t c;
bool any_change = false;
gcc_checking_assert (find (from) == to);
/* Move all complex constraints from src node into to node. */
FOR_EACH_VEC_ELT (graph->complex[from], i, c)
{
/* In complex constraints for node FROM, we may have either
a = *FROM, and *FROM = 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;
}
any_change = constraint_set_union (&graph->complex[to],
&graph->complex[from]);
graph->complex[from].release ();
return any_change;
}
/* 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);
/* The graph solving process does not avoid "triangles", thus
there can be multiple paths from a node to another involving
intermediate other nodes. That causes extra copying which is
most difficult to avoid when the intermediate node is ESCAPED
because there are no edges added from ESCAPED. Avoid
adding the direct edge FROM -> TO when we have FROM -> ESCAPED
and TO contains ESCAPED.
??? Note this is only a heuristic, it does not prevent the
situation from occuring. The heuristic helps PR38474 and
PR99912 significantly. */
if (to < FIRST_REF_NODE
&& bitmap_bit_p (graph->succs[from], find (escaped_id))
&& bitmap_bit_p (get_varinfo (find (to))->solution, escaped_id))
{
stats.num_avoided_edges++;
return false;
}
if (bitmap_set_bit (graph->succs[from], to))
{
r = true;
if (to < FIRST_REF_NODE && from < FIRST_REF_NODE)
stats.num_edges++;
}
return r;
}
}
/* Initialize the constraint graph structure to contain SIZE nodes. */
static void
init_graph (unsigned int size)
{
unsigned int j;
bitmap_obstack_initialize (&predbitmap_obstack);
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);
/* ??? Macros do not support template types with multiple arguments,
so we use a typedef to work around it. */
typedef vec<constraint_t> vec_constraint_t_heap;
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);
bitmap_clear (graph->direct_nodes);
for (j = 1; j < FIRST_REF_NODE; j++)
{
if (!get_varinfo (j)->is_special_var)
bitmap_set_bit (graph->direct_nodes, j);
}
for (j = 0; j < graph->size; j++)
graph->eq_rep[j] = -1;
for (j = 0; j < varmap.length (); j++)
graph->indirect_cycles[j] = -1;
FOR_EACH_VEC_ELT (constraints, i, c)
{
struct constraint_expr lhs = c->lhs;
struct constraint_expr rhs = c->rhs;
unsigned int lhsvar = lhs.var;
unsigned int rhsvar = rhs.var;
if (lhs.type == DEREF)
{
/* *x = y. */
if (rhs.offset == 0 && lhs.offset == 0 && rhs.type == SCALAR)
{
if (lhs.var == anything_id)
add_pred_graph_edge (graph, storedanything_id, rhsvar);
else
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
bitmap_clear_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. */
bitmap_clear_bit (graph->direct_nodes, rhsvar);
v = get_varinfo (rhsvar);
if (!v->is_full_var)
{
v = get_varinfo (v->head);
do
{
bitmap_clear_bit (graph->direct_nodes, v->id);
v = vi_next (v);
}
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)
bitmap_clear_bit (graph->direct_nodes, lhs.var);
else if (lhs.offset != 0)
bitmap_clear_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_EACH_VEC_ELT (constraints, i, c)
{
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 (lhs.var);
rhsvar = find (rhs.var);
if (lhs.type == DEREF)
{
if (rhs.offset == 0 && lhs.offset == 0 && rhs.type == SCALAR)
{
if (lhs.var == anything_id)
add_graph_edge (graph, storedanything_id, rhsvar);
else
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_checking_assert (find (rhs.var) == rhs.var);
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 nodes that can receive pointers. */
t = find (storedanything_id);
for (i = integer_id + 1; i < FIRST_REF_NODE; ++i)
{
if (get_varinfo (i)->may_have_pointers)
add_graph_edge (graph, find (i), t);
}
/* Everything stored to ANYTHING also potentially escapes. */
add_graph_edge (graph, find (escaped_id), t);
}
/* Changed variables on the last iteration. */
static bitmap changed;
/* Strongly Connected Component visitation info. */
class scc_info
{
public:
scc_info (size_t size);
~scc_info ();
auto_sbitmap visited;
auto_sbitmap deleted;
unsigned int *dfs;
unsigned int *node_mapping;
int current_index;
auto_vec<unsigned> 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, class scc_info *si, unsigned int n)
{
unsigned int i;
bitmap_iterator bi;
unsigned int my_dfs;
bitmap_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 (bitmap_bit_p (si->deleted, w))
continue;
if (!bitmap_bit_p (si->visited, w))
scc_visit (graph, si, w);
unsigned int t = find (w);
gcc_checking_assert (find (n) == n);
if (si->dfs[t] < si->dfs[n])
si->dfs[n] = si->dfs[t];
}
/* See if any components have been identified. */
if (si->dfs[n] == my_dfs)
{
if (si->scc_stack.length () > 0
&& si->dfs[si->scc_stack.last ()] >= my_dfs)
{
bitmap scc = BITMAP_ALLOC (NULL);
unsigned int lowest_node;
bitmap_iterator bi;
bitmap_set_bit (scc, n);
while (si->scc_stack.length () != 0
&& si->dfs[si->scc_stack.last ()] >= my_dfs)
{
unsigned int w = si->scc_stack.pop ();
bitmap_set_bit (scc, w);
}
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;
}
}
bitmap_set_bit (si->deleted, lowest_node);
}
else
bitmap_set_bit (si->deleted, n);
}
else
si->scc_stack.safe_push (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_checking_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);
if (merge_node_constraints (graph, to, from))
{
if (update_changed)
bitmap_set_bit (changed, to);
}
/* Mark TO as changed if FROM was changed. If TO was already marked
as changed, decrease the changed count. */
if (update_changed
&& bitmap_clear_bit (changed, from))
bitmap_set_bit (changed, to);
varinfo_t fromvi = get_varinfo (from);
if (fromvi->solution)
{
/* If the solution changes because of the merging, we need to mark
the variable as changed. */
varinfo_t tovi = get_varinfo (to);
if (bitmap_ior_into (tovi->solution, fromvi->solution))
{
if (update_changed)
bitmap_set_bit (changed, to);
}
BITMAP_FREE (fromvi->solution);
if (fromvi->oldsolution)
BITMAP_FREE (fromvi->oldsolution);
if (stats.iterations > 0
&& tovi->oldsolution)
BITMAP_FREE (tovi->oldsolution);
}
if (graph->succs[to])
bitmap_clear_bit (graph->succs[to], to);
}
/* Add a copy edge FROM -> TO, optimizing special cases. Returns TRUE
if the solution of TO changed. */
static bool
solve_add_graph_edge (constraint_graph_t graph, unsigned int to,
unsigned int from)
{
/* Adding edges from the special vars is pointless.
They don't have sets that can change. */
if (get_varinfo (from)->is_special_var)
return bitmap_ior_into (get_varinfo (to)->solution,
get_varinfo (from)->solution);
/* Merging the solution from ESCAPED needlessly increases
the set. Use ESCAPED as representative instead. */
else if (from == find (escaped_id))
return bitmap_set_bit (get_varinfo (to)->solution, escaped_id);
else if (get_varinfo (from)->may_have_pointers
&& add_graph_edge (graph, to, from))
return bitmap_ior_into (get_varinfo (to)->solution,
get_varinfo (from)->solution);
return false;
}
/* Process a constraint C that represents x = *(y + off), using DELTA as the
starting solution for y. */
static void
do_sd_constraint (constraint_graph_t graph, constraint_t c,
bitmap delta, bitmap *expanded_delta)
{
unsigned int lhs = c->lhs.var;
bool flag = false;
bitmap sol = get_varinfo (lhs)->solution;
unsigned int j;
bitmap_iterator bi;
HOST_WIDE_INT roffset = c->rhs.offset;
/* Our IL does not allow this. */
gcc_checking_assert (c->lhs.offset == 0);
/* If the solution of Y contains anything it is good enough to transfer
this to the LHS. */
if (bitmap_bit_p (delta, anything_id))
{
flag |= bitmap_set_bit (sol, anything_id);
goto done;
}
/* If we do not know at with offset the rhs is dereferenced compute
the reachability set of DELTA, conservatively assuming it is
dereferenced at all valid offsets. */
if (roffset == UNKNOWN_OFFSET)
{
delta = solution_set_expand (delta, expanded_delta);
/* No further offset processing is necessary. */
roffset = 0;
}
/* 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)
{
varinfo_t v = get_varinfo (j);
HOST_WIDE_INT fieldoffset = v->offset + roffset;
unsigned HOST_WIDE_INT size = v->size;
unsigned int t;
if (v->is_full_var)
;
else if (roffset != 0)
{
if (fieldoffset < 0)
v = get_varinfo (v->head);
else
v = first_or_preceding_vi_for_offset (v, fieldoffset);
}
/* We have to include all fields that overlap the current field
shifted by roffset. */
do
{
t = find (v->id);
flag |= solve_add_graph_edge (graph, lhs, t);
if (v->is_full_var
|| v->next == 0)
break;
v = vi_next (v);
}
while (v->offset < fieldoffset + size);
}
done:
/* If the LHS solution changed, mark the var as changed. */
if (flag)
bitmap_set_bit (changed, lhs);
}
/* Process a constraint C that represents *(x + off) = y using DELTA
as the starting solution for x. */
static void
do_ds_constraint (constraint_t c, bitmap delta, bitmap *expanded_delta)
{
unsigned int rhs = c->rhs.var;
bitmap sol = get_varinfo (rhs)->solution;
unsigned int j;
bitmap_iterator bi;
HOST_WIDE_INT loff = c->lhs.offset;
bool escaped_p = false;
/* Our IL does not allow this. */
gcc_checking_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 (solve_add_graph_edge (graph, t, rhs))
bitmap_set_bit (changed, t);
return;
}
/* If we do not know at with offset the rhs is dereferenced compute
the reachability set of DELTA, conservatively assuming it is
dereferenced at all valid offsets. */
if (loff == UNKNOWN_OFFSET)
{
delta = solution_set_expand (delta, expanded_delta);
loff = 0;
}
/* 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)
{
varinfo_t v = get_varinfo (j);
unsigned int t;
HOST_WIDE_INT fieldoffset = v->offset + loff;
unsigned HOST_WIDE_INT size = v->size;
if (v->is_full_var)
;
else if (loff != 0)
{
if (fieldoffset < 0)
v = get_varinfo (v->head);
else
v = first_or_preceding_vi_for_offset (v, fieldoffset);
}
/* We have to include all fields that overlap the current field
shifted by loff. */
do
{
if (v->may_have_pointers)
{
/* If v is a global variable then this is an escape point. */
if (v->is_global_var
&& !escaped_p)
{
t = find (escaped_id);
if (add_graph_edge (graph, t, rhs)
&& bitmap_ior_into (get_varinfo (t)->solution, sol))
bitmap_set_bit (changed, t);
/* Enough to let rhs escape once. */
escaped_p = true;
}
if (v->is_special_var)
break;
t = find (v->id);
if (solve_add_graph_edge (graph, t, rhs))
bitmap_set_bit (changed, t);
}
if (v->is_full_var
|| v->next == 0)
break;
v = vi_next (v);
}
while (v->offset < fieldoffset + size);
}
}
/* 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,
bitmap *expanded_delta)
{
if (c->lhs.type == DEREF)
{
if (c->rhs.type == ADDRESSOF)
{
gcc_unreachable ();
}
else
{
/* *x = y */
do_ds_constraint (c, delta, expanded_delta);
}
}
else if (c->rhs.type == DEREF)
{
/* x = *y */
if (!(get_varinfo (c->lhs.var)->is_special_var))
do_sd_constraint (graph, c, delta, expanded_delta);
}
else
{
bitmap tmp;
bool flag = false;
gcc_checking_assert (c->rhs.type == SCALAR && c->lhs.type == SCALAR
&& c->rhs.offset != 0 && c->lhs.offset == 0);
tmp = get_varinfo (c->lhs.var)->solution;
flag = set_union_with_increment (tmp, delta, c->rhs.offset,
expanded_delta);
if (flag)
bitmap_set_bit (changed, c->lhs.var);
}
}
/* Initialize and return a new SCC info structure. */
scc_info::scc_info (size_t size) :
visited (size), deleted (size), current_index (0), scc_stack (1)
{
bitmap_clear (visited);
bitmap_clear (deleted);
node_mapping = XNEWVEC (unsigned int, size);
dfs = XCNEWVEC (unsigned int, size);
for (size_t i = 0; i < size; i++)
node_mapping[i] = i;
}
/* Free an SCC info structure pointed to by SI. */
scc_info::~scc_info ()
{
free (node_mapping);
free (dfs);
}
/* 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;
scc_info si (size);
for (i = 0; i < MIN (LAST_REF_NODE, size); i++)
if (!bitmap_bit_p (si.visited, i) && find (i) == i)
scc_visit (graph, &si, i);
}
/* Visit the graph in topological order starting at node N, and store the
order in TOPO_ORDER using VISITED to indicate visited nodes. */
static void
topo_visit (constraint_graph_t graph, vec<unsigned> &topo_order,
sbitmap visited, unsigned int n)
{
bitmap_iterator bi;
unsigned int j;
bitmap_set_bit (visited, n);
if (graph->succs[n])
EXECUTE_IF_SET_IN_BITMAP (graph->succs[n], 0, j, bi)
{
unsigned k = find (j);
if (!bitmap_bit_p (visited, k))
topo_visit (graph, topo_order, visited, k);
}
/* Also consider copy with offset complex constraints as implicit edges. */
for (auto c : graph->complex[n])
{
/* Constraints are ordered so that SCALAR = SCALAR appear first. */
if (c->lhs.type != SCALAR || c->rhs.type != SCALAR)
break;
gcc_checking_assert (c->rhs.var == n);
unsigned k = find (c->lhs.var);
if (!bitmap_bit_p (visited, k))
topo_visit (graph, topo_order, visited, k);
}
topo_order.quick_push (n);
}
/* Compute a topological ordering for GRAPH, and return the result. */
static auto_vec<unsigned>
compute_topo_order (constraint_graph_t graph)
{
unsigned int i;
unsigned int size = graph->size;
auto_sbitmap visited (size);
bitmap_clear (visited);
/* For the heuristic in add_graph_edge to work optimally make sure to
first visit the connected component of the graph containing
ESCAPED. Do this by extracting the connected component
with ESCAPED and append that to all other components as solve_graph
pops from the order. */
auto_vec<unsigned> tail (size);
topo_visit (graph, tail, visited, find (escaped_id));
auto_vec<unsigned> topo_order (size);
for (i = 0; i != size; ++i)
if (!bitmap_bit_p (visited, i) && find (i) == i)
topo_visit (graph, topo_order, visited, i);
topo_order.splice (tail);
return topo_order;
}
/* 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;
/* Equiv_class_label hashtable helpers. */
struct equiv_class_hasher : nofree_ptr_hash <equiv_class_label>
{
static inline hashval_t hash (const equiv_class_label *);
static inline bool equal (const equiv_class_label *,
const equiv_class_label *);
};
/* A hashtable for mapping a bitmap of labels->pointer equivalence
classes. */
static hash_table<equiv_class_hasher> *pointer_equiv_class_table;
/* A hashtable for mapping a bitmap of labels->location equivalence
classes. */
static hash_table<equiv_class_hasher> *location_equiv_class_table;
/* Hash function for a equiv_class_label_t. */
inline hashval_t
equiv_class_hasher::hash (const equiv_class_label *ecl)
{
return ecl->hashcode;
}
/* Equality function for two equiv_class_label_t's. */
inline bool
equiv_class_hasher::equal (const equiv_class_label *eql1,
const equiv_class_label *eql2)
{
return (eql1->hashcode == eql2->hashcode
&& bitmap_equal_p (eql1->labels, eql2->labels));
}
struct obstack equiv_class_obstack;
/* Lookup a equivalence class in TABLE by the bitmap of LABELS with
hash HAS it contains. Sets *REF_LABELS to the bitmap LABELS
is equivalent to. */
static equiv_class_label *
equiv_class_lookup_or_add (hash_table<equiv_class_hasher> *table,
bitmap labels)
{
equiv_class_label **slot;
equiv_class_label ecl;
ecl.labels = labels;
ecl.hashcode = bitmap_hash (labels);
slot = table->find_slot (&ecl, INSERT);
if (!*slot)
{
*slot = XOBNEW (&equiv_class_obstack, struct equiv_class_label);
(*slot)->labels = labels;
(*slot)->hashcode = ecl.hashcode;
(*slot)->equivalence_class = 0;
}
return *slot;
}
/* 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, class scc_info *si, unsigned int n)
{
unsigned int i;
bitmap_iterator bi;
unsigned int my_dfs;
gcc_checking_assert (si->node_mapping[n] == n);
bitmap_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 (bitmap_bit_p (si->deleted, w))
continue;
if (!bitmap_bit_p (si->visited, w))
condense_visit (graph, si, w);
unsigned int t = si->node_mapping[w];
gcc_checking_assert (si->node_mapping[n] == n);
if (si->dfs[t] < si->dfs[n])
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 (bitmap_bit_p (si->deleted, w))
continue;
if (!bitmap_bit_p (si->visited, w))
condense_visit (graph, si, w);
unsigned int t = si->node_mapping[w];
gcc_assert (si->node_mapping[n] == n);
if (si->dfs[t] < si->dfs[n])
si->dfs[n] = si->dfs[t];
}
/* See if any components have been identified. */
if (si->dfs[n] == my_dfs)
{
if (si->scc_stack.length () != 0
&& si->dfs[si->scc_stack.last ()] >= my_dfs)
{
/* Find the first node of the SCC and do non-bitmap work. */
bool direct_p = true;
unsigned first = si->scc_stack.length ();
do
{
--first;
unsigned int w = si->scc_stack[first];
si->node_mapping[w] = n;
if (!bitmap_bit_p (graph->direct_nodes, w))
direct_p = false;
}
while (first > 0
&& si->dfs[si->scc_stack[first - 1]] >= my_dfs);
if (!direct_p)
bitmap_clear_bit (graph->direct_nodes, n);
/* Want to reduce to node n, push that first. */
si->scc_stack.reserve (1);
si->scc_stack.quick_push (si->scc_stack[first]);
si->scc_stack[first] = n;
unsigned scc_size = si->scc_stack.length () - first;
unsigned split = scc_size / 2;
unsigned carry = scc_size - split * 2;
while (split > 0)
{
for (unsigned i = 0; i < split; ++i)
{
unsigned a = si->scc_stack[first + i];
unsigned b = si->scc_stack[first + split + carry + i];
/* Unify our nodes. */
if (graph->preds[b])
{
if (!graph->preds[a])
std::swap (graph->preds[a], graph->preds[b]);
else
bitmap_ior_into_and_free (graph->preds[a],
&graph->preds[b]);
}
if (graph->implicit_preds[b])
{
if (!graph->implicit_preds[a])
std::swap (graph->implicit_preds[a],
graph->implicit_preds[b]);
else
bitmap_ior_into_and_free (graph->implicit_preds[a],
&graph->implicit_preds[b]);
}
if (graph->points_to[b])
{
if (!graph->points_to[a])
std::swap (graph->points_to[a], graph->points_to[b]);
else
bitmap_ior_into_and_free (graph->points_to[a],
&graph->points_to[b]);
}
}
unsigned remain = split + carry;
split = remain / 2;
carry = remain - split * 2;
}
/* Actually pop the SCC. */
si->scc_stack.truncate (first);
}
bitmap_set_bit (si->deleted, n);
}
else
si->scc_stack.safe_push (n);
}
/* Label pointer equivalences.
This performs a value numbering of the constraint graph to
discover which variables will always have the same points-to sets
under the current set of constraints.
The way it value numbers is to store the set of points-to bits
generated by the constraints and graph edges. This is just used as a
hash and equality comparison. The *actual set of points-to bits* is
completely irrelevant, in that we don't care about being able to
extract them later.
The equality values (currently bitmaps) just have to satisfy a few
constraints, the main ones being:
1. The combining operation must be order independent.
2. The end result of a given set of operations must be unique iff the
combination of input values is unique
3. Hashable. */
static void
label_visit (constraint_graph_t graph, class scc_info *si, unsigned int n)
{
unsigned int i, first_pred;
bitmap_iterator bi;
bitmap_set_bit (si->visited, n);
/* Label and union our incoming edges's points to sets. */
first_pred = -1U;
EXECUTE_IF_IN_NONNULL_BITMAP (graph->preds[n], 0, i, bi)
{
unsigned int w = si->node_mapping[i];
if (!bitmap_bit_p (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])
{
if (!graph->points_to[n])
{
if (first_pred == -1U)
first_pred = w;
else
{
graph->points_to[n] = BITMAP_ALLOC (&predbitmap_obstack);
bitmap_ior (graph->points_to[n],
graph->points_to[first_pred],
graph->points_to[w]);
}
}
else
bitmap_ior_into (graph->points_to[n], graph->points_to[w]);
}
}
/* Indirect nodes get fresh variables and a new pointer equiv class. */
if (!bitmap_bit_p (graph->direct_nodes, n))
{
if (!graph->points_to[n])
{
graph->points_to[n] = BITMAP_ALLOC (&predbitmap_obstack);
if (first_pred != -1U)
bitmap_copy (graph->points_to[n], graph->points_to[first_pred]);
}
bitmap_set_bit (graph->points_to[n], FIRST_REF_NODE + n);
graph->pointer_label[n] = pointer_equiv_class++;
equiv_class_label_t ecl;
ecl = equiv_class_lookup_or_add (pointer_equiv_class_table,
graph->points_to[n]);
ecl->equivalence_class = graph->pointer_label[n];
return;
}
/* If there was only a single non-empty predecessor the pointer equiv
class is the same. */
if (!graph->points_to[n])
{
if (first_pred != -1U)
{
graph->pointer_label[n] = graph->pointer_label[first_pred];
graph->points_to[n] = graph->points_to[first_pred];
}
return;
}
if (!bitmap_empty_p (graph->points_to[n]))
{
equiv_class_label_t ecl;
ecl = equiv_class_lookup_or_add (pointer_equiv_class_table,
graph->points_to[n]);
if (ecl->equivalence_class == 0)
ecl->equivalence_class = pointer_equiv_class++;
else
{
BITMAP_FREE (graph->points_to[n]);
graph->points_to[n] = ecl->labels;
}
graph->pointer_label[n] = ecl->equivalence_class;
}
}
/* Print the pred graph in dot format. */
static void
dump_pred_graph (class scc_info *si, FILE *file)
{
unsigned int i;
/* Only print the graph if it has already been initialized: */
if (!graph)
return;
/* Prints the header of the dot file: */
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 and complex constraints in "
"the constraint graph:\n");
/* The next lines print the nodes in the graph together with the
complex constraints attached to them. */
for (i = 1; i < graph->size; i++)
{
if (i == FIRST_REF_NODE)
continue;
if (si->node_mapping[i] != i)
continue;
if (i < FIRST_REF_NODE)
fprintf (file, "\"%s\"", get_varinfo (i)->name);
else
fprintf (file, "\"*%s\"", get_varinfo (i - FIRST_REF_NODE)->name);
if (graph->points_to[i]
&& !bitmap_empty_p (graph->points_to[i]))
{
if (i < FIRST_REF_NODE)
fprintf (file, "[label=\"%s = {", get_varinfo (i)->name);
else
fprintf (file, "[label=\"*%s = {",
get_varinfo (i - FIRST_REF_NODE)->name);
unsigned j;
bitmap_iterator bi;
EXECUTE_IF_SET_IN_BITMAP (graph->points_to[i], 0, j, bi)
fprintf (file, " %d", j);
fprintf (file, " }\"]");
}
fprintf (file, ";\n");
}
/* Go over the edges. */
fprintf (file, "\n // Edges in the constraint graph:\n");
for (i = 1; i < graph->size; i++)
{
unsigned j;
bitmap_iterator bi;
if (si->node_mapping[i] != i)
continue;
EXECUTE_IF_IN_NONNULL_BITMAP (graph->preds[i], 0, j, bi)
{
unsigned from = si->node_mapping[j];
if (from < FIRST_REF_NODE)
fprintf (file, "\"%s\"", get_varinfo (from)->name);
else
fprintf (file, "\"*%s\"",
get_varinfo (from - FIRST_REF_NODE)->name);
fprintf (file, " -> ");
if (i < FIRST_REF_NODE)
fprintf (file, "\"%s\"", get_varinfo (i)->name);
else
fprintf (file, "\"*%s\"", get_varinfo (i - FIRST_REF_NODE)->name);
fprintf (file, ";\n");
}
}
/* Prints the tail of the dot file. */
fprintf (file, "}\n");
}
/* Perform offline variable substitution, discovering equivalence
classes, and eliminating non-pointer variables. */
static class scc_info *
perform_var_substitution (constraint_graph_t graph)
{
unsigned int i;
unsigned int size = graph->size;
scc_info *si = new scc_info (size);
bitmap_obstack_initialize (&iteration_obstack);
gcc_obstack_init (&equiv_class_obstack);
pointer_equiv_class_table = new hash_table<equiv_class_hasher> (511);
location_equiv_class_table
= new hash_table<equiv_class_hasher> (511);
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 = 1; i < FIRST_REF_NODE; i++)
if (!bitmap_bit_p (si->visited, si->node_mapping[i]))
condense_visit (graph, si, si->node_mapping[i]);
if (dump_file && (dump_flags & TDF_GRAPH))
{
fprintf (dump_file, "\n\n// The constraint graph before var-substitution "
"in dot format:\n");
dump_pred_graph (si, dump_file);
fprintf (dump_file, "\n\n");
}
bitmap_clear (si->visited);
/* Actually the label the nodes for pointer equivalences. */
for (i = 1; i < FIRST_REF_NODE; i++)
if (!bitmap_bit_p (si->visited, si->node_mapping[i]))
label_visit (graph, si, si->node_mapping[i]);
/* Calculate location equivalence labels. */
for (i = 1; i < FIRST_REF_NODE; i++)
{
bitmap pointed_by;
bitmap_iterator bi;
unsigned int j;
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. */
equiv_class_label_t ecl;
ecl = equiv_class_lookup_or_add (location_equiv_class_table, pointed_by);
if (ecl->equivalence_class == 0)
ecl->equivalence_class = location_equiv_class++;
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] = ecl->equivalence_class;
}
if (dump_file && (dump_flags & TDF_DETAILS))
for (i = 1; i < FIRST_REF_NODE; i++)
{
unsigned j = si->node_mapping[i];
if (j != i)
{
fprintf (dump_file, "%s node id %d ",
bitmap_bit_p (graph->direct_nodes, i)
? "Direct" : "Indirect", i);
if (i < FIRST_REF_NODE)
fprintf (dump_file, "\"%s\"", get_varinfo (i)->name);
else
fprintf (dump_file, "\"*%s\"",
get_varinfo (i - FIRST_REF_NODE)->name);
fprintf (dump_file, " mapped to SCC leader node id %d ", j);
if (j < FIRST_REF_NODE)
fprintf (dump_file, "\"%s\"\n", get_varinfo (j)->name);
else
fprintf (dump_file, "\"*%s\"\n",
get_varinfo (j - FIRST_REF_NODE)->name);
}
else
{
fprintf (dump_file,
"Equivalence classes for %s node id %d ",
bitmap_bit_p (graph->direct_nodes, i)
? "direct" : "indirect", i);
if (i < FIRST_REF_NODE)
fprintf (dump_file, "\"%s\"", get_varinfo (i)->name);
else
fprintf (dump_file, "\"*%s\"",
get_varinfo (i - FIRST_REF_NODE)->name);
fprintf (dump_file,
": pointer %d, location %d\n",
graph->pointer_label[i], graph->loc_label[i]);
}
}
/* Quickly eliminate our non-pointer variables. */
for (i = 1; 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 (class scc_info *si)
{
delete 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);
delete pointer_equiv_class_table;
pointer_equiv_class_table = NULL;
delete location_equiv_class_table;
location_equiv_class_table = NULL;
obstack_free (&equiv_class_obstack, NULL);
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_checking_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_checking_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 = 1; 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_EACH_VEC_ELT (constraints, i, c)
{
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,
class scc_info *si)
{
int i;
constraint_t c;
if (flag_checking)
{
for (unsigned int j = 0; j < graph->size; j++)
gcc_assert (find (j) == j);
}
FOR_EACH_VEC_ELT (constraints, i, c)
{
struct constraint_expr lhs = c->lhs;
struct constraint_expr rhs = c->rhs;
unsigned int lhsvar = find (lhs.var);
unsigned int rhsvar = find (rhs.var);
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);
fprintf (dump_file, "\n");
}
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);
fprintf (dump_file, "\n");
}
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;
auto_vec<unsigned> queue;
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))
queue.safe_push (i);
}
}
for (queuepos = 0;
queue.iterate (queuepos, &i);
queuepos++)
{
unify_nodes (graph, to, i, true);
}
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 = BITMAP_ALLOC (NULL);
/* Mark all initial non-collapsed nodes as changed. */
for (i = 1; 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]))
|| graph->complex[i].length () > 0))
bitmap_set_bit (changed, i);
}
/* Allocate a bitmap to be used to store the changed bits. */
pts = BITMAP_ALLOC (&pta_obstack);
while (!bitmap_empty_p (changed))
{
unsigned int i;
stats.iterations++;
bitmap_obstack_initialize (&iteration_obstack);
auto_vec<unsigned> topo_order = compute_topo_order (graph);
while (topo_order.length () != 0)
{
i = topo_order.pop ();
/* 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. For complex
constraints that modify i itself, like the common group of
callarg = callarg + UNKNOWN;
callarg = *callarg + UNKNOWN;
*callarg = callescape;
make sure to iterate immediately because that maximizes
cache reuse and expands the graph quickest, leading to
better visitation order in the next iteration. */
while (bitmap_clear_bit (changed, i))
{
bitmap solution;
vec<constraint_t> &complex = graph->complex[i];
varinfo_t vi = get_varinfo (i);
bool solution_empty;
/* Compute the changed set of solution bits. If anything
is in the solution just propagate that. */
if (bitmap_bit_p (vi->solution, anything_id))
{
/* If anything is also in the old solution there is
nothing to do.
??? But we shouldn't ended up with "changed" set ... */
if (vi->oldsolution
&& bitmap_bit_p (vi->oldsolution, anything_id))
break;
bitmap_copy (pts, get_varinfo (find (anything_id))->solution);
}
else if (vi->oldsolution)
bitmap_and_compl (pts, vi->solution, vi->oldsolution);
else
bitmap_copy (pts, vi->solution);
if (bitmap_empty_p (pts))
break;
if (vi->oldsolution)
bitmap_ior_into (vi->oldsolution, pts);
else
{
vi->oldsolution = BITMAP_ALLOC (&oldpta_obstack);
bitmap_copy (vi->oldsolution, pts);
}
solution = vi->solution;
solution_empty = bitmap_empty_p (solution);
/* Process the complex constraints. */
hash_set<constraint_t> *cvisited = nullptr;
if (flag_checking)
cvisited = new hash_set<constraint_t>;
bitmap expanded_pts = NULL;
for (unsigned j = 0; j < complex.length (); ++j)
{
constraint_t c = complex[j];
/* At unification time only the directly involved nodes
will get their complex constraints updated. Update
our complex constraints now but keep the constraint
vector sorted and clear of duplicates. Also make
sure to evaluate each prevailing constraint only once. */
unsigned int new_lhs = find (c->lhs.var);
unsigned int new_rhs = find (c->rhs.var);
if (c->lhs.var != new_lhs || c->rhs.var != new_rhs)
{
constraint tem = *c;
tem.lhs.var = new_lhs;
tem.rhs.var = new_rhs;
unsigned int place
= complex.lower_bound (&tem, constraint_less);
c->lhs.var = new_lhs;
c->rhs.var = new_rhs;
if (place != j)
{
complex.ordered_remove (j);
if (j < place)
--place;
if (place < complex.length ())
{
if (constraint_equal (*complex[place], *c))
{
j--;
continue;
}
else
complex.safe_insert (place, c);
}
else
complex.quick_push (c);
if (place > j)
{
j--;
continue;
}
}
}
/* 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 (cvisited && cvisited->add (c))
gcc_unreachable ();
if (!solution_empty || c->lhs.type != DEREF)
do_complex_constraint (graph, c, pts, &expanded_pts);
}
if (cvisited)
{
/* When checking, verify the order of constraints is
maintained and each constraint is evaluated exactly
once. */
for (unsigned j = 1; j < complex.length (); ++j)
gcc_assert (constraint_less (complex[j-1], complex[j]));
gcc_assert (cvisited->elements () == complex.length ());
delete cvisited;
}
BITMAP_FREE (expanded_pts);
solution_empty = bitmap_empty_p (solution);
if (!solution_empty)
{
bitmap_iterator bi;
unsigned eff_escaped_id = find (escaped_id);
unsigned j;
/* Propagate solution to all successors. */
unsigned to_remove = ~0U;
EXECUTE_IF_IN_NONNULL_BITMAP (graph->succs[i],
0, j, bi)
{
if (to_remove != ~0U)
{
bitmap_clear_bit (graph->succs[i], to_remove);
to_remove = ~0U;
}
unsigned int to = find (j);
if (to != j)
{
/* Update the succ graph, avoiding duplicate
work. */
to_remove = j;
if (! bitmap_set_bit (graph->succs[i], to))
continue;
/* We eventually end up processing 'to' twice
as it is undefined whether bitmap iteration
iterates over bits set during iteration.
Play safe instead of doing tricks. */
}
/* Don't try to propagate to ourselves. */
if (to == i)
{
to_remove = j;
continue;
}
/* Early node unification can lead to edges from
escaped - remove them. */
if (i == eff_escaped_id)
{
to_remove = j;
if (bitmap_set_bit (get_varinfo (to)->solution,
escaped_id))
bitmap_set_bit (changed, to);
continue;
}
if (bitmap_ior_into (get_varinfo (to)->solution, pts))
bitmap_set_bit (changed, to);
}
if (to_remove != ~0U)
bitmap_clear_bit (graph->succs[i], to_remove);
}
}
}
bitmap_obstack_release (&iteration_obstack);
}
BITMAP_FREE (pts);
BITMAP_FREE (changed);
bitmap_obstack_release (&oldpta_obstack);
}
void
delete_graph (void)
{
unsigned int i;
for (i = 0; i < graph->size; i++)
graph->complex[i].release ();
free (graph->complex);
free (graph->succs);
free (graph->pe);
free (graph->pe_rep);
free (graph->indirect_cycles);
/* We are not doing free (graph->rep) since the representatives mapping is
needed outside of the solver too. */
free (graph);
}
/* 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 = 1; 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 + 1; 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 = varmap.length ();
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);
}
namespace pointer_analysis {
/* Solve the constraint set. The entry function of the solver. */
void
solve_constraints (void)
{
class scc_info *si;
/* Sort varinfos so that ones that cannot be pointed to are last.
This makes bitmaps more efficient. */
unsigned int *map = XNEWVEC (unsigned int, varmap.length ());
for (unsigned i = 0; i < integer_id + 1; ++i)
map[i] = i;
/* Start with address-taken vars, followed by not address-taken vars
to move vars never appearing in the points-to solution bitmaps last. */
unsigned j = integer_id + 1;
for (unsigned i = integer_id + 1; i < varmap.length (); ++i)
if (varmap[varmap[i]->head]->address_taken)
map[i] = j++;
for (unsigned i = integer_id + 1; i < varmap.length (); ++i)
if (! varmap[varmap[i]->head]->address_taken)
map[i] = j++;
/* Shuffle varmap according to map. */
for (unsigned i = integer_id + 1; i < varmap.length (); ++i)
{
while (map[varmap[i]->id] != i)
std::swap (varmap[i], varmap[map[varmap[i]->id]]);
gcc_assert (bitmap_empty_p (varmap[i]->solution));
varmap[i]->id = i;
varmap[i]->next = map[varmap[i]->next];
varmap[i]->head = map[varmap[i]->head];
}
/* Finally rewrite constraints. */
for (unsigned i = 0; i < constraints.length (); ++i)
{
constraints[i]->lhs.var = map[constraints[i]->lhs.var];
constraints[i]->rhs.var = map[constraints[i]->rhs.var];
}
free (map);
if (dump_file)
fprintf (dump_file,
"\nCollapsing static cycles and doing variable "
"substitution\n");
init_graph (varmap.length () * 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);
/* Attach complex constraints to graph nodes. */
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 && (dump_flags & TDF_GRAPH))
{
fprintf (dump_file, "\n\n// The constraint graph before solve-graph "
"in dot format:\n");
dump_constraint_graph (dump_file);
fprintf (dump_file, "\n\n");
}
if (dump_file)
fprintf (dump_file, "Solving graph\n");
solve_graph (graph);
if (dump_file && (dump_flags & TDF_GRAPH))
{
fprintf (dump_file, "\n\n// The constraint graph after solve-graph "
"in dot format:\n");
dump_constraint_graph (dump_file);
fprintf (dump_file, "\n\n");
}
/* The mapping node -> representative is one of the outputs of the
solver. */
union_find_compress_all ();
var_rep = graph->rep;
delete_graph ();
}
} // namespace pointer_analysis