[tree-ssa] more aggressive address folding

Richard Henderson rth@twiddle.net
Wed Nov 19 16:08:00 GMT 2003


The following works around some less-than-desirable behaviour from
the C and C++ front ends, where we lower A[x] and S->y to addition
immediately.  The ostensible reason is for offsetof.

I don't buy it, myself.  If offsetof is the only reason, then we
should add code to fold() to handle ADDR_EXPR of COMPONENT_REF and
ARRAY_REF with an INTEGER_CST base pointer.

The ARRAY_REF folding will always be necessary.  Users often do 
this in C by letting arrays decompose to pointers.

The COMPONENT_REF folding might not be necessary if the front ends
are fixed.  Which would be nice, since it involves some amount of
repeated linear searching.

Bootstrapped and tested on i686-linux.  This fixes two test cases:

-FAIL: g++.old-deja/g++.martin/sts_vectini.C execution test
-FAIL: 27_io/fpos/mbstate_t/2.cc execution test

because get_expr_operands fails to properly handle *(&foo + 4).
Chatting with Diego, we've decided to make this illegal again.  If
we wind up with this, either fold_stmt has failed, or the front-end
has messed up the types, causing fold_stmt to fail.


r~


        * tree.c (recompute_tree_invarant_for_addr_expr): Split out from ...
        (build1): ... here.
        * tree.h: Declare it.
        * gimplify.c (gimplify_addr_expr): Use it.
        * tree-ssa-ccp.c (maybe_fold_offset_to_array_ref): Split out
        from fold_indirect_refs_r.
        (maybe_fold_stmt_indirect): Likewise.
        (maybe_fold_offset_to_component_ref): New.
        (maybe_fold_stmt_plus): New.
        (fold_stmt_r): Rename from fold_indirect_refs_r.
        (fold_stmt): Strip more useless type conversions.

Index: gimplify.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/gimplify.c,v
retrieving revision 1.1.2.118
diff -u -p -c -r1.1.2.118 gimplify.c
*** gimplify.c	17 Nov 2003 23:18:12 -0000	1.1.2.118
--- gimplify.c	19 Nov 2003 15:41:38 -0000
*************** gimplify_addr_expr (tree *expr_p, tree *
*** 2427,2432 ****
--- 2427,2435 ----
  	  /* ??? Could split out the decision code from build1 to verify.  */
  	  TREE_SIDE_EFFECTS (expr) = 0;
  
+ 	  /* Make sure TREE_INVARIANT/TREE_CONSTANT is set properly.  */
+ 	  recompute_tree_invarant_for_addr_expr (expr);
+ 
  	  /* Mark the RHS addressable.  */
  	  (*lang_hooks.mark_addressable) (TREE_OPERAND (expr, 0));
  	}
Index: tree-ssa-ccp.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-ssa-ccp.c,v
retrieving revision 1.1.2.111
diff -u -p -c -r1.1.2.111 tree-ssa-ccp.c
*** tree-ssa-ccp.c	16 Nov 2003 11:00:40 -0000	1.1.2.111
--- tree-ssa-ccp.c	19 Nov 2003 15:41:40 -0000
*************** likely_value (tree stmt)
*** 1485,1517 ****
    return ((found_constant || !uses) ? CONSTANT : VARYING);
  }
  
! /* Subroutine of fold_stmt called via walk_tree.  If *EXPR_P is of the 
!    form *&DECL, it is converted to DECL.  Similarly for offsets, which
!    get converted to array references if possible.  Return non-null if
!    the folding generates/exposes undefined code.  */
  
  static tree
! fold_indirect_refs_r (tree *expr_p, int *walk_subtrees ATTRIBUTE_UNUSED,
! 		      void *data)
  {
!   bool *changed_p = data;
!   tree expr = *expr_p;
!   tree offset, base, t;
  
!   if (TREE_CODE (expr) == INDIRECT_REF)
      {
!       base = TREE_OPERAND (expr, 0);
!       offset = integer_zero_node;
      }
!   else if (TREE_CODE (expr) == ARRAY_REF)
      {
!       base = TREE_OPERAND (expr, 0);
!       offset = TREE_OPERAND (expr, 1);
!       if (TREE_CODE (offset) != INTEGER_CST)
  	return NULL_TREE;
      }
!   else
!     return NULL_TREE;
  
    /* We may well have constructed a double-nested PLUS_EXPR via multiple
       substitutions.  Fold that down to one.  Remove NON_LVALUE_EXPRs that
--- 1485,1646 ----
    return ((found_constant || !uses) ? CONSTANT : VARYING);
  }
  
! /* A subroutine of fold_stmt_r.  Attempts to fold *(A+O) to A[X].
!    BASE is an array type.  OFFSET is a byte displacement.  ORIG_TYPE
!    is the desired result type.   */
  
  static tree
! maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type)
  {
!   unsigned HOST_WIDE_INT lquo, lrem;
!   HOST_WIDE_INT hquo, hrem;
!   tree elt_size, min_idx, idx;
!   tree array_type, elt_type;
! 
!   /* 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);
!   if (TYPE_MAIN_VARIANT (orig_type) != TYPE_MAIN_VARIANT (elt_type))
!     return NULL_TREE;
! 	
!   /* Whee.  Ignore indexing of variable sized types.  */
!   elt_size = TYPE_SIZE_UNIT (elt_type);
!   if (TREE_CODE (elt_size) != INTEGER_CST)
!     return NULL_TREE;
! 
!   /* If the division isn't exact, then don't do anything.  Equally
!      invalid as the above indexing of non-array variables.  */
!   if (div_and_round_double (TRUNC_DIV_EXPR, 1,
! 			    TREE_INT_CST_LOW (offset),
! 			    TREE_INT_CST_HIGH (offset),
! 			    TREE_INT_CST_LOW (elt_size),
! 			    TREE_INT_CST_HIGH (elt_size),
! 			    &lquo, &hquo, &lrem, &hrem)
!       || lrem || hrem)
!     return NULL_TREE;
!   idx = build_int_2_wide (lquo, hquo);
  
!   /* Re-bias the index by the min index of the array type.  */
!   min_idx = TYPE_DOMAIN (TREE_TYPE (base));
!   if (min_idx)
      {
!       min_idx = TYPE_MIN_VALUE (min_idx);
!       if (min_idx)
! 	{
! 	  idx = convert (TREE_TYPE (min_idx), idx);
! 	  if (!integer_zerop (min_idx))
! 	    idx = fold (build (PLUS_EXPR, TREE_TYPE (min_idx),
! 			idx, min_idx));
! 	}
      }
! 
!   return build (ARRAY_REF, orig_type, base, idx);
! }
! 
! /* A subroutine of fold_stmt_r.  Attempts to fold *(S+O) to S.X.
!    BASE is a record type.  OFFSET is a byte displacement.  ORIG_TYPE
!    is the desired result type.  */
! /* ??? This doesn't handle class inheritence.  */
! 
! static tree
! maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
! 				    tree orig_type, bool base_is_ptr)
! {
!   tree f, t;
! 
!   if (TREE_CODE (record_type) != RECORD_TYPE
!       && TREE_CODE (record_type) != UNION_TYPE
!       && TREE_CODE (record_type) != QUAL_UNION_TYPE)
!     return NULL_TREE;
! 
!   /* Short-circuit silly cases.  */
!   if (TYPE_MAIN_VARIANT (record_type) == TYPE_MAIN_VARIANT (orig_type))
!     return NULL_TREE;
! 
!   for (f = TYPE_FIELDS (record_type); f ; f = TREE_CHAIN (f))
      {
!       tree field_type = TREE_TYPE (f);
!       int cmp;
! 
!       if (TREE_CODE (f) != FIELD_DECL)
! 	continue;
!       if (DECL_BIT_FIELD (f))
! 	continue;
!       if (TREE_CODE (DECL_FIELD_OFFSET (f)) != INTEGER_CST)
! 	continue;
! 
!       /* ??? Java creates "interesting" fields for representing base classes.
! 	 They have no name, and have no context.  With no context, we get into
! 	 trouble with nonoverlapping_component_refs_p.  Skip them.  */
!       if (!DECL_FIELD_CONTEXT (f))
! 	continue;
! 
!       /* Check to see if this offset overlaps with the field.  */
!       cmp = tree_int_cst_compare (DECL_FIELD_OFFSET (f), offset);
!       if (cmp > 0)
! 	continue;
!       if (cmp < 0)
! 	{
! 	  /* Don't care about offsets into the middle of scalars.  */
! 	  if (!AGGREGATE_TYPE_P (field_type))
! 	    continue;
! 
! 	  /* Check the end of the field against the offset.  */
! 	  /* ??? Check for array at the end of the struct.  This is often
! 	     used as for flexible array members.  We should be able to turn
! 	     this into an array access anyway.  */
! 	  if (!DECL_SIZE_UNIT (f)
! 	      || TREE_CODE (DECL_SIZE_UNIT (f)) != INTEGER_CST)
! 	    continue;
! 	  t = fold (build (MINUS_EXPR, TREE_TYPE (offset),
! 			   offset, DECL_FIELD_OFFSET (f)));
! 	  if (!tree_int_cst_lt (t, DECL_SIZE_UNIT (f)))
! 	    continue;
! 
! 	  /* If we matched, then set offset to the displacement into
! 	     this field.  */
! 	  offset = t;
! 	}
! 
!       /* Here we exactly match the offset being checked.  If the types match,
! 	 then we can return that field.  */
!       else if (TYPE_MAIN_VARIANT (orig_type) == TYPE_MAIN_VARIANT (field_type))
! 	{
! 	  if (base_is_ptr)
! 	    base = build1 (INDIRECT_REF, record_type, base);
! 	  t = build (COMPONENT_REF, field_type, base, f);
! 	  return t;
! 	}
! 
!       /* Don't care about type-punning of scalars.  */
!       else if (!AGGREGATE_TYPE_P (field_type))
  	return NULL_TREE;
+ 
+       /* If we get here, we've got an aggregate field, and a possibly 
+ 	 non-zero offset into them.  Recurse and hope for a valid match.  */
+       if (base_is_ptr)
+ 	base = build1 (INDIRECT_REF, record_type, base);
+       base = build (COMPONENT_REF, field_type, base, f);
+ 
+       t = maybe_fold_offset_to_array_ref (base, offset, orig_type);
+       if (t)
+ 	return t;
+       return maybe_fold_offset_to_component_ref (field_type, base, offset,
+ 					         orig_type, false);
      }
! 
!   return NULL_TREE;
! }
! 
! /* A subroutine of fold_stmt_r.  Attempt to simplify *(BASE+OFFSET).
!    Return the simplified expression, or NULL if nothing could be done.  */
! 
! static tree
! maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
! {
!   tree t;
  
    /* We may well have constructed a double-nested PLUS_EXPR via multiple
       substitutions.  Fold that down to one.  Remove NON_LVALUE_EXPRs that
*************** fold_indirect_refs_r (tree *expr_p, int 
*** 1523,1533 ****
    /* One possibility is that the address reduces to a string constant.  */
    t = fold_read_from_constant_string (expr);
    if (t)
!     {
!       *expr_p = t;
!       *changed_p = true;
!       return NULL_TREE;
!     }
  
    /* Add in any offset from a PLUS_EXPR.  */
    if (TREE_CODE (base) == PLUS_EXPR)
--- 1652,1658 ----
    /* One possibility is that the address reduces to a string constant.  */
    t = fold_read_from_constant_string (expr);
    if (t)
!     return t;
  
    /* Add in any offset from a PLUS_EXPR.  */
    if (TREE_CODE (base) == PLUS_EXPR)
*************** fold_indirect_refs_r (tree *expr_p, int 
*** 1542,1548 ****
        offset = fold (build (PLUS_EXPR, TREE_TYPE (offset), offset, offset2));
      }
  
!   if (TREE_CODE (base) != ADDR_EXPR)
      {
        /* We can get here for out-of-range string constant accesses, 
  	 such as "_"[3].  Bail out of the entire substitution search
--- 1667,1693 ----
        offset = fold (build (PLUS_EXPR, TREE_TYPE (offset), offset, offset2));
      }
  
!   if (TREE_CODE (base) == ADDR_EXPR)
!     {
!       /* Strip the ADDR_EXPR.  */
!       base = TREE_OPERAND (base, 0);
! 
!       /* Try folding *(&B+O) to B[X].  */
!       t = maybe_fold_offset_to_array_ref (base, offset, TREE_TYPE (expr));
!       if (t)
! 	return t;
! 
!       /* Try folding *(&B+O) to B.X.  */
!       t = maybe_fold_offset_to_component_ref (TREE_TYPE (base), base, offset,
! 					      TREE_TYPE (expr), false);
!       if (t)
! 	return t;
! 
!       /* Fold *&B to B.  */
!       if (integer_zerop (offset))
! 	return base;
!     }
!   else
      {
        /* We can get here for out-of-range string constant accesses, 
  	 such as "_"[3].  Bail out of the entire substitution search
*************** fold_indirect_refs_r (tree *expr_p, int 
*** 1550,1558 ****
  	 call to __builtin_trap.  In all likelyhood this will all be
  	 constant-folded away, but in the meantime we can't leave with
  	 something that get_expr_operands can't understand.  */
!       STRIP_NOPS (base);
!       if (TREE_CODE (base) == ADDR_EXPR
! 	  && TREE_CODE (TREE_OPERAND (base, 0)) == STRING_CST)
  	{
  	  /* FIXME: Except that this causes problems elsewhere with dead
  	     code not being deleted, and we abort in the rtl expanders 
--- 1695,1705 ----
  	 call to __builtin_trap.  In all likelyhood this will all be
  	 constant-folded away, but in the meantime we can't leave with
  	 something that get_expr_operands can't understand.  */
! 
!       t = base;
!       STRIP_NOPS (t);
!       if (TREE_CODE (t) == ADDR_EXPR
! 	  && TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST)
  	{
  	  /* FIXME: Except that this causes problems elsewhere with dead
  	     code not being deleted, and we abort in the rtl expanders 
*************** fold_indirect_refs_r (tree *expr_p, int 
*** 1561,1644 ****
  	  /* FIXME2: This condition should be signaled by
  	     fold_read_from_constant_string directly, rather than 
  	     re-checking for it here.  */
! #if 0
! 	  return base;
! #else
! 	  *expr_p = integer_zero_node;
! #endif
  	}
  
!       return NULL_TREE;
!     }
!   base = TREE_OPERAND (base, 0);
! 
!   if (integer_zerop (offset))
!     {
!       /* If the variable is of ARRAY_TYPE, and we're actually dereferencing
! 	 an element of the array, we need to build an array reference rather
! 	 than re-use the existing INDIRECT_REF.  */
!       if (TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE
! 	  && (TYPE_MAIN_VARIANT (TREE_TYPE (expr))
! 	      == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base)))))
  	{
! 	  t = TYPE_DOMAIN (TREE_TYPE (base));
  	  if (t)
! 	    t = TYPE_MIN_VALUE (t);
! 	  if (!t)
! 	    t = offset;
! 	  t = build (ARRAY_REF, TREE_TYPE (expr), base, t);
  	}
-       else
- 	t = base;
      }
!   else
      {
!       unsigned HOST_WIDE_INT lquo, lrem;
!       HOST_WIDE_INT hquo, hrem;
!       tree elt_size, min_idx, idx;
! 
!       /* Ignore stupid user tricks of indexing non-array variables.  */
!       if (TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE
! 	  || (TYPE_MAIN_VARIANT (TREE_TYPE (expr))
! 	      != TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base)))))
! 	return NULL_TREE;
! 	
!       /* Whee.  Ignore indexing of variable sized types.  */
!       elt_size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (base)));
!       if (TREE_CODE (elt_size) != INTEGER_CST)
! 	return NULL_TREE;
  
!       /* If the division isn't exact, then don't do anything.  Equally
! 	 invalid as the above indexing of non-array variables.  */
!       if (div_and_round_double (TRUNC_DIV_EXPR, 1,
! 				TREE_INT_CST_LOW (offset),
! 				TREE_INT_CST_HIGH (offset),
! 				TREE_INT_CST_LOW (elt_size),
! 				TREE_INT_CST_HIGH (elt_size),
! 				&lquo, &hquo, &lrem, &hrem)
! 	  || lrem || hrem)
! 	return NULL_TREE;
!       idx = build_int_2_wide (lquo, hquo);
  
!       /* Re-bias the index by the min index of the array type.  */
!       min_idx = TYPE_DOMAIN (TREE_TYPE (base));
!       if (min_idx)
! 	{
!           min_idx = TYPE_MIN_VALUE (min_idx);
! 	  if (min_idx)
! 	    {
! 	      idx = convert (TREE_TYPE (min_idx), idx);
! 	      if (!integer_zerop (min_idx))
! 	        idx = fold (build (PLUS_EXPR, TREE_TYPE (min_idx),
! 			    idx, min_idx));
! 	    }
! 	}
  
!       t = build (ARRAY_REF, TREE_TYPE (expr), base, idx);
      }
  
!   *expr_p = t;
!   *changed_p = true;
  
    return NULL_TREE;
  }
--- 1708,1840 ----
  	  /* FIXME2: This condition should be signaled by
  	     fold_read_from_constant_string directly, rather than 
  	     re-checking for it here.  */
! 	  return integer_zero_node;
  	}
  
!       /* Try folding *(B+O) to B->X.  Still an improvement.  */
!       if (POINTER_TYPE_P (TREE_TYPE (base)))
  	{
!           t = maybe_fold_offset_to_component_ref (TREE_TYPE (TREE_TYPE (base)),
! 						  base, offset,
! 						  TREE_TYPE (expr), true);
  	  if (t)
! 	    return t;
  	}
      }
! 
!   /* Otherwise we had an offset that we could not simplify.  */
!   return NULL_TREE;
! }
! 
! /* A subroutine of fold_stmt_r.  EXPR is a PLUS_EXPR.
! 
!    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 the cast doesn't actually exist, but is implicit in the
!    type of the PLUS_EXPR.  We'd like to turn this into
! 	&array[x]
!    which may be able to propagate further.  */
! 
! static tree
! maybe_fold_stmt_plus (tree expr)
! {
!   tree op0 = TREE_OPERAND (expr, 0);
!   tree op1 = TREE_OPERAND (expr, 1);
!   tree ptr_type = TREE_TYPE (expr);
!   tree ptd_type;
!   tree t;
! 
!   /* We're only interested in pointer arithmetic.  */
!   if (!POINTER_TYPE_P (ptr_type))
!     return NULL_TREE;
!   /* Canonicalize the integral operand to op1.  */
!   if (INTEGRAL_TYPE_P (TREE_TYPE (op0)))
!     t = op0, op0 = op1, op1 = t;
!   /* It had better be a constant.  */
!   if (TREE_CODE (op1) != INTEGER_CST)
!     return NULL_TREE;
!   /* The first operand should be an ADDR_EXPR.  */
!   if (TREE_CODE (op0) != ADDR_EXPR)
!     return NULL_TREE;
! 
!   op0 = TREE_OPERAND (op0, 0);
!   ptd_type = TREE_TYPE (ptr_type);
! 
!   /* At which point we can try some of the same things as for indirects.  */
!   t = maybe_fold_offset_to_array_ref (op0, op1, ptd_type);
!   if (!t)
!     t = maybe_fold_offset_to_component_ref (TREE_TYPE (op0), op0, op1,
! 					    ptd_type, false);
!   if (t)
!     t = build1 (ADDR_EXPR, ptr_type, t);
! 
!   return t;
! }
! 
! /* Subroutine of fold_stmt called via walk_tree.  We perform several
!    simplifications of EXPR_P, mostly having to do with pointer arithmetic.  */
! 
! static tree
! fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data)
! {
!   bool *changed_p = data;
!   tree expr = *expr_p, t;
! 
!   /* ??? It'd be nice if walk_tree had a pre-order option.  */
!   switch (TREE_CODE (expr))
      {
!     case INDIRECT_REF:
!       t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
!       if (t)
! 	return t;
!       *walk_subtrees = 0;
! 
!       t = maybe_fold_stmt_indirect (expr, TREE_OPERAND (expr, 0),
! 				    integer_zero_node);
!       break;
! 
!     /* ??? Could handle ARRAY_REF here, as a variant of INDIRECT_REF.
!        We'd only want to bother decomposing an existing ARRAY_REF if
!        the base array is found to have another offset contained within.
!        Otherwise we'd be wasting time.  */
! 
!     case ADDR_EXPR:
!       t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
!       if (t)
! 	return t;
!       *walk_subtrees = 0;
! 
!       /* Set TREE_INVARIANT properly so that the value is properly
! 	 considered constant, and so gets propagated as expected.  */
!       if (*changed_p)
!         recompute_tree_invarant_for_addr_expr (expr);
!       return NULL_TREE;
  
!     case PLUS_EXPR:
!       t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
!       if (t)
! 	return t;
!       t = walk_tree (&TREE_OPERAND (expr, 1), fold_stmt_r, data, NULL);
!       if (t)
! 	return t;
!       *walk_subtrees = 0;
  
!       t = maybe_fold_stmt_plus (expr);
!       break;
  
!     default:
!       return NULL_TREE;
      }
  
!   if (t)
!     {
!       *expr_p = t;
!       *changed_p = true;
!     }
  
    return NULL_TREE;
  }
*************** fold_stmt (tree *stmt_p)
*** 1657,1663 ****
  
    /* If we replaced constants and the statement makes pointer dereferences,
       then we may need to fold instances of *&VAR into VAR, etc.  */
!   if (walk_tree (stmt_p, fold_indirect_refs_r, &changed, NULL))
      {
        *stmt_p
  	= build_function_call_expr (implicit_built_in_decls[BUILT_IN_TRAP],
--- 1853,1859 ----
  
    /* If we replaced constants and the statement makes pointer dereferences,
       then we may need to fold instances of *&VAR into VAR, etc.  */
!   if (walk_tree (stmt_p, fold_stmt_r, &changed, NULL))
      {
        *stmt_p
  	= build_function_call_expr (implicit_built_in_decls[BUILT_IN_TRAP],
*************** fold_stmt (tree *stmt_p)
*** 1681,1691 ****
    if (result == NULL_TREE)
      result = fold (rhs);
  
!   /* Strip away useless type conversions.  */
    if (result != rhs)
      {
        changed = true;
-       STRIP_MAIN_TYPE_NOPS (result);
        set_rhs (stmt_p, result);
      }
  
--- 1877,1892 ----
    if (result == NULL_TREE)
      result = fold (rhs);
  
!   /* Strip away useless type conversions.  Both the NON_LVALUE_EXPR that
!      may have been added by fold, and "useless" type conversions that might
!      now be apparent due to propagation.  */
!   STRIP_MAIN_TYPE_NOPS (result);
!   while (tree_ssa_useless_type_conversion (result))
!     result = TREE_OPERAND (result, 0);
! 
    if (result != rhs)
      {
        changed = true;
        set_rhs (stmt_p, result);
      }
  
Index: tree.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.c,v
retrieving revision 1.263.2.69
diff -u -p -c -r1.263.2.69 tree.c
*** tree.c	17 Nov 2003 23:39:20 -0000	1.263.2.69
--- tree.c	19 Nov 2003 15:41:44 -0000
*************** build (enum tree_code code, tree tt, ...
*** 2377,2382 ****
--- 2377,2419 ----
    return t;
  }
  
+ /* A helper function for build1 and constant folders.
+    Set TREE_CONSTANT and TREE_INVARIANT for an ADDR_EXPR.  */
+ 
+ void
+ recompute_tree_invarant_for_addr_expr (tree t)
+ {
+   tree node = TREE_OPERAND (t, 0);
+   bool tc = false, ti = false;
+ 
+   /* Addresses of constants and static variables are constant;
+      all other decl addresses are invariant.  */
+   if (staticp (node))
+     tc = ti = true;
+   else
+     {
+       /* Step past constant offsets.  */
+       while (1)
+ 	{
+ 	  if (TREE_CODE (node) == COMPONENT_REF
+ 	      && TREE_CODE (TREE_OPERAND (node, 1)) == FIELD_DECL
+ 	      && ! DECL_BIT_FIELD (TREE_OPERAND (node, 1)))
+ 	    ;
+ 	  else if (TREE_CODE (node) == ARRAY_REF
+ 	           && TREE_CONSTANT (TREE_OPERAND (node, 1)))
+ 	    ;
+ 	  else
+ 	    break;
+ 	  node = TREE_OPERAND (node, 0);
+ 	}
+       if (DECL_P (node))
+         ti = true;
+     }
+ 
+   TREE_CONSTANT (t) = tc;
+   TREE_INVARIANT (t) = ti;
+ }
+ 
  /* Same as above, but only builds for unary operators.
     Saves lions share of calls to `build'; cuts down use
     of varargs, which is expensive for RISC machines.  */
*************** build1 (enum tree_code code, tree type, 
*** 2460,2488 ****
      case ADDR_EXPR:
        if (node)
  	{
! 	  /* Addresses of constants and static variables are constant;
! 	     all other decl addresses are invariant.  */
! 	  if (staticp (node))
! 	    TREE_CONSTANT (t) = TREE_INVARIANT (t) = 1;
! 	  else
! 	    {
! 	      /* Step past constant offsets.  */
! 	      while (1)
! 		{
! 		  if (TREE_CODE (node) == COMPONENT_REF
! 		      && TREE_CODE (TREE_OPERAND (node, 1)) == FIELD_DECL
! 		      && ! DECL_BIT_FIELD (TREE_OPERAND (node, 1)))
! 		    ;
! 		  else if (TREE_CODE (node) == ARRAY_REF
! 		           && TREE_CONSTANT (TREE_OPERAND (node, 1)))
! 		    ;
! 		  else
! 		    break;
! 		  node = TREE_OPERAND (node, 0);
! 		}
! 	      if (DECL_P (node))
! 	        TREE_INVARIANT (t) = 1;
! 	    }
  
  	  /* The address of a volatile decl or reference does not have
  	     side-effects.  But be careful not to ignore side-effects from
--- 2497,2503 ----
      case ADDR_EXPR:
        if (node)
  	{
! 	  recompute_tree_invarant_for_addr_expr (t);
  
  	  /* The address of a volatile decl or reference does not have
  	     side-effects.  But be careful not to ignore side-effects from
Index: tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.h,v
retrieving revision 1.342.2.131
diff -u -p -c -r1.342.2.131 tree.h
*** tree.h	18 Nov 2003 23:06:36 -0000	1.342.2.131
--- tree.h	19 Nov 2003 15:41:47 -0000
*************** extern void dump_tree_statistics (void);
*** 3315,3320 ****
--- 3315,3322 ----
  extern void expand_function_end (void);
  extern void expand_function_start (tree, int);
  extern void expand_pending_sizes (tree);
+ extern void recompute_tree_invarant_for_addr_expr (tree);
+ 
  
  extern int real_onep (tree);
  extern int real_twop (tree);



More information about the Gcc-patches mailing list