Patchwork Fix PR48571, remove (most) array-ref re-construction

login
register
mail settings
Submitter Richard Guenther
Date Aug. 30, 2011, 11:59 a.m.
Message ID <alpine.LNX.2.00.1108301349360.2130@zhemvz.fhfr.qr>
Download mbox | patch
Permalink /patch/112265/
State New
Headers show

Comments

Richard Guenther - Aug. 30, 2011, 11:59 a.m.
I've run into PR48571 again, and after having fun with out data-dep
analysis code I indeed believe that we should not try to re-construct
ARRAY_REFs from code involving pointer arithmetic.  ARRAY_REFs
have constraints imposed on them that are relied on by data dependence
analysis and that are indeed useful to have (the index is within
bounds, otherwise undefined behavior).  We can't recover from
lowering that happened, much as Zdenek argued when I initially
proposed to lower all ARRAY_REFs to pointer arithmetic with the
original MEM_REF design.

Thus, the following completes the partial removal of reference
re-constructing I did when merging MEM_REFs (I removed the code
re-constructing COMPONENT_REFs back then).

Not much fallout, much to my surprise (I've tried it initially
on the MEM_REF branch, but quickly gave up on this additional task).
We've appearantly become better in handling of pointer based accesses
(we've already much experience here with GFortran lowering 
multi-dimensional array accesses to one-dimensional ones).

The patch XFAILs one -Warray-bounds testcase, the -Warray-bounds
infrastructure needs some serious TLC which I didn't want to fold
into this patch.  The testcase in question also really asks for
an 'access outside of object' warning, similar to the
'offset outside bounds of constant string' one we have.

Bootstrapped and tested an earlier version on x86_64-unknown-linux-gnu,
a final bootstrap and regtest cycle is currently running.

(yes, there are still 
tree-ssa-forwprop.c:forward_propagate_addr_into_variable_array_index and
fold-const.c:try_move_mult_to_index)

Richard.

2011-08-30  Richard Guenther  <rguenther@suse.de>

	PR middle-end/48571
	* gimple.h (maybe_fold_offset_to_address): Remove.
	(maybe_fold_offset_to_reference): Likewise.
	(maybe_fold_stmt_addition): Likewise.
	(may_propagate_address_into_dereference): Likewise.
	* tree-inline.c (remap_gimple_op_r): Do not reconstruct
	array references.
	* gimple-fold.c (canonicalize_constructor_val): Likewise.
	Canonicalize invariant POINTER_PLUS_EXPRs to invariant MEM_REF
	addresses instead.
	(may_propagate_address_into_dereference): Remove.
	(maybe_fold_offset_to_array_ref): Likewise.
	(maybe_fold_offset_to_reference): Likewise.
	(maybe_fold_offset_to_address): Likewise.
	(maybe_fold_stmt_addition): Likewise.
	(fold_gimple_assign): Do not reconstruct array references but
	instead canonicalize invariant POINTER_PLUS_EXPRs to invariant
	MEM_REF addresses.
	(gimple_fold_stmt_to_constant_1): Likewise.
	* tree-ssa-forwprop.c (forward_propagate_addr_expr_1): Likewise.
	* gimplify.c (gimplify_conversion): Likewise.
	(gimplify_expr): Likewise.

	* gcc.c-torture/execute/pr48571-1.c: New testcase.
	* gcc.dg/pr36902.c: XFAIL.

Index: gcc/tree-inline.c
===================================================================
*** gcc/tree-inline.c.orig	2011-08-30 13:01:16.000000000 +0200
--- gcc/tree-inline.c	2011-08-30 13:05:45.000000000 +0200
*************** remap_gimple_op_r (tree *tp, int *walk_s
*** 853,895 ****
  	  tree ptr = TREE_OPERAND (*tp, 0);
  	  tree type = remap_type (TREE_TYPE (*tp), id);
  	  tree old = *tp;
- 	  tree tem;
  
  	  /* We need to re-canonicalize MEM_REFs from inline substitutions
  	     that can happen when a pointer argument is an ADDR_EXPR.
  	     Recurse here manually to allow that.  */
  	  walk_tree (&ptr, remap_gimple_op_r, data, NULL);
! 	  if ((tem = maybe_fold_offset_to_reference (EXPR_LOCATION (*tp),
! 						     ptr,
! 						     TREE_OPERAND (*tp, 1),
! 						     type))
! 	      && TREE_THIS_VOLATILE (tem) == TREE_THIS_VOLATILE (old))
! 	    {
! 	      tree *tem_basep = &tem;
! 	      while (handled_component_p (*tem_basep))
! 		tem_basep = &TREE_OPERAND (*tem_basep, 0);
! 	      if (TREE_CODE (*tem_basep) == MEM_REF)
! 		*tem_basep
! 		    = build2 (MEM_REF, TREE_TYPE (*tem_basep),
! 			      TREE_OPERAND (*tem_basep, 0),
! 			      fold_convert (TREE_TYPE (TREE_OPERAND (*tp, 1)),
! 					    TREE_OPERAND (*tem_basep, 1)));
! 	      else
! 		*tem_basep
! 		    = build2 (MEM_REF, TREE_TYPE (*tem_basep),
! 			      build_fold_addr_expr (*tem_basep),
! 			      build_int_cst
! 			      (TREE_TYPE (TREE_OPERAND (*tp, 1)), 0));
! 	      *tp = tem;
! 	      TREE_THIS_VOLATILE (*tem_basep) = TREE_THIS_VOLATILE (old);
! 	      TREE_THIS_NOTRAP (*tem_basep) = TREE_THIS_NOTRAP (old);
! 	    }
! 	  else
! 	    {
! 	      *tp = fold_build2 (MEM_REF, type,
! 				 ptr, TREE_OPERAND (*tp, 1));
! 	      TREE_THIS_NOTRAP (*tp) = TREE_THIS_NOTRAP (old);
! 	    }
  	  TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old);
  	  TREE_NO_WARNING (*tp) = TREE_NO_WARNING (old);
  	  *walk_subtrees = 0;
--- 853,866 ----
  	  tree ptr = TREE_OPERAND (*tp, 0);
  	  tree type = remap_type (TREE_TYPE (*tp), id);
  	  tree old = *tp;
  
  	  /* We need to re-canonicalize MEM_REFs from inline substitutions
  	     that can happen when a pointer argument is an ADDR_EXPR.
  	     Recurse here manually to allow that.  */
  	  walk_tree (&ptr, remap_gimple_op_r, data, NULL);
! 	  *tp = fold_build2 (MEM_REF, type,
! 			     ptr, TREE_OPERAND (*tp, 1));
! 	  TREE_THIS_NOTRAP (*tp) = TREE_THIS_NOTRAP (old);
  	  TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old);
  	  TREE_NO_WARNING (*tp) = TREE_NO_WARNING (old);
  	  *walk_subtrees = 0;
Index: gcc/testsuite/gcc.dg/pr36902.c
===================================================================
*** gcc/testsuite/gcc.dg/pr36902.c.orig	2011-08-30 13:01:16.000000000 +0200
--- gcc/testsuite/gcc.dg/pr36902.c	2011-08-30 13:05:45.000000000 +0200
*************** foo2(unsigned char * to, const unsigned
*** 44,50 ****
        *to = *from;
        break;
      case 5:
!       to[4] = from [4]; /* { dg-warning "array subscript is above array bounds" } */
        break;
      }
    return to;
--- 44,50 ----
        *to = *from;
        break;
      case 5:
!       to[4] = from [4]; /* { dg-warning "array subscript is above array bounds" "" { xfail *-*-* } } */
        break;
      }
    return to;
Index: gcc/gimple-fold.c
===================================================================
*** gcc/gimple-fold.c.orig	2011-08-30 13:01:16.000000000 +0200
--- gcc/gimple-fold.c	2011-08-30 13:05:45.000000000 +0200
*************** tree
*** 116,129 ****
  canonicalize_constructor_val (tree cval)
  {
    STRIP_NOPS (cval);
!   if (TREE_CODE (cval) == POINTER_PLUS_EXPR)
      {
!       tree t = maybe_fold_offset_to_address (EXPR_LOCATION (cval),
! 					     TREE_OPERAND (cval, 0),
! 					     TREE_OPERAND (cval, 1),
! 					     TREE_TYPE (cval));
!       if (t)
! 	cval = t;
      }
    if (TREE_CODE (cval) == ADDR_EXPR)
      {
--- 116,132 ----
  canonicalize_constructor_val (tree cval)
  {
    STRIP_NOPS (cval);
!   if (TREE_CODE (cval) == POINTER_PLUS_EXPR
!       && TREE_CODE (TREE_OPERAND (cval, 1)) == INTEGER_CST)
      {
!       tree ptr = TREE_OPERAND (cval, 0);
!       if (is_gimple_min_invariant (ptr))
! 	cval = build1_loc (EXPR_LOCATION (cval),
! 			   ADDR_EXPR, TREE_TYPE (ptr),
! 			   fold_build2 (MEM_REF, TREE_TYPE (TREE_TYPE (ptr)),
! 					ptr,
! 					fold_convert (ptr_type_node,
! 						      TREE_OPERAND (cval, 1))));
      }
    if (TREE_CODE (cval) == ADDR_EXPR)
      {
*************** get_symbol_constant_value (tree sym)
*** 173,556 ****
  }
  
  
- /* Return true if we may propagate the address expression ADDR into the
-    dereference DEREF and cancel them.  */
- 
- bool
- may_propagate_address_into_dereference (tree addr, tree deref)
- {
-   gcc_assert (TREE_CODE (deref) == MEM_REF
- 	      && TREE_CODE (addr) == ADDR_EXPR);
- 
-   /* Don't propagate if ADDR's operand has incomplete type.  */
-   if (!COMPLETE_TYPE_P (TREE_TYPE (TREE_OPERAND (addr, 0))))
-     return false;
- 
-   /* If the address is invariant then we do not need to preserve restrict
-      qualifications.  But we do need to preserve volatile qualifiers until
-      we can annotate the folded dereference itself properly.  */
-   if (is_gimple_min_invariant (addr)
-       && (!TREE_THIS_VOLATILE (deref)
- 	  || TYPE_VOLATILE (TREE_TYPE (addr))))
-     return useless_type_conversion_p (TREE_TYPE (deref),
- 				      TREE_TYPE (TREE_OPERAND (addr, 0)));
- 
-   /* Else both the address substitution and the folding must result in
-      a valid useless type conversion sequence.  */
-   return (useless_type_conversion_p (TREE_TYPE (TREE_OPERAND (deref, 0)),
- 				     TREE_TYPE (addr))
- 	  && useless_type_conversion_p (TREE_TYPE (deref),
- 					TREE_TYPE (TREE_OPERAND (addr, 0))));
- }
- 
- 
- /* A subroutine of fold_stmt.  Attempts to fold *(A+O) to A[X].
-    BASE is an array type.  OFFSET is a byte displacement.
- 
-    LOC is the location of the original expression.  */
- 
- static tree
- maybe_fold_offset_to_array_ref (location_t loc, tree base, tree offset)
- {
-   tree min_idx, idx, idx_type, elt_offset = integer_zero_node;
-   tree array_type, elt_type, elt_size;
-   tree domain_type;
- 
-   /* If BASE is an ARRAY_REF, we can pick up another offset (this time
-      measured in units of the size of elements type) from that ARRAY_REF).
-      We can't do anything if either is variable.
- 
-      The case we handle here is *(&A[N]+O).  */
-   if (TREE_CODE (base) == ARRAY_REF)
-     {
-       tree low_bound = array_ref_low_bound (base);
- 
-       elt_offset = TREE_OPERAND (base, 1);
-       if (TREE_CODE (low_bound) != INTEGER_CST
- 	  || TREE_CODE (elt_offset) != INTEGER_CST)
- 	return NULL_TREE;
- 
-       elt_offset = int_const_binop (MINUS_EXPR, elt_offset, low_bound);
-       base = TREE_OPERAND (base, 0);
-     }
- 
-   /* Ignore stupid user tricks of indexing non-array variables.  */
-   array_type = TREE_TYPE (base);
-   if (TREE_CODE (array_type) != ARRAY_TYPE)
-     return NULL_TREE;
-   elt_type = TREE_TYPE (array_type);
- 
-   /* Use signed size type for intermediate computation on the index.  */
-   idx_type = ssizetype;
- 
-   /* If OFFSET and ELT_OFFSET are zero, we don't care about the size of the
-      element type (so we can use the alignment if it's not constant).
-      Otherwise, compute the offset as an index by using a division.  If the
-      division isn't exact, then don't do anything.  */
-   elt_size = TYPE_SIZE_UNIT (elt_type);
-   if (!elt_size)
-     return NULL;
-   if (integer_zerop (offset))
-     {
-       if (TREE_CODE (elt_size) != INTEGER_CST)
- 	elt_size = size_int (TYPE_ALIGN (elt_type));
- 
-       idx = build_int_cst (idx_type, 0);
-     }
-   else
-     {
-       unsigned HOST_WIDE_INT lquo, lrem;
-       HOST_WIDE_INT hquo, hrem;
-       double_int soffset;
- 
-       /* The final array offset should be signed, so we need
- 	 to sign-extend the (possibly pointer) offset here
- 	 and use signed division.  */
-       soffset = double_int_sext (tree_to_double_int (offset),
- 				 TYPE_PRECISION (TREE_TYPE (offset)));
-       if (TREE_CODE (elt_size) != INTEGER_CST
- 	  || div_and_round_double (TRUNC_DIV_EXPR, 0,
- 				   soffset.low, soffset.high,
- 				   TREE_INT_CST_LOW (elt_size),
- 				   TREE_INT_CST_HIGH (elt_size),
- 				   &lquo, &hquo, &lrem, &hrem)
- 	  || lrem || hrem)
- 	return NULL_TREE;
- 
-       idx = build_int_cst_wide (idx_type, lquo, hquo);
-     }
- 
-   /* Assume the low bound is zero.  If there is a domain type, get the
-      low bound, if any, convert the index into that type, and add the
-      low bound.  */
-   min_idx = build_int_cst (idx_type, 0);
-   domain_type = TYPE_DOMAIN (array_type);
-   if (domain_type)
-     {
-       idx_type = domain_type;
-       if (TYPE_MIN_VALUE (idx_type))
- 	min_idx = TYPE_MIN_VALUE (idx_type);
-       else
- 	min_idx = fold_convert (idx_type, min_idx);
- 
-       if (TREE_CODE (min_idx) != INTEGER_CST)
- 	return NULL_TREE;
- 
-       elt_offset = fold_convert (idx_type, elt_offset);
-     }
- 
-   if (!integer_zerop (min_idx))
-     idx = int_const_binop (PLUS_EXPR, idx, min_idx);
-   if (!integer_zerop (elt_offset))
-     idx = int_const_binop (PLUS_EXPR, idx, elt_offset);
- 
-   /* Make sure to possibly truncate late after offsetting.  */
-   idx = fold_convert (idx_type, idx);
- 
-   /* We don't want to construct access past array bounds. For example
-        char *(c[4]);
-        c[3][2];
-      should not be simplified into (*c)[14] or tree-vrp will
-      give false warnings.
-      This is only an issue for multi-dimensional arrays.  */
-   if (TREE_CODE (elt_type) == ARRAY_TYPE
-       && domain_type)
-     {
-       if (TYPE_MAX_VALUE (domain_type)
- 	  && TREE_CODE (TYPE_MAX_VALUE (domain_type)) == INTEGER_CST
- 	  && tree_int_cst_lt (TYPE_MAX_VALUE (domain_type), idx))
- 	return NULL_TREE;
-       else if (TYPE_MIN_VALUE (domain_type)
- 	       && TREE_CODE (TYPE_MIN_VALUE (domain_type)) == INTEGER_CST
- 	       && tree_int_cst_lt (idx, TYPE_MIN_VALUE (domain_type)))
- 	return NULL_TREE;
-       else if (compare_tree_int (idx, 0) < 0)
- 	return NULL_TREE;
-     }
- 
-   {
-     tree t = build4 (ARRAY_REF, elt_type, base, idx, NULL_TREE, NULL_TREE);
-     SET_EXPR_LOCATION (t, loc);
-     return t;
-   }
- }
- 
- 
- /* Attempt to express (ORIG_TYPE)BASE+OFFSET as BASE[index].
-    LOC is the location of original expression.
- 
-    Before attempting the conversion strip off existing ADDR_EXPRs.  */
- 
- tree
- maybe_fold_offset_to_reference (location_t loc, tree base, tree offset,
- 				tree orig_type)
- {
-   tree ret;
- 
-   STRIP_NOPS (base);
-   if (TREE_CODE (base) != ADDR_EXPR)
-     return NULL_TREE;
- 
-   base = TREE_OPERAND (base, 0);
-   if (types_compatible_p (orig_type, TREE_TYPE (base))
-       && integer_zerop (offset))
-     return base;
- 
-   ret = maybe_fold_offset_to_array_ref (loc, base, offset);
-   if (ret && types_compatible_p (orig_type, TREE_TYPE (ret)))
-     return ret;
-   return NULL_TREE;
- }
- 
- /* Attempt to express (ORIG_TYPE)ADDR+OFFSET as (*ADDR)[index].
-    LOC is the location of the original expression.  */
- 
- tree
- maybe_fold_offset_to_address (location_t loc, tree addr, tree offset,
- 			      tree orig_type)
- {
-   tree base, ret;
- 
-   STRIP_NOPS (addr);
-   if (TREE_CODE (addr) != ADDR_EXPR)
-     return NULL_TREE;
-   base = TREE_OPERAND (addr, 0);
-   ret = maybe_fold_offset_to_array_ref (loc, base, offset);
-   if (ret)
-     {
-       ret = build_fold_addr_expr (ret);
-       if (!useless_type_conversion_p (orig_type, TREE_TYPE (ret)))
- 	return NULL_TREE;
-       SET_EXPR_LOCATION (ret, loc);
-     }
- 
-   return ret;
- }
- 
- 
- /* A quaint feature extant in our address arithmetic is that there
-    can be hidden type changes here.  The type of the result need
-    not be the same as the type of the input pointer.
- 
-    What we're after here is an expression of the form
- 	(T *)(&array + const)
-    where array is OP0, const is OP1, RES_TYPE is T and
-    the cast doesn't actually exist, but is implicit in the
-    type of the POINTER_PLUS_EXPR.  We'd like to turn this into
- 	&array[x]
-    which may be able to propagate further.  */
- 
- tree
- maybe_fold_stmt_addition (location_t loc, tree res_type, tree op0, tree op1)
- {
-   tree ptd_type;
-   tree t;
- 
-   /* The first operand should be an ADDR_EXPR.  */
-   if (TREE_CODE (op0) != ADDR_EXPR)
-     return NULL_TREE;
-   op0 = TREE_OPERAND (op0, 0);
- 
-   /* It had better be a constant.  */
-   if (TREE_CODE (op1) != INTEGER_CST)
-     {
-       /* Or op0 should now be A[0] and the non-constant offset defined
- 	 via a multiplication by the array element size.  */
-       if (TREE_CODE (op0) == ARRAY_REF
- 	  /* As we will end up creating a variable index array access
- 	     in the outermost array dimension make sure there isn't
- 	     a more inner array that the index could overflow to.  */
- 	  && TREE_CODE (TREE_OPERAND (op0, 0)) != ARRAY_REF
- 	  && integer_zerop (TREE_OPERAND (op0, 1))
- 	  && TREE_CODE (op1) == SSA_NAME)
- 	{
- 	  gimple offset_def = SSA_NAME_DEF_STMT (op1);
- 	  tree elsz = TYPE_SIZE_UNIT (TREE_TYPE (op0));
- 	  if (!host_integerp (elsz, 1)
- 	      || !is_gimple_assign (offset_def))
- 	    return NULL_TREE;
- 
- 	  /* Do not build array references of something that we can't
- 	     see the true number of array dimensions for.  */
- 	  if (!DECL_P (TREE_OPERAND (op0, 0))
- 	      && !handled_component_p (TREE_OPERAND (op0, 0)))
- 	    return NULL_TREE;
- 
- 	  if (gimple_assign_rhs_code (offset_def) == MULT_EXPR
- 	      && TREE_CODE (gimple_assign_rhs2 (offset_def)) == INTEGER_CST
- 	      && tree_int_cst_equal (gimple_assign_rhs2 (offset_def), elsz))
- 	    return build_fold_addr_expr
- 			  (build4 (ARRAY_REF, TREE_TYPE (op0),
- 				   TREE_OPERAND (op0, 0),
- 				   gimple_assign_rhs1 (offset_def),
- 				   TREE_OPERAND (op0, 2),
- 				   TREE_OPERAND (op0, 3)));
- 	  else if (integer_onep (elsz)
- 		   && gimple_assign_rhs_code (offset_def) != MULT_EXPR)
- 	    return build_fold_addr_expr
- 			  (build4 (ARRAY_REF, TREE_TYPE (op0),
- 				   TREE_OPERAND (op0, 0),
- 				   op1,
- 				   TREE_OPERAND (op0, 2),
- 				   TREE_OPERAND (op0, 3)));
- 	}
-       else if (TREE_CODE (TREE_TYPE (op0)) == ARRAY_TYPE
- 	       /* Dto.  */
- 	       && TREE_CODE (TREE_TYPE (TREE_TYPE (op0))) != ARRAY_TYPE
- 	       && TREE_CODE (op1) == SSA_NAME)
- 	{
- 	  gimple offset_def = SSA_NAME_DEF_STMT (op1);
- 	  tree elsz = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (op0)));
- 	  if (!host_integerp (elsz, 1)
- 	      || !is_gimple_assign (offset_def))
- 	    return NULL_TREE;
- 
- 	  /* Do not build array references of something that we can't
- 	     see the true number of array dimensions for.  */
- 	  if (!DECL_P (op0)
- 	      && !handled_component_p (op0))
- 	    return NULL_TREE;
- 
- 	  if (gimple_assign_rhs_code (offset_def) == MULT_EXPR
- 	      && TREE_CODE (gimple_assign_rhs2 (offset_def)) == INTEGER_CST
- 	      && tree_int_cst_equal (gimple_assign_rhs2 (offset_def), elsz))
- 	    return build_fold_addr_expr
- 			  (build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (op0)),
- 				   op0, gimple_assign_rhs1 (offset_def),
- 				   integer_zero_node, NULL_TREE));
- 	  else if (integer_onep (elsz)
- 		   && gimple_assign_rhs_code (offset_def) != MULT_EXPR)
- 	    return build_fold_addr_expr
- 			  (build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (op0)),
- 				   op0, op1,
- 				   integer_zero_node, NULL_TREE));
- 	}
- 
-       return NULL_TREE;
-     }
- 
-   /* If the first operand is an ARRAY_REF, expand it so that we can fold
-      the offset into it.  */
-   while (TREE_CODE (op0) == ARRAY_REF)
-     {
-       tree array_obj = TREE_OPERAND (op0, 0);
-       tree array_idx = TREE_OPERAND (op0, 1);
-       tree elt_type = TREE_TYPE (op0);
-       tree elt_size = TYPE_SIZE_UNIT (elt_type);
-       tree min_idx;
- 
-       if (TREE_CODE (array_idx) != INTEGER_CST)
- 	break;
-       if (TREE_CODE (elt_size) != INTEGER_CST)
- 	break;
- 
-       /* Un-bias the index by the min index of the array type.  */
-       min_idx = TYPE_DOMAIN (TREE_TYPE (array_obj));
-       if (min_idx)
- 	{
- 	  min_idx = TYPE_MIN_VALUE (min_idx);
- 	  if (min_idx)
- 	    {
- 	      if (TREE_CODE (min_idx) != INTEGER_CST)
- 		break;
- 
- 	      array_idx = fold_convert (TREE_TYPE (min_idx), array_idx);
- 	      if (!integer_zerop (min_idx))
- 		array_idx = int_const_binop (MINUS_EXPR, array_idx,
- 					     min_idx);
- 	    }
- 	}
- 
-       /* Convert the index to a byte offset.  */
-       array_idx = fold_convert (sizetype, array_idx);
-       array_idx = int_const_binop (MULT_EXPR, array_idx, elt_size);
- 
-       /* Update the operands for the next round, or for folding.  */
-       op1 = int_const_binop (PLUS_EXPR,
- 			     array_idx, op1);
-       op0 = array_obj;
-     }
- 
-   ptd_type = TREE_TYPE (res_type);
-   /* If we want a pointer to void, reconstruct the reference from the
-      array element type.  A pointer to that can be trivially converted
-      to void *.  This happens as we fold (void *)(ptr p+ off).  */
-   if (VOID_TYPE_P (ptd_type)
-       && TREE_CODE (TREE_TYPE (op0)) == ARRAY_TYPE)
-     ptd_type = TREE_TYPE (TREE_TYPE (op0));
- 
-   /* At which point we can try some of the same things as for indirects.  */
-   t = maybe_fold_offset_to_array_ref (loc, op0, op1);
-   if (t)
-     {
-       t = build_fold_addr_expr (t);
-       if (!useless_type_conversion_p (res_type, TREE_TYPE (t)))
- 	return NULL_TREE;
-       SET_EXPR_LOCATION (t, loc);
-     }
- 
-   return t;
- }
  
  /* Subroutine of fold_stmt.  We perform several simplifications of the
     memory reference tree EXPR and make sure to re-gimplify them properly
--- 176,181 ----
*************** fold_gimple_assign (gimple_stmt_iterator
*** 783,823 ****
  	    if (valid_gimple_rhs_p (result))
  	      return result;
  	  }
- 	else if (CONVERT_EXPR_CODE_P (subcode)
- 		 && POINTER_TYPE_P (gimple_expr_type (stmt))
- 		 && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt))))
- 	  {
- 	    tree type = gimple_expr_type (stmt);
- 	    tree t = maybe_fold_offset_to_address (loc,
- 						   gimple_assign_rhs1 (stmt),
- 						   integer_zero_node, type);
- 	    if (t)
- 	      return t;
- 	  }
        }
        break;
  
      case GIMPLE_BINARY_RHS:
-       /* Try to fold pointer addition.  */
-       if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
- 	{
- 	  tree type = TREE_TYPE (gimple_assign_rhs1 (stmt));
- 	  if (TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
- 	    {
- 	      type = build_pointer_type (TREE_TYPE (TREE_TYPE (type)));
- 	      if (!useless_type_conversion_p
- 		    (TREE_TYPE (gimple_assign_lhs (stmt)), type))
- 		type = TREE_TYPE (gimple_assign_rhs1 (stmt));
- 	    }
- 	  result = maybe_fold_stmt_addition (gimple_location (stmt),
- 					     type,
- 					     gimple_assign_rhs1 (stmt),
- 					     gimple_assign_rhs2 (stmt));
- 	}
        /* Try to canonicalize for boolean-typed X the comparisons
  	 X == 0, X == 1, X != 0, and X != 1.  */
!       else if (gimple_assign_rhs_code (stmt) == EQ_EXPR
!                || gimple_assign_rhs_code (stmt) == NE_EXPR)
          {
  	  tree lhs = gimple_assign_lhs (stmt);
  	  tree op1 = gimple_assign_rhs1 (stmt);
--- 408,421 ----
  	    if (valid_gimple_rhs_p (result))
  	      return result;
  	  }
        }
        break;
  
      case GIMPLE_BINARY_RHS:
        /* Try to canonicalize for boolean-typed X the comparisons
  	 X == 0, X == 1, X != 0, and X != 1.  */
!       if (gimple_assign_rhs_code (stmt) == EQ_EXPR
! 	  || gimple_assign_rhs_code (stmt) == NE_EXPR)
          {
  	  tree lhs = gimple_assign_lhs (stmt);
  	  tree op1 = gimple_assign_rhs1 (stmt);
*************** gimple_fold_stmt_to_constant_1 (gimple s
*** 2945,2973 ****
                /* Handle unary operators that can appear in GIMPLE form.
                   Note that we know the single operand must be a constant,
                   so this should almost always return a simplified RHS.  */
!               tree lhs = gimple_assign_lhs (stmt);
                tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
  
  	      /* Conversions are useless for CCP purposes if they are
  		 value-preserving.  Thus the restrictions that
! 		 useless_type_conversion_p places for pointer type conversions
! 		 do not apply here.  Substitution later will only substitute to
! 		 allowed places.  */
  	      if (CONVERT_EXPR_CODE_P (subcode)
  		  && POINTER_TYPE_P (TREE_TYPE (lhs))
! 		  && POINTER_TYPE_P (TREE_TYPE (op0)))
! 		{
! 		  tree tem;
! 		  /* Try to re-construct array references on-the-fly.  */
! 		  if (!useless_type_conversion_p (TREE_TYPE (lhs),
! 						  TREE_TYPE (op0))
! 		      && ((tem = maybe_fold_offset_to_address
! 			   (loc,
! 			    op0, integer_zero_node, TREE_TYPE (lhs)))
! 			  != NULL_TREE))
! 		    return tem;
! 		  return op0;
! 		}
  
                return
  		fold_unary_ignore_overflow_loc (loc, subcode,
--- 2543,2562 ----
                /* Handle unary operators that can appear in GIMPLE form.
                   Note that we know the single operand must be a constant,
                   so this should almost always return a simplified RHS.  */
! 	      tree lhs = gimple_assign_lhs (stmt);
                tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
  
  	      /* Conversions are useless for CCP purposes if they are
  		 value-preserving.  Thus the restrictions that
! 		 useless_type_conversion_p places for restrict qualification
! 		 of pointer types should not apply here.
! 		 Substitution later will only substitute to allowed places.  */
  	      if (CONVERT_EXPR_CODE_P (subcode)
  		  && POINTER_TYPE_P (TREE_TYPE (lhs))
! 		  && POINTER_TYPE_P (TREE_TYPE (op0))
! 		  && (TYPE_ADDR_SPACE (TREE_TYPE (lhs))
! 		      == TYPE_ADDR_SPACE (TREE_TYPE (op0))))
! 		return op0;
  
                return
  		fold_unary_ignore_overflow_loc (loc, subcode,
Index: gcc/gimple.h
===================================================================
*** gcc/gimple.h.orig	2011-08-30 13:01:16.000000000 +0200
--- gcc/gimple.h	2011-08-30 13:05:45.000000000 +0200
*************** void gimplify_and_update_call_from_tree
*** 5069,5080 ****
  tree gimple_fold_builtin (gimple);
  bool fold_stmt (gimple_stmt_iterator *);
  bool fold_stmt_inplace (gimple);
- tree maybe_fold_offset_to_address (location_t, tree, tree, tree);
- tree maybe_fold_offset_to_reference (location_t, tree, tree, tree);
- tree maybe_fold_stmt_addition (location_t, tree, tree, tree);
  tree get_symbol_constant_value (tree);
  tree canonicalize_constructor_val (tree);
- bool may_propagate_address_into_dereference (tree, tree);
  extern tree maybe_fold_and_comparisons (enum tree_code, tree, tree, 
  					enum tree_code, tree, tree);
  extern tree maybe_fold_or_comparisons (enum tree_code, tree, tree,
--- 5069,5076 ----
Index: gcc/tree-ssa-forwprop.c
===================================================================
*** gcc/tree-ssa-forwprop.c.orig	2011-08-30 13:01:16.000000000 +0200
--- gcc/tree-ssa-forwprop.c	2011-08-30 13:05:45.000000000 +0200
*************** forward_propagate_addr_expr_1 (tree name
*** 1002,1032 ****
      return false;
  
    rhs2 = gimple_assign_rhs2 (use_stmt);
!   /* Try to optimize &x[C1] p+ C2 where C2 is a multiple of the size
!      of the elements in X into &x[C1 + C2/element size].  */
    if (TREE_CODE (rhs2) == INTEGER_CST)
      {
!       tree new_rhs = maybe_fold_stmt_addition (gimple_location (use_stmt),
! 	  				       TREE_TYPE (def_rhs),
! 					       def_rhs, rhs2);
!       if (new_rhs)
! 	{
! 	  tree type = TREE_TYPE (gimple_assign_lhs (use_stmt));
! 	  new_rhs = unshare_expr (new_rhs);
! 	  if (!useless_type_conversion_p (type, TREE_TYPE (new_rhs)))
! 	    {
! 	      if (!is_gimple_min_invariant (new_rhs))
! 		new_rhs = force_gimple_operand_gsi (use_stmt_gsi, new_rhs,
! 						    true, NULL_TREE,
! 						    true, GSI_SAME_STMT);
! 	      new_rhs = fold_convert (type, new_rhs);
! 	    }
! 	  gimple_assign_set_rhs_from_tree (use_stmt_gsi, new_rhs);
! 	  use_stmt = gsi_stmt (*use_stmt_gsi);
! 	  update_stmt (use_stmt);
! 	  tidy_after_forward_propagate_addr (use_stmt);
! 	  return true;
! 	}
      }
  
    /* Try to optimize &x[0] p+ OFFSET where OFFSET is defined by
--- 1002,1022 ----
      return false;
  
    rhs2 = gimple_assign_rhs2 (use_stmt);
!   /* Optimize &x[C1] p+ C2 to  &x p+ C3 with C3 = C1 * element_size + C2.  */
    if (TREE_CODE (rhs2) == INTEGER_CST)
      {
!       tree new_rhs = build1_loc (gimple_location (use_stmt),
! 				 ADDR_EXPR, TREE_TYPE (def_rhs),
! 				 fold_build2 (MEM_REF,
! 					      TREE_TYPE (TREE_TYPE (def_rhs)),
! 					      unshare_expr (def_rhs),
! 					      fold_convert (ptr_type_node,
! 							    rhs2)));
!       gimple_assign_set_rhs_from_tree (use_stmt_gsi, new_rhs);
!       use_stmt = gsi_stmt (*use_stmt_gsi);
!       update_stmt (use_stmt);
!       tidy_after_forward_propagate_addr (use_stmt);
!       return true;
      }
  
    /* Try to optimize &x[0] p+ OFFSET where OFFSET is defined by
Index: gcc/gimplify.c
===================================================================
*** gcc/gimplify.c.orig	2011-08-30 13:01:16.000000000 +0200
--- gcc/gimplify.c	2011-08-30 13:28:14.000000000 +0200
*************** canonicalize_addr_expr (tree *expr_p)
*** 1799,1805 ****
  static enum gimplify_status
  gimplify_conversion (tree *expr_p)
  {
-   tree tem;
    location_t loc = EXPR_LOCATION (*expr_p);
    gcc_assert (CONVERT_EXPR_P (*expr_p));
  
--- 1799,1804 ----
*************** gimplify_conversion (tree *expr_p)
*** 1810,1826 ****
    if (tree_ssa_useless_type_conversion (*expr_p))
      *expr_p = TREE_OPERAND (*expr_p, 0);
  
-   /* Attempt to avoid NOP_EXPR by producing reference to a subtype.
-      For example this fold (subclass *)&A into &A->subclass avoiding
-      a need for statement.  */
-   if (CONVERT_EXPR_P (*expr_p)
-       && POINTER_TYPE_P (TREE_TYPE (*expr_p))
-       && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (*expr_p, 0)))
-       && (tem = maybe_fold_offset_to_address
- 	  (EXPR_LOCATION (*expr_p), TREE_OPERAND (*expr_p, 0),
- 	   integer_zero_node, TREE_TYPE (*expr_p))) != NULL_TREE)
-     *expr_p = tem;
- 
    /* If we still have a conversion at the toplevel,
       then canonicalize some constructs.  */
    if (CONVERT_EXPR_P (*expr_p))
--- 1809,1814 ----
*************** gimplify_expr (tree *expr_p, gimple_seq
*** 7302,7337 ****
  	  goto expr_3;
  
  	case POINTER_PLUS_EXPR:
!           /* Convert ((type *)A)+offset into &A->field_of_type_and_offset.
! 	     The second is gimple immediate saving a need for extra statement.
! 	   */
! 	  if (TREE_CODE (TREE_OPERAND (*expr_p, 1)) == INTEGER_CST
! 	      && (tmp = maybe_fold_offset_to_address
! 		  (EXPR_LOCATION (*expr_p),
! 		   TREE_OPERAND (*expr_p, 0), TREE_OPERAND (*expr_p, 1),
! 		   TREE_TYPE (*expr_p))))
! 	    {
! 	      *expr_p = tmp;
! 	      ret = GS_OK;
! 	      break;
! 	    }
! 	  /* Convert (void *)&a + 4 into (void *)&a[1].  */
! 	  if (TREE_CODE (TREE_OPERAND (*expr_p, 0)) == NOP_EXPR
! 	      && TREE_CODE (TREE_OPERAND (*expr_p, 1)) == INTEGER_CST
! 	      && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (*expr_p,
! 									0),0)))
! 	      && (tmp = maybe_fold_offset_to_address
! 		  (EXPR_LOCATION (*expr_p),
! 		   TREE_OPERAND (TREE_OPERAND (*expr_p, 0), 0),
! 		   TREE_OPERAND (*expr_p, 1),
! 		   TREE_TYPE (TREE_OPERAND (TREE_OPERAND (*expr_p, 0),
! 					    0)))))
! 	     {
!                *expr_p = fold_convert (TREE_TYPE (*expr_p), tmp);
! 	       ret = GS_OK;
! 	       break;
! 	     }
!           /* FALLTHRU */
  
  	default:
  	  switch (TREE_CODE_CLASS (TREE_CODE (*expr_p)))
--- 7290,7321 ----
  	  goto expr_3;
  
  	case POINTER_PLUS_EXPR:
! 	  {
! 	    enum gimplify_status r0, r1;
! 	    r0 = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p,
! 				post_p, is_gimple_val, fb_rvalue);
! 	    r1 = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p,
! 				post_p, is_gimple_val, fb_rvalue);
! 	    ret = MIN (r0, r1);
! 	    /* Convert &X + CST to invariant &MEM[&X, CST].  Do this
! 	       after gimplifying operands - this is similar to how
! 	       it would be folding all gimplified stmts on creation
! 	       to have them canonicalized, which is what we eventually
! 	       should do anyway.  */
! 	    if (TREE_CODE (TREE_OPERAND (*expr_p, 1)) == INTEGER_CST
! 		&& is_gimple_min_invariant (TREE_OPERAND (*expr_p, 0)))
! 	      {
! 		*expr_p = build_fold_addr_expr_with_type_loc
! 		   (input_location,
! 		    fold_build2 (MEM_REF, TREE_TYPE (TREE_TYPE (*expr_p)),
! 				 TREE_OPERAND (*expr_p, 0),
! 				 fold_convert (ptr_type_node,
! 					       TREE_OPERAND (*expr_p, 1))),
! 		    TREE_TYPE (*expr_p));
! 		ret = MIN (ret, GS_OK);
! 	      }
! 	    break;
! 	  }
  
  	default:
  	  switch (TREE_CODE_CLASS (TREE_CODE (*expr_p)))
Richard Guenther - Aug. 30, 2011, 1:58 p.m.
On Tue, 30 Aug 2011, Richard Guenther wrote:

> 
> I've run into PR48571 again, and after having fun with out data-dep
> analysis code I indeed believe that we should not try to re-construct
> ARRAY_REFs from code involving pointer arithmetic.  ARRAY_REFs
> have constraints imposed on them that are relied on by data dependence
> analysis and that are indeed useful to have (the index is within
> bounds, otherwise undefined behavior).  We can't recover from
> lowering that happened, much as Zdenek argued when I initially
> proposed to lower all ARRAY_REFs to pointer arithmetic with the
> original MEM_REF design.
> 
> Thus, the following completes the partial removal of reference
> re-constructing I did when merging MEM_REFs (I removed the code
> re-constructing COMPONENT_REFs back then).
> 
> Not much fallout, much to my surprise (I've tried it initially
> on the MEM_REF branch, but quickly gave up on this additional task).
> We've appearantly become better in handling of pointer based accesses
> (we've already much experience here with GFortran lowering 
> multi-dimensional array accesses to one-dimensional ones).
> 
> The patch XFAILs one -Warray-bounds testcase, the -Warray-bounds
> infrastructure needs some serious TLC which I didn't want to fold
> into this patch.  The testcase in question also really asks for
> an 'access outside of object' warning, similar to the
> 'offset outside bounds of constant string' one we have.
> 
> Bootstrapped and tested an earlier version on x86_64-unknown-linux-gnu,
> a final bootstrap and regtest cycle is currently running.
> 
> (yes, there are still 
> tree-ssa-forwprop.c:forward_propagate_addr_into_variable_array_index and
> fold-const.c:try_move_mult_to_index)

Actually I forgot

        * gcc.dg/tree-ssa/ssa-ccp-25.c: Remove.
        * gcc.dg/tree-ssa/ssa-ccp-26.c: Likewise.

which are feature tests for the reconstruction.  Likewise I missed
a recalculate_side_effects (*expr_p) in POINTER_PLUS_EXPR gimplification.

With both fixed, bootstrapped and tested on x86_64-unknown-linxu-gnu
and applied to trunk.

Richard.
H.J. Lu - Sept. 20, 2011, 1:36 p.m.
On Tue, Aug 30, 2011 at 4:59 AM, Richard Guenther <rguenther@suse.de> wrote:
>
> I've run into PR48571 again, and after having fun with out data-dep
> analysis code I indeed believe that we should not try to re-construct
> ARRAY_REFs from code involving pointer arithmetic.  ARRAY_REFs
> have constraints imposed on them that are relied on by data dependence
> analysis and that are indeed useful to have (the index is within
> bounds, otherwise undefined behavior).  We can't recover from
> lowering that happened, much as Zdenek argued when I initially
> proposed to lower all ARRAY_REFs to pointer arithmetic with the
> original MEM_REF design.
>
> Thus, the following completes the partial removal of reference
> re-constructing I did when merging MEM_REFs (I removed the code
> re-constructing COMPONENT_REFs back then).
>
> Not much fallout, much to my surprise (I've tried it initially
> on the MEM_REF branch, but quickly gave up on this additional task).
> We've appearantly become better in handling of pointer based accesses
> (we've already much experience here with GFortran lowering
> multi-dimensional array accesses to one-dimensional ones).
>
> The patch XFAILs one -Warray-bounds testcase, the -Warray-bounds
> infrastructure needs some serious TLC which I didn't want to fold
> into this patch.  The testcase in question also really asks for
> an 'access outside of object' warning, similar to the
> 'offset outside bounds of constant string' one we have.
>
> Bootstrapped and tested an earlier version on x86_64-unknown-linux-gnu,
> a final bootstrap and regtest cycle is currently running.
>
> (yes, there are still
> tree-ssa-forwprop.c:forward_propagate_addr_into_variable_array_index and
> fold-const.c:try_move_mult_to_index)
>
> Richard.
>
> 2011-08-30  Richard Guenther  <rguenther@suse.de>
>
>        PR middle-end/48571
>        * gimple.h (maybe_fold_offset_to_address): Remove.
>        (maybe_fold_offset_to_reference): Likewise.
>        (maybe_fold_stmt_addition): Likewise.
>        (may_propagate_address_into_dereference): Likewise.
>        * tree-inline.c (remap_gimple_op_r): Do not reconstruct
>        array references.
>        * gimple-fold.c (canonicalize_constructor_val): Likewise.
>        Canonicalize invariant POINTER_PLUS_EXPRs to invariant MEM_REF
>        addresses instead.
>        (may_propagate_address_into_dereference): Remove.
>        (maybe_fold_offset_to_array_ref): Likewise.
>        (maybe_fold_offset_to_reference): Likewise.
>        (maybe_fold_offset_to_address): Likewise.
>        (maybe_fold_stmt_addition): Likewise.
>        (fold_gimple_assign): Do not reconstruct array references but
>        instead canonicalize invariant POINTER_PLUS_EXPRs to invariant
>        MEM_REF addresses.
>        (gimple_fold_stmt_to_constant_1): Likewise.
>        * tree-ssa-forwprop.c (forward_propagate_addr_expr_1): Likewise.
>        * gimplify.c (gimplify_conversion): Likewise.
>        (gimplify_expr): Likewise.
>
>        * gcc.c-torture/execute/pr48571-1.c: New testcase.
>        * gcc.dg/pr36902.c: XFAIL.
>

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50460

Patch

Index: gcc/testsuite/gcc.c-torture/execute/pr48571-1.c
===================================================================
--- gcc/testsuite/gcc.c-torture/execute/pr48571-1.c	(revision 0)
+++ gcc/testsuite/gcc.c-torture/execute/pr48571-1.c	(revision 0)
@@ -0,0 +1,28 @@ 
+unsigned int c[624];
+void __attribute__((noinline))
+bar (void)
+{
+  unsigned int i;
+  /* Obfuscated c[i] = c[i-1] * 2.  */
+  for (i = 1; i < 624; ++i)
+    *(unsigned int *)((void *)c + (__SIZE_TYPE__)i * 4)
+	= 2 * *(unsigned int *)((void *)c + ((__SIZE_TYPE__)i +
+					     ((__SIZE_TYPE__)-4)/4) * 4);
+}
+extern void abort (void);
+int
+main()
+{
+  unsigned int i, j;
+  for (i = 0; i < 624; ++i)
+    c[i] = 1;
+  bar();
+  j = 1;
+  for (i = 0; i < 624; ++i)
+    {
+      if (c[i] != j)
+	abort ();
+      j = j * 2;
+    }
+  return 0;
+}