OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / gimple.c
index 9e7d921..a91e83a 100644 (file)
@@ -1085,7 +1085,7 @@ gimple_build_predict (enum br_predictor predictor, enum prediction outcome)
 {
   gimple p = gimple_alloc (GIMPLE_PREDICT, 0);
   /* Ensure all the predictors fit into the lower bits of the subcode.  */
-  gcc_assert (END_PREDICTORS <= GF_PREDICT_TAKEN);
+  gcc_assert ((int) END_PREDICTORS <= GF_PREDICT_TAKEN);
   gimple_predict_set_predictor (p, predictor);
   gimple_predict_set_outcome (p, outcome);
   return p;
@@ -1100,7 +1100,7 @@ gimple_statement_structure (gimple gs)
   return gss_for_code (gimple_code (gs));
 }
 
-#if defined ENABLE_GIMPLE_CHECKING && (GCC_VERSION >= 2007)
+#if defined ENABLE_GIMPLE_CHECKING
 /* Complain of a gimple type mismatch and die.  */
 
 void
@@ -1117,41 +1117,6 @@ gimple_check_failed (const_gimple gs, const char *file, int line,
                    : "",
                  function, trim_filename (file), line);
 }
-
-
-/* Similar to gimple_check_failed, except that instead of specifying a
-   dozen codes, use the knowledge that they're all sequential.  */
-
-void
-gimple_range_check_failed (const_gimple gs, const char *file, int line,
-                          const char *function, enum gimple_code c1,
-                          enum gimple_code c2)
-{
-  char *buffer;
-  unsigned length = 0;
-  enum gimple_code c;
-
-  for (c = c1; c <= c2; ++c)
-    length += 4 + strlen (gimple_code_name[c]);
-
-  length += strlen ("expected ");
-  buffer = XALLOCAVAR (char, length);
-  length = 0;
-
-  for (c = c1; c <= c2; ++c)
-    {
-      const char *prefix = length ? " or " : "expected ";
-
-      strcpy (buffer + length, prefix);
-      length += strlen (prefix);
-      strcpy (buffer + length, gimple_code_name[c]);
-      length += strlen (gimple_code_name[c]);
-    }
-
-  internal_error ("gimple check: %s, have %s in %s, at %s:%d",
-                 buffer, gimple_code_name[gimple_code (gs)],
-                 function, trim_filename (file), line);
-}
 #endif /* ENABLE_GIMPLE_CHECKING */
 
 
@@ -1415,7 +1380,8 @@ walk_gimple_op (gimple stmt, walk_tree_fn callback_op,
       /* Walk the RHS operands.  A formal temporary LHS may use a
         COMPONENT_REF RHS.  */
       if (wi)
-       wi->val_only = !is_gimple_formal_tmp_var (gimple_assign_lhs (stmt));
+       wi->val_only = !is_gimple_reg (gimple_assign_lhs (stmt))
+                       || !gimple_assign_single_p (stmt);
 
       for (i = 1; i < gimple_num_ops (stmt); i++)
        {
@@ -1816,6 +1782,14 @@ gimple_body (tree fndecl)
   return fn ? fn->gimple_body : NULL;
 }
 
+/* Return true when FNDECL has Gimple body either in unlowered
+   or CFG form.  */
+bool
+gimple_has_body_p (tree fndecl)
+{
+  struct function *fn = DECL_STRUCT_FUNCTION (fndecl);
+  return (gimple_body (fndecl) || (fn && fn->cfg));
+}
 
 /* Detect flags from a GIMPLE_CALL.  This is just like
    call_expr_flags, but for gimple tuples.  */
@@ -1928,7 +1902,7 @@ gimple_set_bb (gimple stmt, basic_block bb)
          LABEL_DECL_UID (t) = uid = cfun->cfg->last_label_uid++;
          if (old_len <= (unsigned) uid)
            {
-             unsigned new_len = 3 * uid / 2;
+             unsigned new_len = 3 * uid / 2 + 1;
 
              VEC_safe_grow_cleared (basic_block, gc, label_to_block_map,
                                     new_len);
@@ -2058,7 +2032,7 @@ gimple_assign_set_rhs_with_ops (gimple_stmt_iterator *gsi, enum tree_code code,
 tree
 gimple_get_lhs (const_gimple stmt)
 {
-  enum tree_code code = gimple_code (stmt);
+  enum gimple_code code = gimple_code (stmt);
 
   if (code == GIMPLE_ASSIGN)
     return gimple_assign_lhs (stmt);
@@ -2075,7 +2049,7 @@ gimple_get_lhs (const_gimple stmt)
 void
 gimple_set_lhs (gimple stmt, tree lhs)
 {
-  enum tree_code code = gimple_code (stmt);
+  enum gimple_code code = gimple_code (stmt);
 
   if (code == GIMPLE_ASSIGN)
     gimple_assign_set_lhs (stmt, lhs);
@@ -2221,27 +2195,21 @@ gimple_copy (gimple stmt)
       for (i = 0; i < num_ops; i++)
        gimple_set_op (copy, i, unshare_expr (gimple_op (stmt, i)));
 
-      /* Clear out SSA operand vectors on COPY.  Note that we cannot
-        call the API functions for setting addresses_taken, stores
-        and loads.  These functions free the previous values, and we
-        cannot do that on COPY as it will affect the original
-        statement.  */
+      /* Clear out SSA operand vectors on COPY.  */
       if (gimple_has_ops (stmt))
        {
          gimple_set_def_ops (copy, NULL);
          gimple_set_use_ops (copy, NULL);
-         copy->gsops.opbase.addresses_taken = NULL;
        }
 
       if (gimple_has_mem_ops (stmt))
        {
-         gimple_set_vdef_ops (copy, NULL);
-         gimple_set_vuse_ops (copy, NULL);
-         copy->gsmem.membase.stores = NULL;
-         copy->gsmem.membase.loads = NULL;
+         gimple_set_vdef (copy, gimple_vdef (stmt));
+         gimple_set_vuse (copy, gimple_vuse (stmt));
        }
 
-      update_stmt (copy);
+      /* SSA operands need to be updated.  */
+      gimple_set_modified (copy, true);
     }
 
   return copy;
@@ -2482,46 +2450,6 @@ dump_gimple_statistics (void)
 }
 
 
-/* Deep copy SYMS into the set of symbols stored by STMT.  If SYMS is
-   NULL or empty, the storage used is freed up.  */
-
-void
-gimple_set_stored_syms (gimple stmt, bitmap syms, bitmap_obstack *obs)
-{
-  gcc_assert (gimple_has_mem_ops (stmt));
-
-  if (syms == NULL || bitmap_empty_p (syms))
-    BITMAP_FREE (stmt->gsmem.membase.stores);
-  else
-    {
-      if (stmt->gsmem.membase.stores == NULL)
-       stmt->gsmem.membase.stores = BITMAP_ALLOC (obs);
-
-      bitmap_copy (stmt->gsmem.membase.stores, syms);
-    }
-}
-
-
-/* Deep copy SYMS into the set of symbols loaded by STMT.  If SYMS is
-   NULL or empty, the storage used is freed up.  */
-
-void
-gimple_set_loaded_syms (gimple stmt, bitmap syms, bitmap_obstack *obs)
-{
-  gcc_assert (gimple_has_mem_ops (stmt));
-
-  if (syms == NULL || bitmap_empty_p (syms))
-    BITMAP_FREE (stmt->gsmem.membase.loads);
-  else
-    {
-      if (stmt->gsmem.membase.loads == NULL)
-       stmt->gsmem.membase.loads = BITMAP_ALLOC (obs);
-
-      bitmap_copy (stmt->gsmem.membase.loads, syms);
-    }
-}
-
-
 /* Return the number of operands needed on the RHS of a GIMPLE
    assignment for an expression with tree code CODE.  */
 
@@ -2586,37 +2514,13 @@ is_gimple_operand (const_tree op)
   return op && get_gimple_rhs_class (TREE_CODE (op)) == GIMPLE_SINGLE_RHS;
 }
 
-
-/* Return true if T is a GIMPLE RHS for an assignment to a temporary.  */
-
-bool
-is_gimple_formal_tmp_rhs (tree t)
-{
-  if (is_gimple_lvalue (t) || is_gimple_val (t))
-    return true;
-
-  return get_gimple_rhs_class (TREE_CODE (t)) != GIMPLE_INVALID_RHS;
-}
-
 /* Returns true iff T is a valid RHS for an assignment to a renamed
    user -- or front-end generated artificial -- variable.  */
 
 bool
 is_gimple_reg_rhs (tree t)
 {
-  /* If the RHS of the MODIFY_EXPR may throw or make a nonlocal goto
-     and the LHS is a user variable, then we need to introduce a formal
-     temporary.  This way the optimizers can determine that the user
-     variable is only modified if evaluation of the RHS does not throw.
-
-     Don't force a temp of a non-renamable type; the copy could be
-     arbitrarily expensive.  Instead we will generate a VDEF for
-     the assignment.  */
-
-  if (is_gimple_reg_type (TREE_TYPE (t)) && tree_could_throw_p (t))
-    return false;
-
-  return is_gimple_formal_tmp_rhs (t);
+  return get_gimple_rhs_class (TREE_CODE (t)) != GIMPLE_INVALID_RHS;
 }
 
 /* Returns true iff T is a valid RHS for an assignment to an un-renamed
@@ -2630,7 +2534,7 @@ is_gimple_mem_rhs (tree t)
   if (is_gimple_reg_type (TREE_TYPE (t)))
     return is_gimple_val (t);
   else
-    return is_gimple_formal_tmp_rhs (t);
+    return is_gimple_val (t) || is_gimple_lvalue (t);
 }
 
 /*  Return true if T is a valid LHS for a GIMPLE assignment expression.  */
@@ -2730,17 +2634,12 @@ is_gimple_address (const_tree t)
     }
 }
 
-/* Return true if T is a gimple invariant address.  */
+/* Strip out all handled components that produce invariant
+   offsets.  */
 
-bool
-is_gimple_invariant_address (const_tree t)
+static const_tree
+strip_invariant_refs (const_tree op)
 {
-  tree op;
-
-  if (TREE_CODE (t) != ADDR_EXPR)
-    return false;
-
-  op = TREE_OPERAND (t, 0);
   while (handled_component_p (op))
     {
       switch (TREE_CODE (op))
@@ -2750,12 +2649,12 @@ is_gimple_invariant_address (const_tree t)
          if (!is_gimple_constant (TREE_OPERAND (op, 1))
              || TREE_OPERAND (op, 2) != NULL_TREE
              || TREE_OPERAND (op, 3) != NULL_TREE)
-           return false;
+           return NULL;
          break;
 
        case COMPONENT_REF:
          if (TREE_OPERAND (op, 2) != NULL_TREE)
-           return false;
+           return NULL;
          break;
 
        default:;
@@ -2763,7 +2662,38 @@ is_gimple_invariant_address (const_tree t)
       op = TREE_OPERAND (op, 0);
     }
 
-  return CONSTANT_CLASS_P (op) || decl_address_invariant_p (op);
+  return op;
+}
+
+/* Return true if T is a gimple invariant address.  */
+
+bool
+is_gimple_invariant_address (const_tree t)
+{
+  const_tree op;
+
+  if (TREE_CODE (t) != ADDR_EXPR)
+    return false;
+
+  op = strip_invariant_refs (TREE_OPERAND (t, 0));
+
+  return op && (CONSTANT_CLASS_P (op) || decl_address_invariant_p (op));
+}
+
+/* Return true if T is a gimple invariant address at IPA level
+   (so addresses of variables on stack are not allowed).  */
+
+bool
+is_gimple_ip_invariant_address (const_tree t)
+{
+  const_tree op;
+
+  if (TREE_CODE (t) != ADDR_EXPR)
+    return false;
+
+  op = strip_invariant_refs (TREE_OPERAND (t, 0));
+
+  return op && (CONSTANT_CLASS_P (op) || decl_address_ip_invariant_p (op));
 }
 
 /* Return true if T is a GIMPLE minimal invariant.  It's a restricted
@@ -2778,6 +2708,18 @@ is_gimple_min_invariant (const_tree t)
   return is_gimple_constant (t);
 }
 
+/* Return true if T is a GIMPLE interprocedural invariant.  It's a restricted
+   form of gimple minimal invariant.  */
+
+bool
+is_gimple_ip_invariant (const_tree t)
+{
+  if (TREE_CODE (t) == ADDR_EXPR)
+    return is_gimple_ip_invariant_address (t);
+
+  return is_gimple_constant (t);
+}
+
 /* Return true if T looks like a valid GIMPLE statement.  */
 
 bool
@@ -2878,12 +2820,15 @@ is_gimple_reg (tree t)
   if (TREE_CODE (t) == SSA_NAME)
     t = SSA_NAME_VAR (t);
 
-  if (MTAG_P (t))
-    return false;
-
   if (!is_gimple_variable (t))
     return false;
 
+  /* Complex and vector values must have been put into SSA-like form.
+     That is, no assignments to the individual components.  */
+  if (TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE
+      || TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
+    return DECL_GIMPLE_REG_P (t);
+
   if (!is_gimple_reg_type (TREE_TYPE (t)))
     return false;
 
@@ -2910,45 +2855,10 @@ is_gimple_reg (tree t)
   if (TREE_CODE (t) == VAR_DECL && DECL_HARD_REGISTER (t))
     return false;
 
-  /* Complex and vector values must have been put into SSA-like form.
-     That is, no assignments to the individual components.  */
-  if (TREE_CODE (TREE_TYPE (t)) == COMPLEX_TYPE
-      || TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
-    return DECL_GIMPLE_REG_P (t);
-
   return true;
 }
 
 
-/* Returns true if T is a GIMPLE formal temporary variable.  */
-
-bool
-is_gimple_formal_tmp_var (tree t)
-{
-  if (TREE_CODE (t) == SSA_NAME)
-    return true;
-
-  return TREE_CODE (t) == VAR_DECL && DECL_GIMPLE_FORMAL_TEMP_P (t);
-}
-
-/* Returns true if T is a GIMPLE formal temporary register variable.  */
-
-bool
-is_gimple_formal_tmp_reg (tree t)
-{
-  /* The intent of this is to get hold of a value that won't change.
-     An SSA_NAME qualifies no matter if its of a user variable or not.  */
-  if (TREE_CODE (t) == SSA_NAME)
-    return true;
-
-  /* We don't know the lifetime characteristics of user variables.  */
-  if (!is_gimple_formal_tmp_var (t))
-    return false;
-
-  /* Finally, it must be capable of being placed in a register.  */
-  return is_gimple_reg (t);
-}
-
 /* Return true if T is a GIMPLE variable whose address is not needed.  */
 
 bool
@@ -2995,6 +2905,8 @@ is_gimple_asm_val (tree t)
 bool
 is_gimple_min_lval (tree t)
 {
+  if (!(t = CONST_CAST_TREE (strip_invariant_refs (t))))
+    return false;
   return (is_gimple_id (t) || TREE_CODE (t) == INDIRECT_REF);
 }
 
@@ -3097,8 +3009,11 @@ recalculate_side_effects (tree t)
        }
       break;
 
+    case tcc_constant:
+      /* No side-effects.  */
+      return;
+
     default:
-      /* Can never be used with non-expressions.  */
       gcc_unreachable ();
    }
 }
@@ -3142,4 +3057,362 @@ canonicalize_cond_expr_cond (tree t)
   return NULL_TREE;
 }
 
+/* Build a GIMPLE_CALL identical to STMT but skipping the arguments in
+   the positions marked by the set ARGS_TO_SKIP.  */
+
+gimple
+gimple_call_copy_skip_args (gimple stmt, bitmap args_to_skip)
+{
+  int i;
+  tree fn = gimple_call_fn (stmt);
+  int nargs = gimple_call_num_args (stmt);
+  VEC(tree, heap) *vargs = VEC_alloc (tree, heap, nargs);
+  gimple new_stmt;
+
+  for (i = 0; i < nargs; i++)
+    if (!bitmap_bit_p (args_to_skip, i))
+      VEC_quick_push (tree, vargs, gimple_call_arg (stmt, i));
+
+  new_stmt = gimple_build_call_vec (fn, vargs);
+  VEC_free (tree, heap, vargs);
+  if (gimple_call_lhs (stmt))
+    gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));
+
+  gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+  gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+
+  gimple_set_block (new_stmt, gimple_block (stmt));
+  if (gimple_has_location (stmt))
+    gimple_set_location (new_stmt, gimple_location (stmt));
+
+  /* Carry all the flags to the new GIMPLE_CALL.  */
+  gimple_call_set_chain (new_stmt, gimple_call_chain (stmt));
+  gimple_call_set_tail (new_stmt, gimple_call_tail_p (stmt));
+  gimple_call_set_cannot_inline (new_stmt, gimple_call_cannot_inline_p (stmt));
+  gimple_call_set_return_slot_opt (new_stmt, gimple_call_return_slot_opt_p (stmt));
+  gimple_call_set_from_thunk (new_stmt, gimple_call_from_thunk_p (stmt));
+  gimple_call_set_va_arg_pack (new_stmt, gimple_call_va_arg_pack_p (stmt));
+
+  gimple_set_modified (new_stmt, true);
+
+  return new_stmt;
+}
+
+
+/* Data structure used to count the number of dereferences to PTR
+   inside an expression.  */
+struct count_ptr_d
+{
+  tree ptr;
+  unsigned num_stores;
+  unsigned num_loads;
+};
+
+/* 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, void *data)
+{
+  struct walk_stmt_info *wi_p = (struct walk_stmt_info *) data;
+  struct count_ptr_d *count_p = (struct count_ptr_d *) wi_p->info;
+
+  /* Do not walk inside ADDR_EXPR nodes.  In the expression &ptr->fld,
+     pointer 'ptr' is *not* dereferenced, it is simply used to compute
+     the address of 'fld' as 'ptr + offsetof(fld)'.  */
+  if (TREE_CODE (*tp) == ADDR_EXPR)
+    {
+      *walk_subtrees = 0;
+      return NULL_TREE;
+    }
+
+  if (INDIRECT_REF_P (*tp) && TREE_OPERAND (*tp, 0) == count_p->ptr)
+    {
+      if (wi_p->is_lhs)
+       count_p->num_stores++;
+      else
+       count_p->num_loads++;
+    }
+
+  return NULL_TREE;
+}
+
+/* Count the number of direct and indirect uses for pointer PTR in
+   statement STMT.  The number of direct uses is stored in
+   *NUM_USES_P.  Indirect references are counted separately depending
+   on whether they are store or load operations.  The counts are
+   stored in *NUM_STORES_P and *NUM_LOADS_P.  */
+
+void
+count_uses_and_derefs (tree ptr, gimple stmt, unsigned *num_uses_p,
+                      unsigned *num_loads_p, unsigned *num_stores_p)
+{
+  ssa_op_iter i;
+  tree use;
+
+  *num_uses_p = 0;
+  *num_loads_p = 0;
+  *num_stores_p = 0;
+
+  /* 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 (is_gimple_assign (stmt)
+      || gimple_code (stmt) == GIMPLE_RETURN
+      || gimple_code (stmt) == GIMPLE_ASM
+      || is_gimple_call (stmt))
+    {
+      struct walk_stmt_info wi;
+      struct count_ptr_d count;
+
+      count.ptr = ptr;
+      count.num_stores = 0;
+      count.num_loads = 0;
+
+      memset (&wi, 0, sizeof (wi));
+      wi.info = &count;
+      walk_gimple_op (stmt, count_ptr_derefs, &wi);
+
+      *num_stores_p = count.num_stores;
+      *num_loads_p = count.num_loads;
+    }
+
+  gcc_assert (*num_uses_p >= *num_loads_p + *num_stores_p);
+}
+
+/* From a tree operand OP return the base of a load or store operation
+   or NULL_TREE if OP is not a load or a store.  */
+
+static tree
+get_base_loadstore (tree op)
+{
+  while (handled_component_p (op))
+    op = TREE_OPERAND (op, 0);
+  if (DECL_P (op)
+      || INDIRECT_REF_P (op)
+      || TREE_CODE (op) == TARGET_MEM_REF)
+    return op;
+  return NULL_TREE;
+}
+
+/* For the statement STMT call the callbacks VISIT_LOAD, VISIT_STORE and
+   VISIT_ADDR if non-NULL on loads, store and address-taken operands
+   passing the STMT, the base of the operand and DATA to it.  The base
+   will be either a decl, an indirect reference (including TARGET_MEM_REF)
+   or the argument of an address expression.
+   Returns the results of these callbacks or'ed.  */
+
+bool
+walk_stmt_load_store_addr_ops (gimple stmt, void *data,
+                              bool (*visit_load)(gimple, tree, void *),
+                              bool (*visit_store)(gimple, tree, void *),
+                              bool (*visit_addr)(gimple, tree, void *))
+{
+  bool ret = false;
+  unsigned i;
+  if (gimple_assign_single_p (stmt))
+    {
+      tree lhs, rhs;
+      if (visit_store)
+       {
+         lhs = get_base_loadstore (gimple_assign_lhs (stmt));
+         if (lhs)
+           ret |= visit_store (stmt, lhs, data);
+       }
+      rhs = gimple_assign_rhs1 (stmt);
+      if (visit_addr)
+       {
+         if (TREE_CODE (rhs) == ADDR_EXPR)
+           ret |= visit_addr (stmt, TREE_OPERAND (rhs, 0), data);
+         else if (TREE_CODE (rhs) == TARGET_MEM_REF
+                  && TREE_CODE (TMR_BASE (rhs)) == ADDR_EXPR)
+           ret |= visit_addr (stmt, TREE_OPERAND (TMR_BASE (rhs), 0), data);
+         else if (TREE_CODE (rhs) == OBJ_TYPE_REF
+                  && TREE_CODE (OBJ_TYPE_REF_OBJECT (rhs)) == ADDR_EXPR)
+           ret |= visit_addr (stmt, TREE_OPERAND (OBJ_TYPE_REF_OBJECT (rhs),
+                                                  0), data);
+       }
+      if (visit_load)
+       {
+         rhs = get_base_loadstore (rhs);
+         if (rhs)
+           ret |= visit_load (stmt, rhs, data);
+       }
+    }
+  else if (visit_addr
+          && (is_gimple_assign (stmt)
+              || gimple_code (stmt) == GIMPLE_COND
+              || gimple_code (stmt) == GIMPLE_CHANGE_DYNAMIC_TYPE))
+    {
+      for (i = 0; i < gimple_num_ops (stmt); ++i)
+       if (gimple_op (stmt, i)
+           && TREE_CODE (gimple_op (stmt, i)) == ADDR_EXPR)
+         ret |= visit_addr (stmt, TREE_OPERAND (gimple_op (stmt, i), 0), data);
+    }
+  else if (is_gimple_call (stmt))
+    {
+      if (visit_store)
+       {
+         tree lhs = gimple_call_lhs (stmt);
+         if (lhs)
+           {
+             lhs = get_base_loadstore (lhs);
+             if (lhs)
+               ret |= visit_store (stmt, lhs, data);
+           }
+       }
+      if (visit_load || visit_addr)
+       for (i = 0; i < gimple_call_num_args (stmt); ++i)
+         {
+           tree rhs = gimple_call_arg (stmt, i);
+           if (visit_addr
+               && TREE_CODE (rhs) == ADDR_EXPR)
+             ret |= visit_addr (stmt, TREE_OPERAND (rhs, 0), data);
+           else if (visit_load)
+             {
+               rhs = get_base_loadstore (rhs);
+               if (rhs)
+                 ret |= visit_load (stmt, rhs, data);
+             }
+         }
+      if (visit_addr
+         && gimple_call_chain (stmt)
+         && TREE_CODE (gimple_call_chain (stmt)) == ADDR_EXPR)
+       ret |= visit_addr (stmt, TREE_OPERAND (gimple_call_chain (stmt), 0),
+                          data);
+    }
+  else if (gimple_code (stmt) == GIMPLE_ASM)
+    {
+      unsigned noutputs;
+      const char *constraint;
+      const char **oconstraints;
+      bool allows_mem, allows_reg, is_inout;
+      noutputs = gimple_asm_noutputs (stmt);
+      oconstraints = XALLOCAVEC (const char *, noutputs);
+      if (visit_store || visit_addr)
+       for (i = 0; i < gimple_asm_noutputs (stmt); ++i)
+         {
+           tree link = gimple_asm_output_op (stmt, i);
+           tree op = get_base_loadstore (TREE_VALUE (link));
+           if (op && visit_store)
+             ret |= visit_store (stmt, op, data);
+           if (visit_addr)
+             {
+               constraint = TREE_STRING_POINTER
+                   (TREE_VALUE (TREE_PURPOSE (link)));
+               oconstraints[i] = constraint;
+               parse_output_constraint (&constraint, i, 0, 0, &allows_mem,
+                                        &allows_reg, &is_inout);
+               if (op && !allows_reg && allows_mem)
+                 ret |= visit_addr (stmt, op, data);
+             }
+         }
+      if (visit_load || visit_addr)
+       for (i = 0; i < gimple_asm_ninputs (stmt); ++i)
+         {
+           tree link = gimple_asm_input_op (stmt, i);
+           tree op = TREE_VALUE (link);
+           if (visit_addr
+               && TREE_CODE (op) == ADDR_EXPR)
+             ret |= visit_addr (stmt, TREE_OPERAND (op, 0), data);
+           else if (visit_load || visit_addr)
+             {
+               op = get_base_loadstore (op);
+               if (op)
+                 {
+                   if (visit_load)
+                     ret |= visit_load (stmt, op, data);
+                   if (visit_addr)
+                     {
+                       constraint = TREE_STRING_POINTER
+                           (TREE_VALUE (TREE_PURPOSE (link)));
+                       parse_input_constraint (&constraint, 0, 0, noutputs,
+                                               0, oconstraints,
+                                               &allows_mem, &allows_reg);
+                       if (!allows_reg && allows_mem)
+                         ret |= visit_addr (stmt, op, data);
+                     }
+                 }
+             }
+         }
+    }
+  else if (gimple_code (stmt) == GIMPLE_RETURN)
+    {
+      tree op = gimple_return_retval (stmt);
+      if (op)
+       {
+         if (visit_addr
+             && TREE_CODE (op) == ADDR_EXPR)
+           ret |= visit_addr (stmt, TREE_OPERAND (op, 0), data);
+         else if (visit_load)
+           {
+             op = get_base_loadstore (op);
+             if (op)
+               ret |= visit_load (stmt, op, data);
+           }
+       }
+    }
+  else if (visit_addr
+          && gimple_code (stmt) == GIMPLE_PHI)
+    {
+      for (i = 0; i < gimple_phi_num_args (stmt); ++i)
+       {
+         tree op = PHI_ARG_DEF (stmt, i);
+         if (TREE_CODE (op) == ADDR_EXPR)
+           ret |= visit_addr (stmt, TREE_OPERAND (op, 0), data);
+       }
+    }
+
+  return ret;
+}
+
+/* Like walk_stmt_load_store_addr_ops but with NULL visit_addr.  IPA-CP
+   should make a faster clone for this case.  */
+
+bool
+walk_stmt_load_store_ops (gimple stmt, void *data,
+                         bool (*visit_load)(gimple, tree, void *),
+                         bool (*visit_store)(gimple, tree, void *))
+{
+  return walk_stmt_load_store_addr_ops (stmt, data,
+                                       visit_load, visit_store, NULL);
+}
+
+/* Helper for gimple_ior_addresses_taken_1.  */
+
+static bool
+gimple_ior_addresses_taken_1 (gimple stmt ATTRIBUTE_UNUSED,
+                             tree addr, void *data)
+{
+  bitmap addresses_taken = (bitmap)data;
+  while (handled_component_p (addr))
+    addr = TREE_OPERAND (addr, 0);
+  if (DECL_P (addr))
+    {
+      bitmap_set_bit (addresses_taken, DECL_UID (addr));
+      return true;
+    }
+  return false;
+}
+
+/* Set the bit for the uid of all decls that have their address taken
+   in STMT in the ADDRESSES_TAKEN bitmap.  Returns true if there
+   were any in this stmt.  */
+
+bool
+gimple_ior_addresses_taken (bitmap addresses_taken, gimple stmt)
+{
+  return walk_stmt_load_store_addr_ops (stmt, addresses_taken, NULL, NULL,
+                                       gimple_ior_addresses_taken_1);
+}
+
 #include "gt-gimple.h"