OSDN Git Service

PR middle-end/51516
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-address.c
index 474a798..cf13157 100644 (file)
@@ -1,5 +1,5 @@
 /* Memory address lowering and addressing mode selection.
-   Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010
+   Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2011
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -129,7 +129,7 @@ gen_addr_rtx (enum machine_mode address_mode,
       *addr = act_elem;
     }
 
-  if (base)
+  if (base && base != const0_rtx)
     {
       if (*addr)
        *addr = simplify_gen_binary (PLUS, address_mode, base, *addr);
@@ -189,16 +189,20 @@ addr_for_mem_ref (struct mem_address *addr, addr_space_t as,
                  bool really_expand)
 {
   enum machine_mode address_mode = targetm.addr_space.address_mode (as);
+  enum machine_mode pointer_mode = targetm.addr_space.pointer_mode (as);
   rtx address, sym, bse, idx, st, off;
   struct mem_addr_template *templ;
 
   if (addr->step && !integer_onep (addr->step))
-    st = immed_double_int_const (tree_to_double_int (addr->step), address_mode);
+    st = immed_double_int_const (tree_to_double_int (addr->step), pointer_mode);
   else
     st = NULL_RTX;
 
   if (addr->offset && !integer_zerop (addr->offset))
-    off = immed_double_int_const (tree_to_double_int (addr->offset), address_mode);
+    off = immed_double_int_const
+           (double_int_sext (tree_to_double_int (addr->offset),
+                             TYPE_PRECISION (TREE_TYPE (addr->offset))),
+            pointer_mode);
   else
     off = NULL_RTX;
 
@@ -217,16 +221,16 @@ addr_for_mem_ref (struct mem_address *addr, addr_space_t as,
       if (!templ->ref)
        {
          sym = (addr->symbol ?
-                gen_rtx_SYMBOL_REF (address_mode, ggc_strdup ("test_symbol"))
+                gen_rtx_SYMBOL_REF (pointer_mode, ggc_strdup ("test_symbol"))
                 : NULL_RTX);
          bse = (addr->base ?
-                gen_raw_REG (address_mode, LAST_VIRTUAL_REGISTER + 1)
+                gen_raw_REG (pointer_mode, LAST_VIRTUAL_REGISTER + 1)
                 : NULL_RTX);
          idx = (addr->index ?
-                gen_raw_REG (address_mode, LAST_VIRTUAL_REGISTER + 2)
+                gen_raw_REG (pointer_mode, LAST_VIRTUAL_REGISTER + 2)
                 : NULL_RTX);
 
-         gen_addr_rtx (address_mode, sym, bse, idx,
+         gen_addr_rtx (pointer_mode, sym, bse, idx,
                        st? const0_rtx : NULL_RTX,
                        off? const0_rtx : NULL_RTX,
                        &templ->ref,
@@ -244,17 +248,18 @@ addr_for_mem_ref (struct mem_address *addr, addr_space_t as,
 
   /* Otherwise really expand the expressions.  */
   sym = (addr->symbol
-        ? expand_expr (build_addr (addr->symbol, current_function_decl),
-                       NULL_RTX, address_mode, EXPAND_NORMAL)
+        ? expand_expr (addr->symbol, NULL_RTX, pointer_mode, EXPAND_NORMAL)
         : NULL_RTX);
   bse = (addr->base
-        ? expand_expr (addr->base, NULL_RTX, address_mode, EXPAND_NORMAL)
+        ? expand_expr (addr->base, NULL_RTX, pointer_mode, EXPAND_NORMAL)
         : NULL_RTX);
   idx = (addr->index
-        ? expand_expr (addr->index, NULL_RTX, address_mode, EXPAND_NORMAL)
+        ? expand_expr (addr->index, NULL_RTX, pointer_mode, EXPAND_NORMAL)
         : NULL_RTX);
 
-  gen_addr_rtx (address_mode, sym, bse, idx, st, off, &address, NULL, NULL);
+  gen_addr_rtx (pointer_mode, sym, bse, idx, st, off, &address, NULL, NULL);
+  if (pointer_mode != address_mode)
+    address = convert_memory_address (address_mode, address);
   return address;
 }
 
@@ -266,30 +271,25 @@ tree_mem_ref_addr (tree type, tree mem_ref)
   tree addr;
   tree act_elem;
   tree step = TMR_STEP (mem_ref), offset = TMR_OFFSET (mem_ref);
-  tree sym = TMR_SYMBOL (mem_ref), base = TMR_BASE (mem_ref);
   tree addr_base = NULL_TREE, addr_off = NULL_TREE;
 
-  if (sym)
-    addr_base = fold_convert (type, build_addr (sym, current_function_decl));
-  else if (base && POINTER_TYPE_P (TREE_TYPE (base)))
-    {
-      addr_base = fold_convert (type, base);
-      base = NULL_TREE;
-    }
+  addr_base = fold_convert (type, TMR_BASE (mem_ref));
 
   act_elem = TMR_INDEX (mem_ref);
   if (act_elem)
     {
       if (step)
-       act_elem = fold_build2 (MULT_EXPR, sizetype, act_elem, step);
+       act_elem = fold_build2 (MULT_EXPR, TREE_TYPE (act_elem),
+                               act_elem, step);
       addr_off = act_elem;
     }
 
-  act_elem = base;
+  act_elem = TMR_INDEX2 (mem_ref);
   if (act_elem)
     {
       if (addr_off)
-       addr_off = fold_build2 (PLUS_EXPR, sizetype, addr_off, act_elem);
+       addr_off = fold_build2 (PLUS_EXPR, TREE_TYPE (addr_off),
+                               addr_off, act_elem);
       else
        addr_off = act_elem;
     }
@@ -297,22 +297,16 @@ tree_mem_ref_addr (tree type, tree mem_ref)
   if (offset && !integer_zerop (offset))
     {
       if (addr_off)
-       addr_off = fold_build2 (PLUS_EXPR, sizetype, addr_off, offset);
+       addr_off = fold_build2 (PLUS_EXPR, TREE_TYPE (addr_off), addr_off,
+                               fold_convert (TREE_TYPE (addr_off), offset));
       else
        addr_off = offset;
     }
 
   if (addr_off)
-    {
-      if (addr_base)
-       addr = fold_build2 (POINTER_PLUS_EXPR, type, addr_base, addr_off);
-      else
-       addr = fold_convert (type, addr_off);
-    }
-  else if (addr_base)
-    addr = addr_base;
+    addr = fold_build_pointer_plus (addr_base, addr_off);
   else
-    addr = build_int_cst (type, 0);
+    addr = addr_base;
 
   return addr;
 }
@@ -335,42 +329,54 @@ valid_mem_ref_p (enum machine_mode mode, addr_space_t as,
 
 /* Checks whether a TARGET_MEM_REF with type TYPE and parameters given by ADDR
    is valid on the current target and if so, creates and returns the
-   TARGET_MEM_REF.  */
+   TARGET_MEM_REF.  If VERIFY is false omit the verification step.  */
 
 static tree
-create_mem_ref_raw (tree type, tree alias_ptr_type, struct mem_address *addr)
+create_mem_ref_raw (tree type, tree alias_ptr_type, struct mem_address *addr,
+                   bool verify)
 {
-  if (!valid_mem_ref_p (TYPE_MODE (type), TYPE_ADDR_SPACE (type), addr))
+  tree base, index2;
+
+  if (verify
+      && !valid_mem_ref_p (TYPE_MODE (type), TYPE_ADDR_SPACE (type), addr))
     return NULL_TREE;
 
   if (addr->step && integer_onep (addr->step))
     addr->step = NULL_TREE;
 
-  if (addr->offset && integer_zerop (addr->offset))
-    addr->offset = NULL_TREE;
+  if (addr->offset)
+    addr->offset = fold_convert (alias_ptr_type, addr->offset);
+  else
+    addr->offset = build_int_cst (alias_ptr_type, 0);
 
-  /* If possible use a plain MEM_REF instead of a TARGET_MEM_REF.  */
-  if (alias_ptr_type
-      && !addr->index
-      && !addr->step
-      && (!addr->base || POINTER_TYPE_P (TREE_TYPE (addr->base))))
+  if (addr->symbol)
     {
-      tree base, offset;
-      gcc_assert (!addr->symbol ^ !addr->base);
-      if (addr->symbol)
-       base = build_fold_addr_expr (addr->symbol);
-      else
-       base = addr->base;
-      if (addr->offset)
-       offset = fold_convert (alias_ptr_type, addr->offset);
-      else
-       offset = build_int_cst (alias_ptr_type, 0);
-      return fold_build2 (MEM_REF, type, base, offset);
+      base = addr->symbol;
+      index2 = addr->base;
+    }
+  else if (addr->base
+          && POINTER_TYPE_P (TREE_TYPE (addr->base)))
+    {
+      base = addr->base;
+      index2 = NULL_TREE;
+    }
+  else
+    {
+      base = build_int_cst (ptr_type_node, 0);
+      index2 = addr->base;
     }
 
-  return build6 (TARGET_MEM_REF, type,
-                addr->symbol, addr->base, addr->index,
-                addr->step, addr->offset, NULL);
+  /* If possible use a plain MEM_REF instead of a TARGET_MEM_REF.
+     ???  As IVOPTs does not follow restrictions to where the base
+     pointer may point to create a MEM_REF only if we know that
+     base is valid.  */
+  if ((TREE_CODE (base) == ADDR_EXPR || TREE_CODE (base) == INTEGER_CST)
+      && (!index2 || integer_zerop (index2))
+      && (!addr->index || integer_zerop (addr->index)))
+    return fold_build2 (MEM_REF, type, base, addr->offset);
+
+  return build5 (TARGET_MEM_REF, type,
+                base, addr->offset, addr->index, addr->step, index2);
 }
 
 /* Returns true if OBJ is an object whose address is a link time constant.  */
@@ -407,7 +413,7 @@ move_fixed_address_to_symbol (struct mem_address *parts, aff_tree *addr)
   if (i == addr->n)
     return;
 
-  parts->symbol = TREE_OPERAND (val, 0);
+  parts->symbol = val;
   aff_combination_remove_elt (addr, i);
 }
 
@@ -517,9 +523,7 @@ add_to_parts (struct mem_address *parts, tree elt)
   /* Add ELT to base.  */
   type = TREE_TYPE (parts->base);
   if (POINTER_TYPE_P (type))
-    parts->base = fold_build2 (POINTER_PLUS_EXPR, type,
-                              parts->base,
-                              fold_convert (sizetype, elt));
+    parts->base = fold_build_pointer_plus (parts->base, elt);
   else
     parts->base = fold_build2 (PLUS_EXPR, type,
                               parts->base, elt);
@@ -668,8 +672,8 @@ static void
 gimplify_mem_ref_parts (gimple_stmt_iterator *gsi, struct mem_address *parts)
 {
   if (parts->base)
-    parts->base = force_gimple_operand_gsi (gsi, parts->base,
-                                           true, NULL_TREE,
+    parts->base = force_gimple_operand_gsi_1 (gsi, parts->base,
+                                           is_gimple_mem_ref_addr, NULL_TREE,
                                            true, GSI_SAME_STMT);
   if (parts->index)
     parts->index = force_gimple_operand_gsi (gsi, parts->index,
@@ -688,12 +692,11 @@ create_mem_ref (gimple_stmt_iterator *gsi, tree type, aff_tree *addr,
                tree alias_ptr_type, tree iv_cand, tree base_hint, bool speed)
 {
   tree mem_ref, tmp;
-  tree atype;
   struct mem_address parts;
 
   addr_to_parts (type, addr, iv_cand, base_hint, &parts, speed);
   gimplify_mem_ref_parts (gsi, &parts);
-  mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts);
+  mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
   if (mem_ref)
     return mem_ref;
 
@@ -709,14 +712,14 @@ create_mem_ref (gimple_stmt_iterator *gsi, tree type, aff_tree *addr,
                                true, NULL_TREE, true, GSI_SAME_STMT);
       parts.step = NULL_TREE;
 
-      mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts);
+      mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
       if (mem_ref)
        return mem_ref;
     }
 
   if (parts.symbol)
     {
-      tmp = build_addr (parts.symbol, current_function_decl);
+      tmp = parts.symbol;
       gcc_assert (is_gimple_val (tmp));
 
       /* Add the symbol to base, eventually forcing it to register.  */
@@ -727,12 +730,9 @@ create_mem_ref (gimple_stmt_iterator *gsi, tree type, aff_tree *addr,
 
          if (parts.index)
            {
-             atype = TREE_TYPE (tmp);
-             parts.base = force_gimple_operand_gsi (gsi,
-                       fold_build2 (POINTER_PLUS_EXPR, atype,
-                                    tmp,
-                                    fold_convert (sizetype, parts.base)),
-                       true, NULL_TREE, true, GSI_SAME_STMT);
+             parts.base = force_gimple_operand_gsi_1 (gsi,
+                       fold_build_pointer_plus (tmp, parts.base),
+                       is_gimple_mem_ref_addr, NULL_TREE, true, GSI_SAME_STMT);
            }
          else
            {
@@ -744,7 +744,7 @@ create_mem_ref (gimple_stmt_iterator *gsi, tree type, aff_tree *addr,
        parts.base = tmp;
       parts.symbol = NULL_TREE;
 
-      mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts);
+      mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
       if (mem_ref)
        return mem_ref;
     }
@@ -754,18 +754,15 @@ create_mem_ref (gimple_stmt_iterator *gsi, tree type, aff_tree *addr,
       /* Add index to base.  */
       if (parts.base)
        {
-         atype = TREE_TYPE (parts.base);
-         parts.base = force_gimple_operand_gsi (gsi,
-                       fold_build2 (POINTER_PLUS_EXPR, atype,
-                                    parts.base,
-                                    parts.index),
-                       true, NULL_TREE, true, GSI_SAME_STMT);
+         parts.base = force_gimple_operand_gsi_1 (gsi,
+                       fold_build_pointer_plus (parts.base, parts.index),
+                       is_gimple_mem_ref_addr, NULL_TREE, true, GSI_SAME_STMT);
        }
       else
        parts.base = parts.index;
       parts.index = NULL_TREE;
 
-      mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts);
+      mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
       if (mem_ref)
        return mem_ref;
     }
@@ -775,19 +772,16 @@ create_mem_ref (gimple_stmt_iterator *gsi, tree type, aff_tree *addr,
       /* Try adding offset to base.  */
       if (parts.base)
        {
-         atype = TREE_TYPE (parts.base);
-         parts.base = force_gimple_operand_gsi (gsi,
-                       fold_build2 (POINTER_PLUS_EXPR, atype,
-                                    parts.base,
-                                    fold_convert (sizetype, parts.offset)),
-                       true, NULL_TREE, true, GSI_SAME_STMT);
+         parts.base = force_gimple_operand_gsi_1 (gsi,
+                       fold_build_pointer_plus (parts.base, parts.offset),
+                       is_gimple_mem_ref_addr, NULL_TREE, true, GSI_SAME_STMT);
        }
       else
        parts.base = parts.offset;
 
       parts.offset = NULL_TREE;
 
-      mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts);
+      mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
       if (mem_ref)
        return mem_ref;
     }
@@ -807,8 +801,22 @@ create_mem_ref (gimple_stmt_iterator *gsi, tree type, aff_tree *addr,
 void
 get_address_description (tree op, struct mem_address *addr)
 {
-  addr->symbol = TMR_SYMBOL (op);
-  addr->base = TMR_BASE (op);
+  if (TREE_CODE (TMR_BASE (op)) == ADDR_EXPR)
+    {
+      addr->symbol = TMR_BASE (op);
+      addr->base = TMR_INDEX2 (op);
+    }
+  else
+    {
+      addr->symbol = NULL_TREE;
+      if (TMR_INDEX2 (op))
+       {
+         gcc_assert (integer_zerop (TMR_BASE (op)));
+         addr->base = TMR_INDEX2 (op);
+       }
+      else
+       addr->base = TMR_BASE (op);
+    }
   addr->index = TMR_INDEX (op);
   addr->step = TMR_STEP (op);
   addr->offset = TMR_OFFSET (op);
@@ -820,11 +828,72 @@ void
 copy_mem_ref_info (tree to, tree from)
 {
   /* And the info about the original reference.  */
-  TMR_ORIGINAL (to) = TMR_ORIGINAL (from);
   TREE_SIDE_EFFECTS (to) = TREE_SIDE_EFFECTS (from);
   TREE_THIS_VOLATILE (to) = TREE_THIS_VOLATILE (from);
 }
 
+/* Copies the reference information from OLD_REF to NEW_REF, where
+   NEW_REF should be either a MEM_REF or a TARGET_MEM_REF.  */
+
+void
+copy_ref_info (tree new_ref, tree old_ref)
+{
+  tree new_ptr_base = NULL_TREE;
+
+  gcc_assert (TREE_CODE (new_ref) == MEM_REF
+             || TREE_CODE (new_ref) == TARGET_MEM_REF);
+
+  TREE_SIDE_EFFECTS (new_ref) = TREE_SIDE_EFFECTS (old_ref);
+  TREE_THIS_VOLATILE (new_ref) = TREE_THIS_VOLATILE (old_ref);
+
+  new_ptr_base = TREE_OPERAND (new_ref, 0);
+
+  /* We can transfer points-to information from an old pointer
+     or decl base to the new one.  */
+  if (new_ptr_base
+      && TREE_CODE (new_ptr_base) == SSA_NAME
+      && !SSA_NAME_PTR_INFO (new_ptr_base))
+    {
+      tree base = get_base_address (old_ref);
+      if (!base)
+       ;
+      else if ((TREE_CODE (base) == MEM_REF
+               || TREE_CODE (base) == TARGET_MEM_REF)
+              && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME
+              && SSA_NAME_PTR_INFO (TREE_OPERAND (base, 0)))
+       {
+         struct ptr_info_def *new_pi;
+         duplicate_ssa_name_ptr_info
+           (new_ptr_base, SSA_NAME_PTR_INFO (TREE_OPERAND (base, 0)));
+         new_pi = SSA_NAME_PTR_INFO (new_ptr_base);
+         /* We have to be careful about transfering alignment information.  */
+         if (TREE_CODE (old_ref) == MEM_REF
+             && !(TREE_CODE (new_ref) == TARGET_MEM_REF
+                  && (TMR_INDEX2 (new_ref)
+                      || (TMR_STEP (new_ref)
+                          && (TREE_INT_CST_LOW (TMR_STEP (new_ref))
+                              < new_pi->align)))))
+           {
+             new_pi->misalign += double_int_sub (mem_ref_offset (old_ref),
+                                                 mem_ref_offset (new_ref)).low;
+             new_pi->misalign &= (new_pi->align - 1);
+           }
+         else
+           {
+             new_pi->align = 1;
+             new_pi->misalign = 0;
+           }
+       }
+      else if (TREE_CODE (base) == VAR_DECL
+              || TREE_CODE (base) == PARM_DECL
+              || TREE_CODE (base) == RESULT_DECL)
+       {
+         struct ptr_info_def *pi = get_ptr_info (new_ptr_base);
+         pt_solution_set_var (&pi->pt, base);
+       }
+    }
+}
+
 /* Move constants in target_mem_ref REF to offset.  Returns the new target
    mem ref if anything changes, NULL_TREE otherwise.  */
 
@@ -837,19 +906,39 @@ maybe_fold_tmr (tree ref)
 
   get_address_description (ref, &addr);
 
-  if (addr.base && TREE_CODE (addr.base) == INTEGER_CST)
+  if (addr.base
+      && TREE_CODE (addr.base) == INTEGER_CST
+      && !integer_zerop (addr.base))
     {
-      if (addr.offset)
-       addr.offset = fold_binary_to_constant (PLUS_EXPR, sizetype,
-                       addr.offset,
-                       fold_convert (sizetype, addr.base));
-      else
-       addr.offset = addr.base;
-
+      addr.offset = fold_binary_to_constant (PLUS_EXPR,
+                                            TREE_TYPE (addr.offset),
+                                            addr.offset, addr.base);
       addr.base = NULL_TREE;
       changed = true;
     }
 
+  if (addr.symbol
+      && TREE_CODE (TREE_OPERAND (addr.symbol, 0)) == MEM_REF)
+    {
+      addr.offset = fold_binary_to_constant
+                       (PLUS_EXPR, TREE_TYPE (addr.offset),
+                        addr.offset,
+                        TREE_OPERAND (TREE_OPERAND (addr.symbol, 0), 1));
+      addr.symbol = TREE_OPERAND (TREE_OPERAND (addr.symbol, 0), 0);
+      changed = true;
+    }
+  else if (addr.symbol
+          && handled_component_p (TREE_OPERAND (addr.symbol, 0)))
+    {
+      HOST_WIDE_INT offset;
+      addr.symbol = build_fold_addr_expr
+                     (get_addr_base_and_unit_offset
+                        (TREE_OPERAND (addr.symbol, 0), &offset));
+      addr.offset = int_const_binop (PLUS_EXPR,
+                                    addr.offset, size_int (offset));
+      changed = true;
+    }
+
   if (addr.index && TREE_CODE (addr.index) == INTEGER_CST)
     {
       off = addr.index;
@@ -860,14 +949,9 @@ maybe_fold_tmr (tree ref)
          addr.step = NULL_TREE;
        }
 
-      if (addr.offset)
-       {
-         addr.offset = fold_binary_to_constant (PLUS_EXPR, sizetype,
-                                                addr.offset, off);
-       }
-      else
-       addr.offset = off;
-
+      addr.offset = fold_binary_to_constant (PLUS_EXPR,
+                                            TREE_TYPE (addr.offset),
+                                            addr.offset, off);
       addr.index = NULL_TREE;
       changed = true;
     }
@@ -875,10 +959,12 @@ maybe_fold_tmr (tree ref)
   if (!changed)
     return NULL_TREE;
 
-  ret = create_mem_ref_raw (TREE_TYPE (ref), NULL_TREE, &addr);
-  if (!ret)
-    return NULL_TREE;
-
+  /* If we have propagated something into this TARGET_MEM_REF and thus
+     ended up folding it, always create a new TARGET_MEM_REF regardless
+     if it is valid in this for on the target - the propagation result
+     wouldn't be anyway.  */
+  ret = create_mem_ref_raw (TREE_TYPE (ref),
+                           TREE_TYPE (addr.offset), &addr, false);
   copy_mem_ref_info (ret, ref);
   return ret;
 }
@@ -892,7 +978,7 @@ dump_mem_address (FILE *file, struct mem_address *parts)
   if (parts->symbol)
     {
       fprintf (file, "symbol: ");
-      print_generic_expr (file, parts->symbol, TDF_SLIM);
+      print_generic_expr (file, TREE_OPERAND (parts->symbol, 0), TDF_SLIM);
       fprintf (file, "\n");
     }
   if (parts->base)