[PATCH] Make CCP "complete"

Richard Guenther rguenther@suse.de
Wed Aug 13 14:35:00 GMT 2008


CCP (again) finds new CCP opportunities after itself (at least for
tramp3d).  The following patch fixes that and addresses some
factorization for a common sequence for folding address manipulations.
It also (re?-)enables code in maybe_fold_offset_to_reference to
properly strip component refs off the base before trying to
re-construct them.  This is the key to allow propagating for the
added testcases.  To not break __builtin_object_size we have to
play some games, but at least it's not too ugly.

Bootstrapped and tested on x86_64-unknown-linux-gnu, applied to trunk.

This is where you end up if you try to do some pass 
reorderings/removals...

Richard.

2008-08-12  Richard Guenther  <rguenther@suse.de>

	* tree.h (maybe_fold_offset_to_address): Declare.
	* tree-ssa-ccp.c (surely_varying_stmt_p): Fix typo in last commit.
	(ccp_fold): Handle pointer conversions the same as fold_stmt.
	Likewise for POINTER_PLUS_EXPR.
	(maybe_fold_offset_to_reference): Enable disabled code.
	(maybe_fold_offset_to_address): New function.
	(fold_stmt_r): Use it.
	(fold_gimple_assign): Likewise.
	* gimplify.c (gimplify_conversion): Use maybe_fold_offset_to_address.
	(gimplify_expr): Likewise.

	* gcc.dg/tree-ssa/ssa-ccp-21.c: New testcase.
	* gcc.dg/tree-ssa/ssa-ccp-22.c: Likewise.
	* gcc.dg/tree-ssa/ssa-ccp-23.c: Likewise.

Index: gcc/tree-ssa-ccp.c
===================================================================
*** gcc/tree-ssa-ccp.c.orig	2008-08-13 10:58:24.000000000 +0200
--- gcc/tree-ssa-ccp.c	2008-08-13 15:01:13.000000000 +0200
*************** surely_varying_stmt_p (gimple stmt)
*** 630,636 ****
        tree fndecl;
        if (!gimple_call_lhs (stmt)
  	  || ((fndecl = gimple_call_fndecl (stmt)) != NULL_TREE
! 	      && DECL_BUILT_IN (fndecl)))
  	return true;
      }
  
--- 630,636 ----
        tree fndecl;
        if (!gimple_call_lhs (stmt)
  	  || ((fndecl = gimple_call_fndecl (stmt)) != NULL_TREE
! 	      && !DECL_BUILT_IN (fndecl)))
  	return true;
      }
  
*************** ccp_fold (gimple stmt)
*** 988,1005 ****
  		 useless_type_conversion_p places for pointer type conversions
  		 do not apply here.  Substitution later will only substitute to
  		 allowed places.  */
!               if ((subcode == NOP_EXPR || subcode == CONVERT_EXPR)
! 		  && ((POINTER_TYPE_P (TREE_TYPE (lhs))
! 		       && POINTER_TYPE_P (TREE_TYPE (op0))
! 		       /* Do not allow differences in volatile qualification
! 			  as this might get us confused as to whether a
! 			  propagation destination statement is volatile
! 			  or not.  See PR36988.  */
! 		       && (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (lhs)))
! 			   == TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (op0)))))
! 		      || useless_type_conversion_p (TREE_TYPE (lhs),
! 						    TREE_TYPE (op0))))
!                 return op0;
  
                return fold_unary (subcode, gimple_expr_type (stmt), op0);
              }  
--- 988,1013 ----
  		 useless_type_conversion_p places for pointer type conversions
  		 do not apply here.  Substitution later will only substitute to
  		 allowed places.  */
! 	      if (IS_CONVERT_EXPR_CODE_P (subcode)
! 		  && POINTER_TYPE_P (TREE_TYPE (lhs))
! 		  && POINTER_TYPE_P (TREE_TYPE (op0))
! 		  /* Do not allow differences in volatile qualification
! 		     as this might get us confused as to whether a
! 		     propagation destination statement is volatile
! 		     or not.  See PR36988.  */
! 		  && (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (lhs)))
! 		      == TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (op0)))))
! 		{
! 		  tree tem;
! 		  /* Still try to generate a constant of correct type.  */
! 		  if (!useless_type_conversion_p (TREE_TYPE (lhs),
! 						  TREE_TYPE (op0))
! 		      && ((tem = maybe_fold_offset_to_address
! 				   (op0, integer_zero_node, TREE_TYPE (lhs)))
! 			  != NULL_TREE))
! 		    return tem;
! 		  return op0;
! 		}
  
                return fold_unary (subcode, gimple_expr_type (stmt), op0);
              }  
*************** ccp_fold (gimple stmt)
*** 1025,1030 ****
--- 1033,1050 ----
                      op1 = val->value;
                  }
  
+ 	      /* Fold &foo + CST into an invariant reference if possible.  */
+ 	      if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
+ 		  && TREE_CODE (op0) == ADDR_EXPR
+ 		  && TREE_CODE (op1) == INTEGER_CST)
+ 		{
+ 		  tree lhs = gimple_assign_lhs (stmt);
+ 		  tree tem = maybe_fold_offset_to_address (op0, op1,
+ 							   TREE_TYPE (lhs));
+ 		  if (tem != NULL_TREE)
+ 		    return tem;
+ 		}
+ 
                return fold_binary (subcode, gimple_expr_type (stmt), op0, op1);
              }
  
*************** maybe_fold_offset_to_reference (tree bas
*** 1948,1962 ****
  	 so it needs to be removed and new COMPONENT_REF constructed.
  	 The wrong COMPONENT_REF are often constructed by folding the
  	 (type *)&object within the expression (type *)&object+offset  */
!       if (handled_component_p (base) && 0)
  	{
            HOST_WIDE_INT sub_offset, size, maxsize;
  	  tree newbase;
  	  newbase = get_ref_base_and_extent (base, &sub_offset,
  					     &size, &maxsize);
  	  gcc_assert (newbase);
! 	  gcc_assert (!(sub_offset & (BITS_PER_UNIT - 1)));
! 	  if (size == maxsize)
  	    {
  	      base = newbase;
  	      if (sub_offset)
--- 1968,1982 ----
  	 so it needs to be removed and new COMPONENT_REF constructed.
  	 The wrong COMPONENT_REF are often constructed by folding the
  	 (type *)&object within the expression (type *)&object+offset  */
!       if (handled_component_p (base))
  	{
            HOST_WIDE_INT sub_offset, size, maxsize;
  	  tree newbase;
  	  newbase = get_ref_base_and_extent (base, &sub_offset,
  					     &size, &maxsize);
  	  gcc_assert (newbase);
! 	  if (size == maxsize
! 	      && !(sub_offset & (BITS_PER_UNIT - 1)))
  	    {
  	      base = newbase;
  	      if (sub_offset)
*************** maybe_fold_offset_to_reference (tree bas
*** 1988,1993 ****
--- 2008,2070 ----
    return ret;
  }
  
+ /* Attempt to express (ORIG_TYPE)&BASE+OFFSET as &BASE->field_of_orig_type
+    or &BASE[index] or by combination of those.
+ 
+    Before attempting the conversion strip off existing component refs.  */
+ 
+ tree
+ maybe_fold_offset_to_address (tree addr, tree offset, tree orig_type)
+ {
+   tree t;
+ 
+   gcc_assert (POINTER_TYPE_P (TREE_TYPE (addr))
+ 	      && POINTER_TYPE_P (orig_type));
+ 
+   t = maybe_fold_offset_to_reference (addr, offset, TREE_TYPE (orig_type));
+   if (t != NULL_TREE)
+     {
+       tree orig = addr;
+       tree ptr_type;
+ 
+       /* For __builtin_object_size to function correctly we need to
+          make sure not to fold address arithmetic so that we change
+ 	 reference from one array to another.  This would happen for
+ 	 example for
+ 
+ 	   struct X { char s1[10]; char s2[10] } s;
+ 	   char *foo (void) { return &s.s2[-4]; }
+ 
+ 	 where we need to avoid generating &s.s1[6].  As the C and
+ 	 C++ frontends create different initial trees
+ 	 (char *) &s.s1 + -4  vs.  &s.s1[-4]  we have to do some
+ 	 sophisticated comparisons here.  Note that checking for the
+ 	 condition after the fact is easier than trying to avoid doing
+ 	 the folding.  */
+       STRIP_NOPS (orig);
+       if (TREE_CODE (orig) == ADDR_EXPR)
+ 	orig = TREE_OPERAND (orig, 0);
+       if ((TREE_CODE (orig) == ARRAY_REF
+ 	   || (TREE_CODE (orig) == COMPONENT_REF
+ 	       && TREE_CODE (TREE_TYPE (TREE_OPERAND (orig, 1))) == ARRAY_TYPE))
+ 	  && (TREE_CODE (t) == ARRAY_REF
+ 	      || (TREE_CODE (t) == COMPONENT_REF
+ 		  && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 1))) == ARRAY_TYPE))
+ 	  && !operand_equal_p (TREE_CODE (orig) == ARRAY_REF
+ 			       ? TREE_OPERAND (orig, 0) : orig,
+ 			       TREE_CODE (t) == ARRAY_REF
+ 			       ? TREE_OPERAND (t, 0) : t, 0))
+ 	return NULL_TREE;
+ 
+       ptr_type = build_pointer_type (TREE_TYPE (t));
+       if (!useless_type_conversion_p (orig_type, ptr_type))
+ 	return NULL_TREE;
+       return build_fold_addr_expr_with_type (t, ptr_type);
+     }
+ 
+   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.  */
  
*************** fold_stmt_r (tree *expr_p, int *walk_sub
*** 2223,2238 ****
  
        if (POINTER_TYPE_P (TREE_TYPE (expr))
  	  && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0)))
! 	  && (t = maybe_fold_offset_to_reference
! 		      (TREE_OPERAND (expr, 0),
! 		       integer_zero_node,
! 		       TREE_TYPE (TREE_TYPE (expr)))))
! 	{
! 	  tree ptr_type = build_pointer_type (TREE_TYPE (t));
! 	  if (!useless_type_conversion_p (TREE_TYPE (expr), ptr_type))
! 	    return NULL_TREE;
!           t = build_fold_addr_expr_with_type (t, ptr_type);
! 	}
        break;
  
        /* ??? Could handle more ARRAY_REFs here, as a variant of INDIRECT_REF.
--- 2300,2309 ----
  
        if (POINTER_TYPE_P (TREE_TYPE (expr))
  	  && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0)))
! 	  && (t = maybe_fold_offset_to_address (TREE_OPERAND (expr, 0),
! 						integer_zero_node,
! 						TREE_TYPE (TREE_TYPE (expr)))))
! 	return t;
        break;
  
        /* ??? Could handle more ARRAY_REFs here, as a variant of INDIRECT_REF.
*************** fold_gimple_assign (gimple_stmt_iterator
*** 2715,2729 ****
  	       && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt))))
  	{
  	  tree type = gimple_expr_type (stmt);
! 	  tree t = maybe_fold_offset_to_reference (gimple_assign_rhs1 (stmt),
! 						   integer_zero_node,
! 						   TREE_TYPE (type));
  	  if (t)
! 	    {
! 	      tree ptr_type = build_pointer_type (TREE_TYPE (t));
! 	      if (useless_type_conversion_p (type, ptr_type))
! 		return build_fold_addr_expr_with_type (t, ptr_type);
! 	    }
  	}
        break;
  
--- 2786,2795 ----
  	       && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt))))
  	{
  	  tree type = gimple_expr_type (stmt);
! 	  tree t = maybe_fold_offset_to_address (gimple_assign_rhs1 (stmt),
! 						 integer_zero_node, type);
  	  if (t)
! 	    return t;
  	}
        break;
  
Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-21.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-21.c	2008-08-13 10:58:43.000000000 +0200
***************
*** 0 ****
--- 1,25 ----
+ /* { dg-do compile } */
+ /* { dg-options "-O -fdump-tree-ccp1" } */
+ 
+ struct A {
+     struct B {
+ 	int i;
+     } b;
+ } a;
+ 
+ int foo (void)
+ {
+   struct B *p = &a.b;
+   struct A *q = (struct A *) p;
+   return q->b.i;
+ }
+ 
+ int bar (void)
+ {
+   struct A *p = &a;
+   struct B *q = (struct B *) p;
+   return q->i;
+ }
+ 
+ /* { dg-final { scan-tree-dump-times "a.b.i" 2 "ccp1" } } */
+ /* { dg-final { cleanup-tree-dump "ccp1" } } */
Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-22.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-22.c	2008-08-13 10:58:43.000000000 +0200
***************
*** 0 ****
--- 1,15 ----
+ /* { dg-do compile } */
+ /* { dg-options "-O -fdump-tree-ccp1" } */
+ 
+ /* Make sure we propagate through builtins.  */
+ 
+ int foo (unsigned b)
+ {
+   unsigned t = -1;
+   int x = b <= t;
+   long l = __builtin_expect (x, 0);
+   return l;
+ }
+ 
+ /* { dg-final { scan-tree-dump "return 1;" "ccp1" } } */
+ /* { dg-final { cleanup-tree-dump "ccp1" } } */
Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-23.c
===================================================================
*** /dev/null	1970-01-01 00:00:00.000000000 +0000
--- gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-23.c	2008-08-13 10:58:43.000000000 +0200
***************
*** 0 ****
--- 1,19 ----
+ /* { dg-do compile } */
+ /* { dg-options "-O -fdump-tree-ccp1" } */
+ 
+ /* Make sure we propagate through POINTER_PLUS_EXPRs.  */
+ 
+ struct A {
+   int i[2];
+ } a;
+ 
+ int foo (void)
+ {
+   struct A *p = &a;
+   int *q = (int *)p;
+   int *x = q + 1;
+   return *x;
+ }
+ 
+ /* { dg-final { scan-tree-dump "a.i\\\[1\\\]" "ccp1" } } */
+ /* { dg-final { cleanup-tree-dump "ccp1" } } */
Index: gcc/gimplify.c
===================================================================
*** gcc/gimplify.c.orig	2008-08-13 10:45:31.000000000 +0200
--- gcc/gimplify.c	2008-08-13 13:58:26.000000000 +0200
*************** gimplify_conversion (tree *expr_p)
*** 1842,1858 ****
    /* 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 (TREE_CODE (*expr_p) == NOP_EXPR
        && POINTER_TYPE_P (TREE_TYPE (*expr_p))
        && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (*expr_p, 0)))
!       && (tem = maybe_fold_offset_to_reference
  		  (TREE_OPERAND (*expr_p, 0),
! 		   integer_zero_node, TREE_TYPE (TREE_TYPE (*expr_p)))))
!     {
!       tree ptr_type = build_pointer_type (TREE_TYPE (tem));
!       if (useless_type_conversion_p (TREE_TYPE (*expr_p), ptr_type))
!         *expr_p = build_fold_addr_expr_with_type (tem, ptr_type);
!     }
  
    /* If we still have a conversion at the toplevel,
       then canonicalize some constructs.  */
--- 1842,1854 ----
    /* 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
  		  (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.  */
*************** gimplify_expr (tree *expr_p, gimple_seq
*** 6735,6764 ****
  	     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_reference
  			 (TREE_OPERAND (*expr_p, 0), TREE_OPERAND (*expr_p, 1),
! 		   	  TREE_TYPE (TREE_TYPE (*expr_p)))))
! 	     {
! 	       tree ptr_type = build_pointer_type (TREE_TYPE (tmp));
! 	       if (useless_type_conversion_p (TREE_TYPE (*expr_p), ptr_type))
! 		 {
!                    *expr_p = build_fold_addr_expr_with_type (tmp, ptr_type);
! 		   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_reference
  			 (TREE_OPERAND (TREE_OPERAND (*expr_p, 0), 0),
  			  TREE_OPERAND (*expr_p, 1),
! 		   	  TREE_TYPE (TREE_TYPE
! 				  (TREE_OPERAND (TREE_OPERAND (*expr_p, 0),
! 						 0))))))
  	     {
-                tmp = build_fold_addr_expr (tmp);
                 *expr_p = fold_convert (TREE_TYPE (*expr_p), tmp);
  	       break;
  	     }
--- 6731,6754 ----
  	     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
  			 (TREE_OPERAND (*expr_p, 0), TREE_OPERAND (*expr_p, 1),
! 		   	  TREE_TYPE (*expr_p))))
! 	    {
! 	      *expr_p = tmp;
! 	      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
  			 (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);
  	       break;
  	     }
Index: gcc/tree.h
===================================================================
*** gcc/tree.h.orig	2008-07-29 11:47:30.000000000 +0200
--- gcc/tree.h	2008-08-13 13:46:09.000000000 +0200
*************** extern void fold_undefer_overflow_warnin
*** 4744,4749 ****
--- 4744,4750 ----
  extern void fold_undefer_and_ignore_overflow_warnings (void);
  extern bool fold_deferring_overflow_warnings_p (void);
  extern tree maybe_fold_offset_to_reference (tree, tree, tree);
+ extern tree maybe_fold_offset_to_address (tree, tree, tree);
  extern tree maybe_fold_stmt_addition (tree, tree, tree);
  
  extern tree force_fit_type_double (tree, unsigned HOST_WIDE_INT, HOST_WIDE_INT,



More information about the Gcc-patches mailing list