[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