OSDN Git Service

* doc/install.texi: Update the URL for Jacks.
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-alias.c
index 65b0d7b..8e1b632 100644 (file)
@@ -1,5 +1,5 @@
 /* Alias analysis for trees.
 /* Alias analysis for trees.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
 This file is part of GCC.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
 This file is part of GCC.
@@ -42,7 +42,10 @@ Boston, MA 02111-1307, USA.  */
 #include "tree-pass.h"
 #include "convert.h"
 #include "params.h"
 #include "tree-pass.h"
 #include "convert.h"
 #include "params.h"
+#include "vec.h"
 
 
+/* 'true' after aliases have been computed (see compute_may_aliases).  */
+bool aliases_computed_p;
 
 /* Structure to map a variable to its alias set and keep track of the
    virtual operands that will be needed to represent it.  */
 
 /* Structure to map a variable to its alias set and keep track of the
    virtual operands that will be needed to represent it.  */
@@ -73,7 +76,7 @@ struct alias_info
   /* SSA names visited while collecting points-to information.  If bit I
      is set, it means that SSA variable with version I has already been
      visited.  */
   /* SSA names visited while collecting points-to information.  If bit I
      is set, it means that SSA variable with version I has already been
      visited.  */
-  bitmap ssa_names_visited;
+  sbitmap ssa_names_visited;
 
   /* Array of SSA_NAME pointers processed by the points-to collector.  */
   varray_type processed_ptrs;
 
   /* Array of SSA_NAME pointers processed by the points-to collector.  */
   varray_type processed_ptrs;
@@ -94,6 +97,9 @@ struct alias_info
   /* Number of function calls found in the program.  */
   size_t num_calls_found;
 
   /* Number of function calls found in the program.  */
   size_t num_calls_found;
 
+  /* Number of const/pure function calls found in the program.  */
+  size_t num_pure_const_calls_found;
+
   /* Array of counters to keep track of how many times each pointer has
      been dereferenced in the program.  This is used by the alias grouping
      heuristic in compute_flow_insensitive_aliasing.  */
   /* Array of counters to keep track of how many times each pointer has
      been dereferenced in the program.  This is used by the alias grouping
      heuristic in compute_flow_insensitive_aliasing.  */
@@ -145,15 +151,12 @@ static void compute_points_to_and_addr_escape (struct alias_info *);
 static void compute_flow_sensitive_aliasing (struct alias_info *);
 static void setup_pointers_and_addressables (struct alias_info *);
 static bool collect_points_to_info_r (tree, tree, void *);
 static void compute_flow_sensitive_aliasing (struct alias_info *);
 static void setup_pointers_and_addressables (struct alias_info *);
 static bool collect_points_to_info_r (tree, tree, void *);
-static bool is_escape_site (tree, size_t *);
+static bool is_escape_site (tree, struct alias_info *);
 static void add_pointed_to_var (struct alias_info *, tree, tree);
 static void add_pointed_to_var (struct alias_info *, tree, tree);
-static void add_pointed_to_expr (tree, tree);
 static void create_global_var (void);
 static void collect_points_to_info_for (struct alias_info *, tree);
 static void create_global_var (void);
 static void collect_points_to_info_for (struct alias_info *, tree);
-static bool ptr_is_dereferenced_by (tree, tree, bool *);
 static void maybe_create_global_var (struct alias_info *ai);
 static void group_aliases (struct alias_info *);
 static void maybe_create_global_var (struct alias_info *ai);
 static void group_aliases (struct alias_info *);
-static struct ptr_info_def *get_ptr_info (tree t);
 static void set_pt_anything (tree ptr);
 static void set_pt_malloc (tree ptr);
 
 static void set_pt_anything (tree ptr);
 static void set_pt_malloc (tree ptr);
 
@@ -197,7 +200,8 @@ tree global_var;
    The concept of 'escaping' is the same one used in the Java world.  When
    a pointer or an ADDR_EXPR escapes, it means that it has been exposed
    outside of the current function.  So, assignment to global variables,
    The concept of 'escaping' is the same one used in the Java world.  When
    a pointer or an ADDR_EXPR escapes, it means that it has been exposed
    outside of the current function.  So, assignment to global variables,
-   function arguments and returning a pointer are all escape sites.
+   function arguments and returning a pointer are all escape sites, as are
+   conversions between pointers and integers.
 
    This is where we are currently limited.  Since not everything is renamed
    into SSA, we lose track of escape properties when a pointer is stashed
 
    This is where we are currently limited.  Since not everything is renamed
    into SSA, we lose track of escape properties when a pointer is stashed
@@ -238,15 +242,14 @@ tree global_var;
 
            foo (int i)
            {
 
            foo (int i)
            {
-             int *p, *q, a, b;
+             int *p, a, b;
            
              if (i > 10)
                p = &a;
              else
            
              if (i > 10)
                p = &a;
              else
-               q = &b;
+               p = &b;
            
              *p = 3;
            
              *p = 3;
-             *q = 5;
              a = b + 2;
              return *p;
            }
              a = b + 2;
              return *p;
            }
@@ -340,6 +343,19 @@ compute_may_aliases (void)
 
   /* Deallocate memory used by aliasing data structures.  */
   delete_alias_info (ai);
 
   /* Deallocate memory used by aliasing data structures.  */
   delete_alias_info (ai);
+
+  {
+    block_stmt_iterator bsi;
+    basic_block bb;
+    FOR_EACH_BB (bb)
+      {
+        for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+          {
+            update_stmt_if_modified (bsi_stmt (bsi));
+          }
+      }
+  }
+
 }
 
 struct tree_opt_pass pass_may_alias = 
 }
 
 struct tree_opt_pass pass_may_alias = 
@@ -356,45 +372,153 @@ struct tree_opt_pass pass_may_alias =
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
   TODO_dump_func | TODO_rename_vars
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
   TODO_dump_func | TODO_rename_vars
-    | TODO_ggc_collect | TODO_verify_ssa,  /* todo_flags_finish */
+    | TODO_ggc_collect | TODO_verify_ssa
+    | TODO_verify_stmts,               /* todo_flags_finish */
   0                                    /* letter */
 };
 
 
   0                                    /* letter */
 };
 
 
+/* Data structure used to count the number of dereferences to PTR
+   inside an expression.  */
+struct count_ptr_d
+{
+  tree ptr;
+  unsigned count;
+};
+
+
+/* Helper for count_uses_and_derefs.  Called by walk_tree to look for
+   (ALIGN/MISALIGNED_)INDIRECT_REF nodes for the pointer passed in DATA.  */
+
+static tree
+count_ptr_derefs (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data)
+{
+  struct count_ptr_d *count_p = (struct count_ptr_d *) data;
+
+  if (INDIRECT_REF_P (*tp) && TREE_OPERAND (*tp, 0) == count_p->ptr)
+    count_p->count++;
+
+  return NULL_TREE;
+}
+
+
+/* Count the number of direct and indirect uses for pointer PTR in
+   statement STMT.  The two counts are stored in *NUM_USES_P and
+   *NUM_DEREFS_P respectively.  *IS_STORE_P is set to 'true' if at
+   least one of those dereferences is a store operation.  */
+
+static void
+count_uses_and_derefs (tree ptr, tree stmt, unsigned *num_uses_p,
+                      unsigned *num_derefs_p, bool *is_store)
+{
+  ssa_op_iter i;
+  tree use;
+
+  *num_uses_p = 0;
+  *num_derefs_p = 0;
+  *is_store = false;
+
+  /* Find out the total number of uses of PTR in STMT.  */
+  FOR_EACH_SSA_TREE_OPERAND (use, stmt, i, SSA_OP_USE)
+    if (use == ptr)
+      (*num_uses_p)++;
+
+  /* Now count the number of indirect references to PTR.  This is
+     truly awful, but we don't have much choice.  There are no parent
+     pointers inside INDIRECT_REFs, so an expression like
+     '*x_1 = foo (x_1, *x_1)' needs to be traversed piece by piece to
+     find all the indirect and direct uses of x_1 inside.  The only
+     shortcut we can take is the fact that GIMPLE only allows
+     INDIRECT_REFs inside the expressions below.  */
+  if (TREE_CODE (stmt) == MODIFY_EXPR
+      || (TREE_CODE (stmt) == RETURN_EXPR
+         && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR)
+      || TREE_CODE (stmt) == ASM_EXPR
+      || TREE_CODE (stmt) == CALL_EXPR)
+    {
+      tree lhs, rhs;
+
+      if (TREE_CODE (stmt) == MODIFY_EXPR)
+       {
+         lhs = TREE_OPERAND (stmt, 0);
+         rhs = TREE_OPERAND (stmt, 1);
+       }
+      else if (TREE_CODE (stmt) == RETURN_EXPR)
+       {
+         tree e = TREE_OPERAND (stmt, 0);
+         lhs = TREE_OPERAND (e, 0);
+         rhs = TREE_OPERAND (e, 1);
+       }
+      else if (TREE_CODE (stmt) == ASM_EXPR)
+       {
+         lhs = ASM_OUTPUTS (stmt);
+         rhs = ASM_INPUTS (stmt);
+       }
+      else
+       {
+         lhs = NULL_TREE;
+         rhs = stmt;
+       }
+
+      if (lhs && (TREE_CODE (lhs) == TREE_LIST || EXPR_P (lhs)))
+       {
+         struct count_ptr_d count;
+         count.ptr = ptr;
+         count.count = 0;
+         walk_tree (&lhs, count_ptr_derefs, &count, NULL);
+         *is_store = true;
+         *num_derefs_p = count.count;
+       }
+
+      if (rhs && (TREE_CODE (rhs) == TREE_LIST || EXPR_P (rhs)))
+       {
+         struct count_ptr_d count;
+         count.ptr = ptr;
+         count.count = 0;
+         walk_tree (&rhs, count_ptr_derefs, &count, NULL);
+         *num_derefs_p += count.count;
+       }
+    }
+
+  gcc_assert (*num_uses_p >= *num_derefs_p);
+}
+
+
 /* Initialize the data structures used for alias analysis.  */
 
 static struct alias_info *
 init_alias_info (void)
 {
   struct alias_info *ai;
 /* Initialize the data structures used for alias analysis.  */
 
 static struct alias_info *
 init_alias_info (void)
 {
   struct alias_info *ai;
-  static bool aliases_computed_p = false;
 
   ai = xcalloc (1, sizeof (struct alias_info));
 
   ai = xcalloc (1, sizeof (struct alias_info));
-  ai->ssa_names_visited = BITMAP_XMALLOC ();
+  ai->ssa_names_visited = sbitmap_alloc (num_ssa_names);
+  sbitmap_zero (ai->ssa_names_visited);
   VARRAY_TREE_INIT (ai->processed_ptrs, 50, "processed_ptrs");
   VARRAY_TREE_INIT (ai->processed_ptrs, 50, "processed_ptrs");
-  ai->addresses_needed = BITMAP_XMALLOC ();
+  ai->addresses_needed = BITMAP_ALLOC (NULL);
   VARRAY_UINT_INIT (ai->num_references, num_referenced_vars, "num_references");
   VARRAY_UINT_INIT (ai->num_references, num_referenced_vars, "num_references");
-  ai->written_vars = BITMAP_XMALLOC ();
-  ai->dereferenced_ptrs_store = BITMAP_XMALLOC ();
-  ai->dereferenced_ptrs_load = BITMAP_XMALLOC ();
+  ai->written_vars = BITMAP_ALLOC (NULL);
+  ai->dereferenced_ptrs_store = BITMAP_ALLOC (NULL);
+  ai->dereferenced_ptrs_load = BITMAP_ALLOC (NULL);
 
   /* If aliases have been computed before, clear existing information.  */
   if (aliases_computed_p)
     {
 
   /* If aliases have been computed before, clear existing information.  */
   if (aliases_computed_p)
     {
-      size_t i;
-
-      /* Clear the call-clobbered set.  We are going to re-discover
-         call-clobbered variables.  */
-      EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i,
+      unsigned i;
+      basic_block bb;
+  
+     /* Make sure that every statement has a valid set of operands.
+       If a statement needs to be scanned for operands while we
+       compute aliases, it may get erroneous operands because all
+       the alias relations are not built at that point.
+       FIXME: This code will become obsolete when operands are not
+       lazily updated.  */
+      FOR_EACH_BB (bb)
        {
        {
-         tree var = referenced_var (i);
-
-         /* Variables that are intrinsically call-clobbered (globals,
-            local statics, etc) will not be marked by the aliasing
-            code, so we can't remove them from CALL_CLOBBERED_VARS.  */
-         if (!is_call_clobbered (var))
-           bitmap_clear_bit (call_clobbered_vars, var_ann (var)->uid);
-       });
+         block_stmt_iterator si;
+         for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
+           get_stmt_operands (bsi_stmt (si));
+       }
 
       /* Similarly, clear the set of addressable variables.  In this
         case, we can just clear the set because addressability is
 
       /* Similarly, clear the set of addressable variables.  In this
         case, we can just clear the set because addressability is
@@ -404,9 +528,26 @@ init_alias_info (void)
       /* Clear flow-insensitive alias information from each symbol.  */
       for (i = 0; i < num_referenced_vars; i++)
        {
       /* Clear flow-insensitive alias information from each symbol.  */
       for (i = 0; i < num_referenced_vars; i++)
        {
-         var_ann_t ann = var_ann (referenced_var (i));
+         tree var = referenced_var (i);
+         var_ann_t ann = var_ann (var);
+
          ann->is_alias_tag = 0;
          ann->may_aliases = NULL;
          ann->is_alias_tag = 0;
          ann->may_aliases = NULL;
+
+         /* Since we are about to re-discover call-clobbered
+            variables, clear the call-clobbered flag.  Variables that
+            are intrinsically call-clobbered (globals, local statics,
+            etc) will not be marked by the aliasing code, so we can't
+            remove them from CALL_CLOBBERED_VARS.  
+
+            NB: STRUCT_FIELDS are still call clobbered if they are for
+            a global variable, so we *don't* clear their call clobberedness
+            just because they are tags, though we will clear it if they
+            aren't for global variables.  */
+         if (ann->mem_tag_kind == NAME_TAG 
+             || ann->mem_tag_kind == TYPE_TAG 
+             || !is_global_var (var))
+           clear_call_clobbered (var);
        }
 
       /* Clear flow-sensitive points-to information from each SSA name.  */
        }
 
       /* Clear flow-sensitive points-to information from each SSA name.  */
@@ -414,7 +555,7 @@ init_alias_info (void)
        {
          tree name = ssa_name (i);
 
        {
          tree name = ssa_name (i);
 
-         if (!POINTER_TYPE_P (TREE_TYPE (name)))
+         if (!name || !POINTER_TYPE_P (TREE_TYPE (name)))
            continue;
 
          if (SSA_NAME_PTR_INFO (name))
            continue;
 
          if (SSA_NAME_PTR_INFO (name))
@@ -428,6 +569,7 @@ init_alias_info (void)
                 tag will need to be created in create_name_tags.  */
              pi->pt_anything = 0;
              pi->pt_malloc = 0;
                 tag will need to be created in create_name_tags.  */
              pi->pt_anything = 0;
              pi->pt_malloc = 0;
+             pi->pt_null = 0;
              pi->value_escapes_p = 0;
              pi->is_dereferenced = 0;
              if (pi->pt_vars)
              pi->value_escapes_p = 0;
              pi->is_dereferenced = 0;
              if (pi->pt_vars)
@@ -450,9 +592,9 @@ delete_alias_info (struct alias_info *ai)
 {
   size_t i;
 
 {
   size_t i;
 
-  BITMAP_XFREE (ai->ssa_names_visited);
+  sbitmap_free (ai->ssa_names_visited);
   ai->processed_ptrs = NULL;
   ai->processed_ptrs = NULL;
-  BITMAP_XFREE (ai->addresses_needed);
+  BITMAP_FREE (ai->addresses_needed);
 
   for (i = 0; i < ai->num_addressable_vars; i++)
     {
 
   for (i = 0; i < ai->num_addressable_vars; i++)
     {
@@ -469,9 +611,9 @@ delete_alias_info (struct alias_info *ai)
   free (ai->pointers);
 
   ai->num_references = NULL;
   free (ai->pointers);
 
   ai->num_references = NULL;
-  BITMAP_XFREE (ai->written_vars);
-  BITMAP_XFREE (ai->dereferenced_ptrs_store);
-  BITMAP_XFREE (ai->dereferenced_ptrs_load);
+  BITMAP_FREE (ai->written_vars);
+  BITMAP_FREE (ai->dereferenced_ptrs_store);
+  BITMAP_FREE (ai->dereferenced_ptrs_load);
 
   free (ai);
 }
 
   free (ai);
 }
@@ -485,79 +627,15 @@ collect_points_to_info_for (struct alias_info *ai, tree ptr)
 {
   gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr)));
 
 {
   gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr)));
 
-  if (!bitmap_bit_p (ai->ssa_names_visited, SSA_NAME_VERSION (ptr)))
+  if (!TEST_BIT (ai->ssa_names_visited, SSA_NAME_VERSION (ptr)))
     {
     {
-      bitmap_set_bit (ai->ssa_names_visited, SSA_NAME_VERSION (ptr));
+      SET_BIT (ai->ssa_names_visited, SSA_NAME_VERSION (ptr));
       walk_use_def_chains (ptr, collect_points_to_info_r, ai, true);
       VARRAY_PUSH_TREE (ai->processed_ptrs, ptr);
     }
 }
 
 
       walk_use_def_chains (ptr, collect_points_to_info_r, ai, true);
       VARRAY_PUSH_TREE (ai->processed_ptrs, ptr);
     }
 }
 
 
-/* Helper for ptr_is_dereferenced_by.  Called by walk_tree to look for
-   INDIRECT_REF nodes for the pointer passed in DATA.  */
-
-static tree
-find_ptr_dereference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data)
-{
-  tree ptr = (tree) data;
-
-  if (TREE_CODE (*tp) == INDIRECT_REF
-      && TREE_OPERAND (*tp, 0) == ptr)
-    return *tp;
-
-  return NULL_TREE;
-}
-
-
-/* Return true if STMT contains INDIRECT_REF <PTR>.  *IS_STORE is set
-   to 'true' if the dereference is on the LHS of an assignment.  */
-
-static bool
-ptr_is_dereferenced_by (tree ptr, tree stmt, bool *is_store)
-{
-  *is_store = false;
-
-  if (TREE_CODE (stmt) == MODIFY_EXPR
-      || (TREE_CODE (stmt) == RETURN_EXPR
-         && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR))
-    {
-      tree e, lhs, rhs;
-
-      e = (TREE_CODE (stmt) == RETURN_EXPR) ? TREE_OPERAND (stmt, 0) : stmt;
-      lhs = TREE_OPERAND (e, 0);
-      rhs = TREE_OPERAND (e, 1);
-
-      if (EXPR_P (lhs)
-         && walk_tree (&lhs, find_ptr_dereference, ptr, NULL))
-       {
-         *is_store = true;
-         return true;
-       }
-      else if (EXPR_P (rhs)
-              && walk_tree (&rhs, find_ptr_dereference, ptr, NULL))
-       {
-         return true;
-       }
-    }
-  else if (TREE_CODE (stmt) == ASM_EXPR)
-    {
-      if (walk_tree (&ASM_OUTPUTS (stmt), find_ptr_dereference, ptr, NULL)
-         || walk_tree (&ASM_CLOBBERS (stmt), find_ptr_dereference, ptr, NULL))
-       {
-         *is_store = true;
-         return true;
-       }
-      else if (walk_tree (&ASM_INPUTS (stmt), find_ptr_dereference, ptr, NULL))
-       {
-         return true;
-       }
-    }
-
-  return false;
-}
-
-
 /* Traverse use-def links for all the pointers in the program to collect
    address escape and points-to information.
    
 /* Traverse use-def links for all the pointers in the program to collect
    address escape and points-to information.
    
@@ -570,7 +648,7 @@ static void
 compute_points_to_and_addr_escape (struct alias_info *ai)
 {
   basic_block bb;
 compute_points_to_and_addr_escape (struct alias_info *ai)
 {
   basic_block bb;
-  size_t i;
+  unsigned i;
   tree op;
   ssa_op_iter iter;
 
   tree op;
   ssa_op_iter iter;
 
@@ -585,7 +663,8 @@ compute_points_to_and_addr_escape (struct alias_info *ai)
        {
          bitmap addr_taken;
          tree stmt = bsi_stmt (si);
        {
          bitmap addr_taken;
          tree stmt = bsi_stmt (si);
-         bool stmt_escapes_p = is_escape_site (stmt, &ai->num_calls_found);
+         bool stmt_escapes_p = is_escape_site (stmt, ai);
+         bitmap_iterator bi;
 
          /* Mark all the variables whose address are taken by the
             statement.  Note that this will miss all the addresses taken
 
          /* Mark all the variables whose address are taken by the
             statement.  Note that this will miss all the addresses taken
@@ -594,38 +673,23 @@ compute_points_to_and_addr_escape (struct alias_info *ai)
          get_stmt_operands (stmt);
          addr_taken = addresses_taken (stmt);
          if (addr_taken)
          get_stmt_operands (stmt);
          addr_taken = addresses_taken (stmt);
          if (addr_taken)
-           EXECUTE_IF_SET_IN_BITMAP (addr_taken, 0, i,
-               {
-                 tree var = referenced_var (i);
-                 bitmap_set_bit (ai->addresses_needed, var_ann (var)->uid);
-                 if (stmt_escapes_p)
-                   mark_call_clobbered (var);
-               });
+           EXECUTE_IF_SET_IN_BITMAP (addr_taken, 0, i, bi)
+             {
+               tree var = referenced_var (i);
+               bitmap_set_bit (ai->addresses_needed, var_ann (var)->uid);
+               if (stmt_escapes_p)
+                 mark_call_clobbered (var);
+             }
 
          if (stmt_escapes_p)
            block_ann->has_escape_site = 1;
 
 
          if (stmt_escapes_p)
            block_ann->has_escape_site = 1;
 
-         /* Special case for silly ADDR_EXPR tricks
-            (gcc.c-torture/unsorted/pass.c).  If this statement is an
-            assignment to a non-pointer variable and the RHS takes the
-            address of a variable, assume that the variable on the RHS is
-            call-clobbered.  We could add the LHS to the list of
-            "pointers" and follow it to see if it really escapes, but it's
-            not worth the pain.  */
-         if (addr_taken
-             && TREE_CODE (stmt) == MODIFY_EXPR
-             && !POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (stmt, 0))))
-           EXECUTE_IF_SET_IN_BITMAP (addr_taken, 0, i,
-               {
-                 tree var = referenced_var (i);
-                 mark_call_clobbered (var);
-               });
-
          FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
            {
              var_ann_t v_ann = var_ann (SSA_NAME_VAR (op));
              struct ptr_info_def *pi;
              bool is_store;
          FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
            {
              var_ann_t v_ann = var_ann (SSA_NAME_VAR (op));
              struct ptr_info_def *pi;
              bool is_store;
+             unsigned num_uses, num_derefs;
 
              /* If the operand's variable may be aliased, keep track
                 of how many times we've referenced it.  This is used
 
              /* If the operand's variable may be aliased, keep track
                 of how many times we've referenced it.  This is used
@@ -643,7 +707,10 @@ compute_points_to_and_addr_escape (struct alias_info *ai)
              collect_points_to_info_for (ai, op);
 
              pi = SSA_NAME_PTR_INFO (op);
              collect_points_to_info_for (ai, op);
 
              pi = SSA_NAME_PTR_INFO (op);
-             if (ptr_is_dereferenced_by (op, stmt, &is_store))
+             count_uses_and_derefs (op, stmt, &num_uses, &num_derefs,
+                                    &is_store);
+
+             if (num_derefs > 0)
                {
                  /* Mark OP as dereferenced.  In a subsequent pass,
                     dereferenced pointers that point to a set of
                {
                  /* Mark OP as dereferenced.  In a subsequent pass,
                     dereferenced pointers that point to a set of
@@ -665,12 +732,13 @@ compute_points_to_and_addr_escape (struct alias_info *ai)
                  else
                    bitmap_set_bit (ai->dereferenced_ptrs_load, v_ann->uid);
                }
                  else
                    bitmap_set_bit (ai->dereferenced_ptrs_load, v_ann->uid);
                }
-             else if (stmt_escapes_p)
+
+             if (stmt_escapes_p && num_derefs < num_uses)
                {
                {
-                 /* Note that even if STMT is an escape point, pointer OP
-                    will not escape if it is being dereferenced.  That's
-                    why we only check for escape points if OP is not
-                    dereferenced by STMT.  */
+                 /* If STMT is an escape point and STMT contains at
+                    least one direct use of OP, then the value of OP
+                    escapes and so the pointed-to variables need to
+                    be marked call-clobbered.  */
                  pi->value_escapes_p = 1;
 
                  /* If the statement makes a function call, assume
                  pi->value_escapes_p = 1;
 
                  /* If the statement makes a function call, assume
@@ -694,6 +762,9 @@ compute_points_to_and_addr_escape (struct alias_info *ai)
              bitmap_set_bit (ai->written_vars, ann->uid);
              if (may_be_aliased (var))
                (VARRAY_UINT (ai->num_references, ann->uid))++;
              bitmap_set_bit (ai->written_vars, ann->uid);
              if (may_be_aliased (var))
                (VARRAY_UINT (ai->num_references, ann->uid))++;
+
+             if (POINTER_TYPE_P (TREE_TYPE (op)))
+               collect_points_to_info_for (ai, op);
            }
 
          /* Mark variables in V_MAY_DEF operands as being written to.  */
            }
 
          /* Mark variables in V_MAY_DEF operands as being written to.  */
@@ -708,7 +779,7 @@ compute_points_to_and_addr_escape (struct alias_info *ai)
             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.  */
             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.  */
-         modify_stmt (stmt);
+         mark_stmt_modified (stmt);
        }
     }
 
        }
     }
 
@@ -743,8 +814,7 @@ create_name_tags (struct alias_info *ai)
          continue;
        }
 
          continue;
        }
 
-      if (pi->pt_vars
-         && bitmap_first_set_bit (pi->pt_vars) >= 0)
+      if (pi->pt_vars && !bitmap_empty_p (pi->pt_vars))
        {
          size_t j;
          tree old_name_tag = pi->name_mem_tag;
        {
          size_t j;
          tree old_name_tag = pi->name_mem_tag;
@@ -801,6 +871,9 @@ create_name_tags (struct alias_info *ai)
          continue;
        }
 
          continue;
        }
 
+      TREE_THIS_VOLATILE (pi->name_mem_tag)
+         |= TREE_THIS_VOLATILE (TREE_TYPE (TREE_TYPE (ptr)));
+
       /* Mark the new name tag for renaming.  */
       bitmap_set_bit (vars_to_rename, var_ann (pi->name_mem_tag)->uid);
     }
       /* Mark the new name tag for renaming.  */
       bitmap_set_bit (vars_to_rename, var_ann (pi->name_mem_tag)->uid);
     }
@@ -825,10 +898,11 @@ compute_flow_sensitive_aliasing (struct alias_info *ai)
 
   for (i = 0; i < VARRAY_ACTIVE_SIZE (ai->processed_ptrs); i++)
     {
 
   for (i = 0; i < VARRAY_ACTIVE_SIZE (ai->processed_ptrs); i++)
     {
-      size_t j;
+      unsigned j;
       tree ptr = VARRAY_TREE (ai->processed_ptrs, i);
       struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
       var_ann_t v_ann = var_ann (SSA_NAME_VAR (ptr));
       tree ptr = VARRAY_TREE (ai->processed_ptrs, i);
       struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
       var_ann_t v_ann = var_ann (SSA_NAME_VAR (ptr));
+      bitmap_iterator bi;
 
       if (pi->value_escapes_p || pi->pt_anything)
        {
 
       if (pi->value_escapes_p || pi->pt_anything)
        {
@@ -841,16 +915,21 @@ compute_flow_sensitive_aliasing (struct alias_info *ai)
            mark_call_clobbered (v_ann->type_mem_tag);
 
          if (pi->pt_vars)
            mark_call_clobbered (v_ann->type_mem_tag);
 
          if (pi->pt_vars)
-           EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, j,
-               mark_call_clobbered (referenced_var (j)));
+           EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, j, bi)
+             {
+               mark_call_clobbered (referenced_var (j));
+             }
        }
 
       /* Set up aliasing information for PTR's name memory tag (if it has
         one).  Note that only pointers that have been dereferenced will
         have a name memory tag.  */
       if (pi->name_mem_tag && pi->pt_vars)
        }
 
       /* Set up aliasing information for PTR's name memory tag (if it has
         one).  Note that only pointers that have been dereferenced will
         have a name memory tag.  */
       if (pi->name_mem_tag && pi->pt_vars)
-       EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, j,
-           add_may_alias (pi->name_mem_tag, referenced_var (j)));
+       EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, j, bi)
+         {
+           add_may_alias (pi->name_mem_tag, referenced_var (j));
+           add_may_alias (v_ann->type_mem_tag, referenced_var (j));
+         }
 
       /* If the name tag is call clobbered, so is the type tag
         associated with the base VAR_DECL.  */
 
       /* If the name tag is call clobbered, so is the type tag
         associated with the base VAR_DECL.  */
@@ -875,7 +954,6 @@ static void
 compute_flow_insensitive_aliasing (struct alias_info *ai)
 {
   size_t i;
 compute_flow_insensitive_aliasing (struct alias_info *ai)
 {
   size_t i;
-  sbitmap res;
 
   /* Initialize counter for the total number of virtual operands that
      aliasing will introduce.  When AI->TOTAL_ALIAS_VOPS goes beyond the
 
   /* Initialize counter for the total number of virtual operands that
      aliasing will introduce.  When AI->TOTAL_ALIAS_VOPS goes beyond the
@@ -910,23 +988,51 @@ compute_flow_insensitive_aliasing (struct alias_info *ai)
          /* Skip memory tags and variables that have never been
             written to.  We also need to check if the variables are
             call-clobbered because they may be overwritten by
          /* Skip memory tags and variables that have never been
             written to.  We also need to check if the variables are
             call-clobbered because they may be overwritten by
-            function calls.  */
-         tag_stored_p = bitmap_bit_p (ai->written_vars, tag_ann->uid)
-                        || is_call_clobbered (tag);
-         var_stored_p = bitmap_bit_p (ai->written_vars, v_ann->uid)
-                        || is_call_clobbered (var);
+            function calls.
+
+            Note this is effectively random accessing elements in
+            the sparse bitset, which can be highly inefficient.
+            So we first check the call_clobbered status of the
+            tag and variable before querying the bitmap.  */
+         tag_stored_p = is_call_clobbered (tag)
+                        || bitmap_bit_p (ai->written_vars, tag_ann->uid);
+         var_stored_p = is_call_clobbered (var)
+                        || bitmap_bit_p (ai->written_vars, v_ann->uid);
          if (!tag_stored_p && !var_stored_p)
            continue;
             
          if (may_alias_p (p_map->var, p_map->set, var, v_map->set))
            {
          if (!tag_stored_p && !var_stored_p)
            continue;
             
          if (may_alias_p (p_map->var, p_map->set, var, v_map->set))
            {
+             subvar_t svars;
              size_t num_tag_refs, num_var_refs;
 
              num_tag_refs = VARRAY_UINT (ai->num_references, tag_ann->uid);
              num_var_refs = VARRAY_UINT (ai->num_references, v_ann->uid);
 
              /* Add VAR to TAG's may-aliases set.  */
              size_t num_tag_refs, num_var_refs;
 
              num_tag_refs = VARRAY_UINT (ai->num_references, tag_ann->uid);
              num_var_refs = VARRAY_UINT (ai->num_references, v_ann->uid);
 
              /* Add VAR to TAG's may-aliases set.  */
-             add_may_alias (tag, var);
+
+             /* If this is an aggregate, we may have subvariables for it
+                that need to be pointed to.  */
+             if (var_can_have_subvars (var)
+                 && (svars = get_subvars_for_var (var)))
+               {
+                 subvar_t sv;
+
+                 for (sv = svars; sv; sv = sv->next)
+                   {
+                     add_may_alias (tag, sv->var);
+                     /* Update the bitmap used to represent TAG's alias set
+                        in case we need to group aliases.  */
+                     SET_BIT (p_map->may_aliases, var_ann (sv->var)->uid);
+                   }
+               }
+             else
+               {
+                 add_may_alias (tag, var);
+                 /* Update the bitmap used to represent TAG's alias set
+                    in case we need to group aliases.  */
+                 SET_BIT (p_map->may_aliases, var_ann (var)->uid);
+               }
 
              /* Update the total number of virtual operands due to
                 aliasing.  Since we are adding one more alias to TAG's
 
              /* Update the total number of virtual operands due to
                 aliasing.  Since we are adding one more alias to TAG's
@@ -937,9 +1043,7 @@ compute_flow_insensitive_aliasing (struct alias_info *ai)
              ai->total_alias_vops += (num_var_refs + num_tag_refs);
              p_map->total_alias_vops += (num_var_refs + num_tag_refs);
 
              ai->total_alias_vops += (num_var_refs + num_tag_refs);
              p_map->total_alias_vops += (num_var_refs + num_tag_refs);
 
-             /* Update the bitmap used to represent TAG's alias set
-                in case we need to group aliases.  */
-             SET_BIT (p_map->may_aliases, var_ann (var)->uid);
+
            }
        }
     }
            }
        }
     }
@@ -964,8 +1068,6 @@ compute_flow_insensitive_aliasing (struct alias_info *ai)
      To avoid this problem, we do a final traversal of AI->POINTERS
      looking for pairs of pointers that have no aliased symbols in
      common and yet have conflicting alias set numbers.  */
      To avoid this problem, we do a final traversal of AI->POINTERS
      looking for pairs of pointers that have no aliased symbols in
      common and yet have conflicting alias set numbers.  */
-  res = sbitmap_alloc (num_referenced_vars);
-
   for (i = 0; i < ai->num_pointers; i++)
     {
       size_t j;
   for (i = 0; i < ai->num_pointers; i++)
     {
       size_t j;
@@ -977,17 +1079,15 @@ compute_flow_insensitive_aliasing (struct alias_info *ai)
        {
          struct alias_map_d *p_map2 = ai->pointers[j];
          tree tag2 = var_ann (p_map2->var)->type_mem_tag;
        {
          struct alias_map_d *p_map2 = ai->pointers[j];
          tree tag2 = var_ann (p_map2->var)->type_mem_tag;
-         var_ann_t tag2_ann = var_ann (tag2);
          sbitmap may_aliases2 = p_map2->may_aliases;
 
          /* If the pointers may not point to each other, do nothing.  */
          sbitmap may_aliases2 = p_map2->may_aliases;
 
          /* If the pointers may not point to each other, do nothing.  */
-         if (!may_alias_p (p_map1->var, p_map1->set, p_map2->var, p_map2->set))
+         if (!may_alias_p (p_map1->var, p_map1->set, tag2, p_map2->set))
            continue;
 
          /* The two pointers may alias each other.  If they already have
             symbols in common, do nothing.  */
            continue;
 
          /* The two pointers may alias each other.  If they already have
             symbols in common, do nothing.  */
-         sbitmap_a_and_b (res, may_aliases1, may_aliases2);
-         if (sbitmap_first_set_bit (res) >= 0)
+         if (sbitmap_any_common_bits (may_aliases1, may_aliases2))
            continue;
 
          if (sbitmap_first_set_bit (may_aliases2) >= 0)
            continue;
 
          if (sbitmap_first_set_bit (may_aliases2) >= 0)
@@ -999,20 +1099,17 @@ compute_flow_insensitive_aliasing (struct alias_info *ai)
              EXECUTE_IF_SET_IN_SBITMAP (may_aliases2, 0, k,
                  add_may_alias (tag1, referenced_var (k)));
              sbitmap_a_or_b (may_aliases1, may_aliases1, may_aliases2);
              EXECUTE_IF_SET_IN_SBITMAP (may_aliases2, 0, k,
                  add_may_alias (tag1, referenced_var (k)));
              sbitmap_a_or_b (may_aliases1, may_aliases1, may_aliases2);
-             sbitmap_zero (may_aliases2);
-             tag2_ann->may_aliases = NULL;
            }
          else
            {
              /* Since TAG2 does not have any aliases of its own, add
                 TAG2 itself to the alias set of TAG1.  */
              add_may_alias (tag1, tag2);
            }
          else
            {
              /* Since TAG2 does not have any aliases of its own, add
                 TAG2 itself to the alias set of TAG1.  */
              add_may_alias (tag1, tag2);
+             SET_BIT (may_aliases1, var_ann (tag2)->uid);
            }
        }
     }
 
            }
        }
     }
 
-  sbitmap_free (res);
-
   if (dump_file)
     fprintf (dump_file, "%s: Total number of aliased vops: %ld\n",
             get_name (current_function_decl),
   if (dump_file)
     fprintf (dump_file, "%s: Total number of aliased vops: %ld\n",
             get_name (current_function_decl),
@@ -1155,15 +1252,12 @@ static void
 group_aliases (struct alias_info *ai)
 {
   size_t i;
 group_aliases (struct alias_info *ai)
 {
   size_t i;
-  sbitmap res;
 
   /* Sort the POINTERS array in descending order of contributed
      virtual operands.  */
   qsort (ai->pointers, ai->num_pointers, sizeof (struct alias_map_d *),
          total_alias_vops_cmp);
 
 
   /* Sort the POINTERS array in descending order of contributed
      virtual operands.  */
   qsort (ai->pointers, ai->num_pointers, sizeof (struct alias_map_d *),
          total_alias_vops_cmp);
 
-  res = sbitmap_alloc (num_referenced_vars);
-
   /* For every pointer in AI->POINTERS, reverse the roles of its tag
      and the tag's may-aliases set.  */
   for (i = 0; i < ai->num_pointers; i++)
   /* For every pointer in AI->POINTERS, reverse the roles of its tag
      and the tag's may-aliases set.  */
   for (i = 0; i < ai->num_pointers; i++)
@@ -1183,8 +1277,7 @@ group_aliases (struct alias_info *ai)
        {
          sbitmap tag2_aliases = ai->pointers[j]->may_aliases;
 
        {
          sbitmap tag2_aliases = ai->pointers[j]->may_aliases;
 
-         sbitmap_a_and_b (res, tag1_aliases, tag2_aliases);
-         if (sbitmap_first_set_bit (res) >= 0)
+          if (sbitmap_any_common_bits (tag1_aliases, tag2_aliases))
            {
              tree tag2 = var_ann (ai->pointers[j]->var)->type_mem_tag;
 
            {
              tree tag2 = var_ann (ai->pointers[j]->var)->type_mem_tag;
 
@@ -1242,7 +1335,9 @@ group_aliases (struct alias_info *ai)
          tree alias = VARRAY_TREE (aliases, j);
          var_ann_t ann = var_ann (alias);
 
          tree alias = VARRAY_TREE (aliases, j);
          var_ann_t ann = var_ann (alias);
 
-         if (ann->mem_tag_kind == NOT_A_TAG && ann->may_aliases)
+         if ((ann->mem_tag_kind == NOT_A_TAG 
+              || ann->mem_tag_kind == STRUCT_FIELD)
+             && ann->may_aliases)
            {
              tree new_alias;
 
            {
              tree new_alias;
 
@@ -1254,8 +1349,6 @@ group_aliases (struct alias_info *ai)
        }
     }
 
        }
     }
 
-  sbitmap_free (res);
-
   if (dump_file)
     fprintf (dump_file,
             "%s: Total number of aliased vops after grouping: %ld%s\n",
   if (dump_file)
     fprintf (dump_file,
             "%s: Total number of aliased vops after grouping: %ld%s\n",
@@ -1329,13 +1422,19 @@ setup_pointers_and_addressables (struct alias_info *ai)
     {
       tree var = referenced_var (i);
       var_ann_t v_ann = var_ann (var);
     {
       tree var = referenced_var (i);
       var_ann_t v_ann = var_ann (var);
+      subvar_t svars;
 
       /* Name memory tags already have flow-sensitive aliasing
         information, so they need not be processed by
         compute_flow_insensitive_aliasing.  Similarly, type memory
         tags are already accounted for when we process their
 
       /* Name memory tags already have flow-sensitive aliasing
         information, so they need not be processed by
         compute_flow_insensitive_aliasing.  Similarly, type memory
         tags are already accounted for when we process their
-        associated pointer.  */
-      if (v_ann->mem_tag_kind != NOT_A_TAG)
+        associated pointer. 
+      
+         Structure fields, on the other hand, have to have some of this
+         information processed for them, but it's pointless to mark them
+         non-addressable (since they are fake variables anyway).  */
+      if (v_ann->mem_tag_kind != NOT_A_TAG
+         && v_ann->mem_tag_kind != STRUCT_FIELD) 
        continue;
 
       /* Remove the ADDRESSABLE flag from every addressable variable whose
        continue;
 
       /* Remove the ADDRESSABLE flag from every addressable variable whose
@@ -1343,20 +1442,36 @@ setup_pointers_and_addressables (struct alias_info *ai)
          of ADDR_EXPR constants into INDIRECT_REF expressions and the
          removal of dead pointer assignments done by the early scalar
          cleanup passes.  */
          of ADDR_EXPR constants into INDIRECT_REF expressions and the
          removal of dead pointer assignments done by the early scalar
          cleanup passes.  */
-      if (TREE_ADDRESSABLE (var))
+      if (TREE_ADDRESSABLE (var) && v_ann->mem_tag_kind != STRUCT_FIELD)
        {
          if (!bitmap_bit_p (ai->addresses_needed, v_ann->uid)
        {
          if (!bitmap_bit_p (ai->addresses_needed, v_ann->uid)
-             && v_ann->mem_tag_kind == NOT_A_TAG
+             && TREE_CODE (var) != RESULT_DECL
              && !is_global_var (var))
            {
              && !is_global_var (var))
            {
+             bool okay_to_mark = true;
+             /* Since VAR is now a regular GIMPLE register, we will need
+                to rename VAR into SSA afterwards.  */
+             bitmap_set_bit (vars_to_rename, v_ann->uid);
+
+             if (var_can_have_subvars (var)
+                 && (svars = get_subvars_for_var (var)))
+               {
+                 subvar_t sv;
+
+                 for (sv = svars; sv; sv = sv->next)
+                   {         
+                     var_ann_t svann = var_ann (sv->var);
+                     if (bitmap_bit_p (ai->addresses_needed, svann->uid))
+                       okay_to_mark = false;
+                     bitmap_set_bit (vars_to_rename, svann->uid);
+                   }
+               }
              /* The address of VAR is not needed, remove the
                 addressable bit, so that it can be optimized as a
                 regular variable.  */
              /* The address of VAR is not needed, remove the
                 addressable bit, so that it can be optimized as a
                 regular variable.  */
-             mark_non_addressable (var);
+             if (okay_to_mark)
+               mark_non_addressable (var);
 
 
-             /* Since VAR is now a regular GIMPLE register, we will need
-                to rename VAR into SSA afterwards.  */
-             bitmap_set_bit (vars_to_rename, v_ann->uid);
            }
          else
            {
            }
          else
            {
@@ -1365,6 +1480,14 @@ setup_pointers_and_addressables (struct alias_info *ai)
                 clobber memory.  In those cases, we need to clobber
                 all call-clobbered variables and all addressables.  */
              bitmap_set_bit (addressable_vars, v_ann->uid);
                 clobber memory.  In those cases, we need to clobber
                 all call-clobbered variables and all addressables.  */
              bitmap_set_bit (addressable_vars, v_ann->uid);
+             if (var_can_have_subvars (var)
+                 && (svars = get_subvars_for_var (var)))
+               {
+                 subvar_t sv;
+                 for (sv = svars; sv; sv = sv->next)
+                   bitmap_set_bit (addressable_vars, var_ann (sv->var)->uid);
+               }
+
            }
        }
 
            }
        }
 
@@ -1373,7 +1496,7 @@ setup_pointers_and_addressables (struct alias_info *ai)
       if (may_be_aliased (var))
        {
          create_alias_map_for (var, ai);
       if (may_be_aliased (var))
        {
          create_alias_map_for (var, ai);
-         bitmap_set_bit (vars_to_rename, var_ann (var)->uid);
+         bitmap_set_bit (vars_to_rename, var_ann (var)->uid);    
        }
 
       /* Add pointer variables that have been dereferenced to the POINTERS
        }
 
       /* Add pointer variables that have been dereferenced to the POINTERS
@@ -1471,50 +1594,79 @@ setup_pointers_and_addressables (struct alias_info *ai)
 static void
 maybe_create_global_var (struct alias_info *ai)
 {
 static void
 maybe_create_global_var (struct alias_info *ai)
 {
-  size_t i, n_clobbered;
+  unsigned i, n_clobbered;
+  bitmap_iterator bi;
   
   /* No need to create it, if we have one already.  */
   if (global_var == NULL_TREE)
     {
       /* Count all the call-clobbered variables.  */
       n_clobbered = 0;
   
   /* No need to create it, if we have one already.  */
   if (global_var == NULL_TREE)
     {
       /* Count all the call-clobbered variables.  */
       n_clobbered = 0;
-      EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, n_clobbered++);
+      EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, bi)
+       {
+         n_clobbered++;
+       }
 
 
-      /* Create .GLOBAL_VAR if we have too many call-clobbered
-        variables.  We also create .GLOBAL_VAR when there no
-        call-clobbered variables to prevent code motion
-        transformations from re-arranging function calls that may
-        have side effects.  For instance,
+      /* If the number of virtual operands that would be needed to
+        model all the call-clobbered variables is larger than
+        GLOBAL_VAR_THRESHOLD, create .GLOBAL_VAR.
 
 
-               foo ()
-               {
-                 int a = f ();
-                 g ();
-                 h (a);
-               }
+        Also create .GLOBAL_VAR if there are no call-clobbered
+        variables and the program contains a mixture of pure/const
+        and regular function calls.  This is to avoid the problem
+        described in PR 20115:
+
+             int X;
+             int func_pure (void) { return X; }
+             int func_non_pure (int a) { X += a; }
+             int foo ()
+             {
+               int a = func_pure ();
+               func_non_pure (a);
+               a = func_pure ();
+               return a;
+             }
 
 
-        There are no call-clobbered variables in foo(), so it would
-        be entirely possible for a pass to want to move the call to
-        f() after the call to g().  If f() has side effects, that
-        would be wrong.  Creating .GLOBAL_VAR in this case will
-        insert VDEFs for it and prevent such transformations.  */
-      if (n_clobbered == 0
-         || ai->num_calls_found * n_clobbered >= (size_t) GLOBAL_VAR_THRESHOLD)
+        Since foo() has no call-clobbered variables, there is
+        no relationship between the calls to func_pure and
+        func_non_pure.  Since func_pure has no side-effects, value
+        numbering optimizations elide the second call to func_pure.
+        So, if we have some pure/const and some regular calls in the
+        program we create .GLOBAL_VAR to avoid missing these
+        relations.  */
+      if (ai->num_calls_found * n_clobbered >= (size_t) GLOBAL_VAR_THRESHOLD
+         || (n_clobbered == 0
+             && ai->num_calls_found > 0
+             && ai->num_pure_const_calls_found > 0
+             && ai->num_calls_found > ai->num_pure_const_calls_found))
        create_global_var ();
     }
 
        create_global_var ();
     }
 
-  /* If the function has calls to clobbering functions and .GLOBAL_VAR has
-     been created, make it an alias for all call-clobbered variables.  */
-  if (global_var)
-    EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i,
-      {
-       tree var = referenced_var (i);
-       if (var != global_var)
-         {
-            add_may_alias (var, global_var);
-            bitmap_set_bit (vars_to_rename, var_ann (var)->uid);
-         }
-      });
+  /* Mark all call-clobbered symbols for renaming.  Since the initial
+     rewrite into SSA ignored all call sites, we may need to rename
+     .GLOBAL_VAR and the call-clobbered variables.  */
+  EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, bi)
+    {
+      tree var = referenced_var (i);
+
+      /* If the function has calls to clobbering functions and
+        .GLOBAL_VAR has been created, make it an alias for all
+        call-clobbered variables.  */
+      if (global_var && var != global_var)
+       {
+         subvar_t svars;
+         add_may_alias (var, global_var);
+         if (var_can_have_subvars (var)
+             && (svars = get_subvars_for_var (var)))
+           {
+             subvar_t sv;
+             for (sv = svars; sv; sv = sv->next)
+               bitmap_set_bit (vars_to_rename, var_ann (sv->var)->uid);
+           }
+       }
+      
+      bitmap_set_bit (vars_to_rename, var_ann (var)->uid);
+    }
 }
 
 
 }
 
 
@@ -1532,7 +1684,7 @@ may_alias_p (tree ptr, HOST_WIDE_INT mem_alias_set,
             tree var, HOST_WIDE_INT var_alias_set)
 {
   tree mem;
             tree var, HOST_WIDE_INT var_alias_set)
 {
   tree mem;
-  var_ann_t v_ann, m_ann;
+  var_ann_t m_ann;
 
   alias_stats.alias_queries++;
   alias_stats.simple_queries++;
 
   alias_stats.alias_queries++;
   alias_stats.simple_queries++;
@@ -1546,7 +1698,6 @@ may_alias_p (tree ptr, HOST_WIDE_INT mem_alias_set,
       return false;
     }
 
       return false;
     }
 
-  v_ann = var_ann (var);
   m_ann = var_ann (mem);
 
   gcc_assert (m_ann->mem_tag_kind == TYPE_TAG);
   m_ann = var_ann (mem);
 
   gcc_assert (m_ann->mem_tag_kind == TYPE_TAG);
@@ -1558,7 +1709,8 @@ may_alias_p (tree ptr, HOST_WIDE_INT mem_alias_set,
      for PTR's alias set here, not its pointed-to type.  We also can't
      do this check with relaxed aliasing enabled.  */
   if (POINTER_TYPE_P (TREE_TYPE (var))
      for PTR's alias set here, not its pointed-to type.  We also can't
      do this check with relaxed aliasing enabled.  */
   if (POINTER_TYPE_P (TREE_TYPE (var))
-      && var_alias_set != 0)
+      && var_alias_set != 0
+      && mem_alias_set != 0)
     {
       HOST_WIDE_INT ptr_alias_set = get_alias_set (ptr);
       if (ptr_alias_set == var_alias_set)
     {
       HOST_WIDE_INT ptr_alias_set = get_alias_set (ptr);
       if (ptr_alias_set == var_alias_set)
@@ -1576,7 +1728,6 @@ may_alias_p (tree ptr, HOST_WIDE_INT mem_alias_set,
       alias_stats.tbaa_resolved++;
       return false;
     }
       alias_stats.tbaa_resolved++;
       return false;
     }
-
   alias_stats.alias_mayalias++;
   return true;
 }
   alias_stats.alias_mayalias++;
   return true;
 }
@@ -1673,14 +1824,17 @@ set_pt_malloc (tree ptr)
 }
 
 
 }
 
 
-/* Given two pointers DEST and ORIG.  Merge the points-to information in
-   ORIG into DEST.  AI is as in collect_points_to_info.  */
+/* Given two different pointers DEST and ORIG.  Merge the points-to
+   information in ORIG into DEST.  AI contains all the alias
+   information collected up to this point.  */
 
 static void
 merge_pointed_to_info (struct alias_info *ai, tree dest, tree orig)
 {
   struct ptr_info_def *dest_pi, *orig_pi;
 
 
 static void
 merge_pointed_to_info (struct alias_info *ai, tree dest, tree orig)
 {
   struct ptr_info_def *dest_pi, *orig_pi;
 
+  gcc_assert (dest != orig);
+
   /* Make sure we have points-to information for ORIG.  */
   collect_points_to_info_for (ai, orig);
 
   /* Make sure we have points-to information for ORIG.  */
   collect_points_to_info_for (ai, orig);
 
@@ -1689,6 +1843,8 @@ merge_pointed_to_info (struct alias_info *ai, tree dest, tree orig)
 
   if (orig_pi)
     {
 
   if (orig_pi)
     {
+      gcc_assert (orig_pi != dest_pi);
+
       /* Notice that we never merge PT_MALLOC.  This attribute is only
         true if the pointer is the result of a malloc() call.
         Otherwise, we can end up in this situation:
       /* Notice that we never merge PT_MALLOC.  This attribute is only
         true if the pointer is the result of a malloc() call.
         Otherwise, we can end up in this situation:
@@ -1697,28 +1853,26 @@ merge_pointed_to_info (struct alias_info *ai, tree dest, tree orig)
         ...
         P_j = P_i + X;
 
         ...
         P_j = P_i + X;
 
-        P_j would be marked as PT_MALLOC, which is wrong because
-        PT_MALLOC implies that the pointer may not point to another
-        variable.
-
-        FIXME 1: Subsequent analysis may determine that P_j
-        cannot alias anything else, but we are being conservative
-        here.
-
-        FIXME 2: If the merging comes from a copy assignment, we
-        ought to merge PT_MALLOC, but then both pointers would end up
-        getting different name tags because create_name_tags is not
-        smart enough to determine that the two come from the same
-        malloc call.  Copy propagation before aliasing should cure
-        this.  */
+        P_j would be marked as PT_MALLOC, however we currently do not
+        handle cases of more than one pointer pointing to the same
+        malloc'd area.
+
+        FIXME: If the merging comes from an expression that preserves
+        the PT_MALLOC attribute (copy assignment, address
+        arithmetic), we ought to merge PT_MALLOC, but then both
+        pointers would end up getting different name tags because
+        create_name_tags is not smart enough to determine that the
+        two come from the same malloc call.  Copy propagation before
+        aliasing should cure this.  */
       dest_pi->pt_malloc = 0;
       dest_pi->pt_malloc = 0;
-
       if (orig_pi->pt_malloc || orig_pi->pt_anything)
        set_pt_anything (dest);
 
       if (orig_pi->pt_malloc || orig_pi->pt_anything)
        set_pt_anything (dest);
 
+      dest_pi->pt_null |= orig_pi->pt_null;
+
       if (!dest_pi->pt_anything
          && orig_pi->pt_vars
       if (!dest_pi->pt_anything
          && orig_pi->pt_vars
-         && bitmap_first_set_bit (orig_pi->pt_vars) >= 0)
+         && !bitmap_empty_p (orig_pi->pt_vars))
        {
          if (dest_pi->pt_vars == NULL)
            {
        {
          if (dest_pi->pt_vars == NULL)
            {
@@ -1726,9 +1880,7 @@ merge_pointed_to_info (struct alias_info *ai, tree dest, tree orig)
              bitmap_copy (dest_pi->pt_vars, orig_pi->pt_vars);
            }
          else
              bitmap_copy (dest_pi->pt_vars, orig_pi->pt_vars);
            }
          else
-           bitmap_a_or_b (dest_pi->pt_vars,
-                          dest_pi->pt_vars,
-                          orig_pi->pt_vars);
+           bitmap_ior_into (dest_pi->pt_vars, orig_pi->pt_vars);
        }
     }
   else
        }
     }
   else
@@ -1736,70 +1888,162 @@ merge_pointed_to_info (struct alias_info *ai, tree dest, tree orig)
 }
 
 
 }
 
 
-/* Add VALUE to the list of expressions pointed-to by PTR.  */
+/* Add EXPR to the list of expressions pointed-to by PTR.  */
 
 static void
 
 static void
-add_pointed_to_expr (tree ptr, tree value)
+add_pointed_to_expr (struct alias_info *ai, tree ptr, tree expr)
 {
 {
-  if (TREE_CODE (value) == WITH_SIZE_EXPR)
-    value = TREE_OPERAND (value, 0);
-
-  /* Pointer variables should have been handled by merge_pointed_to_info.  */
-  gcc_assert (TREE_CODE (value) != SSA_NAME
-             || !POINTER_TYPE_P (TREE_TYPE (value)));
+  if (TREE_CODE (expr) == WITH_SIZE_EXPR)
+    expr = TREE_OPERAND (expr, 0);
 
   get_ptr_info (ptr);
 
 
   get_ptr_info (ptr);
 
-  /* If VALUE is the result of a malloc-like call, then the area pointed to
-     PTR is guaranteed to not alias with anything else.  */
-  if (TREE_CODE (value) == CALL_EXPR
-      && (call_expr_flags (value) & (ECF_MALLOC | ECF_MAY_BE_ALLOCA)))
-    set_pt_malloc (ptr);
-  else
-    set_pt_anything (ptr);
-
-  if (dump_file)
+  if (TREE_CODE (expr) == CALL_EXPR
+      && (call_expr_flags (expr) & (ECF_MALLOC | ECF_MAY_BE_ALLOCA)))
     {
     {
-      struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
+      /* If EXPR is a malloc-like call, then the area pointed to PTR
+        is guaranteed to not alias with anything else.  */
+      set_pt_malloc (ptr);
+    }
+  else if (TREE_CODE (expr) == ADDR_EXPR)
+    {
+      /* Found P_i = ADDR_EXPR  */
+      add_pointed_to_var (ai, ptr, expr);
+    }
+  else if (TREE_CODE (expr) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (expr)))
+    {
+      /* Found P_i = Q_j.  */
+      merge_pointed_to_info (ai, ptr, expr);
+    }
+  else if (TREE_CODE (expr) == PLUS_EXPR || TREE_CODE (expr) == MINUS_EXPR)
+    {
+      /* Found P_i = PLUS_EXPR or P_i = MINUS_EXPR  */
+      tree op0 = TREE_OPERAND (expr, 0);
+      tree op1 = TREE_OPERAND (expr, 1);
+
+      /* Both operands may be of pointer type.  FIXME: Shouldn't
+        we just expect PTR + OFFSET always?  */
+      if (POINTER_TYPE_P (TREE_TYPE (op0))
+         && TREE_CODE (op0) != INTEGER_CST)
+       {
+         if (TREE_CODE (op0) == SSA_NAME)
+           merge_pointed_to_info (ai, ptr, op0);
+         else if (TREE_CODE (op0) == ADDR_EXPR)
+           add_pointed_to_var (ai, ptr, op0);
+         else
+           set_pt_anything (ptr);
+       }
 
 
-      fprintf (dump_file, "Pointer ");
-      print_generic_expr (dump_file, ptr, dump_flags);
-      fprintf (dump_file, " points to ");
-      if (pi->pt_malloc)
-       fprintf (dump_file, "malloc space: ");
-      else
-       fprintf (dump_file, "an arbitrary address: ");
-      print_generic_expr (dump_file, value, dump_flags);
-      fprintf (dump_file, "\n");
+      if (POINTER_TYPE_P (TREE_TYPE (op1))
+         && TREE_CODE (op1) != INTEGER_CST)
+       {
+         if (TREE_CODE (op1) == SSA_NAME)
+           merge_pointed_to_info (ai, ptr, op1);
+         else if (TREE_CODE (op1) == ADDR_EXPR)
+           add_pointed_to_var (ai, ptr, op1);
+         else
+           set_pt_anything (ptr);
+       }
+
+      /* Neither operand is a pointer?  VAR can be pointing anywhere.
+        FIXME: Shouldn't we abort here?  If we get here, we found
+        PTR = INT_CST + INT_CST, which should not be a valid pointer
+        expression.  */
+      if (!(POINTER_TYPE_P (TREE_TYPE (op0))
+           && TREE_CODE (op0) != INTEGER_CST)
+         && !(POINTER_TYPE_P (TREE_TYPE (op1))
+              && TREE_CODE (op1) != INTEGER_CST))
+       set_pt_anything (ptr);
+    }
+  else if (integer_zerop (expr))
+    {
+      /* EXPR is the NULL pointer.  Mark PTR as pointing to NULL.  */
+      SSA_NAME_PTR_INFO (ptr)->pt_null = 1;
+    }
+  else
+    {
+      /* If we can't recognize the expression, assume that PTR may
+        point anywhere.  */
+      set_pt_anything (ptr);
     }
 }
 
 
 /* If VALUE is of the form &DECL, add DECL to the set of variables
    pointed-to by PTR.  Otherwise, add VALUE as a pointed-to expression by
     }
 }
 
 
 /* If VALUE is of the form &DECL, add DECL to the set of variables
    pointed-to by PTR.  Otherwise, add VALUE as a pointed-to expression by
-   PTR.  AI is as in collect_points_to_info.  */
+   PTR.  AI points to the collected alias information.  */
 
 static void
 add_pointed_to_var (struct alias_info *ai, tree ptr, tree value)
 {
   struct ptr_info_def *pi = get_ptr_info (ptr);
 
 static void
 add_pointed_to_var (struct alias_info *ai, tree ptr, tree value)
 {
   struct ptr_info_def *pi = get_ptr_info (ptr);
-  tree pt_var;
+  tree pt_var = NULL_TREE;
+  HOST_WIDE_INT offset, size;
+  tree addrop;
   size_t uid;
   size_t uid;
+  tree ref;
+  subvar_t svars;
 
   gcc_assert (TREE_CODE (value) == ADDR_EXPR);
 
 
   gcc_assert (TREE_CODE (value) == ADDR_EXPR);
 
-  pt_var = TREE_OPERAND (value, 0);
-  if (TREE_CODE_CLASS (TREE_CODE (pt_var)) == 'r')
-    pt_var = get_base_address (pt_var);
+  addrop = TREE_OPERAND (value, 0);
+  if (REFERENCE_CLASS_P (addrop))
+    pt_var = get_base_address (addrop);
+  else 
+    pt_var = addrop;
+
+  /* If this is a component_ref, see if we can get a smaller number of
+     variables to take the address of.  */
+  if (TREE_CODE (addrop) == COMPONENT_REF
+      && (ref = okay_component_ref_for_subvars (addrop, &offset ,&size)))
+    {    
+      subvar_t sv;
+      svars = get_subvars_for_var (ref);
 
 
-  if (pt_var && SSA_VAR_P (pt_var))
-    {
       uid = var_ann (pt_var)->uid;
       uid = var_ann (pt_var)->uid;
-      bitmap_set_bit (ai->addresses_needed, uid);
+      
+      if (pi->pt_vars == NULL)
+       pi->pt_vars = BITMAP_GGC_ALLOC ();
+       /* If the variable is a global, mark the pointer as pointing to
+        global memory (which will make its tag a global variable).  */
+      if (is_global_var (pt_var))
+       pi->pt_global_mem = 1;     
 
 
+      for (sv = svars; sv; sv = sv->next)
+       {
+         if (overlap_subvar (offset, size, sv, NULL))
+           {
+             bitmap_set_bit (pi->pt_vars, var_ann (sv->var)->uid);
+             bitmap_set_bit (ai->addresses_needed, var_ann (sv->var)->uid);
+           }
+       }
+    }
+  else if (pt_var && SSA_VAR_P (pt_var))
+    {
+    
+      uid = var_ann (pt_var)->uid;
+      
       if (pi->pt_vars == NULL)
        pi->pt_vars = BITMAP_GGC_ALLOC ();
       if (pi->pt_vars == NULL)
        pi->pt_vars = BITMAP_GGC_ALLOC ();
-      bitmap_set_bit (pi->pt_vars, uid);
+
+      /* If this is an aggregate, we may have subvariables for it that need
+        to be pointed to.  */
+      if (var_can_have_subvars (pt_var)
+         && (svars = get_subvars_for_var (pt_var)))
+       {
+         subvar_t sv;
+         for (sv = svars; sv; sv = sv->next)
+           {
+             uid = var_ann (sv->var)->uid;
+             bitmap_set_bit (ai->addresses_needed, uid);             
+             bitmap_set_bit (pi->pt_vars, uid);
+           }
+       }
+      else     
+       {
+         bitmap_set_bit (ai->addresses_needed, uid);
+         bitmap_set_bit (pi->pt_vars, uid);      
+       }
 
       /* If the variable is a global, mark the pointer as pointing to
         global memory (which will make its tag a global variable).  */
 
       /* If the variable is a global, mark the pointer as pointing to
         global memory (which will make its tag a global variable).  */
@@ -1833,66 +2077,19 @@ collect_points_to_info_r (tree var, tree stmt, void *data)
 
   switch (TREE_CODE (stmt))
     {
 
   switch (TREE_CODE (stmt))
     {
+    case RETURN_EXPR:
+      gcc_assert (TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR);
+      stmt = TREE_OPERAND (stmt, 0);
+      /* FALLTHRU  */
+
     case MODIFY_EXPR:
       {
        tree rhs = TREE_OPERAND (stmt, 1);
        STRIP_NOPS (rhs);
     case MODIFY_EXPR:
       {
        tree rhs = TREE_OPERAND (stmt, 1);
        STRIP_NOPS (rhs);
-
-       /* Found P_i = ADDR_EXPR  */
-       if (TREE_CODE (rhs) == ADDR_EXPR)
-         add_pointed_to_var (ai, var, rhs);
-
-       /* Found P_i = Q_j.  */
-       else if (TREE_CODE (rhs) == SSA_NAME
-                && POINTER_TYPE_P (TREE_TYPE (rhs)))
-         merge_pointed_to_info (ai, var, rhs);
-
-       /* Found P_i = PLUS_EXPR or P_i = MINUS_EXPR  */
-       else if (TREE_CODE (rhs) == PLUS_EXPR
-                || TREE_CODE (rhs) == MINUS_EXPR)
-         {
-           tree op0 = TREE_OPERAND (rhs, 0);
-           tree op1 = TREE_OPERAND (rhs, 1);
-           
-           /* Both operands may be of pointer type.  FIXME: Shouldn't
-              we just expect PTR + OFFSET always?  */
-           if (POINTER_TYPE_P (TREE_TYPE (op0))
-               && TREE_CODE (op0) != INTEGER_CST)
-             {
-               if (TREE_CODE (op0) == SSA_NAME)
-                 merge_pointed_to_info (ai, var, op0);
-               else if (TREE_CODE (op0) == ADDR_EXPR)
-                 add_pointed_to_var (ai, var, op0);
-               else
-                 add_pointed_to_expr (var, op0);
-             }
-
-           if (POINTER_TYPE_P (TREE_TYPE (op1))
-               && TREE_CODE (op1) != INTEGER_CST)
-             {
-               if (TREE_CODE (op1) == SSA_NAME)
-                 merge_pointed_to_info (ai, var, op1);
-               else if (TREE_CODE (op1) == ADDR_EXPR)
-                 add_pointed_to_var (ai, var, op1);
-               else
-                 add_pointed_to_expr (var, op1);
-             }
-
-           /* Neither operand is a pointer?  VAR can be pointing
-              anywhere.  FIXME: Is this right?  If we get here, we
-              found PTR = INT_CST + INT_CST.  */
-           if (!(POINTER_TYPE_P (TREE_TYPE (op0))
-                 && TREE_CODE (op0) != INTEGER_CST)
-               && !(POINTER_TYPE_P (TREE_TYPE (op1))
-                    && TREE_CODE (op1) != INTEGER_CST))
-             add_pointed_to_expr (var, rhs);
-         }
-
-       /* Something else.  */
-       else
-         add_pointed_to_expr (var, rhs);
+       add_pointed_to_expr (ai, var, rhs);
        break;
       }
        break;
       }
+
     case ASM_EXPR:
       /* Pointers defined by __asm__ statements can point anywhere.  */
       set_pt_anything (var);
     case ASM_EXPR:
       /* Pointers defined by __asm__ statements can point anywhere.  */
       set_pt_anything (var);
@@ -1904,13 +2101,14 @@ collect_points_to_info_r (tree var, tree stmt, void *data)
          tree decl = SSA_NAME_VAR (var);
          
          if (TREE_CODE (decl) == PARM_DECL)
          tree decl = SSA_NAME_VAR (var);
          
          if (TREE_CODE (decl) == PARM_DECL)
-           add_pointed_to_expr (var, decl);
+           add_pointed_to_expr (ai, var, decl);
          else if (DECL_INITIAL (decl))
          else if (DECL_INITIAL (decl))
-           add_pointed_to_var (ai, var, DECL_INITIAL (decl));
+           add_pointed_to_expr (ai, var, DECL_INITIAL (decl));
          else
          else
-           add_pointed_to_expr (var, decl);
+           add_pointed_to_expr (ai, var, decl);
        }
       break;
        }
       break;
+
     case PHI_NODE:
       {
         /* It STMT is a PHI node, then VAR is one of its arguments.  The
     case PHI_NODE:
       {
         /* It STMT is a PHI node, then VAR is one of its arguments.  The
@@ -1924,16 +2122,19 @@ collect_points_to_info_r (tree var, tree stmt, void *data)
            break;
            
          case SSA_NAME:
            break;
            
          case SSA_NAME:
-           merge_pointed_to_info (ai, lhs, var);
+           /* Avoid unnecessary merges.  */
+           if (lhs != var)
+             merge_pointed_to_info (ai, lhs, var);
            break;
            
          default:
            gcc_assert (is_gimple_min_invariant (var));
            break;
            
          default:
            gcc_assert (is_gimple_min_invariant (var));
-           add_pointed_to_expr (lhs, var);
+           add_pointed_to_expr (ai, lhs, var);
            break;
          }
        break;
       }
            break;
          }
        break;
       }
+
     default:
       gcc_unreachable ();
     }
     default:
       gcc_unreachable ();
     }
@@ -1951,16 +2152,18 @@ collect_points_to_info_r (tree var, tree stmt, void *data)
        3- STMT is an assignment to a non-local variable, or
        4- STMT is a return statement.
 
        3- STMT is an assignment to a non-local variable, or
        4- STMT is a return statement.
 
-   If NUM_CALLS_P is not NULL, the counter is incremented if STMT contains
-   a function call.  */
+   AI points to the alias information collected so far.  */
 
 static bool
 
 static bool
-is_escape_site (tree stmt, size_t *num_calls_p)
+is_escape_site (tree stmt, struct alias_info *ai)
 {
 {
-  if (get_call_expr_in (stmt) != NULL_TREE)
+  tree call = get_call_expr_in (stmt);
+  if (call != NULL_TREE)
     {
     {
-      if (num_calls_p)
-       (*num_calls_p)++;
+      ai->num_calls_found++;
+
+      if (!TREE_SIDE_EFFECTS (call))
+       ai->num_pure_const_calls_found++;
 
       return true;
     }
 
       return true;
     }
@@ -1979,6 +2182,16 @@ is_escape_site (tree stmt, size_t *num_calls_p)
       if (lhs == NULL_TREE)
        return true;
 
       if (lhs == NULL_TREE)
        return true;
 
+      /* If the RHS is a conversion between a pointer and an integer, the
+        pointer escapes since we can't track the integer.  */
+      if ((TREE_CODE (TREE_OPERAND (stmt, 1)) == NOP_EXPR
+          || TREE_CODE (TREE_OPERAND (stmt, 1)) == CONVERT_EXPR
+          || TREE_CODE (TREE_OPERAND (stmt, 1)) == VIEW_CONVERT_EXPR)
+         && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND
+                                       (TREE_OPERAND (stmt, 1), 0)))
+         && !POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (stmt, 1))))
+       return true;
+
       /* If the LHS is an SSA name, it can't possibly represent a non-local
         memory store.  */
       if (TREE_CODE (lhs) == SSA_NAME)
       /* If the LHS is an SSA name, it can't possibly represent a non-local
         memory store.  */
       if (TREE_CODE (lhs) == SSA_NAME)
@@ -2017,9 +2230,6 @@ create_memory_tag (tree type, bool is_type_tag)
      determine whether they should be considered globals.  */
   DECL_CONTEXT (tag) = current_function_decl;
 
      determine whether they should be considered globals.  */
   DECL_CONTEXT (tag) = current_function_decl;
 
-  /* If the pointed-to type is volatile, so is the tag.  */
-  TREE_THIS_VOLATILE (tag) = TREE_THIS_VOLATILE (type);
-
   /* Memory tags are by definition addressable.  This also prevents
      is_gimple_ref frome confusing memory tags with optimizable
      variables.  */
   /* Memory tags are by definition addressable.  This also prevents
      is_gimple_ref frome confusing memory tags with optimizable
      variables.  */
@@ -2118,6 +2328,9 @@ get_tmt_for (tree ptr, struct alias_info *ai)
       ai->pointers[ai->num_pointers++] = alias_map;
     }
 
       ai->pointers[ai->num_pointers++] = alias_map;
     }
 
+  /* If the pointed-to type is volatile, so is the tag.  */
+  TREE_THIS_VOLATILE (tag) |= TREE_THIS_VOLATILE (tag_type);
+
   /* Make sure that the type tag has the same alias set as the
      pointed-to type.  */
   gcc_assert (tag_set == get_alias_set (tag));
   /* Make sure that the type tag has the same alias set as the
      pointed-to type.  */
   gcc_assert (tag_set == get_alias_set (tag));
@@ -2134,7 +2347,7 @@ static void
 create_global_var (void)
 {
   global_var = build_decl (VAR_DECL, get_identifier (".GLOBAL_VAR"),
 create_global_var (void)
 {
   global_var = build_decl (VAR_DECL, get_identifier (".GLOBAL_VAR"),
-                           size_type_node);
+                           void_type_node);
   DECL_ARTIFICIAL (global_var) = 1;
   TREE_READONLY (global_var) = 0;
   DECL_EXTERNAL (global_var) = 1;
   DECL_ARTIFICIAL (global_var) = 1;
   TREE_READONLY (global_var) = 0;
   DECL_EXTERNAL (global_var) = 1;
@@ -2216,7 +2429,12 @@ dump_alias_info (FILE *file)
   for (i = 1; i < num_ssa_names; i++)
     {
       tree ptr = ssa_name (i);
   for (i = 1; i < num_ssa_names; i++)
     {
       tree ptr = ssa_name (i);
-      struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
+      struct ptr_info_def *pi;
+      
+      if (ptr == NULL_TREE)
+       continue;
+
+      pi = SSA_NAME_PTR_INFO (ptr);
       if (!SSA_NAME_IN_FREE_LIST (ptr)
          && pi
          && pi->name_mem_tag)
       if (!SSA_NAME_IN_FREE_LIST (ptr)
          && pi
          && pi->name_mem_tag)
@@ -2248,7 +2466,7 @@ debug_alias_info (void)
 /* Return the alias information associated with pointer T.  It creates a
    new instance if none existed.  */
 
 /* Return the alias information associated with pointer T.  It creates a
    new instance if none existed.  */
 
-static struct ptr_info_def *
+struct ptr_info_def *
 get_ptr_info (tree t)
 {
   struct ptr_info_def *pi;
 get_ptr_info (tree t)
 {
   struct ptr_info_def *pi;
@@ -2296,16 +2514,20 @@ dump_points_to_info_for (FILE *file, tree ptr)
       if (pi->pt_malloc)
        fprintf (file, ", points-to malloc");
 
       if (pi->pt_malloc)
        fprintf (file, ", points-to malloc");
 
+      if (pi->pt_null)
+       fprintf (file, ", points-to NULL");
+
       if (pi->pt_vars)
        {
          unsigned ix;
       if (pi->pt_vars)
        {
          unsigned ix;
+         bitmap_iterator bi;
 
          fprintf (file, ", points-to vars: { ");
 
          fprintf (file, ", points-to vars: { ");
-         EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, ix,
-             {
-               print_generic_expr (file, referenced_var (ix), dump_flags);
-               fprintf (file, " ");
-             });
+         EXECUTE_IF_SET_IN_BITMAP (pi->pt_vars, 0, ix, bi)
+           {
+             print_generic_expr (file, referenced_var (ix), dump_flags);
+             fprintf (file, " ");
+           }
          fprintf (file, "}");
        }
     }
          fprintf (file, "}");
        }
     }
@@ -2451,3 +2673,422 @@ may_be_aliased (tree var)
   return true;
 }
 
   return true;
 }
 
+/* This structure is simply 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.  */
+
+typedef struct fieldoff
+{
+  tree field;
+  HOST_WIDE_INT offset;  
+} *fieldoff_t;
+
+DEF_VEC_MALLOC_P(fieldoff_t);
+
+/* Return the position, in bits, of FIELD_DECL from the beginning of its
+   structure. 
+   Return -1 if the position is conditional or otherwise non-constant
+   integer.  */
+
+static HOST_WIDE_INT
+bitpos_of_field (const tree fdecl)
+{
+
+  if (TREE_CODE (DECL_FIELD_OFFSET (fdecl)) != INTEGER_CST
+      || TREE_CODE (DECL_FIELD_BIT_OFFSET (fdecl)) != INTEGER_CST)
+    return -1;
+
+  return (tree_low_cst (DECL_FIELD_OFFSET (fdecl), 1) * 8) 
+    + tree_low_cst (DECL_FIELD_BIT_OFFSET (fdecl), 1);
+}
+
+/* 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.  */
+
+static void
+push_fields_onto_fieldstack (tree type, VEC(fieldoff_t) **fieldstack, 
+                            HOST_WIDE_INT offset)
+{
+  fieldoff_t pair;
+  tree field = TYPE_FIELDS (type);
+  if (!field)
+    return;
+  if (var_can_have_subvars (field)
+      && TREE_CODE (field) == FIELD_DECL)
+    {
+      size_t before = VEC_length (fieldoff_t, *fieldstack);
+      /* Empty structures may have actual size, like in C++. So see if we
+        actually end up pushing a field, and if not, if the size is nonzero,
+        push the field onto the stack */
+      push_fields_onto_fieldstack (TREE_TYPE (field), fieldstack, offset);
+      if (before == VEC_length (fieldoff_t, *fieldstack)
+         && DECL_SIZE (field)
+         && !integer_zerop (DECL_SIZE (field)))
+       {
+         pair = xmalloc (sizeof (struct fieldoff));
+         pair->field = field;
+         pair->offset = offset;
+         VEC_safe_push (fieldoff_t, *fieldstack, pair);
+       }
+    }
+  else if (TREE_CODE (field) == FIELD_DECL)
+    {
+      pair = xmalloc (sizeof (struct fieldoff));
+      pair->field = field;
+      pair->offset = offset + bitpos_of_field (field);
+      VEC_safe_push (fieldoff_t, *fieldstack, pair);
+    }
+  for (field = TREE_CHAIN (field); field; field = TREE_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+       continue;
+      if (var_can_have_subvars (field))
+       {
+         size_t before = VEC_length (fieldoff_t, *fieldstack);
+         push_fields_onto_fieldstack (TREE_TYPE (field), fieldstack, 
+                                      offset + bitpos_of_field (field));
+      /* Empty structures may have actual size, like in C++. So see if we
+        actually end up pushing a field, and if not, if the size is nonzero,
+        push the field onto the stack */
+         if (before == VEC_length (fieldoff_t, *fieldstack)
+             && DECL_SIZE (field)
+             && !integer_zerop (DECL_SIZE (field)))
+           {
+             pair = xmalloc (sizeof (struct fieldoff));
+             pair->field = field;
+             pair->offset = offset + bitpos_of_field (field);
+             VEC_safe_push (fieldoff_t, *fieldstack, pair);
+           }
+       }
+      else
+       {
+         pair = xmalloc (sizeof (struct fieldoff));
+         pair->field = field;
+         pair->offset = offset + bitpos_of_field (field);
+         VEC_safe_push (fieldoff_t, *fieldstack, pair);
+       }
+    }
+}
+
+
+/* This represents the used range of a variable.  */
+
+typedef struct used_part
+{
+  HOST_WIDE_INT minused;
+  HOST_WIDE_INT maxused;
+} *used_part_t;
+
+/* An array of used_part structures, indexed by variable uid.  */
+
+static used_part_t *used_portions;
+
+/* Given a variable uid, UID, get or create the entry in the used portions
+   table for the variable.  */
+
+static used_part_t
+get_or_create_used_part_for (size_t uid)
+{
+  used_part_t up;
+  if (used_portions[uid] == NULL)
+    {
+      up = xcalloc (1, sizeof (struct used_part));
+      up->minused = INT_MAX;
+      up->maxused = 0;
+    }
+  else
+    up = used_portions[uid];
+  return up;
+}
+
+           
+  
+/* Given an aggregate VAR, create the subvariables that represent its
+   fields.  */
+
+static void
+create_overlap_variables_for (tree var)
+{
+  VEC(fieldoff_t) *fieldstack = NULL;
+  used_part_t up;
+  size_t uid = var_ann (var)->uid;
+
+  if (used_portions[uid] == NULL)
+    return;
+
+  push_fields_onto_fieldstack (TREE_TYPE (var), &fieldstack, 0);
+  if (VEC_length (fieldoff_t, fieldstack) != 0)
+    {
+      subvar_t *subvars;
+      fieldoff_t fo;
+      bool notokay = false;
+      int i;
+
+      /* Not all fields have DECL_SIZE set, and those that don't, we don't
+        know their size, and thus, can't handle.
+        The same is true of fields with DECL_SIZE that is not an integer
+        constant (such as variable sized fields).
+        Fields with offsets which are not constant will have an offset < 0 
+        We *could* handle fields that are constant sized arrays, but
+        currently don't.  Doing so would require some extra changes to
+        tree-ssa-operands.c.  */
+
+      for (i = 0; VEC_iterate (fieldoff_t, fieldstack, i, fo); i++)
+       {
+         if (!DECL_SIZE (fo->field) 
+             || TREE_CODE (DECL_SIZE (fo->field)) != INTEGER_CST
+             || TREE_CODE (TREE_TYPE (fo->field)) == ARRAY_TYPE
+             || fo->offset < 0)
+           {
+             notokay = true;
+             break;
+           }
+       }
+      /* Cleanup after ourselves if we can't create overlap variables.  */
+      if (notokay)
+       {
+         while (VEC_length (fieldoff_t, fieldstack) != 0)
+           {
+             fo = VEC_pop (fieldoff_t, fieldstack);
+             free (fo);
+           }
+         VEC_free (fieldoff_t, fieldstack);
+         return;
+       }
+      /* Otherwise, create the variables.  */
+      subvars = lookup_subvars_for_var (var);
+      up = used_portions[uid];
+      
+      while (VEC_length (fieldoff_t, fieldstack) != 0)
+       {
+         subvar_t sv = ggc_alloc (sizeof (struct subvar));
+         HOST_WIDE_INT fosize;
+         var_ann_t ann;
+
+         fo = VEC_pop (fieldoff_t, fieldstack);          
+         fosize = TREE_INT_CST_LOW (DECL_SIZE (fo->field));
+
+         if ((fo->offset <= up->minused
+              && fo->offset + fosize <= up->minused)
+             || fo->offset >= up->maxused)
+           {
+             free (fo);
+             continue;
+           }
+
+         sv->offset = fo->offset;
+         sv->size = fosize;
+         sv->next = *subvars;
+         sv->var = create_tmp_var_raw (TREE_TYPE (fo->field), "SFT");
+         if (dump_file)
+           {
+             fprintf (dump_file, "structure field tag %s created for var %s",
+                      get_name (sv->var), get_name (var));
+             fprintf (dump_file, " offset " HOST_WIDE_INT_PRINT_DEC,
+                      sv->offset);
+             fprintf (dump_file, " size " HOST_WIDE_INT_PRINT_DEC,
+                      sv->size);
+             fprintf (dump_file, "\n");
+             
+           }
+         
+         /* We need to copy the various flags from var to sv->var, so that
+            they are is_global_var iff the original variable was.  */
+
+         DECL_EXTERNAL (sv->var) = DECL_EXTERNAL (var);
+         TREE_PUBLIC  (sv->var) = TREE_PUBLIC (var);
+         TREE_STATIC (sv->var) = TREE_STATIC (var);
+         TREE_READONLY (sv->var) = TREE_READONLY (var);
+
+         /* Like other memory tags, these need to be marked addressable to
+            keep is_gimple_reg from thinking they are real.  */
+         TREE_ADDRESSABLE (sv->var) = 1;
+
+         DECL_CONTEXT (sv->var) = DECL_CONTEXT (var);
+
+         ann = get_var_ann (sv->var);
+         ann->mem_tag_kind = STRUCT_FIELD; 
+         ann->type_mem_tag = NULL;     
+         add_referenced_tmp_var (sv->var);
+           
+         *subvars = sv;
+         free (fo);
+       }
+
+      /* Once we have created subvars, the original is no longer call
+        clobbered on its own.  Its call clobbered status depends
+        completely on the call clobbered status of the subvars.
+
+        add_referenced_var in the above loop will take care of
+        marking subvars of global variables as call clobbered for us
+        to start, since they are global as well.  */
+      clear_call_clobbered (var);
+
+    }
+
+  VEC_free (fieldoff_t, fieldstack);
+}
+
+
+/* Find the conservative answer to the question of what portions of what 
+   structures are used by this statement.  We assume that if we have a
+   component ref with a known size + offset, that we only need that part
+   of the structure.  For unknown cases, or cases where we do something
+   to the whole structure, we assume we need to create fields for the 
+   entire structure.  */
+
+static tree
+find_used_portions (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
+{
+  switch (TREE_CODE (*tp))
+    {
+    case COMPONENT_REF:
+      {
+       HOST_WIDE_INT bitsize;
+       HOST_WIDE_INT bitpos;
+       tree offset;
+       enum machine_mode mode;
+       int unsignedp;
+       int volatilep;  
+       tree ref;
+       ref = get_inner_reference (*tp, &bitsize, &bitpos, &offset, &mode,
+                                  &unsignedp, &volatilep, false);
+       if (DECL_P (ref) && offset == NULL && bitsize != -1)
+         {         
+           size_t uid = var_ann (ref)->uid;
+           used_part_t up;
+
+           up = get_or_create_used_part_for (uid);         
+
+           if (bitpos <= up->minused)
+             up->minused = bitpos;
+           if ((bitpos + bitsize >= up->maxused))
+             up->maxused = bitpos + bitsize;       
+
+           used_portions[uid] = up;
+
+           *walk_subtrees = 0;
+           return NULL_TREE;
+         }
+       else if (DECL_P (ref))
+         {
+           if (DECL_SIZE (ref)
+               && var_can_have_subvars (ref)
+               && TREE_CODE (DECL_SIZE (ref)) == INTEGER_CST)
+             {
+               used_part_t up;
+               size_t uid = var_ann (ref)->uid;
+
+               up = get_or_create_used_part_for (uid);
+
+               up->minused = 0;
+               up->maxused = TREE_INT_CST_LOW (DECL_SIZE (ref));
+
+               used_portions[uid] = up;
+
+               *walk_subtrees = 0;
+               return NULL_TREE;
+             }
+         }
+      }
+      break;
+    case VAR_DECL:
+    case PARM_DECL:
+      {
+       tree var = *tp;
+       if (DECL_SIZE (var)
+           && var_can_have_subvars (var)
+           && TREE_CODE (DECL_SIZE (var)) == INTEGER_CST)
+         {
+           used_part_t up;
+           size_t uid = var_ann (var)->uid;        
+           
+           up = get_or_create_used_part_for (uid);
+           up->minused = 0;
+           up->maxused = TREE_INT_CST_LOW (DECL_SIZE (var));
+
+           used_portions[uid] = up;
+           *walk_subtrees = 0;
+           return NULL_TREE;
+         }
+      }
+      break;
+      
+    default:
+      break;
+      
+    }
+  return NULL_TREE;
+}
+
+/* We are about to create some new referenced variables, and we need the
+   before size.  */
+
+static size_t old_referenced_vars;
+
+
+/* Create structure field variables for structures used in this function.  */
+
+static void
+create_structure_vars (void)
+{
+  basic_block bb;
+  size_t i;
+
+  old_referenced_vars = num_referenced_vars;
+  used_portions = xcalloc (num_referenced_vars, sizeof (used_part_t));
+  
+  FOR_EACH_BB (bb)
+    {
+      block_stmt_iterator bsi;
+      for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+       {
+         walk_tree_without_duplicates (bsi_stmt_ptr (bsi), 
+                                       find_used_portions,
+                                       NULL);
+       }
+    }
+  for (i = 0; i < old_referenced_vars; i++)
+    {
+      tree var = referenced_var (i);
+      /* The C++ FE creates vars without DECL_SIZE set, for some reason.  */
+      if (var    
+         && DECL_SIZE (var)
+         && var_can_have_subvars (var)
+         && var_ann (var)->mem_tag_kind == NOT_A_TAG
+         && TREE_CODE (DECL_SIZE (var)) == INTEGER_CST)
+       create_overlap_variables_for (var);
+    }
+  for (i = 0; i < old_referenced_vars; i++)
+    free (used_portions[i]);
+
+  free (used_portions);
+}
+
+static bool
+gate_structure_vars (void)
+{
+  return flag_tree_salias != 0;
+}
+
+struct tree_opt_pass pass_create_structure_vars = 
+{
+  "salias",             /* name */
+  gate_structure_vars,  /* gate */
+  create_structure_vars, /* execute */
+  NULL,                         /* sub */
+  NULL,                         /* next */
+  0,                    /* static_pass_number */
+  0,                    /* tv_id */
+  PROP_cfg,             /* properties_required */
+  0,                    /* properties_provided */
+  0,                    /* properties_destroyed */
+  0,                    /* todo_flags_start */
+  TODO_dump_func,       /* todo_flags_finish */
+  0                     /* letter */
+};
+