OSDN Git Service

PR c/20740
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-operands.c
index 2a63d08..a010b30 100644 (file)
@@ -50,8 +50,6 @@ Boston, MA 02111-1307, USA.  */
    The routines in this file are concerned with creating this operand cache 
    from a stmt tree.
 
-   get_stmt_operands() in the primary entry point. 
-
    The operand tree is the parsed by the various get_* routines which look 
    through the stmt tree for the occurrence of operands which may be of 
    interest, and calls are made to the append_* routines whenever one is 
@@ -67,7 +65,7 @@ Boston, MA 02111-1307, USA.  */
    on each of the 5 operand vectors which have been built up.
 
    If the stmt had a previous operand cache, the finalization routines 
-   attempt to match up the new operands with the old ones.  If its a perfect 
+   attempt to match up the new operands with the old ones.  If it's a perfect 
    match, the old vector is simply reused.  If it isn't a perfect match, then 
    a new vector is created and the new operands are placed there.  For 
    virtual operands, if the previous cache had SSA_NAME version of a 
@@ -81,7 +79,7 @@ Boston, MA 02111-1307, USA.  */
 */
 
 
-/* Flags to describe operand properties in get_stmt_operands and helpers.  */
+/* Flags to describe operand properties in helpers.  */
 
 /* By default, operands are loaded.  */
 #define opf_none       0
@@ -127,9 +125,9 @@ static GTY (()) varray_type ro_call_vuses;
 static bool clobbered_aliased_loads;
 static bool clobbered_aliased_stores;
 static bool ro_call_aliased_loads;
+static stmt_operands_p parse_old_ops = NULL;
 
 def_operand_p NULL_DEF_OPERAND_P = { NULL };
-use_operand_p NULL_USE_OPERAND_P = { NULL };
 
 static void note_addressable (tree, stmt_ann_t);
 static void get_expr_operands (tree, tree *, int);
@@ -165,7 +163,7 @@ allocate_use_optype (unsigned num)
 {
   use_optype use_ops;
   unsigned size;
-  size = sizeof (struct use_optype_d) + sizeof (tree *) * (num - 1);
+  size = sizeof (struct use_optype_d) + sizeof (use_operand_type_t) * (num - 1);
   use_ops =  ggc_alloc (size);
   use_ops->num_uses = num;
   return use_ops;
@@ -194,7 +192,8 @@ allocate_vuse_optype (unsigned num)
 {
   vuse_optype vuse_ops;
   unsigned size;
-  size = sizeof (struct vuse_optype_d) + sizeof (tree) * (num - 1);
+  size = sizeof (struct vuse_optype_d) 
+       + sizeof (vuse_operand_type_t) * (num - 1);
   vuse_ops =  ggc_alloc (size);
   vuse_ops->num_vuses = num;
   return vuse_ops;
@@ -222,6 +221,10 @@ free_uses (use_optype *uses)
 {
   if (*uses)
     {
+      unsigned int x;
+      use_optype use = *uses;
+      for (x = 0; x < use->num_uses; x++)
+        delink_imm_use (&(use->uses[x]));
       ggc_free (*uses);
       *uses = NULL;
     }
@@ -248,6 +251,10 @@ free_vuses (vuse_optype *vuses)
 {
   if (*vuses)
     {
+      unsigned int x;
+      vuse_optype vuse = *vuses;
+      for (x = 0; x < vuse->num_vuses; x++)
+        delink_imm_use (&(vuse->vuses[x].imm_use));
       ggc_free (*vuses);
       *vuses = NULL;
     }
@@ -261,6 +268,10 @@ free_v_may_defs (v_may_def_optype *v_may_defs)
 {
   if (*v_may_defs)
     {
+      unsigned int x;
+      v_may_def_optype v_may_def = *v_may_defs;
+      for (x = 0; x < v_may_def->num_v_may_defs; x++)
+        delink_imm_use (&(v_may_def->v_may_defs[x].imm_use));
       ggc_free (*v_may_defs);
       *v_may_defs = NULL;
     }
@@ -274,6 +285,10 @@ free_v_must_defs (v_must_def_optype *v_must_defs)
 {
   if (*v_must_defs)
     {
+      unsigned int x;
+      v_must_def_optype v_must_def = *v_must_defs;
+      for (x = 0; x < v_must_def->num_v_must_defs; x++)
+        delink_imm_use (&(v_must_def->v_must_defs[x].imm_use));
       ggc_free (*v_must_defs);
       *v_must_defs = NULL;
     }
@@ -322,6 +337,62 @@ fini_ssa_operands (void)
     }
 }
 
+/* Initialize V_USES index INDEX to VAL for STMT.  If OLD is present, preserve
+   the position of the may-def in the immediate_use list.  */
+
+static inline void
+initialize_vuse_operand (vuse_optype vuses, unsigned int index, tree val, 
+                        tree stmt, ssa_imm_use_t *old)
+{
+  vuse_operand_type_t *ptr;
+  ptr = &(vuses->vuses[index]);
+  ptr->use = val;
+  ptr->imm_use.use = &(ptr->use);
+  if (old)
+    relink_imm_use_stmt (&(ptr->imm_use), old, stmt);
+  else
+    link_imm_use_stmt (&(ptr->imm_use), ptr->use, stmt);
+}
+
+
+/* Initialize V_MAY_DEF_OPS index X to be DEF = MAY_DEF <USE> for STMT.  If
+   OLD is present, preserve the position of the may-def in the immediate_use
+   list.  */
+
+static inline void
+initialize_v_may_def_operand (v_may_def_optype v_may_def_ops, unsigned int x, 
+                             tree def, tree use, tree stmt, ssa_imm_use_t *old)
+{
+  v_def_use_operand_type_t *ptr;
+  ptr = &(v_may_def_ops->v_may_defs[x]);
+  ptr->def = def;
+  ptr->use = use;
+  ptr->imm_use.use = &(ptr->use);
+  if (old)
+    relink_imm_use_stmt (&(ptr->imm_use), old, stmt);
+  else
+    link_imm_use_stmt (&(ptr->imm_use), ptr->use, stmt);
+}
+
+
+/* Initialize V_MUST_DEF_OPS index X to be DEF = MUST_DEF <USE> for STMT.  If
+   OLD is present, preserve the position of the may-def in the immediate_use
+   list.  */
+
+static inline void
+initialize_v_must_def_operand (v_must_def_optype v_must_def_ops, unsigned int x,
+                             tree def, tree use, tree stmt, ssa_imm_use_t *old)
+{
+  v_def_use_operand_type_t *ptr;
+  ptr = &(v_must_def_ops->v_must_defs[x]);
+  ptr->def = def;
+  ptr->use = use;
+  ptr->imm_use.use = &(ptr->use);
+  if (old)
+    relink_imm_use_stmt (&(ptr->imm_use), old, stmt);
+  else
+    link_imm_use_stmt (&(ptr->imm_use), ptr->use, stmt);
+}
 
 /* All the finalize_ssa_* routines do the work required to turn the build_
    VARRAY into an operand_vector of the appropriate type.  The original vector,
@@ -332,7 +403,7 @@ fini_ssa_operands (void)
 /* Return a new def operand vector for STMT, comparing to OLD_OPS_P.  */
 
 static def_optype
-finalize_ssa_defs (def_optype *old_ops_p, tree stmt ATTRIBUTE_UNUSED)
+finalize_ssa_defs (def_optype *old_ops_p, tree stmt)
 {
   unsigned num, x;
   def_optype def_ops, old_ops;
@@ -343,13 +414,13 @@ finalize_ssa_defs (def_optype *old_ops_p, tree stmt ATTRIBUTE_UNUSED)
     return NULL;
 
   /* There should only be a single real definition per assignment.  */
-  gcc_assert (TREE_CODE (stmt) != MODIFY_EXPR || num <= 1);
+  gcc_assert ((stmt && TREE_CODE (stmt) != MODIFY_EXPR) || num <= 1);
 
   old_ops = *old_ops_p;
 
   /* Compare old vector and new array.  */
   build_diff = true;
-  if (old_ops && old_ops->num_defs == num)
+  if (stmt && old_ops && old_ops->num_defs == num)
     {
       build_diff = false;
       for (x = 0; x < num; x++)
@@ -378,12 +449,64 @@ finalize_ssa_defs (def_optype *old_ops_p, tree stmt ATTRIBUTE_UNUSED)
 }
 
 
+/* Make sure PTR is inn the correct immediate use list.  Since uses are simply
+   pointers into the stmt TREE, there is no way of telling if anyone has
+   changed what this pointer points to via TREE_OPERANDS (exp, 0) = <...>.
+   THe contents are different, but the the pointer is still the same.  This
+   routine will check to make sure PTR is in the correct list, and if it isn't
+   put it in the correct list.  We cannot simply check the previous node 
+   because all nodes in the same stmt might have be changed.  */
+
+static inline void
+correct_use_link (ssa_imm_use_t *ptr, tree stmt)
+{
+  ssa_imm_use_t *prev;
+  tree root;
+
+  /*  Fold_stmt () may have changed the stmt pointers.  */
+  if (ptr->stmt != stmt)
+    ptr->stmt = stmt;
+
+  prev = ptr->prev;
+  if (prev)
+    {
+      bool stmt_mod = true;
+      /* Find the first element which isn't a SAFE iterator, is in a different
+        stmt, and is not a a modified stmt,  That node is in the correct list,
+        see if we are too.  */
+
+      while (stmt_mod)
+       {
+         while (prev->stmt == stmt || prev->stmt == NULL)
+           prev = prev->prev;
+         if (prev->use == NULL)
+           stmt_mod = false;
+         else
+           if ((stmt_mod = stmt_modified_p (prev->stmt)))
+             prev = prev->prev;
+       }
+
+      /* Get the ssa_name of the list the node is in.  */
+      if (prev->use == NULL)
+       root = prev->stmt;
+      else
+       root = *(prev->use);
+      /* If it's the right list, simply return.  */
+      if (root == *(ptr->use))
+       return;
+    }
+  /* Its in the wrong list if we reach here.  */
+  delink_imm_use (ptr);
+  link_imm_use (ptr, *(ptr->use));
+}
+
+
 /* Return a new use operand vector for STMT, comparing to OLD_OPS_P.  */
 
 static use_optype
-finalize_ssa_uses (use_optype *old_ops_p, tree stmt ATTRIBUTE_UNUSED)
+finalize_ssa_uses (use_optype *old_ops_p, tree stmt)
 {
-  unsigned num, x;
+  unsigned num, x, num_old, i;
   use_optype use_ops, old_ops;
   bool build_diff;
 
@@ -395,38 +518,59 @@ finalize_ssa_uses (use_optype *old_ops_p, tree stmt ATTRIBUTE_UNUSED)
   {
     unsigned x;
     /* If the pointer to the operand is the statement itself, something is
-       wrong.  It means that we are pointing to a local variable (the 
-       initial call to get_stmt_operands does not pass a pointer to a 
-       statement).  */
+       wrong.  It means that we are pointing to a local variable.  */
     for (x = 0; x < num; x++)
       gcc_assert (*(VARRAY_TREE_PTR (build_uses, x)) != stmt);
   }
 #endif
   old_ops = *old_ops_p;
+  num_old = ((stmt && old_ops) ? old_ops->num_uses : 0);
 
   /* Check if the old vector and the new array are the same.  */
   build_diff = true;
-  if (old_ops && old_ops->num_uses == num)
+  if (stmt && old_ops && num_old == num)
     {
       build_diff = false;
       for (x = 0; x < num; x++)
-        if (old_ops->uses[x].use != VARRAY_TREE_PTR (build_uses, x))
-         {
-           build_diff = true;
-           break;
-         }
+        {
+         tree *var_p = VARRAY_TREE_PTR (build_uses, x);
+         tree *node = old_ops->uses[x].use;
+         /* Check the pointer values to see if they are the same. */
+         if (node != var_p)
+           {
+             build_diff = true;
+             break;
+           }
+       }
     }
 
   if (!build_diff)
     {
       use_ops = old_ops;
       *old_ops_p = NULL;
+      for (i = 0; i < num_old; i++)
+        correct_use_link (&(use_ops->uses[i]), stmt);
     }
   else
     {
       use_ops = allocate_use_optype (num);
       for (x = 0; x < num ; x++)
-       use_ops->uses[x].use = VARRAY_TREE_PTR (build_uses, x);
+        {
+         tree *var = VARRAY_TREE_PTR (build_uses, x);
+         use_ops->uses[x].use = var;
+         for (i = 0; i < num_old; i++)
+           {
+             ssa_imm_use_t *ptr = &(old_ops->uses[i]);
+             if (ptr->use == var)
+               {
+                 relink_imm_use_stmt (&(use_ops->uses[x]), ptr, stmt);
+                 correct_use_link (&(use_ops->uses[x]), stmt);
+                 break;
+               }
+           }
+         if (i == num_old)
+           link_imm_use_stmt (&(use_ops->uses[x]), *var, stmt);
+       }
     }
   VARRAY_POP_ALL (build_uses);
 
@@ -437,7 +581,7 @@ finalize_ssa_uses (use_optype *old_ops_p, tree stmt ATTRIBUTE_UNUSED)
 /* Return a new v_may_def operand vector for STMT, comparing to OLD_OPS_P.  */
 
 static v_may_def_optype
-finalize_ssa_v_may_defs (v_may_def_optype *old_ops_p)
+finalize_ssa_v_may_defs (v_may_def_optype *old_ops_p, tree stmt)
 {
   unsigned num, x, i, old_num;
   v_may_def_optype v_may_def_ops, old_ops;
@@ -452,7 +596,7 @@ finalize_ssa_v_may_defs (v_may_def_optype *old_ops_p)
 
   /* Check if the old vector and the new array are the same.  */
   build_diff = true;
-  if (old_ops && old_ops->num_v_may_defs == num)
+  if (stmt && old_ops && old_ops->num_v_may_defs == num)
     {
       old_num = num;
       build_diff = false;
@@ -475,6 +619,8 @@ finalize_ssa_v_may_defs (v_may_def_optype *old_ops_p)
     {
       v_may_def_ops = old_ops;
       *old_ops_p = NULL;
+      for (x = 0; x < num; x++)
+        correct_use_link (&(v_may_def_ops->v_may_defs[x].imm_use), stmt);
     }
   else
     {
@@ -490,14 +636,18 @@ finalize_ssa_v_may_defs (v_may_def_optype *old_ops_p)
                result = SSA_NAME_VAR (result);
              if (result == var)
                {
-                 v_may_def_ops->v_may_defs[x] = old_ops->v_may_defs[i];
+                 initialize_v_may_def_operand (v_may_def_ops, x, 
+                                               old_ops->v_may_defs[i].def,
+                                               old_ops->v_may_defs[i].use,
+                                               stmt, 
+                                               &(old_ops->v_may_defs[i].imm_use));
                  break;
                }
            }
          if (i == old_num)
            {
-             v_may_def_ops->v_may_defs[x].def = var;
-             v_may_def_ops->v_may_defs[x].use = var;
+             initialize_v_may_def_operand (v_may_def_ops, x, var, var, stmt, 
+                                           NULL);
            }
        }
     }
@@ -528,7 +678,7 @@ cleanup_v_may_defs (void)
 /* Return a new vuse operand vector, comparing to OLD_OPS_P.  */
 
 static vuse_optype
-finalize_ssa_vuses (vuse_optype *old_ops_p)
+finalize_ssa_vuses (vuse_optype *old_ops_p, tree stmt)
 {
   unsigned num, x, i, num_v_may_defs, old_num;
   vuse_optype vuse_ops, old_ops;
@@ -613,14 +763,14 @@ finalize_ssa_vuses (vuse_optype *old_ops_p)
 
   /* Determine whether vuses is the same as the old vector.  */
   build_diff = true;
-  if (old_ops && old_ops->num_vuses == num)
+  if (stmt && old_ops && old_ops->num_vuses == num)
     {
       old_num = num;
       build_diff = false;
       for (x = 0; x < num ; x++)
         {
          tree v;
-         v = old_ops->vuses[x];
+         v = old_ops->vuses[x].use;
          if (TREE_CODE (v) == SSA_NAME)
            v = SSA_NAME_VAR (v);
          if (v != VARRAY_TREE (build_vuses, x))
@@ -637,6 +787,8 @@ finalize_ssa_vuses (vuse_optype *old_ops_p)
     {
       vuse_ops = old_ops;
       *old_ops_p = NULL;
+      for (x = 0; x < num; x++)
+        correct_use_link (&(vuse_ops->vuses[x].imm_use), stmt);
     }
   else
     {
@@ -647,17 +799,18 @@ finalize_ssa_vuses (vuse_optype *old_ops_p)
          /* Look for VAR in the old vector, and use that SSA_NAME.  */
          for (i = 0; i < old_num; i++)
            {
-             result = old_ops->vuses[i];
+             result = old_ops->vuses[i].use;
              if (TREE_CODE (result) == SSA_NAME)
                result = SSA_NAME_VAR (result);
              if (result == var)
                {
-                 vuse_ops->vuses[x] = old_ops->vuses[i];
+                 initialize_vuse_operand (vuse_ops, x, old_ops->vuses[i].use, 
+                                          stmt, &(old_ops->vuses[i].imm_use));
                  break;
                }
            }
          if (i == old_num)
-           vuse_ops->vuses[x] = var;
+           initialize_vuse_operand (vuse_ops, x, var, stmt, NULL);
        }
     }
 
@@ -672,11 +825,11 @@ finalize_ssa_vuses (vuse_optype *old_ops_p)
 /* Return a new v_must_def operand vector for STMT, comparing to OLD_OPS_P.  */
 
 static v_must_def_optype
-finalize_ssa_v_must_defs (v_must_def_optype *old_ops_p, 
-                         tree stmt ATTRIBUTE_UNUSED)
+finalize_ssa_v_must_defs (v_must_def_optype *old_ops_p, tree stmt)
 {
   unsigned num, x, i, old_num = 0;
   v_must_def_optype v_must_def_ops, old_ops;
+  tree result, var;
   bool build_diff;
 
   num = VARRAY_ACTIVE_SIZE (build_v_must_defs);
@@ -694,7 +847,7 @@ finalize_ssa_v_must_defs (v_must_def_optype *old_ops_p,
 
   /* Check if the old vector and the new array are the same.  */
   build_diff = true;
-  if (old_ops && old_ops->num_v_must_defs == num)
+  if (stmt && old_ops && old_ops->num_v_must_defs == num)
     {
       old_num = num;
       build_diff = false;
@@ -717,13 +870,15 @@ finalize_ssa_v_must_defs (v_must_def_optype *old_ops_p,
     {
       v_must_def_ops = old_ops;
       *old_ops_p = NULL;
+      for (x = 0; x < num; x++)
+        correct_use_link (&(v_must_def_ops->v_must_defs[x].imm_use), stmt);
     }
   else
     {
       v_must_def_ops = allocate_v_must_def_optype (num);
       for (x = 0; x < num ; x++)
        {
-         tree result, var = VARRAY_TREE (build_v_must_defs, x);
+         var = VARRAY_TREE (build_v_must_defs, x);
          /* Look for VAR in the original vector.  */
          for (i = 0; i < old_num; i++)
            {
@@ -732,15 +887,18 @@ finalize_ssa_v_must_defs (v_must_def_optype *old_ops_p,
                result = SSA_NAME_VAR (result);
              if (result == var)
                {
-                 v_must_def_ops->v_must_defs[x].def = old_ops->v_must_defs[i].def;
-                 v_must_def_ops->v_must_defs[x].use = old_ops->v_must_defs[i].use;
+                 initialize_v_must_def_operand (v_must_def_ops, x,
+                                                old_ops->v_must_defs[i].def,
+                                                old_ops->v_must_defs[i].use,
+                                                stmt,
+                                                &(old_ops->v_must_defs[i].imm_use));
                  break;
                }
            }
          if (i == old_num)
            {
-             v_must_def_ops->v_must_defs[x].def = var;
-             v_must_def_ops->v_must_defs[x].use = var;
+             initialize_v_must_def_operand (v_must_def_ops, x, var, var, stmt,
+                                            NULL);
            }
        }
     }
@@ -760,8 +918,9 @@ finalize_ssa_stmt_operands (tree stmt, stmt_operands_p old_ops,
   new_ops->use_ops = finalize_ssa_uses (&(old_ops->use_ops), stmt);
   new_ops->v_must_def_ops 
     = finalize_ssa_v_must_defs (&(old_ops->v_must_def_ops), stmt);
-  new_ops->v_may_def_ops = finalize_ssa_v_may_defs (&(old_ops->v_may_def_ops));
-  new_ops->vuse_ops = finalize_ssa_vuses (&(old_ops->vuse_ops));
+  new_ops->v_may_def_ops 
+    = finalize_ssa_v_may_defs (&(old_ops->v_may_def_ops), stmt);
+  new_ops->vuse_ops = finalize_ssa_vuses (&(old_ops->vuse_ops), stmt);
 }
 
 
@@ -847,46 +1006,15 @@ append_v_must_def (tree var)
   VARRAY_PUSH_TREE (build_v_must_defs, var);
 }
 
-/* Create an operands cache for STMT, returning it in NEW_OPS. OLD_OPS are the
-   original operands, and if ANN is non-null, appropriate stmt flags are set
-   in the stmt's annotation.  Note that some fields in old_ops may 
-   change to NULL, although none of the memory they originally pointed to 
-   will be destroyed.  It is appropriate to call free_stmt_operands() on 
-   the value returned in old_ops.
-
-   The rationale for this: Certain optimizations wish to examine the difference
-   between new_ops and old_ops after processing.  If a set of operands don't
-   change, new_ops will simply assume the pointer in old_ops, and the old_ops
-   pointer will be set to NULL, indicating no memory needs to be cleared.  
-   Usage might appear something like:
-
-       old_ops_copy = old_ops = stmt_ann(stmt)->operands;
-       build_ssa_operands (stmt, NULL, &old_ops, &new_ops);
-          <* compare old_ops_copy and new_ops *>
-       free_ssa_operands (old_ops);                                    */
 
+/* Parse STMT looking for operands.  OLD_OPS is the original stmt operand
+   cache for STMT, if it existed before.  When finished, the various build_*
+   operand vectors will have potential operands. in them.  */
+                                                                                
 static void
-build_ssa_operands (tree stmt, stmt_ann_t ann, stmt_operands_p old_ops, 
-                   stmt_operands_p new_ops)
+parse_ssa_operands (tree stmt)
 {
   enum tree_code code;
-  tree_ann_t saved_ann = stmt->common.ann;
-  
-  /* Replace stmt's annotation with the one passed in for the duration
-     of the operand building process.  This allows "fake" stmts to be built
-     and not be included in other data structures which can be built here.  */
-  stmt->common.ann = (tree_ann_t) ann;
-  
-  /* Initially assume that the statement has no volatile operands, nor
-     makes aliased loads or stores.  */
-  if (ann)
-    {
-      ann->has_volatile_ops = false;
-      ann->makes_aliased_stores = false;
-      ann->makes_aliased_loads = false;
-    }
-
-  start_ssa_stmt_operands ();
 
   code = TREE_CODE (stmt);
   switch (code)
@@ -956,15 +1084,69 @@ build_ssa_operands (tree stmt, stmt_ann_t ann, stmt_operands_p old_ops,
 
     default:
       /* Notice that if get_expr_operands tries to use &STMT as the operand
-        pointer (which may only happen for USE operands), we will abort in
+        pointer (which may only happen for USE operands), we will fail in
         append_use.  This default will handle statements like empty
         statements, or CALL_EXPRs that may appear on the RHS of a statement
         or as statements themselves.  */
       get_expr_operands (stmt, &stmt, opf_none);
       break;
     }
+}
+
+/* Create an operands cache for STMT, returning it in NEW_OPS. OLD_OPS are the
+   original operands, and if ANN is non-null, appropriate stmt flags are set
+   in the stmt's annotation.  If ANN is NULL, this is not considered a "real"
+   stmt, and none of the operands will be entered into their respective
+   immediate uses tables.  This is to allow stmts to be processed when they
+   are not actually in the CFG.
+
+   Note that some fields in old_ops may change to NULL, although none of the
+   memory they originally pointed to will be destroyed.  It is appropriate
+   to call free_stmt_operands() on the value returned in old_ops.
+
+   The rationale for this: Certain optimizations wish to examine the difference
+   between new_ops and old_ops after processing.  If a set of operands don't
+   change, new_ops will simply assume the pointer in old_ops, and the old_ops
+   pointer will be set to NULL, indicating no memory needs to be cleared.  
+   Usage might appear something like:
+
+       old_ops_copy = old_ops = stmt_ann(stmt)->operands;
+       build_ssa_operands (stmt, NULL, &old_ops, &new_ops);
+          <* compare old_ops_copy and new_ops *>
+       free_ssa_operands (old_ops);                                    */
+
+static void
+build_ssa_operands (tree stmt, stmt_ann_t ann, stmt_operands_p old_ops, 
+                   stmt_operands_p new_ops)
+{
+  tree_ann_t saved_ann = stmt->common.ann;
+  
+  /* Replace stmt's annotation with the one passed in for the duration
+     of the operand building process.  This allows "fake" stmts to be built
+     and not be included in other data structures which can be built here.  */
+  stmt->common.ann = (tree_ann_t) ann;
+
+  parse_old_ops = old_ops;
+  
+  /* Initially assume that the statement has no volatile operands, nor
+     makes aliased loads or stores.  */
+  if (ann)
+    {
+      ann->has_volatile_ops = false;
+      ann->makes_aliased_stores = false;
+      ann->makes_aliased_loads = false;
+    }
 
-  finalize_ssa_stmt_operands (stmt, old_ops, new_ops);
+  start_ssa_stmt_operands ();
+
+  parse_ssa_operands (stmt);
+
+  parse_old_ops = NULL;
+
+  if (ann)
+    finalize_ssa_stmt_operands (stmt, old_ops, new_ops);
+  else
+    finalize_ssa_stmt_operands (NULL, old_ops, new_ops);
   stmt->common.ann = saved_ann;
 }
 
@@ -987,25 +1169,70 @@ free_ssa_operands (stmt_operands_p ops)
 }
 
 
-/* Get the operands of statement STMT.  Note that repeated calls to
-   get_stmt_operands for the same statement will do nothing until the
-   statement is marked modified by a call to modify_stmt().  */
+/* Swap operands EXP0 and EXP1 in STMT.  */
+
+static void
+swap_tree_operands (tree *exp0, tree *exp1)
+{
+  tree op0, op1;
+  op0 = *exp0;
+  op1 = *exp1;
+
+  /* If the operand cache is active, attempt to preserve the relative positions
+     of these two operands in their respective immediate use lists.  */
+  if (build_defs != NULL && op0 != op1 && parse_old_ops != NULL)
+    {
+      unsigned x, use0, use1;
+      use_optype uses = parse_old_ops->use_ops;
+      use0 = use1 = NUM_USES (uses);
+      /* Find the 2 operands in the cache, if they are there.  */
+      for (x = 0; x < NUM_USES (uses); x++)
+       if (USE_OP_PTR (uses, x)->use == exp0)
+         {
+           use0 = x;
+           break;
+         }
+      for (x = 0; x < NUM_USES (uses); x++)
+       if (USE_OP_PTR (uses, x)->use == exp1)
+         {
+           use1 = x;
+           break;
+         }
+      /* If both uses don't have operand entries, there isnt much we can do
+         at this point. Presumably we dont need to worry about it.  */
+      if (use0 != NUM_USES (uses) && use1 != NUM_USES (uses))
+        {
+         tree *tmp = USE_OP_PTR (uses, use1)->use;
+         gcc_assert (use0 != use1);
+
+         USE_OP_PTR (uses, use1)->use = USE_OP_PTR (uses, use0)->use;
+         USE_OP_PTR (uses, use0)->use = tmp;
+       }
+    }
+
+  /* Now swap the data.  */
+  *exp0 = op1;
+  *exp1 = op0;
+}
+
+/* Get the operands of statement STMT.  */
 
 void
-get_stmt_operands (tree stmt)
+update_stmt_operands (tree stmt)
 {
   stmt_ann_t ann;
   stmt_operands_t old_operands;
 
+  /* Don't do anything if we are called before SSA is initialized.  */
+  if (build_defs == NULL)
+    return;
   /* The optimizers cannot handle statements that are nothing but a
      _DECL.  This indicates a bug in the gimplifier.  */
   gcc_assert (!SSA_VAR_P (stmt));
 
   ann = get_stmt_ann (stmt);
 
-  /* If the statement has not been modified, the operands are still valid.  */
-  if (!ann->modified)
-    return;
+  gcc_assert (ann->modified);
 
   timevar_push (TV_TREE_OPS);
 
@@ -1015,58 +1242,13 @@ get_stmt_operands (tree stmt)
   build_ssa_operands (stmt, ann, &old_operands, &(ann->operands));
   free_ssa_operands (&old_operands);
 
-  /* Clear the modified bit for STMT.  Subsequent calls to
-     get_stmt_operands for this statement will do nothing until the
-     statement is marked modified by a call to modify_stmt().  */
+  /* Clear the modified bit for STMT.  */
   ann->modified = 0;
 
   timevar_pop (TV_TREE_OPS);
 }
 
 
-/* Return true if OFFSET and SIZE define a range that overlaps with some
-   portion of the range of SV, a subvar.  If there was an exact overlap,
-   *EXACT will be set to true upon return. */
-
-static bool
-overlap_subvar (HOST_WIDE_INT offset, HOST_WIDE_INT size,
-               subvar_t sv,  bool *exact)
-{
-  /* There are three possible cases of overlap.
-     1. We can have an exact overlap, like so:   
-     |offset, offset + size             |
-     |sv->offset, sv->offset + sv->size |
-     
-     2. We can have offset starting after sv->offset, like so:
-     
-           |offset, offset + size              |
-     |sv->offset, sv->offset + sv->size  |
-
-     3. We can have offset starting before sv->offset, like so:
-     
-     |offset, offset + size    |
-       |sv->offset, sv->offset + sv->size|
-  */
-
-  if (exact)
-    *exact = false;
-  if (offset == sv->offset && size == sv->size)
-    {
-      if (exact)
-       *exact = true;
-      return true;
-    }
-  else if (offset >= sv->offset && offset < (sv->offset + sv->size))
-    {
-      return true;
-    }
-  else if (offset < sv->offset && (offset + size > sv->offset))
-    {
-      return true;
-    }
-  return false;
-
-}
 /* Recursively scan the expression pointed by EXPR_P in statement referred to
    by INFO.  FLAGS is one of the OPF_* constants modifying how to interpret the
    operands found.  */
@@ -1261,6 +1443,7 @@ get_expr_operands (tree stmt, tree *expr_p, int flags)
     case TRUTH_XOR_EXPR:
     case COMPOUND_EXPR:
     case OBJ_TYPE_REF:
+    case ASSERT_EXPR:
     do_binary:
       {
        tree op0 = TREE_OPERAND (expr, 0);
@@ -1282,15 +1465,15 @@ get_expr_operands (tree stmt, tree *expr_p, int flags)
                || code == GE_EXPR)
              {
                TREE_SET_CODE (expr, swap_tree_comparison (code));
-               TREE_OPERAND (expr, 0) = op1;
-               TREE_OPERAND (expr, 1) = op0;
+               swap_tree_operands (&TREE_OPERAND (expr, 0),                    
+                                   &TREE_OPERAND (expr, 1));
              }
          
            /* For a commutative operator we can just swap the operands.  */
            else if (commutative_tree_code (code))
              {
-               TREE_OPERAND (expr, 0) = op1;
-               TREE_OPERAND (expr, 1) = op0;
+               swap_tree_operands (&TREE_OPERAND (expr, 0),                    
+                                   &TREE_OPERAND (expr, 1));
              }
          }
 
@@ -1413,6 +1596,17 @@ get_asm_expr_operands (tree stmt)
        EXECUTE_IF_SET_IN_BITMAP (addressable_vars, 0, i, bi)
            {
              tree var = referenced_var (i);
+
+             /* Subvars are explicitly represented in this list, so
+                we don't need the original to be added to the clobber
+                ops, but the original *will* be in this list because 
+                we keep the addressability of the original
+                variable up-to-date so we don't screw up the rest of
+                the backend.  */
+             if (var_can_have_subvars (var)
+                 && get_subvars_for_var (var) != NULL)
+               continue;               
+
              add_stmt_operand (&var, s_ann, opf_is_def);
            }
 
@@ -1486,7 +1680,7 @@ get_indirect_ref_operands (tree stmt, tree expr, int flags)
 
   /* Everything else *should* have been folded elsewhere, but users
      are smarter than we in finding ways to write invalid code.  We
-     cannot just abort here.  If we were absolutely certain that we
+     cannot just assert here.  If we were absolutely certain that we
      do handle all valid cases, then we could just do nothing here.
      That seems optimistic, so attempt to do something logical... */
   else if ((TREE_CODE (ptr) == PLUS_EXPR || TREE_CODE (ptr) == MINUS_EXPR)
@@ -1533,7 +1727,7 @@ get_call_expr_operands (tree stmt, tree expr)
       && !bitmap_empty_p (call_clobbered_vars)
       && !(call_flags & ECF_NOVOPS))
     {
-      /* A 'pure' or a 'const' functions never call clobber anything. 
+      /* A 'pure' or a 'const' function never call-clobbers anything. 
         A 'noreturn' function might, but since we don't return anyway 
         there is no point in recording that.  */ 
       if (TREE_SIDE_EFFECTS (expr)
@@ -1596,6 +1790,24 @@ add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags)
   if (TREE_THIS_VOLATILE (sym) && s_ann)
     s_ann->has_volatile_ops = true;
 
+  /* If the variable cannot be modified and this is a V_MAY_DEF change
+     it into a VUSE.  This happens when read-only variables are marked
+     call-clobbered and/or aliased to writeable variables.  So we only
+     check that this only happens on stores, and not writes to GIMPLE
+     registers.
+     
+     FIXME: The C++ FE is emitting assignments in the IL stream for
+     read-only globals.  This is wrong, but for the time being disable
+     this transformation on V_MUST_DEF operands (otherwise, we
+     mis-optimize SPEC2000's eon).  */
+  if ((flags & opf_is_def)
+      && !(flags & opf_kill_def)
+      && unmodifiable_var_p (var))
+    {
+      gcc_assert (!is_real_op);
+      flags &= ~opf_is_def;
+    }
+
   if (is_real_op)
     {
       /* The variable is a GIMPLE register.  Add it to real operands.  */
@@ -1656,17 +1868,35 @@ add_stmt_operand (tree *var_p, stmt_ann_t s_ann, int flags)
 
          if (flags & opf_is_def)
            {
+             bool added_may_defs_p = false;
+
              /* If the variable is also an alias tag, add a virtual
                 operand for it, otherwise we will miss representing
                 references to the members of the variable's alias set.
                 This fixes the bug in gcc.c-torture/execute/20020503-1.c.  */
              if (v_ann->is_alias_tag)
-               append_v_may_def (var);
+               {
+                 added_may_defs_p = true;
+                 append_v_may_def (var);
+               }
 
              for (i = 0; i < VARRAY_ACTIVE_SIZE (aliases); i++)
-               append_v_may_def (VARRAY_TREE (aliases, i));
+               {
+                 /* While VAR may be modifiable, some of its aliases
+                    may not be.  If that's the case, we don't really
+                    need to add them a V_MAY_DEF for them.  */
+                 tree alias = VARRAY_TREE (aliases, i);
+
+                 if (unmodifiable_var_p (alias))
+                   append_vuse (alias);
+                 else
+                   {
+                     append_v_may_def (alias);
+                     added_may_defs_p = true;
+                   }
+               }
 
-             if (s_ann)
+             if (s_ann && added_may_defs_p)
                s_ann->makes_aliased_stores = 1;
            }
          else
@@ -1729,7 +1959,7 @@ note_addressable (tree var, stmt_ann_t s_ann)
       if (s_ann->addresses_taken == NULL)
        s_ann->addresses_taken = BITMAP_GGC_ALLOC ();      
       
-      bitmap_set_bit (s_ann->addresses_taken, var_ann (var)->uid);
+
       if (var_can_have_subvars (var)
          && (svars = get_subvars_for_var (var)))
        {
@@ -1737,6 +1967,8 @@ note_addressable (tree var, stmt_ann_t s_ann)
          for (sv = svars; sv; sv = sv->next)
            bitmap_set_bit (s_ann->addresses_taken, var_ann (sv->var)->uid);
        }
+      else
+       bitmap_set_bit (s_ann->addresses_taken, var_ann (var)->uid);
     }
 }
 
@@ -1796,8 +2028,7 @@ add_call_clobber_ops (tree stmt)
   EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i, bi)
     {
       tree var = referenced_var (i);
-      if (TREE_READONLY (var)
-         && (TREE_STATIC (var) || DECL_EXTERNAL (var)))
+      if (unmodifiable_var_p (var))
        add_stmt_operand (&var, &empty_ann, opf_none);
       else
        add_stmt_operand (&var, &empty_ann, opf_is_def);
@@ -1914,7 +2145,7 @@ copy_virtual_operands (tree dst, tree src)
     {
       *vuses_new = allocate_vuse_optype (NUM_VUSES (vuses));
       for (i = 0; i < NUM_VUSES (vuses); i++)
-       SET_VUSE_OP (*vuses_new, i, VUSE_OP (vuses, i));
+       initialize_vuse_operand (*vuses_new, i, VUSE_OP (vuses, i), dst, NULL);
     }
 
   if (v_may_defs)
@@ -1922,19 +2153,23 @@ copy_virtual_operands (tree dst, tree src)
       *v_may_defs_new = allocate_v_may_def_optype (NUM_V_MAY_DEFS (v_may_defs));
       for (i = 0; i < NUM_V_MAY_DEFS (v_may_defs); i++)
        {
-         SET_V_MAY_DEF_OP (*v_may_defs_new, i, V_MAY_DEF_OP (v_may_defs, i));
-         SET_V_MAY_DEF_RESULT (*v_may_defs_new, i, 
-                               V_MAY_DEF_RESULT (v_may_defs, i));
+         initialize_v_may_def_operand (*v_may_defs_new, i, 
+                                       V_MAY_DEF_RESULT (v_may_defs, i),
+                                       V_MAY_DEF_OP (v_may_defs, i), dst,
+                                       NULL);
        }
     }
 
   if (v_must_defs)
     {
-      *v_must_defs_new = allocate_v_must_def_optype (NUM_V_MUST_DEFS (v_must_defs));
+      *v_must_defs_new 
+        = allocate_v_must_def_optype (NUM_V_MUST_DEFS (v_must_defs));
       for (i = 0; i < NUM_V_MUST_DEFS (v_must_defs); i++)
        {
-         SET_V_MUST_DEF_RESULT (*v_must_defs_new, i, V_MUST_DEF_RESULT (v_must_defs, i));
-         SET_V_MUST_DEF_KILL (*v_must_defs_new, i, V_MUST_DEF_KILL (v_must_defs, i));
+         initialize_v_must_def_operand (*v_must_defs_new, i, 
+                                        V_MUST_DEF_RESULT (v_must_defs, i),
+                                        V_MUST_DEF_KILL (v_must_defs, i), dst,
+                                        NULL);
        }
     }
 }
@@ -1981,7 +2216,140 @@ create_ssa_artficial_load_stmt (stmt_operands_p old_ops, tree new_stmt)
     }
 
   /* Now set the vuses for this new stmt.  */
-  ann->operands.vuse_ops = finalize_ssa_vuses (&(tmp.vuse_ops));
+  ann->operands.vuse_ops = finalize_ssa_vuses (&(tmp.vuse_ops), NULL);
+}
+
+/* Scan the immediate_use list for VAR making sure its linked properly.
+   return RTUE iof there is a problem.  */
+
+bool
+verify_imm_links (FILE *f, tree var)
+{
+  ssa_imm_use_t *ptr, *prev;
+  ssa_imm_use_t *list;
+  int count;
+
+  gcc_assert (TREE_CODE (var) == SSA_NAME);
+
+  list = &(SSA_NAME_IMM_USE_NODE (var));
+  gcc_assert (list->use == NULL);
+
+  if (list->prev == NULL)
+    {
+      gcc_assert (list->next == NULL);
+      return false;
+    }
+
+  prev = list;
+  count = 0;
+  for (ptr = list->next; ptr != list; )
+    {
+      if (prev != ptr->prev)
+       goto error;
+      
+      if (ptr->use == NULL)
+       goto error; /* 2 roots, or SAFE guard node.  */
+      else if (*(ptr->use) != var)
+       goto error;
+
+      prev = ptr;
+      ptr = ptr->next;
+      /* Avoid infinite loops.  */
+      if (count++ > 30000)
+       goto error;
+    }
+
+  /* Verify list in the other direction.  */
+  prev = list;
+  for (ptr = list->prev; ptr != list; )
+    {
+      if (prev != ptr->next)
+       goto error;
+      prev = ptr;
+      ptr = ptr->prev;
+      if (count-- < 0)
+       goto error;
+    }
+
+  if (count != 0)
+    goto error;
+
+  return false;
+
+ error:
+  if (ptr->stmt && stmt_modified_p (ptr->stmt))
+    {
+      fprintf (f, " STMT MODIFIED. - <%p> ", (void *)ptr->stmt);
+      print_generic_stmt (f, ptr->stmt, TDF_SLIM);
+    }
+  fprintf (f, " IMM ERROR : (use_p : tree - %p:%p)", (void *)ptr, 
+          (void *)ptr->use);
+  print_generic_expr (f, USE_FROM_PTR (ptr), TDF_SLIM);
+  fprintf(f, "\n");
+  return true;
+}
+
+
+/* Dump all the immediate uses to FILE.  */
+
+void
+dump_immediate_uses_for (FILE *file, tree var)
+{
+  imm_use_iterator iter;
+  use_operand_p use_p;
+
+  gcc_assert (var && TREE_CODE (var) == SSA_NAME);
+
+  print_generic_expr (file, var, TDF_SLIM);
+  fprintf (file, " : -->");
+  if (has_zero_uses (var))
+    fprintf (file, " no uses.\n");
+  else
+    if (has_single_use (var))
+      fprintf (file, " single use.\n");
+    else
+      fprintf (file, "%d uses.\n", num_imm_uses (var));
+
+  FOR_EACH_IMM_USE_FAST (use_p, iter, var)
+    {
+      print_generic_stmt (file, USE_STMT (use_p), TDF_SLIM);
+    }
+  fprintf(file, "\n");
+}
+
+/* Dump all the immediate uses to FILE.  */
+
+void
+dump_immediate_uses (FILE *file)
+{
+  tree var;
+  unsigned int x;
+
+  fprintf (file, "Immediate_uses: \n\n");
+  for (x = 1; x < num_ssa_names; x++)
+    {
+      var = ssa_name(x);
+      if (!var)
+        continue;
+      dump_immediate_uses_for (file, var);
+    }
+}
+
+
+/* Dump def-use edges on stderr.  */
+
+void
+debug_immediate_uses (void)
+{
+  dump_immediate_uses (stderr);
+}
+
+/* Dump def-use edges on stderr.  */
+
+void
+debug_immediate_uses_for (tree var)
+{
+  dump_immediate_uses_for (stderr, var);
 }
 
 #include "gt-tree-ssa-operands.h"