diff options
author | Richard Guenther <rguenther@suse.de> | 2006-12-31 17:27:35 +0000 |
---|---|---|
committer | Richard Biener <rguenth@gcc.gnu.org> | 2006-12-31 17:27:35 +0000 |
commit | e015f578884bce00d4fe8d7b8d14b94b11879ba3 (patch) | |
tree | 37fae3dae27d12d89d0e81b7873ac69ef8dd0b39 /gcc/fold-const.c | |
parent | 870aa1ebe0d3e05912f763ece1ed4491282d34e2 (diff) | |
download | gcc-e015f578884bce00d4fe8d7b8d14b94b11879ba3.zip gcc-e015f578884bce00d4fe8d7b8d14b94b11879ba3.tar.gz gcc-e015f578884bce00d4fe8d7b8d14b94b11879ba3.tar.bz2 |
re PR tree-optimization/30137 (Missed folding of pointer comparison)
2006-12-31 Richard Guenther <rguenther@suse.de>
PR middle-end/30137
* fold-const.c (fold_comparison): Fold comparison of addresses
of components.
* testsuite/gcc.dg/pr30137-1.c: New testcase.
* testsuite/gcc.dg/pr30137-2.c: Likewise.
From-SVN: r120301
Diffstat (limited to 'gcc/fold-const.c')
-rw-r--r-- | gcc/fold-const.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/gcc/fold-const.c b/gcc/fold-const.c index da9579e..071ddcd 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -7988,6 +7988,110 @@ fold_comparison (enum tree_code code, tree type, tree op0, tree op1) return fold_build2 (code, type, variable, lhs); } + /* For comparisons of pointers we can decompose it to a compile time + comparison of the base objects and the offsets into the object. + This requires at least one operand being an ADDR_EXPR to do more + than the operand_equal_p test below. */ + if (POINTER_TYPE_P (TREE_TYPE (arg0)) + && (TREE_CODE (arg0) == ADDR_EXPR + || TREE_CODE (arg1) == ADDR_EXPR)) + { + tree base0, base1, offset0 = NULL_TREE, offset1 = NULL_TREE; + HOST_WIDE_INT bitsize, bitpos0 = 0, bitpos1 = 0; + enum machine_mode mode; + int volatilep, unsignedp; + bool indirect_base0 = false; + + /* Get base and offset for the access. Strip ADDR_EXPR for + get_inner_reference, but put it back by stripping INDIRECT_REF + off the base object if possible. */ + base0 = arg0; + if (TREE_CODE (arg0) == ADDR_EXPR) + { + base0 = get_inner_reference (TREE_OPERAND (arg0, 0), + &bitsize, &bitpos0, &offset0, &mode, + &unsignedp, &volatilep, false); + if (TREE_CODE (base0) == INDIRECT_REF) + base0 = TREE_OPERAND (base0, 0); + else + indirect_base0 = true; + } + + base1 = arg1; + if (TREE_CODE (arg1) == ADDR_EXPR) + { + base1 = get_inner_reference (TREE_OPERAND (arg1, 0), + &bitsize, &bitpos1, &offset1, &mode, + &unsignedp, &volatilep, false); + /* We have to make sure to have an indirect/non-indirect base1 + just the same as we did for base0. */ + if (TREE_CODE (base1) == INDIRECT_REF + && !indirect_base0) + base1 = TREE_OPERAND (base1, 0); + else if (!indirect_base0) + base1 = NULL_TREE; + } + else if (indirect_base0) + base1 = NULL_TREE; + + /* If we have equivalent bases we might be able to simplify. */ + if (base0 && base1 + && operand_equal_p (base0, base1, 0)) + { + /* We can fold this expression to a constant if the non-constant + offset parts are equal. */ + if (offset0 == offset1 + || (offset0 && offset1 + && operand_equal_p (offset0, offset1, 0))) + { + switch (code) + { + case EQ_EXPR: + return build_int_cst (boolean_type_node, bitpos0 == bitpos1); + case NE_EXPR: + return build_int_cst (boolean_type_node, bitpos0 != bitpos1); + case LT_EXPR: + return build_int_cst (boolean_type_node, bitpos0 < bitpos1); + case LE_EXPR: + return build_int_cst (boolean_type_node, bitpos0 <= bitpos1); + case GE_EXPR: + return build_int_cst (boolean_type_node, bitpos0 >= bitpos1); + case GT_EXPR: + return build_int_cst (boolean_type_node, bitpos0 > bitpos1); + default:; + } + } + /* We can simplify the comparison to a comparison of the variable + offset parts if the constant offset parts are equal. + Be careful to use signed size type here because otherwise we + mess with array offsets in the wrong way. This is possible + because pointer arithmetic is restricted to retain within an + object and overflow on pointer differences is undefined as of + 6.5.6/8 and /9 with respect to the signed ptrdiff_t. */ + else if (bitpos0 == bitpos1) + { + tree signed_size_type_node; + signed_size_type_node = signed_type_for (size_type_node); + + /* By converting to signed size type we cover middle-end pointer + arithmetic which operates on unsigned pointer types of size + type size and ARRAY_REF offsets which are properly sign or + zero extended from their type in case it is narrower than + size type. */ + if (offset0 == NULL_TREE) + offset0 = build_int_cst (signed_size_type_node, 0); + else + offset0 = fold_convert (signed_size_type_node, offset0); + if (offset1 == NULL_TREE) + offset1 = build_int_cst (signed_size_type_node, 0); + else + offset1 = fold_convert (signed_size_type_node, offset1); + + return fold_build2 (code, type, offset0, offset1); + } + } + } + /* If this is a comparison of two exprs that look like an ARRAY_REF of the same object, then we can fold this to a comparison of the two offsets in signed size type. This is possible because pointer arithmetic is |